@shware/workflow 5.0.0 → 6.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 +165 -42
- package/dist/_virtual/_rolldown/runtime.cjs +1 -2
- package/dist/apns-relay.cjs +1 -2
- package/dist/apns-relay.d.cts +0 -2
- package/dist/apns-relay.d.cts.map +1 -1
- package/dist/apns-relay.d.mts +0 -2
- package/dist/apns-relay.d.mts.map +1 -1
- package/dist/apns-relay.mjs +1 -2
- package/dist/apns.cjs +1 -2
- package/dist/apns.d.cts +0 -2
- package/dist/apns.d.cts.map +1 -1
- package/dist/apns.d.mts +0 -2
- package/dist/apns.d.mts.map +1 -1
- package/dist/apns.mjs +1 -2
- package/dist/aws/engine-step.cjs +1 -2
- package/dist/aws/engine-step.d.cts +0 -2
- package/dist/aws/engine-step.d.cts.map +1 -1
- package/dist/aws/engine-step.d.mts +0 -2
- package/dist/aws/engine-step.d.mts.map +1 -1
- package/dist/aws/engine-step.mjs +1 -2
- package/dist/aws/function-url.cjs +1 -2
- package/dist/aws/function-url.d.cts +0 -2
- package/dist/aws/function-url.d.cts.map +1 -1
- package/dist/aws/function-url.d.mts +0 -2
- package/dist/aws/function-url.d.mts.map +1 -1
- package/dist/aws/function-url.mjs +1 -2
- package/dist/aws/index.cjs +2 -3
- package/dist/aws/index.d.cts +0 -2
- package/dist/aws/index.d.cts.map +1 -1
- package/dist/aws/index.d.mts +0 -2
- package/dist/aws/index.d.mts.map +1 -1
- package/dist/aws/index.mjs +2 -3
- package/dist/aws/instances.cjs +1 -2
- package/dist/aws/instances.d.cts +0 -2
- package/dist/aws/instances.d.cts.map +1 -1
- package/dist/aws/instances.d.mts +0 -2
- package/dist/aws/instances.d.mts.map +1 -1
- package/dist/aws/instances.mjs +1 -2
- package/dist/aws/lambda-api.cjs +1 -2
- package/dist/aws/lambda-api.d.cts +0 -2
- package/dist/aws/lambda-api.d.cts.map +1 -1
- package/dist/aws/lambda-api.d.mts +0 -2
- package/dist/aws/lambda-api.d.mts.map +1 -1
- package/dist/aws/lambda-api.mjs +1 -2
- package/dist/client.cjs +46 -6
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +25 -5
- package/dist/client.d.cts.map +1 -1
- package/dist/client.d.mts +25 -5
- package/dist/client.d.mts.map +1 -1
- package/dist/client.mjs +46 -6
- package/dist/client.mjs.map +1 -1
- package/dist/cloudflare/bindings.cjs +1 -2
- package/dist/cloudflare/bindings.d.cts +0 -2
- package/dist/cloudflare/bindings.d.cts.map +1 -1
- package/dist/cloudflare/bindings.d.mts +0 -2
- package/dist/cloudflare/bindings.d.mts.map +1 -1
- package/dist/cloudflare/bindings.mjs +1 -2
- package/dist/cloudflare/index.cjs +2 -2
- package/dist/cloudflare/index.mjs +2 -2
- package/dist/cloudflare/runner.cjs +2 -3
- package/dist/cloudflare/runner.d.cts +1 -3
- package/dist/cloudflare/runner.d.cts.map +1 -1
- package/dist/cloudflare/runner.d.mts +1 -3
- package/dist/cloudflare/runner.d.mts.map +1 -1
- package/dist/cloudflare/runner.mjs +2 -3
- package/dist/cloudflare/worker.cjs +1 -2
- package/dist/cloudflare/worker.d.cts +0 -2
- package/dist/cloudflare/worker.d.cts.map +1 -1
- package/dist/cloudflare/worker.d.mts +0 -2
- package/dist/cloudflare/worker.d.mts.map +1 -1
- package/dist/cloudflare/worker.mjs +1 -2
- package/dist/cloudflare/workers.d.cts +0 -2
- package/dist/cloudflare/workers.d.cts.map +1 -1
- package/dist/cloudflare/workers.d.mts +0 -2
- package/dist/cloudflare/workers.d.mts.map +1 -1
- package/dist/config.cjs +25 -5
- package/dist/config.cjs.map +1 -1
- package/dist/config.d.cts +15 -11
- package/dist/config.d.cts.map +1 -1
- package/dist/config.d.mts +15 -11
- package/dist/config.d.mts.map +1 -1
- package/dist/config.mjs +26 -6
- package/dist/config.mjs.map +1 -1
- package/dist/contract.cjs +12 -2
- package/dist/contract.cjs.map +1 -1
- package/dist/contract.d.cts +28 -9
- package/dist/contract.d.cts.map +1 -1
- package/dist/contract.d.mts +28 -9
- package/dist/contract.d.mts.map +1 -1
- package/dist/contract.mjs +12 -2
- package/dist/contract.mjs.map +1 -1
- package/dist/dsl/action.cjs +1 -2
- package/dist/dsl/action.d.cts +0 -2
- package/dist/dsl/action.d.cts.map +1 -1
- package/dist/dsl/action.d.mts +0 -2
- package/dist/dsl/action.d.mts.map +1 -1
- package/dist/dsl/action.mjs +1 -2
- package/dist/dsl/base.cjs +1 -2
- package/dist/dsl/base.d.cts +0 -2
- package/dist/dsl/base.d.cts.map +1 -1
- package/dist/dsl/base.d.mts +0 -2
- package/dist/dsl/base.d.mts.map +1 -1
- package/dist/dsl/base.mjs +1 -2
- package/dist/dsl/bundle.cjs +5 -5
- package/dist/dsl/bundle.cjs.map +1 -1
- package/dist/dsl/bundle.d.cts +5 -3
- package/dist/dsl/bundle.d.cts.map +1 -1
- package/dist/dsl/bundle.d.mts +5 -3
- package/dist/dsl/bundle.d.mts.map +1 -1
- package/dist/dsl/bundle.mjs +5 -5
- package/dist/dsl/bundle.mjs.map +1 -1
- package/dist/dsl/condition.cjs +1 -2
- package/dist/dsl/condition.d.cts +0 -2
- package/dist/dsl/condition.d.cts.map +1 -1
- package/dist/dsl/condition.d.mts +0 -2
- package/dist/dsl/condition.d.mts.map +1 -1
- package/dist/dsl/condition.mjs +1 -2
- package/dist/dsl/flow.cjs +3 -3
- package/dist/dsl/flow.cjs.map +1 -1
- package/dist/dsl/flow.d.cts +1 -3
- package/dist/dsl/flow.d.cts.map +1 -1
- package/dist/dsl/flow.d.mts +1 -3
- package/dist/dsl/flow.d.mts.map +1 -1
- package/dist/dsl/flow.mjs +3 -3
- package/dist/dsl/flow.mjs.map +1 -1
- package/dist/dsl/refs.cjs +1 -2
- package/dist/dsl/refs.d.cts +0 -2
- package/dist/dsl/refs.d.cts.map +1 -1
- package/dist/dsl/refs.d.mts +0 -2
- package/dist/dsl/refs.d.mts.map +1 -1
- package/dist/dsl/refs.mjs +1 -2
- package/dist/dsl/template.cjs +1 -2
- package/dist/dsl/template.d.cts +0 -2
- package/dist/dsl/template.d.cts.map +1 -1
- package/dist/dsl/template.d.mts +0 -2
- package/dist/dsl/template.d.mts.map +1 -1
- package/dist/dsl/template.mjs +1 -2
- package/dist/dsl/trigger.cjs +1 -2
- package/dist/dsl/trigger.d.cts +0 -2
- package/dist/dsl/trigger.d.cts.map +1 -1
- package/dist/dsl/trigger.d.mts +0 -2
- package/dist/dsl/trigger.d.mts.map +1 -1
- package/dist/dsl/trigger.mjs +1 -2
- package/dist/dsl/workflow.cjs +3 -4
- package/dist/dsl/workflow.d.cts +0 -2
- package/dist/dsl/workflow.d.cts.map +1 -1
- package/dist/dsl/workflow.d.mts +0 -2
- package/dist/dsl/workflow.d.mts.map +1 -1
- package/dist/dsl/workflow.mjs +3 -4
- package/dist/email-context.cjs +1 -2
- package/dist/email-context.d.cts +0 -2
- package/dist/email-context.d.cts.map +1 -1
- package/dist/email-context.d.mts +0 -2
- package/dist/email-context.d.mts.map +1 -1
- package/dist/email-context.mjs +1 -2
- package/dist/email-sender.cjs +2 -3
- package/dist/email-sender.cjs.map +1 -1
- package/dist/email-sender.d.cts +1 -3
- package/dist/email-sender.d.cts.map +1 -1
- package/dist/email-sender.d.mts +1 -3
- package/dist/email-sender.d.mts.map +1 -1
- package/dist/email-sender.mjs +2 -3
- package/dist/email-sender.mjs.map +1 -1
- package/dist/email.cjs +33 -7
- package/dist/email.cjs.map +1 -1
- package/dist/email.d.cts +38 -11
- package/dist/email.d.cts.map +1 -1
- package/dist/email.d.mts +38 -11
- package/dist/email.d.mts.map +1 -1
- package/dist/email.mjs +33 -8
- package/dist/email.mjs.map +1 -1
- package/dist/engine/actions.cjs +1 -2
- package/dist/engine/actions.d.cts +0 -2
- package/dist/engine/actions.d.cts.map +1 -1
- package/dist/engine/actions.d.mts +0 -2
- package/dist/engine/actions.d.mts.map +1 -1
- package/dist/engine/actions.mjs +1 -2
- package/dist/engine/bucket.cjs +1 -2
- package/dist/engine/bucket.d.cts +0 -2
- package/dist/engine/bucket.d.cts.map +1 -1
- package/dist/engine/bucket.d.mts +0 -2
- package/dist/engine/bucket.d.mts.map +1 -1
- package/dist/engine/bucket.mjs +1 -2
- package/dist/engine/condition.cjs +1 -2
- package/dist/engine/condition.cjs.map +1 -1
- package/dist/engine/condition.d.cts +0 -2
- package/dist/engine/condition.d.cts.map +1 -1
- package/dist/engine/condition.d.mts +0 -2
- package/dist/engine/condition.d.mts.map +1 -1
- package/dist/engine/condition.mjs +1 -2
- package/dist/engine/condition.mjs.map +1 -1
- package/dist/engine/duration.cjs +1 -2
- package/dist/engine/duration.d.cts +0 -2
- package/dist/engine/duration.d.cts.map +1 -1
- package/dist/engine/duration.d.mts +0 -2
- package/dist/engine/duration.d.mts.map +1 -1
- package/dist/engine/duration.mjs +1 -2
- package/dist/engine/index.cjs +1 -1
- package/dist/engine/index.mjs +1 -1
- package/dist/engine/interpreter.cjs +22 -6
- package/dist/engine/interpreter.cjs.map +1 -1
- package/dist/engine/interpreter.d.cts +0 -2
- package/dist/engine/interpreter.d.cts.map +1 -1
- package/dist/engine/interpreter.d.mts +0 -2
- package/dist/engine/interpreter.d.mts.map +1 -1
- package/dist/engine/interpreter.mjs +22 -6
- package/dist/engine/interpreter.mjs.map +1 -1
- package/dist/engine/ports.d.cts +8 -4
- package/dist/engine/ports.d.cts.map +1 -1
- package/dist/engine/ports.d.mts +8 -4
- package/dist/engine/ports.d.mts.map +1 -1
- package/dist/engine/recipient.cjs +1 -2
- package/dist/engine/recipient.d.cts +0 -2
- package/dist/engine/recipient.d.cts.map +1 -1
- package/dist/engine/recipient.d.mts +0 -2
- package/dist/engine/recipient.d.mts.map +1 -1
- package/dist/engine/recipient.mjs +1 -2
- package/dist/engine/retry.cjs +1 -2
- package/dist/engine/retry.d.cts +0 -2
- package/dist/engine/retry.d.cts.map +1 -1
- package/dist/engine/retry.d.mts +0 -2
- package/dist/engine/retry.d.mts.map +1 -1
- package/dist/engine/retry.mjs +1 -2
- package/dist/engine/time-scale.cjs +1 -2
- package/dist/engine/time-scale.d.cts +0 -2
- package/dist/engine/time-scale.d.cts.map +1 -1
- package/dist/engine/time-scale.d.mts +0 -2
- package/dist/engine/time-scale.d.mts.map +1 -1
- package/dist/engine/time-scale.mjs +1 -2
- package/dist/engine/time-window.cjs +1 -2
- package/dist/engine/time-window.d.cts +0 -2
- package/dist/engine/time-window.d.cts.map +1 -1
- package/dist/engine/time-window.d.mts +0 -2
- package/dist/engine/time-window.d.mts.map +1 -1
- package/dist/engine/time-window.mjs +1 -2
- package/dist/fcm.cjs +1 -2
- package/dist/fcm.d.cts +0 -2
- package/dist/fcm.d.cts.map +1 -1
- package/dist/fcm.d.mts +0 -2
- package/dist/fcm.d.mts.map +1 -1
- package/dist/fcm.mjs +1 -2
- package/dist/hash.cjs +1 -2
- package/dist/hash.d.cts +0 -2
- package/dist/hash.d.cts.map +1 -1
- package/dist/hash.d.mts +0 -2
- package/dist/hash.d.mts.map +1 -1
- package/dist/hash.mjs +1 -2
- package/dist/host/auth.cjs +1 -2
- package/dist/host/auth.d.cts +0 -2
- package/dist/host/auth.d.cts.map +1 -1
- package/dist/host/auth.d.mts +0 -2
- package/dist/host/auth.d.mts.map +1 -1
- package/dist/host/auth.mjs +1 -2
- package/dist/host/channels.cjs +2 -3
- package/dist/host/channels.d.cts +0 -2
- package/dist/host/channels.d.cts.map +1 -1
- package/dist/host/channels.d.mts +0 -2
- package/dist/host/channels.d.mts.map +1 -1
- package/dist/host/channels.mjs +2 -3
- package/dist/host/execute.cjs +3 -4
- package/dist/host/execute.d.cts +1 -3
- package/dist/host/execute.d.cts.map +1 -1
- package/dist/host/execute.d.mts +1 -3
- package/dist/host/execute.d.mts.map +1 -1
- package/dist/host/execute.mjs +3 -4
- package/dist/host/fresh-read.cjs +1 -2
- package/dist/host/fresh-read.d.cts +0 -2
- package/dist/host/fresh-read.d.cts.map +1 -1
- package/dist/host/fresh-read.d.mts +0 -2
- package/dist/host/fresh-read.d.mts.map +1 -1
- package/dist/host/fresh-read.mjs +1 -2
- package/dist/host/http.cjs +39 -5
- package/dist/host/http.cjs.map +1 -1
- package/dist/host/http.d.cts +2 -4
- package/dist/host/http.d.cts.map +1 -1
- package/dist/host/http.d.mts +2 -4
- package/dist/host/http.d.mts.map +1 -1
- package/dist/host/http.mjs +40 -6
- package/dist/host/http.mjs.map +1 -1
- package/dist/host/ingest.cjs +129 -6
- package/dist/host/ingest.cjs.map +1 -1
- package/dist/host/ingest.d.cts +71 -2
- package/dist/host/ingest.d.cts.map +1 -1
- package/dist/host/ingest.d.mts +71 -2
- package/dist/host/ingest.d.mts.map +1 -1
- package/dist/host/ingest.mjs +126 -7
- package/dist/host/ingest.mjs.map +1 -1
- package/dist/host/journey-host.cjs +11 -10
- package/dist/host/journey-host.cjs.map +1 -1
- package/dist/host/journey-host.d.cts +0 -2
- package/dist/host/journey-host.d.cts.map +1 -1
- package/dist/host/journey-host.d.mts +0 -2
- package/dist/host/journey-host.d.mts.map +1 -1
- package/dist/host/journey-host.mjs +11 -10
- package/dist/host/journey-host.mjs.map +1 -1
- package/dist/host/platform.cjs +1 -2
- package/dist/host/platform.d.cts +0 -2
- package/dist/host/platform.d.cts.map +1 -1
- package/dist/host/platform.d.mts +0 -2
- package/dist/host/platform.d.mts.map +1 -1
- package/dist/host/platform.mjs +1 -2
- package/dist/host/senders.cjs +1 -2
- package/dist/host/senders.d.cts +0 -2
- package/dist/host/senders.d.cts.map +1 -1
- package/dist/host/senders.d.mts +0 -2
- package/dist/host/senders.d.mts.map +1 -1
- package/dist/host/senders.mjs +1 -2
- package/dist/http2-client.cjs +1 -2
- package/dist/http2-client.d.cts +0 -2
- package/dist/http2-client.d.cts.map +1 -1
- package/dist/http2-client.d.mts +0 -2
- package/dist/http2-client.d.mts.map +1 -1
- package/dist/http2-client.mjs +1 -2
- package/dist/index.cjs +5 -2
- package/dist/index.d.cts +5 -5
- package/dist/index.d.mts +5 -5
- package/dist/index.mjs +5 -5
- package/dist/ir.cjs +12 -4
- package/dist/ir.cjs.map +1 -1
- package/dist/ir.d.cts +17 -2
- package/dist/ir.d.cts.map +1 -1
- package/dist/ir.d.mts +17 -2
- package/dist/ir.d.mts.map +1 -1
- package/dist/ir.mjs +12 -5
- package/dist/ir.mjs.map +1 -1
- package/dist/jwt.cjs +1 -2
- package/dist/jwt.d.cts +0 -2
- package/dist/jwt.d.cts.map +1 -1
- package/dist/jwt.d.mts +0 -2
- package/dist/jwt.d.mts.map +1 -1
- package/dist/jwt.mjs +1 -2
- package/dist/plan.cjs +1 -2
- package/dist/plan.d.cts +0 -2
- package/dist/plan.d.cts.map +1 -1
- package/dist/plan.d.mts +0 -2
- package/dist/plan.d.mts.map +1 -1
- package/dist/plan.mjs +1 -2
- package/dist/postgres-js.cjs +1 -2
- package/dist/postgres-js.d.cts +0 -2
- package/dist/postgres-js.d.cts.map +1 -1
- package/dist/postgres-js.d.mts +0 -2
- package/dist/postgres-js.d.mts.map +1 -1
- package/dist/postgres-js.mjs +1 -2
- package/dist/provenance.cjs +1 -2
- package/dist/provenance.d.cts +0 -2
- package/dist/provenance.d.cts.map +1 -1
- package/dist/provenance.d.mts +0 -2
- package/dist/provenance.d.mts.map +1 -1
- package/dist/provenance.mjs +1 -2
- package/dist/push.cjs +1 -2
- package/dist/push.d.cts +0 -2
- package/dist/push.d.cts.map +1 -1
- package/dist/push.d.mts +0 -2
- package/dist/push.d.mts.map +1 -1
- package/dist/push.mjs +1 -2
- package/dist/react-email.cjs +1 -2
- package/dist/react-email.d.cts +0 -2
- package/dist/react-email.d.cts.map +1 -1
- package/dist/react-email.d.mts +0 -2
- package/dist/react-email.d.mts.map +1 -1
- package/dist/react-email.mjs +1 -2
- package/dist/reporting/classify.cjs +1 -2
- package/dist/reporting/classify.d.cts +0 -2
- package/dist/reporting/classify.d.cts.map +1 -1
- package/dist/reporting/classify.d.mts +0 -2
- package/dist/reporting/classify.d.mts.map +1 -1
- package/dist/reporting/classify.mjs +1 -2
- package/dist/reporting/observer.cjs +2 -2
- package/dist/reporting/observer.cjs.map +1 -1
- package/dist/reporting/observer.d.cts +0 -2
- package/dist/reporting/observer.d.cts.map +1 -1
- package/dist/reporting/observer.d.mts +0 -2
- package/dist/reporting/observer.d.mts.map +1 -1
- package/dist/reporting/observer.mjs +2 -2
- package/dist/reporting/observer.mjs.map +1 -1
- package/dist/reporting/tracking.cjs +29 -12
- package/dist/reporting/tracking.cjs.map +1 -1
- package/dist/reporting/tracking.d.cts +13 -8
- package/dist/reporting/tracking.d.cts.map +1 -1
- package/dist/reporting/tracking.d.mts +13 -8
- package/dist/reporting/tracking.d.mts.map +1 -1
- package/dist/reporting/tracking.mjs +29 -12
- package/dist/reporting/tracking.mjs.map +1 -1
- package/dist/runtime.cjs +1 -2
- package/dist/runtime.d.cts +0 -2
- package/dist/runtime.d.cts.map +1 -1
- package/dist/runtime.d.mts +0 -2
- package/dist/runtime.d.mts.map +1 -1
- package/dist/runtime.mjs +1 -2
- package/dist/store/erasure.cjs +1 -2
- package/dist/store/erasure.d.cts +0 -2
- package/dist/store/erasure.d.cts.map +1 -1
- package/dist/store/erasure.d.mts +0 -2
- package/dist/store/erasure.d.mts.map +1 -1
- package/dist/store/erasure.mjs +1 -2
- package/dist/store/facts.cjs +4 -5
- package/dist/store/facts.cjs.map +1 -1
- package/dist/store/facts.d.cts +2 -4
- package/dist/store/facts.d.cts.map +1 -1
- package/dist/store/facts.d.mts +2 -4
- package/dist/store/facts.d.mts.map +1 -1
- package/dist/store/facts.mjs +4 -5
- package/dist/store/facts.mjs.map +1 -1
- package/dist/store/index.cjs +2 -3
- package/dist/store/index.cjs.map +1 -1
- package/dist/store/index.d.cts +44 -15
- package/dist/store/index.d.cts.map +1 -1
- package/dist/store/index.d.mts +44 -15
- package/dist/store/index.d.mts.map +1 -1
- package/dist/store/index.mjs +2 -3
- package/dist/store/index.mjs.map +1 -1
- package/dist/store/postgres-reporting.cjs +31 -12
- package/dist/store/postgres-reporting.cjs.map +1 -1
- package/dist/store/postgres-reporting.d.cts +4 -4
- package/dist/store/postgres-reporting.d.cts.map +1 -1
- package/dist/store/postgres-reporting.d.mts +4 -4
- package/dist/store/postgres-reporting.d.mts.map +1 -1
- package/dist/store/postgres-reporting.mjs +31 -12
- package/dist/store/postgres-reporting.mjs.map +1 -1
- package/dist/store/postgres.cjs +40 -5
- package/dist/store/postgres.cjs.map +1 -1
- package/dist/store/postgres.d.cts +4 -4
- package/dist/store/postgres.d.cts.map +1 -1
- package/dist/store/postgres.d.mts +4 -4
- package/dist/store/postgres.d.mts.map +1 -1
- package/dist/store/postgres.mjs +41 -6
- package/dist/store/postgres.mjs.map +1 -1
- package/dist/store/sql.cjs +1 -2
- package/dist/store/sql.d.cts +0 -2
- package/dist/store/sql.d.cts.map +1 -1
- package/dist/store/sql.d.mts +0 -2
- package/dist/store/sql.d.mts.map +1 -1
- package/dist/store/sql.mjs +1 -2
- package/dist/store/tables.cjs +76 -13
- package/dist/store/tables.cjs.map +1 -1
- package/dist/store/tables.d.cts +17 -3
- package/dist/store/tables.d.cts.map +1 -1
- package/dist/store/tables.d.mts +17 -3
- package/dist/store/tables.d.mts.map +1 -1
- package/dist/store/tables.mjs +75 -14
- package/dist/store/tables.mjs.map +1 -1
- package/dist/store/user-lock.cjs +1 -2
- package/dist/store/user-lock.d.cts +0 -2
- package/dist/store/user-lock.d.cts.map +1 -1
- package/dist/store/user-lock.d.mts +0 -2
- package/dist/store/user-lock.d.mts.map +1 -1
- package/dist/store/user-lock.mjs +1 -2
- package/dist/templates.cjs +61 -9
- package/dist/templates.cjs.map +1 -1
- package/dist/templates.d.cts +101 -15
- package/dist/templates.d.cts.map +1 -1
- package/dist/templates.d.mts +101 -15
- package/dist/templates.d.mts.map +1 -1
- package/dist/templates.mjs +62 -11
- package/dist/templates.mjs.map +1 -1
- package/dist/transports/cloudflare-email.cjs +1 -2
- package/dist/transports/cloudflare-email.d.cts +0 -2
- package/dist/transports/cloudflare-email.d.cts.map +1 -1
- package/dist/transports/cloudflare-email.d.mts +0 -2
- package/dist/transports/cloudflare-email.d.mts.map +1 -1
- package/dist/transports/cloudflare-email.mjs +1 -2
- package/dist/transports/ses.cjs +1 -2
- package/dist/transports/ses.d.cts +0 -2
- package/dist/transports/ses.d.cts.map +1 -1
- package/dist/transports/ses.d.mts +0 -2
- package/dist/transports/ses.d.mts.map +1 -1
- package/dist/transports/ses.mjs +1 -2
- package/dist/transports/sigv4.cjs +1 -2
- package/dist/transports/sigv4.d.cts +0 -2
- package/dist/transports/sigv4.d.cts.map +1 -1
- package/dist/transports/sigv4.d.mts +0 -2
- package/dist/transports/sigv4.d.mts.map +1 -1
- package/dist/transports/sigv4.mjs +1 -2
- package/dist/webhooks.cjs +1 -2
- package/dist/webhooks.d.cts +0 -2
- package/dist/webhooks.d.cts.map +1 -1
- package/dist/webhooks.d.mts +0 -2
- package/dist/webhooks.d.mts.map +1 -1
- package/dist/webhooks.mjs +1 -2
- package/dist/webpush.cjs +1 -2
- package/dist/webpush.d.cts +0 -2
- package/dist/webpush.d.cts.map +1 -1
- package/dist/webpush.d.mts +0 -2
- package/dist/webpush.d.mts.map +1 -1
- package/dist/webpush.mjs +1 -2
- package/package.json +5 -5
package/dist/client.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.mjs","names":[],"sources":["../src/client.ts"],"sourcesContent":["import type * as core from 'zod/v4/core';\nimport type { Contract, ContractIssue, ProfilePatch } from './contract';\nimport type { DeployResult, IdentifyResult, IngestResult } from './host/ingest';\nimport type { ProfileProps } from './store';\n\n/**\n * `@shware/workflow/client` — the producer side of the ingest API, for the\n * service that knows what users do (an api server, a webhook handler). Plain\n * fetch, no dependencies; errors carry the status and body so a caller can\n * log them without failing the request that triggered the report.\n */\n\nexport interface JourneyClientOptions<C extends Contract | undefined = undefined> {\n /**\n * The shared `defineContract(...)`: types `track` (the event name picks the\n * payload type) and `identify`, and checks both before anything is sent.\n */\n contract?: C;\n /** The journey Worker's origin, e.g. `https://workflow.example.com`. */\n origin: string;\n /** Its API_TOKEN, sent as a bearer; omit against an open local instance. */\n token?: string;\n /** Fetch implementation (defaults to the global one). */\n fetch?: typeof fetch;\n}\n\nexport class JourneyClientError extends Error {\n constructor(\n readonly path: string,\n readonly status: number,\n readonly body: string\n ) {\n super(`journey ${path} failed: ${status} ${body}`);\n this.name = 'JourneyClientError';\n }\n}\n\n/** A payload or profile patch the contract refuses — raised before the request, with the field paths. */\nexport class JourneyValidationError extends Error {\n constructor(\n readonly path: string,\n readonly event: string | undefined,\n readonly issues: ContractIssue[]\n ) {\n super(\n `journey ${path}${event === undefined ? '' : ` '${event}'`} refused by the contract: ${issues\n .map((i) => `${i.path || '(root)'} ${i.message}`)\n .join('; ')}`\n );\n this.name = 'JourneyValidationError';\n }\n}\n\ntype EventsOf<C> = C extends Contract<infer E> ? E : never;\n/** Event names: the contract's, or any string without one. */\nexport type ClientEventName<C> = [C] extends [undefined] ? string : keyof EventsOf<C> & string;\ntype TrackArgs<C, K> = [C] extends [undefined]\n ? [payload?: Record<string, unknown>, options?: TrackOptions]\n : K extends keyof EventsOf<C>\n ? [payload: core.input<EventsOf<C>[K]>, options?: TrackOptions]\n : never;\ntype IdentifyProps<C> = [C] extends [undefined]\n ? ProfileProps\n : C extends Contract<Record<string, core.$ZodObject>, infer P>\n ? ProfilePatch<P>\n : never;\n\nexport interface TrackOptions {\n /** The occurrence's identity: a retry with the same id is recorded once. */\n id?: string;\n /** When it happened (epoch ms); the Worker's clock when omitted. */\n ts?: number;\n}\n\nexport class JourneyClient<C extends Contract | undefined = undefined> {\n constructor(private readonly options: JourneyClientOptions<C>) {}\n\n /** Merge profile properties (`null` clears one). Every property a journey reads must be identified before the event that starts it. */\n identify(userId: string, props: IdentifyProps<C>): Promise<IdentifyResult> {\n const contract = this.options.contract;\n if (contract !== undefined) {\n const result = contract.parseProfilePatch(props);\n if (!result.ok)\n return Promise.reject(new JourneyValidationError('/identify', undefined, result.issues));\n }\n return this.post<IdentifyResult>('/identify', { userId, props });\n }\n\n /**\n * Record an event; the Worker starts every journey whose trigger matches it\n * and wakes waits on it. A caller that retries should pass the same `id`\n * each time: the Worker stores one occurrence per id, so a retry after a\n * timeout cannot double-count the event. `ts` defaults to the Worker's clock.\n */\n track<K extends ClientEventName<C>>(\n userId: string,\n event: K,\n ...args: TrackArgs<C, K>\n ): Promise<IngestResult> {\n const [payload = {}, options = {}] = args as [Record<string, unknown>?, TrackOptions?];\n const contract = this.options.contract;\n if (contract !== undefined) {\n const result = contract.parseEvent(event, payload);\n if (!result.ok)\n return Promise.reject(new JourneyValidationError('/events', event, result.issues));\n }\n return this.post<IngestResult>('/events', {\n userId,\n event,\n payload,\n ...(options.id === undefined ? {} : { id: options.id }),\n ...(options.ts === undefined ? {} : { ts: options.ts }),\n });\n }\n\n /**\n * A push token the app registered for the user (call it on every registration: a token moves\n * to the latest user). The engine keeps devices itself — pushes go to them, a token APNs / FCM\n * reports as gone is dropped — and journeys waiting on `reachable('push')` wake.\n */\n registerDevice(\n userId: string,\n device: { token: string; platform: 'ios' | 'android' | 'web'; ts?: number }\n ): Promise<{ ok: true; woke: number }> {\n const { token, platform, ts } = device;\n return this.post('/devices', {\n userId,\n token,\n platform,\n ...(ts === undefined ? {} : { ts }),\n });\n }\n\n /**\n * A journey push tapped open: the `message_id` the push carried, reported for the user who\n * tapped it. Recorded as a human open on the copy (an email's opens are tracked by the Worker).\n */\n messageOpened(userId: string, messageId: string): Promise<{ ok: true; recorded: boolean }> {\n return this.post('/messages/opened', { userId, messageId });\n }\n\n /**\n * A deleted account: the engine records the erasure, stops the user's running journeys, then\n * erases everything it keeps about them (profile, devices, events, reporting). Once this\n * resolves the request is durable: `erased: false` means an instance would not stop yet, and\n * the Worker's scheduled sweep finishes the job.\n */\n forgetUser(userId: string): Promise<{ ok: true; erased: boolean; pending: string[] }> {\n return this.post('/users/forget', { userId });\n }\n\n /** The user signed out on that device: the token stays registered, bound to nobody. */\n unbindDevice(token: string): Promise<{ ok: true; woke: number }> {\n return this.post('/devices/unbind', { token });\n }\n\n /** Forget a push token for good (an uninstall the app learned of, a token it replaced). */\n removeDevice(token: string): Promise<{ ok: true; woke: number }> {\n return this.post('/devices/remove', { token });\n }\n\n /** Deploy a compiled bundle (BundleIR). */\n deploy(bundle: unknown): Promise<DeployResult> {\n return this.post<DeployResult>('/deploy', bundle);\n }\n\n private async post<T>(path: string, body: unknown): Promise<T> {\n const headers: Record<string, string> = { 'content-type': 'application/json' };\n if (this.options.token !== undefined) headers.authorization = `Bearer ${this.options.token}`;\n const doFetch = this.options.fetch ?? fetch;\n const response = await doFetch(`${this.options.origin.replace(/\\/$/, '')}${path}`, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n });\n if (!response.ok) throw new JourneyClientError(path, response.status, await response.text());\n return (await response.json()) as T;\n }\n}\n"],"mappings":";AA0BA,IAAa,qBAAb,cAAwC,MAAM;CAEjC;CACA;CACA;CAHX,YACE,MACA,QACA,MACA;EACA,MAAM,WAAW,KAAK,WAAW,OAAO,GAAG,MAAM;EAJxC,KAAA,OAAA;EACA,KAAA,SAAA;EACA,KAAA,OAAA;EAGT,KAAK,OAAO;CACd;AACF;;AAGA,IAAa,yBAAb,cAA4C,MAAM;CAErC;CACA;CACA;CAHX,YACE,MACA,OACA,QACA;EACA,MACE,WAAW,OAAO,UAAU,KAAA,IAAY,KAAK,KAAK,MAAM,GAAG,4BAA4B,OACpF,KAAK,MAAM,GAAG,EAAE,QAAQ,SAAS,GAAG,EAAE,SAAS,CAAC,CAChD,KAAK,IAAI,GACd;EARS,KAAA,OAAA;EACA,KAAA,QAAA;EACA,KAAA,SAAA;EAOT,KAAK,OAAO;CACd;AACF;AAuBA,IAAa,gBAAb,MAAuE;CACxC;CAA7B,YAAY,SAAmD;EAAlC,KAAA,UAAA;CAAmC;;CAGhE,SAAS,QAAgB,OAAkD;EACzE,MAAM,WAAW,KAAK,QAAQ;EAC9B,IAAI,aAAa,KAAA,GAAW;GAC1B,MAAM,SAAS,SAAS,kBAAkB,KAAK;GAC/C,IAAI,CAAC,OAAO,IACV,OAAO,QAAQ,OAAO,IAAI,uBAAuB,aAAa,KAAA,GAAW,OAAO,MAAM,CAAC;EAC3F;EACA,OAAO,KAAK,KAAqB,aAAa;GAAE;GAAQ;EAAM,CAAC;CACjE;;;;;;;CAQA,MACE,QACA,OACA,GAAG,MACoB;EACvB,MAAM,CAAC,UAAU,CAAC,GAAG,UAAU,CAAC,KAAK;EACrC,MAAM,WAAW,KAAK,QAAQ;EAC9B,IAAI,aAAa,KAAA,GAAW;GAC1B,MAAM,SAAS,SAAS,WAAW,OAAO,OAAO;GACjD,IAAI,CAAC,OAAO,IACV,OAAO,QAAQ,OAAO,IAAI,uBAAuB,WAAW,OAAO,OAAO,MAAM,CAAC;EACrF;EACA,OAAO,KAAK,KAAmB,WAAW;GACxC;GACA;GACA;GACA,GAAI,QAAQ,OAAO,KAAA,IAAY,CAAC,IAAI,EAAE,IAAI,QAAQ,GAAG;GACrD,GAAI,QAAQ,OAAO,KAAA,IAAY,CAAC,IAAI,EAAE,IAAI,QAAQ,GAAG;EACvD,CAAC;CACH;;;;;;CAOA,eACE,QACA,QACqC;EACrC,MAAM,EAAE,OAAO,UAAU,OAAO;EAChC,OAAO,KAAK,KAAK,YAAY;GAC3B;GACA;GACA;GACA,GAAI,OAAO,KAAA,IAAY,CAAC,IAAI,EAAE,GAAG;EACnC,CAAC;CACH;;;;;CAMA,cAAc,QAAgB,WAA6D;EACzF,OAAO,KAAK,KAAK,oBAAoB;GAAE;GAAQ;EAAU,CAAC;CAC5D;;;;;;;CAQA,WAAW,QAA2E;EACpF,OAAO,KAAK,KAAK,iBAAiB,EAAE,OAAO,CAAC;CAC9C;;CAGA,aAAa,OAAoD;EAC/D,OAAO,KAAK,KAAK,mBAAmB,EAAE,MAAM,CAAC;CAC/C;;CAGA,aAAa,OAAoD;EAC/D,OAAO,KAAK,KAAK,mBAAmB,EAAE,MAAM,CAAC;CAC/C;;CAGA,OAAO,QAAwC;EAC7C,OAAO,KAAK,KAAmB,WAAW,MAAM;CAClD;CAEA,MAAc,KAAQ,MAAc,MAA2B;EAC7D,MAAM,UAAkC,EAAE,gBAAgB,mBAAmB;EAC7E,IAAI,KAAK,QAAQ,UAAU,KAAA,GAAW,QAAQ,gBAAgB,UAAU,KAAK,QAAQ;EAErF,MAAM,WAAW,OADD,KAAK,QAAQ,SAAS,MAAA,CACP,GAAG,KAAK,QAAQ,OAAO,QAAQ,OAAO,EAAE,IAAI,QAAQ;GACjF,QAAQ;GACR;GACA,MAAM,KAAK,UAAU,IAAI;EAC3B,CAAC;EACD,IAAI,CAAC,SAAS,IAAI,MAAM,IAAI,mBAAmB,MAAM,SAAS,QAAQ,MAAM,SAAS,KAAK,CAAC;EAC3F,OAAQ,MAAM,SAAS,KAAK;CAC9B;AACF"}
|
|
1
|
+
{"version":3,"file":"client.mjs","names":[],"sources":["../src/client.ts"],"sourcesContent":["import type * as core from 'zod/v4/core';\nimport type { Contract, ContractIssue, ProfilePatch, Topic } from './contract';\nimport {\n type DeployResult,\n type IdentifyResult,\n type IngestResult,\n SUBSCRIPTION_CHANNELS,\n type Subscriptions,\n type SubscriptionsPatch,\n} from './host/ingest';\nimport type { ProfileProps } from './store';\n\n/**\n * `@shware/workflow/client` — the producer side of the ingest API, for the\n * service that knows what users do (an api server, a webhook handler). Plain\n * fetch, no dependencies; errors carry the status and body so a caller can\n * log them without failing the request that triggered the report.\n */\n\nexport interface JourneyClientOptions<C extends Contract | undefined = undefined> {\n /**\n * The shared `defineContract(...)`: types `track` (the event name picks the\n * payload type) and `identify`, and checks both before anything is sent.\n */\n contract?: C;\n /** The journey Worker's origin, e.g. `https://workflow.example.com`. */\n origin: string;\n /** Its API_TOKEN, sent as a bearer; omit against an open local instance. */\n token?: string;\n /** Fetch implementation (defaults to the global one). */\n fetch?: typeof fetch;\n}\n\nexport class JourneyClientError extends Error {\n constructor(\n readonly path: string,\n readonly status: number,\n readonly body: string\n ) {\n super(`journey ${path} failed: ${status} ${body}`);\n this.name = 'JourneyClientError';\n }\n}\n\n/** A payload or profile patch the contract refuses — raised before the request, with the field paths. */\nexport class JourneyValidationError extends Error {\n constructor(\n readonly path: string,\n readonly event: string | undefined,\n readonly issues: ContractIssue[]\n ) {\n super(\n `journey ${path}${event === undefined ? '' : ` '${event}'`} refused by the contract: ${issues\n .map((i) => `${i.path || '(root)'} ${i.message}`)\n .join('; ')}`\n );\n this.name = 'JourneyValidationError';\n }\n}\n\ntype EventsOf<C> = C extends Contract<infer E> ? E : never;\n/** Event names: the contract's, or any string without one. */\nexport type ClientEventName<C> = [C] extends [undefined] ? string : keyof EventsOf<C> & string;\ntype TrackArgs<C, K> = [C] extends [undefined]\n ? [payload?: Record<string, unknown>, options?: TrackOptions]\n : K extends keyof EventsOf<C>\n ? [payload: core.input<EventsOf<C>[K]>, options?: TrackOptions]\n : never;\ntype IdentifyProps<C> = [C] extends [undefined]\n ? ProfileProps\n : C extends Contract<Record<string, core.$ZodObject>, infer P>\n ? ProfilePatch<P>\n : never;\n\n/** The topics the client's contract declares; any string without a contract. */\nexport type ClientTopic<C> = [C] extends [undefined] ? string : Topic<C>;\n\nexport type {\n ChannelSubscriptions,\n ChannelSubscriptionsPatch,\n SubscriptionChannel,\n Subscriptions,\n SubscriptionsPatch,\n} from './host/ingest';\n\nexport interface TrackOptions {\n /** The occurrence's identity: a retry with the same id is recorded once. */\n id?: string;\n /** When it happened (epoch ms); the Worker's clock when omitted. */\n ts?: number;\n}\n\nexport class JourneyClient<C extends Contract | undefined = undefined> {\n constructor(private readonly options: JourneyClientOptions<C>) {}\n\n /** Merge profile properties (`null` clears one). Every property a journey reads must be identified before the event that starts it. */\n identify(userId: string, props: IdentifyProps<C>): Promise<IdentifyResult> {\n const contract = this.options.contract;\n if (contract !== undefined) {\n const result = contract.parseProfilePatch(props);\n if (!result.ok)\n return Promise.reject(new JourneyValidationError('/identify', undefined, result.issues));\n }\n return this.post<IdentifyResult>('/identify', { userId, props });\n }\n\n /**\n * Record an event; the Worker starts every journey whose trigger matches it\n * and wakes waits on it. A caller that retries should pass the same `id`\n * each time: the Worker stores one occurrence per id, so a retry after a\n * timeout cannot double-count the event. `ts` defaults to the Worker's clock.\n */\n track<K extends ClientEventName<C>>(\n userId: string,\n event: K,\n ...args: TrackArgs<C, K>\n ): Promise<IngestResult> {\n const [payload = {}, options = {}] = args as [Record<string, unknown>?, TrackOptions?];\n const contract = this.options.contract;\n if (contract !== undefined) {\n const result = contract.parseEvent(event, payload);\n if (!result.ok)\n return Promise.reject(new JourneyValidationError('/events', event, result.issues));\n }\n return this.post<IngestResult>('/events', {\n userId,\n event,\n payload,\n ...(options.id === undefined ? {} : { id: options.id }),\n ...(options.ts === undefined ? {} : { ts: options.ts }),\n });\n }\n\n /**\n * A push token the app registered for the user (call it on every registration: a token moves\n * to the latest user). The engine keeps devices itself — pushes go to them, a token APNs / FCM\n * reports as gone is dropped — and journeys waiting on `reachable('push')` wake.\n */\n registerDevice(\n userId: string,\n device: { token: string; platform: 'ios' | 'android' | 'web'; ts?: number }\n ): Promise<{ ok: true; woke: number }> {\n const { token, platform, ts } = device;\n return this.post('/devices', {\n userId,\n token,\n platform,\n ...(ts === undefined ? {} : { ts }),\n });\n }\n\n /**\n * A journey push tapped open: the `message_id` the push carried, reported for the user who\n * tapped it. Recorded as a human open on the copy (an email's opens are tracked by the Worker).\n */\n messageOpened(userId: string, messageId: string): Promise<{ ok: true; recorded: boolean }> {\n return this.post('/messages/opened', { userId, messageId });\n }\n\n /**\n * A deleted account: the engine records the erasure, stops the user's running journeys, then\n * erases everything it keeps about them (profile, devices, events, reporting). Once this\n * resolves the request is durable: `erased: false` means an instance would not stop yet, and\n * the Worker's scheduled sweep finishes the job.\n */\n forgetUser(userId: string): Promise<{ ok: true; erased: boolean; pending: string[] }> {\n return this.post('/users/forget', { userId });\n }\n\n /** The user signed out on that device: the token stays registered, bound to nobody. */\n unbindDevice(token: string): Promise<{ ok: true; woke: number }> {\n return this.post('/devices/unbind', { token });\n }\n\n /** Forget a push token for good (an uninstall the app learned of, a token it replaced). */\n removeDevice(token: string): Promise<{ ok: true; woke: number }> {\n return this.post('/devices/remove', { token });\n }\n\n /**\n * Where the user stands, topic × channel: for `email` and for `push`, `all` (false once they\n * opted out of the whole channel) and each declared topic (false once they opted out of it on\n * that channel) — the engine's record, which a preference page shows as it is. The two are\n * independent flags: a topic answers its own opt-out even while `all` is false, so `all: false`\n * masks every topic however they read (nothing is sent on the channel until it is true again).\n * Rejects with a 410 for an erased user.\n */\n getSubscriptions(userId: string): Promise<Subscriptions<ClientTopic<C>>> {\n return this.request('GET', subscriptionsPath(userId));\n }\n\n /**\n * Set subscriptions: only what the patch names changes, per channel (`{ email: { topics: {\n * newsletter: false } } }` opts out of one topic's emails, `{ push: { all: false } }` of every\n * push — the in-app opt-out App Store Review Guideline 4.5.4 asks for). `all: true` clears the\n * channel-wide opt-out only: a topic opted out on its own stays off until the patch names it,\n * so a preference page's \"subscribe to all\" sends `all: true` plus every topic `true`. Answers\n * the subscriptions as they now stand. With a contract, a topic it does not declare is refused\n * here, before the request; the engine refuses it too.\n */\n setSubscriptions(\n userId: string,\n patch: SubscriptionsPatch<ClientTopic<C>>\n ): Promise<Subscriptions<ClientTopic<C>>> {\n const contract = this.options.contract;\n if (contract !== undefined) {\n const issues: ContractIssue[] = [];\n for (const channel of SUBSCRIPTION_CHANNELS) {\n for (const topic of Object.keys(patch[channel]?.topics ?? {})) {\n // `includes`, not the predicate: narrowing `string` by a `topic is string` leaves `never`\n if (contract.topics.includes(topic)) continue;\n issues.push({ path: `${channel}.topics.${topic}`, message: 'not a declared topic' });\n }\n }\n if (issues.length > 0) {\n return Promise.reject(new JourneyValidationError('/subscriptions', undefined, issues));\n }\n }\n return this.request('PUT', subscriptionsPath(userId), patch);\n }\n\n /** Deploy a compiled bundle (BundleIR). */\n deploy(bundle: unknown): Promise<DeployResult> {\n return this.post<DeployResult>('/deploy', bundle);\n }\n\n private post<T>(path: string, body: unknown): Promise<T> {\n return this.request<T>('POST', path, body);\n }\n\n private async request<T>(\n method: 'GET' | 'POST' | 'PUT',\n path: string,\n body?: unknown\n ): Promise<T> {\n const headers: Record<string, string> = {};\n if (body !== undefined) headers['content-type'] = 'application/json';\n if (this.options.token !== undefined) headers.authorization = `Bearer ${this.options.token}`;\n const doFetch = this.options.fetch ?? fetch;\n const response = await doFetch(`${this.options.origin.replace(/\\/$/, '')}${path}`, {\n method,\n headers,\n ...(body === undefined ? {} : { body: JSON.stringify(body) }),\n });\n if (!response.ok) throw new JourneyClientError(path, response.status, await response.text());\n return (await response.json()) as T;\n }\n}\n\nconst subscriptionsPath = (userId: string) => `/subscriptions/${encodeURIComponent(userId)}`;\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_host_platform = require("../host/platform.cjs");
|
|
3
|
-
|
|
3
|
+
// src/cloudflare/bindings.ts
|
|
4
4
|
/** How Cloudflare's `get` reports an unknown instance id. */
|
|
5
5
|
const CF_NOT_FOUND = "instance.not_found";
|
|
6
6
|
/**
|
|
@@ -42,7 +42,6 @@ const WAKE_TIMEOUT_TOLERANCE_MS = 5e3;
|
|
|
42
42
|
function wakeExpired(deadlineMs, now) {
|
|
43
43
|
return now >= deadlineMs - WAKE_TIMEOUT_TOLERANCE_MS;
|
|
44
44
|
}
|
|
45
|
-
//#endregion
|
|
46
45
|
exports.WAKE_TIMEOUT_TOLERANCE_MS = WAKE_TIMEOUT_TOLERANCE_MS;
|
|
47
46
|
exports.cloudflareJourneyBinding = cloudflareJourneyBinding;
|
|
48
47
|
exports.wakeExpired = wakeExpired;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { EmailAddress } from "../email.cjs";
|
|
2
2
|
import { WorkflowBindingLike } from "../host/platform.cjs";
|
|
3
|
-
//#region src/cloudflare/bindings.d.ts
|
|
4
3
|
/**
|
|
5
4
|
* A minimal structural interface over the Cloudflare bindings.
|
|
6
5
|
*
|
|
@@ -59,5 +58,4 @@ export declare const WAKE_TIMEOUT_TOLERANCE_MS = 5000;
|
|
|
59
58
|
* genuine expiry as a failure.
|
|
60
59
|
*/
|
|
61
60
|
export declare function wakeExpired(deadlineMs: number, now: number): boolean;
|
|
62
|
-
//#endregion
|
|
63
61
|
//# sourceMappingURL=bindings.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bindings.d.cts","names":[],"sources":["../../src/cloudflare/bindings.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"bindings.d.cts","names":[],"sources":["../../src/cloudflare/bindings.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;iBAsBiB;EACf,KAAK;IACH,MAAM;IACN,IAAI;IACJ,UAAU;IACV;IACA;;IAEA,UAAU,SAAS;;IAEnB;MACE;;;YAIM,oBAAoB,KAAK;;;;;;wBAUrB,yBAAyB,SAAS,oBAAoB;;;;;;qBAqBzD;;;;;;;;;;;;;wBAcG,YAAY,oBAAoB"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { EmailAddress } from "../email.mjs";
|
|
2
2
|
import { WorkflowBindingLike } from "../host/platform.mjs";
|
|
3
|
-
//#region src/cloudflare/bindings.d.ts
|
|
4
3
|
/**
|
|
5
4
|
* A minimal structural interface over the Cloudflare bindings.
|
|
6
5
|
*
|
|
@@ -59,5 +58,4 @@ export declare const WAKE_TIMEOUT_TOLERANCE_MS = 5000;
|
|
|
59
58
|
* genuine expiry as a failure.
|
|
60
59
|
*/
|
|
61
60
|
export declare function wakeExpired(deadlineMs: number, now: number): boolean;
|
|
62
|
-
//#endregion
|
|
63
61
|
//# sourceMappingURL=bindings.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bindings.d.mts","names":[],"sources":["../../src/cloudflare/bindings.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"bindings.d.mts","names":[],"sources":["../../src/cloudflare/bindings.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;iBAsBiB;EACf,KAAK;IACH,MAAM;IACN,IAAI;IACJ,UAAU;IACV;IACA;;IAEA,UAAU,SAAS;;IAEnB;MACE;;;YAIM,oBAAoB,KAAK;;;;;;wBAUrB,yBAAyB,SAAS,oBAAoB;;;;;;qBAqBzD;;;;;;;;;;;;;wBAcG,YAAY,oBAAoB"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { InstanceNotFoundError } from "../host/platform.mjs";
|
|
2
|
-
|
|
2
|
+
// src/cloudflare/bindings.ts
|
|
3
3
|
/** How Cloudflare's `get` reports an unknown instance id. */
|
|
4
4
|
const CF_NOT_FOUND = "instance.not_found";
|
|
5
5
|
/**
|
|
@@ -41,7 +41,6 @@ const WAKE_TIMEOUT_TOLERANCE_MS = 5e3;
|
|
|
41
41
|
function wakeExpired(deadlineMs, now) {
|
|
42
42
|
return now >= deadlineMs - WAKE_TIMEOUT_TOLERANCE_MS;
|
|
43
43
|
}
|
|
44
|
-
//#endregion
|
|
45
44
|
export { WAKE_TIMEOUT_TOLERANCE_MS, cloudflareJourneyBinding, wakeExpired };
|
|
46
45
|
|
|
47
46
|
//# sourceMappingURL=bindings.mjs.map
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const
|
|
2
|
+
const require_store_facts = require("../store/facts.cjs");
|
|
3
3
|
const require_store_erasure = require("../store/erasure.cjs");
|
|
4
4
|
const require_store_postgres_reporting = require("../store/postgres-reporting.cjs");
|
|
5
5
|
const require_store_postgres = require("../store/postgres.cjs");
|
|
6
|
-
const require_store_facts = require("../store/facts.cjs");
|
|
7
6
|
const require_store_index = require("../store/index.cjs");
|
|
8
7
|
const require_host_platform = require("../host/platform.cjs");
|
|
9
8
|
const require_host_ingest = require("../host/ingest.cjs");
|
|
9
|
+
const require_email_sender = require("../email-sender.cjs");
|
|
10
10
|
const require_reporting_observer = require("../reporting/observer.cjs");
|
|
11
11
|
const require_host_senders = require("../host/senders.cjs");
|
|
12
12
|
const require_host_channels = require("../host/channels.cjs");
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { JourneyFactSource } from "../store/facts.mjs";
|
|
2
2
|
import { UserErasedError } from "../store/erasure.mjs";
|
|
3
3
|
import { PostgresReporting } from "../store/postgres-reporting.mjs";
|
|
4
4
|
import { PostgresJourneyStore } from "../store/postgres.mjs";
|
|
5
|
-
import { JourneyFactSource } from "../store/facts.mjs";
|
|
6
5
|
import { SchemaMismatchError } from "../store/index.mjs";
|
|
7
6
|
import { InstanceNotFoundError, WAKE_EVENT_TYPE } from "../host/platform.mjs";
|
|
8
7
|
import { BundleIntegrityError, SWEEP_PAGE_SIZE, deployBundle, forgetUser, identifyUser, ingestEvent, registerDevice, removeDevice, sweepMaintenance, sweepSegments, sweepSegmentsPaged, unbindDevice } from "../host/ingest.mjs";
|
|
8
|
+
import { EmailSender, trackEmail } from "../email-sender.mjs";
|
|
9
9
|
import { reportingObserver } from "../reporting/observer.mjs";
|
|
10
10
|
import { LogMessageSender, WebhookMessageSender, routeByChannel, routeByPlatform } from "../host/senders.mjs";
|
|
11
11
|
import { channelsFromConfig, journeyMode, runtimeFromConfig } from "../host/channels.mjs";
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_engine_retry = require("../engine/retry.cjs");
|
|
3
|
-
const require_runtime = require("../runtime.cjs");
|
|
4
3
|
const require_host_platform = require("../host/platform.cjs");
|
|
4
|
+
const require_runtime = require("../runtime.cjs");
|
|
5
5
|
const require_host_execute = require("../host/execute.cjs");
|
|
6
6
|
const require_engine_actions = require("../engine/actions.cjs");
|
|
7
7
|
const require_host_senders = require("../host/senders.cjs");
|
|
8
8
|
const require_cloudflare_bindings = require("./bindings.cjs");
|
|
9
9
|
let cloudflare_workers = require("cloudflare:workers");
|
|
10
10
|
let cloudflare_workflows = require("cloudflare:workflows");
|
|
11
|
-
|
|
11
|
+
// src/cloudflare/runner.ts
|
|
12
12
|
/**
|
|
13
13
|
* Adapts CF's WorkflowStep to EngineStep.
|
|
14
14
|
* subscribe/unsubscribe write the subscription table inside durable steps (the
|
|
@@ -183,7 +183,6 @@ var JourneyRunner = class extends cloudflare_workers.WorkflowEntrypoint {
|
|
|
183
183
|
}
|
|
184
184
|
}
|
|
185
185
|
};
|
|
186
|
-
//#endregion
|
|
187
186
|
exports.JourneyRunner = JourneyRunner;
|
|
188
187
|
|
|
189
188
|
//# sourceMappingURL=runner.cjs.map
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { ActionInvoker, JourneyObserver, JourneyOutcome, MessageSender, MessageValidator } from "../engine/ports.cjs";
|
|
2
2
|
import { RegisteredAction } from "../engine/actions.cjs";
|
|
3
|
-
import { JourneyStore } from "../store/index.cjs";
|
|
4
3
|
import { Contract } from "../contract.cjs";
|
|
4
|
+
import { JourneyStore } from "../store/index.cjs";
|
|
5
5
|
import { JourneyRuntimeOptions } from "../runtime.cjs";
|
|
6
6
|
import { JourneyEnv, JourneyParams } from "../host/platform.cjs";
|
|
7
7
|
import { JourneyRunParts } from "../host/execute.cjs";
|
|
8
8
|
import { WorkflowEntrypoint, WorkflowEvent, WorkflowStep } from "cloudflare:workers";
|
|
9
|
-
//#region src/cloudflare/runner.d.ts
|
|
10
9
|
/**
|
|
11
10
|
* The generic journey executor: one class runs every workflow, because IR is
|
|
12
11
|
* data. It is loaded from the store by the contentHash pinned at entry —
|
|
@@ -81,5 +80,4 @@ export declare class JourneyRunner extends WorkflowEntrypoint<JourneyEnv, Journe
|
|
|
81
80
|
protected createRunParts(store: JourneyStore, params: JourneyParams): JourneyRunParts;
|
|
82
81
|
run(event: WorkflowEvent<JourneyParams>, step: WorkflowStep): Promise<JourneyOutcome>;
|
|
83
82
|
}
|
|
84
|
-
//#endregion
|
|
85
83
|
//# sourceMappingURL=runner.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.d.cts","names":[],"sources":["../../src/cloudflare/runner.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"runner.d.cts","names":[],"sources":["../../src/cloudflare/runner.ts"],"mappings":";;;;;;;;;;;;;;qBAuGa,sBAAsB,mBAAmB,YAAY;;;;;;;YAOtD,eAAe;;YAKT,WAAW,QAAQ,eAAe;UAE1C;;;;;;YAOE,WAAW;;;;;;;;;;;;YAeX,QAAQ,SAAS,gBAAgB;;;;;;YASjC,uBAAuB;;;;;;;;YAavB,oBAAoB;;;;;YAQpB,YAAY;;;;;;YASZ,0BAA0B;;;;;YAQ1B,eAAe,QAAQ,eAAe;;YAKtC,uBAAuB;;;;;YAQvB,eAAe,OAAO,cAAc,QAAQ,gBAAgB;EAahE,IAAI,OAAO,cAAc,gBAAgB,MAAM,eAAe,QAAQ"}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { ActionInvoker, JourneyObserver, JourneyOutcome, MessageSender, MessageValidator } from "../engine/ports.mjs";
|
|
2
2
|
import { RegisteredAction } from "../engine/actions.mjs";
|
|
3
|
-
import { JourneyStore } from "../store/index.mjs";
|
|
4
3
|
import { Contract } from "../contract.mjs";
|
|
4
|
+
import { JourneyStore } from "../store/index.mjs";
|
|
5
5
|
import { JourneyRuntimeOptions } from "../runtime.mjs";
|
|
6
6
|
import { JourneyEnv, JourneyParams } from "../host/platform.mjs";
|
|
7
7
|
import { JourneyRunParts } from "../host/execute.mjs";
|
|
8
8
|
import { WorkflowEntrypoint, WorkflowEvent, WorkflowStep } from "cloudflare:workers";
|
|
9
|
-
//#region src/cloudflare/runner.d.ts
|
|
10
9
|
/**
|
|
11
10
|
* The generic journey executor: one class runs every workflow, because IR is
|
|
12
11
|
* data. It is loaded from the store by the contentHash pinned at entry —
|
|
@@ -81,5 +80,4 @@ export declare class JourneyRunner extends WorkflowEntrypoint<JourneyEnv, Journe
|
|
|
81
80
|
protected createRunParts(store: JourneyStore, params: JourneyParams): JourneyRunParts;
|
|
82
81
|
run(event: WorkflowEvent<JourneyParams>, step: WorkflowStep): Promise<JourneyOutcome>;
|
|
83
82
|
}
|
|
84
|
-
//#endregion
|
|
85
83
|
//# sourceMappingURL=runner.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.d.mts","names":[],"sources":["../../src/cloudflare/runner.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"runner.d.mts","names":[],"sources":["../../src/cloudflare/runner.ts"],"mappings":";;;;;;;;;;;;;;qBAuGa,sBAAsB,mBAAmB,YAAY;;;;;;;YAOtD,eAAe;;YAKT,WAAW,QAAQ,eAAe;UAE1C;;;;;;YAOE,WAAW;;;;;;;;;;;;YAeX,QAAQ,SAAS,gBAAgB;;;;;;YASjC,uBAAuB;;;;;;;;YAavB,oBAAoB;;;;;YAQpB,YAAY;;;;;;YASZ,0BAA0B;;;;;YAQ1B,eAAe,QAAQ,eAAe;;YAKtC,uBAAuB;;;;;YAQvB,eAAe,OAAO,cAAc,QAAQ,gBAAgB;EAahE,IAAI,OAAO,cAAc,gBAAgB,MAAM,eAAe,QAAQ"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { isRetryable } from "../engine/retry.mjs";
|
|
2
|
-
import { resolveRuntime } from "../runtime.mjs";
|
|
3
2
|
import { WAKE_EVENT_TYPE } from "../host/platform.mjs";
|
|
3
|
+
import { resolveRuntime } from "../runtime.mjs";
|
|
4
4
|
import { executeJourney } from "../host/execute.mjs";
|
|
5
5
|
import { RegistryActionInvoker } from "../engine/actions.mjs";
|
|
6
6
|
import { LogMessageSender, WebhookMessageSender } from "../host/senders.mjs";
|
|
7
7
|
import { cloudflareJourneyBinding, wakeExpired } from "./bindings.mjs";
|
|
8
8
|
import { WorkflowEntrypoint } from "cloudflare:workers";
|
|
9
9
|
import { NonRetryableError } from "cloudflare:workflows";
|
|
10
|
-
|
|
10
|
+
// src/cloudflare/runner.ts
|
|
11
11
|
/**
|
|
12
12
|
* Adapts CF's WorkflowStep to EngineStep.
|
|
13
13
|
* subscribe/unsubscribe write the subscription table inside durable steps (the
|
|
@@ -182,7 +182,6 @@ var JourneyRunner = class extends WorkflowEntrypoint {
|
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
184
|
};
|
|
185
|
-
//#endregion
|
|
186
185
|
export { JourneyRunner };
|
|
187
186
|
|
|
188
187
|
//# sourceMappingURL=runner.mjs.map
|
|
@@ -2,7 +2,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
2
2
|
const require_host_journey_host = require("../host/journey-host.cjs");
|
|
3
3
|
const require_cloudflare_bindings = require("./bindings.cjs");
|
|
4
4
|
const require_cloudflare_runner = require("./runner.cjs");
|
|
5
|
-
|
|
5
|
+
// src/cloudflare/worker.ts
|
|
6
6
|
/**
|
|
7
7
|
* A complete journey host in one call: the runner class with the app's store,
|
|
8
8
|
* outlets and runtime plugged in, and the fetch handler that serves the ingest
|
|
@@ -89,7 +89,6 @@ function journeyWorker(options) {
|
|
|
89
89
|
scheduled
|
|
90
90
|
};
|
|
91
91
|
}
|
|
92
|
-
//#endregion
|
|
93
92
|
exports.EnvValidationError = require_host_journey_host.EnvValidationError;
|
|
94
93
|
exports.journeyWorker = journeyWorker;
|
|
95
94
|
|
|
@@ -2,7 +2,6 @@ import { JourneyEnv } from "../host/platform.cjs";
|
|
|
2
2
|
import { SweepResult } from "../host/ingest.cjs";
|
|
3
3
|
import { EnvValidationError, JourneyHostOptions } from "../host/journey-host.cjs";
|
|
4
4
|
import { JourneyRunner } from "./runner.cjs";
|
|
5
|
-
//#region src/cloudflare/worker.d.ts
|
|
6
5
|
/** The fetch handler's execution context (the subset used). */
|
|
7
6
|
export interface ExecutionContextLike {
|
|
8
7
|
waitUntil(promise: Promise<unknown>): void;
|
|
@@ -41,6 +40,5 @@ export interface JourneyWorker<Env extends JourneyEnv> {
|
|
|
41
40
|
* export default { fetch: journeys.fetch };
|
|
42
41
|
*/
|
|
43
42
|
export declare function journeyWorker<Env extends JourneyEnv>(options: JourneyWorkerOptions<Env>): JourneyWorker<Env>;
|
|
44
|
-
//#endregion
|
|
45
43
|
export { EnvValidationError };
|
|
46
44
|
//# sourceMappingURL=worker.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.d.cts","names":[],"sources":["../../src/cloudflare/worker.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"worker.d.cts","names":[],"sources":["../../src/cloudflare/worker.ts"],"mappings":";;;;;iBAeiB;EACf,UAAU,SAAS;;;;;;;YAQT,qBAAqB,YAAY,cAAc,mBAAmB;iBAE7D,cAAc,YAAY;;EAEzC,gBAAgB,kBAAkB;;EAElC,QAAQ,SAAS,SAAS,KAAK,KAAK,MAAM,yBAAyB,QAAQ;;;;;;;EAO3E,QAAQ,KAAK,QAAQ,QAAQ;;EAE7B,YAAY,qBAAqB,KAAK,KAAK,KAAK,yBAAyB;;;;;;;;;;;;;;wBAe3D,cAAc,YAAY,YACxC,SAAS,qBAAqB,OAC7B,cAAc"}
|
|
@@ -2,7 +2,6 @@ import { JourneyEnv } from "../host/platform.mjs";
|
|
|
2
2
|
import { SweepResult } from "../host/ingest.mjs";
|
|
3
3
|
import { EnvValidationError, JourneyHostOptions } from "../host/journey-host.mjs";
|
|
4
4
|
import { JourneyRunner } from "./runner.mjs";
|
|
5
|
-
//#region src/cloudflare/worker.d.ts
|
|
6
5
|
/** The fetch handler's execution context (the subset used). */
|
|
7
6
|
export interface ExecutionContextLike {
|
|
8
7
|
waitUntil(promise: Promise<unknown>): void;
|
|
@@ -41,6 +40,5 @@ export interface JourneyWorker<Env extends JourneyEnv> {
|
|
|
41
40
|
* export default { fetch: journeys.fetch };
|
|
42
41
|
*/
|
|
43
42
|
export declare function journeyWorker<Env extends JourneyEnv>(options: JourneyWorkerOptions<Env>): JourneyWorker<Env>;
|
|
44
|
-
//#endregion
|
|
45
43
|
export { EnvValidationError };
|
|
46
44
|
//# sourceMappingURL=worker.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.d.mts","names":[],"sources":["../../src/cloudflare/worker.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"worker.d.mts","names":[],"sources":["../../src/cloudflare/worker.ts"],"mappings":";;;;;iBAeiB;EACf,UAAU,SAAS;;;;;;;YAQT,qBAAqB,YAAY,cAAc,mBAAmB;iBAE7D,cAAc,YAAY;;EAEzC,gBAAgB,kBAAkB;;EAElC,QAAQ,SAAS,SAAS,KAAK,KAAK,MAAM,yBAAyB,QAAQ;;;;;;;EAO3E,QAAQ,KAAK,QAAQ,QAAQ;;EAE7B,YAAY,qBAAqB,KAAK,KAAK,KAAK,yBAAyB;;;;;;;;;;;;;;wBAe3D,cAAc,YAAY,YACxC,SAAS,qBAAqB,OAC7B,cAAc"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { EnvValidationError, createJourneyHost } from "../host/journey-host.mjs";
|
|
2
2
|
import { cloudflareJourneyBinding } from "./bindings.mjs";
|
|
3
3
|
import { JourneyRunner } from "./runner.mjs";
|
|
4
|
-
|
|
4
|
+
// src/cloudflare/worker.ts
|
|
5
5
|
/**
|
|
6
6
|
* A complete journey host in one call: the runner class with the app's store,
|
|
7
7
|
* outlets and runtime plugged in, and the fetch handler that serves the ingest
|
|
@@ -88,7 +88,6 @@ function journeyWorker(options) {
|
|
|
88
88
|
scheduled
|
|
89
89
|
};
|
|
90
90
|
}
|
|
91
|
-
//#endregion
|
|
92
91
|
export { EnvValidationError, journeyWorker };
|
|
93
92
|
|
|
94
93
|
//# sourceMappingURL=worker.mjs.map
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//#region src/cloudflare/workers.d.ts
|
|
2
1
|
/**
|
|
3
2
|
* Minimal ambient declaration for cloudflare:workers — only the surface
|
|
4
3
|
* JourneyRunner actually uses. @cloudflare/workers-types is avoided because its
|
|
@@ -39,6 +38,5 @@ declare module 'cloudflare:workflows' {
|
|
|
39
38
|
constructor(message: string, name?: string);
|
|
40
39
|
}
|
|
41
40
|
}
|
|
42
|
-
//#endregion
|
|
43
41
|
export {};
|
|
44
42
|
//# sourceMappingURL=workers.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workers.d.cts","names":[],"sources":["../../src/cloudflare/workers.d.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"workers.d.cts","names":[],"sources":["../../src/cloudflare/workers.d.ts"],"mappings":";;;;;;;mBAOmB,cAAc;IAC7B,SAAS,SAAS;IAClB,WAAW;IACX;;mBAGe,kBAAkB;IACjC,SAAS;IACT,WAAW;IACX;;mBAGe;IACf,GAAG,GAAG,cAAc,gBAAgB,QAAQ,KAAK,QAAQ;IACzD,MAAM,cAAc,8BAA8B;IAClD,WAAW,cAAc,WAAW,gBAAgB;IACpD,aAAa,mBACX,cACA;MAAW;MAAc;QACxB,QAAQ,kBAAkB;;wBAGT,mBAAmB,eAAe;;IAEtD,YAAY,cAAc,KAAK;uBACZ;uBACA,KAAK;aACf,IAAI,OAAO,cAAc,SAAS,MAAM,eAAe;;;;;eAMrD,0BAA0B;IACrC,YAAY,iBAAiB"}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//#region src/cloudflare/workers.d.ts
|
|
2
1
|
/**
|
|
3
2
|
* Minimal ambient declaration for cloudflare:workers — only the surface
|
|
4
3
|
* JourneyRunner actually uses. @cloudflare/workers-types is avoided because its
|
|
@@ -39,6 +38,5 @@ declare module 'cloudflare:workflows' {
|
|
|
39
38
|
constructor(message: string, name?: string);
|
|
40
39
|
}
|
|
41
40
|
}
|
|
42
|
-
//#endregion
|
|
43
41
|
export {};
|
|
44
42
|
//# sourceMappingURL=workers.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workers.d.mts","names":[],"sources":["../../src/cloudflare/workers.d.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"workers.d.mts","names":[],"sources":["../../src/cloudflare/workers.d.ts"],"mappings":";;;;;;;mBAOmB,cAAc;IAC7B,SAAS,SAAS;IAClB,WAAW;IACX;;mBAGe,kBAAkB;IACjC,SAAS;IACT,WAAW;IACX;;mBAGe;IACf,GAAG,GAAG,cAAc,gBAAgB,QAAQ,KAAK,QAAQ;IACzD,MAAM,cAAc,8BAA8B;IAClD,WAAW,cAAc,WAAW,gBAAgB;IACpD,aAAa,mBACX,cACA;MAAW;MAAc;QACxB,QAAQ,kBAAkB;;wBAGT,mBAAmB,eAAe;;IAEtD,YAAY,cAAc,KAAK;uBACZ;uBACA,KAAK;aACf,IAAI,OAAO,cAAc,SAAS,MAAM,eAAe;;;;;eAMrD,0BAA0B;IACrC,YAAY,iBAAiB"}
|
package/dist/config.cjs
CHANGED
|
@@ -7,7 +7,7 @@ const require_dsl_bundle = require("./dsl/bundle.cjs");
|
|
|
7
7
|
require("./dsl/index.cjs");
|
|
8
8
|
let zod_mini = require("zod/mini");
|
|
9
9
|
zod_mini = require_runtime.__toESM(zod_mini, 1);
|
|
10
|
-
|
|
10
|
+
// src/config.ts
|
|
11
11
|
function defineConfig(definition) {
|
|
12
12
|
return {
|
|
13
13
|
kind: "journey-config",
|
|
@@ -31,7 +31,9 @@ var ConfigCompileError = class extends Error {
|
|
|
31
31
|
* by id, not a hand-kept list). The registry is checked first, used or not:
|
|
32
32
|
* every entry is a template of its channel with an id, an id is registered
|
|
33
33
|
* once per channel, and every email template's metadata is valid, as is the
|
|
34
|
-
* config's `email` (which replaced `emails`: a config still carrying it fails)
|
|
34
|
+
* config's `email` (which replaced `emails`: a config still carrying it fails),
|
|
35
|
+
* and every email and push template's `topic` is one the contract declares (a
|
|
36
|
+
* template with a topic under a config whose contract declares none fails too).
|
|
35
37
|
* Then every message node is checked against it — an
|
|
36
38
|
* unregistered or cross-channel template fails with the workflow, node and
|
|
37
39
|
* channel named, and so does a literal prop the template's schema refuses (a
|
|
@@ -45,10 +47,15 @@ function compileConfig(config) {
|
|
|
45
47
|
let index;
|
|
46
48
|
try {
|
|
47
49
|
const email = require_templates.indexTemplates("email", config.templates?.email);
|
|
50
|
+
const push = require_templates.indexTemplates("push", config.templates?.push);
|
|
48
51
|
require_email.assertEmailConfig(config, email.values());
|
|
52
|
+
for (const template of push.values()) require_email.assertTemplateTopic(template, `push template '${template.id}'`, "push", true);
|
|
53
|
+
const declared = config.contract?.topics ?? [];
|
|
54
|
+
assertTopicsDeclared("email", email.values(), declared);
|
|
55
|
+
assertTopicsDeclared("push", push.values(), declared);
|
|
49
56
|
index = {
|
|
50
57
|
email,
|
|
51
|
-
push
|
|
58
|
+
push,
|
|
52
59
|
discord: require_templates.indexTemplates("discord", config.templates?.discord),
|
|
53
60
|
slack: require_templates.indexTemplates("slack", config.templates?.slack)
|
|
54
61
|
};
|
|
@@ -59,7 +66,8 @@ function compileConfig(config) {
|
|
|
59
66
|
workflows: config.workflows,
|
|
60
67
|
...config.segments === void 0 ? {} : { segments: config.segments },
|
|
61
68
|
...config.actions === void 0 ? {} : { actions: config.actions },
|
|
62
|
-
templates: TEMPLATE_CHANNELS.flatMap((channel) => [...index[channel].values()])
|
|
69
|
+
templates: TEMPLATE_CHANNELS.flatMap((channel) => [...index[channel].values()]),
|
|
70
|
+
subscriptions: { topics: config.contract?.topics ?? [] }
|
|
63
71
|
});
|
|
64
72
|
bundle.workflows.forEach((workflow, i) => {
|
|
65
73
|
checkMessages(workflow, workflow.flow, index, config);
|
|
@@ -73,6 +81,19 @@ const TEMPLATE_CHANNELS = [
|
|
|
73
81
|
"discord",
|
|
74
82
|
"slack"
|
|
75
83
|
];
|
|
84
|
+
/**
|
|
85
|
+
* Every topic template names a topic the contract declares: the engine checks opt-outs by topic,
|
|
86
|
+
* so one the producer cannot set (`JourneyClient.setSubscriptions`) would be a topic nobody
|
|
87
|
+
* can opt out of through the app.
|
|
88
|
+
*/
|
|
89
|
+
function assertTopicsDeclared(channel, templates, declared) {
|
|
90
|
+
const known = new Set(declared);
|
|
91
|
+
for (const template of templates) {
|
|
92
|
+
if (template.topic === null || known.has(template.topic)) continue;
|
|
93
|
+
const list = declared.length === 0 ? "the contract declares no topics (defineContract({ subscriptions: { topics: [...] } }))" : `the contract declares ${declared.map((topic) => `'${topic}'`).join(", ")}`;
|
|
94
|
+
throw new TypeError(`${channel} template '${template.id}': topic '${template.topic}' is not declared — ${list}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
76
97
|
/** The channel's templates by id; undefined for a channel templates cannot be declared for. */
|
|
77
98
|
function indexOf(index, channel) {
|
|
78
99
|
return TEMPLATE_CHANNELS.includes(channel) ? index[channel] : void 0;
|
|
@@ -181,7 +202,6 @@ function checkEmit(workflow, node, config) {
|
|
|
181
202
|
for (const [field, fieldSchema] of Object.entries(shape)) if (!canBeOmitted(fieldSchema) && !Object.hasOwn(node.payload, field)) problems.push(`required field '${field}' is missing`);
|
|
182
203
|
if (problems.length > 0) throw new ConfigCompileError(`${where}: event '${node.event}': ${problems.join("; ")}`);
|
|
183
204
|
}
|
|
184
|
-
//#endregion
|
|
185
205
|
exports.ConfigCompileError = ConfigCompileError;
|
|
186
206
|
exports.apnsEnvironment = apnsEnvironment;
|
|
187
207
|
exports.compileConfig = compileConfig;
|
package/dist/config.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.cjs","names":["indexTemplates","compileBundle","templateUses","CHANNEL_OF","z"],"sources":["../src/config.ts"],"sourcesContent":["import * as z from 'zod/mini';\nimport type * as core from 'zod/v4/core';\nimport type { Contract } from './contract';\nimport { type ActionRef, type SegmentRef, type WorkflowBuilder, compileBundle } from './dsl';\nimport { templateUses } from './dsl/flow';\nimport { type EmailConfig, assertEmailConfig } from './email';\nimport type { BundleIR, ChannelIR, NodeIR, WorkflowIR } from './ir';\nimport type { JourneyMode, JourneyRuntimeConfig } from './runtime';\nimport {\n type AnyTemplate,\n CHANNEL_OF,\n type TemplateChannel,\n type TemplateRegistry,\n indexTemplates,\n} from './templates';\n\n/**\n * `workflow.config.ts` — the project's journey configuration, typed, flat and\n * committed with the code: what it deploys (workflows, segments, actions),\n * the template registries behind them, the contract every inbound event is\n * checked against, what the email templates share, and how the engine runs per\n * environment (time scale, APNs, FCM). The compiler (`compileConfig`), the\n * Worker (`journeyWorker({ config })`) and the Workflow Studio all read this\n * one object, so everything is registered exactly once:\n *\n * export default defineConfig({\n * title: 'Workflow Studio · Acme',\n * contract,\n * workflows, // [onboarding, notificationTest]\n * templates, // { email, push }: the definitions the workflows send\n * email: { unsubscribeUrl: 'https://acme.test/unsubscribe' },\n * runtime: { development: { timeScale: 600, logMessages: true } },\n * apns: { topic: 'io.acme.app', keyId: 'ABC123DEFG', teamId: 'TEAM123456' },\n * });\n *\n * Secrets are the one thing that stay out: a `.p8`, a service account, the\n * ingest bearer. The host reads them from its environment under fixed names\n * (see `JourneyEnv`), so the config says *which* key without carrying it. The\n * config is a source object (schemas, components, functions); only its\n * compiled bundle and preview-safe template data may cross to a browser.\n */\n\n/** Token-based APNs, the `.p8` auth key; the key itself is `APNS_PRIVATE_KEY_BASE64` in the environment. */\nexport interface ApnsConfig {\n /** The app's bundle id (`apns-topic`). */\n topic: string;\n /** The key's 10-character id. */\n keyId: string;\n /** The Apple Developer team id. */\n teamId: string;\n /**\n * Which APNs host each mode's device tokens belong to: `sandbox` for builds\n * signed with `aps-environment: development` (Xcode, the simulator),\n * `production` for App Store and TestFlight builds. Unlisted modes default\n * to `production` for the production mode and `sandbox` for every other.\n */\n environment?: Partial<Record<JourneyMode, 'sandbox' | 'production'>>;\n}\n\n/**\n * Standard Web Push for browsers (VAPID); the key pair is `VAPID_PUBLIC_KEY` /\n * `VAPID_PRIVATE_KEY` in the environment, and the site subscribes with the same public key.\n */\nexport interface WebPushConfig {\n /** Who push services may contact about this sender: `mailto:` or an `https:` URL. */\n subject: string;\n /**\n * The contract event a notification tap the browser reports (`opened_url`) is also recorded\n * as, with `{ message_id }`: the app reports its own taps as an event, and a service worker has\n * no session to do so — without this, browser opens reach the reports but not the conditions\n * (a `performed(e.push_opened)` segment). One event per copy, however often it is tapped.\n */\n openedEvent?: string;\n}\n\n/** FCM HTTP v1 for Android; the service account is `GOOGLE_APPLICATION_CREDENTIALS_BASE64` in the environment. */\nexport interface FcmConfig {\n /** Android notification channel the pushes land in (`android.notification.channel_id`). */\n androidChannelId?: string;\n}\n\n/**\n * The engine's tables live in a Postgres schema of their own, so their short names (`event`,\n * `profile`, `message`, …) never meet the app's. `workflow-ui generate` writes them as one drizzle\n * file on this schema; the host migrates it with its own tooling.\n */\nexport interface DatabaseConfig {\n /** The Postgres schema; `workflow` by default. */\n schema?: string;\n}\n\nexport interface JourneyConfigDefinition {\n /** Studio title (the browser tab names the project). */\n title?: string;\n /** The shared `defineContract(...)` the Worker validates ingest against. */\n contract?: Contract;\n workflows: readonly WorkflowBuilder[];\n segments?: readonly SegmentRef[];\n actions?: readonly ActionRef[];\n /** The template definitions, per channel; each is identified by its own `id`. */\n templates?: TemplateRegistry;\n /** What every email template shares: the default unsubscribe link, how opens and clicks are judged. */\n email?: EmailConfig;\n /** How the engine behaves per mode (`development`, `production`, …): time scale, message logging. */\n runtime?: JourneyRuntimeConfig;\n apns?: ApnsConfig;\n fcm?: FcmConfig;\n webPush?: WebPushConfig;\n /** The schema the store keeps the engine's tables in. */\n database?: DatabaseConfig;\n}\n\nexport interface JourneyConfig extends JourneyConfigDefinition {\n readonly kind: 'journey-config';\n}\n\nexport function defineConfig(definition: JourneyConfigDefinition): JourneyConfig {\n return { kind: 'journey-config', ...definition };\n}\n\nexport function isConfig(value: unknown): value is JourneyConfig {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { kind?: unknown }).kind === 'journey-config'\n );\n}\n\n/** The APNs environment for `mode`: the configured one, else production only for production. */\nexport function apnsEnvironment(config: ApnsConfig, mode: JourneyMode): 'sandbox' | 'production' {\n return config.environment?.[mode] ?? (mode === 'production' ? 'production' : 'sandbox');\n}\n\n/** A message node the config's registries cannot serve, or whose literal props its template refuses. */\nexport class ConfigCompileError extends Error {\n override readonly name = 'ConfigCompileError';\n}\n\n/**\n * Compile the config: the bundle `compileBundle` would produce, with the\n * template manifest generated from the registry (every registered template,\n * by id, not a hand-kept list). The registry is checked first, used or not:\n * every entry is a template of its channel with an id, an id is registered\n * once per channel, and every email template's metadata is valid, as is the\n * config's `email` (which replaced `emails`: a config still carrying it fails).\n * Then every message node is checked against it — an\n * unregistered or cross-channel template fails with the workflow, node and\n * channel named, and so does a literal prop the template's schema refuses (a\n * missing required prop, an undeclared one, a wrong literal) — and every\n * definition a workflow sends must be the very object registered under its id,\n * not merely one with the same id. User-property references cannot be parsed\n * here (their values exist only at send time) — they are checked by name only,\n * and parsed before sending.\n */\nexport function compileConfig(config: JourneyConfig): BundleIR {\n let index: RegistryIndex;\n try {\n const email = indexTemplates('email', config.templates?.email);\n assertEmailConfig(config, email.values());\n index = {\n email,\n push: indexTemplates('push', config.templates?.push),\n discord: indexTemplates('discord', config.templates?.discord),\n slack: indexTemplates('slack', config.templates?.slack),\n };\n } catch (error) {\n throw new ConfigCompileError((error as Error).message);\n }\n const bundle = compileBundle({\n workflows: config.workflows,\n ...(config.segments === undefined ? {} : { segments: config.segments }),\n ...(config.actions === undefined ? {} : { actions: config.actions }),\n templates: TEMPLATE_CHANNELS.flatMap((channel) => [...index[channel].values()]),\n });\n bundle.workflows.forEach((workflow, i) => {\n checkMessages(workflow, workflow.flow, index, config);\n checkUses(workflow, config.workflows[i], index, config);\n });\n return bundle;\n}\n\nconst TEMPLATE_CHANNELS = ['email', 'push', 'discord', 'slack'] as const;\n\n/** The registry by id, per channel. */\ntype RegistryIndex = Record<TemplateChannel, ReadonlyMap<string, AnyTemplate>>;\n\n/** The channel's templates by id; undefined for a channel templates cannot be declared for. */\nfunction indexOf(\n index: RegistryIndex,\n channel: ChannelIR\n): ReadonlyMap<string, AnyTemplate> | undefined {\n return (TEMPLATE_CHANNELS as readonly ChannelIR[]).includes(channel)\n ? index[channel as TemplateChannel]\n : undefined;\n}\n\n/**\n * The definitions a workflow sends against the registered ones: same id is not enough — a\n * workflow typed by one definition and a registry holding another under that id would render\n * with a schema the call was never checked against.\n */\nfunction checkUses(\n workflow: WorkflowIR,\n builder: WorkflowBuilder,\n index: RegistryIndex,\n config: JourneyConfig\n): void {\n for (const { template, loc } of templateUses(builder)) {\n const channel = CHANNEL_OF[template.kind];\n const registered = index[channel].get(template.id);\n if (registered === template) continue;\n const at = loc === undefined ? '' : ` (${loc.file}:${loc.line})`;\n const where = `workflow '${workflow.id}'${at}: ${channel} template '${template.id}'`;\n if (registered === undefined) throw new ConfigCompileError(`${where} is not registered`);\n const name = Object.entries(config.templates?.[channel] ?? {}).find(\n ([, entry]) => entry === registered\n )?.[0];\n throw new ConfigCompileError(\n `${where} is not the definition registered under that id (templates.${channel}.${name}) — two definitions share the id`\n );\n }\n}\n\n/** A field that may be left out: optional, or with a default the schema fills in. */\nfunction canBeOmitted(schema: core.$ZodType): boolean {\n const type = schema._zod.def.type;\n return type === 'optional' || type === 'default' || type === 'prefault';\n}\n\nfunction checkMessages(\n workflow: WorkflowIR,\n nodes: readonly NodeIR[],\n index: RegistryIndex,\n config: JourneyConfig\n): void {\n for (const node of nodes) {\n switch (node.type) {\n case 'message':\n checkMessage(workflow, node, index, config);\n break;\n case 'send_event':\n checkEmit(workflow, node, config);\n break;\n case 'branch':\n for (const c of node.cases) checkMessages(workflow, c.flow, index, config);\n if (node.otherwise) checkMessages(workflow, node.otherwise, index, config);\n break;\n case 'cohort':\n for (const arm of node.arms) checkMessages(workflow, arm.flow, index, config);\n break;\n case 'wait_until':\n if (Array.isArray(node.onTimeout)) checkMessages(workflow, node.onTimeout, index, config);\n break;\n default:\n break;\n }\n }\n}\n\nfunction checkMessage(\n workflow: WorkflowIR,\n node: Extract<NodeIR, { type: 'message' }>,\n index: RegistryIndex,\n config: JourneyConfig\n): void {\n const where = `workflow '${workflow.id}' node ${node.id}`;\n const registry = indexOf(index, node.channel);\n // Channels without template definitions (sms, …) are rendered by app-supplied senders\n if (registry === undefined) return;\n const entry = registry.get(node.template);\n if (entry === undefined) {\n const other = TEMPLATE_CHANNELS.find(\n (channel) => channel !== node.channel && index[channel].has(node.template)\n );\n const hint = other === undefined ? '' : ` (it is registered as a ${other} template)`;\n throw new ConfigCompileError(\n `${where}: ${node.channel} template '${node.template}' is not registered${hint}`\n );\n }\n const shape = (entry.props._zod.def as core.$ZodObjectDef).shape;\n const problems: string[] = [];\n for (const [field, value] of Object.entries(node.props)) {\n if (!Object.hasOwn(shape, field)) {\n problems.push(`'${field}' is not a prop of the template`);\n continue;\n }\n if (typeof value === 'object' && 'type' in value) {\n // A user ref's value exists only at send time (parsed then); here its name can be checked\n const profile = config.contract?.schemas.profile._zod.def;\n if (\n profile !== undefined &&\n value.path !== 'userId' &&\n !Object.hasOwn(profile.shape, value.path)\n ) {\n problems.push(\n `'${field}' reads u.${value.path}, which the contract's profile does not declare`\n );\n }\n continue;\n }\n const result = z.safeParse(shape[field], value);\n if (!result.success)\n problems.push(`'${field}' = ${JSON.stringify(value)} is refused by the schema`);\n }\n for (const [field, schema] of Object.entries(shape)) {\n const optional = canBeOmitted(schema);\n if (!optional && !Object.hasOwn(node.props, field))\n problems.push(`required prop '${field}' is missing`);\n }\n if (problems.length > 0) {\n throw new ConfigCompileError(\n `${where}: ${node.channel} '${node.template}': ${problems.join('; ')}`\n );\n }\n}\n\n/**\n * Whether an object schema refuses keys it does not declare: `z.strictObject`\n * sets its catchall to `never`; `z.object` strips unknown keys and\n * `z.looseObject` passes them through, and neither refuses.\n */\nfunction refusesUnknownKeys(schema: core.$ZodType): boolean {\n return (schema._zod.def as core.$ZodObjectDef).catchall?._zod.def.type === 'never';\n}\n\n/**\n * An emitted event against the contract, as far as compile time can see: the\n * event is declared, literal payload values parse against their field, no\n * required field is missing, and — when the event's schema is strict — no\n * literal field is undeclared (the Worker would refuse the whole event at\n * emit time, failing the journey where the compile could have failed the\n * build). Values read from the profile are checked when the event is emitted,\n * by the same contract.\n */\nfunction checkEmit(\n workflow: WorkflowIR,\n node: Extract<NodeIR, { type: 'send_event' }>,\n config: JourneyConfig\n): void {\n const contract = config.contract;\n if (contract === undefined) return;\n const where = `workflow '${workflow.id}' node ${node.id}`;\n if (!contract.isEvent(node.event)) {\n throw new ConfigCompileError(\n `${where}: emits '${String(node.event)}', which is not in the contract`\n );\n }\n const schema = contract.schemas.events[node.event];\n const shape = (schema._zod.def as core.$ZodObjectDef).shape;\n const strict = refusesUnknownKeys(schema);\n const problems: string[] = [];\n for (const [field, value] of Object.entries(node.payload)) {\n if (typeof value === 'object' && 'type' in value) continue; // read at emit time\n if (!Object.hasOwn(shape, field)) {\n // A loose or stripping payload may carry extra fields; a strict one refuses them\n if (strict) problems.push(`'${field}' is not a field the contract declares for the event`);\n continue;\n }\n const result = z.safeParse(shape[field], value);\n if (!result.success)\n problems.push(`'${field}' = ${JSON.stringify(value)} is refused by the contract`);\n }\n for (const [field, fieldSchema] of Object.entries(shape)) {\n const optional = canBeOmitted(fieldSchema);\n if (!optional && !Object.hasOwn(node.payload, field))\n problems.push(`required field '${field}' is missing`);\n }\n if (problems.length > 0) {\n throw new ConfigCompileError(`${where}: event '${node.event}': ${problems.join('; ')}`);\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"config.cjs","names":["indexTemplates","compileBundle","templateUses","CHANNEL_OF","z"],"sources":["../src/config.ts"],"sourcesContent":["import * as z from 'zod/mini';\nimport type * as core from 'zod/v4/core';\nimport type { Contract, Topic } from './contract';\nimport { type ActionRef, type SegmentRef, type WorkflowBuilder, compileBundle } from './dsl';\nimport { templateUses } from './dsl/flow';\nimport { type EmailConfig, assertEmailConfig, assertTemplateTopic } from './email';\nimport type { BundleIR, ChannelIR, NodeIR, WorkflowIR } from './ir';\nimport type { JourneyMode, JourneyRuntimeConfig } from './runtime';\nimport {\n type AnyTemplate,\n CHANNEL_OF,\n type EmailTemplate,\n type PushTemplate,\n type TemplateChannel,\n type TemplateRegistry,\n indexTemplates,\n} from './templates';\n\n/**\n * `workflow.config.ts` — the project's journey configuration, typed, flat and\n * committed with the code: what it deploys (workflows, segments, actions),\n * the template registries behind them, the contract every inbound event is\n * checked against, what the email templates share, and how the engine runs per\n * environment (time scale, APNs, FCM). The compiler (`compileConfig`), the\n * Worker (`journeyWorker({ config })`) and the Workflow Studio all read this\n * one object, so everything is registered exactly once:\n *\n * export default defineConfig({\n * title: 'Workflow Studio · Acme',\n * contract,\n * workflows, // [onboarding, notificationTest]\n * templates, // { email, push }: the definitions the workflows send\n * email: { unsubscribeUrl: 'https://acme.test/unsubscribe' },\n * runtime: { development: { timeScale: 600, logMessages: true } },\n * apns: { topic: 'io.acme.app', keyId: 'ABC123DEFG', teamId: 'TEAM123456' },\n * });\n *\n * Secrets are the one thing that stay out: a `.p8`, a service account, the\n * ingest bearer. The host reads them from its environment under fixed names\n * (see `JourneyEnv`), so the config says *which* key without carrying it. The\n * config is a source object (schemas, components, functions); only its\n * compiled bundle and preview-safe template data may cross to a browser.\n */\n\n/** Token-based APNs, the `.p8` auth key; the key itself is `APNS_PRIVATE_KEY_BASE64` in the environment. */\nexport interface ApnsConfig {\n /** The app's bundle id (`apns-topic`). */\n topic: string;\n /** The key's 10-character id. */\n keyId: string;\n /** The Apple Developer team id. */\n teamId: string;\n /**\n * Which APNs host each mode's device tokens belong to: `sandbox` for builds\n * signed with `aps-environment: development` (Xcode, the simulator),\n * `production` for App Store and TestFlight builds. Unlisted modes default\n * to `production` for the production mode and `sandbox` for every other.\n */\n environment?: Partial<Record<JourneyMode, 'sandbox' | 'production'>>;\n}\n\n/**\n * Standard Web Push for browsers (VAPID); the key pair is `VAPID_PUBLIC_KEY` /\n * `VAPID_PRIVATE_KEY` in the environment, and the site subscribes with the same public key.\n */\nexport interface WebPushConfig {\n /** Who push services may contact about this sender: `mailto:` or an `https:` URL. */\n subject: string;\n /**\n * The contract event a notification tap the browser reports (`opened_url`) is also recorded\n * as, with `{ message_id }`: the app reports its own taps as an event, and a service worker has\n * no session to do so — without this, browser opens reach the reports but not the conditions\n * (a `performed(e.push_opened)` segment). One event per copy, however often it is tapped.\n */\n openedEvent?: string;\n}\n\n/** FCM HTTP v1 for Android; the service account is `GOOGLE_APPLICATION_CREDENTIALS_BASE64` in the environment. */\nexport interface FcmConfig {\n /** Android notification channel the pushes land in (`android.notification.channel_id`). */\n androidChannelId?: string;\n}\n\n/**\n * The engine's tables live in a Postgres schema of their own, so their short names (`event`,\n * `profile`, `message`, …) never meet the app's. `workflow-ui generate` writes them as one drizzle\n * file on this schema; the host migrates it with its own tooling.\n */\nexport interface DatabaseConfig {\n /** The Postgres schema; `workflow` by default. */\n schema?: string;\n}\n\nexport interface JourneyConfigDefinition<C extends Contract | undefined = Contract | undefined> {\n /** Studio title (the browser tab names the project). */\n title?: string;\n /** The shared `defineContract(...)` the Worker validates ingest against. */\n contract?: C;\n workflows: readonly WorkflowBuilder[];\n segments?: readonly SegmentRef[];\n actions?: readonly ActionRef[];\n /**\n * The template definitions, per channel; each is identified by its own `id`. With a\n * `contract`, an email or push template's `topic` must be one it declares — in the editor\n * here, and at compile for anything the types did not see.\n */\n templates?: TemplateRegistry<Topic<C>>;\n /** What every email template shares: the default unsubscribe link, how opens and clicks are judged. */\n email?: EmailConfig;\n /** How the engine behaves per mode (`development`, `production`, …): time scale, message logging. */\n runtime?: JourneyRuntimeConfig;\n apns?: ApnsConfig;\n fcm?: FcmConfig;\n webPush?: WebPushConfig;\n /** The schema the store keeps the engine's tables in. */\n database?: DatabaseConfig;\n}\n\nexport interface JourneyConfig extends JourneyConfigDefinition {\n readonly kind: 'journey-config';\n}\n\nexport function defineConfig<C extends Contract | undefined = undefined>(\n definition: JourneyConfigDefinition<C>\n): JourneyConfig {\n return { kind: 'journey-config', ...(definition as JourneyConfigDefinition) };\n}\n\nexport function isConfig(value: unknown): value is JourneyConfig {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { kind?: unknown }).kind === 'journey-config'\n );\n}\n\n/** The APNs environment for `mode`: the configured one, else production only for production. */\nexport function apnsEnvironment(config: ApnsConfig, mode: JourneyMode): 'sandbox' | 'production' {\n return config.environment?.[mode] ?? (mode === 'production' ? 'production' : 'sandbox');\n}\n\n/** A message node the config's registries cannot serve, or whose literal props its template refuses. */\nexport class ConfigCompileError extends Error {\n override readonly name = 'ConfigCompileError';\n}\n\n/**\n * Compile the config: the bundle `compileBundle` would produce, with the\n * template manifest generated from the registry (every registered template,\n * by id, not a hand-kept list). The registry is checked first, used or not:\n * every entry is a template of its channel with an id, an id is registered\n * once per channel, and every email template's metadata is valid, as is the\n * config's `email` (which replaced `emails`: a config still carrying it fails),\n * and every email and push template's `topic` is one the contract declares (a\n * template with a topic under a config whose contract declares none fails too).\n * Then every message node is checked against it — an\n * unregistered or cross-channel template fails with the workflow, node and\n * channel named, and so does a literal prop the template's schema refuses (a\n * missing required prop, an undeclared one, a wrong literal) — and every\n * definition a workflow sends must be the very object registered under its id,\n * not merely one with the same id. User-property references cannot be parsed\n * here (their values exist only at send time) — they are checked by name only,\n * and parsed before sending.\n */\nexport function compileConfig(config: JourneyConfig): BundleIR {\n let index: RegistryIndex;\n try {\n const email = indexTemplates('email', config.templates?.email);\n const push = indexTemplates('push', config.templates?.push);\n assertEmailConfig(config, email.values());\n for (const template of push.values()) {\n assertTemplateTopic(template, `push template '${template.id}'`, 'push', true);\n }\n const declared = config.contract?.topics ?? [];\n assertTopicsDeclared('email', email.values(), declared);\n assertTopicsDeclared('push', push.values(), declared);\n index = {\n email,\n push,\n discord: indexTemplates('discord', config.templates?.discord),\n slack: indexTemplates('slack', config.templates?.slack),\n };\n } catch (error) {\n throw new ConfigCompileError((error as Error).message);\n }\n const bundle = compileBundle({\n workflows: config.workflows,\n ...(config.segments === undefined ? {} : { segments: config.segments }),\n ...(config.actions === undefined ? {} : { actions: config.actions }),\n templates: TEMPLATE_CHANNELS.flatMap((channel) => [...index[channel].values()]),\n subscriptions: { topics: config.contract?.topics ?? [] },\n });\n bundle.workflows.forEach((workflow, i) => {\n checkMessages(workflow, workflow.flow, index, config);\n checkUses(workflow, config.workflows[i], index, config);\n });\n return bundle;\n}\n\nconst TEMPLATE_CHANNELS = ['email', 'push', 'discord', 'slack'] as const;\n\n/**\n * Every topic template names a topic the contract declares: the engine checks opt-outs by topic,\n * so one the producer cannot set (`JourneyClient.setSubscriptions`) would be a topic nobody\n * can opt out of through the app.\n */\nfunction assertTopicsDeclared(\n channel: 'email' | 'push',\n templates: Iterable<EmailTemplate | PushTemplate>,\n declared: readonly string[]\n) {\n const known = new Set(declared);\n for (const template of templates) {\n if (template.topic === null || known.has(template.topic)) continue;\n const list =\n declared.length === 0\n ? 'the contract declares no topics (defineContract({ subscriptions: { topics: [...] } }))'\n : `the contract declares ${declared.map((topic) => `'${topic}'`).join(', ')}`;\n throw new TypeError(\n `${channel} template '${template.id}': topic '${template.topic}' is not declared — ${list}`\n );\n }\n}\n\n/** The registry by id, per channel. */\ntype RegistryIndex = Record<TemplateChannel, ReadonlyMap<string, AnyTemplate>>;\n\n/** The channel's templates by id; undefined for a channel templates cannot be declared for. */\nfunction indexOf(\n index: RegistryIndex,\n channel: ChannelIR\n): ReadonlyMap<string, AnyTemplate> | undefined {\n return (TEMPLATE_CHANNELS as readonly ChannelIR[]).includes(channel)\n ? index[channel as TemplateChannel]\n : undefined;\n}\n\n/**\n * The definitions a workflow sends against the registered ones: same id is not enough — a\n * workflow typed by one definition and a registry holding another under that id would render\n * with a schema the call was never checked against.\n */\nfunction checkUses(\n workflow: WorkflowIR,\n builder: WorkflowBuilder,\n index: RegistryIndex,\n config: JourneyConfig\n): void {\n for (const { template, loc } of templateUses(builder)) {\n const channel = CHANNEL_OF[template.kind];\n const registered = index[channel].get(template.id);\n if (registered === template) continue;\n const at = loc === undefined ? '' : ` (${loc.file}:${loc.line})`;\n const where = `workflow '${workflow.id}'${at}: ${channel} template '${template.id}'`;\n if (registered === undefined) throw new ConfigCompileError(`${where} is not registered`);\n const name = Object.entries(config.templates?.[channel] ?? {}).find(\n ([, entry]) => entry === registered\n )?.[0];\n throw new ConfigCompileError(\n `${where} is not the definition registered under that id (templates.${channel}.${name}) — two definitions share the id`\n );\n }\n}\n\n/** A field that may be left out: optional, or with a default the schema fills in. */\nfunction canBeOmitted(schema: core.$ZodType): boolean {\n const type = schema._zod.def.type;\n return type === 'optional' || type === 'default' || type === 'prefault';\n}\n\nfunction checkMessages(\n workflow: WorkflowIR,\n nodes: readonly NodeIR[],\n index: RegistryIndex,\n config: JourneyConfig\n): void {\n for (const node of nodes) {\n switch (node.type) {\n case 'message':\n checkMessage(workflow, node, index, config);\n break;\n case 'send_event':\n checkEmit(workflow, node, config);\n break;\n case 'branch':\n for (const c of node.cases) checkMessages(workflow, c.flow, index, config);\n if (node.otherwise) checkMessages(workflow, node.otherwise, index, config);\n break;\n case 'cohort':\n for (const arm of node.arms) checkMessages(workflow, arm.flow, index, config);\n break;\n case 'wait_until':\n if (Array.isArray(node.onTimeout)) checkMessages(workflow, node.onTimeout, index, config);\n break;\n default:\n break;\n }\n }\n}\n\nfunction checkMessage(\n workflow: WorkflowIR,\n node: Extract<NodeIR, { type: 'message' }>,\n index: RegistryIndex,\n config: JourneyConfig\n): void {\n const where = `workflow '${workflow.id}' node ${node.id}`;\n const registry = indexOf(index, node.channel);\n // Channels without template definitions (sms, …) are rendered by app-supplied senders\n if (registry === undefined) return;\n const entry = registry.get(node.template);\n if (entry === undefined) {\n const other = TEMPLATE_CHANNELS.find(\n (channel) => channel !== node.channel && index[channel].has(node.template)\n );\n const hint = other === undefined ? '' : ` (it is registered as a ${other} template)`;\n throw new ConfigCompileError(\n `${where}: ${node.channel} template '${node.template}' is not registered${hint}`\n );\n }\n const shape = (entry.props._zod.def as core.$ZodObjectDef).shape;\n const problems: string[] = [];\n for (const [field, value] of Object.entries(node.props)) {\n if (!Object.hasOwn(shape, field)) {\n problems.push(`'${field}' is not a prop of the template`);\n continue;\n }\n if (typeof value === 'object' && 'type' in value) {\n // A user ref's value exists only at send time (parsed then); here its name can be checked\n const profile = config.contract?.schemas.profile._zod.def;\n if (\n profile !== undefined &&\n value.path !== 'userId' &&\n !Object.hasOwn(profile.shape, value.path)\n ) {\n problems.push(\n `'${field}' reads u.${value.path}, which the contract's profile does not declare`\n );\n }\n continue;\n }\n const result = z.safeParse(shape[field], value);\n if (!result.success)\n problems.push(`'${field}' = ${JSON.stringify(value)} is refused by the schema`);\n }\n for (const [field, schema] of Object.entries(shape)) {\n const optional = canBeOmitted(schema);\n if (!optional && !Object.hasOwn(node.props, field))\n problems.push(`required prop '${field}' is missing`);\n }\n if (problems.length > 0) {\n throw new ConfigCompileError(\n `${where}: ${node.channel} '${node.template}': ${problems.join('; ')}`\n );\n }\n}\n\n/**\n * Whether an object schema refuses keys it does not declare: `z.strictObject`\n * sets its catchall to `never`; `z.object` strips unknown keys and\n * `z.looseObject` passes them through, and neither refuses.\n */\nfunction refusesUnknownKeys(schema: core.$ZodType): boolean {\n return (schema._zod.def as core.$ZodObjectDef).catchall?._zod.def.type === 'never';\n}\n\n/**\n * An emitted event against the contract, as far as compile time can see: the\n * event is declared, literal payload values parse against their field, no\n * required field is missing, and — when the event's schema is strict — no\n * literal field is undeclared (the Worker would refuse the whole event at\n * emit time, failing the journey where the compile could have failed the\n * build). Values read from the profile are checked when the event is emitted,\n * by the same contract.\n */\nfunction checkEmit(\n workflow: WorkflowIR,\n node: Extract<NodeIR, { type: 'send_event' }>,\n config: JourneyConfig\n): void {\n const contract = config.contract;\n if (contract === undefined) return;\n const where = `workflow '${workflow.id}' node ${node.id}`;\n if (!contract.isEvent(node.event)) {\n throw new ConfigCompileError(\n `${where}: emits '${String(node.event)}', which is not in the contract`\n );\n }\n const schema = contract.schemas.events[node.event];\n const shape = (schema._zod.def as core.$ZodObjectDef).shape;\n const strict = refusesUnknownKeys(schema);\n const problems: string[] = [];\n for (const [field, value] of Object.entries(node.payload)) {\n if (typeof value === 'object' && 'type' in value) continue; // read at emit time\n if (!Object.hasOwn(shape, field)) {\n // A loose or stripping payload may carry extra fields; a strict one refuses them\n if (strict) problems.push(`'${field}' is not a field the contract declares for the event`);\n continue;\n }\n const result = z.safeParse(shape[field], value);\n if (!result.success)\n problems.push(`'${field}' = ${JSON.stringify(value)} is refused by the contract`);\n }\n for (const [field, fieldSchema] of Object.entries(shape)) {\n const optional = canBeOmitted(fieldSchema);\n if (!optional && !Object.hasOwn(node.payload, field))\n problems.push(`required field '${field}' is missing`);\n }\n if (problems.length > 0) {\n throw new ConfigCompileError(`${where}: event '${node.event}': ${problems.join('; ')}`);\n }\n}\n"],"mappings":"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"}
|