From 11396841127b6a872b0920fb9ba400f0c14f351f Mon Sep 17 00:00:00 2001 From: "Gustavo H. Strassburger" Date: Fri, 31 Oct 2025 11:55:34 -0300 Subject: [PATCH 1/2] Add real-time feature flags support via Server-Sent Events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a new `realtime_flags` configuration option that enables real-time feature flag updates through Server-Sent Events (SSE). When enabled, the SDK establishes a persistent connection to receive flag updates without polling. Key changes: - Add `realtime_flags` config option (default: false) - Implement SSE connection management in PostHogFeatureFlags - Auto-reconnect on connection errors with 5-second delay - Clean shutdown of SSE connection 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- packages/browser/src/posthog-core.ts | 1 + packages/browser/src/posthog-featureflags.ts | 79 ++++++++++++++++++++ packages/browser/src/types.ts | 8 ++ 3 files changed, 88 insertions(+) diff --git a/packages/browser/src/posthog-core.ts b/packages/browser/src/posthog-core.ts index 7e23d1e4ad..a876dc5a4d 100644 --- a/packages/browser/src/posthog-core.ts +++ b/packages/browser/src/posthog-core.ts @@ -202,6 +202,7 @@ export const defaultConfig = (defaults?: ConfigDefaults): PostHogConfig => ({ advanced_enable_surveys: false, advanced_disable_toolbar_metrics: false, feature_flag_request_timeout_ms: 3000, + realtime_flags: false, surveys_request_timeout_ms: SURVEYS_REQUEST_TIMEOUT_MS, on_request_error: (res) => { const error = 'Bad HTTP status: ' + res.statusCode + ' ' + res.text diff --git a/packages/browser/src/posthog-featureflags.ts b/packages/browser/src/posthog-featureflags.ts index e2249dacf9..20ee3ec72c 100644 --- a/packages/browser/src/posthog-featureflags.ts +++ b/packages/browser/src/posthog-featureflags.ts @@ -163,6 +163,8 @@ export class PostHogFeatureFlags { private _reloadDebouncer?: any private _flagsCalled: boolean = false private _flagsLoadedFromRemote: boolean = false + private _eventSource?: EventSource + private _sseConnected: boolean = false constructor(private _instance: PostHog) { this.featureFlagEventHandlers = [] @@ -662,6 +664,83 @@ export class PostHogFeatureFlags { const currentFlagDetails = this.getFlagsWithDetails() parseFlagsResponse(response, this._instance.persistence, currentFlags, currentFlagPayloads, currentFlagDetails) this._fireFeatureFlagsCallbacks(errorsLoading) + + // Set up SSE connection after initial flags are loaded successfully + if (this._instance.config.realtime_flags && !errorsLoading && !this._sseConnected) { + this._setupSSEConnection() + } + } + + private _setupSSEConnection(): void { + if (this._eventSource || this._sseConnected) { + // Already connected or connecting + return + } + + if (this._instance._shouldDisableFlags() || this._instance.config.advanced_disable_feature_flags) { + return + } + + const token = this._instance.config.token + const distinctId = this._instance.get_distinct_id() + + if (!token || !distinctId) { + logger.warn('Cannot set up SSE connection without token and distinct_id') + return + } + + const url = this._instance.requestRouter.endpointFor( + 'api', + `/flags/stream?token=${encodeURIComponent(token)}&distinct_id=${encodeURIComponent(distinctId)}` + ) + + logger.info(url) + + try { + this._eventSource = new EventSource(url) + this._eventSource.onopen = () => { + this._sseConnected = true + logger.info('SSE connection established for real-time feature flags') + } + + this._eventSource.onmessage = (event) => { + try { + const flags = JSON.parse(event.data) + if (flags && typeof flags === 'object') { + // Update flags cache and dispatch callbacks + this.receivedFeatureFlags(flags) + } + } catch (error) { + logger.error('Error parsing SSE message:', error) + } + } + + this._eventSource.onerror = (error) => { + logger.error('SSE connection error:', error) + this._closeSSEConnection() + + // Attempt to reconnect after a delay + setTimeout(() => { + if (this._instance.config.realtime_flags && !this._sseConnected) { + this._setupSSEConnection() + } + }, 5000) + } + } catch (error) { + logger.error('Failed to establish SSE connection:', error) + } + } + + private _closeSSEConnection(): void { + if (this._eventSource) { + this._eventSource.close() + this._eventSource = undefined + } + this._sseConnected = false + } + + shutdown(): void { + this._closeSSEConnection() } /** diff --git a/packages/browser/src/types.ts b/packages/browser/src/types.ts index 497f1f0a21..23062f5539 100644 --- a/packages/browser/src/types.ts +++ b/packages/browser/src/types.ts @@ -816,6 +816,14 @@ export interface PostHogConfig { */ feature_flag_request_timeout_ms: number + /** + * Determines whether PostHog should listen to real-time feature flag updates via Server-Sent Events (SSE). + * When enabled, the SDK will maintain a persistent connection to receive flag updates without polling. + * + * @default false + */ + realtime_flags: boolean + /** * Sets timeout for fetching surveys * From 7d86dfd125804415b27fa2885eefc8bdb9b83226 Mon Sep 17 00:00:00 2001 From: "Gustavo H. Strassburger" Date: Tue, 18 Nov 2025 17:45:44 -0300 Subject: [PATCH 2/2] feat: Add real-time feature flags (SSE) support to Node SDK --- packages/browser/src/posthog-core.ts | 1 - packages/browser/src/posthog-featureflags.ts | 79 ------ packages/browser/src/types.ts | 8 - packages/node/package.json | 4 +- packages/node/src/client.ts | 1 + .../extensions/feature-flags/feature-flags.ts | 96 +++++++ packages/node/src/types.ts | 10 + pnpm-lock.yaml | 245 ++++-------------- 8 files changed, 154 insertions(+), 290 deletions(-) diff --git a/packages/browser/src/posthog-core.ts b/packages/browser/src/posthog-core.ts index a876dc5a4d..7e23d1e4ad 100644 --- a/packages/browser/src/posthog-core.ts +++ b/packages/browser/src/posthog-core.ts @@ -202,7 +202,6 @@ export const defaultConfig = (defaults?: ConfigDefaults): PostHogConfig => ({ advanced_enable_surveys: false, advanced_disable_toolbar_metrics: false, feature_flag_request_timeout_ms: 3000, - realtime_flags: false, surveys_request_timeout_ms: SURVEYS_REQUEST_TIMEOUT_MS, on_request_error: (res) => { const error = 'Bad HTTP status: ' + res.statusCode + ' ' + res.text diff --git a/packages/browser/src/posthog-featureflags.ts b/packages/browser/src/posthog-featureflags.ts index 20ee3ec72c..e2249dacf9 100644 --- a/packages/browser/src/posthog-featureflags.ts +++ b/packages/browser/src/posthog-featureflags.ts @@ -163,8 +163,6 @@ export class PostHogFeatureFlags { private _reloadDebouncer?: any private _flagsCalled: boolean = false private _flagsLoadedFromRemote: boolean = false - private _eventSource?: EventSource - private _sseConnected: boolean = false constructor(private _instance: PostHog) { this.featureFlagEventHandlers = [] @@ -664,83 +662,6 @@ export class PostHogFeatureFlags { const currentFlagDetails = this.getFlagsWithDetails() parseFlagsResponse(response, this._instance.persistence, currentFlags, currentFlagPayloads, currentFlagDetails) this._fireFeatureFlagsCallbacks(errorsLoading) - - // Set up SSE connection after initial flags are loaded successfully - if (this._instance.config.realtime_flags && !errorsLoading && !this._sseConnected) { - this._setupSSEConnection() - } - } - - private _setupSSEConnection(): void { - if (this._eventSource || this._sseConnected) { - // Already connected or connecting - return - } - - if (this._instance._shouldDisableFlags() || this._instance.config.advanced_disable_feature_flags) { - return - } - - const token = this._instance.config.token - const distinctId = this._instance.get_distinct_id() - - if (!token || !distinctId) { - logger.warn('Cannot set up SSE connection without token and distinct_id') - return - } - - const url = this._instance.requestRouter.endpointFor( - 'api', - `/flags/stream?token=${encodeURIComponent(token)}&distinct_id=${encodeURIComponent(distinctId)}` - ) - - logger.info(url) - - try { - this._eventSource = new EventSource(url) - this._eventSource.onopen = () => { - this._sseConnected = true - logger.info('SSE connection established for real-time feature flags') - } - - this._eventSource.onmessage = (event) => { - try { - const flags = JSON.parse(event.data) - if (flags && typeof flags === 'object') { - // Update flags cache and dispatch callbacks - this.receivedFeatureFlags(flags) - } - } catch (error) { - logger.error('Error parsing SSE message:', error) - } - } - - this._eventSource.onerror = (error) => { - logger.error('SSE connection error:', error) - this._closeSSEConnection() - - // Attempt to reconnect after a delay - setTimeout(() => { - if (this._instance.config.realtime_flags && !this._sseConnected) { - this._setupSSEConnection() - } - }, 5000) - } - } catch (error) { - logger.error('Failed to establish SSE connection:', error) - } - } - - private _closeSSEConnection(): void { - if (this._eventSource) { - this._eventSource.close() - this._eventSource = undefined - } - this._sseConnected = false - } - - shutdown(): void { - this._closeSSEConnection() } /** diff --git a/packages/browser/src/types.ts b/packages/browser/src/types.ts index 23062f5539..497f1f0a21 100644 --- a/packages/browser/src/types.ts +++ b/packages/browser/src/types.ts @@ -816,14 +816,6 @@ export interface PostHogConfig { */ feature_flag_request_timeout_ms: number - /** - * Determines whether PostHog should listen to real-time feature flag updates via Server-Sent Events (SSE). - * When enabled, the SDK will maintain a persistent connection to receive flag updates without polling. - * - * @default false - */ - realtime_flags: boolean - /** * Sets timeout for fetching surveys * diff --git a/packages/node/package.json b/packages/node/package.json index 5d967e0151..d3c489db94 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -37,10 +37,12 @@ "module": "dist/entrypoints/index.node.mjs", "types": "dist/entrypoints/index.node.d.ts", "dependencies": { - "@posthog/core": "workspace:*" + "@posthog/core": "workspace:*", + "eventsource": "^2.0.2" }, "devDependencies": { "@types/node": "^20.0.0", + "@types/eventsource": "^1.1.15", "@posthog-tooling/tsconfig-base": "workspace:*", "jest": "catalog:", "@rslib/core": "catalog:" diff --git a/packages/node/src/client.ts b/packages/node/src/client.ts index 51c95eb9fc..fa9cd4d111 100644 --- a/packages/node/src/client.ts +++ b/packages/node/src/client.ts @@ -111,6 +111,7 @@ export abstract class PostHogBackendClient extends PostHogCoreStateless implemen this._events.emit('localEvaluationFlagsLoaded', count) }, customHeaders: this.getCustomHeaders(), + realtimeFlags: options.realtimeFlags ?? false, }) } } diff --git a/packages/node/src/extensions/feature-flags/feature-flags.ts b/packages/node/src/extensions/feature-flags/feature-flags.ts index 77f735bd84..f65ce25167 100644 --- a/packages/node/src/extensions/feature-flags/feature-flags.ts +++ b/packages/node/src/extensions/feature-flags/feature-flags.ts @@ -2,6 +2,7 @@ import { FeatureFlagCondition, FlagProperty, FlagPropertyValue, PostHogFeatureFl import type { FeatureFlagValue, JsonType, PostHogFetchOptions, PostHogFetchResponse } from '@posthog/core' import { safeSetTimeout } from '@posthog/core' import { hashSHA1 } from './crypto' +import EventSource from 'eventsource' const SIXTY_SECONDS = 60 * 1000 @@ -53,6 +54,7 @@ type FeatureFlagsPollerOptions = { onError?: (error: Error) => void onLoad?: (count: number) => void customHeaders?: { [key: string]: string } + realtimeFlags?: boolean } class FeatureFlagsPoller { @@ -74,6 +76,9 @@ class FeatureFlagsPoller { shouldBeginExponentialBackoff: boolean = false backOffCount: number = 0 onLoad?: (count: number) => void + eventSource?: EventSource + sseConnected: boolean = false + realtimeFlags: boolean = false constructor({ pollingInterval, @@ -98,6 +103,7 @@ class FeatureFlagsPoller { this.fetch = options.fetch || fetch this.onError = options.onError this.customHeaders = customHeaders + this.realtimeFlags = options.realtimeFlags ?? false this.onLoad = options.onLoad void this.loadFeatureFlags() } @@ -648,6 +654,11 @@ class FeatureFlagsPoller { this.shouldBeginExponentialBackoff = false this.backOffCount = 0 this.onLoad?.(this.featureFlags.length) + + // Set up SSE connection after initial flags are loaded successfully + if (this.realtimeFlags && !this.sseConnected) { + this._setupSSEConnection() + } break } @@ -696,8 +707,93 @@ class FeatureFlagsPoller { } } + private _setupSSEConnection(): void { + if (this.eventSource || this.sseConnected) { + // Already connected or connecting + return + } + + if (!this.realtimeFlags) { + return + } + + const token = this.projectApiKey + const url = `${this.host}/flags/definitions/stream?token=${encodeURIComponent(token)}` + + try { + const eventSourceOptions: Record = { + headers: { + ...this.customHeaders, + Authorization: `Bearer ${this.personalApiKey}`, + }, + } + + this.eventSource = new EventSource(url, eventSourceOptions) + + this.eventSource.onopen = () => { + this.sseConnected = true + } + + this.eventSource.onmessage = (event) => { + try { + const flagData = JSON.parse(event.data) + if (flagData && typeof flagData === 'object') { + // Update flags from SSE message + this._processFlagUpdate(flagData) + } + } catch (error) { + this.onError?.(new Error(`Error parsing SSE message: ${error}`)) + } + } + + this.eventSource.onerror = (error) => { + this.onError?.(new Error(`SSE connection error: ${error}`)) + this._closeSSEConnection() + + // Attempt to reconnect after a delay + setTimeout(() => { + if (this.realtimeFlags && !this.sseConnected) { + this._setupSSEConnection() + } + }, 5000) + } + } catch (error) { + this.onError?.(new Error(`Failed to establish SSE connection: ${error}`)) + } + } + + private _closeSSEConnection(): void { + if (this.eventSource) { + this.eventSource.close() + this.eventSource = undefined + } + this.sseConnected = false + } + + private _processFlagUpdate(flagData: { [key: string]: any }): void { + const flag = flagData as PostHogFeatureFlag + + if (flag.deleted) { + // Remove the flag + delete this.featureFlagsByKey[flag.key] + this.featureFlags = this.featureFlags.filter((f) => f.key !== flag.key) + } else { + // Update or add the flag + this.featureFlagsByKey[flag.key] = flag + + // Update in the array + const existingIndex = this.featureFlags.findIndex((f) => f.key === flag.key) + if (existingIndex >= 0) { + this.featureFlags[existingIndex] = flag + } else { + this.featureFlags.push(flag) + } + } + } + stopPoller(): void { clearTimeout(this.poller) + this._closeSSEConnection() } } diff --git a/packages/node/src/types.ts b/packages/node/src/types.ts index cbe1ef7cd6..8888c2ba19 100644 --- a/packages/node/src/types.ts +++ b/packages/node/src/types.ts @@ -89,6 +89,16 @@ export type PostHogOptions = PostHogCoreOptions & { * @default undefined */ evaluationEnvironments?: readonly string[] + /** + * Enable real-time feature flag updates via Server-Sent Events (SSE). + * When enabled, the SDK will establish a persistent connection to receive + * flag updates in real-time instead of relying solely on polling. + * + * Requires personalApiKey to be set for authentication. + * + * @default false + */ + realtimeFlags?: boolean } export type PostHogFeatureFlag = { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 64cf76bd89..ec67673a26 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -432,19 +432,22 @@ importers: version: 7.7.0 jest: specifier: 'catalog:' - version: 29.7.0(@types/node@22.16.5)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.16.5)(typescript@5.8.2)) + version: 29.7.0(@types/node@22.16.5)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.17.0)(typescript@5.8.2)) next: specifier: ^15.4.1 version: 15.4.6(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(react-dom@18.3.1(react@18.2.0))(react@18.2.0) ts-jest: specifier: 'catalog:' - version: 29.4.0(@babel/core@7.28.4)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.4))(jest-util@29.7.0)(jest@29.7.0(@types/node@22.16.5)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.16.5)(typescript@5.8.2)))(typescript@5.8.2) + version: 29.4.0(@babel/core@7.28.4)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.4))(jest-util@29.7.0)(jest@29.7.0(@types/node@22.16.5)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.17.0)(typescript@5.8.2)))(typescript@5.8.2) packages/node: dependencies: '@posthog/core': specifier: workspace:* version: link:../core + eventsource: + specifier: ^2.0.2 + version: 2.0.2 devDependencies: '@posthog-tooling/tsconfig-base': specifier: workspace:* @@ -452,12 +455,15 @@ importers: '@rslib/core': specifier: 'catalog:' version: 0.10.6(@microsoft/api-extractor@7.52.8(@types/node@20.19.9))(typescript@5.8.2) + '@types/eventsource': + specifier: ^1.1.15 + version: 1.1.15 '@types/node': specifier: ^20.0.0 version: 20.19.9 jest: specifier: 'catalog:' - version: 29.7.0(@types/node@20.19.9)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.2)) + version: 29.7.0(@types/node@20.19.9)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.17.0)(typescript@5.8.2)) packages/nuxt: dependencies: @@ -4010,6 +4016,9 @@ packages: '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/eventsource@1.1.15': + resolution: {integrity: sha512-XQmGcbnxUNa06HR3VBVkc9+A2Vpi9ZyLJcdS5dwaQQ/4ZMWFO+5c90FnMUpbtMZwB/FChoYHwuVg8TvkECacTA==} + '@types/fs-extra@8.1.5': resolution: {integrity: sha512-0dzKcwO+S8s2kuF5Z9oUWatQJj5Uq/iqphEtE3GQJVRRYm/tD1LglU2UnXi2A8jLq5umkGouOXOR9y0n613ZwQ==} @@ -6607,6 +6616,10 @@ packages: resolution: {integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==} engines: {node: '>=18.0.0'} + eventsource@2.0.2: + resolution: {integrity: sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==} + engines: {node: '>=12.0.0'} + exec-async@2.2.0: resolution: {integrity: sha512-87OpwcEiMia/DeiKFzaQNBNFeN3XkkpYIh9FyOqq5mS2oKv3CBE67PXoEKcr6nodWdXNogTiQ0jE2NGuoffXPw==} @@ -15316,80 +15329,6 @@ snapshots: jest-util: 29.7.0 slash: 3.0.0 - '@jest/core@29.7.0(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.2))': - dependencies: - '@jest/console': 29.7.0 - '@jest/reporters': 29.7.0(node-notifier@8.0.2) - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 22.17.0 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - ci-info: 3.9.0 - exit: 0.1.2 - graceful-fs: 4.2.9 - jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@22.17.0)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.2)) - jest-haste-map: 29.7.0 - jest-message-util: 29.7.0 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-resolve-dependencies: 29.7.0 - jest-runner: 29.7.0 - jest-runtime: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - jest-watcher: 29.7.0 - micromatch: 4.0.8 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-ansi: 6.0.1 - optionalDependencies: - node-notifier: 8.0.2 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - ts-node - - '@jest/core@29.7.0(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.16.5)(typescript@5.8.2))': - dependencies: - '@jest/console': 29.7.0 - '@jest/reporters': 29.7.0(node-notifier@8.0.2) - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 22.17.0 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - ci-info: 3.9.0 - exit: 0.1.2 - graceful-fs: 4.2.9 - jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@22.17.0)(ts-node@10.9.2(@types/node@22.16.5)(typescript@5.8.2)) - jest-haste-map: 29.7.0 - jest-message-util: 29.7.0 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-resolve-dependencies: 29.7.0 - jest-runner: 29.7.0 - jest-runtime: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - jest-watcher: 29.7.0 - micromatch: 4.0.8 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-ansi: 6.0.1 - optionalDependencies: - node-notifier: 8.0.2 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - ts-node - '@jest/core@29.7.0(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.17.0)(typescript@5.8.2))': dependencies: '@jest/console': 29.7.0 @@ -17796,6 +17735,8 @@ snapshots: '@types/estree@1.0.8': {} + '@types/eventsource@1.1.15': {} + '@types/fs-extra@8.1.5': dependencies: '@types/node': 22.17.0 @@ -19699,13 +19640,13 @@ snapshots: crc-32: 1.2.2 readable-stream: 4.7.0 - create-jest@29.7.0(@types/node@20.19.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.2)): + create-jest@29.7.0(@types/node@20.19.9)(ts-node@10.9.2(@types/node@22.17.0)(typescript@5.8.2)): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.9 - jest-config: 29.7.0(@types/node@20.19.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.2)) + jest-config: 29.7.0(@types/node@20.19.9)(ts-node@10.9.2(@types/node@22.17.0)(typescript@5.8.2)) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -19714,13 +19655,13 @@ snapshots: - supports-color - ts-node - create-jest@29.7.0(@types/node@22.16.5)(ts-node@10.9.2(@types/node@22.16.5)(typescript@5.8.2)): + create-jest@29.7.0(@types/node@22.16.5)(ts-node@10.9.2(@types/node@22.17.0)(typescript@5.8.2)): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.9 - jest-config: 29.7.0(@types/node@22.16.5)(ts-node@10.9.2(@types/node@22.16.5)(typescript@5.8.2)) + jest-config: 29.7.0(@types/node@22.16.5)(ts-node@10.9.2(@types/node@22.17.0)(typescript@5.8.2)) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -20955,6 +20896,8 @@ snapshots: eventsource-parser@3.0.6: {} + eventsource@2.0.2: {} + exec-async@2.2.0: {} exec-sh@0.3.4: {} @@ -22745,16 +22688,16 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@29.7.0(@types/node@20.19.9)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.2)): + jest-cli@29.7.0(@types/node@20.19.9)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.17.0)(typescript@5.8.2)): dependencies: - '@jest/core': 29.7.0(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.2)) + '@jest/core': 29.7.0(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.17.0)(typescript@5.8.2)) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@20.19.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.2)) + create-jest: 29.7.0(@types/node@20.19.9)(ts-node@10.9.2(@types/node@22.17.0)(typescript@5.8.2)) exit: 0.1.2 import-local: 3.0.2 - jest-config: 29.7.0(@types/node@20.19.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.2)) + jest-config: 29.7.0(@types/node@20.19.9)(ts-node@10.9.2(@types/node@22.17.0)(typescript@5.8.2)) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -22766,16 +22709,16 @@ snapshots: - supports-color - ts-node - jest-cli@29.7.0(@types/node@22.16.5)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.16.5)(typescript@5.8.2)): + jest-cli@29.7.0(@types/node@22.16.5)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.17.0)(typescript@5.8.2)): dependencies: - '@jest/core': 29.7.0(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.16.5)(typescript@5.8.2)) + '@jest/core': 29.7.0(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.17.0)(typescript@5.8.2)) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@22.16.5)(ts-node@10.9.2(@types/node@22.16.5)(typescript@5.8.2)) + create-jest: 29.7.0(@types/node@22.16.5)(ts-node@10.9.2(@types/node@22.17.0)(typescript@5.8.2)) exit: 0.1.2 import-local: 3.0.2 - jest-config: 29.7.0(@types/node@22.16.5)(ts-node@10.9.2(@types/node@22.16.5)(typescript@5.8.2)) + jest-config: 29.7.0(@types/node@22.16.5)(ts-node@10.9.2(@types/node@22.17.0)(typescript@5.8.2)) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -22829,7 +22772,7 @@ snapshots: - supports-color - ts-node - jest-config@29.7.0(@types/node@20.19.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.2)): + jest-config@29.7.0(@types/node@20.19.9)(ts-node@10.9.2(@types/node@22.17.0)(typescript@5.8.2)): dependencies: '@babel/core': 7.27.1 '@jest/test-sequencer': 29.7.0 @@ -22855,12 +22798,12 @@ snapshots: strip-json-comments: 3.1.1 optionalDependencies: '@types/node': 20.19.9 - ts-node: 10.9.2(@types/node@20.19.9)(typescript@5.8.2) + ts-node: 10.9.2(@types/node@22.17.0)(typescript@5.8.2) transitivePeerDependencies: - babel-plugin-macros - supports-color - jest-config@29.7.0(@types/node@22.16.5)(ts-node@10.9.2(@types/node@22.16.5)(typescript@5.8.2)): + jest-config@29.7.0(@types/node@22.16.5)(ts-node@10.9.2(@types/node@22.17.0)(typescript@5.8.2)): dependencies: '@babel/core': 7.27.1 '@jest/test-sequencer': 29.7.0 @@ -22886,69 +22829,7 @@ snapshots: strip-json-comments: 3.1.1 optionalDependencies: '@types/node': 22.16.5 - ts-node: 10.9.2(@types/node@22.16.5)(typescript@5.8.2) - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - jest-config@29.7.0(@types/node@22.17.0)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.2)): - dependencies: - '@babel/core': 7.27.1 - '@jest/test-sequencer': 29.7.0 - '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.27.1) - chalk: 4.1.2 - ci-info: 3.9.0 - deepmerge: 4.2.2 - glob: 7.2.3 - graceful-fs: 4.2.9 - jest-circus: 29.7.0 - jest-environment-node: 29.7.0 - jest-get-type: 29.6.3 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-runner: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - micromatch: 4.0.8 - parse-json: 5.2.0 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - optionalDependencies: - '@types/node': 22.17.0 - ts-node: 10.9.2(@types/node@20.19.9)(typescript@5.8.2) - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - jest-config@29.7.0(@types/node@22.17.0)(ts-node@10.9.2(@types/node@22.16.5)(typescript@5.8.2)): - dependencies: - '@babel/core': 7.27.1 - '@jest/test-sequencer': 29.7.0 - '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.27.1) - chalk: 4.1.2 - ci-info: 3.9.0 - deepmerge: 4.2.2 - glob: 7.2.3 - graceful-fs: 4.2.9 - jest-circus: 29.7.0 - jest-environment-node: 29.7.0 - jest-get-type: 29.6.3 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-runner: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - micromatch: 4.0.8 - parse-json: 5.2.0 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - optionalDependencies: - '@types/node': 22.17.0 - ts-node: 10.9.2(@types/node@22.16.5)(typescript@5.8.2) + ts-node: 10.9.2(@types/node@22.17.0)(typescript@5.8.2) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -23461,12 +23342,12 @@ snapshots: merge-stream: 2.0.0 supports-color: 8.1.1 - jest@29.7.0(@types/node@20.19.9)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.2)): + jest@29.7.0(@types/node@20.19.9)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.17.0)(typescript@5.8.2)): dependencies: - '@jest/core': 29.7.0(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.2)) + '@jest/core': 29.7.0(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.17.0)(typescript@5.8.2)) '@jest/types': 29.6.3 import-local: 3.0.2 - jest-cli: 29.7.0(@types/node@20.19.9)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.2)) + jest-cli: 29.7.0(@types/node@20.19.9)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.17.0)(typescript@5.8.2)) optionalDependencies: node-notifier: 8.0.2 transitivePeerDependencies: @@ -23475,12 +23356,12 @@ snapshots: - supports-color - ts-node - jest@29.7.0(@types/node@22.16.5)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.16.5)(typescript@5.8.2)): + jest@29.7.0(@types/node@22.16.5)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.17.0)(typescript@5.8.2)): dependencies: - '@jest/core': 29.7.0(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.16.5)(typescript@5.8.2)) + '@jest/core': 29.7.0(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.17.0)(typescript@5.8.2)) '@jest/types': 29.6.3 import-local: 3.0.2 - jest-cli: 29.7.0(@types/node@22.16.5)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.16.5)(typescript@5.8.2)) + jest-cli: 29.7.0(@types/node@22.16.5)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.17.0)(typescript@5.8.2)) optionalDependencies: node-notifier: 8.0.2 transitivePeerDependencies: @@ -28228,12 +28109,12 @@ snapshots: babel-jest: 29.7.0(@babel/core@7.27.1) jest-util: 29.7.0 - ts-jest@29.4.0(@babel/core@7.28.4)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.4))(jest-util@29.7.0)(jest@29.7.0(@types/node@22.16.5)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.16.5)(typescript@5.8.2)))(typescript@5.8.2): + ts-jest@29.4.0(@babel/core@7.28.4)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.4))(jest-util@29.7.0)(jest@29.7.0(@types/node@22.16.5)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.17.0)(typescript@5.8.2)))(typescript@5.8.2): dependencies: bs-logger: 0.2.6 ejs: 3.1.10 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@22.16.5)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.16.5)(typescript@5.8.2)) + jest: 29.7.0(@types/node@22.16.5)(node-notifier@8.0.2)(ts-node@10.9.2(@types/node@22.17.0)(typescript@5.8.2)) json5: 2.2.3 lodash.memoize: 4.1.2 make-error: 1.3.6 @@ -28248,44 +28129,6 @@ snapshots: babel-jest: 29.7.0(@babel/core@7.28.4) jest-util: 29.7.0 - ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.2): - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.11 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.4 - '@types/node': 20.19.9 - acorn: 8.11.3 - acorn-walk: 8.3.3 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.2 - make-error: 1.3.6 - typescript: 5.8.2 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - optional: true - - ts-node@10.9.2(@types/node@22.16.5)(typescript@5.8.2): - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.11 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.4 - '@types/node': 22.16.5 - acorn: 8.11.3 - acorn-walk: 8.3.3 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.2 - make-error: 1.3.6 - typescript: 5.8.2 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - optional: true - ts-node@10.9.2(@types/node@22.17.0)(typescript@5.8.2): dependencies: '@cspotcode/source-map-support': 0.8.1