From 32e76c99ec3fa0eb0e3daf0e5c1f2f2d621bac80 Mon Sep 17 00:00:00 2001 From: pgflow bot Date: Mon, 8 Jun 2026 01:07:44 +0200 Subject: [PATCH] feat: build edge worker for npm --- .changeset/portable-edge-worker-npm.md | 9 +++++++ package.json | 5 ++-- pkgs/edge-worker/project.json | 6 ++--- pkgs/edge-worker/src/control-plane/index.ts | 4 +-- pkgs/edge-worker/src/platform/processDeps.ts | 2 +- .../src/platform/resolveConnection.ts | 2 +- pkgs/edge-worker/src/shared/authValidation.ts | 24 ++++++++++++------ pkgs/edge-worker/tsconfig.json | 1 + scripts/smoke-edge-worker-dist.mjs | 25 +++++++++++++++++++ 9 files changed, 60 insertions(+), 18 deletions(-) create mode 100644 .changeset/portable-edge-worker-npm.md create mode 100644 scripts/smoke-edge-worker-dist.mjs diff --git a/.changeset/portable-edge-worker-npm.md b/.changeset/portable-edge-worker-npm.md new file mode 100644 index 000000000..c5cb1e68f --- /dev/null +++ b/.changeset/portable-edge-worker-npm.md @@ -0,0 +1,9 @@ +--- +"@pgflow/core": minor +"@pgflow/dsl": minor +"@pgflow/client": minor +"@pgflow/edge-worker": minor +"pgflow": minor +--- + +Publish `@pgflow/edge-worker` to npm and add Node/Bun process runtime support. diff --git a/package.json b/package.json index 8c3462853..8b2a7d0d5 100644 --- a/package.json +++ b/package.json @@ -6,10 +6,11 @@ "scripts": { "build": "nx run-many --target=build --all", "version": "pnpm changeset version && ./scripts/update-jsr-json-version.sh", - "validate:publish:npm": "pnpm nx run-many -t build --exclude=demo,website,example-flows && git status && pnpm publish --dry-run --provenance --recursive --filter=!./pkgs/edge-worker", + "smoke:edge-worker:dist": "pnpm nx build edge-worker && node ./scripts/smoke-edge-worker-dist.mjs", + "validate:publish:npm": "pnpm nx run-many -t build --exclude=demo,website,example-flows && pnpm smoke:edge-worker:dist && git status && pnpm publish --dry-run --provenance --recursive", "validate:publish:jsr": "cd ./pkgs/edge-worker && jsr publish --dry-run --allow-slow-types", "validate:publish": "pnpm run validate:publish:npm && pnpm run validate:publish:jsr", - "publish:npm": "pnpm nx run-many -t build --exclude=demo,website,example-flows && pnpm publish --provenance --recursive --filter=!./pkgs/edge-worker", + "publish:npm": "pnpm nx run-many -t build --exclude=demo,website,example-flows && pnpm publish --provenance --recursive", "publish:jsr": "cd ./pkgs/edge-worker && jsr publish --allow-slow-types", "changeset:tag": "pnpm changeset tag && git push --follow-tags", "release": "git status && pnpm run validate:publish && pnpm run publish:npm && pnpm run publish:jsr && pnpm run changeset:tag" diff --git a/pkgs/edge-worker/project.json b/pkgs/edge-worker/project.json index fdf9ea5d4..38d4ab617 100644 --- a/pkgs/edge-worker/project.json +++ b/pkgs/edge-worker/project.json @@ -8,11 +8,9 @@ "executor": "nx:noop" }, "build": { - "executor": "nx:noop", - "dependsOn": ["^build"] - }, - "_build_disabled": { "executor": "@nx/js:tsc", + "outputs": ["{options.outputPath}"], + "dependsOn": ["^build"], "options": { "outputPath": "pkgs/edge-worker/dist", "main": "pkgs/edge-worker/src/index.ts", diff --git a/pkgs/edge-worker/src/control-plane/index.ts b/pkgs/edge-worker/src/control-plane/index.ts index f92a9c19f..02984021a 100644 --- a/pkgs/edge-worker/src/control-plane/index.ts +++ b/pkgs/edge-worker/src/control-plane/index.ts @@ -8,7 +8,7 @@ * ```typescript * // Using namespace import (recommended) * import { ControlPlane } from '@pgflow/edge-worker'; - * import * as flows from '../../flows/index.ts'; + * import * as flows from '../../flows/index.js'; * * ControlPlane.serve(flows); * ``` @@ -17,7 +17,7 @@ * ```typescript * // Using array (legacy) * import { ControlPlane } from '@pgflow/edge-worker'; - * import { MyFlow } from '../../flows/my_flow.ts'; + * import { MyFlow } from '../../flows/my_flow.js'; * * ControlPlane.serve([MyFlow]); * ``` diff --git a/pkgs/edge-worker/src/platform/processDeps.ts b/pkgs/edge-worker/src/platform/processDeps.ts index a0fac1f03..31230f9a7 100644 --- a/pkgs/edge-worker/src/platform/processDeps.ts +++ b/pkgs/edge-worker/src/platform/processDeps.ts @@ -21,7 +21,7 @@ type CryptoLike = { export function getProcessDeps(): ProcessDeps { const processLike = (globalThis as { process?: ProcessLike }).process; - const cryptoLike = globalThis.crypto as CryptoLike | undefined; + const cryptoLike = (globalThis as { crypto?: CryptoLike }).crypto; if (!processLike?.env || !processLike.on || !processLike.exit || !cryptoLike?.randomUUID) { throw new Error('Process runtime is not available'); diff --git a/pkgs/edge-worker/src/platform/resolveConnection.ts b/pkgs/edge-worker/src/platform/resolveConnection.ts index 44b545eed..a72fdb6a9 100644 --- a/pkgs/edge-worker/src/platform/resolveConnection.ts +++ b/pkgs/edge-worker/src/platform/resolveConnection.ts @@ -1,4 +1,4 @@ -import { isLocalSupabaseEnv } from '../shared/localDetection.ts'; +import { isLocalSupabaseEnv } from '../shared/localDetection.js'; import postgres from 'postgres'; /** diff --git a/pkgs/edge-worker/src/shared/authValidation.ts b/pkgs/edge-worker/src/shared/authValidation.ts index 493a34758..7b8a64cc0 100644 --- a/pkgs/edge-worker/src/shared/authValidation.ts +++ b/pkgs/edge-worker/src/shared/authValidation.ts @@ -1,5 +1,18 @@ -import { timingSafeEqual } from '@std/crypto/timing-safe-equal'; -import { isLocalSupabaseEnv } from './localDetection.ts'; +import { isLocalSupabaseEnv } from './localDetection.js'; + +function timingSafeEqualBytes(a: Uint8Array, b: Uint8Array): boolean { + if (a.length !== b.length) { + return false; + } + + let diff = 0; + + for (let index = 0; index < a.length; index += 1) { + diff |= a[index] ^ b[index]; + } + + return diff === 0; +} export interface AuthValidationResult { valid: boolean; @@ -38,12 +51,7 @@ export function validateServiceRoleAuth( const authBytes = encoder.encode(authHeader); const expectedBytes = encoder.encode(expected); - // Length check first (timingSafeEqual requires same length) - if (authBytes.length !== expectedBytes.length) { - return { valid: false, error: 'Invalid Authorization header' }; - } - - if (!timingSafeEqual(authBytes, expectedBytes)) { + if (!timingSafeEqualBytes(authBytes, expectedBytes)) { return { valid: false, error: 'Invalid Authorization header' }; } diff --git a/pkgs/edge-worker/tsconfig.json b/pkgs/edge-worker/tsconfig.json index 986088967..22912e6a5 100644 --- a/pkgs/edge-worker/tsconfig.json +++ b/pkgs/edge-worker/tsconfig.json @@ -6,6 +6,7 @@ "rootDir": "src", "outDir": "dist", "tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo", + "lib": ["es2022", "dom", "dom.iterable"], "typeRoots": ["./node_modules/@types", "."], "types": [ "node", diff --git a/scripts/smoke-edge-worker-dist.mjs b/scripts/smoke-edge-worker-dist.mjs new file mode 100644 index 000000000..36f38c7ce --- /dev/null +++ b/scripts/smoke-edge-worker-dist.mjs @@ -0,0 +1,25 @@ +import * as edgeWorker from '../pkgs/edge-worker/dist/index.js'; +import * as internal from '../pkgs/edge-worker/dist/_internal.js'; +import * as testing from '../pkgs/edge-worker/dist/testing.js'; + +const requiredExports = [ + ['EdgeWorker', edgeWorker.EdgeWorker], + ['createQueueWorker', edgeWorker.createQueueWorker], + ['createFlowWorker', edgeWorker.createFlowWorker], + ['ProcessPlatformAdapter', edgeWorker.ProcessPlatformAdapter], + ['SupabasePlatformAdapter', edgeWorker.SupabasePlatformAdapter], +]; + +for (const [name, value] of requiredExports) { + if (value === undefined) { + throw new Error(`Missing edge-worker export: ${name}`); + } +} + +if (Object.keys(internal).length === 0) { + throw new Error('Expected _internal export surface to load'); +} + +if (Object.keys(testing).length === 0) { + throw new Error('Expected testing export surface to load'); +}