@wayai/executor-sdk 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,7 +15,8 @@ npm i @wayai/executor-sdk
15
15
 
16
16
  ## The wire
17
17
 
18
- Data signs every request it sends over **Signing v1**. The signature is HMAC-SHA256 over a canonical
18
+ Data signs the requests it sends over **Signing v1**, except a webhook trigger's that holds no signing secret,
19
+ which goes out unsigned for now. The signature is HMAC-SHA256 over a canonical
19
20
  string, sent as `X-Data-Signature: v1,<hex>` (a space-delimited list so a key rotation can carry old +
20
21
  new). The canonical is:
21
22
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/hmac.ts","../src/core/headers.ts","../src/core/canonical.ts","../src/core/verify.ts","../src/store/memory.ts","../src/errors/envelope.ts","../src/executor.ts","../src/adapters/fetch.ts","../src/adapters/node.ts","../src/core/sign.ts","../src/core/credential.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * HMAC-SHA256 over Web Crypto — pure, dependency-free, runs in any runtime with `crypto.subtle`\n * (Cloudflare Workers, Deno, Bun, browsers, and Node — see the package's declared `engines`).\n * Ported verbatim from the backend's implementation; the cross-package conformance vector\n * (test/conformance.test.ts) guards against drift.\n */\n\nexport async function signPayload(secret: string, payload: string): Promise<string> {\n const key = await crypto.subtle.importKey(\n 'raw',\n new TextEncoder().encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign'],\n );\n const signature = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(payload));\n return arrayBufferToHex(signature);\n}\n\n/** Constant-time-ish equality of two hex strings (length-checks first, then XOR-accumulate). */\nexport function timingSafeEqual(a: string, b: string): boolean {\n if (a.length !== b.length) return false;\n let result = 0;\n for (let i = 0; i < a.length; i++) result |= a.charCodeAt(i) ^ b.charCodeAt(i);\n return result === 0;\n}\n\nfunction arrayBufferToHex(buffer: ArrayBuffer): string {\n return Array.from(new Uint8Array(buffer))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n","/** Case-insensitive header reader for a Fetch `Headers` or a plain object. A duplicate (array) value is\n * ambiguous and resolves to `undefined` (the signature/timestamp must be unambiguous). Shared by the\n * verify path and the dev-bypass path so they read headers identically. */\nexport type HeaderGetter = (name: string) => string | undefined;\n\nexport function headerGetter(headers: Headers | Record<string, string | string[] | undefined>): HeaderGetter {\n if (headers instanceof Headers) return (name) => headers.get(name) ?? undefined;\n const lower: Record<string, string | string[] | undefined> = {};\n for (const [k, v] of Object.entries(headers)) lower[k.toLowerCase()] = v;\n return (name) => {\n const v = lower[name];\n if (Array.isArray(v)) return v.length === 1 ? v[0] : undefined;\n return v;\n };\n}\n","/**\n * The \"Signing v1\" canonical — the exact string Data HMAC-signs for every request it sends\n * (trigger webhooks + signed proxied calls). Ported verbatim from the backend's implementation and\n * kept byte-identical to it by the cross-package conformance vector (test/conformance.test.ts).\n *\n * canonical = [\"v1\", id, String(unixTimestamp), method, path, body].join(\"\\n\")\n *\n * SECURITY INVARIANT: newline-joined; the only data-influenced field, `body`, is LAST, and every\n * other field is provably newline-free (the \"v1\" literal, an integer, a uuid/hex id, an HTTP method\n * token, and a URL pathname+search). The signature binds method + path so it can't be replayed as a\n * different request.\n */\n\nexport const SIGNING_SCHEME = 'v1';\n\nexport interface SignedParts {\n /** Stable message id — `X-Data-Id` (= delivery id on webhooks, idempotency key on proxied writes). */\n id: string;\n /** `X-Data-Timestamp`, unix seconds (integer). */\n timestamp: number;\n /** HTTP method (`POST` for trigger webhooks). */\n method: string;\n /** Request path: `new URL(receiverUrl).pathname + search`. */\n path: string;\n /** Exact raw request body string (`\"\"` when none). */\n body: string;\n}\n\n/** Build the canonical signing string. Component order and the `v1` prefix are part of the wire. */\nexport function buildCanonical(parts: SignedParts): string {\n return [SIGNING_SCHEME, parts.id, String(parts.timestamp), parts.method, parts.path, parts.body].join('\\n');\n}\n\n/** Current unix time in whole seconds (Standard Webhooks convention). `nowMs` override for tests. */\nexport function unixSeconds(nowMs?: number): number {\n return Math.floor((nowMs ?? Date.now()) / 1000);\n}\n","/**\n * Verify a \"Signing v1\" request: HMAC over the canonical, plus timestamp-skew enforcement\n * (the receiver's job — Data only emits). All inputs are the raw request bits the adapter extracts.\n */\n\nimport { signPayload, timingSafeEqual } from './hmac.js';\nimport { headerGetter } from './headers.js';\nimport { SIGNING_SCHEME, buildCanonical, unixSeconds, type SignedParts } from './canonical.js';\n\n/** Lowercased header names Data emits. */\nexport const DATA_HEADERS = {\n signature: 'x-data-signature',\n timestamp: 'x-data-timestamp',\n id: 'x-data-id',\n idempotencyKey: 'x-data-idempotency-key',\n deliveryId: 'x-data-delivery-id',\n // Executor credential pull: advertised on a proxied dispatch to a source declaring executor_secrets.\n credentialUrl: 'x-data-credential-url',\n credentialToken: 'x-data-credential-token',\n credentialNames: 'x-data-credential-names',\n} as const;\n\n/**\n * Max `scheme,hex` candidates we'll HMAC-check from one `X-Data-Signature` header. Data emits one\n * (two during a future key rotation); the cap stops a hostile header with thousands of entries from\n * forcing thousands of HMACs per request.\n */\nexport const MAX_SIG_ENTRIES = 4;\n\n/**\n * Verify a signature header value (`X-Data-Signature`) against the canonical of `parts`. The header\n * is a space-delimited list of `scheme,hex` entries; returns true if any `v1` entry matches (constant-\n * time compare). Malformed/empty/unknown-scheme entries are skipped, never throw. Candidates beyond\n * `MAX_SIG_ENTRIES` are ignored. Does NOT check timestamp skew (see {@link verify}).\n */\nexport async function verifySignatureHeader(secret: string, parts: SignedParts, header: string): Promise<boolean> {\n const canonical = buildCanonical(parts);\n let expected: string | undefined; // loop-invariant HMAC — computed at most once, lazily\n let checked = 0;\n let matched = false;\n for (const entry of header.split(' ')) {\n const comma = entry.indexOf(',');\n if (comma <= 0) continue; // no scheme/hex separator (e.g. \"v1\" or \"\")\n if (entry.slice(0, comma) !== SIGNING_SCHEME) continue;\n const hex = entry.slice(comma + 1);\n if (hex.length === 0) continue;\n if (++checked > MAX_SIG_ENTRIES) break;\n if (expected === undefined) expected = await signPayload(secret, canonical);\n if (timingSafeEqual(expected, hex)) matched = true;\n }\n return matched;\n}\n\nexport type VerifyResult =\n | { ok: true; id: string; timestamp: number; idempotencyKey?: string; deliveryId?: string; body: string }\n | { ok: false; reason: 'missing_signature' | 'invalid_signature' | 'stale_timestamp' | 'malformed' };\n\nexport interface VerifyInput {\n /** The HMAC signing secret shared with Data (the trigger/source signing secret). */\n secret: string;\n /** Request headers — a Fetch `Headers` or a plain object (case-insensitive lookup either way). */\n headers: Headers | Record<string, string | string[] | undefined>;\n /** HTTP method of the incoming request. */\n method: string;\n /** Request path the signature was computed over: `pathname + search` (see README path-matching). */\n path: string;\n /** RAW request body bytes as a string — NEVER a re-stringified parsed object. */\n rawBody: string;\n /** Allowed clock skew in seconds (both directions). A finite value is clamped to ≥1 (never 0);\n * unset / NaN / Infinity fall back to the 300s default (a bad value can't disable the window). */\n toleranceSec?: number;\n /** Override \"now\" in ms (tests). */\n now?: number;\n}\n\n/**\n * Verify an inbound Data request end to end: signature present + valid, and timestamp within the\n * skew window. Returns a discriminated result; never throws on bad input.\n */\nexport async function verify(input: VerifyInput): Promise<VerifyResult> {\n const get = headerGetter(input.headers);\n const signature = get(DATA_HEADERS.signature);\n if (!signature) return { ok: false, reason: 'missing_signature' };\n\n const tsRaw = get(DATA_HEADERS.timestamp);\n const id = get(DATA_HEADERS.id);\n if (!tsRaw || !id) return { ok: false, reason: 'malformed' };\n if (!/^\\d+$/.test(tsRaw)) return { ok: false, reason: 'malformed' };\n const timestamp = Number(tsRaw);\n if (!Number.isFinite(timestamp)) return { ok: false, reason: 'malformed' };\n\n // Non-finite (unset / NaN / Infinity, e.g. `Number(process.env.X)` with X missing) → the 300s\n // default, so a misconfigured tolerance can never silently disable the replay window. A finite value\n // is clamped to ≥1 (never 0). To accept a wider window, pass an explicit large finite number.\n const t = input.toleranceSec;\n const tolerance = Number.isFinite(t) ? Math.max(1, Math.floor(t as number)) : 300;\n const now = unixSeconds(input.now);\n if (Math.abs(now - timestamp) > tolerance) return { ok: false, reason: 'stale_timestamp' };\n\n const parts: SignedParts = { id, timestamp, method: input.method, path: input.path, body: input.rawBody };\n const valid = await verifySignatureHeader(input.secret, parts, signature);\n if (!valid) return { ok: false, reason: 'invalid_signature' };\n\n return {\n ok: true,\n id,\n timestamp,\n idempotencyKey: get(DATA_HEADERS.idempotencyKey),\n deliveryId: get(DATA_HEADERS.deliveryId),\n body: input.rawBody,\n };\n}\n","import type { ClaimResult, IdempotencyStore, StoredResult } from './types.js';\n\ninterface Entry {\n state: 'pending' | 'done';\n result?: StoredResult;\n expiresAt: number; // epoch ms\n}\n\n/**\n * In-memory idempotency store (per-process). Single-threaded JS gives the atomic first-wins claim for\n * free. An accessed key expires lazily; since every delivery has a unique key, completed entries are\n * otherwise dropped by a periodic full sweep (every {@link SWEEP_EVERY} writes) — so the map is bounded\n * by the deliveries within one TTL window (default 24h), not unbounded. For multiple instances, or to\n * cap memory tightly, implement {@link IdempotencyStore} over a shared backend instead.\n */\nconst DEFAULT_SWEEP_EVERY = 1000;\n\nexport class MemoryStore implements IdempotencyStore {\n private readonly map = new Map<string, Entry>();\n private readonly nowMs: () => number;\n private readonly sweepEvery: number;\n private writes = 0;\n\n constructor(opts?: { now?: () => number; sweepEvery?: number }) {\n this.nowMs = opts?.now ?? (() => Date.now());\n this.sweepEvery = opts?.sweepEvery ?? DEFAULT_SWEEP_EVERY;\n }\n\n /** Number of live entries currently held (after lazily ignoring nothing — counts raw map size). */\n get size(): number {\n return this.map.size;\n }\n\n private live(key: string): Entry | undefined {\n const e = this.map.get(key);\n if (!e) return undefined;\n if (e.expiresAt <= this.nowMs()) {\n this.map.delete(key);\n return undefined;\n }\n return e;\n }\n\n /** Amortized O(1)/write: every SWEEP_EVERY writes, drop all expired entries so the map can't grow forever. */\n private maybeSweep(): void {\n if (++this.writes < this.sweepEvery) return;\n this.writes = 0;\n const now = this.nowMs();\n for (const [k, e] of this.map) if (e.expiresAt <= now) this.map.delete(k);\n }\n\n async claim(key: string, ttlSec: number): Promise<ClaimResult> {\n const e = this.live(key);\n if (!e) {\n this.map.set(key, { state: 'pending', expiresAt: this.nowMs() + ttlSec * 1000 });\n this.maybeSweep();\n return { state: 'fresh' };\n }\n if (e.state === 'done' && e.result) return { state: 'done', result: e.result };\n return { state: 'in_progress' };\n }\n\n async complete(key: string, result: StoredResult, ttlSec: number): Promise<void> {\n this.map.set(key, { state: 'done', result, expiresAt: this.nowMs() + ttlSec * 1000 });\n this.maybeSweep();\n }\n\n async fail(key: string): Promise<void> {\n this.map.delete(key);\n }\n}\n","/**\n * The normalized error envelope an executor returns. The HTTP STATUS is the real contract a sender's\n * retry logic reads (2xx ack, 4xx permanent/no-retry, 5xx & 429 retry); the JSON body is for humans.\n */\nexport interface ErrorEnvelope {\n status: number;\n code: string;\n message: string;\n retriable: boolean;\n}\n\n/**\n * Throw this from a handler to fail with a specific code/retriability. `retriable` decides the status\n * family ({@link toHttpStatus}) so the sender retries (5xx/429) or drops (4xx) correctly.\n */\nexport class ExecutorError extends Error {\n readonly code: string;\n readonly retriable: boolean;\n readonly httpStatus?: number;\n\n constructor(args: { code: string; message: string; retriable: boolean; status?: number }) {\n super(args.message);\n this.name = 'ExecutorError';\n this.code = args.code;\n this.retriable = args.retriable;\n this.httpStatus = args.status;\n }\n\n toEnvelope(): ErrorEnvelope {\n return { status: toHttpStatus(this), code: this.code, message: this.message, retriable: this.retriable };\n }\n}\n\n/**\n * Map an error to an HTTP status. An explicit `status` is honored when it agrees with `retriable`\n * (5xx/429 for retriable, 4xx for permanent); otherwise the family default is used: retriable → 503,\n * permanent → 400.\n */\nexport function toHttpStatus(e: { retriable: boolean; httpStatus?: number }): number {\n const s = e.httpStatus;\n if (e.retriable) {\n return s !== undefined && (s === 429 || (s >= 500 && s <= 599)) ? s : 503;\n }\n return s !== undefined && s >= 400 && s <= 499 ? s : 400;\n}\n\n/** Serialize an envelope to a JSON body string. */\nexport function jsonResponseBody(env: ErrorEnvelope): string {\n return JSON.stringify(env);\n}\n","import { verify, DATA_HEADERS, type VerifyResult } from './core/verify.js';\nimport { headerGetter } from './core/headers.js';\nimport { MemoryStore } from './store/memory.js';\nimport type { IdempotencyStore, StoredResult } from './store/types.js';\nimport { ExecutorError, toHttpStatus, jsonResponseBody, type ErrorEnvelope } from './errors/envelope.js';\n\n/** Framework-neutral request the adapters hand to the executor. `rawBody` is the RAW bytes as a string. */\nexport interface NormalizedRequest {\n method: string;\n path: string;\n headers: Headers | Record<string, string | string[] | undefined>;\n rawBody: string;\n}\n\nexport interface NormalizedResponse {\n status: number;\n body: string;\n contentType: string;\n}\n\n/** Verified request context passed to your handler. Never contains the signing secret. */\nexport interface ExecutorContext {\n id: string;\n idempotencyKey?: string;\n deliveryId?: string;\n timestamp: number;\n body: string;\n}\n\n/** Your business logic. Return a `StoredResult` (cached for idempotent replays) or nothing (→ 204). */\nexport type ExecutorHandler = (ctx: ExecutorContext) => Promise<StoredResult | void> | StoredResult | void;\n\nexport interface CreateExecutorOptions {\n /** HMAC signing secret shared with Data. */\n secret: string;\n handler: ExecutorHandler;\n /** Idempotency store. Default: a per-process {@link MemoryStore}. */\n store?: IdempotencyStore;\n /** Clock-skew tolerance (seconds, both directions). Default 300; clamped to ≥1. */\n toleranceSec?: number;\n /** Skip signature/skew verification (LOCAL DEV ONLY). Dedup still applies if headers are present. */\n devBypass?: boolean;\n /** Idempotency record TTL (seconds). Default 86400 (24h). */\n idempotencyTtlSec?: number;\n /** Reports unexpected handler errors. MUST NOT log secrets/PII — it never receives the secret. */\n onError?: (err: unknown, ctx?: ExecutorContext) => void;\n /**\n * Reproduce the path Data SIGNED when a reverse proxy rewrites it. Must return the exact\n * `pathname+search` Data used (see README path-matching), not necessarily the incoming path.\n */\n pathOverride?: (req: NormalizedRequest) => string;\n}\n\nexport interface Executor {\n handle(req: NormalizedRequest): Promise<NormalizedResponse>;\n}\n\nconst JSON_CT = 'application/json';\n\nfunction errorResponse(env: ErrorEnvelope): NormalizedResponse {\n return { status: env.status, body: jsonResponseBody(env), contentType: JSON_CT };\n}\n\nfunction reasonResponse(reason: Exclude<VerifyResult, { ok: true }>['reason']): NormalizedResponse {\n // missing/invalid → 401 (unauthenticated); stale → 403; malformed → 400.\n const status = reason === 'malformed' ? 400 : reason === 'stale_timestamp' ? 403 : 401;\n return errorResponse({ status, code: reason.toUpperCase(), message: `signature ${reason}`, retriable: false });\n}\n\n/** Read the dedupe context from headers without verifying (dev-bypass path). */\nfunction bypassContext(req: NormalizedRequest): ExecutorContext | null {\n const get = headerGetter(req.headers);\n const id = get(DATA_HEADERS.id);\n const tsRaw = get(DATA_HEADERS.timestamp);\n return {\n id: id ?? '',\n idempotencyKey: get(DATA_HEADERS.idempotencyKey),\n deliveryId: get(DATA_HEADERS.deliveryId),\n timestamp: tsRaw && /^\\d+$/.test(tsRaw) ? Number(tsRaw) : 0,\n body: req.rawBody,\n };\n}\n\nexport function createExecutor(opts: CreateExecutorOptions): Executor {\n const store = opts.store ?? new MemoryStore();\n const ttl = opts.idempotencyTtlSec ?? 86_400;\n\n async function run(ctx: ExecutorContext): Promise<NormalizedResponse> {\n // Dedupe key: the logical idempotency key (proxy writes) or the stable message id (webhooks).\n // Absent both (e.g. dev-bypass with no signed headers) → run without dedupe.\n const key = ctx.idempotencyKey || ctx.id;\n if (!key) return await invoke(ctx);\n\n const claim = await store.claim(key, ttl);\n if (claim.state === 'done') {\n return { status: claim.result.status, body: claim.result.body, contentType: claim.result.contentType ?? JSON_CT };\n }\n if (claim.state === 'in_progress') {\n // A concurrent attempt holds this key — tell the sender to retry. Use a retriable (5xx) status,\n // not 409, so it matches `retriable: true` and the sender's retry convention.\n return errorResponse({ status: 503, code: 'IN_PROGRESS', message: 'duplicate delivery in progress', retriable: true });\n }\n try {\n const res = await invoke(ctx);\n await store.complete(key, { status: res.status, body: res.body, contentType: res.contentType }, ttl);\n return res;\n } catch (err) {\n const env = toEnvelope(err);\n const res = errorResponse(env);\n if (env.retriable) {\n await store.fail(key); // retriable: release the key so the retry re-runs (don't cache failures)\n } else {\n // permanent: cache the 4xx so a duplicate delivery replays it instead of re-running.\n await store.complete(key, { status: res.status, body: res.body, contentType: res.contentType }, ttl);\n }\n if (!(err instanceof ExecutorError)) opts.onError?.(err, ctx);\n return res;\n }\n }\n\n async function invoke(ctx: ExecutorContext): Promise<NormalizedResponse> {\n const result = await opts.handler(ctx);\n if (!result) return { status: 204, body: '', contentType: JSON_CT };\n return { status: result.status, body: result.body, contentType: result.contentType ?? JSON_CT };\n }\n\n return {\n async handle(req: NormalizedRequest): Promise<NormalizedResponse> {\n const path = opts.pathOverride ? opts.pathOverride(req) : req.path;\n\n if (opts.devBypass) {\n const ctx = bypassContext(req);\n return ctx ? await run(ctx) : reasonResponse('malformed');\n }\n\n const result = await verify({\n secret: opts.secret,\n headers: req.headers,\n method: req.method,\n path,\n rawBody: req.rawBody,\n toleranceSec: opts.toleranceSec,\n });\n if (!result.ok) return reasonResponse(result.reason);\n\n return await run({\n id: result.id,\n idempotencyKey: result.idempotencyKey,\n deliveryId: result.deliveryId,\n timestamp: result.timestamp,\n body: result.body,\n });\n },\n };\n}\n\nfunction toEnvelope(err: unknown): ErrorEnvelope {\n if (err instanceof ExecutorError) return err.toEnvelope();\n // Unknown error → retriable 500 so the sender retries; message is generic (no secret/PII).\n return { status: toHttpStatus({ retriable: true }), code: 'INTERNAL', message: 'handler error', retriable: true };\n}\n","import type { Executor } from '../executor.js';\n\n/**\n * Wrap an {@link Executor} as a Fetch-API handler `(Request) => Promise<Response>` — works on Hono,\n * Next.js route handlers, Cloudflare Workers, Deno, and Bun.\n *\n * Reads the RAW body via `req.text()` BEFORE any parsing (verifying a re-stringified parsed body would\n * fail). The signed `path` is `new URL(req.url).pathname + search`; if a reverse proxy rewrites the\n * path, pass `pathOverride` to `createExecutor`.\n */\nexport function toFetchHandler(executor: Executor): (req: Request) => Promise<Response> {\n return async (req: Request): Promise<Response> => {\n const rawBody = await req.text();\n const url = new URL(req.url);\n const res = await executor.handle({\n method: req.method,\n path: url.pathname + url.search,\n headers: req.headers,\n rawBody,\n });\n // 204/205/304 are null-body statuses — passing any body (even '') throws in spec-compliant\n // runtimes, so send `null` when the body is empty.\n return new Response(res.body || null, { status: res.status, headers: { 'content-type': res.contentType } });\n };\n}\n","import type { Executor } from '../executor.js';\n\n/**\n * Minimal structural shapes of Node's `http.IncomingMessage` / `http.ServerResponse` (and Express's\n * `req`/`res`). Declared structurally so the package imports no Node built-ins at runtime.\n */\nexport interface NodeReq extends AsyncIterable<unknown> {\n method?: string;\n url?: string;\n headers: Record<string, string | string[] | undefined>;\n}\nexport interface NodeRes {\n writeHead(status: number, headers?: Record<string, string>): unknown;\n end(chunk?: string): unknown;\n}\n\n/**\n * Wrap an {@link Executor} as a Node/Express handler `(req, res) => Promise<void>`.\n *\n * IMPORTANT: this reads the RAW request stream itself, so it MUST run BEFORE any body parser\n * (`express.json()`, `body-parser`, …). If a parser has already consumed the stream, the raw bytes are\n * gone and verification fails. Mount this on your executor route before such middleware.\n *\n * The signed `path` comes from `req.url` (already `pathname+search`); use `pathOverride` for proxies.\n */\nexport function toNodeHandler(executor: Executor): (req: NodeReq, res: NodeRes) => Promise<void> {\n return async (req: NodeReq, res: NodeRes): Promise<void> => {\n const chunks: Buffer[] = [];\n for await (const chunk of req) {\n chunks.push(typeof chunk === 'string' ? Buffer.from(chunk) : (chunk as Buffer));\n }\n const rawBody = Buffer.concat(chunks).toString('utf8');\n\n const out = await executor.handle({\n method: req.method ?? 'GET',\n path: req.url ?? '/',\n headers: req.headers,\n rawBody,\n });\n res.writeHead(out.status, { 'content-type': out.contentType });\n res.end(out.body);\n };\n}\n","/**\n * Sign a request TO Data — the inverse of the executor's verify side. Use this when an executor POSTs\n * a result back to an inbound webhook on a base (the async write-back leg): the inbound webhook ingest\n * verifies a Signing v1 signature, and this produces it from the inbound webhook's shared secret.\n *\n * Returns the three headers to attach (`X-Data-Signature`, `X-Data-Timestamp`, `X-Data-Id`). The\n * canonical is byte-identical to the verify side (the cross-package conformance vector guards drift).\n */\n\nimport { buildCanonical, SIGNING_SCHEME, unixSeconds } from './canonical.js';\nimport { signPayload } from './hmac.js';\n\nexport interface SignRequestInput {\n /** HMAC shared secret for the target inbound webhook. */\n secret: string;\n /** HTTP method of the request being signed (e.g. 'POST'). */\n method: string;\n /** Full target URL — its `pathname + search` is bound into the signature. */\n url: string;\n /** Raw request body string (default `''`). MUST be the exact bytes sent. */\n body?: string;\n /** Message id → `X-Data-Id`, bound into the signature. Default: a generated UUID. */\n id?: string;\n /** Unix seconds → `X-Data-Timestamp`. Default: now. */\n timestamp?: number;\n}\n\nexport interface SignedHeaders {\n 'X-Data-Signature': string;\n 'X-Data-Timestamp': string;\n 'X-Data-Id': string;\n}\n\n/** Sign a request to Data and return the headers to attach. */\nexport async function signRequest(input: SignRequestInput): Promise<SignedHeaders> {\n const id = input.id ?? crypto.randomUUID();\n const timestamp = input.timestamp ?? unixSeconds();\n const body = input.body ?? '';\n const { pathname, search } = new URL(input.url);\n const hex = await signPayload(input.secret, buildCanonical({ id, timestamp, method: input.method, path: pathname + search, body }));\n return {\n 'X-Data-Signature': `${SIGNING_SCHEME},${hex}`,\n 'X-Data-Timestamp': String(timestamp),\n 'X-Data-Id': id,\n };\n}\n","/**\n * Executor credential pull — the client side of Data's scoped, single-use binary-credential delivery.\n *\n * When a source declares `executor_secrets`, every proxied dispatch to the executor carries\n * `X-Data-Credential-Url`/`-Token`/`-Names`. The executor calls `fetchExecutorCredential` with the\n * INBOUND request to pull a declared credential by name (e.g. an mTLS client cert it must present to a\n * downstream upstream). The grant is single-use per name and short-lived, so fetch each credential ONCE\n * per invocation and reuse the value — a re-pull of the same name fails (the grant is already claimed).\n *\n * The returned `value` is delivered verbatim (binary credentials are stored base64-encoded); decode it\n * per `contentType` (e.g. base64-decode an `application/x-pkcs12` bundle). This helper never decodes.\n *\n * SECURITY: call this ONLY from a handler whose inbound request the SDK has already verified (e.g. inside\n * `createExecutor`'s handler). The grant headers are NOT part of the request's HMAC signature, so anything\n * that can rewrite headers in front of your executor (a TLS-terminating proxy/gateway) could swap the\n * callback URL. Pass `allowedOrigins` (your Data origin, e.g. `['https://data.wayai.pro']`) to reject a\n * rewritten URL — recommended whenever your executor sits behind such an intermediary.\n */\n\nimport { headerGetter } from './headers.js';\nimport { DATA_HEADERS } from './verify.js';\nimport { ExecutorError } from '../errors/envelope.js';\n\n/** A pulled credential. `value` is delivered verbatim (base64 for binary); decode per `contentType`. */\nexport interface ExecutorCredential {\n name: string;\n contentType: string;\n value: string;\n}\n\nexport interface FetchCredentialOptions {\n /** Override the fetch implementation (defaults to the global `fetch`). */\n fetchImpl?: typeof fetch;\n /**\n * If set (and non-empty), the callback URL's origin must be one of these — rejects a URL rewritten in\n * transit (the grant headers are NOT covered by the request signature). Pass your Data origin(s),\n * e.g. `['https://data.wayai.pro']`. Recommended when the executor sits behind a TLS-terminating proxy.\n *\n * Fails closed on an exact origin match: if the Data origin changes and this list still names only\n * the old one, every pull throws `credential_untrusted_url` (never retried). List every origin your\n * deployment may send from, and add a new one BEFORE it starts arriving.\n */\n allowedOrigins?: string[];\n}\n\n/** Anything carrying the inbound request headers: a NormalizedRequest, a Fetch `Request`, or raw headers. */\ntype HeaderSource =\n | Headers\n | Record<string, string | string[] | undefined>\n | { headers: Headers | Record<string, string | string[] | undefined> };\n\nfunction resolveHeaders(source: HeaderSource): Headers | Record<string, string | string[] | undefined> {\n if (source instanceof Headers) return source;\n // A NormalizedRequest / Fetch Request carries headers under `.headers` (a Headers or a record); a bare\n // headers record does not (its `.headers`, if any, would be a string value, not an object) — so guard\n // on the shape rather than `'headers' in source`, which a record with a \"headers\" key would spoof.\n const nested = (source as { headers?: unknown }).headers;\n if (nested instanceof Headers) return nested;\n if (nested && typeof nested === 'object') return nested as Record<string, string | string[] | undefined>;\n return source as Record<string, string | string[] | undefined>;\n}\n\n/**\n * Pull a declared credential by name from Data, using the grant advertised on the inbound dispatch.\n * Throws `ExecutorError` if the request carries no grant, the name isn't in the grant, or the pull is\n * denied/fails. A 403 (grant invalid/expired/already-used) is never retried; a transient 5xx/network\n * error is retried once.\n */\nexport async function fetchExecutorCredential(\n source: HeaderSource,\n name: string,\n opts: FetchCredentialOptions = {},\n): Promise<ExecutorCredential> {\n const get = headerGetter(resolveHeaders(source));\n const url = get(DATA_HEADERS.credentialUrl);\n const token = get(DATA_HEADERS.credentialToken);\n const namesHeader = get(DATA_HEADERS.credentialNames);\n if (!url || !token || namesHeader === undefined) {\n throw new ExecutorError({ code: 'no_credential_grant', message: 'Request carries no executor credential grant', retriable: false });\n }\n // The callback URL is transport-trusted, not signature-bound — pin it when an allowlist is supplied so a\n // rewritten URL can't redirect the single-use grant token to an attacker host.\n if (opts.allowedOrigins && opts.allowedOrigins.length > 0) {\n let origin: string;\n try { origin = new URL(url).origin; } catch { throw new ExecutorError({ code: 'credential_bad_url', message: 'Credential callback URL is not a valid URL', retriable: false }); }\n // Normalize each allowlist entry through URL.origin too, so a caller's trailing slash / explicit\n // default port (e.g. 'https://data.wayai.pro/' or ':443') still matches. A malformed entry keeps its\n // raw form and simply never matches (it can't grant), rather than breaking the whole check.\n const allowed = opts.allowedOrigins.map((o) => { try { return new URL(o).origin; } catch { return o; } });\n if (!allowed.includes(origin)) {\n throw new ExecutorError({ code: 'credential_untrusted_url', message: `Credential callback URL origin '${origin}' is not in allowedOrigins`, retriable: false });\n }\n }\n const names = namesHeader.split(',').map((n) => n.trim()).filter(Boolean);\n if (!names.includes(name)) {\n throw new ExecutorError({ code: 'undeclared_credential', message: `Credential '${name}' is not in the grant (declared: ${names.join(', ') || 'none'})`, retriable: false });\n }\n\n const doFetch = opts.fetchImpl ?? fetch;\n const body = JSON.stringify({ token, name });\n\n // Up to 2 attempts: retry once on a transient 5xx/network error, never on a 403 (single-use — a retry\n // would just hit `already_redeemed`). A 5xx that landed AFTER the grant was claimed surfaces as a clean\n // 403 on retry, which is the correct terminal outcome (the credential is consumed; Data mints a fresh\n // grant on the next dispatch).\n for (let attempt = 0; attempt < 2; attempt++) {\n let res: Response;\n try {\n // `redirect: 'manual'` so a 3xx from the (pinned) URL can't re-POST the token-bearing body to\n // another host — a redirect surfaces as a non-ok status → credential_fetch_failed below.\n res = await doFetch(url, { method: 'POST', headers: { 'content-type': 'application/json' }, body, redirect: 'manual' });\n } catch {\n if (attempt === 0) continue; // network error → retry once\n throw new ExecutorError({ code: 'credential_unreachable', message: 'Credential pull is unreachable', retriable: true });\n }\n\n if (res.status === 403) {\n throw new ExecutorError({ code: 'credential_denied', message: `Credential '${name}' pull was denied (grant invalid, expired, or already used)`, retriable: false, status: 403 });\n }\n if (res.status >= 500) {\n if (attempt === 0) continue; // transient → retry once\n throw new ExecutorError({ code: 'credential_unavailable', message: `Credential pull failed (status ${res.status})`, retriable: true, status: res.status });\n }\n if (!res.ok) {\n throw new ExecutorError({ code: 'credential_fetch_failed', message: `Credential pull failed (status ${res.status})`, retriable: false, status: res.status });\n }\n\n const parsed = (await res.json().catch(() => null)) as { data?: { name?: string; content_type?: string; value?: string } } | null;\n const data = parsed?.data;\n if (!data || typeof data.value !== 'string') {\n throw new ExecutorError({ code: 'credential_malformed', message: 'Credential pull returned an unexpected body', retriable: false });\n }\n return { name: data.name ?? name, contentType: data.content_type ?? 'application/octet-stream', value: data.value };\n }\n\n // Unreachable: the loop returns or throws on every path.\n throw new ExecutorError({ code: 'credential_unavailable', message: 'Credential pull failed', retriable: true });\n}\n"]}
1
+ {"version":3,"sources":["../src/core/hmac.ts","../src/core/headers.ts","../src/core/canonical.ts","../src/core/verify.ts","../src/store/memory.ts","../src/errors/envelope.ts","../src/executor.ts","../src/adapters/fetch.ts","../src/adapters/node.ts","../src/core/sign.ts","../src/core/credential.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * HMAC-SHA256 over Web Crypto — pure, dependency-free, runs in any runtime with `crypto.subtle`\n * (Cloudflare Workers, Deno, Bun, browsers, and Node — see the package's declared `engines`).\n * Ported verbatim from the backend's implementation; the cross-package conformance vector\n * (test/conformance.test.ts) guards against drift.\n */\n\nexport async function signPayload(secret: string, payload: string): Promise<string> {\n const key = await crypto.subtle.importKey(\n 'raw',\n new TextEncoder().encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign'],\n );\n const signature = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(payload));\n return arrayBufferToHex(signature);\n}\n\n/** Constant-time-ish equality of two hex strings (length-checks first, then XOR-accumulate). */\nexport function timingSafeEqual(a: string, b: string): boolean {\n if (a.length !== b.length) return false;\n let result = 0;\n for (let i = 0; i < a.length; i++) result |= a.charCodeAt(i) ^ b.charCodeAt(i);\n return result === 0;\n}\n\nfunction arrayBufferToHex(buffer: ArrayBuffer): string {\n return Array.from(new Uint8Array(buffer))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n","/** Case-insensitive header reader for a Fetch `Headers` or a plain object. A duplicate (array) value is\n * ambiguous and resolves to `undefined` (the signature/timestamp must be unambiguous). Shared by the\n * verify path and the dev-bypass path so they read headers identically. */\nexport type HeaderGetter = (name: string) => string | undefined;\n\nexport function headerGetter(headers: Headers | Record<string, string | string[] | undefined>): HeaderGetter {\n if (headers instanceof Headers) return (name) => headers.get(name) ?? undefined;\n const lower: Record<string, string | string[] | undefined> = {};\n for (const [k, v] of Object.entries(headers)) lower[k.toLowerCase()] = v;\n return (name) => {\n const v = lower[name];\n if (Array.isArray(v)) return v.length === 1 ? v[0] : undefined;\n return v;\n };\n}\n","/**\n * The \"Signing v1\" canonical — the exact string Data HMAC-signs for each request it signs\n * (trigger webhooks holding a secret + signed proxied calls). Ported verbatim from the backend's implementation and\n * kept byte-identical to it by the cross-package conformance vector (test/conformance.test.ts).\n *\n * canonical = [\"v1\", id, String(unixTimestamp), method, path, body].join(\"\\n\")\n *\n * SECURITY INVARIANT: newline-joined; the only data-influenced field, `body`, is LAST, and every\n * other field is provably newline-free (the \"v1\" literal, an integer, a uuid/hex id, an HTTP method\n * token, and a URL pathname+search). The signature binds method + path so it can't be replayed as a\n * different request.\n */\n\nexport const SIGNING_SCHEME = 'v1';\n\nexport interface SignedParts {\n /** Stable message id — `X-Data-Id` (= delivery id on webhooks, idempotency key on proxied writes). */\n id: string;\n /** `X-Data-Timestamp`, unix seconds (integer). */\n timestamp: number;\n /** HTTP method (`POST` for trigger webhooks). */\n method: string;\n /** Request path: `new URL(receiverUrl).pathname + search`. */\n path: string;\n /** Exact raw request body string (`\"\"` when none). */\n body: string;\n}\n\n/** Build the canonical signing string. Component order and the `v1` prefix are part of the wire. */\nexport function buildCanonical(parts: SignedParts): string {\n return [SIGNING_SCHEME, parts.id, String(parts.timestamp), parts.method, parts.path, parts.body].join('\\n');\n}\n\n/** Current unix time in whole seconds (Standard Webhooks convention). `nowMs` override for tests. */\nexport function unixSeconds(nowMs?: number): number {\n return Math.floor((nowMs ?? Date.now()) / 1000);\n}\n","/**\n * Verify a \"Signing v1\" request: HMAC over the canonical, plus timestamp-skew enforcement\n * (the receiver's job — Data only emits). All inputs are the raw request bits the adapter extracts.\n */\n\nimport { signPayload, timingSafeEqual } from './hmac.js';\nimport { headerGetter } from './headers.js';\nimport { SIGNING_SCHEME, buildCanonical, unixSeconds, type SignedParts } from './canonical.js';\n\n/** Lowercased header names Data emits. */\nexport const DATA_HEADERS = {\n signature: 'x-data-signature',\n timestamp: 'x-data-timestamp',\n id: 'x-data-id',\n idempotencyKey: 'x-data-idempotency-key',\n deliveryId: 'x-data-delivery-id',\n // Executor credential pull: advertised on a proxied dispatch to a source declaring executor_secrets.\n credentialUrl: 'x-data-credential-url',\n credentialToken: 'x-data-credential-token',\n credentialNames: 'x-data-credential-names',\n} as const;\n\n/**\n * Max `scheme,hex` candidates we'll HMAC-check from one `X-Data-Signature` header. Data emits one\n * (two during a future key rotation); the cap stops a hostile header with thousands of entries from\n * forcing thousands of HMACs per request.\n */\nexport const MAX_SIG_ENTRIES = 4;\n\n/**\n * Verify a signature header value (`X-Data-Signature`) against the canonical of `parts`. The header\n * is a space-delimited list of `scheme,hex` entries; returns true if any `v1` entry matches (constant-\n * time compare). Malformed/empty/unknown-scheme entries are skipped, never throw. Candidates beyond\n * `MAX_SIG_ENTRIES` are ignored. Does NOT check timestamp skew (see {@link verify}).\n */\nexport async function verifySignatureHeader(secret: string, parts: SignedParts, header: string): Promise<boolean> {\n const canonical = buildCanonical(parts);\n let expected: string | undefined; // loop-invariant HMAC — computed at most once, lazily\n let checked = 0;\n let matched = false;\n for (const entry of header.split(' ')) {\n const comma = entry.indexOf(',');\n if (comma <= 0) continue; // no scheme/hex separator (e.g. \"v1\" or \"\")\n if (entry.slice(0, comma) !== SIGNING_SCHEME) continue;\n const hex = entry.slice(comma + 1);\n if (hex.length === 0) continue;\n if (++checked > MAX_SIG_ENTRIES) break;\n if (expected === undefined) expected = await signPayload(secret, canonical);\n if (timingSafeEqual(expected, hex)) matched = true;\n }\n return matched;\n}\n\nexport type VerifyResult =\n | { ok: true; id: string; timestamp: number; idempotencyKey?: string; deliveryId?: string; body: string }\n | { ok: false; reason: 'missing_signature' | 'invalid_signature' | 'stale_timestamp' | 'malformed' };\n\nexport interface VerifyInput {\n /** The HMAC signing secret shared with Data (the trigger/source signing secret). */\n secret: string;\n /** Request headers — a Fetch `Headers` or a plain object (case-insensitive lookup either way). */\n headers: Headers | Record<string, string | string[] | undefined>;\n /** HTTP method of the incoming request. */\n method: string;\n /** Request path the signature was computed over: `pathname + search` (see README path-matching). */\n path: string;\n /** RAW request body bytes as a string — NEVER a re-stringified parsed object. */\n rawBody: string;\n /** Allowed clock skew in seconds (both directions). A finite value is clamped to ≥1 (never 0);\n * unset / NaN / Infinity fall back to the 300s default (a bad value can't disable the window). */\n toleranceSec?: number;\n /** Override \"now\" in ms (tests). */\n now?: number;\n}\n\n/**\n * Verify an inbound Data request end to end: signature present + valid, and timestamp within the\n * skew window. Returns a discriminated result; never throws on bad input.\n */\nexport async function verify(input: VerifyInput): Promise<VerifyResult> {\n const get = headerGetter(input.headers);\n const signature = get(DATA_HEADERS.signature);\n if (!signature) return { ok: false, reason: 'missing_signature' };\n\n const tsRaw = get(DATA_HEADERS.timestamp);\n const id = get(DATA_HEADERS.id);\n if (!tsRaw || !id) return { ok: false, reason: 'malformed' };\n if (!/^\\d+$/.test(tsRaw)) return { ok: false, reason: 'malformed' };\n const timestamp = Number(tsRaw);\n if (!Number.isFinite(timestamp)) return { ok: false, reason: 'malformed' };\n\n // Non-finite (unset / NaN / Infinity, e.g. `Number(process.env.X)` with X missing) → the 300s\n // default, so a misconfigured tolerance can never silently disable the replay window. A finite value\n // is clamped to ≥1 (never 0). To accept a wider window, pass an explicit large finite number.\n const t = input.toleranceSec;\n const tolerance = Number.isFinite(t) ? Math.max(1, Math.floor(t as number)) : 300;\n const now = unixSeconds(input.now);\n if (Math.abs(now - timestamp) > tolerance) return { ok: false, reason: 'stale_timestamp' };\n\n const parts: SignedParts = { id, timestamp, method: input.method, path: input.path, body: input.rawBody };\n const valid = await verifySignatureHeader(input.secret, parts, signature);\n if (!valid) return { ok: false, reason: 'invalid_signature' };\n\n return {\n ok: true,\n id,\n timestamp,\n idempotencyKey: get(DATA_HEADERS.idempotencyKey),\n deliveryId: get(DATA_HEADERS.deliveryId),\n body: input.rawBody,\n };\n}\n","import type { ClaimResult, IdempotencyStore, StoredResult } from './types.js';\n\ninterface Entry {\n state: 'pending' | 'done';\n result?: StoredResult;\n expiresAt: number; // epoch ms\n}\n\n/**\n * In-memory idempotency store (per-process). Single-threaded JS gives the atomic first-wins claim for\n * free. An accessed key expires lazily; since every delivery has a unique key, completed entries are\n * otherwise dropped by a periodic full sweep (every {@link SWEEP_EVERY} writes) — so the map is bounded\n * by the deliveries within one TTL window (default 24h), not unbounded. For multiple instances, or to\n * cap memory tightly, implement {@link IdempotencyStore} over a shared backend instead.\n */\nconst DEFAULT_SWEEP_EVERY = 1000;\n\nexport class MemoryStore implements IdempotencyStore {\n private readonly map = new Map<string, Entry>();\n private readonly nowMs: () => number;\n private readonly sweepEvery: number;\n private writes = 0;\n\n constructor(opts?: { now?: () => number; sweepEvery?: number }) {\n this.nowMs = opts?.now ?? (() => Date.now());\n this.sweepEvery = opts?.sweepEvery ?? DEFAULT_SWEEP_EVERY;\n }\n\n /** Number of live entries currently held (after lazily ignoring nothing — counts raw map size). */\n get size(): number {\n return this.map.size;\n }\n\n private live(key: string): Entry | undefined {\n const e = this.map.get(key);\n if (!e) return undefined;\n if (e.expiresAt <= this.nowMs()) {\n this.map.delete(key);\n return undefined;\n }\n return e;\n }\n\n /** Amortized O(1)/write: every SWEEP_EVERY writes, drop all expired entries so the map can't grow forever. */\n private maybeSweep(): void {\n if (++this.writes < this.sweepEvery) return;\n this.writes = 0;\n const now = this.nowMs();\n for (const [k, e] of this.map) if (e.expiresAt <= now) this.map.delete(k);\n }\n\n async claim(key: string, ttlSec: number): Promise<ClaimResult> {\n const e = this.live(key);\n if (!e) {\n this.map.set(key, { state: 'pending', expiresAt: this.nowMs() + ttlSec * 1000 });\n this.maybeSweep();\n return { state: 'fresh' };\n }\n if (e.state === 'done' && e.result) return { state: 'done', result: e.result };\n return { state: 'in_progress' };\n }\n\n async complete(key: string, result: StoredResult, ttlSec: number): Promise<void> {\n this.map.set(key, { state: 'done', result, expiresAt: this.nowMs() + ttlSec * 1000 });\n this.maybeSweep();\n }\n\n async fail(key: string): Promise<void> {\n this.map.delete(key);\n }\n}\n","/**\n * The normalized error envelope an executor returns. The HTTP STATUS is the real contract a sender's\n * retry logic reads (2xx ack, 4xx permanent/no-retry, 5xx & 429 retry); the JSON body is for humans.\n */\nexport interface ErrorEnvelope {\n status: number;\n code: string;\n message: string;\n retriable: boolean;\n}\n\n/**\n * Throw this from a handler to fail with a specific code/retriability. `retriable` decides the status\n * family ({@link toHttpStatus}) so the sender retries (5xx/429) or drops (4xx) correctly.\n */\nexport class ExecutorError extends Error {\n readonly code: string;\n readonly retriable: boolean;\n readonly httpStatus?: number;\n\n constructor(args: { code: string; message: string; retriable: boolean; status?: number }) {\n super(args.message);\n this.name = 'ExecutorError';\n this.code = args.code;\n this.retriable = args.retriable;\n this.httpStatus = args.status;\n }\n\n toEnvelope(): ErrorEnvelope {\n return { status: toHttpStatus(this), code: this.code, message: this.message, retriable: this.retriable };\n }\n}\n\n/**\n * Map an error to an HTTP status. An explicit `status` is honored when it agrees with `retriable`\n * (5xx/429 for retriable, 4xx for permanent); otherwise the family default is used: retriable → 503,\n * permanent → 400.\n */\nexport function toHttpStatus(e: { retriable: boolean; httpStatus?: number }): number {\n const s = e.httpStatus;\n if (e.retriable) {\n return s !== undefined && (s === 429 || (s >= 500 && s <= 599)) ? s : 503;\n }\n return s !== undefined && s >= 400 && s <= 499 ? s : 400;\n}\n\n/** Serialize an envelope to a JSON body string. */\nexport function jsonResponseBody(env: ErrorEnvelope): string {\n return JSON.stringify(env);\n}\n","import { verify, DATA_HEADERS, type VerifyResult } from './core/verify.js';\nimport { headerGetter } from './core/headers.js';\nimport { MemoryStore } from './store/memory.js';\nimport type { IdempotencyStore, StoredResult } from './store/types.js';\nimport { ExecutorError, toHttpStatus, jsonResponseBody, type ErrorEnvelope } from './errors/envelope.js';\n\n/** Framework-neutral request the adapters hand to the executor. `rawBody` is the RAW bytes as a string. */\nexport interface NormalizedRequest {\n method: string;\n path: string;\n headers: Headers | Record<string, string | string[] | undefined>;\n rawBody: string;\n}\n\nexport interface NormalizedResponse {\n status: number;\n body: string;\n contentType: string;\n}\n\n/** Verified request context passed to your handler. Never contains the signing secret. */\nexport interface ExecutorContext {\n id: string;\n idempotencyKey?: string;\n deliveryId?: string;\n timestamp: number;\n body: string;\n}\n\n/** Your business logic. Return a `StoredResult` (cached for idempotent replays) or nothing (→ 204). */\nexport type ExecutorHandler = (ctx: ExecutorContext) => Promise<StoredResult | void> | StoredResult | void;\n\nexport interface CreateExecutorOptions {\n /** HMAC signing secret shared with Data. */\n secret: string;\n handler: ExecutorHandler;\n /** Idempotency store. Default: a per-process {@link MemoryStore}. */\n store?: IdempotencyStore;\n /** Clock-skew tolerance (seconds, both directions). Default 300; clamped to ≥1. */\n toleranceSec?: number;\n /** Skip signature/skew verification (LOCAL DEV ONLY). Dedup still applies if headers are present. */\n devBypass?: boolean;\n /** Idempotency record TTL (seconds). Default 86400 (24h). */\n idempotencyTtlSec?: number;\n /** Reports unexpected handler errors. MUST NOT log secrets/PII — it never receives the secret. */\n onError?: (err: unknown, ctx?: ExecutorContext) => void;\n /**\n * Reproduce the path Data SIGNED when a reverse proxy rewrites it. Must return the exact\n * `pathname+search` Data used (see README path-matching), not necessarily the incoming path.\n */\n pathOverride?: (req: NormalizedRequest) => string;\n}\n\nexport interface Executor {\n handle(req: NormalizedRequest): Promise<NormalizedResponse>;\n}\n\nconst JSON_CT = 'application/json';\n\nfunction errorResponse(env: ErrorEnvelope): NormalizedResponse {\n return { status: env.status, body: jsonResponseBody(env), contentType: JSON_CT };\n}\n\nfunction reasonResponse(reason: Exclude<VerifyResult, { ok: true }>['reason']): NormalizedResponse {\n // missing/invalid → 401 (unauthenticated); stale → 403; malformed → 400.\n const status = reason === 'malformed' ? 400 : reason === 'stale_timestamp' ? 403 : 401;\n return errorResponse({ status, code: reason.toUpperCase(), message: `signature ${reason}`, retriable: false });\n}\n\n/** Read the dedupe context from headers without verifying (dev-bypass path). */\nfunction bypassContext(req: NormalizedRequest): ExecutorContext | null {\n const get = headerGetter(req.headers);\n const id = get(DATA_HEADERS.id);\n const tsRaw = get(DATA_HEADERS.timestamp);\n return {\n id: id ?? '',\n idempotencyKey: get(DATA_HEADERS.idempotencyKey),\n deliveryId: get(DATA_HEADERS.deliveryId),\n timestamp: tsRaw && /^\\d+$/.test(tsRaw) ? Number(tsRaw) : 0,\n body: req.rawBody,\n };\n}\n\nexport function createExecutor(opts: CreateExecutorOptions): Executor {\n const store = opts.store ?? new MemoryStore();\n const ttl = opts.idempotencyTtlSec ?? 86_400;\n\n async function run(ctx: ExecutorContext): Promise<NormalizedResponse> {\n // Dedupe key: the logical idempotency key (proxy writes) or the stable message id (webhooks).\n // Absent both (e.g. dev-bypass with no signed headers) → run without dedupe.\n const key = ctx.idempotencyKey || ctx.id;\n if (!key) return await invoke(ctx);\n\n const claim = await store.claim(key, ttl);\n if (claim.state === 'done') {\n return { status: claim.result.status, body: claim.result.body, contentType: claim.result.contentType ?? JSON_CT };\n }\n if (claim.state === 'in_progress') {\n // A concurrent attempt holds this key — tell the sender to retry. Use a retriable (5xx) status,\n // not 409, so it matches `retriable: true` and the sender's retry convention.\n return errorResponse({ status: 503, code: 'IN_PROGRESS', message: 'duplicate delivery in progress', retriable: true });\n }\n try {\n const res = await invoke(ctx);\n await store.complete(key, { status: res.status, body: res.body, contentType: res.contentType }, ttl);\n return res;\n } catch (err) {\n const env = toEnvelope(err);\n const res = errorResponse(env);\n if (env.retriable) {\n await store.fail(key); // retriable: release the key so the retry re-runs (don't cache failures)\n } else {\n // permanent: cache the 4xx so a duplicate delivery replays it instead of re-running.\n await store.complete(key, { status: res.status, body: res.body, contentType: res.contentType }, ttl);\n }\n if (!(err instanceof ExecutorError)) opts.onError?.(err, ctx);\n return res;\n }\n }\n\n async function invoke(ctx: ExecutorContext): Promise<NormalizedResponse> {\n const result = await opts.handler(ctx);\n if (!result) return { status: 204, body: '', contentType: JSON_CT };\n return { status: result.status, body: result.body, contentType: result.contentType ?? JSON_CT };\n }\n\n return {\n async handle(req: NormalizedRequest): Promise<NormalizedResponse> {\n const path = opts.pathOverride ? opts.pathOverride(req) : req.path;\n\n if (opts.devBypass) {\n const ctx = bypassContext(req);\n return ctx ? await run(ctx) : reasonResponse('malformed');\n }\n\n const result = await verify({\n secret: opts.secret,\n headers: req.headers,\n method: req.method,\n path,\n rawBody: req.rawBody,\n toleranceSec: opts.toleranceSec,\n });\n if (!result.ok) return reasonResponse(result.reason);\n\n return await run({\n id: result.id,\n idempotencyKey: result.idempotencyKey,\n deliveryId: result.deliveryId,\n timestamp: result.timestamp,\n body: result.body,\n });\n },\n };\n}\n\nfunction toEnvelope(err: unknown): ErrorEnvelope {\n if (err instanceof ExecutorError) return err.toEnvelope();\n // Unknown error → retriable 500 so the sender retries; message is generic (no secret/PII).\n return { status: toHttpStatus({ retriable: true }), code: 'INTERNAL', message: 'handler error', retriable: true };\n}\n","import type { Executor } from '../executor.js';\n\n/**\n * Wrap an {@link Executor} as a Fetch-API handler `(Request) => Promise<Response>` — works on Hono,\n * Next.js route handlers, Cloudflare Workers, Deno, and Bun.\n *\n * Reads the RAW body via `req.text()` BEFORE any parsing (verifying a re-stringified parsed body would\n * fail). The signed `path` is `new URL(req.url).pathname + search`; if a reverse proxy rewrites the\n * path, pass `pathOverride` to `createExecutor`.\n */\nexport function toFetchHandler(executor: Executor): (req: Request) => Promise<Response> {\n return async (req: Request): Promise<Response> => {\n const rawBody = await req.text();\n const url = new URL(req.url);\n const res = await executor.handle({\n method: req.method,\n path: url.pathname + url.search,\n headers: req.headers,\n rawBody,\n });\n // 204/205/304 are null-body statuses — passing any body (even '') throws in spec-compliant\n // runtimes, so send `null` when the body is empty.\n return new Response(res.body || null, { status: res.status, headers: { 'content-type': res.contentType } });\n };\n}\n","import type { Executor } from '../executor.js';\n\n/**\n * Minimal structural shapes of Node's `http.IncomingMessage` / `http.ServerResponse` (and Express's\n * `req`/`res`). Declared structurally so the package imports no Node built-ins at runtime.\n */\nexport interface NodeReq extends AsyncIterable<unknown> {\n method?: string;\n url?: string;\n headers: Record<string, string | string[] | undefined>;\n}\nexport interface NodeRes {\n writeHead(status: number, headers?: Record<string, string>): unknown;\n end(chunk?: string): unknown;\n}\n\n/**\n * Wrap an {@link Executor} as a Node/Express handler `(req, res) => Promise<void>`.\n *\n * IMPORTANT: this reads the RAW request stream itself, so it MUST run BEFORE any body parser\n * (`express.json()`, `body-parser`, …). If a parser has already consumed the stream, the raw bytes are\n * gone and verification fails. Mount this on your executor route before such middleware.\n *\n * The signed `path` comes from `req.url` (already `pathname+search`); use `pathOverride` for proxies.\n */\nexport function toNodeHandler(executor: Executor): (req: NodeReq, res: NodeRes) => Promise<void> {\n return async (req: NodeReq, res: NodeRes): Promise<void> => {\n const chunks: Buffer[] = [];\n for await (const chunk of req) {\n chunks.push(typeof chunk === 'string' ? Buffer.from(chunk) : (chunk as Buffer));\n }\n const rawBody = Buffer.concat(chunks).toString('utf8');\n\n const out = await executor.handle({\n method: req.method ?? 'GET',\n path: req.url ?? '/',\n headers: req.headers,\n rawBody,\n });\n res.writeHead(out.status, { 'content-type': out.contentType });\n res.end(out.body);\n };\n}\n","/**\n * Sign a request TO Data — the inverse of the executor's verify side. Use this when an executor POSTs\n * a result back to an inbound webhook on a base (the async write-back leg): the inbound webhook ingest\n * verifies a Signing v1 signature, and this produces it from the inbound webhook's shared secret.\n *\n * Returns the three headers to attach (`X-Data-Signature`, `X-Data-Timestamp`, `X-Data-Id`). The\n * canonical is byte-identical to the verify side (the cross-package conformance vector guards drift).\n */\n\nimport { buildCanonical, SIGNING_SCHEME, unixSeconds } from './canonical.js';\nimport { signPayload } from './hmac.js';\n\nexport interface SignRequestInput {\n /** HMAC shared secret for the target inbound webhook. */\n secret: string;\n /** HTTP method of the request being signed (e.g. 'POST'). */\n method: string;\n /** Full target URL — its `pathname + search` is bound into the signature. */\n url: string;\n /** Raw request body string (default `''`). MUST be the exact bytes sent. */\n body?: string;\n /** Message id → `X-Data-Id`, bound into the signature. Default: a generated UUID. */\n id?: string;\n /** Unix seconds → `X-Data-Timestamp`. Default: now. */\n timestamp?: number;\n}\n\nexport interface SignedHeaders {\n 'X-Data-Signature': string;\n 'X-Data-Timestamp': string;\n 'X-Data-Id': string;\n}\n\n/** Sign a request to Data and return the headers to attach. */\nexport async function signRequest(input: SignRequestInput): Promise<SignedHeaders> {\n const id = input.id ?? crypto.randomUUID();\n const timestamp = input.timestamp ?? unixSeconds();\n const body = input.body ?? '';\n const { pathname, search } = new URL(input.url);\n const hex = await signPayload(input.secret, buildCanonical({ id, timestamp, method: input.method, path: pathname + search, body }));\n return {\n 'X-Data-Signature': `${SIGNING_SCHEME},${hex}`,\n 'X-Data-Timestamp': String(timestamp),\n 'X-Data-Id': id,\n };\n}\n","/**\n * Executor credential pull — the client side of Data's scoped, single-use binary-credential delivery.\n *\n * When a source declares `executor_secrets`, every proxied dispatch to the executor carries\n * `X-Data-Credential-Url`/`-Token`/`-Names`. The executor calls `fetchExecutorCredential` with the\n * INBOUND request to pull a declared credential by name (e.g. an mTLS client cert it must present to a\n * downstream upstream). The grant is single-use per name and short-lived, so fetch each credential ONCE\n * per invocation and reuse the value — a re-pull of the same name fails (the grant is already claimed).\n *\n * The returned `value` is delivered verbatim (binary credentials are stored base64-encoded); decode it\n * per `contentType` (e.g. base64-decode an `application/x-pkcs12` bundle). This helper never decodes.\n *\n * SECURITY: call this ONLY from a handler whose inbound request the SDK has already verified (e.g. inside\n * `createExecutor`'s handler). The grant headers are NOT part of the request's HMAC signature, so anything\n * that can rewrite headers in front of your executor (a TLS-terminating proxy/gateway) could swap the\n * callback URL. Pass `allowedOrigins` (your Data origin, e.g. `['https://data.wayai.pro']`) to reject a\n * rewritten URL — recommended whenever your executor sits behind such an intermediary.\n */\n\nimport { headerGetter } from './headers.js';\nimport { DATA_HEADERS } from './verify.js';\nimport { ExecutorError } from '../errors/envelope.js';\n\n/** A pulled credential. `value` is delivered verbatim (base64 for binary); decode per `contentType`. */\nexport interface ExecutorCredential {\n name: string;\n contentType: string;\n value: string;\n}\n\nexport interface FetchCredentialOptions {\n /** Override the fetch implementation (defaults to the global `fetch`). */\n fetchImpl?: typeof fetch;\n /**\n * If set (and non-empty), the callback URL's origin must be one of these — rejects a URL rewritten in\n * transit (the grant headers are NOT covered by the request signature). Pass your Data origin(s),\n * e.g. `['https://data.wayai.pro']`. Recommended when the executor sits behind a TLS-terminating proxy.\n *\n * Fails closed on an exact origin match: if the Data origin changes and this list still names only\n * the old one, every pull throws `credential_untrusted_url` (never retried). List every origin your\n * deployment may send from, and add a new one BEFORE it starts arriving.\n */\n allowedOrigins?: string[];\n}\n\n/** Anything carrying the inbound request headers: a NormalizedRequest, a Fetch `Request`, or raw headers. */\ntype HeaderSource =\n | Headers\n | Record<string, string | string[] | undefined>\n | { headers: Headers | Record<string, string | string[] | undefined> };\n\nfunction resolveHeaders(source: HeaderSource): Headers | Record<string, string | string[] | undefined> {\n if (source instanceof Headers) return source;\n // A NormalizedRequest / Fetch Request carries headers under `.headers` (a Headers or a record); a bare\n // headers record does not (its `.headers`, if any, would be a string value, not an object) — so guard\n // on the shape rather than `'headers' in source`, which a record with a \"headers\" key would spoof.\n const nested = (source as { headers?: unknown }).headers;\n if (nested instanceof Headers) return nested;\n if (nested && typeof nested === 'object') return nested as Record<string, string | string[] | undefined>;\n return source as Record<string, string | string[] | undefined>;\n}\n\n/**\n * Pull a declared credential by name from Data, using the grant advertised on the inbound dispatch.\n * Throws `ExecutorError` if the request carries no grant, the name isn't in the grant, or the pull is\n * denied/fails. A 403 (grant invalid/expired/already-used) is never retried; a transient 5xx/network\n * error is retried once.\n */\nexport async function fetchExecutorCredential(\n source: HeaderSource,\n name: string,\n opts: FetchCredentialOptions = {},\n): Promise<ExecutorCredential> {\n const get = headerGetter(resolveHeaders(source));\n const url = get(DATA_HEADERS.credentialUrl);\n const token = get(DATA_HEADERS.credentialToken);\n const namesHeader = get(DATA_HEADERS.credentialNames);\n if (!url || !token || namesHeader === undefined) {\n throw new ExecutorError({ code: 'no_credential_grant', message: 'Request carries no executor credential grant', retriable: false });\n }\n // The callback URL is transport-trusted, not signature-bound — pin it when an allowlist is supplied so a\n // rewritten URL can't redirect the single-use grant token to an attacker host.\n if (opts.allowedOrigins && opts.allowedOrigins.length > 0) {\n let origin: string;\n try { origin = new URL(url).origin; } catch { throw new ExecutorError({ code: 'credential_bad_url', message: 'Credential callback URL is not a valid URL', retriable: false }); }\n // Normalize each allowlist entry through URL.origin too, so a caller's trailing slash / explicit\n // default port (e.g. 'https://data.wayai.pro/' or ':443') still matches. A malformed entry keeps its\n // raw form and simply never matches (it can't grant), rather than breaking the whole check.\n const allowed = opts.allowedOrigins.map((o) => { try { return new URL(o).origin; } catch { return o; } });\n if (!allowed.includes(origin)) {\n throw new ExecutorError({ code: 'credential_untrusted_url', message: `Credential callback URL origin '${origin}' is not in allowedOrigins`, retriable: false });\n }\n }\n const names = namesHeader.split(',').map((n) => n.trim()).filter(Boolean);\n if (!names.includes(name)) {\n throw new ExecutorError({ code: 'undeclared_credential', message: `Credential '${name}' is not in the grant (declared: ${names.join(', ') || 'none'})`, retriable: false });\n }\n\n const doFetch = opts.fetchImpl ?? fetch;\n const body = JSON.stringify({ token, name });\n\n // Up to 2 attempts: retry once on a transient 5xx/network error, never on a 403 (single-use — a retry\n // would just hit `already_redeemed`). A 5xx that landed AFTER the grant was claimed surfaces as a clean\n // 403 on retry, which is the correct terminal outcome (the credential is consumed; Data mints a fresh\n // grant on the next dispatch).\n for (let attempt = 0; attempt < 2; attempt++) {\n let res: Response;\n try {\n // `redirect: 'manual'` so a 3xx from the (pinned) URL can't re-POST the token-bearing body to\n // another host — a redirect surfaces as a non-ok status → credential_fetch_failed below.\n res = await doFetch(url, { method: 'POST', headers: { 'content-type': 'application/json' }, body, redirect: 'manual' });\n } catch {\n if (attempt === 0) continue; // network error → retry once\n throw new ExecutorError({ code: 'credential_unreachable', message: 'Credential pull is unreachable', retriable: true });\n }\n\n if (res.status === 403) {\n throw new ExecutorError({ code: 'credential_denied', message: `Credential '${name}' pull was denied (grant invalid, expired, or already used)`, retriable: false, status: 403 });\n }\n if (res.status >= 500) {\n if (attempt === 0) continue; // transient → retry once\n throw new ExecutorError({ code: 'credential_unavailable', message: `Credential pull failed (status ${res.status})`, retriable: true, status: res.status });\n }\n if (!res.ok) {\n throw new ExecutorError({ code: 'credential_fetch_failed', message: `Credential pull failed (status ${res.status})`, retriable: false, status: res.status });\n }\n\n const parsed = (await res.json().catch(() => null)) as { data?: { name?: string; content_type?: string; value?: string } } | null;\n const data = parsed?.data;\n if (!data || typeof data.value !== 'string') {\n throw new ExecutorError({ code: 'credential_malformed', message: 'Credential pull returned an unexpected body', retriable: false });\n }\n return { name: data.name ?? name, contentType: data.content_type ?? 'application/octet-stream', value: data.value };\n }\n\n // Unreachable: the loop returns or throws on every path.\n throw new ExecutorError({ code: 'credential_unavailable', message: 'Credential pull failed', retriable: true });\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -108,8 +108,8 @@ interface NodeRes {
108
108
  declare function toNodeHandler(executor: Executor): (req: NodeReq, res: NodeRes) => Promise<void>;
109
109
 
110
110
  /**
111
- * The "Signing v1" canonical — the exact string Data HMAC-signs for every request it sends
112
- * (trigger webhooks + signed proxied calls). Ported verbatim from the backend's implementation and
111
+ * The "Signing v1" canonical — the exact string Data HMAC-signs for each request it signs
112
+ * (trigger webhooks holding a secret + signed proxied calls). Ported verbatim from the backend's implementation and
113
113
  * kept byte-identical to it by the cross-package conformance vector (test/conformance.test.ts).
114
114
  *
115
115
  * canonical = ["v1", id, String(unixTimestamp), method, path, body].join("\n")
package/dist/index.d.ts CHANGED
@@ -108,8 +108,8 @@ interface NodeRes {
108
108
  declare function toNodeHandler(executor: Executor): (req: NodeReq, res: NodeRes) => Promise<void>;
109
109
 
110
110
  /**
111
- * The "Signing v1" canonical — the exact string Data HMAC-signs for every request it sends
112
- * (trigger webhooks + signed proxied calls). Ported verbatim from the backend's implementation and
111
+ * The "Signing v1" canonical — the exact string Data HMAC-signs for each request it signs
112
+ * (trigger webhooks holding a secret + signed proxied calls). Ported verbatim from the backend's implementation and
113
113
  * kept byte-identical to it by the cross-package conformance vector (test/conformance.test.ts).
114
114
  *
115
115
  * canonical = ["v1", id, String(unixTimestamp), method, path, body].join("\n")
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/hmac.ts","../src/core/headers.ts","../src/core/canonical.ts","../src/core/verify.ts","../src/store/memory.ts","../src/errors/envelope.ts","../src/executor.ts","../src/adapters/fetch.ts","../src/adapters/node.ts","../src/core/sign.ts","../src/core/credential.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * HMAC-SHA256 over Web Crypto — pure, dependency-free, runs in any runtime with `crypto.subtle`\n * (Cloudflare Workers, Deno, Bun, browsers, and Node — see the package's declared `engines`).\n * Ported verbatim from the backend's implementation; the cross-package conformance vector\n * (test/conformance.test.ts) guards against drift.\n */\n\nexport async function signPayload(secret: string, payload: string): Promise<string> {\n const key = await crypto.subtle.importKey(\n 'raw',\n new TextEncoder().encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign'],\n );\n const signature = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(payload));\n return arrayBufferToHex(signature);\n}\n\n/** Constant-time-ish equality of two hex strings (length-checks first, then XOR-accumulate). */\nexport function timingSafeEqual(a: string, b: string): boolean {\n if (a.length !== b.length) return false;\n let result = 0;\n for (let i = 0; i < a.length; i++) result |= a.charCodeAt(i) ^ b.charCodeAt(i);\n return result === 0;\n}\n\nfunction arrayBufferToHex(buffer: ArrayBuffer): string {\n return Array.from(new Uint8Array(buffer))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n","/** Case-insensitive header reader for a Fetch `Headers` or a plain object. A duplicate (array) value is\n * ambiguous and resolves to `undefined` (the signature/timestamp must be unambiguous). Shared by the\n * verify path and the dev-bypass path so they read headers identically. */\nexport type HeaderGetter = (name: string) => string | undefined;\n\nexport function headerGetter(headers: Headers | Record<string, string | string[] | undefined>): HeaderGetter {\n if (headers instanceof Headers) return (name) => headers.get(name) ?? undefined;\n const lower: Record<string, string | string[] | undefined> = {};\n for (const [k, v] of Object.entries(headers)) lower[k.toLowerCase()] = v;\n return (name) => {\n const v = lower[name];\n if (Array.isArray(v)) return v.length === 1 ? v[0] : undefined;\n return v;\n };\n}\n","/**\n * The \"Signing v1\" canonical — the exact string Data HMAC-signs for every request it sends\n * (trigger webhooks + signed proxied calls). Ported verbatim from the backend's implementation and\n * kept byte-identical to it by the cross-package conformance vector (test/conformance.test.ts).\n *\n * canonical = [\"v1\", id, String(unixTimestamp), method, path, body].join(\"\\n\")\n *\n * SECURITY INVARIANT: newline-joined; the only data-influenced field, `body`, is LAST, and every\n * other field is provably newline-free (the \"v1\" literal, an integer, a uuid/hex id, an HTTP method\n * token, and a URL pathname+search). The signature binds method + path so it can't be replayed as a\n * different request.\n */\n\nexport const SIGNING_SCHEME = 'v1';\n\nexport interface SignedParts {\n /** Stable message id — `X-Data-Id` (= delivery id on webhooks, idempotency key on proxied writes). */\n id: string;\n /** `X-Data-Timestamp`, unix seconds (integer). */\n timestamp: number;\n /** HTTP method (`POST` for trigger webhooks). */\n method: string;\n /** Request path: `new URL(receiverUrl).pathname + search`. */\n path: string;\n /** Exact raw request body string (`\"\"` when none). */\n body: string;\n}\n\n/** Build the canonical signing string. Component order and the `v1` prefix are part of the wire. */\nexport function buildCanonical(parts: SignedParts): string {\n return [SIGNING_SCHEME, parts.id, String(parts.timestamp), parts.method, parts.path, parts.body].join('\\n');\n}\n\n/** Current unix time in whole seconds (Standard Webhooks convention). `nowMs` override for tests. */\nexport function unixSeconds(nowMs?: number): number {\n return Math.floor((nowMs ?? Date.now()) / 1000);\n}\n","/**\n * Verify a \"Signing v1\" request: HMAC over the canonical, plus timestamp-skew enforcement\n * (the receiver's job — Data only emits). All inputs are the raw request bits the adapter extracts.\n */\n\nimport { signPayload, timingSafeEqual } from './hmac.js';\nimport { headerGetter } from './headers.js';\nimport { SIGNING_SCHEME, buildCanonical, unixSeconds, type SignedParts } from './canonical.js';\n\n/** Lowercased header names Data emits. */\nexport const DATA_HEADERS = {\n signature: 'x-data-signature',\n timestamp: 'x-data-timestamp',\n id: 'x-data-id',\n idempotencyKey: 'x-data-idempotency-key',\n deliveryId: 'x-data-delivery-id',\n // Executor credential pull: advertised on a proxied dispatch to a source declaring executor_secrets.\n credentialUrl: 'x-data-credential-url',\n credentialToken: 'x-data-credential-token',\n credentialNames: 'x-data-credential-names',\n} as const;\n\n/**\n * Max `scheme,hex` candidates we'll HMAC-check from one `X-Data-Signature` header. Data emits one\n * (two during a future key rotation); the cap stops a hostile header with thousands of entries from\n * forcing thousands of HMACs per request.\n */\nexport const MAX_SIG_ENTRIES = 4;\n\n/**\n * Verify a signature header value (`X-Data-Signature`) against the canonical of `parts`. The header\n * is a space-delimited list of `scheme,hex` entries; returns true if any `v1` entry matches (constant-\n * time compare). Malformed/empty/unknown-scheme entries are skipped, never throw. Candidates beyond\n * `MAX_SIG_ENTRIES` are ignored. Does NOT check timestamp skew (see {@link verify}).\n */\nexport async function verifySignatureHeader(secret: string, parts: SignedParts, header: string): Promise<boolean> {\n const canonical = buildCanonical(parts);\n let expected: string | undefined; // loop-invariant HMAC — computed at most once, lazily\n let checked = 0;\n let matched = false;\n for (const entry of header.split(' ')) {\n const comma = entry.indexOf(',');\n if (comma <= 0) continue; // no scheme/hex separator (e.g. \"v1\" or \"\")\n if (entry.slice(0, comma) !== SIGNING_SCHEME) continue;\n const hex = entry.slice(comma + 1);\n if (hex.length === 0) continue;\n if (++checked > MAX_SIG_ENTRIES) break;\n if (expected === undefined) expected = await signPayload(secret, canonical);\n if (timingSafeEqual(expected, hex)) matched = true;\n }\n return matched;\n}\n\nexport type VerifyResult =\n | { ok: true; id: string; timestamp: number; idempotencyKey?: string; deliveryId?: string; body: string }\n | { ok: false; reason: 'missing_signature' | 'invalid_signature' | 'stale_timestamp' | 'malformed' };\n\nexport interface VerifyInput {\n /** The HMAC signing secret shared with Data (the trigger/source signing secret). */\n secret: string;\n /** Request headers — a Fetch `Headers` or a plain object (case-insensitive lookup either way). */\n headers: Headers | Record<string, string | string[] | undefined>;\n /** HTTP method of the incoming request. */\n method: string;\n /** Request path the signature was computed over: `pathname + search` (see README path-matching). */\n path: string;\n /** RAW request body bytes as a string — NEVER a re-stringified parsed object. */\n rawBody: string;\n /** Allowed clock skew in seconds (both directions). A finite value is clamped to ≥1 (never 0);\n * unset / NaN / Infinity fall back to the 300s default (a bad value can't disable the window). */\n toleranceSec?: number;\n /** Override \"now\" in ms (tests). */\n now?: number;\n}\n\n/**\n * Verify an inbound Data request end to end: signature present + valid, and timestamp within the\n * skew window. Returns a discriminated result; never throws on bad input.\n */\nexport async function verify(input: VerifyInput): Promise<VerifyResult> {\n const get = headerGetter(input.headers);\n const signature = get(DATA_HEADERS.signature);\n if (!signature) return { ok: false, reason: 'missing_signature' };\n\n const tsRaw = get(DATA_HEADERS.timestamp);\n const id = get(DATA_HEADERS.id);\n if (!tsRaw || !id) return { ok: false, reason: 'malformed' };\n if (!/^\\d+$/.test(tsRaw)) return { ok: false, reason: 'malformed' };\n const timestamp = Number(tsRaw);\n if (!Number.isFinite(timestamp)) return { ok: false, reason: 'malformed' };\n\n // Non-finite (unset / NaN / Infinity, e.g. `Number(process.env.X)` with X missing) → the 300s\n // default, so a misconfigured tolerance can never silently disable the replay window. A finite value\n // is clamped to ≥1 (never 0). To accept a wider window, pass an explicit large finite number.\n const t = input.toleranceSec;\n const tolerance = Number.isFinite(t) ? Math.max(1, Math.floor(t as number)) : 300;\n const now = unixSeconds(input.now);\n if (Math.abs(now - timestamp) > tolerance) return { ok: false, reason: 'stale_timestamp' };\n\n const parts: SignedParts = { id, timestamp, method: input.method, path: input.path, body: input.rawBody };\n const valid = await verifySignatureHeader(input.secret, parts, signature);\n if (!valid) return { ok: false, reason: 'invalid_signature' };\n\n return {\n ok: true,\n id,\n timestamp,\n idempotencyKey: get(DATA_HEADERS.idempotencyKey),\n deliveryId: get(DATA_HEADERS.deliveryId),\n body: input.rawBody,\n };\n}\n","import type { ClaimResult, IdempotencyStore, StoredResult } from './types.js';\n\ninterface Entry {\n state: 'pending' | 'done';\n result?: StoredResult;\n expiresAt: number; // epoch ms\n}\n\n/**\n * In-memory idempotency store (per-process). Single-threaded JS gives the atomic first-wins claim for\n * free. An accessed key expires lazily; since every delivery has a unique key, completed entries are\n * otherwise dropped by a periodic full sweep (every {@link SWEEP_EVERY} writes) — so the map is bounded\n * by the deliveries within one TTL window (default 24h), not unbounded. For multiple instances, or to\n * cap memory tightly, implement {@link IdempotencyStore} over a shared backend instead.\n */\nconst DEFAULT_SWEEP_EVERY = 1000;\n\nexport class MemoryStore implements IdempotencyStore {\n private readonly map = new Map<string, Entry>();\n private readonly nowMs: () => number;\n private readonly sweepEvery: number;\n private writes = 0;\n\n constructor(opts?: { now?: () => number; sweepEvery?: number }) {\n this.nowMs = opts?.now ?? (() => Date.now());\n this.sweepEvery = opts?.sweepEvery ?? DEFAULT_SWEEP_EVERY;\n }\n\n /** Number of live entries currently held (after lazily ignoring nothing — counts raw map size). */\n get size(): number {\n return this.map.size;\n }\n\n private live(key: string): Entry | undefined {\n const e = this.map.get(key);\n if (!e) return undefined;\n if (e.expiresAt <= this.nowMs()) {\n this.map.delete(key);\n return undefined;\n }\n return e;\n }\n\n /** Amortized O(1)/write: every SWEEP_EVERY writes, drop all expired entries so the map can't grow forever. */\n private maybeSweep(): void {\n if (++this.writes < this.sweepEvery) return;\n this.writes = 0;\n const now = this.nowMs();\n for (const [k, e] of this.map) if (e.expiresAt <= now) this.map.delete(k);\n }\n\n async claim(key: string, ttlSec: number): Promise<ClaimResult> {\n const e = this.live(key);\n if (!e) {\n this.map.set(key, { state: 'pending', expiresAt: this.nowMs() + ttlSec * 1000 });\n this.maybeSweep();\n return { state: 'fresh' };\n }\n if (e.state === 'done' && e.result) return { state: 'done', result: e.result };\n return { state: 'in_progress' };\n }\n\n async complete(key: string, result: StoredResult, ttlSec: number): Promise<void> {\n this.map.set(key, { state: 'done', result, expiresAt: this.nowMs() + ttlSec * 1000 });\n this.maybeSweep();\n }\n\n async fail(key: string): Promise<void> {\n this.map.delete(key);\n }\n}\n","/**\n * The normalized error envelope an executor returns. The HTTP STATUS is the real contract a sender's\n * retry logic reads (2xx ack, 4xx permanent/no-retry, 5xx & 429 retry); the JSON body is for humans.\n */\nexport interface ErrorEnvelope {\n status: number;\n code: string;\n message: string;\n retriable: boolean;\n}\n\n/**\n * Throw this from a handler to fail with a specific code/retriability. `retriable` decides the status\n * family ({@link toHttpStatus}) so the sender retries (5xx/429) or drops (4xx) correctly.\n */\nexport class ExecutorError extends Error {\n readonly code: string;\n readonly retriable: boolean;\n readonly httpStatus?: number;\n\n constructor(args: { code: string; message: string; retriable: boolean; status?: number }) {\n super(args.message);\n this.name = 'ExecutorError';\n this.code = args.code;\n this.retriable = args.retriable;\n this.httpStatus = args.status;\n }\n\n toEnvelope(): ErrorEnvelope {\n return { status: toHttpStatus(this), code: this.code, message: this.message, retriable: this.retriable };\n }\n}\n\n/**\n * Map an error to an HTTP status. An explicit `status` is honored when it agrees with `retriable`\n * (5xx/429 for retriable, 4xx for permanent); otherwise the family default is used: retriable → 503,\n * permanent → 400.\n */\nexport function toHttpStatus(e: { retriable: boolean; httpStatus?: number }): number {\n const s = e.httpStatus;\n if (e.retriable) {\n return s !== undefined && (s === 429 || (s >= 500 && s <= 599)) ? s : 503;\n }\n return s !== undefined && s >= 400 && s <= 499 ? s : 400;\n}\n\n/** Serialize an envelope to a JSON body string. */\nexport function jsonResponseBody(env: ErrorEnvelope): string {\n return JSON.stringify(env);\n}\n","import { verify, DATA_HEADERS, type VerifyResult } from './core/verify.js';\nimport { headerGetter } from './core/headers.js';\nimport { MemoryStore } from './store/memory.js';\nimport type { IdempotencyStore, StoredResult } from './store/types.js';\nimport { ExecutorError, toHttpStatus, jsonResponseBody, type ErrorEnvelope } from './errors/envelope.js';\n\n/** Framework-neutral request the adapters hand to the executor. `rawBody` is the RAW bytes as a string. */\nexport interface NormalizedRequest {\n method: string;\n path: string;\n headers: Headers | Record<string, string | string[] | undefined>;\n rawBody: string;\n}\n\nexport interface NormalizedResponse {\n status: number;\n body: string;\n contentType: string;\n}\n\n/** Verified request context passed to your handler. Never contains the signing secret. */\nexport interface ExecutorContext {\n id: string;\n idempotencyKey?: string;\n deliveryId?: string;\n timestamp: number;\n body: string;\n}\n\n/** Your business logic. Return a `StoredResult` (cached for idempotent replays) or nothing (→ 204). */\nexport type ExecutorHandler = (ctx: ExecutorContext) => Promise<StoredResult | void> | StoredResult | void;\n\nexport interface CreateExecutorOptions {\n /** HMAC signing secret shared with Data. */\n secret: string;\n handler: ExecutorHandler;\n /** Idempotency store. Default: a per-process {@link MemoryStore}. */\n store?: IdempotencyStore;\n /** Clock-skew tolerance (seconds, both directions). Default 300; clamped to ≥1. */\n toleranceSec?: number;\n /** Skip signature/skew verification (LOCAL DEV ONLY). Dedup still applies if headers are present. */\n devBypass?: boolean;\n /** Idempotency record TTL (seconds). Default 86400 (24h). */\n idempotencyTtlSec?: number;\n /** Reports unexpected handler errors. MUST NOT log secrets/PII — it never receives the secret. */\n onError?: (err: unknown, ctx?: ExecutorContext) => void;\n /**\n * Reproduce the path Data SIGNED when a reverse proxy rewrites it. Must return the exact\n * `pathname+search` Data used (see README path-matching), not necessarily the incoming path.\n */\n pathOverride?: (req: NormalizedRequest) => string;\n}\n\nexport interface Executor {\n handle(req: NormalizedRequest): Promise<NormalizedResponse>;\n}\n\nconst JSON_CT = 'application/json';\n\nfunction errorResponse(env: ErrorEnvelope): NormalizedResponse {\n return { status: env.status, body: jsonResponseBody(env), contentType: JSON_CT };\n}\n\nfunction reasonResponse(reason: Exclude<VerifyResult, { ok: true }>['reason']): NormalizedResponse {\n // missing/invalid → 401 (unauthenticated); stale → 403; malformed → 400.\n const status = reason === 'malformed' ? 400 : reason === 'stale_timestamp' ? 403 : 401;\n return errorResponse({ status, code: reason.toUpperCase(), message: `signature ${reason}`, retriable: false });\n}\n\n/** Read the dedupe context from headers without verifying (dev-bypass path). */\nfunction bypassContext(req: NormalizedRequest): ExecutorContext | null {\n const get = headerGetter(req.headers);\n const id = get(DATA_HEADERS.id);\n const tsRaw = get(DATA_HEADERS.timestamp);\n return {\n id: id ?? '',\n idempotencyKey: get(DATA_HEADERS.idempotencyKey),\n deliveryId: get(DATA_HEADERS.deliveryId),\n timestamp: tsRaw && /^\\d+$/.test(tsRaw) ? Number(tsRaw) : 0,\n body: req.rawBody,\n };\n}\n\nexport function createExecutor(opts: CreateExecutorOptions): Executor {\n const store = opts.store ?? new MemoryStore();\n const ttl = opts.idempotencyTtlSec ?? 86_400;\n\n async function run(ctx: ExecutorContext): Promise<NormalizedResponse> {\n // Dedupe key: the logical idempotency key (proxy writes) or the stable message id (webhooks).\n // Absent both (e.g. dev-bypass with no signed headers) → run without dedupe.\n const key = ctx.idempotencyKey || ctx.id;\n if (!key) return await invoke(ctx);\n\n const claim = await store.claim(key, ttl);\n if (claim.state === 'done') {\n return { status: claim.result.status, body: claim.result.body, contentType: claim.result.contentType ?? JSON_CT };\n }\n if (claim.state === 'in_progress') {\n // A concurrent attempt holds this key — tell the sender to retry. Use a retriable (5xx) status,\n // not 409, so it matches `retriable: true` and the sender's retry convention.\n return errorResponse({ status: 503, code: 'IN_PROGRESS', message: 'duplicate delivery in progress', retriable: true });\n }\n try {\n const res = await invoke(ctx);\n await store.complete(key, { status: res.status, body: res.body, contentType: res.contentType }, ttl);\n return res;\n } catch (err) {\n const env = toEnvelope(err);\n const res = errorResponse(env);\n if (env.retriable) {\n await store.fail(key); // retriable: release the key so the retry re-runs (don't cache failures)\n } else {\n // permanent: cache the 4xx so a duplicate delivery replays it instead of re-running.\n await store.complete(key, { status: res.status, body: res.body, contentType: res.contentType }, ttl);\n }\n if (!(err instanceof ExecutorError)) opts.onError?.(err, ctx);\n return res;\n }\n }\n\n async function invoke(ctx: ExecutorContext): Promise<NormalizedResponse> {\n const result = await opts.handler(ctx);\n if (!result) return { status: 204, body: '', contentType: JSON_CT };\n return { status: result.status, body: result.body, contentType: result.contentType ?? JSON_CT };\n }\n\n return {\n async handle(req: NormalizedRequest): Promise<NormalizedResponse> {\n const path = opts.pathOverride ? opts.pathOverride(req) : req.path;\n\n if (opts.devBypass) {\n const ctx = bypassContext(req);\n return ctx ? await run(ctx) : reasonResponse('malformed');\n }\n\n const result = await verify({\n secret: opts.secret,\n headers: req.headers,\n method: req.method,\n path,\n rawBody: req.rawBody,\n toleranceSec: opts.toleranceSec,\n });\n if (!result.ok) return reasonResponse(result.reason);\n\n return await run({\n id: result.id,\n idempotencyKey: result.idempotencyKey,\n deliveryId: result.deliveryId,\n timestamp: result.timestamp,\n body: result.body,\n });\n },\n };\n}\n\nfunction toEnvelope(err: unknown): ErrorEnvelope {\n if (err instanceof ExecutorError) return err.toEnvelope();\n // Unknown error → retriable 500 so the sender retries; message is generic (no secret/PII).\n return { status: toHttpStatus({ retriable: true }), code: 'INTERNAL', message: 'handler error', retriable: true };\n}\n","import type { Executor } from '../executor.js';\n\n/**\n * Wrap an {@link Executor} as a Fetch-API handler `(Request) => Promise<Response>` — works on Hono,\n * Next.js route handlers, Cloudflare Workers, Deno, and Bun.\n *\n * Reads the RAW body via `req.text()` BEFORE any parsing (verifying a re-stringified parsed body would\n * fail). The signed `path` is `new URL(req.url).pathname + search`; if a reverse proxy rewrites the\n * path, pass `pathOverride` to `createExecutor`.\n */\nexport function toFetchHandler(executor: Executor): (req: Request) => Promise<Response> {\n return async (req: Request): Promise<Response> => {\n const rawBody = await req.text();\n const url = new URL(req.url);\n const res = await executor.handle({\n method: req.method,\n path: url.pathname + url.search,\n headers: req.headers,\n rawBody,\n });\n // 204/205/304 are null-body statuses — passing any body (even '') throws in spec-compliant\n // runtimes, so send `null` when the body is empty.\n return new Response(res.body || null, { status: res.status, headers: { 'content-type': res.contentType } });\n };\n}\n","import type { Executor } from '../executor.js';\n\n/**\n * Minimal structural shapes of Node's `http.IncomingMessage` / `http.ServerResponse` (and Express's\n * `req`/`res`). Declared structurally so the package imports no Node built-ins at runtime.\n */\nexport interface NodeReq extends AsyncIterable<unknown> {\n method?: string;\n url?: string;\n headers: Record<string, string | string[] | undefined>;\n}\nexport interface NodeRes {\n writeHead(status: number, headers?: Record<string, string>): unknown;\n end(chunk?: string): unknown;\n}\n\n/**\n * Wrap an {@link Executor} as a Node/Express handler `(req, res) => Promise<void>`.\n *\n * IMPORTANT: this reads the RAW request stream itself, so it MUST run BEFORE any body parser\n * (`express.json()`, `body-parser`, …). If a parser has already consumed the stream, the raw bytes are\n * gone and verification fails. Mount this on your executor route before such middleware.\n *\n * The signed `path` comes from `req.url` (already `pathname+search`); use `pathOverride` for proxies.\n */\nexport function toNodeHandler(executor: Executor): (req: NodeReq, res: NodeRes) => Promise<void> {\n return async (req: NodeReq, res: NodeRes): Promise<void> => {\n const chunks: Buffer[] = [];\n for await (const chunk of req) {\n chunks.push(typeof chunk === 'string' ? Buffer.from(chunk) : (chunk as Buffer));\n }\n const rawBody = Buffer.concat(chunks).toString('utf8');\n\n const out = await executor.handle({\n method: req.method ?? 'GET',\n path: req.url ?? '/',\n headers: req.headers,\n rawBody,\n });\n res.writeHead(out.status, { 'content-type': out.contentType });\n res.end(out.body);\n };\n}\n","/**\n * Sign a request TO Data — the inverse of the executor's verify side. Use this when an executor POSTs\n * a result back to an inbound webhook on a base (the async write-back leg): the inbound webhook ingest\n * verifies a Signing v1 signature, and this produces it from the inbound webhook's shared secret.\n *\n * Returns the three headers to attach (`X-Data-Signature`, `X-Data-Timestamp`, `X-Data-Id`). The\n * canonical is byte-identical to the verify side (the cross-package conformance vector guards drift).\n */\n\nimport { buildCanonical, SIGNING_SCHEME, unixSeconds } from './canonical.js';\nimport { signPayload } from './hmac.js';\n\nexport interface SignRequestInput {\n /** HMAC shared secret for the target inbound webhook. */\n secret: string;\n /** HTTP method of the request being signed (e.g. 'POST'). */\n method: string;\n /** Full target URL — its `pathname + search` is bound into the signature. */\n url: string;\n /** Raw request body string (default `''`). MUST be the exact bytes sent. */\n body?: string;\n /** Message id → `X-Data-Id`, bound into the signature. Default: a generated UUID. */\n id?: string;\n /** Unix seconds → `X-Data-Timestamp`. Default: now. */\n timestamp?: number;\n}\n\nexport interface SignedHeaders {\n 'X-Data-Signature': string;\n 'X-Data-Timestamp': string;\n 'X-Data-Id': string;\n}\n\n/** Sign a request to Data and return the headers to attach. */\nexport async function signRequest(input: SignRequestInput): Promise<SignedHeaders> {\n const id = input.id ?? crypto.randomUUID();\n const timestamp = input.timestamp ?? unixSeconds();\n const body = input.body ?? '';\n const { pathname, search } = new URL(input.url);\n const hex = await signPayload(input.secret, buildCanonical({ id, timestamp, method: input.method, path: pathname + search, body }));\n return {\n 'X-Data-Signature': `${SIGNING_SCHEME},${hex}`,\n 'X-Data-Timestamp': String(timestamp),\n 'X-Data-Id': id,\n };\n}\n","/**\n * Executor credential pull — the client side of Data's scoped, single-use binary-credential delivery.\n *\n * When a source declares `executor_secrets`, every proxied dispatch to the executor carries\n * `X-Data-Credential-Url`/`-Token`/`-Names`. The executor calls `fetchExecutorCredential` with the\n * INBOUND request to pull a declared credential by name (e.g. an mTLS client cert it must present to a\n * downstream upstream). The grant is single-use per name and short-lived, so fetch each credential ONCE\n * per invocation and reuse the value — a re-pull of the same name fails (the grant is already claimed).\n *\n * The returned `value` is delivered verbatim (binary credentials are stored base64-encoded); decode it\n * per `contentType` (e.g. base64-decode an `application/x-pkcs12` bundle). This helper never decodes.\n *\n * SECURITY: call this ONLY from a handler whose inbound request the SDK has already verified (e.g. inside\n * `createExecutor`'s handler). The grant headers are NOT part of the request's HMAC signature, so anything\n * that can rewrite headers in front of your executor (a TLS-terminating proxy/gateway) could swap the\n * callback URL. Pass `allowedOrigins` (your Data origin, e.g. `['https://data.wayai.pro']`) to reject a\n * rewritten URL — recommended whenever your executor sits behind such an intermediary.\n */\n\nimport { headerGetter } from './headers.js';\nimport { DATA_HEADERS } from './verify.js';\nimport { ExecutorError } from '../errors/envelope.js';\n\n/** A pulled credential. `value` is delivered verbatim (base64 for binary); decode per `contentType`. */\nexport interface ExecutorCredential {\n name: string;\n contentType: string;\n value: string;\n}\n\nexport interface FetchCredentialOptions {\n /** Override the fetch implementation (defaults to the global `fetch`). */\n fetchImpl?: typeof fetch;\n /**\n * If set (and non-empty), the callback URL's origin must be one of these — rejects a URL rewritten in\n * transit (the grant headers are NOT covered by the request signature). Pass your Data origin(s),\n * e.g. `['https://data.wayai.pro']`. Recommended when the executor sits behind a TLS-terminating proxy.\n *\n * Fails closed on an exact origin match: if the Data origin changes and this list still names only\n * the old one, every pull throws `credential_untrusted_url` (never retried). List every origin your\n * deployment may send from, and add a new one BEFORE it starts arriving.\n */\n allowedOrigins?: string[];\n}\n\n/** Anything carrying the inbound request headers: a NormalizedRequest, a Fetch `Request`, or raw headers. */\ntype HeaderSource =\n | Headers\n | Record<string, string | string[] | undefined>\n | { headers: Headers | Record<string, string | string[] | undefined> };\n\nfunction resolveHeaders(source: HeaderSource): Headers | Record<string, string | string[] | undefined> {\n if (source instanceof Headers) return source;\n // A NormalizedRequest / Fetch Request carries headers under `.headers` (a Headers or a record); a bare\n // headers record does not (its `.headers`, if any, would be a string value, not an object) — so guard\n // on the shape rather than `'headers' in source`, which a record with a \"headers\" key would spoof.\n const nested = (source as { headers?: unknown }).headers;\n if (nested instanceof Headers) return nested;\n if (nested && typeof nested === 'object') return nested as Record<string, string | string[] | undefined>;\n return source as Record<string, string | string[] | undefined>;\n}\n\n/**\n * Pull a declared credential by name from Data, using the grant advertised on the inbound dispatch.\n * Throws `ExecutorError` if the request carries no grant, the name isn't in the grant, or the pull is\n * denied/fails. A 403 (grant invalid/expired/already-used) is never retried; a transient 5xx/network\n * error is retried once.\n */\nexport async function fetchExecutorCredential(\n source: HeaderSource,\n name: string,\n opts: FetchCredentialOptions = {},\n): Promise<ExecutorCredential> {\n const get = headerGetter(resolveHeaders(source));\n const url = get(DATA_HEADERS.credentialUrl);\n const token = get(DATA_HEADERS.credentialToken);\n const namesHeader = get(DATA_HEADERS.credentialNames);\n if (!url || !token || namesHeader === undefined) {\n throw new ExecutorError({ code: 'no_credential_grant', message: 'Request carries no executor credential grant', retriable: false });\n }\n // The callback URL is transport-trusted, not signature-bound — pin it when an allowlist is supplied so a\n // rewritten URL can't redirect the single-use grant token to an attacker host.\n if (opts.allowedOrigins && opts.allowedOrigins.length > 0) {\n let origin: string;\n try { origin = new URL(url).origin; } catch { throw new ExecutorError({ code: 'credential_bad_url', message: 'Credential callback URL is not a valid URL', retriable: false }); }\n // Normalize each allowlist entry through URL.origin too, so a caller's trailing slash / explicit\n // default port (e.g. 'https://data.wayai.pro/' or ':443') still matches. A malformed entry keeps its\n // raw form and simply never matches (it can't grant), rather than breaking the whole check.\n const allowed = opts.allowedOrigins.map((o) => { try { return new URL(o).origin; } catch { return o; } });\n if (!allowed.includes(origin)) {\n throw new ExecutorError({ code: 'credential_untrusted_url', message: `Credential callback URL origin '${origin}' is not in allowedOrigins`, retriable: false });\n }\n }\n const names = namesHeader.split(',').map((n) => n.trim()).filter(Boolean);\n if (!names.includes(name)) {\n throw new ExecutorError({ code: 'undeclared_credential', message: `Credential '${name}' is not in the grant (declared: ${names.join(', ') || 'none'})`, retriable: false });\n }\n\n const doFetch = opts.fetchImpl ?? fetch;\n const body = JSON.stringify({ token, name });\n\n // Up to 2 attempts: retry once on a transient 5xx/network error, never on a 403 (single-use — a retry\n // would just hit `already_redeemed`). A 5xx that landed AFTER the grant was claimed surfaces as a clean\n // 403 on retry, which is the correct terminal outcome (the credential is consumed; Data mints a fresh\n // grant on the next dispatch).\n for (let attempt = 0; attempt < 2; attempt++) {\n let res: Response;\n try {\n // `redirect: 'manual'` so a 3xx from the (pinned) URL can't re-POST the token-bearing body to\n // another host — a redirect surfaces as a non-ok status → credential_fetch_failed below.\n res = await doFetch(url, { method: 'POST', headers: { 'content-type': 'application/json' }, body, redirect: 'manual' });\n } catch {\n if (attempt === 0) continue; // network error → retry once\n throw new ExecutorError({ code: 'credential_unreachable', message: 'Credential pull is unreachable', retriable: true });\n }\n\n if (res.status === 403) {\n throw new ExecutorError({ code: 'credential_denied', message: `Credential '${name}' pull was denied (grant invalid, expired, or already used)`, retriable: false, status: 403 });\n }\n if (res.status >= 500) {\n if (attempt === 0) continue; // transient → retry once\n throw new ExecutorError({ code: 'credential_unavailable', message: `Credential pull failed (status ${res.status})`, retriable: true, status: res.status });\n }\n if (!res.ok) {\n throw new ExecutorError({ code: 'credential_fetch_failed', message: `Credential pull failed (status ${res.status})`, retriable: false, status: res.status });\n }\n\n const parsed = (await res.json().catch(() => null)) as { data?: { name?: string; content_type?: string; value?: string } } | null;\n const data = parsed?.data;\n if (!data || typeof data.value !== 'string') {\n throw new ExecutorError({ code: 'credential_malformed', message: 'Credential pull returned an unexpected body', retriable: false });\n }\n return { name: data.name ?? name, contentType: data.content_type ?? 'application/octet-stream', value: data.value };\n }\n\n // Unreachable: the loop returns or throws on every path.\n throw new ExecutorError({ code: 'credential_unavailable', message: 'Credential pull failed', retriable: true });\n}\n"]}
1
+ {"version":3,"sources":["../src/core/hmac.ts","../src/core/headers.ts","../src/core/canonical.ts","../src/core/verify.ts","../src/store/memory.ts","../src/errors/envelope.ts","../src/executor.ts","../src/adapters/fetch.ts","../src/adapters/node.ts","../src/core/sign.ts","../src/core/credential.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * HMAC-SHA256 over Web Crypto — pure, dependency-free, runs in any runtime with `crypto.subtle`\n * (Cloudflare Workers, Deno, Bun, browsers, and Node — see the package's declared `engines`).\n * Ported verbatim from the backend's implementation; the cross-package conformance vector\n * (test/conformance.test.ts) guards against drift.\n */\n\nexport async function signPayload(secret: string, payload: string): Promise<string> {\n const key = await crypto.subtle.importKey(\n 'raw',\n new TextEncoder().encode(secret),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign'],\n );\n const signature = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(payload));\n return arrayBufferToHex(signature);\n}\n\n/** Constant-time-ish equality of two hex strings (length-checks first, then XOR-accumulate). */\nexport function timingSafeEqual(a: string, b: string): boolean {\n if (a.length !== b.length) return false;\n let result = 0;\n for (let i = 0; i < a.length; i++) result |= a.charCodeAt(i) ^ b.charCodeAt(i);\n return result === 0;\n}\n\nfunction arrayBufferToHex(buffer: ArrayBuffer): string {\n return Array.from(new Uint8Array(buffer))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n","/** Case-insensitive header reader for a Fetch `Headers` or a plain object. A duplicate (array) value is\n * ambiguous and resolves to `undefined` (the signature/timestamp must be unambiguous). Shared by the\n * verify path and the dev-bypass path so they read headers identically. */\nexport type HeaderGetter = (name: string) => string | undefined;\n\nexport function headerGetter(headers: Headers | Record<string, string | string[] | undefined>): HeaderGetter {\n if (headers instanceof Headers) return (name) => headers.get(name) ?? undefined;\n const lower: Record<string, string | string[] | undefined> = {};\n for (const [k, v] of Object.entries(headers)) lower[k.toLowerCase()] = v;\n return (name) => {\n const v = lower[name];\n if (Array.isArray(v)) return v.length === 1 ? v[0] : undefined;\n return v;\n };\n}\n","/**\n * The \"Signing v1\" canonical — the exact string Data HMAC-signs for each request it signs\n * (trigger webhooks holding a secret + signed proxied calls). Ported verbatim from the backend's implementation and\n * kept byte-identical to it by the cross-package conformance vector (test/conformance.test.ts).\n *\n * canonical = [\"v1\", id, String(unixTimestamp), method, path, body].join(\"\\n\")\n *\n * SECURITY INVARIANT: newline-joined; the only data-influenced field, `body`, is LAST, and every\n * other field is provably newline-free (the \"v1\" literal, an integer, a uuid/hex id, an HTTP method\n * token, and a URL pathname+search). The signature binds method + path so it can't be replayed as a\n * different request.\n */\n\nexport const SIGNING_SCHEME = 'v1';\n\nexport interface SignedParts {\n /** Stable message id — `X-Data-Id` (= delivery id on webhooks, idempotency key on proxied writes). */\n id: string;\n /** `X-Data-Timestamp`, unix seconds (integer). */\n timestamp: number;\n /** HTTP method (`POST` for trigger webhooks). */\n method: string;\n /** Request path: `new URL(receiverUrl).pathname + search`. */\n path: string;\n /** Exact raw request body string (`\"\"` when none). */\n body: string;\n}\n\n/** Build the canonical signing string. Component order and the `v1` prefix are part of the wire. */\nexport function buildCanonical(parts: SignedParts): string {\n return [SIGNING_SCHEME, parts.id, String(parts.timestamp), parts.method, parts.path, parts.body].join('\\n');\n}\n\n/** Current unix time in whole seconds (Standard Webhooks convention). `nowMs` override for tests. */\nexport function unixSeconds(nowMs?: number): number {\n return Math.floor((nowMs ?? Date.now()) / 1000);\n}\n","/**\n * Verify a \"Signing v1\" request: HMAC over the canonical, plus timestamp-skew enforcement\n * (the receiver's job — Data only emits). All inputs are the raw request bits the adapter extracts.\n */\n\nimport { signPayload, timingSafeEqual } from './hmac.js';\nimport { headerGetter } from './headers.js';\nimport { SIGNING_SCHEME, buildCanonical, unixSeconds, type SignedParts } from './canonical.js';\n\n/** Lowercased header names Data emits. */\nexport const DATA_HEADERS = {\n signature: 'x-data-signature',\n timestamp: 'x-data-timestamp',\n id: 'x-data-id',\n idempotencyKey: 'x-data-idempotency-key',\n deliveryId: 'x-data-delivery-id',\n // Executor credential pull: advertised on a proxied dispatch to a source declaring executor_secrets.\n credentialUrl: 'x-data-credential-url',\n credentialToken: 'x-data-credential-token',\n credentialNames: 'x-data-credential-names',\n} as const;\n\n/**\n * Max `scheme,hex` candidates we'll HMAC-check from one `X-Data-Signature` header. Data emits one\n * (two during a future key rotation); the cap stops a hostile header with thousands of entries from\n * forcing thousands of HMACs per request.\n */\nexport const MAX_SIG_ENTRIES = 4;\n\n/**\n * Verify a signature header value (`X-Data-Signature`) against the canonical of `parts`. The header\n * is a space-delimited list of `scheme,hex` entries; returns true if any `v1` entry matches (constant-\n * time compare). Malformed/empty/unknown-scheme entries are skipped, never throw. Candidates beyond\n * `MAX_SIG_ENTRIES` are ignored. Does NOT check timestamp skew (see {@link verify}).\n */\nexport async function verifySignatureHeader(secret: string, parts: SignedParts, header: string): Promise<boolean> {\n const canonical = buildCanonical(parts);\n let expected: string | undefined; // loop-invariant HMAC — computed at most once, lazily\n let checked = 0;\n let matched = false;\n for (const entry of header.split(' ')) {\n const comma = entry.indexOf(',');\n if (comma <= 0) continue; // no scheme/hex separator (e.g. \"v1\" or \"\")\n if (entry.slice(0, comma) !== SIGNING_SCHEME) continue;\n const hex = entry.slice(comma + 1);\n if (hex.length === 0) continue;\n if (++checked > MAX_SIG_ENTRIES) break;\n if (expected === undefined) expected = await signPayload(secret, canonical);\n if (timingSafeEqual(expected, hex)) matched = true;\n }\n return matched;\n}\n\nexport type VerifyResult =\n | { ok: true; id: string; timestamp: number; idempotencyKey?: string; deliveryId?: string; body: string }\n | { ok: false; reason: 'missing_signature' | 'invalid_signature' | 'stale_timestamp' | 'malformed' };\n\nexport interface VerifyInput {\n /** The HMAC signing secret shared with Data (the trigger/source signing secret). */\n secret: string;\n /** Request headers — a Fetch `Headers` or a plain object (case-insensitive lookup either way). */\n headers: Headers | Record<string, string | string[] | undefined>;\n /** HTTP method of the incoming request. */\n method: string;\n /** Request path the signature was computed over: `pathname + search` (see README path-matching). */\n path: string;\n /** RAW request body bytes as a string — NEVER a re-stringified parsed object. */\n rawBody: string;\n /** Allowed clock skew in seconds (both directions). A finite value is clamped to ≥1 (never 0);\n * unset / NaN / Infinity fall back to the 300s default (a bad value can't disable the window). */\n toleranceSec?: number;\n /** Override \"now\" in ms (tests). */\n now?: number;\n}\n\n/**\n * Verify an inbound Data request end to end: signature present + valid, and timestamp within the\n * skew window. Returns a discriminated result; never throws on bad input.\n */\nexport async function verify(input: VerifyInput): Promise<VerifyResult> {\n const get = headerGetter(input.headers);\n const signature = get(DATA_HEADERS.signature);\n if (!signature) return { ok: false, reason: 'missing_signature' };\n\n const tsRaw = get(DATA_HEADERS.timestamp);\n const id = get(DATA_HEADERS.id);\n if (!tsRaw || !id) return { ok: false, reason: 'malformed' };\n if (!/^\\d+$/.test(tsRaw)) return { ok: false, reason: 'malformed' };\n const timestamp = Number(tsRaw);\n if (!Number.isFinite(timestamp)) return { ok: false, reason: 'malformed' };\n\n // Non-finite (unset / NaN / Infinity, e.g. `Number(process.env.X)` with X missing) → the 300s\n // default, so a misconfigured tolerance can never silently disable the replay window. A finite value\n // is clamped to ≥1 (never 0). To accept a wider window, pass an explicit large finite number.\n const t = input.toleranceSec;\n const tolerance = Number.isFinite(t) ? Math.max(1, Math.floor(t as number)) : 300;\n const now = unixSeconds(input.now);\n if (Math.abs(now - timestamp) > tolerance) return { ok: false, reason: 'stale_timestamp' };\n\n const parts: SignedParts = { id, timestamp, method: input.method, path: input.path, body: input.rawBody };\n const valid = await verifySignatureHeader(input.secret, parts, signature);\n if (!valid) return { ok: false, reason: 'invalid_signature' };\n\n return {\n ok: true,\n id,\n timestamp,\n idempotencyKey: get(DATA_HEADERS.idempotencyKey),\n deliveryId: get(DATA_HEADERS.deliveryId),\n body: input.rawBody,\n };\n}\n","import type { ClaimResult, IdempotencyStore, StoredResult } from './types.js';\n\ninterface Entry {\n state: 'pending' | 'done';\n result?: StoredResult;\n expiresAt: number; // epoch ms\n}\n\n/**\n * In-memory idempotency store (per-process). Single-threaded JS gives the atomic first-wins claim for\n * free. An accessed key expires lazily; since every delivery has a unique key, completed entries are\n * otherwise dropped by a periodic full sweep (every {@link SWEEP_EVERY} writes) — so the map is bounded\n * by the deliveries within one TTL window (default 24h), not unbounded. For multiple instances, or to\n * cap memory tightly, implement {@link IdempotencyStore} over a shared backend instead.\n */\nconst DEFAULT_SWEEP_EVERY = 1000;\n\nexport class MemoryStore implements IdempotencyStore {\n private readonly map = new Map<string, Entry>();\n private readonly nowMs: () => number;\n private readonly sweepEvery: number;\n private writes = 0;\n\n constructor(opts?: { now?: () => number; sweepEvery?: number }) {\n this.nowMs = opts?.now ?? (() => Date.now());\n this.sweepEvery = opts?.sweepEvery ?? DEFAULT_SWEEP_EVERY;\n }\n\n /** Number of live entries currently held (after lazily ignoring nothing — counts raw map size). */\n get size(): number {\n return this.map.size;\n }\n\n private live(key: string): Entry | undefined {\n const e = this.map.get(key);\n if (!e) return undefined;\n if (e.expiresAt <= this.nowMs()) {\n this.map.delete(key);\n return undefined;\n }\n return e;\n }\n\n /** Amortized O(1)/write: every SWEEP_EVERY writes, drop all expired entries so the map can't grow forever. */\n private maybeSweep(): void {\n if (++this.writes < this.sweepEvery) return;\n this.writes = 0;\n const now = this.nowMs();\n for (const [k, e] of this.map) if (e.expiresAt <= now) this.map.delete(k);\n }\n\n async claim(key: string, ttlSec: number): Promise<ClaimResult> {\n const e = this.live(key);\n if (!e) {\n this.map.set(key, { state: 'pending', expiresAt: this.nowMs() + ttlSec * 1000 });\n this.maybeSweep();\n return { state: 'fresh' };\n }\n if (e.state === 'done' && e.result) return { state: 'done', result: e.result };\n return { state: 'in_progress' };\n }\n\n async complete(key: string, result: StoredResult, ttlSec: number): Promise<void> {\n this.map.set(key, { state: 'done', result, expiresAt: this.nowMs() + ttlSec * 1000 });\n this.maybeSweep();\n }\n\n async fail(key: string): Promise<void> {\n this.map.delete(key);\n }\n}\n","/**\n * The normalized error envelope an executor returns. The HTTP STATUS is the real contract a sender's\n * retry logic reads (2xx ack, 4xx permanent/no-retry, 5xx & 429 retry); the JSON body is for humans.\n */\nexport interface ErrorEnvelope {\n status: number;\n code: string;\n message: string;\n retriable: boolean;\n}\n\n/**\n * Throw this from a handler to fail with a specific code/retriability. `retriable` decides the status\n * family ({@link toHttpStatus}) so the sender retries (5xx/429) or drops (4xx) correctly.\n */\nexport class ExecutorError extends Error {\n readonly code: string;\n readonly retriable: boolean;\n readonly httpStatus?: number;\n\n constructor(args: { code: string; message: string; retriable: boolean; status?: number }) {\n super(args.message);\n this.name = 'ExecutorError';\n this.code = args.code;\n this.retriable = args.retriable;\n this.httpStatus = args.status;\n }\n\n toEnvelope(): ErrorEnvelope {\n return { status: toHttpStatus(this), code: this.code, message: this.message, retriable: this.retriable };\n }\n}\n\n/**\n * Map an error to an HTTP status. An explicit `status` is honored when it agrees with `retriable`\n * (5xx/429 for retriable, 4xx for permanent); otherwise the family default is used: retriable → 503,\n * permanent → 400.\n */\nexport function toHttpStatus(e: { retriable: boolean; httpStatus?: number }): number {\n const s = e.httpStatus;\n if (e.retriable) {\n return s !== undefined && (s === 429 || (s >= 500 && s <= 599)) ? s : 503;\n }\n return s !== undefined && s >= 400 && s <= 499 ? s : 400;\n}\n\n/** Serialize an envelope to a JSON body string. */\nexport function jsonResponseBody(env: ErrorEnvelope): string {\n return JSON.stringify(env);\n}\n","import { verify, DATA_HEADERS, type VerifyResult } from './core/verify.js';\nimport { headerGetter } from './core/headers.js';\nimport { MemoryStore } from './store/memory.js';\nimport type { IdempotencyStore, StoredResult } from './store/types.js';\nimport { ExecutorError, toHttpStatus, jsonResponseBody, type ErrorEnvelope } from './errors/envelope.js';\n\n/** Framework-neutral request the adapters hand to the executor. `rawBody` is the RAW bytes as a string. */\nexport interface NormalizedRequest {\n method: string;\n path: string;\n headers: Headers | Record<string, string | string[] | undefined>;\n rawBody: string;\n}\n\nexport interface NormalizedResponse {\n status: number;\n body: string;\n contentType: string;\n}\n\n/** Verified request context passed to your handler. Never contains the signing secret. */\nexport interface ExecutorContext {\n id: string;\n idempotencyKey?: string;\n deliveryId?: string;\n timestamp: number;\n body: string;\n}\n\n/** Your business logic. Return a `StoredResult` (cached for idempotent replays) or nothing (→ 204). */\nexport type ExecutorHandler = (ctx: ExecutorContext) => Promise<StoredResult | void> | StoredResult | void;\n\nexport interface CreateExecutorOptions {\n /** HMAC signing secret shared with Data. */\n secret: string;\n handler: ExecutorHandler;\n /** Idempotency store. Default: a per-process {@link MemoryStore}. */\n store?: IdempotencyStore;\n /** Clock-skew tolerance (seconds, both directions). Default 300; clamped to ≥1. */\n toleranceSec?: number;\n /** Skip signature/skew verification (LOCAL DEV ONLY). Dedup still applies if headers are present. */\n devBypass?: boolean;\n /** Idempotency record TTL (seconds). Default 86400 (24h). */\n idempotencyTtlSec?: number;\n /** Reports unexpected handler errors. MUST NOT log secrets/PII — it never receives the secret. */\n onError?: (err: unknown, ctx?: ExecutorContext) => void;\n /**\n * Reproduce the path Data SIGNED when a reverse proxy rewrites it. Must return the exact\n * `pathname+search` Data used (see README path-matching), not necessarily the incoming path.\n */\n pathOverride?: (req: NormalizedRequest) => string;\n}\n\nexport interface Executor {\n handle(req: NormalizedRequest): Promise<NormalizedResponse>;\n}\n\nconst JSON_CT = 'application/json';\n\nfunction errorResponse(env: ErrorEnvelope): NormalizedResponse {\n return { status: env.status, body: jsonResponseBody(env), contentType: JSON_CT };\n}\n\nfunction reasonResponse(reason: Exclude<VerifyResult, { ok: true }>['reason']): NormalizedResponse {\n // missing/invalid → 401 (unauthenticated); stale → 403; malformed → 400.\n const status = reason === 'malformed' ? 400 : reason === 'stale_timestamp' ? 403 : 401;\n return errorResponse({ status, code: reason.toUpperCase(), message: `signature ${reason}`, retriable: false });\n}\n\n/** Read the dedupe context from headers without verifying (dev-bypass path). */\nfunction bypassContext(req: NormalizedRequest): ExecutorContext | null {\n const get = headerGetter(req.headers);\n const id = get(DATA_HEADERS.id);\n const tsRaw = get(DATA_HEADERS.timestamp);\n return {\n id: id ?? '',\n idempotencyKey: get(DATA_HEADERS.idempotencyKey),\n deliveryId: get(DATA_HEADERS.deliveryId),\n timestamp: tsRaw && /^\\d+$/.test(tsRaw) ? Number(tsRaw) : 0,\n body: req.rawBody,\n };\n}\n\nexport function createExecutor(opts: CreateExecutorOptions): Executor {\n const store = opts.store ?? new MemoryStore();\n const ttl = opts.idempotencyTtlSec ?? 86_400;\n\n async function run(ctx: ExecutorContext): Promise<NormalizedResponse> {\n // Dedupe key: the logical idempotency key (proxy writes) or the stable message id (webhooks).\n // Absent both (e.g. dev-bypass with no signed headers) → run without dedupe.\n const key = ctx.idempotencyKey || ctx.id;\n if (!key) return await invoke(ctx);\n\n const claim = await store.claim(key, ttl);\n if (claim.state === 'done') {\n return { status: claim.result.status, body: claim.result.body, contentType: claim.result.contentType ?? JSON_CT };\n }\n if (claim.state === 'in_progress') {\n // A concurrent attempt holds this key — tell the sender to retry. Use a retriable (5xx) status,\n // not 409, so it matches `retriable: true` and the sender's retry convention.\n return errorResponse({ status: 503, code: 'IN_PROGRESS', message: 'duplicate delivery in progress', retriable: true });\n }\n try {\n const res = await invoke(ctx);\n await store.complete(key, { status: res.status, body: res.body, contentType: res.contentType }, ttl);\n return res;\n } catch (err) {\n const env = toEnvelope(err);\n const res = errorResponse(env);\n if (env.retriable) {\n await store.fail(key); // retriable: release the key so the retry re-runs (don't cache failures)\n } else {\n // permanent: cache the 4xx so a duplicate delivery replays it instead of re-running.\n await store.complete(key, { status: res.status, body: res.body, contentType: res.contentType }, ttl);\n }\n if (!(err instanceof ExecutorError)) opts.onError?.(err, ctx);\n return res;\n }\n }\n\n async function invoke(ctx: ExecutorContext): Promise<NormalizedResponse> {\n const result = await opts.handler(ctx);\n if (!result) return { status: 204, body: '', contentType: JSON_CT };\n return { status: result.status, body: result.body, contentType: result.contentType ?? JSON_CT };\n }\n\n return {\n async handle(req: NormalizedRequest): Promise<NormalizedResponse> {\n const path = opts.pathOverride ? opts.pathOverride(req) : req.path;\n\n if (opts.devBypass) {\n const ctx = bypassContext(req);\n return ctx ? await run(ctx) : reasonResponse('malformed');\n }\n\n const result = await verify({\n secret: opts.secret,\n headers: req.headers,\n method: req.method,\n path,\n rawBody: req.rawBody,\n toleranceSec: opts.toleranceSec,\n });\n if (!result.ok) return reasonResponse(result.reason);\n\n return await run({\n id: result.id,\n idempotencyKey: result.idempotencyKey,\n deliveryId: result.deliveryId,\n timestamp: result.timestamp,\n body: result.body,\n });\n },\n };\n}\n\nfunction toEnvelope(err: unknown): ErrorEnvelope {\n if (err instanceof ExecutorError) return err.toEnvelope();\n // Unknown error → retriable 500 so the sender retries; message is generic (no secret/PII).\n return { status: toHttpStatus({ retriable: true }), code: 'INTERNAL', message: 'handler error', retriable: true };\n}\n","import type { Executor } from '../executor.js';\n\n/**\n * Wrap an {@link Executor} as a Fetch-API handler `(Request) => Promise<Response>` — works on Hono,\n * Next.js route handlers, Cloudflare Workers, Deno, and Bun.\n *\n * Reads the RAW body via `req.text()` BEFORE any parsing (verifying a re-stringified parsed body would\n * fail). The signed `path` is `new URL(req.url).pathname + search`; if a reverse proxy rewrites the\n * path, pass `pathOverride` to `createExecutor`.\n */\nexport function toFetchHandler(executor: Executor): (req: Request) => Promise<Response> {\n return async (req: Request): Promise<Response> => {\n const rawBody = await req.text();\n const url = new URL(req.url);\n const res = await executor.handle({\n method: req.method,\n path: url.pathname + url.search,\n headers: req.headers,\n rawBody,\n });\n // 204/205/304 are null-body statuses — passing any body (even '') throws in spec-compliant\n // runtimes, so send `null` when the body is empty.\n return new Response(res.body || null, { status: res.status, headers: { 'content-type': res.contentType } });\n };\n}\n","import type { Executor } from '../executor.js';\n\n/**\n * Minimal structural shapes of Node's `http.IncomingMessage` / `http.ServerResponse` (and Express's\n * `req`/`res`). Declared structurally so the package imports no Node built-ins at runtime.\n */\nexport interface NodeReq extends AsyncIterable<unknown> {\n method?: string;\n url?: string;\n headers: Record<string, string | string[] | undefined>;\n}\nexport interface NodeRes {\n writeHead(status: number, headers?: Record<string, string>): unknown;\n end(chunk?: string): unknown;\n}\n\n/**\n * Wrap an {@link Executor} as a Node/Express handler `(req, res) => Promise<void>`.\n *\n * IMPORTANT: this reads the RAW request stream itself, so it MUST run BEFORE any body parser\n * (`express.json()`, `body-parser`, …). If a parser has already consumed the stream, the raw bytes are\n * gone and verification fails. Mount this on your executor route before such middleware.\n *\n * The signed `path` comes from `req.url` (already `pathname+search`); use `pathOverride` for proxies.\n */\nexport function toNodeHandler(executor: Executor): (req: NodeReq, res: NodeRes) => Promise<void> {\n return async (req: NodeReq, res: NodeRes): Promise<void> => {\n const chunks: Buffer[] = [];\n for await (const chunk of req) {\n chunks.push(typeof chunk === 'string' ? Buffer.from(chunk) : (chunk as Buffer));\n }\n const rawBody = Buffer.concat(chunks).toString('utf8');\n\n const out = await executor.handle({\n method: req.method ?? 'GET',\n path: req.url ?? '/',\n headers: req.headers,\n rawBody,\n });\n res.writeHead(out.status, { 'content-type': out.contentType });\n res.end(out.body);\n };\n}\n","/**\n * Sign a request TO Data — the inverse of the executor's verify side. Use this when an executor POSTs\n * a result back to an inbound webhook on a base (the async write-back leg): the inbound webhook ingest\n * verifies a Signing v1 signature, and this produces it from the inbound webhook's shared secret.\n *\n * Returns the three headers to attach (`X-Data-Signature`, `X-Data-Timestamp`, `X-Data-Id`). The\n * canonical is byte-identical to the verify side (the cross-package conformance vector guards drift).\n */\n\nimport { buildCanonical, SIGNING_SCHEME, unixSeconds } from './canonical.js';\nimport { signPayload } from './hmac.js';\n\nexport interface SignRequestInput {\n /** HMAC shared secret for the target inbound webhook. */\n secret: string;\n /** HTTP method of the request being signed (e.g. 'POST'). */\n method: string;\n /** Full target URL — its `pathname + search` is bound into the signature. */\n url: string;\n /** Raw request body string (default `''`). MUST be the exact bytes sent. */\n body?: string;\n /** Message id → `X-Data-Id`, bound into the signature. Default: a generated UUID. */\n id?: string;\n /** Unix seconds → `X-Data-Timestamp`. Default: now. */\n timestamp?: number;\n}\n\nexport interface SignedHeaders {\n 'X-Data-Signature': string;\n 'X-Data-Timestamp': string;\n 'X-Data-Id': string;\n}\n\n/** Sign a request to Data and return the headers to attach. */\nexport async function signRequest(input: SignRequestInput): Promise<SignedHeaders> {\n const id = input.id ?? crypto.randomUUID();\n const timestamp = input.timestamp ?? unixSeconds();\n const body = input.body ?? '';\n const { pathname, search } = new URL(input.url);\n const hex = await signPayload(input.secret, buildCanonical({ id, timestamp, method: input.method, path: pathname + search, body }));\n return {\n 'X-Data-Signature': `${SIGNING_SCHEME},${hex}`,\n 'X-Data-Timestamp': String(timestamp),\n 'X-Data-Id': id,\n };\n}\n","/**\n * Executor credential pull — the client side of Data's scoped, single-use binary-credential delivery.\n *\n * When a source declares `executor_secrets`, every proxied dispatch to the executor carries\n * `X-Data-Credential-Url`/`-Token`/`-Names`. The executor calls `fetchExecutorCredential` with the\n * INBOUND request to pull a declared credential by name (e.g. an mTLS client cert it must present to a\n * downstream upstream). The grant is single-use per name and short-lived, so fetch each credential ONCE\n * per invocation and reuse the value — a re-pull of the same name fails (the grant is already claimed).\n *\n * The returned `value` is delivered verbatim (binary credentials are stored base64-encoded); decode it\n * per `contentType` (e.g. base64-decode an `application/x-pkcs12` bundle). This helper never decodes.\n *\n * SECURITY: call this ONLY from a handler whose inbound request the SDK has already verified (e.g. inside\n * `createExecutor`'s handler). The grant headers are NOT part of the request's HMAC signature, so anything\n * that can rewrite headers in front of your executor (a TLS-terminating proxy/gateway) could swap the\n * callback URL. Pass `allowedOrigins` (your Data origin, e.g. `['https://data.wayai.pro']`) to reject a\n * rewritten URL — recommended whenever your executor sits behind such an intermediary.\n */\n\nimport { headerGetter } from './headers.js';\nimport { DATA_HEADERS } from './verify.js';\nimport { ExecutorError } from '../errors/envelope.js';\n\n/** A pulled credential. `value` is delivered verbatim (base64 for binary); decode per `contentType`. */\nexport interface ExecutorCredential {\n name: string;\n contentType: string;\n value: string;\n}\n\nexport interface FetchCredentialOptions {\n /** Override the fetch implementation (defaults to the global `fetch`). */\n fetchImpl?: typeof fetch;\n /**\n * If set (and non-empty), the callback URL's origin must be one of these — rejects a URL rewritten in\n * transit (the grant headers are NOT covered by the request signature). Pass your Data origin(s),\n * e.g. `['https://data.wayai.pro']`. Recommended when the executor sits behind a TLS-terminating proxy.\n *\n * Fails closed on an exact origin match: if the Data origin changes and this list still names only\n * the old one, every pull throws `credential_untrusted_url` (never retried). List every origin your\n * deployment may send from, and add a new one BEFORE it starts arriving.\n */\n allowedOrigins?: string[];\n}\n\n/** Anything carrying the inbound request headers: a NormalizedRequest, a Fetch `Request`, or raw headers. */\ntype HeaderSource =\n | Headers\n | Record<string, string | string[] | undefined>\n | { headers: Headers | Record<string, string | string[] | undefined> };\n\nfunction resolveHeaders(source: HeaderSource): Headers | Record<string, string | string[] | undefined> {\n if (source instanceof Headers) return source;\n // A NormalizedRequest / Fetch Request carries headers under `.headers` (a Headers or a record); a bare\n // headers record does not (its `.headers`, if any, would be a string value, not an object) — so guard\n // on the shape rather than `'headers' in source`, which a record with a \"headers\" key would spoof.\n const nested = (source as { headers?: unknown }).headers;\n if (nested instanceof Headers) return nested;\n if (nested && typeof nested === 'object') return nested as Record<string, string | string[] | undefined>;\n return source as Record<string, string | string[] | undefined>;\n}\n\n/**\n * Pull a declared credential by name from Data, using the grant advertised on the inbound dispatch.\n * Throws `ExecutorError` if the request carries no grant, the name isn't in the grant, or the pull is\n * denied/fails. A 403 (grant invalid/expired/already-used) is never retried; a transient 5xx/network\n * error is retried once.\n */\nexport async function fetchExecutorCredential(\n source: HeaderSource,\n name: string,\n opts: FetchCredentialOptions = {},\n): Promise<ExecutorCredential> {\n const get = headerGetter(resolveHeaders(source));\n const url = get(DATA_HEADERS.credentialUrl);\n const token = get(DATA_HEADERS.credentialToken);\n const namesHeader = get(DATA_HEADERS.credentialNames);\n if (!url || !token || namesHeader === undefined) {\n throw new ExecutorError({ code: 'no_credential_grant', message: 'Request carries no executor credential grant', retriable: false });\n }\n // The callback URL is transport-trusted, not signature-bound — pin it when an allowlist is supplied so a\n // rewritten URL can't redirect the single-use grant token to an attacker host.\n if (opts.allowedOrigins && opts.allowedOrigins.length > 0) {\n let origin: string;\n try { origin = new URL(url).origin; } catch { throw new ExecutorError({ code: 'credential_bad_url', message: 'Credential callback URL is not a valid URL', retriable: false }); }\n // Normalize each allowlist entry through URL.origin too, so a caller's trailing slash / explicit\n // default port (e.g. 'https://data.wayai.pro/' or ':443') still matches. A malformed entry keeps its\n // raw form and simply never matches (it can't grant), rather than breaking the whole check.\n const allowed = opts.allowedOrigins.map((o) => { try { return new URL(o).origin; } catch { return o; } });\n if (!allowed.includes(origin)) {\n throw new ExecutorError({ code: 'credential_untrusted_url', message: `Credential callback URL origin '${origin}' is not in allowedOrigins`, retriable: false });\n }\n }\n const names = namesHeader.split(',').map((n) => n.trim()).filter(Boolean);\n if (!names.includes(name)) {\n throw new ExecutorError({ code: 'undeclared_credential', message: `Credential '${name}' is not in the grant (declared: ${names.join(', ') || 'none'})`, retriable: false });\n }\n\n const doFetch = opts.fetchImpl ?? fetch;\n const body = JSON.stringify({ token, name });\n\n // Up to 2 attempts: retry once on a transient 5xx/network error, never on a 403 (single-use — a retry\n // would just hit `already_redeemed`). A 5xx that landed AFTER the grant was claimed surfaces as a clean\n // 403 on retry, which is the correct terminal outcome (the credential is consumed; Data mints a fresh\n // grant on the next dispatch).\n for (let attempt = 0; attempt < 2; attempt++) {\n let res: Response;\n try {\n // `redirect: 'manual'` so a 3xx from the (pinned) URL can't re-POST the token-bearing body to\n // another host — a redirect surfaces as a non-ok status → credential_fetch_failed below.\n res = await doFetch(url, { method: 'POST', headers: { 'content-type': 'application/json' }, body, redirect: 'manual' });\n } catch {\n if (attempt === 0) continue; // network error → retry once\n throw new ExecutorError({ code: 'credential_unreachable', message: 'Credential pull is unreachable', retriable: true });\n }\n\n if (res.status === 403) {\n throw new ExecutorError({ code: 'credential_denied', message: `Credential '${name}' pull was denied (grant invalid, expired, or already used)`, retriable: false, status: 403 });\n }\n if (res.status >= 500) {\n if (attempt === 0) continue; // transient → retry once\n throw new ExecutorError({ code: 'credential_unavailable', message: `Credential pull failed (status ${res.status})`, retriable: true, status: res.status });\n }\n if (!res.ok) {\n throw new ExecutorError({ code: 'credential_fetch_failed', message: `Credential pull failed (status ${res.status})`, retriable: false, status: res.status });\n }\n\n const parsed = (await res.json().catch(() => null)) as { data?: { name?: string; content_type?: string; value?: string } } | null;\n const data = parsed?.data;\n if (!data || typeof data.value !== 'string') {\n throw new ExecutorError({ code: 'credential_malformed', message: 'Credential pull returned an unexpected body', retriable: false });\n }\n return { name: data.name ?? name, contentType: data.content_type ?? 'application/octet-stream', value: data.value };\n }\n\n // Unreachable: the loop returns or throws on every path.\n throw new ExecutorError({ code: 'credential_unavailable', message: 'Credential pull failed', retriable: true });\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wayai/executor-sdk",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Receive and verify WayAI Data's HMAC-signed, idempotency-keyed webhooks and proxied calls — the executor side of the WayAI Data executor contract.",
5
5
  "keywords": [
6
6
  "wayai",