diff --git a/packages/world/package.json b/packages/world/package.json index 2862d98..e59cbdf 100644 --- a/packages/world/package.json +++ b/packages/world/package.json @@ -23,6 +23,7 @@ "dist" ], "dependencies": { + "@platformatic/globals": "^3.57.0", "cbor-x": "1.6.4", "undici": "^8.0.0" }, diff --git a/packages/world/src/index.ts b/packages/world/src/index.ts index 76aa128..25ab9c6 100644 --- a/packages/world/src/index.ts +++ b/packages/world/src/index.ts @@ -1,6 +1,6 @@ import { readFileSync } from 'node:fs' import { join } from 'node:path' -import { Agent } from 'undici' +import { getSharedContext } from '@platformatic/globals' import type { World } from '@workflow/world' import { SPEC_VERSION_SUPPORTS_CBOR_QUEUE_TRANSPORT } from '@workflow/world' import { HttpClient } from './lib/client.ts' @@ -56,6 +56,21 @@ export interface CreateWorldOptions { deploymentVersion: string } +// Read the deployment version from the watt runtime shared context, if something +// pushed one there. getSharedContext returns { get, update } inside a runtime, or +// undefined off-runtime with throwOnMissing:false -- a safe no-op standalone. +async function versionFromSharedContext (): Promise { + try { + const shared = getSharedContext({ throwOnMissing: false }) as + { get?: () => unknown } | undefined + if (!shared?.get) return undefined + const ctx = await shared.get() as { deploymentVersion?: string } | undefined + return ctx?.deploymentVersion + } catch { + return undefined + } +} + function saPath (file: string): string { const base = process.env.PLT_WORLD_SA_PATH || '/var/run/secrets/kubernetes.io/serviceaccount' return `${base}/${file}` @@ -70,31 +85,6 @@ function isRunningInK8s (): boolean { } } -async function readVersionFromK8sApi (): Promise { - try { - const token = readFileSync(saPath('token'), 'utf8').trim() - const namespace = readFileSync(saPath('namespace'), 'utf8').trim() - const podName = process.env.HOSTNAME - if (!podName) return undefined - - const ca = readFileSync(saPath('ca.crt')) - const dispatcher = new Agent({ connect: { ca } }) - const res = await fetch( - `https://kubernetes.default.svc/api/v1/namespaces/${namespace}/pods/${podName}`, - { - headers: { Authorization: `Bearer ${token}` }, - dispatcher, - } as RequestInit - ) - - if (!res.ok) return undefined - const pod = await res.json() as { metadata?: { labels?: Record } } - return pod?.metadata?.labels?.['plt.dev/version'] || undefined - } catch { - return undefined - } -} - function readAppName (): string { try { const pkg = JSON.parse(readFileSync(join(process.cwd(), 'package.json'), 'utf8')) @@ -111,19 +101,34 @@ export function createWorld (options?: Partial): World { } const appId = options?.appId || process.env.PLT_WORLD_APP_ID || readAppName() - const explicitVersion = options?.deploymentVersion || process.env.PLT_WORLD_DEPLOYMENT_VERSION - const config = { serviceUrl, appId, deploymentVersion: explicitVersion || 'local' } - const world = createPlatformaticWorld(config) + const explicitVersion = options?.deploymentVersion || + process.env.PLT_WORLD_DEPLOYMENT_VERSION || + process.env.PLT_DEPLOYMENT_VERSION + // Version comes from the environment first: options, PLT_WORLD_DEPLOYMENT_VERSION, + // or PLT_DEPLOYMENT_VERSION. No K8s API read. + const config: PlatformaticWorldConfig = { + serviceUrl, + appId, + deploymentVersion: explicitVersion || 'local', + // In K8s ICC assigns the version, so a 'local' stamp means "not resolved yet" and + // must not be used to enqueue (see queue.ts). Standalone/local dev keeps 'local'. + requireResolvedVersion: isRunningInK8s(), + } - if (!explicitVersion && isRunningInK8s()) { - const originalStart = world.start! - world.start = async function () { - const version = await readVersionFromK8sApi() + // No explicit version: start at 'local'. When running inside a watt runtime, the + // version can be provided later via the shared context, so refresh before each + // queue read and latch it -- the pod starts stamping the real version with no + // restart. Off-runtime (standalone) this is a no-op. + if (!explicitVersion) { + config.refreshDeploymentVersion = async () => { + if (config.deploymentVersion !== 'local') return + const version = await versionFromSharedContext() if (version) config.deploymentVersion = version - return originalStart.call(this) } } + const world = createPlatformaticWorld(config) + return world } diff --git a/packages/world/src/lib/queue.ts b/packages/world/src/lib/queue.ts index 13379ea..fac58fc 100644 --- a/packages/world/src/lib/queue.ts +++ b/packages/world/src/lib/queue.ts @@ -5,6 +5,13 @@ import { decode } from 'cbor-x' export interface QueueConfig { deploymentVersion: string + // Optional late resolver: when the version was not known at startup + refreshDeploymentVersion?: () => Promise + // When true (K8s, where ICC assigns the version), 'local' means "not resolved yet", + // so enqueuing is refused instead of stamping 'local' -- otherwise the run pins to + // 'local' and its messages never route to the ICC-registered (real-version) + // handlers. In standalone/local dev this is false and 'local' is a valid version. + requireResolvedVersion?: boolean } interface EnqueueEnvelope { @@ -28,10 +35,19 @@ async function readRequestBody (req: Request): Promise { export function createQueue (client: HttpClient, config: QueueConfig) { const queue = async (queueName: ValidQueueName, message: QueuePayload, opts?: QueueOptions): Promise<{ messageId: MessageId | null }> => { + // Pick up a version that arrived after startup (ICC recovery) before stamping. + await config.refreshDeploymentVersion?.() + const deploymentId = opts?.deploymentId ?? config.deploymentVersion + // In K8s the version is assigned by ICC, so 'local' means "not resolved yet". + // Refuse rather than pin the run to 'local' (its messages would never route to + // the real-version handlers). The caller retries until the version lands. + if (config.requireResolvedVersion && deploymentId === 'local') { + throw new Error('World deployment version not resolved yet (ICC unreachable); retry') + } const envelope: EnqueueEnvelope = { queueName, message, - deploymentId: opts?.deploymentId ?? config.deploymentVersion, + deploymentId, idempotencyKey: opts?.idempotencyKey, delaySeconds: opts?.delaySeconds, } @@ -80,6 +96,7 @@ export function createQueue (client: HttpClient, config: QueueConfig) { } const getDeploymentId = async (): Promise => { + await config.refreshDeploymentVersion?.() return config.deploymentVersion } diff --git a/packages/world/test/world.test.ts b/packages/world/test/world.test.ts index 4185c4e..bd3b152 100644 --- a/packages/world/test/world.test.ts +++ b/packages/world/test/world.test.ts @@ -67,9 +67,11 @@ test('reads from env vars', async () => { test('defaults deploymentVersion to local', async () => { const originalUrl = process.env.PLT_WORLD_SERVICE_URL const originalVersion = process.env.PLT_WORLD_DEPLOYMENT_VERSION + const originalGeneralVersion = process.env.PLT_DEPLOYMENT_VERSION process.env.PLT_WORLD_SERVICE_URL = 'http://localhost:9999' delete process.env.PLT_WORLD_DEPLOYMENT_VERSION + delete process.env.PLT_DEPLOYMENT_VERSION try { const world = createWorld() @@ -81,6 +83,113 @@ test('defaults deploymentVersion to local', async () => { else delete process.env.PLT_WORLD_SERVICE_URL if (originalVersion) process.env.PLT_WORLD_DEPLOYMENT_VERSION = originalVersion else delete process.env.PLT_WORLD_DEPLOYMENT_VERSION + if (originalGeneralVersion) process.env.PLT_DEPLOYMENT_VERSION = originalGeneralVersion + else delete process.env.PLT_DEPLOYMENT_VERSION + } +}) + +test('falls back to PLT_DEPLOYMENT_VERSION when PLT_WORLD_DEPLOYMENT_VERSION is unset', async () => { + const originalUrl = process.env.PLT_WORLD_SERVICE_URL + const originalWorldVersion = process.env.PLT_WORLD_DEPLOYMENT_VERSION + const originalVersion = process.env.PLT_DEPLOYMENT_VERSION + + process.env.PLT_WORLD_SERVICE_URL = 'http://localhost:9999' + delete process.env.PLT_WORLD_DEPLOYMENT_VERSION + process.env.PLT_DEPLOYMENT_VERSION = 'img-1.4.2' + + try { + const world = createWorld() + const deploymentId = await world.getDeploymentId() + assert.equal(deploymentId, 'img-1.4.2') + await world.close() + } finally { + if (originalUrl) process.env.PLT_WORLD_SERVICE_URL = originalUrl + else delete process.env.PLT_WORLD_SERVICE_URL + if (originalWorldVersion) process.env.PLT_WORLD_DEPLOYMENT_VERSION = originalWorldVersion + else delete process.env.PLT_WORLD_DEPLOYMENT_VERSION + if (originalVersion) process.env.PLT_DEPLOYMENT_VERSION = originalVersion + else delete process.env.PLT_DEPLOYMENT_VERSION + } +}) + +test('starts at local and picks up the version pushed to the shared context later', async () => { + const originalUrl = process.env.PLT_WORLD_SERVICE_URL + const originalWorldVersion = process.env.PLT_WORLD_DEPLOYMENT_VERSION + const originalVersion = process.env.PLT_DEPLOYMENT_VERSION + const originalGlobal = (globalThis as any).platformatic + + process.env.PLT_WORLD_SERVICE_URL = 'http://localhost:9999' + delete process.env.PLT_WORLD_DEPLOYMENT_VERSION + delete process.env.PLT_DEPLOYMENT_VERSION + + // Simulate the watt runtime shared context; starts with no version. + let sharedCtx: { deploymentVersion?: string } = {} + ;(globalThis as any).platformatic = { sharedContext: { get: () => sharedCtx } } + + try { + const world = createWorld() + // No env version and nothing in the shared context yet -> local. + assert.equal(await world.getDeploymentId(), 'local') + + // Version arrives later (e.g. watt-extra pushes it once ICC resolves it). + sharedCtx = { deploymentVersion: 'v9' } + assert.equal(await world.getDeploymentId(), 'v9') + + // Latched: once resolved it stops consulting the shared context. + sharedCtx = {} + assert.equal(await world.getDeploymentId(), 'v9') + + await world.close() + } finally { + if (originalUrl) process.env.PLT_WORLD_SERVICE_URL = originalUrl + else delete process.env.PLT_WORLD_SERVICE_URL + if (originalWorldVersion) process.env.PLT_WORLD_DEPLOYMENT_VERSION = originalWorldVersion + else delete process.env.PLT_WORLD_DEPLOYMENT_VERSION + if (originalVersion) process.env.PLT_DEPLOYMENT_VERSION = originalVersion + else delete process.env.PLT_DEPLOYMENT_VERSION + ;(globalThis as any).platformatic = originalGlobal + } +}) + +test('refuses to enqueue while the version is unresolved (local) in K8s', async () => { + const originalUrl = process.env.PLT_WORLD_SERVICE_URL + const originalSaPath = process.env.PLT_WORLD_SA_PATH + const originalWorldVersion = process.env.PLT_WORLD_DEPLOYMENT_VERSION + const originalVersion = process.env.PLT_DEPLOYMENT_VERSION + const originalGlobal = (globalThis as any).platformatic + + // Fake SA mount so isRunningInK8s() is true. + const fakeSaDir = join(tmpdir(), `plt-world-k8s-block-${process.pid}`) + mkdirSync(fakeSaDir, { recursive: true }) + writeFileSync(join(fakeSaDir, 'token'), 'fake-token') + + process.env.PLT_WORLD_SERVICE_URL = 'http://localhost:9999' + process.env.PLT_WORLD_SA_PATH = fakeSaDir + delete process.env.PLT_WORLD_DEPLOYMENT_VERSION + delete process.env.PLT_DEPLOYMENT_VERSION + ;(globalThis as any).platformatic = undefined // no shared context -> stays local + + try { + const world = createWorld() + assert.equal(await world.getDeploymentId(), 'local') + // Enqueuing is refused so the run is not pinned to 'local' (its messages would + // never route to the ICC-registered real-version handlers). Caller retries. + await assert.rejects( + () => world.queue('my-queue' as any, {} as any), + /not resolved/ + ) + await world.close() + } finally { + if (originalUrl) process.env.PLT_WORLD_SERVICE_URL = originalUrl + else delete process.env.PLT_WORLD_SERVICE_URL + if (originalSaPath) process.env.PLT_WORLD_SA_PATH = originalSaPath + else delete process.env.PLT_WORLD_SA_PATH + if (originalWorldVersion) process.env.PLT_WORLD_DEPLOYMENT_VERSION = originalWorldVersion + else delete process.env.PLT_WORLD_DEPLOYMENT_VERSION + if (originalVersion) process.env.PLT_DEPLOYMENT_VERSION = originalVersion + else delete process.env.PLT_DEPLOYMENT_VERSION + ;(globalThis as any).platformatic = originalGlobal + rmSync(fakeSaDir, { recursive: true, force: true }) } }) @@ -114,7 +223,7 @@ test('falls back to local when not in K8s and no env var', async () => { } }) -test('env var takes precedence over K8s API lookup', async () => { +test('reads the deployment version from PLT_WORLD_DEPLOYMENT_VERSION', async () => { const originalUrl = process.env.PLT_WORLD_SERVICE_URL const originalVersion = process.env.PLT_WORLD_DEPLOYMENT_VERSION diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5e85163..8a13fa4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,10 +10,10 @@ importers: devDependencies: eslint: specifier: ^9.39.3 - version: 9.39.4(jiti@2.7.0) + version: 9.39.4(jiti@2.7.0)(supports-color@8.1.1) neostandard: specifier: ^0.13.0 - version: 0.13.0(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) + version: 0.13.0(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) e2e-v4: dependencies: @@ -34,7 +34,7 @@ importers: version: 19.2.7(react@19.2.7) workflow: specifier: 4.5.0 - version: 4.5.0(@nestjs/common@11.1.27(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.27(@nestjs/common@11.1.27(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2))(@opentelemetry/api@1.9.1)(@swc/cli@0.8.1(@swc/core@1.15.3)(chokidar@5.0.0))(@swc/core@1.15.3)(next@16.2.10(@opentelemetry/api@1.9.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@6.0.3)(ws@8.21.0) + version: 4.5.0(@nestjs/common@11.1.27(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.27(@nestjs/common@11.1.27(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2))(@opentelemetry/api@1.9.1)(@swc/cli@0.8.1(@swc/core@1.15.3)(chokidar@5.0.0))(@swc/core@1.15.3)(next@16.2.10(@opentelemetry/api@1.9.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(supports-color@8.1.1)(typescript@6.0.3)(ws@8.21.0) devDependencies: '@types/node': specifier: ^24.0.0 @@ -159,6 +159,9 @@ importers: packages/world: dependencies: + '@platformatic/globals': + specifier: ^3.57.0 + version: 3.59.0 cbor-x: specifier: 1.6.4 version: 1.6.4 @@ -4842,14 +4845,14 @@ snapshots: '@esbuild/win32-x64@0.28.1': optional: true - '@eslint-community/eslint-utils@4.9.1(eslint@9.39.4(jiti@2.7.0))': + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1))': dependencies: - eslint: 9.39.4(jiti@2.7.0) + eslint: 9.39.4(jiti@2.7.0)(supports-color@8.1.1) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} - '@eslint/config-array@0.21.2': + '@eslint/config-array@0.21.2(supports-color@8.1.1)': dependencies: '@eslint/object-schema': 2.1.7 debug: 4.4.3(supports-color@8.1.1) @@ -4865,7 +4868,7 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 - '@eslint/eslintrc@3.3.5': + '@eslint/eslintrc@3.3.5(supports-color@8.1.1)': dependencies: ajv: 6.15.0 debug: 4.4.3(supports-color@8.1.1) @@ -5997,10 +6000,10 @@ snapshots: '@standard-schema/spec@1.0.0': {} - '@stylistic/eslint-plugin@2.11.0(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)': + '@stylistic/eslint-plugin@2.11.0(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1))(typescript@6.0.3)': dependencies: - '@typescript-eslint/utils': 8.62.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) - eslint: 9.39.4(jiti@2.7.0) + '@typescript-eslint/utils': 8.62.1(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1))(typescript@6.0.3) + eslint: 9.39.4(jiti@2.7.0)(supports-color@8.1.1) eslint-visitor-keys: 4.2.1 espree: 10.4.0 estraverse: 5.3.0 @@ -6125,15 +6128,15 @@ snapshots: dependencies: '@types/node': 24.13.2 - '@typescript-eslint/eslint-plugin@8.62.1(@typescript-eslint/parser@8.62.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)': + '@typescript-eslint/eslint-plugin@8.62.1(@typescript-eslint/parser@8.62.1(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.62.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/parser': 8.62.1(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) '@typescript-eslint/scope-manager': 8.62.1 - '@typescript-eslint/type-utils': 8.62.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) - '@typescript-eslint/utils': 8.62.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/type-utils': 8.62.1(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) + '@typescript-eslint/utils': 8.62.1(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1))(typescript@6.0.3) '@typescript-eslint/visitor-keys': 8.62.1 - eslint: 9.39.4(jiti@2.7.0) + eslint: 9.39.4(jiti@2.7.0)(supports-color@8.1.1) ignore: 7.0.5 natural-compare: 1.4.0 ts-api-utils: 2.5.0(typescript@6.0.3) @@ -6141,14 +6144,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.62.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)': + '@typescript-eslint/parser@8.62.1(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3)': dependencies: '@typescript-eslint/scope-manager': 8.62.1 '@typescript-eslint/types': 8.62.1 '@typescript-eslint/typescript-estree': 8.62.1(typescript@6.0.3) '@typescript-eslint/visitor-keys': 8.62.1 debug: 4.4.3(supports-color@8.1.1) - eslint: 9.39.4(jiti@2.7.0) + eslint: 9.39.4(jiti@2.7.0)(supports-color@8.1.1) typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -6171,13 +6174,13 @@ snapshots: dependencies: typescript: 6.0.3 - '@typescript-eslint/type-utils@8.62.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)': + '@typescript-eslint/type-utils@8.62.1(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3)': dependencies: '@typescript-eslint/types': 8.62.1 '@typescript-eslint/typescript-estree': 8.62.1(typescript@6.0.3) - '@typescript-eslint/utils': 8.62.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/utils': 8.62.1(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1))(typescript@6.0.3) debug: 4.4.3(supports-color@8.1.1) - eslint: 9.39.4(jiti@2.7.0) + eslint: 9.39.4(jiti@2.7.0)(supports-color@8.1.1) ts-api-utils: 2.5.0(typescript@6.0.3) typescript: 6.0.3 transitivePeerDependencies: @@ -6200,13 +6203,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.62.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)': + '@typescript-eslint/utils@8.62.1(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1))(typescript@6.0.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1)) '@typescript-eslint/scope-manager': 8.62.1 '@typescript-eslint/types': 8.62.1 '@typescript-eslint/typescript-estree': 8.62.1(typescript@6.0.3) - eslint: 9.39.4(jiti@2.7.0) + eslint: 9.39.4(jiti@2.7.0)(supports-color@8.1.1) typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -6380,7 +6383,7 @@ snapshots: - supports-color - ws - '@workflow/cli@4.2.10(@opentelemetry/api@1.9.1)(ws@8.21.0)': + '@workflow/cli@4.2.10(@opentelemetry/api@1.9.1)(supports-color@8.1.1)(ws@8.21.0)': dependencies: '@oclif/core': 4.11.4 '@oclif/plugin-help': 6.2.37 @@ -6391,7 +6394,7 @@ snapshots: '@workflow/errors': 4.1.4 '@workflow/swc-plugin': 4.1.1(@swc/core@1.15.3) '@workflow/utils': 4.1.3 - '@workflow/web': 4.1.11 + '@workflow/web': 4.1.11(supports-color@8.1.1) '@workflow/world': 4.2.0(zod@4.3.6) '@workflow/world-local': 4.2.0(@opentelemetry/api@1.9.1) '@workflow/world-vercel': 4.4.1(@opentelemetry/api@1.9.1) @@ -6590,7 +6593,7 @@ snapshots: '@workflow/rollup': 4.0.10(@opentelemetry/api@1.9.1)(ws@8.21.0) '@workflow/swc-plugin': 4.1.1(@swc/core@1.15.3) '@workflow/vite': 4.0.10(@opentelemetry/api@1.9.1)(ws@8.21.0) - '@workflow/web': 4.1.11 + '@workflow/web': 4.1.11(supports-color@8.1.1) exsolve: 1.0.8 pathe: 2.0.3 transitivePeerDependencies: @@ -6740,15 +6743,15 @@ snapshots: - supports-color - ws - '@workflow/web@4.1.11': + '@workflow/web@4.1.11(supports-color@8.1.1)': dependencies: - express: 5.2.1 + express: 5.2.1(supports-color@8.1.1) transitivePeerDependencies: - supports-color '@workflow/web@5.0.0-beta.26': dependencies: - express: 5.2.1 + express: 5.2.1(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -7089,7 +7092,7 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 - body-parser@2.3.0: + body-parser@2.3.0(supports-color@8.1.1): dependencies: bytes: 3.1.2 content-type: 2.0.0 @@ -7628,24 +7631,24 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-compat-utils@0.5.1(eslint@9.39.4(jiti@2.7.0)): + eslint-compat-utils@0.5.1(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1)): dependencies: - eslint: 9.39.4(jiti@2.7.0) + eslint: 9.39.4(jiti@2.7.0)(supports-color@8.1.1) semver: 7.8.5 - eslint-plugin-es-x@7.8.0(eslint@9.39.4(jiti@2.7.0)): + eslint-plugin-es-x@7.8.0(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1)): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1)) '@eslint-community/regexpp': 4.12.2 - eslint: 9.39.4(jiti@2.7.0) - eslint-compat-utils: 0.5.1(eslint@9.39.4(jiti@2.7.0)) + eslint: 9.39.4(jiti@2.7.0)(supports-color@8.1.1) + eslint-compat-utils: 0.5.1(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1)) - eslint-plugin-n@17.24.0(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3): + eslint-plugin-n@17.24.0(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1))(typescript@6.0.3): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1)) enhanced-resolve: 5.24.1 - eslint: 9.39.4(jiti@2.7.0) - eslint-plugin-es-x: 7.8.0(eslint@9.39.4(jiti@2.7.0)) + eslint: 9.39.4(jiti@2.7.0)(supports-color@8.1.1) + eslint-plugin-es-x: 7.8.0(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1)) get-tsconfig: 4.14.0 globals: 15.15.0 globrex: 0.1.2 @@ -7655,12 +7658,12 @@ snapshots: transitivePeerDependencies: - typescript - eslint-plugin-promise@7.3.0(eslint@9.39.4(jiti@2.7.0)): + eslint-plugin-promise@7.3.0(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1)): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0)) - eslint: 9.39.4(jiti@2.7.0) + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1)) + eslint: 9.39.4(jiti@2.7.0)(supports-color@8.1.1) - eslint-plugin-react@7.37.5(eslint@9.39.4(jiti@2.7.0)): + eslint-plugin-react@7.37.5(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1)): dependencies: array-includes: 3.1.9 array.prototype.findlast: 1.2.5 @@ -7668,7 +7671,7 @@ snapshots: array.prototype.tosorted: 1.1.4 doctrine: 2.1.0 es-iterator-helpers: 1.3.3 - eslint: 9.39.4(jiti@2.7.0) + eslint: 9.39.4(jiti@2.7.0)(supports-color@8.1.1) estraverse: 5.3.0 hasown: 2.0.4 jsx-ast-utils: 3.3.5 @@ -7693,14 +7696,14 @@ snapshots: eslint-visitor-keys@5.0.1: {} - eslint@9.39.4(jiti@2.7.0): + eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1)) '@eslint-community/regexpp': 4.12.2 - '@eslint/config-array': 0.21.2 + '@eslint/config-array': 0.21.2(supports-color@8.1.1) '@eslint/config-helpers': 0.4.2 '@eslint/core': 0.17.0 - '@eslint/eslintrc': 3.3.5 + '@eslint/eslintrc': 3.3.5(supports-color@8.1.1) '@eslint/js': 9.39.4 '@eslint/plugin-kit': 0.4.1 '@humanfs/node': 0.16.8 @@ -7809,10 +7812,10 @@ snapshots: strip-final-newline: 4.0.0 yoctocolors: 2.1.2 - express@5.2.1: + express@5.2.1(supports-color@8.1.1): dependencies: accepts: 2.0.0 - body-parser: 2.3.0 + body-parser: 2.3.0(supports-color@8.1.1) content-disposition: 1.1.0 content-type: 1.0.5 cookie: 0.7.2 @@ -7822,7 +7825,7 @@ snapshots: encodeurl: 2.0.0 escape-html: 1.0.3 etag: 1.8.1 - finalhandler: 2.1.1 + finalhandler: 2.1.1(supports-color@8.1.1) fresh: 2.0.0 http-errors: 2.0.1 merge-descriptors: 2.0.0 @@ -7833,8 +7836,8 @@ snapshots: proxy-addr: 2.0.7 qs: 6.15.3 range-parser: 1.3.0 - router: 2.2.0 - send: 1.2.1 + router: 2.2.0(supports-color@8.1.1) + send: 1.2.1(supports-color@8.1.1) serve-static: 2.2.1 statuses: 2.0.2 type-is: 2.1.0 @@ -7966,7 +7969,7 @@ snapshots: dependencies: filename-reserved-regex: 4.0.0 - finalhandler@2.1.1: + finalhandler@2.1.1(supports-color@8.1.1): dependencies: debug: 4.4.3(supports-color@8.1.1) encodeurl: 2.0.0 @@ -8686,18 +8689,18 @@ snapshots: negotiator@1.0.0: {} - neostandard@0.13.0(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3): + neostandard@0.13.0(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3): dependencies: '@humanwhocodes/gitignore-to-minimatch': 1.0.2 - '@stylistic/eslint-plugin': 2.11.0(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) - eslint: 9.39.4(jiti@2.7.0) - eslint-plugin-n: 17.24.0(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) - eslint-plugin-promise: 7.3.0(eslint@9.39.4(jiti@2.7.0)) - eslint-plugin-react: 7.37.5(eslint@9.39.4(jiti@2.7.0)) + '@stylistic/eslint-plugin': 2.11.0(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1))(typescript@6.0.3) + eslint: 9.39.4(jiti@2.7.0)(supports-color@8.1.1) + eslint-plugin-n: 17.24.0(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1))(typescript@6.0.3) + eslint-plugin-promise: 7.3.0(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1)) + eslint-plugin-react: 7.37.5(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1)) find-up: 8.0.0 globals: 17.7.0 peowly: 1.3.3 - typescript-eslint: 8.62.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) + typescript-eslint: 8.62.1(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) transitivePeerDependencies: - supports-color - typescript @@ -9268,7 +9271,7 @@ snapshots: rfdc@1.4.1: {} - router@2.2.0: + router@2.2.0(supports-color@8.1.1): dependencies: debug: 4.4.3(supports-color@8.1.1) depd: 2.0.0 @@ -9343,7 +9346,7 @@ snapshots: semver@7.8.5: {} - send@1.2.1: + send@1.2.1(supports-color@8.1.1): dependencies: debug: 4.4.3(supports-color@8.1.1) encodeurl: 2.0.0 @@ -9364,7 +9367,7 @@ snapshots: encodeurl: 2.0.0 escape-html: 1.0.3 parseurl: 1.3.3 - send: 1.2.1 + send: 1.2.1(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -9771,13 +9774,13 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 - typescript-eslint@8.62.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3): + typescript-eslint@8.62.1(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.62.1(@typescript-eslint/parser@8.62.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) - '@typescript-eslint/parser': 8.62.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/eslint-plugin': 8.62.1(@typescript-eslint/parser@8.62.1(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) + '@typescript-eslint/parser': 8.62.1(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) '@typescript-eslint/typescript-estree': 8.62.1(typescript@6.0.3) - '@typescript-eslint/utils': 8.62.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) - eslint: 9.39.4(jiti@2.7.0) + '@typescript-eslint/utils': 8.62.1(eslint@9.39.4(jiti@2.7.0)(supports-color@8.1.1))(typescript@6.0.3) + eslint: 9.39.4(jiti@2.7.0)(supports-color@8.1.1) typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -9961,10 +9964,10 @@ snapshots: wordwrap@1.0.0: {} - workflow@4.5.0(@nestjs/common@11.1.27(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.27(@nestjs/common@11.1.27(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2))(@opentelemetry/api@1.9.1)(@swc/cli@0.8.1(@swc/core@1.15.3)(chokidar@5.0.0))(@swc/core@1.15.3)(next@16.2.10(@opentelemetry/api@1.9.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@6.0.3)(ws@8.21.0): + workflow@4.5.0(@nestjs/common@11.1.27(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.27(@nestjs/common@11.1.27(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2))(@opentelemetry/api@1.9.1)(@swc/cli@0.8.1(@swc/core@1.15.3)(chokidar@5.0.0))(@swc/core@1.15.3)(next@16.2.10(@opentelemetry/api@1.9.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(supports-color@8.1.1)(typescript@6.0.3)(ws@8.21.0): dependencies: '@workflow/astro': 4.0.10(@opentelemetry/api@1.9.1)(ws@8.21.0) - '@workflow/cli': 4.2.10(@opentelemetry/api@1.9.1)(ws@8.21.0) + '@workflow/cli': 4.2.10(@opentelemetry/api@1.9.1)(supports-color@8.1.1)(ws@8.21.0) '@workflow/core': 4.5.0(@opentelemetry/api@1.9.1)(ws@8.21.0) '@workflow/errors': 4.1.4 '@workflow/nest': 0.0.10(@nestjs/common@11.1.27(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.27(@nestjs/common@11.1.27(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2))(@opentelemetry/api@1.9.1)(@swc/cli@0.8.1(@swc/core@1.15.3)(chokidar@5.0.0))(@swc/core@1.15.3)(ws@8.21.0)