diff --git a/.changeset/modern-carrots-deny.md b/.changeset/modern-carrots-deny.md new file mode 100644 index 0000000000000..55877e1f43312 --- /dev/null +++ b/.changeset/modern-carrots-deny.md @@ -0,0 +1,5 @@ +--- +"@graphql-mesh/utils": patch +--- + +Register terminate handler utility diff --git a/.changeset/nervous-lions-buy.md b/.changeset/nervous-lions-buy.md new file mode 100644 index 0000000000000..c78363c42fe55 --- /dev/null +++ b/.changeset/nervous-lions-buy.md @@ -0,0 +1,9 @@ +--- +"@graphql-mesh/plugin-prometheus": patch +"@graphql-mesh/utils": patch +"@graphql-mesh/plugin-hive": patch +"@graphql-mesh/cli": patch +"@graphql-mesh/serve-cli": patch +--- + +Terminate handler registry diff --git a/packages/legacy/cli/src/commands/serve/serve.ts b/packages/legacy/cli/src/commands/serve/serve.ts index e795d4afc95fd..22a701032bae0 100644 --- a/packages/legacy/cli/src/commands/serve/serve.ts +++ b/packages/legacy/cli/src/commands/serve/serve.ts @@ -11,9 +11,9 @@ import { process } from '@graphql-mesh/cross-helpers'; import { createMeshHTTPHandler } from '@graphql-mesh/http'; import { MeshInstance, ServeMeshOptions } from '@graphql-mesh/runtime'; import type { Logger } from '@graphql-mesh/types'; +import { registerTerminateHandler } from '@graphql-mesh/utils'; import { handleFatalError } from '../../handleFatalError.js'; import { GraphQLMeshCLIParams } from '../../index.js'; -import { registerTerminateHandler } from '../../terminateHandler.js'; function portSelectorFn(sources: [number, number, number], logger: Logger) { const port = sources.find(source => Boolean(source)) || 4000; diff --git a/packages/legacy/cli/src/index.ts b/packages/legacy/cli/src/index.ts index ca650a4006e1a..e290958ea6593 100644 --- a/packages/legacy/cli/src/index.ts +++ b/packages/legacy/cli/src/index.ts @@ -8,13 +8,19 @@ import { fs, path as pathModule, process } from '@graphql-mesh/cross-helpers'; import { getMesh, GetMeshOptions, MeshInstance, ServeMeshOptions } from '@graphql-mesh/runtime'; import { FsStoreStorageAdapter, MeshStore } from '@graphql-mesh/store'; import { Logger, YamlConfig } from '@graphql-mesh/types'; -import { defaultImportFn, DefaultLogger, pathExists, rmdirs, writeFile } from '@graphql-mesh/utils'; +import { + defaultImportFn, + DefaultLogger, + pathExists, + registerTerminateHandler, + rmdirs, + writeFile, +} from '@graphql-mesh/utils'; import { printSchemaWithDirectives } from '@graphql-tools/utils'; import { serveMesh } from './commands/serve/serve.js'; import { generateTsArtifacts } from './commands/ts-artifacts.js'; import { findAndParseConfig } from './config.js'; import { handleFatalError } from './handleFatalError.js'; -import { registerTerminateHandler } from './terminateHandler.js'; export { generateTsArtifacts, serveMesh, findAndParseConfig, handleFatalError }; diff --git a/packages/legacy/cli/src/terminateHandler.ts b/packages/legacy/cli/src/terminateHandler.ts deleted file mode 100644 index 00258aa53520e..0000000000000 --- a/packages/legacy/cli/src/terminateHandler.ts +++ /dev/null @@ -1,19 +0,0 @@ -const terminateEvents = ['SIGINT', 'SIGTERM'] as const; -type TerminateEvents = (typeof terminateEvents)[number]; -type TerminateHandler = (eventName: TerminateEvents) => void; -const terminateHandlers = new Set(); -for (const eventName of terminateEvents) { - process.once(eventName, () => { - for (const handler of terminateHandlers) { - handler(eventName); - terminateHandlers.delete(handler); - } - }); -} - -export function registerTerminateHandler(callback: TerminateHandler) { - terminateHandlers.add(callback); - return () => { - terminateHandlers.delete(callback); - }; -} diff --git a/packages/legacy/utils/src/index.ts b/packages/legacy/utils/src/index.ts index fee7534c8562f..3c02c17cc830a 100644 --- a/packages/legacy/utils/src/index.ts +++ b/packages/legacy/utils/src/index.ts @@ -21,3 +21,4 @@ export * from './sanitize-name-for-graphql.js'; export * from './with-cancel.js'; export * from './with-cookies.js'; export * from './wrapFetchWithHooks.js'; +export * from './registerTerminateHandler.js'; diff --git a/packages/legacy/utils/src/registerTerminateHandler.ts b/packages/legacy/utils/src/registerTerminateHandler.ts new file mode 100644 index 0000000000000..d2b176c30bb80 --- /dev/null +++ b/packages/legacy/utils/src/registerTerminateHandler.ts @@ -0,0 +1,30 @@ +const terminateEvents = ['SIGINT', 'SIGTERM'] as const; + +type TerminateEvents = (typeof terminateEvents)[number]; +type TerminateHandler = (eventName: TerminateEvents) => void; + +let eventListenerRegistered = false; + +const terminateHandlers = new Set(); + +function registerEventListener() { + for (const eventName of terminateEvents) { + globalThis.process?.once(eventName, () => { + for (const handler of terminateHandlers) { + handler(eventName); + terminateHandlers.delete(handler); + } + }); + } +} + +export function registerTerminateHandler(callback: TerminateHandler) { + if (!eventListenerRegistered) { + registerEventListener(); + eventListenerRegistered = true; + } + terminateHandlers.add(callback); + return () => { + terminateHandlers.delete(callback); + }; +} diff --git a/packages/plugins/hive/package.json b/packages/plugins/hive/package.json index 171fc3dd8f7bc..daf559530aff3 100644 --- a/packages/plugins/hive/package.json +++ b/packages/plugins/hive/package.json @@ -39,10 +39,8 @@ }, "dependencies": { "@graphql-hive/client": "^0.28.0", - "@graphql-mesh/string-interpolation": "0.5.3" - }, - "devDependencies": { - "@types/http-cache-semantics": "4.0.4" + "@graphql-mesh/string-interpolation": "0.5.3", + "@graphql-mesh/utils": "^0.97.3" }, "publishConfig": { "access": "public", diff --git a/packages/plugins/hive/src/index.ts b/packages/plugins/hive/src/index.ts index 4fc8e09379101..c08f580af89bf 100644 --- a/packages/plugins/hive/src/index.ts +++ b/packages/plugins/hive/src/index.ts @@ -2,6 +2,7 @@ import { createHive, HivePluginOptions, useYogaHive } from '@graphql-hive/client import { process } from '@graphql-mesh/cross-helpers'; import { stringInterpolator } from '@graphql-mesh/string-interpolation'; import { MeshPlugin, MeshPluginOptions, YamlConfig } from '@graphql-mesh/types'; +import { registerTerminateHandler } from '@graphql-mesh/utils'; export default function useMeshHive( pluginOptions: MeshPluginOptions, @@ -95,12 +96,16 @@ export default function useMeshHive( reporting, selfHosting, }); - const id = pluginOptions.pubsub.subscribe('destroy', () => { - hiveClient + function onTerminate() { + return hiveClient .dispose() - .catch(e => pluginOptions.logger.error(`Hive client failed to dispose`, e)) - .finally(() => pluginOptions.pubsub.unsubscribe(id)); - }); + .catch(e => pluginOptions.logger.error(`Hive client failed to dispose`, e)); + } + const id: number = pluginOptions.pubsub.subscribe('destroy', () => + onTerminate().finally(() => pluginOptions.pubsub.unsubscribe(id)), + ); + registerTerminateHandler(onTerminate); + return { onPluginInit({ addPlugin }) { addPlugin(useYogaHive(hiveClient)); diff --git a/packages/plugins/prometheus/tests/prometheus.spec.ts b/packages/plugins/prometheus/tests/prometheus.spec.ts index a91876ddaf30f..92db789dcb69a 100644 --- a/packages/plugins/prometheus/tests/prometheus.spec.ts +++ b/packages/plugins/prometheus/tests/prometheus.spec.ts @@ -1,3 +1,4 @@ +/* eslint-disable import/no-extraneous-dependencies */ import { createSchema } from 'graphql-yoga'; import { register as registry } from 'prom-client'; import { composeSubgraphs } from '@graphql-mesh/fusion-composition'; diff --git a/packages/serve-cli/src/runServeCLI.ts b/packages/serve-cli/src/runServeCLI.ts index a687df74a4d2d..bf66ace2d90d3 100644 --- a/packages/serve-cli/src/runServeCLI.ts +++ b/packages/serve-cli/src/runServeCLI.ts @@ -5,7 +5,7 @@ import { dirname, isAbsolute, join, relative } from 'path'; import { App, SSLApp } from 'uWebSockets.js'; import { createServeRuntime, UnifiedGraphConfig } from '@graphql-mesh/serve-runtime'; // eslint-disable-next-line import/no-extraneous-dependencies -import { DefaultLogger } from '@graphql-mesh/utils'; +import { DefaultLogger, registerTerminateHandler } from '@graphql-mesh/utils'; import { GitLoader } from '@graphql-tools/git-loader'; import { GithubLoader } from '@graphql-tools/github-loader'; import { GraphQLFileLoader } from '@graphql-tools/graphql-file-loader'; @@ -196,23 +196,3 @@ export async function runServeCLI({ } }); } - -const terminateEvents = ['SIGINT', 'SIGTERM'] as const; -type TerminateEvents = (typeof terminateEvents)[number]; -type TerminateHandler = (eventName: TerminateEvents) => void; -const terminateHandlers = new Set(); -for (const eventName of terminateEvents) { - process.once(eventName, () => { - for (const handler of terminateHandlers) { - handler(eventName); - terminateHandlers.delete(handler); - } - }); -} - -function registerTerminateHandler(callback: TerminateHandler) { - terminateHandlers.add(callback); - return () => { - terminateHandlers.delete(callback); - }; -} diff --git a/yarn.lock b/yarn.lock index aa37db81d321e..f5346753707c8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5161,6 +5161,7 @@ __metadata: dependencies: "@graphql-hive/client": "npm:^0.28.0" "@graphql-mesh/string-interpolation": "npm:0.5.3" + "@graphql-mesh/utils": "npm:^0.97.3" "@types/http-cache-semantics": "npm:4.0.4" peerDependencies: "@graphql-mesh/cross-helpers": ^0.4.1