@shware/workflow 3.2.0 → 4.0.0
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 +100 -29
- package/dist/apns.cjs +113 -26
- package/dist/apns.cjs.map +1 -1
- package/dist/apns.d.cts +52 -6
- package/dist/apns.d.cts.map +1 -1
- package/dist/apns.d.mts +52 -6
- package/dist/apns.d.mts.map +1 -1
- package/dist/apns.mjs +111 -27
- package/dist/apns.mjs.map +1 -1
- package/dist/aws/engine-step.cjs +5 -5
- package/dist/aws/engine-step.cjs.map +1 -1
- package/dist/aws/engine-step.d.cts +8 -2
- package/dist/aws/engine-step.d.cts.map +1 -1
- package/dist/aws/engine-step.d.mts +8 -2
- package/dist/aws/engine-step.d.mts.map +1 -1
- package/dist/aws/engine-step.mjs +5 -5
- package/dist/aws/engine-step.mjs.map +1 -1
- package/dist/aws/index.cjs +47 -6
- package/dist/aws/index.cjs.map +1 -1
- package/dist/aws/index.d.cts +2 -2
- package/dist/aws/index.d.cts.map +1 -1
- package/dist/aws/index.d.mts +2 -2
- package/dist/aws/index.d.mts.map +1 -1
- package/dist/aws/index.mjs +47 -6
- package/dist/aws/index.mjs.map +1 -1
- package/dist/aws/instances.cjs +29 -6
- package/dist/aws/instances.cjs.map +1 -1
- package/dist/aws/instances.d.cts +11 -1
- package/dist/aws/instances.d.cts.map +1 -1
- package/dist/aws/instances.d.mts +11 -1
- package/dist/aws/instances.d.mts.map +1 -1
- package/dist/aws/instances.mjs +27 -4
- package/dist/aws/instances.mjs.map +1 -1
- package/dist/client.cjs +4 -1
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +2 -2
- package/dist/client.d.cts.map +1 -1
- package/dist/client.d.mts +2 -2
- package/dist/client.d.mts.map +1 -1
- package/dist/client.mjs +4 -1
- package/dist/client.mjs.map +1 -1
- package/dist/cloudflare/bindings.cjs +28 -25
- package/dist/cloudflare/bindings.cjs.map +1 -1
- package/dist/cloudflare/bindings.d.cts +17 -129
- package/dist/cloudflare/bindings.d.cts.map +1 -1
- package/dist/cloudflare/bindings.d.mts +17 -129
- package/dist/cloudflare/bindings.d.mts.map +1 -1
- package/dist/cloudflare/bindings.mjs +28 -23
- package/dist/cloudflare/bindings.mjs.map +1 -1
- package/dist/cloudflare/d1-env.cjs +6 -6
- package/dist/cloudflare/d1-env.cjs.map +1 -1
- package/dist/cloudflare/d1-env.d.cts +5 -5
- package/dist/cloudflare/d1-env.d.mts +5 -5
- package/dist/cloudflare/d1-env.mjs +6 -6
- package/dist/cloudflare/d1-env.mjs.map +1 -1
- package/dist/cloudflare/index.cjs +28 -18
- package/dist/cloudflare/index.d.cts +8 -4
- package/dist/cloudflare/index.d.mts +8 -4
- package/dist/cloudflare/index.mjs +9 -5
- package/dist/cloudflare/runner.cjs +29 -14
- package/dist/cloudflare/runner.cjs.map +1 -1
- package/dist/cloudflare/runner.d.cts +9 -2
- package/dist/cloudflare/runner.d.cts.map +1 -1
- package/dist/cloudflare/runner.d.mts +9 -2
- package/dist/cloudflare/runner.d.mts.map +1 -1
- package/dist/cloudflare/runner.mjs +28 -13
- package/dist/cloudflare/runner.mjs.map +1 -1
- package/dist/cloudflare/schema.sql +27 -6
- package/dist/cloudflare/worker.cjs +19 -9
- package/dist/cloudflare/worker.cjs.map +1 -1
- package/dist/cloudflare/worker.d.cts +2 -2
- package/dist/cloudflare/worker.d.cts.map +1 -1
- package/dist/cloudflare/worker.d.mts +2 -2
- package/dist/cloudflare/worker.d.mts.map +1 -1
- package/dist/cloudflare/worker.mjs +19 -9
- package/dist/cloudflare/worker.mjs.map +1 -1
- package/dist/config.cjs +21 -5
- package/dist/config.cjs.map +1 -1
- package/dist/config.mjs +21 -5
- package/dist/config.mjs.map +1 -1
- package/dist/dsl/action.cjs +3 -6
- package/dist/dsl/action.cjs.map +1 -1
- package/dist/dsl/action.d.cts +12 -11
- package/dist/dsl/action.d.cts.map +1 -1
- package/dist/dsl/action.d.mts +12 -11
- package/dist/dsl/action.d.mts.map +1 -1
- package/dist/dsl/action.mjs +3 -6
- package/dist/dsl/action.mjs.map +1 -1
- package/dist/dsl/bundle.cjs +35 -31
- package/dist/dsl/bundle.cjs.map +1 -1
- package/dist/dsl/bundle.d.cts.map +1 -1
- package/dist/dsl/bundle.d.mts.map +1 -1
- package/dist/dsl/bundle.mjs +35 -31
- package/dist/dsl/bundle.mjs.map +1 -1
- package/dist/dsl/condition.cjs +13 -1
- package/dist/dsl/condition.cjs.map +1 -1
- package/dist/dsl/condition.d.cts +9 -1
- package/dist/dsl/condition.d.cts.map +1 -1
- package/dist/dsl/condition.d.mts +9 -1
- package/dist/dsl/condition.d.mts.map +1 -1
- package/dist/dsl/condition.mjs +13 -2
- package/dist/dsl/condition.mjs.map +1 -1
- package/dist/dsl/flow.cjs +33 -11
- package/dist/dsl/flow.cjs.map +1 -1
- package/dist/dsl/flow.d.cts +9 -1
- package/dist/dsl/flow.d.cts.map +1 -1
- package/dist/dsl/flow.d.mts +9 -1
- package/dist/dsl/flow.d.mts.map +1 -1
- package/dist/dsl/flow.mjs +34 -13
- package/dist/dsl/flow.mjs.map +1 -1
- package/dist/dsl/workflow.cjs +2 -3
- package/dist/dsl/workflow.cjs.map +1 -1
- package/dist/dsl/workflow.d.cts.map +1 -1
- package/dist/dsl/workflow.d.mts.map +1 -1
- package/dist/dsl/workflow.mjs +3 -4
- package/dist/dsl/workflow.mjs.map +1 -1
- package/dist/email-sender.cjs +24 -0
- package/dist/email-sender.cjs.map +1 -1
- package/dist/email-sender.d.cts +18 -0
- package/dist/email-sender.d.cts.map +1 -1
- package/dist/email-sender.d.mts +18 -0
- package/dist/email-sender.d.mts.map +1 -1
- package/dist/email-sender.mjs +24 -1
- package/dist/email-sender.mjs.map +1 -1
- package/dist/engine/actions.cjs +1 -8
- package/dist/engine/actions.cjs.map +1 -1
- package/dist/engine/actions.d.cts +1 -12
- package/dist/engine/actions.d.cts.map +1 -1
- package/dist/engine/actions.d.mts +1 -12
- package/dist/engine/actions.d.mts.map +1 -1
- package/dist/engine/actions.mjs +1 -8
- package/dist/engine/actions.mjs.map +1 -1
- package/dist/engine/condition.cjs +25 -5
- package/dist/engine/condition.cjs.map +1 -1
- package/dist/engine/condition.d.cts +12 -3
- package/dist/engine/condition.d.cts.map +1 -1
- package/dist/engine/condition.d.mts +12 -3
- package/dist/engine/condition.d.mts.map +1 -1
- package/dist/engine/condition.mjs +25 -6
- package/dist/engine/condition.mjs.map +1 -1
- package/dist/engine/index.cjs +3 -0
- package/dist/engine/index.d.cts +5 -5
- package/dist/engine/index.d.mts +5 -5
- package/dist/engine/index.mjs +4 -4
- package/dist/engine/interpreter.cjs +27 -20
- package/dist/engine/interpreter.cjs.map +1 -1
- package/dist/engine/interpreter.d.cts.map +1 -1
- package/dist/engine/interpreter.d.mts.map +1 -1
- package/dist/engine/interpreter.mjs +27 -20
- package/dist/engine/interpreter.mjs.map +1 -1
- package/dist/engine/ports.d.cts +21 -6
- package/dist/engine/ports.d.cts.map +1 -1
- package/dist/engine/ports.d.mts +21 -6
- package/dist/engine/ports.d.mts.map +1 -1
- package/dist/engine/retry.cjs +13 -0
- package/dist/engine/retry.cjs.map +1 -1
- package/dist/engine/retry.d.cts +12 -0
- package/dist/engine/retry.d.cts.map +1 -1
- package/dist/engine/retry.d.mts +12 -0
- package/dist/engine/retry.d.mts.map +1 -1
- package/dist/engine/retry.mjs +13 -1
- package/dist/engine/retry.mjs.map +1 -1
- package/dist/engine/time-scale.cjs +90 -15
- package/dist/engine/time-scale.cjs.map +1 -1
- package/dist/engine/time-scale.d.cts +9 -6
- package/dist/engine/time-scale.d.cts.map +1 -1
- package/dist/engine/time-scale.d.mts +9 -6
- package/dist/engine/time-scale.d.mts.map +1 -1
- package/dist/engine/time-scale.mjs +90 -16
- package/dist/engine/time-scale.mjs.map +1 -1
- package/dist/fcm.cjs +111 -38
- package/dist/fcm.cjs.map +1 -1
- package/dist/fcm.d.cts +45 -5
- package/dist/fcm.d.cts.map +1 -1
- package/dist/fcm.d.mts +45 -5
- package/dist/fcm.d.mts.map +1 -1
- package/dist/fcm.mjs +110 -39
- package/dist/fcm.mjs.map +1 -1
- package/dist/hash.cjs +6 -6
- package/dist/hash.cjs.map +1 -1
- package/dist/hash.mjs +6 -6
- package/dist/hash.mjs.map +1 -1
- package/dist/host/auth.cjs +49 -0
- package/dist/host/auth.cjs.map +1 -0
- package/dist/host/auth.d.cts +21 -0
- package/dist/host/auth.d.cts.map +1 -0
- package/dist/host/auth.d.mts +21 -0
- package/dist/host/auth.d.mts.map +1 -0
- package/dist/host/auth.mjs +47 -0
- package/dist/host/auth.mjs.map +1 -0
- package/dist/host/channels.cjs +118 -40
- package/dist/host/channels.cjs.map +1 -1
- package/dist/host/channels.d.cts +27 -1
- package/dist/host/channels.d.cts.map +1 -1
- package/dist/host/channels.d.mts +27 -1
- package/dist/host/channels.d.mts.map +1 -1
- package/dist/host/channels.mjs +121 -44
- package/dist/host/channels.mjs.map +1 -1
- package/dist/host/execute.cjs +14 -2
- package/dist/host/execute.cjs.map +1 -1
- package/dist/host/execute.d.cts +4 -4
- package/dist/host/execute.d.cts.map +1 -1
- package/dist/host/execute.d.mts +4 -4
- package/dist/host/execute.d.mts.map +1 -1
- package/dist/host/execute.mjs +13 -1
- package/dist/host/execute.mjs.map +1 -1
- package/dist/host/fresh-read.cjs +30 -0
- package/dist/host/fresh-read.cjs.map +1 -0
- package/dist/host/fresh-read.d.cts +18 -0
- package/dist/host/fresh-read.d.cts.map +1 -0
- package/dist/host/fresh-read.d.mts +18 -0
- package/dist/host/fresh-read.d.mts.map +1 -0
- package/dist/host/fresh-read.mjs +29 -0
- package/dist/host/fresh-read.mjs.map +1 -0
- package/dist/host/http.cjs +321 -0
- package/dist/host/http.cjs.map +1 -0
- package/dist/host/http.d.cts +54 -0
- package/dist/host/http.d.cts.map +1 -0
- package/dist/host/http.d.mts +54 -0
- package/dist/host/http.d.mts.map +1 -0
- package/dist/host/http.mjs +317 -0
- package/dist/host/http.mjs.map +1 -0
- package/dist/host/{router.cjs → ingest.cjs} +147 -175
- package/dist/host/ingest.cjs.map +1 -0
- package/dist/host/{router.d.cts → ingest.d.cts} +48 -30
- package/dist/host/ingest.d.cts.map +1 -0
- package/dist/host/{router.d.mts → ingest.d.mts} +48 -30
- package/dist/host/ingest.d.mts.map +1 -0
- package/dist/host/{router.mjs → ingest.mjs} +144 -173
- package/dist/host/ingest.mjs.map +1 -0
- package/dist/host/journey-host.cjs +65 -93
- package/dist/host/journey-host.cjs.map +1 -1
- package/dist/host/journey-host.d.cts +19 -26
- package/dist/host/journey-host.d.cts.map +1 -1
- package/dist/host/journey-host.d.mts +19 -26
- package/dist/host/journey-host.d.mts.map +1 -1
- package/dist/host/journey-host.mjs +65 -93
- package/dist/host/journey-host.mjs.map +1 -1
- package/dist/host/platform.cjs +21 -0
- package/dist/host/platform.cjs.map +1 -0
- package/dist/host/platform.d.cts +126 -0
- package/dist/host/platform.d.cts.map +1 -0
- package/dist/host/platform.d.mts +126 -0
- package/dist/host/platform.d.mts.map +1 -0
- package/dist/host/platform.mjs +19 -0
- package/dist/host/platform.mjs.map +1 -0
- package/dist/host/senders.cjs +18 -9
- package/dist/host/senders.cjs.map +1 -1
- package/dist/host/senders.d.cts +9 -4
- package/dist/host/senders.d.cts.map +1 -1
- package/dist/host/senders.d.mts +9 -4
- package/dist/host/senders.d.mts.map +1 -1
- package/dist/host/senders.mjs +18 -9
- package/dist/host/senders.mjs.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/ir.cjs +5 -7
- package/dist/ir.cjs.map +1 -1
- package/dist/ir.d.cts +3 -12
- package/dist/ir.d.cts.map +1 -1
- package/dist/ir.d.mts +3 -12
- package/dist/ir.d.mts.map +1 -1
- package/dist/ir.mjs +5 -7
- package/dist/ir.mjs.map +1 -1
- package/dist/plan.cjs +27 -33
- package/dist/plan.cjs.map +1 -1
- package/dist/plan.d.cts +1 -1
- package/dist/plan.d.cts.map +1 -1
- package/dist/plan.d.mts +1 -1
- package/dist/plan.d.mts.map +1 -1
- package/dist/plan.mjs +27 -33
- package/dist/plan.mjs.map +1 -1
- package/dist/reporting/observer.cjs +1 -0
- package/dist/reporting/observer.cjs.map +1 -1
- package/dist/reporting/observer.mjs +1 -0
- package/dist/reporting/observer.mjs.map +1 -1
- package/dist/reporting/tracking.cjs +19 -4
- package/dist/reporting/tracking.cjs.map +1 -1
- package/dist/reporting/tracking.d.cts +13 -11
- package/dist/reporting/tracking.d.cts.map +1 -1
- package/dist/reporting/tracking.d.mts +13 -11
- package/dist/reporting/tracking.d.mts.map +1 -1
- package/dist/reporting/tracking.mjs +19 -4
- package/dist/reporting/tracking.mjs.map +1 -1
- package/dist/store/d1.cjs +88 -29
- package/dist/store/d1.cjs.map +1 -1
- package/dist/store/d1.d.cts +29 -11
- package/dist/store/d1.d.cts.map +1 -1
- package/dist/store/d1.d.mts +29 -11
- package/dist/store/d1.d.mts.map +1 -1
- package/dist/store/d1.mjs +88 -29
- package/dist/store/d1.mjs.map +1 -1
- package/dist/store/erasure.cjs +22 -0
- package/dist/store/erasure.cjs.map +1 -0
- package/dist/store/erasure.d.cts +18 -0
- package/dist/store/erasure.d.cts.map +1 -0
- package/dist/store/erasure.d.mts +18 -0
- package/dist/store/erasure.d.mts.map +1 -0
- package/dist/store/erasure.mjs +21 -0
- package/dist/store/erasure.mjs.map +1 -0
- package/dist/store/index.cjs +2 -0
- package/dist/store/index.cjs.map +1 -1
- package/dist/store/index.d.cts +67 -13
- package/dist/store/index.d.cts.map +1 -1
- package/dist/store/index.d.mts +67 -13
- package/dist/store/index.d.mts.map +1 -1
- package/dist/store/index.mjs +2 -1
- package/dist/store/index.mjs.map +1 -1
- package/dist/store/postgres-reporting.cjs +125 -62
- package/dist/store/postgres-reporting.cjs.map +1 -1
- package/dist/store/postgres-reporting.d.cts +30 -2
- package/dist/store/postgres-reporting.d.cts.map +1 -1
- package/dist/store/postgres-reporting.d.mts +30 -2
- package/dist/store/postgres-reporting.d.mts.map +1 -1
- package/dist/store/postgres-reporting.mjs +125 -62
- package/dist/store/postgres-reporting.mjs.map +1 -1
- package/dist/store/postgres.cjs +179 -60
- package/dist/store/postgres.cjs.map +1 -1
- package/dist/store/postgres.d.cts +44 -7
- package/dist/store/postgres.d.cts.map +1 -1
- package/dist/store/postgres.d.mts +44 -7
- package/dist/store/postgres.d.mts.map +1 -1
- package/dist/store/postgres.mjs +179 -60
- package/dist/store/postgres.mjs.map +1 -1
- package/dist/store/tables.cjs +7 -5
- package/dist/store/tables.cjs.map +1 -1
- package/dist/store/tables.d.cts.map +1 -1
- package/dist/store/tables.d.mts.map +1 -1
- package/dist/store/tables.mjs +7 -5
- package/dist/store/tables.mjs.map +1 -1
- package/dist/store/user-lock.cjs +43 -0
- package/dist/store/user-lock.cjs.map +1 -0
- package/dist/store/user-lock.d.cts +32 -0
- package/dist/store/user-lock.d.cts.map +1 -0
- package/dist/store/user-lock.d.mts +32 -0
- package/dist/store/user-lock.d.mts.map +1 -0
- package/dist/store/user-lock.mjs +40 -0
- package/dist/store/user-lock.mjs.map +1 -0
- package/dist/transports/cloudflare-email.cjs +3 -2
- package/dist/transports/cloudflare-email.cjs.map +1 -1
- package/dist/transports/cloudflare-email.mjs +3 -2
- package/dist/transports/cloudflare-email.mjs.map +1 -1
- package/dist/transports/ses.cjs +52 -7
- package/dist/transports/ses.cjs.map +1 -1
- package/dist/transports/ses.d.cts +9 -1
- package/dist/transports/ses.d.cts.map +1 -1
- package/dist/transports/ses.d.mts +9 -1
- package/dist/transports/ses.d.mts.map +1 -1
- package/dist/transports/ses.mjs +52 -8
- package/dist/transports/ses.mjs.map +1 -1
- package/dist/webpush.cjs +133 -47
- package/dist/webpush.cjs.map +1 -1
- package/dist/webpush.d.cts +49 -8
- package/dist/webpush.d.cts.map +1 -1
- package/dist/webpush.d.mts +49 -8
- package/dist/webpush.d.mts.map +1 -1
- package/dist/webpush.mjs +130 -47
- package/dist/webpush.mjs.map +1 -1
- package/package.json +1 -1
- package/src/cloudflare/schema.sql +27 -6
- package/dist/host/router.cjs.map +0 -1
- package/dist/host/router.d.cts.map +0 -1
- package/dist/host/router.d.mts.map +0 -1
- package/dist/host/router.mjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform.mjs","names":[],"sources":["../../src/host/platform.ts"],"sourcesContent":["import type { D1DatabaseLike, EmailBindingLike } from '../cloudflare/bindings';\nimport type { ExecutionStart } from '../store/index';\n\n/**\n * The runtime port: what the host needs from the platform it runs on, whichever one that is.\n * Cloudflare's Workflow binding satisfies it (`cloudflareJourneyBinding`), the AWS adapter's\n * `AwsInstances` implements it over durable executions.\n */\n\nexport interface WorkflowInstanceLike {\n id: string;\n sendEvent(event: { type: string; payload?: unknown }): Promise<void>;\n /** Stop the instance for good (Cloudflare Workflows' `terminate`). */\n terminate?(): Promise<void>;\n /** Where it stands (`complete`, `errored`, `terminated`, `running`, …). */\n status?(): Promise<{ status: string }>;\n}\n\n/** How the host starts, wakes and stops instances. */\nexport interface WorkflowBindingLike {\n create(options: { id: string; params: unknown }): Promise<WorkflowInstanceLike>;\n /** The instance, or an `InstanceNotFoundError` when the runtime does not know the id. */\n get(id: string): Promise<WorkflowInstanceLike>;\n /**\n * For a runtime whose start is a submit that can be lost in flight (AWS): the start to record\n * with the entry, in one transaction. A failed `create` then keeps the entry — its recorded\n * start is retried by the next ingest for the pair (`resume`) or by maintenance, never rolled\n * back and re-entered as a second execution.\n */\n plan?(options: {\n id: string;\n params: JourneyParams;\n ts: number;\n /** The code version whose routes named `params.contentHash` (absent after a forced deploy). */\n version?: string | undefined;\n }): Promise<ExecutionStart>;\n /** Retry the recorded, unconfirmed start of the (workflow, user) entry still running, if any. */\n resume?(entry: { workflow: string; userId: string }): Promise<void>;\n /**\n * Maintenance the sweep runs first: old unconfirmed starts retried (or given up on), and\n * executions the platform ended on its own (timeout, stop, crash) reconciled into the ledger.\n */\n maintain?(): Promise<void>;\n}\n\n/**\n * The runtime does not know the instance: the only lookup failure that lets the host drop the\n * instance's wakes, or count it as stopped. Every `WorkflowBindingLike.get` answers it this way.\n */\nexport class InstanceNotFoundError extends Error {\n override readonly name = 'InstanceNotFoundError';\n constructor(\n readonly instanceId: string,\n options?: ErrorOptions\n ) {\n super(`no such instance: ${instanceId}`, options);\n }\n}\n\n/** The event type the host uses to wake waiting instances (sendEvent's `type`). */\nexport const WAKE_EVENT_TYPE = 'wake';\n\n/** Journey instance parameters, passed by the host when it creates one. */\nexport interface JourneyParams {\n workflowId: string;\n /** The IR version pinned at entry. */\n contentHash: string;\n userId: string;\n trigger: { event: string; payload: Record<string, unknown> };\n [key: string]: unknown;\n}\n\n/** Every binding, variable and secret the journey host reads (names match the wrangler config). */\nexport interface JourneyEnv {\n /**\n * The default D1 store's database (events / profiles / segments / triggers / entries /\n * subscriptions / workflow IR). Optional: a host on another JourneyStore (Postgres, say)\n * does not declare it.\n */\n DB?: D1DatabaseLike;\n /** The runtime port: creating instances, waking and stopping them. */\n JOURNEY: WorkflowBindingLike;\n /** Optional message-delivery webhook (defaults to the console logging sender). */\n MESSAGE_WEBHOOK_URL?: string;\n /**\n * Bearer token required on every mutating HTTP endpoint (`Authorization:\n * Bearer <token>`). Unset = open — acceptable only for local dev; /deploy in\n * particular rewrites the whole routing table, so production must set this.\n */\n API_TOKEN?: string;\n /**\n * The host's public origin. With a store that keeps reporting (Postgres), journey emails'\n * links and open pixel point at its `/t/...` routes; without it emails go out untracked.\n */\n WORKFLOW_ORIGIN?: string;\n /** Which `workflow.config.ts` entry applies (`development`, `production`, …); production when unset. */\n ENVIRONMENT?: string;\n /** Cloudflare Email Sending, when the config has `emails`. */\n EMAIL?: EmailBindingLike;\n /** The APNs auth key (`.p8`, base64) named by the config's `apns.keyId`. */\n APNS_PRIVATE_KEY_BASE64?: string;\n /** The Firebase service account (base64 JSON) the config's `fcm` sends with. */\n GOOGLE_APPLICATION_CREDENTIALS_BASE64?: string;\n /** The VAPID key pair web pushes are signed with (`generateVapidKeys`); the config's `webPush` turns the channel on. */\n VAPID_PUBLIC_KEY?: string;\n VAPID_PRIVATE_KEY?: string;\n /**\n * Cloudflare's version metadata binding (wrangler `\"version_metadata\": { \"binding\":\n * \"CF_VERSION_METADATA\" }`). With it, and a store that records the live version (Postgres),\n * journeyWorker makes the bundle compiled into each new version live by itself: `wrangler\n * deploy` is the whole deploy.\n */\n CF_VERSION_METADATA?: { id: string; tag?: string; timestamp: string };\n /** Development only: the HTTP/2 relay `wrangler dev` sends APNs requests through (see `@shware/workflow/apns-relay`). */\n APNS_RELAY_ORIGIN?: string;\n}\n"],"mappings":";;;;;AAiDA,IAAa,wBAAb,cAA2C,MAAM;CAGpC;CAFX,OAAyB;CACzB,YACE,YACA,SACA;EACA,MAAM,qBAAqB,cAAc,OAAO;EAHvC,KAAA,aAAA;CAIX;AACF;;AAGA,MAAa,kBAAkB"}
|
package/dist/host/senders.cjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_engine_retry = require("../engine/retry.cjs");
|
|
2
3
|
//#region src/host/senders.ts
|
|
3
4
|
/** Development sender: structured logs, for eyeballing the pipeline. */
|
|
4
5
|
var LogMessageSender = class {
|
|
@@ -10,15 +11,19 @@ var LogMessageSender = class {
|
|
|
10
11
|
/**
|
|
11
12
|
* Webhook sender: POST to an external delivery service (a Resend/SES gateway,
|
|
12
13
|
* say). The idempotency key travels with it, so the receiver de-duplicates
|
|
13
|
-
* retries and replays on idempotencyKey.
|
|
14
|
+
* retries and replays on idempotencyKey. A 4xx other than 408 / 429 is the
|
|
15
|
+
* receiver refusing this message: a `PermanentError`, so the step does not
|
|
16
|
+
* retry into it; timeouts, rate limits and 5xx are retried.
|
|
14
17
|
*/
|
|
15
18
|
var WebhookMessageSender = class {
|
|
16
19
|
url;
|
|
17
|
-
|
|
20
|
+
fetchImpl;
|
|
21
|
+
constructor(url, fetchImpl = fetch) {
|
|
18
22
|
this.url = url;
|
|
23
|
+
this.fetchImpl = fetchImpl;
|
|
19
24
|
}
|
|
20
25
|
async send(message) {
|
|
21
|
-
const response = await
|
|
26
|
+
const response = await this.fetchImpl(this.url, {
|
|
22
27
|
method: "POST",
|
|
23
28
|
headers: {
|
|
24
29
|
"content-type": "application/json",
|
|
@@ -26,17 +31,20 @@ var WebhookMessageSender = class {
|
|
|
26
31
|
},
|
|
27
32
|
body: JSON.stringify(message)
|
|
28
33
|
});
|
|
29
|
-
if (
|
|
34
|
+
if (response.ok) return;
|
|
35
|
+
if (require_engine_retry.isRetryableStatus(response.status)) throw new Error(`message webhook failed: ${response.status}`);
|
|
36
|
+
throw new require_engine_retry.PermanentError(`message webhook refused the message: ${response.status}`);
|
|
30
37
|
}
|
|
31
38
|
};
|
|
32
39
|
/**
|
|
33
40
|
* One sender per channel: the runner has a single outlet, this fans it out.
|
|
34
|
-
* A channel without a sender fails the send
|
|
41
|
+
* A channel without a sender fails the send for good (`PermanentError`): the
|
|
42
|
+
* configuration is the same on every attempt.
|
|
35
43
|
*/
|
|
36
44
|
function routeByChannel(senders) {
|
|
37
45
|
return { async send(message) {
|
|
38
46
|
const sender = senders[message.channel];
|
|
39
|
-
if (sender === void 0) throw new
|
|
47
|
+
if (sender === void 0) throw new require_engine_retry.PermanentError(`no sender configured for channel '${message.channel}'`);
|
|
40
48
|
return sender.send(message);
|
|
41
49
|
} };
|
|
42
50
|
}
|
|
@@ -44,13 +52,14 @@ function routeByChannel(senders) {
|
|
|
44
52
|
* One push outlet per platform, chosen by the device each copy goes to (the
|
|
45
53
|
* engine sends one message per device, and the host's directory says which
|
|
46
54
|
* platform a token belongs to): `ios` straight to APNs, `android` through FCM,
|
|
47
|
-
* say. A platform with no sender fails the step
|
|
55
|
+
* say. A platform with no sender fails the step for good (`PermanentError`)
|
|
56
|
+
* rather than guessing or retrying the same configuration.
|
|
48
57
|
*/
|
|
49
58
|
function routeByPlatform(senders) {
|
|
50
59
|
return { async send(message) {
|
|
51
60
|
const platform = message.device?.platform;
|
|
52
|
-
if (platform === void 0) throw new
|
|
53
|
-
if (!Object.hasOwn(senders, platform)) throw new
|
|
61
|
+
if (platform === void 0) throw new require_engine_retry.PermanentError(`push for user '${message.userId}' carries no device`);
|
|
62
|
+
if (!Object.hasOwn(senders, platform)) throw new require_engine_retry.PermanentError(`no push sender configured for platform '${platform}'`);
|
|
54
63
|
return senders[platform].send(message);
|
|
55
64
|
} };
|
|
56
65
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"senders.cjs","names":[],"sources":["../../src/host/senders.ts"],"sourcesContent":["import type { MessageSender, OutboundMessage } from '../engine';\n\n/** Development sender: structured logs, for eyeballing the pipeline. */\nexport class LogMessageSender implements MessageSender {\n async send(message: OutboundMessage): Promise<void> {\n const { tracking: _tracking, ...logged } = message;\n console.log(`[send] ${JSON.stringify(logged)}`);\n }\n}\n\n/**\n * Webhook sender: POST to an external delivery service (a Resend/SES gateway,\n * say). The idempotency key travels with it, so the receiver de-duplicates\n * retries and replays on idempotencyKey.\n */\nexport class WebhookMessageSender implements MessageSender {\n constructor(private readonly url: string) {}\n\n async send(message: OutboundMessage): Promise<void> {\n const response = await
|
|
1
|
+
{"version":3,"file":"senders.cjs","names":["isRetryableStatus","PermanentError"],"sources":["../../src/host/senders.ts"],"sourcesContent":["import type { MessageSender, OutboundMessage } from '../engine';\nimport { PermanentError, isRetryableStatus } from '../engine/retry';\n\n/** Development sender: structured logs, for eyeballing the pipeline. */\nexport class LogMessageSender implements MessageSender {\n async send(message: OutboundMessage): Promise<void> {\n const { tracking: _tracking, ...logged } = message;\n console.log(`[send] ${JSON.stringify(logged)}`);\n }\n}\n\n/**\n * Webhook sender: POST to an external delivery service (a Resend/SES gateway,\n * say). The idempotency key travels with it, so the receiver de-duplicates\n * retries and replays on idempotencyKey. A 4xx other than 408 / 429 is the\n * receiver refusing this message: a `PermanentError`, so the step does not\n * retry into it; timeouts, rate limits and 5xx are retried.\n */\nexport class WebhookMessageSender implements MessageSender {\n constructor(\n private readonly url: string,\n private readonly fetchImpl: typeof fetch = fetch\n ) {}\n\n async send(message: OutboundMessage): Promise<void> {\n const response = await this.fetchImpl(this.url, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'idempotency-key': message.idempotencyKey,\n },\n body: JSON.stringify(message),\n });\n if (response.ok) return;\n if (isRetryableStatus(response.status)) {\n throw new Error(`message webhook failed: ${response.status}`);\n }\n throw new PermanentError(`message webhook refused the message: ${response.status}`);\n }\n}\n\n/**\n * One sender per channel: the runner has a single outlet, this fans it out.\n * A channel without a sender fails the send for good (`PermanentError`): the\n * configuration is the same on every attempt.\n */\nexport function routeByChannel(\n senders: Partial<Record<OutboundMessage['channel'], MessageSender>>\n): MessageSender {\n return {\n async send(message) {\n const sender = senders[message.channel];\n if (sender === undefined) {\n throw new PermanentError(`no sender configured for channel '${message.channel}'`);\n }\n return sender.send(message);\n },\n };\n}\n\n/**\n * One push outlet per platform, chosen by the device each copy goes to (the\n * engine sends one message per device, and the host's directory says which\n * platform a token belongs to): `ios` straight to APNs, `android` through FCM,\n * say. A platform with no sender fails the step for good (`PermanentError`)\n * rather than guessing or retrying the same configuration.\n */\nexport function routeByPlatform(senders: Record<string, MessageSender>): MessageSender {\n return {\n async send(message) {\n const platform = message.device?.platform;\n if (platform === undefined) {\n throw new PermanentError(`push for user '${message.userId}' carries no device`);\n }\n if (!Object.hasOwn(senders, platform)) {\n throw new PermanentError(`no push sender configured for platform '${platform}'`);\n }\n return senders[platform].send(message);\n },\n };\n}\n"],"mappings":";;;;AAIA,IAAa,mBAAb,MAAuD;CACrD,MAAM,KAAK,SAAyC;EAClD,MAAM,EAAE,UAAU,WAAW,GAAG,WAAW;EAC3C,QAAQ,IAAI,UAAU,KAAK,UAAU,MAAM,GAAG;CAChD;AACF;;;;;;;;AASA,IAAa,uBAAb,MAA2D;CAEtC;CACA;CAFnB,YACE,KACA,YAA2C,OAC3C;EAFiB,KAAA,MAAA;EACA,KAAA,YAAA;CAChB;CAEH,MAAM,KAAK,SAAyC;EAClD,MAAM,WAAW,MAAM,KAAK,UAAU,KAAK,KAAK;GAC9C,QAAQ;GACR,SAAS;IACP,gBAAgB;IAChB,mBAAmB,QAAQ;GAC7B;GACA,MAAM,KAAK,UAAU,OAAO;EAC9B,CAAC;EACD,IAAI,SAAS,IAAI;EACjB,IAAIA,qBAAAA,kBAAkB,SAAS,MAAM,GACnC,MAAM,IAAI,MAAM,2BAA2B,SAAS,QAAQ;EAE9D,MAAM,IAAIC,qBAAAA,eAAe,wCAAwC,SAAS,QAAQ;CACpF;AACF;;;;;;AAOA,SAAgB,eACd,SACe;CACf,OAAO,EACL,MAAM,KAAK,SAAS;EAClB,MAAM,SAAS,QAAQ,QAAQ;EAC/B,IAAI,WAAW,KAAA,GACb,MAAM,IAAIA,qBAAAA,eAAe,qCAAqC,QAAQ,QAAQ,EAAE;EAElF,OAAO,OAAO,KAAK,OAAO;CAC5B,EACF;AACF;;;;;;;;AASA,SAAgB,gBAAgB,SAAuD;CACrF,OAAO,EACL,MAAM,KAAK,SAAS;EAClB,MAAM,WAAW,QAAQ,QAAQ;EACjC,IAAI,aAAa,KAAA,GACf,MAAM,IAAIA,qBAAAA,eAAe,kBAAkB,QAAQ,OAAO,oBAAoB;EAEhF,IAAI,CAAC,OAAO,OAAO,SAAS,QAAQ,GAClC,MAAM,IAAIA,qBAAAA,eAAe,2CAA2C,SAAS,EAAE;EAEjF,OAAO,QAAQ,SAAS,CAAC,KAAK,OAAO;CACvC,EACF;AACF"}
|
package/dist/host/senders.d.cts
CHANGED
|
@@ -8,23 +8,28 @@ export declare class LogMessageSender implements MessageSender {
|
|
|
8
8
|
/**
|
|
9
9
|
* Webhook sender: POST to an external delivery service (a Resend/SES gateway,
|
|
10
10
|
* say). The idempotency key travels with it, so the receiver de-duplicates
|
|
11
|
-
* retries and replays on idempotencyKey.
|
|
11
|
+
* retries and replays on idempotencyKey. A 4xx other than 408 / 429 is the
|
|
12
|
+
* receiver refusing this message: a `PermanentError`, so the step does not
|
|
13
|
+
* retry into it; timeouts, rate limits and 5xx are retried.
|
|
12
14
|
*/
|
|
13
15
|
export declare class WebhookMessageSender implements MessageSender {
|
|
14
16
|
private readonly url;
|
|
15
|
-
|
|
17
|
+
private readonly fetchImpl;
|
|
18
|
+
constructor(url: string, fetchImpl?: typeof fetch);
|
|
16
19
|
send(message: OutboundMessage): Promise<void>;
|
|
17
20
|
}
|
|
18
21
|
/**
|
|
19
22
|
* One sender per channel: the runner has a single outlet, this fans it out.
|
|
20
|
-
* A channel without a sender fails the send
|
|
23
|
+
* A channel without a sender fails the send for good (`PermanentError`): the
|
|
24
|
+
* configuration is the same on every attempt.
|
|
21
25
|
*/
|
|
22
26
|
export declare function routeByChannel(senders: Partial<Record<OutboundMessage['channel'], MessageSender>>): MessageSender;
|
|
23
27
|
/**
|
|
24
28
|
* One push outlet per platform, chosen by the device each copy goes to (the
|
|
25
29
|
* engine sends one message per device, and the host's directory says which
|
|
26
30
|
* platform a token belongs to): `ios` straight to APNs, `android` through FCM,
|
|
27
|
-
* say. A platform with no sender fails the step
|
|
31
|
+
* say. A platform with no sender fails the step for good (`PermanentError`)
|
|
32
|
+
* rather than guessing or retrying the same configuration.
|
|
28
33
|
*/
|
|
29
34
|
export declare function routeByPlatform(senders: Record<string, MessageSender>): MessageSender;
|
|
30
35
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"senders.d.cts","names":[],"sources":["../../src/host/senders.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"senders.d.cts","names":[],"sources":["../../src/host/senders.ts"],"mappings":";;;;qBAIa,4BAA4B;EACjC,KAAK,SAAS,kBAAkB;;;;;;;;;qBAa3B,gCAAgC;mBAExB;mBACA;EAFnB,YACmB,aACA,mBAAkB;EAG/B,KAAK,SAAS,kBAAkB;;;;;;;wBAsBxB,eACd,SAAS,QAAQ,OAAO,4BAA4B,kBACnD;;;;;;;;wBAmBa,gBAAgB,SAAS,eAAe,iBAAiB"}
|
package/dist/host/senders.d.mts
CHANGED
|
@@ -8,23 +8,28 @@ export declare class LogMessageSender implements MessageSender {
|
|
|
8
8
|
/**
|
|
9
9
|
* Webhook sender: POST to an external delivery service (a Resend/SES gateway,
|
|
10
10
|
* say). The idempotency key travels with it, so the receiver de-duplicates
|
|
11
|
-
* retries and replays on idempotencyKey.
|
|
11
|
+
* retries and replays on idempotencyKey. A 4xx other than 408 / 429 is the
|
|
12
|
+
* receiver refusing this message: a `PermanentError`, so the step does not
|
|
13
|
+
* retry into it; timeouts, rate limits and 5xx are retried.
|
|
12
14
|
*/
|
|
13
15
|
export declare class WebhookMessageSender implements MessageSender {
|
|
14
16
|
private readonly url;
|
|
15
|
-
|
|
17
|
+
private readonly fetchImpl;
|
|
18
|
+
constructor(url: string, fetchImpl?: typeof fetch);
|
|
16
19
|
send(message: OutboundMessage): Promise<void>;
|
|
17
20
|
}
|
|
18
21
|
/**
|
|
19
22
|
* One sender per channel: the runner has a single outlet, this fans it out.
|
|
20
|
-
* A channel without a sender fails the send
|
|
23
|
+
* A channel without a sender fails the send for good (`PermanentError`): the
|
|
24
|
+
* configuration is the same on every attempt.
|
|
21
25
|
*/
|
|
22
26
|
export declare function routeByChannel(senders: Partial<Record<OutboundMessage['channel'], MessageSender>>): MessageSender;
|
|
23
27
|
/**
|
|
24
28
|
* One push outlet per platform, chosen by the device each copy goes to (the
|
|
25
29
|
* engine sends one message per device, and the host's directory says which
|
|
26
30
|
* platform a token belongs to): `ios` straight to APNs, `android` through FCM,
|
|
27
|
-
* say. A platform with no sender fails the step
|
|
31
|
+
* say. A platform with no sender fails the step for good (`PermanentError`)
|
|
32
|
+
* rather than guessing or retrying the same configuration.
|
|
28
33
|
*/
|
|
29
34
|
export declare function routeByPlatform(senders: Record<string, MessageSender>): MessageSender;
|
|
30
35
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"senders.d.mts","names":[],"sources":["../../src/host/senders.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"senders.d.mts","names":[],"sources":["../../src/host/senders.ts"],"mappings":";;;;qBAIa,4BAA4B;EACjC,KAAK,SAAS,kBAAkB;;;;;;;;;qBAa3B,gCAAgC;mBAExB;mBACA;EAFnB,YACmB,aACA,mBAAkB;EAG/B,KAAK,SAAS,kBAAkB;;;;;;;wBAsBxB,eACd,SAAS,QAAQ,OAAO,4BAA4B,kBACnD;;;;;;;;wBAmBa,gBAAgB,SAAS,eAAe,iBAAiB"}
|
package/dist/host/senders.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PermanentError, isRetryableStatus } from "../engine/retry.mjs";
|
|
1
2
|
//#region src/host/senders.ts
|
|
2
3
|
/** Development sender: structured logs, for eyeballing the pipeline. */
|
|
3
4
|
var LogMessageSender = class {
|
|
@@ -9,15 +10,19 @@ var LogMessageSender = class {
|
|
|
9
10
|
/**
|
|
10
11
|
* Webhook sender: POST to an external delivery service (a Resend/SES gateway,
|
|
11
12
|
* say). The idempotency key travels with it, so the receiver de-duplicates
|
|
12
|
-
* retries and replays on idempotencyKey.
|
|
13
|
+
* retries and replays on idempotencyKey. A 4xx other than 408 / 429 is the
|
|
14
|
+
* receiver refusing this message: a `PermanentError`, so the step does not
|
|
15
|
+
* retry into it; timeouts, rate limits and 5xx are retried.
|
|
13
16
|
*/
|
|
14
17
|
var WebhookMessageSender = class {
|
|
15
18
|
url;
|
|
16
|
-
|
|
19
|
+
fetchImpl;
|
|
20
|
+
constructor(url, fetchImpl = fetch) {
|
|
17
21
|
this.url = url;
|
|
22
|
+
this.fetchImpl = fetchImpl;
|
|
18
23
|
}
|
|
19
24
|
async send(message) {
|
|
20
|
-
const response = await
|
|
25
|
+
const response = await this.fetchImpl(this.url, {
|
|
21
26
|
method: "POST",
|
|
22
27
|
headers: {
|
|
23
28
|
"content-type": "application/json",
|
|
@@ -25,17 +30,20 @@ var WebhookMessageSender = class {
|
|
|
25
30
|
},
|
|
26
31
|
body: JSON.stringify(message)
|
|
27
32
|
});
|
|
28
|
-
if (
|
|
33
|
+
if (response.ok) return;
|
|
34
|
+
if (isRetryableStatus(response.status)) throw new Error(`message webhook failed: ${response.status}`);
|
|
35
|
+
throw new PermanentError(`message webhook refused the message: ${response.status}`);
|
|
29
36
|
}
|
|
30
37
|
};
|
|
31
38
|
/**
|
|
32
39
|
* One sender per channel: the runner has a single outlet, this fans it out.
|
|
33
|
-
* A channel without a sender fails the send
|
|
40
|
+
* A channel without a sender fails the send for good (`PermanentError`): the
|
|
41
|
+
* configuration is the same on every attempt.
|
|
34
42
|
*/
|
|
35
43
|
function routeByChannel(senders) {
|
|
36
44
|
return { async send(message) {
|
|
37
45
|
const sender = senders[message.channel];
|
|
38
|
-
if (sender === void 0) throw new
|
|
46
|
+
if (sender === void 0) throw new PermanentError(`no sender configured for channel '${message.channel}'`);
|
|
39
47
|
return sender.send(message);
|
|
40
48
|
} };
|
|
41
49
|
}
|
|
@@ -43,13 +51,14 @@ function routeByChannel(senders) {
|
|
|
43
51
|
* One push outlet per platform, chosen by the device each copy goes to (the
|
|
44
52
|
* engine sends one message per device, and the host's directory says which
|
|
45
53
|
* platform a token belongs to): `ios` straight to APNs, `android` through FCM,
|
|
46
|
-
* say. A platform with no sender fails the step
|
|
54
|
+
* say. A platform with no sender fails the step for good (`PermanentError`)
|
|
55
|
+
* rather than guessing or retrying the same configuration.
|
|
47
56
|
*/
|
|
48
57
|
function routeByPlatform(senders) {
|
|
49
58
|
return { async send(message) {
|
|
50
59
|
const platform = message.device?.platform;
|
|
51
|
-
if (platform === void 0) throw new
|
|
52
|
-
if (!Object.hasOwn(senders, platform)) throw new
|
|
60
|
+
if (platform === void 0) throw new PermanentError(`push for user '${message.userId}' carries no device`);
|
|
61
|
+
if (!Object.hasOwn(senders, platform)) throw new PermanentError(`no push sender configured for platform '${platform}'`);
|
|
53
62
|
return senders[platform].send(message);
|
|
54
63
|
} };
|
|
55
64
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"senders.mjs","names":[],"sources":["../../src/host/senders.ts"],"sourcesContent":["import type { MessageSender, OutboundMessage } from '../engine';\n\n/** Development sender: structured logs, for eyeballing the pipeline. */\nexport class LogMessageSender implements MessageSender {\n async send(message: OutboundMessage): Promise<void> {\n const { tracking: _tracking, ...logged } = message;\n console.log(`[send] ${JSON.stringify(logged)}`);\n }\n}\n\n/**\n * Webhook sender: POST to an external delivery service (a Resend/SES gateway,\n * say). The idempotency key travels with it, so the receiver de-duplicates\n * retries and replays on idempotencyKey.\n */\nexport class WebhookMessageSender implements MessageSender {\n constructor(private readonly url: string) {}\n\n async send(message: OutboundMessage): Promise<void> {\n const response = await
|
|
1
|
+
{"version":3,"file":"senders.mjs","names":[],"sources":["../../src/host/senders.ts"],"sourcesContent":["import type { MessageSender, OutboundMessage } from '../engine';\nimport { PermanentError, isRetryableStatus } from '../engine/retry';\n\n/** Development sender: structured logs, for eyeballing the pipeline. */\nexport class LogMessageSender implements MessageSender {\n async send(message: OutboundMessage): Promise<void> {\n const { tracking: _tracking, ...logged } = message;\n console.log(`[send] ${JSON.stringify(logged)}`);\n }\n}\n\n/**\n * Webhook sender: POST to an external delivery service (a Resend/SES gateway,\n * say). The idempotency key travels with it, so the receiver de-duplicates\n * retries and replays on idempotencyKey. A 4xx other than 408 / 429 is the\n * receiver refusing this message: a `PermanentError`, so the step does not\n * retry into it; timeouts, rate limits and 5xx are retried.\n */\nexport class WebhookMessageSender implements MessageSender {\n constructor(\n private readonly url: string,\n private readonly fetchImpl: typeof fetch = fetch\n ) {}\n\n async send(message: OutboundMessage): Promise<void> {\n const response = await this.fetchImpl(this.url, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'idempotency-key': message.idempotencyKey,\n },\n body: JSON.stringify(message),\n });\n if (response.ok) return;\n if (isRetryableStatus(response.status)) {\n throw new Error(`message webhook failed: ${response.status}`);\n }\n throw new PermanentError(`message webhook refused the message: ${response.status}`);\n }\n}\n\n/**\n * One sender per channel: the runner has a single outlet, this fans it out.\n * A channel without a sender fails the send for good (`PermanentError`): the\n * configuration is the same on every attempt.\n */\nexport function routeByChannel(\n senders: Partial<Record<OutboundMessage['channel'], MessageSender>>\n): MessageSender {\n return {\n async send(message) {\n const sender = senders[message.channel];\n if (sender === undefined) {\n throw new PermanentError(`no sender configured for channel '${message.channel}'`);\n }\n return sender.send(message);\n },\n };\n}\n\n/**\n * One push outlet per platform, chosen by the device each copy goes to (the\n * engine sends one message per device, and the host's directory says which\n * platform a token belongs to): `ios` straight to APNs, `android` through FCM,\n * say. A platform with no sender fails the step for good (`PermanentError`)\n * rather than guessing or retrying the same configuration.\n */\nexport function routeByPlatform(senders: Record<string, MessageSender>): MessageSender {\n return {\n async send(message) {\n const platform = message.device?.platform;\n if (platform === undefined) {\n throw new PermanentError(`push for user '${message.userId}' carries no device`);\n }\n if (!Object.hasOwn(senders, platform)) {\n throw new PermanentError(`no push sender configured for platform '${platform}'`);\n }\n return senders[platform].send(message);\n },\n };\n}\n"],"mappings":";;;AAIA,IAAa,mBAAb,MAAuD;CACrD,MAAM,KAAK,SAAyC;EAClD,MAAM,EAAE,UAAU,WAAW,GAAG,WAAW;EAC3C,QAAQ,IAAI,UAAU,KAAK,UAAU,MAAM,GAAG;CAChD;AACF;;;;;;;;AASA,IAAa,uBAAb,MAA2D;CAEtC;CACA;CAFnB,YACE,KACA,YAA2C,OAC3C;EAFiB,KAAA,MAAA;EACA,KAAA,YAAA;CAChB;CAEH,MAAM,KAAK,SAAyC;EAClD,MAAM,WAAW,MAAM,KAAK,UAAU,KAAK,KAAK;GAC9C,QAAQ;GACR,SAAS;IACP,gBAAgB;IAChB,mBAAmB,QAAQ;GAC7B;GACA,MAAM,KAAK,UAAU,OAAO;EAC9B,CAAC;EACD,IAAI,SAAS,IAAI;EACjB,IAAI,kBAAkB,SAAS,MAAM,GACnC,MAAM,IAAI,MAAM,2BAA2B,SAAS,QAAQ;EAE9D,MAAM,IAAI,eAAe,wCAAwC,SAAS,QAAQ;CACpF;AACF;;;;;;AAOA,SAAgB,eACd,SACe;CACf,OAAO,EACL,MAAM,KAAK,SAAS;EAClB,MAAM,SAAS,QAAQ,QAAQ;EAC/B,IAAI,WAAW,KAAA,GACb,MAAM,IAAI,eAAe,qCAAqC,QAAQ,QAAQ,EAAE;EAElF,OAAO,OAAO,KAAK,OAAO;CAC5B,EACF;AACF;;;;;;;;AASA,SAAgB,gBAAgB,SAAuD;CACrF,OAAO,EACL,MAAM,KAAK,SAAS;EAClB,MAAM,WAAW,QAAQ,QAAQ;EACjC,IAAI,aAAa,KAAA,GACf,MAAM,IAAI,eAAe,kBAAkB,QAAQ,OAAO,oBAAoB;EAEhF,IAAI,CAAC,OAAO,OAAO,SAAS,QAAQ,GAClC,MAAM,IAAI,eAAe,2CAA2C,SAAS,EAAE;EAEjF,OAAO,QAAQ,SAAS,CAAC,KAAK,OAAO;CACvC,EACF;AACF"}
|
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_hash = require("./hash.cjs");
|
|
3
2
|
const require_dsl_action = require("./dsl/action.cjs");
|
|
4
3
|
const require_dsl_refs = require("./dsl/refs.cjs");
|
|
5
4
|
const require_dsl_template = require("./dsl/template.cjs");
|
|
@@ -8,6 +7,7 @@ const require_email = require("./email.cjs");
|
|
|
8
7
|
const require_templates = require("./templates.cjs");
|
|
9
8
|
const require_dsl_flow = require("./dsl/flow.cjs");
|
|
10
9
|
const require_dsl_trigger = require("./dsl/trigger.cjs");
|
|
10
|
+
const require_hash = require("./hash.cjs");
|
|
11
11
|
const require_ir = require("./ir.cjs");
|
|
12
12
|
const require_dsl_workflow = require("./dsl/workflow.cjs");
|
|
13
13
|
const require_dsl_bundle = require("./dsl/bundle.cjs");
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { canonicalJSON, fullHash, semanticHash, sha256Hex, stripMeta, stripProvenance } from "./hash.mjs";
|
|
2
1
|
import { action } from "./dsl/action.mjs";
|
|
3
2
|
import { event, user } from "./dsl/refs.mjs";
|
|
4
3
|
import { template } from "./dsl/template.mjs";
|
|
@@ -7,6 +6,7 @@ import { assertEmailAddress, assertEmailConfig, assertEmailMetadata, assertUnsub
|
|
|
7
6
|
import { CHANNEL_OF, TemplateValidationError, assertSupportedSchema, discordTemplate, emailTemplate, indexTemplates, isDiscordTemplate, isEmailTemplate, isPushTemplate, isSlackTemplate, isTemplate, lookupTemplate, parseTemplateProps, pushTemplate, registryValidator, renderDiscordContent, renderPushContent, renderSlackContent, slackTemplate } from "./templates.mjs";
|
|
8
7
|
import { flow } from "./dsl/flow.mjs";
|
|
9
8
|
import { trigger } from "./dsl/trigger.mjs";
|
|
9
|
+
import { canonicalJSON, fullHash, semanticHash, sha256Hex, stripMeta, stripProvenance } from "./hash.mjs";
|
|
10
10
|
import { ActionIR, BundleIR, ChannelIR, ConditionIR, DurationIR, GoalIR, IR_VERSION, MetaIR, NodeIR, NodeMetaIR, PropValueIR, PropertyOperatorIR, ScalarIR, SegmentIR, SourceLocIR, TemplateIR, TriggerIR, UserPropertyRefIR, WeekdayIR, WorkflowIR } from "./ir.mjs";
|
|
11
11
|
import { workflow } from "./dsl/workflow.mjs";
|
|
12
12
|
import { compileBundle } from "./dsl/bundle.mjs";
|
package/dist/ir.cjs
CHANGED
|
@@ -268,8 +268,7 @@ const NodeIR = zod_mini.lazy(() => zod_mini.discriminatedUnion("type", [
|
|
|
268
268
|
...nodeBase,
|
|
269
269
|
type: zod_mini.literal("action"),
|
|
270
270
|
action: zod_mini.string(),
|
|
271
|
-
args: zod_mini.record(zod_mini.string(), PropValueIR)
|
|
272
|
-
codeHash: zod_mini.optional(zod_mini.string())
|
|
271
|
+
args: zod_mini.record(zod_mini.string(), PropValueIR)
|
|
273
272
|
})
|
|
274
273
|
]));
|
|
275
274
|
const GoalIR = zod_mini.object({
|
|
@@ -320,14 +319,13 @@ const TemplateIR = zod_mini.object({
|
|
|
320
319
|
channel: ChannelIR
|
|
321
320
|
});
|
|
322
321
|
/**
|
|
323
|
-
* Action manifest entry: IR records the name
|
|
324
|
-
*
|
|
325
|
-
*
|
|
322
|
+
* Action manifest entry: IR records the name; the implementation solidifies
|
|
323
|
+
* into the Worker bundle at deploy time, never into IR (custom code stays out
|
|
324
|
+
* of the data plane by design).
|
|
326
325
|
*/
|
|
327
326
|
const ActionIR = zod_mini.object({
|
|
328
327
|
irVersion: zod_mini.literal(2),
|
|
329
|
-
name: zod_mini.string()
|
|
330
|
-
codeHash: zod_mini.string()
|
|
328
|
+
name: zod_mini.string()
|
|
331
329
|
});
|
|
332
330
|
/**
|
|
333
331
|
* Deployment unit: the complete snapshot one `deploy` produces (terraform
|
package/dist/ir.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ir.cjs","names":["z"],"sources":["../src/ir.ts"],"sourcesContent":["import * as z from 'zod/mini';\n\n/**\n * IR (Intermediate Representation) — what the DSL compiles to: serializable\n * JSON. It is the three-way contract between the engine that interprets it,\n * the UI (react-flow) that renders it, and diff / audit tooling.\n *\n * Validation strategy: the zod/mini schemas are the authoritative definition.\n * TS types are either inferred from a schema or hand-written under the same\n * name (zod cannot infer recursive types; the hand-written type is anchored\n * with a ZodMiniType<T> annotation, so any drift fails to compile). IR crosses\n * process boundaries (database, network, UI), so runtime validation is a must.\n *\n * ## Versioning (two independent axes)\n *\n * 1. IR format version (irVersion): evolution of the IR schema itself. Adding\n * optional fields does not bump it; only structurally incompatible changes\n * do (changing a discriminant, dropping a field, altering semantics).\n * Readers dispatch on irVersion to the matching schema/migrator; writers\n * always write the current version.\n * 2. Content version (contentHash): a content-addressed id for a single\n * definition's **execution semantics** — the canonical-JSON hash after\n * metadata (meta / label / reason) is stripped, see hash.ts. It is used to:\n * - pin a version when a user enters, so an in-flight journey runs the\n * pinned version to completion (the default policy; compatible hot\n * updates are a future explicit-migration topic). Known exception:\n * segment conditions are resolved **by id at evaluation time** — a\n * segment redeploy changes behaviour of in-flight journeys that\n * reference it. Pinning covers the workflow's own structure; per-hash\n * segment snapshots are future work;\n * - diff on deploy: same hash means unchanged, skip publishing;\n * - audit: answer \"which version was this user on\" at any point.\n *\n * ## Node ids\n *\n * Derived from the structural path at compile time — stable and deterministic:\n * root level '0', '1', …; the j-th child of a branch's i-th case is\n * '{branchId}.c{i}.{j}', the default arm '{branchId}.o.{j}'; cohort arms use\n * the arm name '{cohortId}.{armName}.{j}'; a waitUntil timeout flow '{id}.t.{j}'.\n * Editing a node's parameters keeps its id; inserting a node shifts the ids of\n * its later siblings (a known trade-off — locating in-flight users relies on\n * the pinned version rather than cross-version id alignment, and cross-version\n * mapping is an explicit migrator's job).\n */\n\nexport const IR_VERSION = 2;\n\n/* -------------------------------- Base values ------------------------------- */\n\nexport const ScalarIR = z.union([z.string(), z.number(), z.boolean()]);\nexport type ScalarIR = z.infer<typeof ScalarIR>;\n\n/** Duration: `value` keeps the DSL literal ('23 hours', for display), `ms` is what the engine uses. */\n/*\n * TODO: `value` is hashed alongside `ms`, so '7 days' / '1 week' / '168 hours'\n * — identical to the engine, which only reads ms — produce three different\n * contentHashes. Rewording a duration therefore reports as a semantic change in\n * plan and repins in-flight users. Moving `value` out of the hash (it is a\n * display string) would fix it; deferred because it changes existing hashes.\n */\nexport const DurationIR = z.object({ value: z.string(), ms: z.number() });\nexport type DurationIR = z.infer<typeof DurationIR>;\n\n/** User-property reference — isomorphic to the DSL's UserPropertyRef once serialized (phantom type dropped). */\nexport const UserPropertyRefIR = z.object({\n type: z.literal('user_property'),\n path: z.string(),\n});\nexport type UserPropertyRefIR = z.infer<typeof UserPropertyRefIR>;\n\n/** A message prop / event payload value: either a literal or a user-property reference. */\nexport const PropValueIR = z.union([ScalarIR, UserPropertyRefIR]);\nexport type PropValueIR = z.infer<typeof PropValueIR>;\n\nexport const ChannelIR = z.enum(['email', 'sms', 'push', 'in_app', 'slack', 'discord', 'survey']);\nexport type ChannelIR = z.infer<typeof ChannelIR>;\n\nexport const WeekdayIR = z.enum(['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']);\nexport type WeekdayIR = z.infer<typeof WeekdayIR>;\n\n/* --------------------------------- Provenance -------------------------------- */\n\n/**\n * Source location of the builder call that produced a definition — the IR's\n * source map. Lives under `meta`, so it is **excluded from contentHash**\n * (see hash.ts): provenance never affects execution identity or plan.\n * `file` is cwd-relative when compiled under Node, a URL under Vite dev.\n */\nexport const SourceLocIR = z.object({\n file: z.string(),\n line: z.number(),\n column: z.number(),\n});\nexport type SourceLocIR = z.infer<typeof SourceLocIR>;\n\n/** Node-level metadata: provenance only, for now. Excluded from contentHash. */\nexport const NodeMetaIR = z.object({\n loc: z.optional(SourceLocIR),\n});\nexport type NodeMetaIR = z.infer<typeof NodeMetaIR>;\n\n/* -------------------------------- Conditions -------------------------------- */\n\nexport const PropertyOperatorIR = z.enum([\n 'eq',\n 'ne',\n 'gt',\n 'gte',\n 'lt',\n 'lte',\n 'between',\n 'not_between',\n 'in_array',\n 'not_in_array',\n 'exists',\n 'not_exists',\n 'contains',\n 'not_contains',\n]);\nexport type PropertyOperatorIR = z.infer<typeof PropertyOperatorIR>;\n\n/**\n * Condition expression tree. The shape of value/values follows `op`:\n * eq/ne/gt/gte/lt/lte/contains/not_contains → value; in_array/not_in_array →\n * values; between/not_between → values = [min, max]; exists/not_exists → neither.\n *\n * `payload` predicates test the payload of an event (dotted paths reach nested\n * fields). They are only valid inside a `where` subtree — `performed.where`\n * and the event trigger's `where` — where an event row is in scope; the\n * compiler (compileBundle) and the evaluator both enforce the placement.\n * TODO: runtime refinement narrowing by op.\n */\n/**\n * Provenance rides on conditions too, so the studio can point at the exact\n * `eq(...)` / `and(...)` that produced a node — same rule as nodes: `meta` is\n * dropped before hashing, so carrying it never affects execution identity.\n */\ninterface ConditionBaseIR {\n meta?: NodeMetaIR | undefined;\n}\n\nexport type ConditionIR =\n | (ConditionBaseIR & { type: 'and'; conditions: ConditionIR[] })\n | (ConditionBaseIR & { type: 'or'; conditions: ConditionIR[] })\n | (ConditionBaseIR & { type: 'not'; condition: ConditionIR })\n /** Reference a segment by id (defined in SegmentIR, versioned separately). */\n | (ConditionBaseIR & { type: 'segment'; segment: string })\n | (ConditionBaseIR & {\n type: 'property';\n path: string;\n op: PropertyOperatorIR;\n value?: ScalarIR | undefined;\n values?: ScalarIR[] | undefined;\n })\n | (ConditionBaseIR & {\n /** Event payload predicate; `path` is dotted for nested fields ('tags.utm_source'). */\n type: 'payload';\n path: string;\n op: PropertyOperatorIR;\n value?: ScalarIR | undefined;\n values?: ScalarIR[] | undefined;\n })\n | (ConditionBaseIR & {\n type: 'performed';\n event: string;\n /** Payload filter: only events matching it count (a payload-only subtree). */\n where?: ConditionIR | undefined;\n within?: DurationIR | undefined;\n count?: number | undefined;\n })\n /**\n * The user can be reached on the channel right now: for push, the host's\n * device directory lists at least one device; for the others, the channel's\n * recipient property (email, phone, …) is set.\n */\n | (ConditionBaseIR & { type: 'reachable'; channel: ChannelIR });\n\nconst conditionBase = { meta: z.optional(NodeMetaIR) };\n\nexport const ConditionIR: z.ZodMiniType<ConditionIR> = z.lazy(() =>\n z.discriminatedUnion('type', [\n z.object({ ...conditionBase, type: z.literal('and'), conditions: z.array(ConditionIR) }),\n z.object({ ...conditionBase, type: z.literal('or'), conditions: z.array(ConditionIR) }),\n z.object({ ...conditionBase, type: z.literal('not'), condition: ConditionIR }),\n z.object({ ...conditionBase, type: z.literal('segment'), segment: z.string() }),\n z.object({\n ...conditionBase,\n type: z.literal('property'),\n path: z.string(),\n op: PropertyOperatorIR,\n value: z.optional(ScalarIR),\n values: z.optional(z.array(ScalarIR)),\n }),\n z.object({\n ...conditionBase,\n type: z.literal('payload'),\n path: z.string(),\n op: PropertyOperatorIR,\n value: z.optional(ScalarIR),\n values: z.optional(z.array(ScalarIR)),\n }),\n z.object({\n ...conditionBase,\n type: z.literal('performed'),\n event: z.string(),\n where: z.optional(ConditionIR),\n within: z.optional(DurationIR),\n count: z.optional(z.number()),\n }),\n z.object({ ...conditionBase, type: z.literal('reachable'), channel: ChannelIR }),\n ])\n);\n\n/* --------------------------------- Triggers --------------------------------- */\n\n/**\n * Trigger shapes. The event trigger has two independent gates: `where` tests\n * the triggering event's payload (payload-only subtree, evaluated against the\n * incoming event before anything else), `filter` tests the user's profile.\n */\nexport const TriggerIR = z.discriminatedUnion('type', [\n z.object({\n type: z.literal('event'),\n event: z.string(),\n where: z.optional(ConditionIR),\n filter: z.optional(ConditionIR),\n }),\n z.object({ type: z.literal('segment'), segment: z.string() }),\n z.object({ type: z.literal('date'), at: z.string() }),\n z.object({ type: z.literal('webhook') }),\n]);\nexport type TriggerIR = z.infer<typeof TriggerIR>;\n\n/* ----------------------------------- Nodes ---------------------------------- */\n\ninterface NodeBaseIR {\n /** Stable id derived from the structural path (rules in the file header). */\n id: string;\n /** Optional node name: UI title / observability handle (branch's optional first argument lands here). */\n label?: string | undefined;\n /** Metadata (provenance); excluded from contentHash. */\n meta?: NodeMetaIR | undefined;\n}\n\nexport type NodeIR =\n | (NodeBaseIR & {\n type: 'message';\n channel: ChannelIR;\n template: string;\n props: Record<string, PropValueIR>;\n /**\n * Push only: which of the user's devices get a copy. Absent means every\n * device the directory lists; 'latest' only the most recently registered.\n */\n devices?: 'latest' | undefined;\n })\n | (NodeBaseIR & { type: 'delay'; duration: DurationIR })\n | (NodeBaseIR & { type: 'random_delay'; min: DurationIR; max: DurationIR })\n | (NodeBaseIR & {\n type: 'time_window';\n days: WeekdayIR[];\n between: [string, string];\n tz: string;\n })\n | (NodeBaseIR & {\n type: 'wait_until';\n condition: ConditionIR;\n timeout: DurationIR;\n onTimeout: 'continue' | 'exit' | NodeIR[];\n })\n | (NodeBaseIR & {\n type: 'branch';\n cases: { label?: string | undefined; condition: ConditionIR; flow: NodeIR[] }[];\n /** Default arm (the DSL's bare tail argument). Absent = fall through to the main line. */\n otherwise?: NodeIR[] | undefined;\n })\n | (NodeBaseIR & {\n type: 'filter';\n /** Audit-log line for the users this gate drops. Metadata: excluded from contentHash. */\n reason?: string | undefined;\n condition: ConditionIR;\n })\n | (NodeBaseIR & {\n type: 'cohort';\n /**\n * Explicit experiment key: bucketing hashes `userId:key` instead of the\n * structural node id, so the assignment survives node insertion/moves and\n * the experiment keeps one identity across workflow versions. Absent →\n * the node id is used (fine for one-off splits).\n */\n key?: string | undefined;\n /**\n * Arms in authoring order — the order the interpreter accumulates weights\n * in, so it decides which bucket range each arm owns. The DSL keeps that\n * promise by rejecting integer-like arm names, which JavaScript would\n * hoist to the front of the object in numeric order. Weights sum to 100.\n */\n arms: { name: string; weight: number; flow: NodeIR[] }[];\n })\n | (NodeBaseIR & {\n /** `reason` is the audit-log line. Metadata: excluded from contentHash. */\n type: 'exit';\n reason?: string | undefined;\n })\n | (NodeBaseIR & { type: 'send_event'; event: string; payload: Record<string, PropValueIR> })\n | (NodeBaseIR & {\n type: 'action';\n /** Registry name of the custom action; the code itself lives in the deployed Worker bundle. */\n action: string;\n /** Argument values; user_property references are resolved at run time, like message props. */\n args: Record<string, PropValueIR>;\n /**\n * Hash of the action's source at compile time — the code-identity pin.\n * IR carries identity only (dual-plane model): the runtime looks the\n * action up by name and compares this hash against the registered\n * implementation to detect version skew (warn by default).\n */\n codeHash?: string | undefined;\n });\n\nconst nodeBase = { id: z.string(), label: z.optional(z.string()), meta: z.optional(NodeMetaIR) };\n\nexport const NodeIR: z.ZodMiniType<NodeIR> = z.lazy(() =>\n z.discriminatedUnion('type', [\n z.object({\n ...nodeBase,\n type: z.literal('message'),\n channel: ChannelIR,\n template: z.string(),\n props: z.record(z.string(), PropValueIR),\n devices: z.optional(z.literal('latest')),\n }),\n z.object({ ...nodeBase, type: z.literal('delay'), duration: DurationIR }),\n z.object({ ...nodeBase, type: z.literal('random_delay'), min: DurationIR, max: DurationIR }),\n z.object({\n ...nodeBase,\n type: z.literal('time_window'),\n days: z.array(WeekdayIR),\n between: z.tuple([z.string(), z.string()]),\n tz: z.string(),\n }),\n z.object({\n ...nodeBase,\n type: z.literal('wait_until'),\n condition: ConditionIR,\n timeout: DurationIR,\n onTimeout: z.union([z.literal('continue'), z.literal('exit'), z.array(NodeIR)]),\n }),\n z.object({\n ...nodeBase,\n type: z.literal('branch'),\n cases: z.array(\n z.object({\n label: z.optional(z.string()),\n condition: ConditionIR,\n flow: z.array(NodeIR),\n })\n ),\n otherwise: z.optional(z.array(NodeIR)),\n }),\n z.object({\n ...nodeBase,\n type: z.literal('filter'),\n condition: ConditionIR,\n reason: z.optional(z.string()),\n }),\n z.object({\n ...nodeBase,\n type: z.literal('cohort'),\n key: z.optional(z.string()),\n arms: z.array(z.object({ name: z.string(), weight: z.number(), flow: z.array(NodeIR) })),\n }),\n z.object({ ...nodeBase, type: z.literal('exit'), reason: z.optional(z.string()) }),\n z.object({\n ...nodeBase,\n type: z.literal('send_event'),\n event: z.string(),\n payload: z.record(z.string(), PropValueIR),\n }),\n z.object({\n ...nodeBase,\n type: z.literal('action'),\n action: z.string(),\n args: z.record(z.string(), PropValueIR),\n codeHash: z.optional(z.string()),\n }),\n ])\n);\n\n/* ---------------------- Top-level definitions and bundle --------------------- */\n\nexport const GoalIR = z.object({\n condition: ConditionIR,\n within: z.optional(DurationIR),\n exitOnMatch: z.boolean(),\n});\nexport type GoalIR = z.infer<typeof GoalIR>;\n\n/**\n * Human-facing metadata: **excluded from contentHash** (see hash.ts).\n * Editing a name or description does not touch execution semantics, so it never\n * invalidates the version in-flight users are pinned to, nor shows up in plan.\n */\nexport const MetaIR = z.object({\n /**\n * Human label. The asset's `id` is its identity — a wire key that routing\n * tables and the entry ledger are built on — so it is not something to\n * rename for readability. This is: it is metadata, dropped before hashing,\n * and the studio shows it in place of the key wherever it is set.\n */\n name: z.optional(z.string()),\n description: z.optional(z.string()),\n loc: z.optional(SourceLocIR),\n});\nexport type MetaIR = z.infer<typeof MetaIR>;\n\nexport const WorkflowIR = z.object({\n irVersion: z.literal(IR_VERSION),\n /** Wire identity (`workflow({ id })`): routing tables, the entry ledger and reporting are keyed on it. */\n id: z.string(),\n contentHash: z.string(),\n meta: z.optional(MetaIR),\n trigger: TriggerIR,\n goal: z.optional(GoalIR),\n exitWhen: z.optional(ConditionIR),\n flow: z.array(NodeIR),\n});\nexport type WorkflowIR = z.infer<typeof WorkflowIR>;\n\nexport const SegmentIR = z.object({\n irVersion: z.literal(IR_VERSION),\n /** Wire identity (`segment({ id })`): segment references and the membership store are keyed on it. */\n id: z.string(),\n contentHash: z.string(),\n /** Provenance plus the human label — MetaIR, same as a workflow's. */\n meta: z.optional(MetaIR),\n condition: ConditionIR,\n});\nexport type SegmentIR = z.infer<typeof SegmentIR>;\n\n/** Template manifest entry: IR records only the reference; the body lives in the template system. */\nexport const TemplateIR = z.object({\n irVersion: z.literal(IR_VERSION),\n key: z.string(),\n channel: ChannelIR,\n});\nexport type TemplateIR = z.infer<typeof TemplateIR>;\n\n/**\n * Action manifest entry: IR records the name and the code-identity hash; the\n * implementation solidifies into the Worker bundle at deploy time, never into\n * IR (custom code stays out of the data plane by design).\n */\nexport const ActionIR = z.object({\n irVersion: z.literal(IR_VERSION),\n name: z.string(),\n codeHash: z.string(),\n});\nexport type ActionIR = z.infer<typeof ActionIR>;\n\n/**\n * Deployment unit: the complete snapshot one `deploy` produces (terraform\n * apply, same mental model). The server diffs each definition's contentHash to\n * decide publish / skip / retire.\n */\nexport const BundleIR = z.object({\n irVersion: z.literal(IR_VERSION),\n workflows: z.array(WorkflowIR),\n segments: z.array(SegmentIR),\n templates: z.array(TemplateIR),\n actions: z.array(ActionIR),\n});\nexport type BundleIR = z.infer<typeof BundleIR>;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ir.cjs","names":["z"],"sources":["../src/ir.ts"],"sourcesContent":["import * as z from 'zod/mini';\n\n/**\n * IR (Intermediate Representation) — what the DSL compiles to: serializable\n * JSON. It is the three-way contract between the engine that interprets it,\n * the UI (react-flow) that renders it, and diff / audit tooling.\n *\n * Validation strategy: the zod/mini schemas are the authoritative definition.\n * TS types are either inferred from a schema or hand-written under the same\n * name (zod cannot infer recursive types; the hand-written type is anchored\n * with a ZodMiniType<T> annotation, so any drift fails to compile). IR crosses\n * process boundaries (database, network, UI), so runtime validation is a must.\n *\n * ## Versioning (two independent axes)\n *\n * 1. IR format version (irVersion): evolution of the IR schema itself. Adding\n * optional fields does not bump it; only structurally incompatible changes\n * do (changing a discriminant, dropping a field, altering semantics).\n * Readers dispatch on irVersion to the matching schema/migrator; writers\n * always write the current version.\n * 2. Content version (contentHash): a content-addressed id for a single\n * definition's **execution semantics** — the canonical-JSON hash after\n * metadata (meta / label / reason) is stripped, see hash.ts. It is used to:\n * - pin a version when a user enters, so an in-flight journey runs the\n * pinned version to completion (the default policy; compatible hot\n * updates are a future explicit-migration topic). Known exception:\n * segment conditions are resolved **by id at evaluation time** — a\n * segment redeploy changes behaviour of in-flight journeys that\n * reference it. Pinning covers the workflow's own structure; per-hash\n * segment snapshots are future work;\n * - diff on deploy: same hash means unchanged, skip publishing;\n * - audit: answer \"which version was this user on\" at any point.\n *\n * ## Node ids\n *\n * Derived from the structural path at compile time — stable and deterministic:\n * root level '0', '1', …; the j-th child of a branch's i-th case is\n * '{branchId}.c{i}.{j}', the default arm '{branchId}.o.{j}'; cohort arms use\n * the arm name '{cohortId}.{armName}.{j}'; a waitUntil timeout flow '{id}.t.{j}'.\n * Editing a node's parameters keeps its id; inserting a node shifts the ids of\n * its later siblings (a known trade-off — locating in-flight users relies on\n * the pinned version rather than cross-version id alignment, and cross-version\n * mapping is an explicit migrator's job).\n */\n\nexport const IR_VERSION = 2;\n\n/* -------------------------------- Base values ------------------------------- */\n\nexport const ScalarIR = z.union([z.string(), z.number(), z.boolean()]);\nexport type ScalarIR = z.infer<typeof ScalarIR>;\n\n/** Duration: `value` keeps the DSL literal ('23 hours', for display), `ms` is what the engine uses. */\n/*\n * TODO: `value` is hashed alongside `ms`, so '7 days' / '1 week' / '168 hours'\n * — identical to the engine, which only reads ms — produce three different\n * contentHashes. Rewording a duration therefore reports as a semantic change in\n * plan and repins in-flight users. Moving `value` out of the hash (it is a\n * display string) would fix it; deferred because it changes existing hashes.\n */\nexport const DurationIR = z.object({ value: z.string(), ms: z.number() });\nexport type DurationIR = z.infer<typeof DurationIR>;\n\n/** User-property reference — isomorphic to the DSL's UserPropertyRef once serialized (phantom type dropped). */\nexport const UserPropertyRefIR = z.object({\n type: z.literal('user_property'),\n path: z.string(),\n});\nexport type UserPropertyRefIR = z.infer<typeof UserPropertyRefIR>;\n\n/** A message prop / event payload value: either a literal or a user-property reference. */\nexport const PropValueIR = z.union([ScalarIR, UserPropertyRefIR]);\nexport type PropValueIR = z.infer<typeof PropValueIR>;\n\nexport const ChannelIR = z.enum(['email', 'sms', 'push', 'in_app', 'slack', 'discord', 'survey']);\nexport type ChannelIR = z.infer<typeof ChannelIR>;\n\nexport const WeekdayIR = z.enum(['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']);\nexport type WeekdayIR = z.infer<typeof WeekdayIR>;\n\n/* --------------------------------- Provenance -------------------------------- */\n\n/**\n * Source location of the builder call that produced a definition — the IR's\n * source map. Lives under `meta`, so it is **excluded from contentHash**\n * (see hash.ts): provenance never affects execution identity or plan.\n * `file` is cwd-relative when compiled under Node, a URL under Vite dev.\n */\nexport const SourceLocIR = z.object({\n file: z.string(),\n line: z.number(),\n column: z.number(),\n});\nexport type SourceLocIR = z.infer<typeof SourceLocIR>;\n\n/** Node-level metadata: provenance only, for now. Excluded from contentHash. */\nexport const NodeMetaIR = z.object({\n loc: z.optional(SourceLocIR),\n});\nexport type NodeMetaIR = z.infer<typeof NodeMetaIR>;\n\n/* -------------------------------- Conditions -------------------------------- */\n\nexport const PropertyOperatorIR = z.enum([\n 'eq',\n 'ne',\n 'gt',\n 'gte',\n 'lt',\n 'lte',\n 'between',\n 'not_between',\n 'in_array',\n 'not_in_array',\n 'exists',\n 'not_exists',\n 'contains',\n 'not_contains',\n]);\nexport type PropertyOperatorIR = z.infer<typeof PropertyOperatorIR>;\n\n/**\n * Condition expression tree. The shape of value/values follows `op`:\n * eq/ne/gt/gte/lt/lte/contains/not_contains → value; in_array/not_in_array →\n * values; between/not_between → values = [min, max]; exists/not_exists → neither.\n *\n * `payload` predicates test the payload of an event (dotted paths reach nested\n * fields). They are only valid inside a `where` subtree — `performed.where`\n * and the event trigger's `where` — where an event row is in scope; the\n * compiler (compileBundle) and the evaluator both enforce the placement.\n * TODO: runtime refinement narrowing by op.\n */\n/**\n * Provenance rides on conditions too, so the studio can point at the exact\n * `eq(...)` / `and(...)` that produced a node — same rule as nodes: `meta` is\n * dropped before hashing, so carrying it never affects execution identity.\n */\ninterface ConditionBaseIR {\n meta?: NodeMetaIR | undefined;\n}\n\nexport type ConditionIR =\n | (ConditionBaseIR & { type: 'and'; conditions: ConditionIR[] })\n | (ConditionBaseIR & { type: 'or'; conditions: ConditionIR[] })\n | (ConditionBaseIR & { type: 'not'; condition: ConditionIR })\n /** Reference a segment by id (defined in SegmentIR, versioned separately). */\n | (ConditionBaseIR & { type: 'segment'; segment: string })\n | (ConditionBaseIR & {\n type: 'property';\n path: string;\n op: PropertyOperatorIR;\n value?: ScalarIR | undefined;\n values?: ScalarIR[] | undefined;\n })\n | (ConditionBaseIR & {\n /** Event payload predicate; `path` is dotted for nested fields ('tags.utm_source'). */\n type: 'payload';\n path: string;\n op: PropertyOperatorIR;\n value?: ScalarIR | undefined;\n values?: ScalarIR[] | undefined;\n })\n | (ConditionBaseIR & {\n type: 'performed';\n event: string;\n /** Payload filter: only events matching it count (a payload-only subtree). */\n where?: ConditionIR | undefined;\n within?: DurationIR | undefined;\n count?: number | undefined;\n })\n /**\n * The user can be reached on the channel right now: for push, the host's\n * device directory lists at least one device; for the others, the channel's\n * recipient property (email, phone, …) is set.\n */\n | (ConditionBaseIR & { type: 'reachable'; channel: ChannelIR });\n\nconst conditionBase = { meta: z.optional(NodeMetaIR) };\n\nexport const ConditionIR: z.ZodMiniType<ConditionIR> = z.lazy(() =>\n z.discriminatedUnion('type', [\n z.object({ ...conditionBase, type: z.literal('and'), conditions: z.array(ConditionIR) }),\n z.object({ ...conditionBase, type: z.literal('or'), conditions: z.array(ConditionIR) }),\n z.object({ ...conditionBase, type: z.literal('not'), condition: ConditionIR }),\n z.object({ ...conditionBase, type: z.literal('segment'), segment: z.string() }),\n z.object({\n ...conditionBase,\n type: z.literal('property'),\n path: z.string(),\n op: PropertyOperatorIR,\n value: z.optional(ScalarIR),\n values: z.optional(z.array(ScalarIR)),\n }),\n z.object({\n ...conditionBase,\n type: z.literal('payload'),\n path: z.string(),\n op: PropertyOperatorIR,\n value: z.optional(ScalarIR),\n values: z.optional(z.array(ScalarIR)),\n }),\n z.object({\n ...conditionBase,\n type: z.literal('performed'),\n event: z.string(),\n where: z.optional(ConditionIR),\n within: z.optional(DurationIR),\n count: z.optional(z.number()),\n }),\n z.object({ ...conditionBase, type: z.literal('reachable'), channel: ChannelIR }),\n ])\n);\n\n/* --------------------------------- Triggers --------------------------------- */\n\n/**\n * Trigger shapes. The event trigger has two independent gates: `where` tests\n * the triggering event's payload (payload-only subtree, evaluated against the\n * incoming event before anything else), `filter` tests the user's profile.\n */\nexport const TriggerIR = z.discriminatedUnion('type', [\n z.object({\n type: z.literal('event'),\n event: z.string(),\n where: z.optional(ConditionIR),\n filter: z.optional(ConditionIR),\n }),\n z.object({ type: z.literal('segment'), segment: z.string() }),\n z.object({ type: z.literal('date'), at: z.string() }),\n z.object({ type: z.literal('webhook') }),\n]);\nexport type TriggerIR = z.infer<typeof TriggerIR>;\n\n/* ----------------------------------- Nodes ---------------------------------- */\n\ninterface NodeBaseIR {\n /** Stable id derived from the structural path (rules in the file header). */\n id: string;\n /** Optional node name: UI title / observability handle (branch's optional first argument lands here). */\n label?: string | undefined;\n /** Metadata (provenance); excluded from contentHash. */\n meta?: NodeMetaIR | undefined;\n}\n\nexport type NodeIR =\n | (NodeBaseIR & {\n type: 'message';\n channel: ChannelIR;\n template: string;\n props: Record<string, PropValueIR>;\n /**\n * Push only: which of the user's devices get a copy. Absent means every\n * device the directory lists; 'latest' only the most recently registered.\n */\n devices?: 'latest' | undefined;\n })\n | (NodeBaseIR & { type: 'delay'; duration: DurationIR })\n | (NodeBaseIR & { type: 'random_delay'; min: DurationIR; max: DurationIR })\n | (NodeBaseIR & {\n type: 'time_window';\n days: WeekdayIR[];\n between: [string, string];\n tz: string;\n })\n | (NodeBaseIR & {\n type: 'wait_until';\n condition: ConditionIR;\n timeout: DurationIR;\n onTimeout: 'continue' | 'exit' | NodeIR[];\n })\n | (NodeBaseIR & {\n type: 'branch';\n cases: { label?: string | undefined; condition: ConditionIR; flow: NodeIR[] }[];\n /** Default arm (the DSL's bare tail argument). Absent = fall through to the main line. */\n otherwise?: NodeIR[] | undefined;\n })\n | (NodeBaseIR & {\n type: 'filter';\n /** Audit-log line for the users this gate drops. Metadata: excluded from contentHash. */\n reason?: string | undefined;\n condition: ConditionIR;\n })\n | (NodeBaseIR & {\n type: 'cohort';\n /**\n * Explicit experiment key: bucketing hashes `userId:key` instead of the\n * structural node id, so the assignment survives node insertion/moves and\n * the experiment keeps one identity across workflow versions. Absent →\n * the node id is used (fine for one-off splits).\n */\n key?: string | undefined;\n /**\n * Arms in authoring order — the order the interpreter accumulates weights\n * in, so it decides which bucket range each arm owns. The DSL keeps that\n * promise by rejecting integer-like arm names, which JavaScript would\n * hoist to the front of the object in numeric order. Weights sum to 100.\n */\n arms: { name: string; weight: number; flow: NodeIR[] }[];\n })\n | (NodeBaseIR & {\n /** `reason` is the audit-log line. Metadata: excluded from contentHash. */\n type: 'exit';\n reason?: string | undefined;\n })\n | (NodeBaseIR & { type: 'send_event'; event: string; payload: Record<string, PropValueIR> })\n | (NodeBaseIR & {\n type: 'action';\n /** Registry name of the custom action; the code itself lives in the deployed Worker bundle. */\n action: string;\n /** Argument values; user_property references are resolved at run time, like message props. */\n args: Record<string, PropValueIR>;\n });\n\nconst nodeBase = { id: z.string(), label: z.optional(z.string()), meta: z.optional(NodeMetaIR) };\n\nexport const NodeIR: z.ZodMiniType<NodeIR> = z.lazy(() =>\n z.discriminatedUnion('type', [\n z.object({\n ...nodeBase,\n type: z.literal('message'),\n channel: ChannelIR,\n template: z.string(),\n props: z.record(z.string(), PropValueIR),\n devices: z.optional(z.literal('latest')),\n }),\n z.object({ ...nodeBase, type: z.literal('delay'), duration: DurationIR }),\n z.object({ ...nodeBase, type: z.literal('random_delay'), min: DurationIR, max: DurationIR }),\n z.object({\n ...nodeBase,\n type: z.literal('time_window'),\n days: z.array(WeekdayIR),\n between: z.tuple([z.string(), z.string()]),\n tz: z.string(),\n }),\n z.object({\n ...nodeBase,\n type: z.literal('wait_until'),\n condition: ConditionIR,\n timeout: DurationIR,\n onTimeout: z.union([z.literal('continue'), z.literal('exit'), z.array(NodeIR)]),\n }),\n z.object({\n ...nodeBase,\n type: z.literal('branch'),\n cases: z.array(\n z.object({\n label: z.optional(z.string()),\n condition: ConditionIR,\n flow: z.array(NodeIR),\n })\n ),\n otherwise: z.optional(z.array(NodeIR)),\n }),\n z.object({\n ...nodeBase,\n type: z.literal('filter'),\n condition: ConditionIR,\n reason: z.optional(z.string()),\n }),\n z.object({\n ...nodeBase,\n type: z.literal('cohort'),\n key: z.optional(z.string()),\n arms: z.array(z.object({ name: z.string(), weight: z.number(), flow: z.array(NodeIR) })),\n }),\n z.object({ ...nodeBase, type: z.literal('exit'), reason: z.optional(z.string()) }),\n z.object({\n ...nodeBase,\n type: z.literal('send_event'),\n event: z.string(),\n payload: z.record(z.string(), PropValueIR),\n }),\n z.object({\n ...nodeBase,\n type: z.literal('action'),\n action: z.string(),\n args: z.record(z.string(), PropValueIR),\n }),\n ])\n);\n\n/* ---------------------- Top-level definitions and bundle --------------------- */\n\nexport const GoalIR = z.object({\n condition: ConditionIR,\n within: z.optional(DurationIR),\n exitOnMatch: z.boolean(),\n});\nexport type GoalIR = z.infer<typeof GoalIR>;\n\n/**\n * Human-facing metadata: **excluded from contentHash** (see hash.ts).\n * Editing a name or description does not touch execution semantics, so it never\n * invalidates the version in-flight users are pinned to, nor shows up in plan.\n */\nexport const MetaIR = z.object({\n /**\n * Human label. The asset's `id` is its identity — a wire key that routing\n * tables and the entry ledger are built on — so it is not something to\n * rename for readability. This is: it is metadata, dropped before hashing,\n * and the studio shows it in place of the key wherever it is set.\n */\n name: z.optional(z.string()),\n description: z.optional(z.string()),\n loc: z.optional(SourceLocIR),\n});\nexport type MetaIR = z.infer<typeof MetaIR>;\n\nexport const WorkflowIR = z.object({\n irVersion: z.literal(IR_VERSION),\n /** Wire identity (`workflow({ id })`): routing tables, the entry ledger and reporting are keyed on it. */\n id: z.string(),\n contentHash: z.string(),\n meta: z.optional(MetaIR),\n trigger: TriggerIR,\n goal: z.optional(GoalIR),\n exitWhen: z.optional(ConditionIR),\n flow: z.array(NodeIR),\n});\nexport type WorkflowIR = z.infer<typeof WorkflowIR>;\n\nexport const SegmentIR = z.object({\n irVersion: z.literal(IR_VERSION),\n /** Wire identity (`segment({ id })`): segment references and the membership store are keyed on it. */\n id: z.string(),\n contentHash: z.string(),\n /** Provenance plus the human label — MetaIR, same as a workflow's. */\n meta: z.optional(MetaIR),\n condition: ConditionIR,\n});\nexport type SegmentIR = z.infer<typeof SegmentIR>;\n\n/** Template manifest entry: IR records only the reference; the body lives in the template system. */\nexport const TemplateIR = z.object({\n irVersion: z.literal(IR_VERSION),\n key: z.string(),\n channel: ChannelIR,\n});\nexport type TemplateIR = z.infer<typeof TemplateIR>;\n\n/**\n * Action manifest entry: IR records the name; the implementation solidifies\n * into the Worker bundle at deploy time, never into IR (custom code stays out\n * of the data plane by design).\n */\nexport const ActionIR = z.object({\n irVersion: z.literal(IR_VERSION),\n name: z.string(),\n});\nexport type ActionIR = z.infer<typeof ActionIR>;\n\n/**\n * Deployment unit: the complete snapshot one `deploy` produces (terraform\n * apply, same mental model). The server diffs each definition's contentHash to\n * decide publish / skip / retire.\n */\nexport const BundleIR = z.object({\n irVersion: z.literal(IR_VERSION),\n workflows: z.array(WorkflowIR),\n segments: z.array(SegmentIR),\n templates: z.array(TemplateIR),\n actions: z.array(ActionIR),\n});\nexport type BundleIR = z.infer<typeof BundleIR>;\n"],"mappings":"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"}
|
package/dist/ir.d.cts
CHANGED
|
@@ -288,13 +288,6 @@ export type NodeIR = (NodeBaseIR & {
|
|
|
288
288
|
action: string;
|
|
289
289
|
/** Argument values; user_property references are resolved at run time, like message props. */
|
|
290
290
|
args: Record<string, PropValueIR>;
|
|
291
|
-
/**
|
|
292
|
-
* Hash of the action's source at compile time — the code-identity pin.
|
|
293
|
-
* IR carries identity only (dual-plane model): the runtime looks the
|
|
294
|
-
* action up by name and compares this hash against the registered
|
|
295
|
-
* implementation to detect version skew (warn by default).
|
|
296
|
-
*/
|
|
297
|
-
codeHash?: string | undefined;
|
|
298
291
|
});
|
|
299
292
|
export declare const NodeIR: z.ZodMiniType<NodeIR>;
|
|
300
293
|
export declare const GoalIR: z.ZodMiniObject<{
|
|
@@ -392,14 +385,13 @@ export declare const TemplateIR: z.ZodMiniObject<{
|
|
|
392
385
|
}, z.core.$strip>;
|
|
393
386
|
export type TemplateIR = z.infer<typeof TemplateIR>;
|
|
394
387
|
/**
|
|
395
|
-
* Action manifest entry: IR records the name
|
|
396
|
-
*
|
|
397
|
-
*
|
|
388
|
+
* Action manifest entry: IR records the name; the implementation solidifies
|
|
389
|
+
* into the Worker bundle at deploy time, never into IR (custom code stays out
|
|
390
|
+
* of the data plane by design).
|
|
398
391
|
*/
|
|
399
392
|
export declare const ActionIR: z.ZodMiniObject<{
|
|
400
393
|
irVersion: z.ZodMiniLiteral<2>;
|
|
401
394
|
name: z.ZodMiniString<string>;
|
|
402
|
-
codeHash: z.ZodMiniString<string>;
|
|
403
395
|
}, z.core.$strip>;
|
|
404
396
|
export type ActionIR = z.infer<typeof ActionIR>;
|
|
405
397
|
/**
|
|
@@ -478,7 +470,6 @@ export declare const BundleIR: z.ZodMiniObject<{
|
|
|
478
470
|
actions: z.ZodMiniArray<z.ZodMiniObject<{
|
|
479
471
|
irVersion: z.ZodMiniLiteral<2>;
|
|
480
472
|
name: z.ZodMiniString<string>;
|
|
481
|
-
codeHash: z.ZodMiniString<string>;
|
|
482
473
|
}, z.core.$strip>>;
|
|
483
474
|
}, z.core.$strip>;
|
|
484
475
|
export type BundleIR = z.infer<typeof BundleIR>;
|
package/dist/ir.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ir.d.cts","names":[],"sources":["../src/ir.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBA6Ca;qBAIA,UAAQ,EAAA,uBAAA,EAAA,uBAAA,EAAA,uBAAA,EAAA;YACT,WAAW,EAAE,aAAa;;qBAUzB,YAAU,EAAA;;;GAAkD,EAAA,KAAA;YAC7D,aAAa,EAAE,aAAa;;qBAG3B,mBAAiB,EAAA;;;GAG5B,EAAA,KAAA;YACU,oBAAoB,EAAE,aAAa;;qBAGlC,aAAW,EAAA,uBAAA,EAAA,uBAAA,EAAA,uBAAA,EAAA,uBAAA,EAAA,2BAAA,EAAA;;;GAAyC,EAAA,KAAA;YACrD,cAAc,EAAE,aAAa;qBAE5B,WAAS,EAAA;;;;;;;;;YACV,YAAY,EAAE,aAAa;qBAE1B,WAAS,EAAA;;;;;;;;;YACV,YAAY,EAAE,aAAa;;;;;;;qBAU1B,aAAW,EAAA;;;;GAItB,EAAA,KAAA;YACU,cAAc,EAAE,aAAa;;qBAG5B,YAAU,EAAA;;;;;;GAErB,EAAA,KAAA;YACU,aAAa,EAAE,aAAa;qBAI3B,oBAAkB,EAAA;;;;;;;;;;;;;;;;YAgBnB,qBAAqB,EAAE,aAAa;;;;;;;;;;;;;;;;;UAkBtC;EACR,OAAO;;YAGG,eACP;EAAoB;EAAa,YAAY;MAC7C;EAAoB;EAAY,YAAY;MAC5C;EAAoB;EAAa,WAAW;;;CAE5C;EAAoB;EAAiB;MACrC;EACC;EACA;EACA,IAAI;EACJ,QAAQ;EACR,SAAS;MAEV;;EAEC;EACA;EACA,IAAI;EACJ,QAAQ;EACR,SAAS;MAEV;EACC;EACA;;EAEA,QAAQ;EACR,SAAS;EACT;;;;;;;CAOD;EAAoB;EAAmB,SAAS;;qBAIxC,aAAa,EAAE,YAAY;;;;;;qBAyC3B,WAAS,EAAA,2BAAA,EAAA;;;;;;;;;;;;;GAUpB,EAAA,KAAA;YACU,YAAY,EAAE,aAAa;UAI7B;;EAER;;EAEA;;EAEA,OAAO;;YAGG,UACP;EACC;EACA,SAAS;EACT;EACA,OAAO,eAAe;;;;;EAKtB;MAED;EAAe;EAAe,UAAU;MACxC;EAAe;EAAsB,KAAK;EAAY,KAAK;MAC3D;EACC;EACA,MAAM;EACN;EACA;MAED;EACC;EACA,WAAW;EACX,SAAS;EACT,iCAAiC;MAElC;EACC;EACA;IAAS;IAA4B,WAAW;IAAa,MAAM;;;EAEnE,YAAY;MAEb;EACC;;EAEA;EACA,WAAW;MAEZ;EACC;;;;;;;EAOA;;;;;;;EAOA;IAAQ;IAAc;IAAgB,MAAM;;MAE7C;;EAEC;EACA;MAED;EAAe;EAAoB;EAAe,SAAS,eAAe;MAC1E;EACC;;EAEA;;EAEA,MAAM,eAAe
|
|
1
|
+
{"version":3,"file":"ir.d.cts","names":[],"sources":["../src/ir.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBA6Ca;qBAIA,UAAQ,EAAA,uBAAA,EAAA,uBAAA,EAAA,uBAAA,EAAA;YACT,WAAW,EAAE,aAAa;;qBAUzB,YAAU,EAAA;;;GAAkD,EAAA,KAAA;YAC7D,aAAa,EAAE,aAAa;;qBAG3B,mBAAiB,EAAA;;;GAG5B,EAAA,KAAA;YACU,oBAAoB,EAAE,aAAa;;qBAGlC,aAAW,EAAA,uBAAA,EAAA,uBAAA,EAAA,uBAAA,EAAA,uBAAA,EAAA,2BAAA,EAAA;;;GAAyC,EAAA,KAAA;YACrD,cAAc,EAAE,aAAa;qBAE5B,WAAS,EAAA;;;;;;;;;YACV,YAAY,EAAE,aAAa;qBAE1B,WAAS,EAAA;;;;;;;;;YACV,YAAY,EAAE,aAAa;;;;;;;qBAU1B,aAAW,EAAA;;;;GAItB,EAAA,KAAA;YACU,cAAc,EAAE,aAAa;;qBAG5B,YAAU,EAAA;;;;;;GAErB,EAAA,KAAA;YACU,aAAa,EAAE,aAAa;qBAI3B,oBAAkB,EAAA;;;;;;;;;;;;;;;;YAgBnB,qBAAqB,EAAE,aAAa;;;;;;;;;;;;;;;;;UAkBtC;EACR,OAAO;;YAGG,eACP;EAAoB;EAAa,YAAY;MAC7C;EAAoB;EAAY,YAAY;MAC5C;EAAoB;EAAa,WAAW;;;CAE5C;EAAoB;EAAiB;MACrC;EACC;EACA;EACA,IAAI;EACJ,QAAQ;EACR,SAAS;MAEV;;EAEC;EACA;EACA,IAAI;EACJ,QAAQ;EACR,SAAS;MAEV;EACC;EACA;;EAEA,QAAQ;EACR,SAAS;EACT;;;;;;;CAOD;EAAoB;EAAmB,SAAS;;qBAIxC,aAAa,EAAE,YAAY;;;;;;qBAyC3B,WAAS,EAAA,2BAAA,EAAA;;;;;;;;;;;;;GAUpB,EAAA,KAAA;YACU,YAAY,EAAE,aAAa;UAI7B;;EAER;;EAEA;;EAEA,OAAO;;YAGG,UACP;EACC;EACA,SAAS;EACT;EACA,OAAO,eAAe;;;;;EAKtB;MAED;EAAe;EAAe,UAAU;MACxC;EAAe;EAAsB,KAAK;EAAY,KAAK;MAC3D;EACC;EACA,MAAM;EACN;EACA;MAED;EACC;EACA,WAAW;EACX,SAAS;EACT,iCAAiC;MAElC;EACC;EACA;IAAS;IAA4B,WAAW;IAAa,MAAM;;;EAEnE,YAAY;MAEb;EACC;;EAEA;EACA,WAAW;MAEZ;EACC;;;;;;;EAOA;;;;;;;EAOA;IAAQ;IAAc;IAAgB,MAAM;;MAE7C;;EAEC;EACA;MAED;EAAe;EAAoB;EAAe,SAAS,eAAe;MAC1E;EACC;;EAEA;;EAEA,MAAM,eAAe;;qBAKd,QAAQ,EAAE,YAAY;qBAoEtB,QAAM,EAAA;;;;;;;GAIjB,EAAA,KAAA;YACU,SAAS,EAAE,aAAa;;;;;;qBAOvB,QAAM,EAAA;;;;;;;;GAUjB,EAAA,KAAA;YACU,SAAS,EAAE,aAAa;qBAEvB,YAAU,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAUrB,EAAA,KAAA;YACU,aAAa,EAAE,aAAa;qBAE3B,WAAS,EAAA;;;;;;;;;;;;;;GAQpB,EAAA,KAAA;YACU,YAAY,EAAE,aAAa;;qBAG1B,YAAU,EAAA;;;;;;;;;;;;GAIrB,EAAA,KAAA;YACU,aAAa,EAAE,aAAa;;;;;;qBAO3B,UAAQ,EAAA;;;GAGnB,EAAA,KAAA;YACU,WAAW,EAAE,aAAa;;;;;;qBAOzB,UAAQ,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAMnB,EAAA,KAAA;YACU,WAAW,EAAE,aAAa"}
|
package/dist/ir.d.mts
CHANGED
|
@@ -288,13 +288,6 @@ export type NodeIR = (NodeBaseIR & {
|
|
|
288
288
|
action: string;
|
|
289
289
|
/** Argument values; user_property references are resolved at run time, like message props. */
|
|
290
290
|
args: Record<string, PropValueIR>;
|
|
291
|
-
/**
|
|
292
|
-
* Hash of the action's source at compile time — the code-identity pin.
|
|
293
|
-
* IR carries identity only (dual-plane model): the runtime looks the
|
|
294
|
-
* action up by name and compares this hash against the registered
|
|
295
|
-
* implementation to detect version skew (warn by default).
|
|
296
|
-
*/
|
|
297
|
-
codeHash?: string | undefined;
|
|
298
291
|
});
|
|
299
292
|
export declare const NodeIR: z.ZodMiniType<NodeIR>;
|
|
300
293
|
export declare const GoalIR: z.ZodMiniObject<{
|
|
@@ -392,14 +385,13 @@ export declare const TemplateIR: z.ZodMiniObject<{
|
|
|
392
385
|
}, z.core.$strip>;
|
|
393
386
|
export type TemplateIR = z.infer<typeof TemplateIR>;
|
|
394
387
|
/**
|
|
395
|
-
* Action manifest entry: IR records the name
|
|
396
|
-
*
|
|
397
|
-
*
|
|
388
|
+
* Action manifest entry: IR records the name; the implementation solidifies
|
|
389
|
+
* into the Worker bundle at deploy time, never into IR (custom code stays out
|
|
390
|
+
* of the data plane by design).
|
|
398
391
|
*/
|
|
399
392
|
export declare const ActionIR: z.ZodMiniObject<{
|
|
400
393
|
irVersion: z.ZodMiniLiteral<2>;
|
|
401
394
|
name: z.ZodMiniString<string>;
|
|
402
|
-
codeHash: z.ZodMiniString<string>;
|
|
403
395
|
}, z.core.$strip>;
|
|
404
396
|
export type ActionIR = z.infer<typeof ActionIR>;
|
|
405
397
|
/**
|
|
@@ -478,7 +470,6 @@ export declare const BundleIR: z.ZodMiniObject<{
|
|
|
478
470
|
actions: z.ZodMiniArray<z.ZodMiniObject<{
|
|
479
471
|
irVersion: z.ZodMiniLiteral<2>;
|
|
480
472
|
name: z.ZodMiniString<string>;
|
|
481
|
-
codeHash: z.ZodMiniString<string>;
|
|
482
473
|
}, z.core.$strip>>;
|
|
483
474
|
}, z.core.$strip>;
|
|
484
475
|
export type BundleIR = z.infer<typeof BundleIR>;
|
package/dist/ir.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ir.d.mts","names":[],"sources":["../src/ir.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBA6Ca;qBAIA,UAAQ,EAAA,uBAAA,EAAA,uBAAA,EAAA,uBAAA,EAAA;YACT,WAAW,EAAE,aAAa;;qBAUzB,YAAU,EAAA;;;GAAkD,EAAA,KAAA;YAC7D,aAAa,EAAE,aAAa;;qBAG3B,mBAAiB,EAAA;;;GAG5B,EAAA,KAAA;YACU,oBAAoB,EAAE,aAAa;;qBAGlC,aAAW,EAAA,uBAAA,EAAA,uBAAA,EAAA,uBAAA,EAAA,uBAAA,EAAA,2BAAA,EAAA;;;GAAyC,EAAA,KAAA;YACrD,cAAc,EAAE,aAAa;qBAE5B,WAAS,EAAA;;;;;;;;;YACV,YAAY,EAAE,aAAa;qBAE1B,WAAS,EAAA;;;;;;;;;YACV,YAAY,EAAE,aAAa;;;;;;;qBAU1B,aAAW,EAAA;;;;GAItB,EAAA,KAAA;YACU,cAAc,EAAE,aAAa;;qBAG5B,YAAU,EAAA;;;;;;GAErB,EAAA,KAAA;YACU,aAAa,EAAE,aAAa;qBAI3B,oBAAkB,EAAA;;;;;;;;;;;;;;;;YAgBnB,qBAAqB,EAAE,aAAa;;;;;;;;;;;;;;;;;UAkBtC;EACR,OAAO;;YAGG,eACP;EAAoB;EAAa,YAAY;MAC7C;EAAoB;EAAY,YAAY;MAC5C;EAAoB;EAAa,WAAW;;;CAE5C;EAAoB;EAAiB;MACrC;EACC;EACA;EACA,IAAI;EACJ,QAAQ;EACR,SAAS;MAEV;;EAEC;EACA;EACA,IAAI;EACJ,QAAQ;EACR,SAAS;MAEV;EACC;EACA;;EAEA,QAAQ;EACR,SAAS;EACT;;;;;;;CAOD;EAAoB;EAAmB,SAAS;;qBAIxC,aAAa,EAAE,YAAY;;;;;;qBAyC3B,WAAS,EAAA,2BAAA,EAAA;;;;;;;;;;;;;GAUpB,EAAA,KAAA;YACU,YAAY,EAAE,aAAa;UAI7B;;EAER;;EAEA;;EAEA,OAAO;;YAGG,UACP;EACC;EACA,SAAS;EACT;EACA,OAAO,eAAe;;;;;EAKtB;MAED;EAAe;EAAe,UAAU;MACxC;EAAe;EAAsB,KAAK;EAAY,KAAK;MAC3D;EACC;EACA,MAAM;EACN;EACA;MAED;EACC;EACA,WAAW;EACX,SAAS;EACT,iCAAiC;MAElC;EACC;EACA;IAAS;IAA4B,WAAW;IAAa,MAAM;;;EAEnE,YAAY;MAEb;EACC;;EAEA;EACA,WAAW;MAEZ;EACC;;;;;;;EAOA;;;;;;;EAOA;IAAQ;IAAc;IAAgB,MAAM;;MAE7C;;EAEC;EACA;MAED;EAAe;EAAoB;EAAe,SAAS,eAAe;MAC1E;EACC;;EAEA;;EAEA,MAAM,eAAe
|
|
1
|
+
{"version":3,"file":"ir.d.mts","names":[],"sources":["../src/ir.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBA6Ca;qBAIA,UAAQ,EAAA,uBAAA,EAAA,uBAAA,EAAA,uBAAA,EAAA;YACT,WAAW,EAAE,aAAa;;qBAUzB,YAAU,EAAA;;;GAAkD,EAAA,KAAA;YAC7D,aAAa,EAAE,aAAa;;qBAG3B,mBAAiB,EAAA;;;GAG5B,EAAA,KAAA;YACU,oBAAoB,EAAE,aAAa;;qBAGlC,aAAW,EAAA,uBAAA,EAAA,uBAAA,EAAA,uBAAA,EAAA,uBAAA,EAAA,2BAAA,EAAA;;;GAAyC,EAAA,KAAA;YACrD,cAAc,EAAE,aAAa;qBAE5B,WAAS,EAAA;;;;;;;;;YACV,YAAY,EAAE,aAAa;qBAE1B,WAAS,EAAA;;;;;;;;;YACV,YAAY,EAAE,aAAa;;;;;;;qBAU1B,aAAW,EAAA;;;;GAItB,EAAA,KAAA;YACU,cAAc,EAAE,aAAa;;qBAG5B,YAAU,EAAA;;;;;;GAErB,EAAA,KAAA;YACU,aAAa,EAAE,aAAa;qBAI3B,oBAAkB,EAAA;;;;;;;;;;;;;;;;YAgBnB,qBAAqB,EAAE,aAAa;;;;;;;;;;;;;;;;;UAkBtC;EACR,OAAO;;YAGG,eACP;EAAoB;EAAa,YAAY;MAC7C;EAAoB;EAAY,YAAY;MAC5C;EAAoB;EAAa,WAAW;;;CAE5C;EAAoB;EAAiB;MACrC;EACC;EACA;EACA,IAAI;EACJ,QAAQ;EACR,SAAS;MAEV;;EAEC;EACA;EACA,IAAI;EACJ,QAAQ;EACR,SAAS;MAEV;EACC;EACA;;EAEA,QAAQ;EACR,SAAS;EACT;;;;;;;CAOD;EAAoB;EAAmB,SAAS;;qBAIxC,aAAa,EAAE,YAAY;;;;;;qBAyC3B,WAAS,EAAA,2BAAA,EAAA;;;;;;;;;;;;;GAUpB,EAAA,KAAA;YACU,YAAY,EAAE,aAAa;UAI7B;;EAER;;EAEA;;EAEA,OAAO;;YAGG,UACP;EACC;EACA,SAAS;EACT;EACA,OAAO,eAAe;;;;;EAKtB;MAED;EAAe;EAAe,UAAU;MACxC;EAAe;EAAsB,KAAK;EAAY,KAAK;MAC3D;EACC;EACA,MAAM;EACN;EACA;MAED;EACC;EACA,WAAW;EACX,SAAS;EACT,iCAAiC;MAElC;EACC;EACA;IAAS;IAA4B,WAAW;IAAa,MAAM;;;EAEnE,YAAY;MAEb;EACC;;EAEA;EACA,WAAW;MAEZ;EACC;;;;;;;EAOA;;;;;;;EAOA;IAAQ;IAAc;IAAgB,MAAM;;MAE7C;;EAEC;EACA;MAED;EAAe;EAAoB;EAAe,SAAS,eAAe;MAC1E;EACC;;EAEA;;EAEA,MAAM,eAAe;;qBAKd,QAAQ,EAAE,YAAY;qBAoEtB,QAAM,EAAA;;;;;;;GAIjB,EAAA,KAAA;YACU,SAAS,EAAE,aAAa;;;;;;qBAOvB,QAAM,EAAA;;;;;;;;GAUjB,EAAA,KAAA;YACU,SAAS,EAAE,aAAa;qBAEvB,YAAU,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAUrB,EAAA,KAAA;YACU,aAAa,EAAE,aAAa;qBAE3B,WAAS,EAAA;;;;;;;;;;;;;;GAQpB,EAAA,KAAA;YACU,YAAY,EAAE,aAAa;;qBAG1B,YAAU,EAAA;;;;;;;;;;;;GAIrB,EAAA,KAAA;YACU,aAAa,EAAE,aAAa;;;;;;qBAO3B,UAAQ,EAAA;;;GAGnB,EAAA,KAAA;YACU,WAAW,EAAE,aAAa;;;;;;qBAOzB,UAAQ,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAMnB,EAAA,KAAA;YACU,WAAW,EAAE,aAAa"}
|
package/dist/ir.mjs
CHANGED
|
@@ -265,8 +265,7 @@ const NodeIR = z.lazy(() => z.discriminatedUnion("type", [
|
|
|
265
265
|
...nodeBase,
|
|
266
266
|
type: z.literal("action"),
|
|
267
267
|
action: z.string(),
|
|
268
|
-
args: z.record(z.string(), PropValueIR)
|
|
269
|
-
codeHash: z.optional(z.string())
|
|
268
|
+
args: z.record(z.string(), PropValueIR)
|
|
270
269
|
})
|
|
271
270
|
]));
|
|
272
271
|
const GoalIR = z.object({
|
|
@@ -317,14 +316,13 @@ const TemplateIR = z.object({
|
|
|
317
316
|
channel: ChannelIR
|
|
318
317
|
});
|
|
319
318
|
/**
|
|
320
|
-
* Action manifest entry: IR records the name
|
|
321
|
-
*
|
|
322
|
-
*
|
|
319
|
+
* Action manifest entry: IR records the name; the implementation solidifies
|
|
320
|
+
* into the Worker bundle at deploy time, never into IR (custom code stays out
|
|
321
|
+
* of the data plane by design).
|
|
323
322
|
*/
|
|
324
323
|
const ActionIR = z.object({
|
|
325
324
|
irVersion: z.literal(2),
|
|
326
|
-
name: z.string()
|
|
327
|
-
codeHash: z.string()
|
|
325
|
+
name: z.string()
|
|
328
326
|
});
|
|
329
327
|
/**
|
|
330
328
|
* Deployment unit: the complete snapshot one `deploy` produces (terraform
|