@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/contract.d.mts
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import { EventRefs, UserRefs } from "./dsl/refs.mjs";
|
|
2
2
|
import "./dsl/index.mjs";
|
|
3
3
|
import * as core from "zod/v4/core";
|
|
4
|
-
//#region src/contract.d.ts
|
|
5
4
|
/**
|
|
6
|
-
* `defineContract({ events, profile })` — the producer/engine contract as Zod
|
|
5
|
+
* `defineContract({ events, profile, subscriptions? })` — the producer/engine contract as Zod
|
|
7
6
|
* schemas, written once in a package both sides import. From it:
|
|
8
7
|
*
|
|
9
8
|
* - `contract.events` / `contract.user` are the DSL's typed reference tables
|
|
10
9
|
* (what `event<E>()` / `user<U>()` used to be, now derived from the schemas);
|
|
10
|
+
* - `contract.topics` are the subscription topics: every email and push template names one (or
|
|
11
|
+
* is transactional), a user opts out of one at a time per channel, and the compile refuses a
|
|
12
|
+
* topic the contract does not declare;
|
|
11
13
|
* - `JourneyClient({ contract })` types `track` / `identify` by event name and
|
|
12
|
-
* checks payloads before sending;
|
|
13
|
-
* - `journeyWorker({ contract })` re-validates every inbound `/events
|
|
14
|
-
* `/identify` — never trusting the caller used the SDK — and refuses a bad
|
|
14
|
+
* checks payloads before sending, and `getSubscriptions` / `setSubscriptions` by topic;
|
|
15
|
+
* - `journeyWorker({ contract })` re-validates every inbound `/events`,
|
|
16
|
+
* `/identify` and `/subscriptions` — never trusting the caller used the SDK — and refuses a bad
|
|
15
17
|
* one with a 4xx before anything is stored, woken or started.
|
|
16
18
|
*
|
|
17
19
|
* Supported schemas (v1), synchronous and JSON-shaped: event payloads are
|
|
@@ -25,9 +27,22 @@ import * as core from "zod/v4/core";
|
|
|
25
27
|
type ObjectSchema = core.$ZodObject;
|
|
26
28
|
/** The declared fields of an object schema — without a loose object's `[key: string]: unknown`, so the DSL only offers fields that were written down. */
|
|
27
29
|
export type DeclaredOutput<S> = S extends core.$ZodObject<infer Shape> ? core.$InferObjectOutput<Shape, core.$strict['out']> : never;
|
|
28
|
-
|
|
30
|
+
/**
|
|
31
|
+
* The subscription topics a producer and the engine agree on: content categories ('offers',
|
|
32
|
+
* 'tips'), not channels — an email or push template declares one (`emailTemplate({ topic })`,
|
|
33
|
+
* `pushTemplate({ topic })`), and a user opts out of a topic per channel (the preference page is
|
|
34
|
+
* topic × channel). Ids only — display names belong to the app's i18n. A plain array literal
|
|
35
|
+
* is enough: `defineContract` keeps the literals (no `as const` needed), and the union they form
|
|
36
|
+
* types the templates, the config and `JourneyClient.setSubscriptions`.
|
|
37
|
+
*/
|
|
38
|
+
export interface SubscriptionsDefinition<T extends string> {
|
|
39
|
+
topics: readonly T[];
|
|
40
|
+
}
|
|
41
|
+
export interface ContractDefinition<E extends Record<string, ObjectSchema>, P extends ObjectSchema, T extends string = never> {
|
|
29
42
|
events: E;
|
|
30
43
|
profile: P;
|
|
44
|
+
/** The topics, e.g. `{ topics: ['promotions', 'newsletter'] }`; none without it (transactional email and push only). */
|
|
45
|
+
subscriptions?: SubscriptionsDefinition<T>;
|
|
31
46
|
}
|
|
32
47
|
/** One rejected field, with the dotted path the producer can fix. */
|
|
33
48
|
export interface ContractIssue {
|
|
@@ -41,8 +56,11 @@ export type ContractResult<T> = {
|
|
|
41
56
|
ok: false;
|
|
42
57
|
issues: ContractIssue[];
|
|
43
58
|
};
|
|
44
|
-
export interface Contract<E extends Record<string, ObjectSchema> = Record<string, ObjectSchema>, P extends ObjectSchema = ObjectSchema> {
|
|
59
|
+
export interface Contract<E extends Record<string, ObjectSchema> = Record<string, ObjectSchema>, P extends ObjectSchema = ObjectSchema, T extends string = string> {
|
|
45
60
|
readonly kind: 'journey-contract';
|
|
61
|
+
/** The declared subscription topics (`subscriptions.topics`), empty when none were declared. */
|
|
62
|
+
readonly topics: readonly T[];
|
|
63
|
+
isTopic(topic: string): topic is T;
|
|
46
64
|
/** The DSL's event reference table: `e.purchase` — payload refs from the declared fields only. */
|
|
47
65
|
readonly events: EventRefs<{ [K in keyof E]: DeclaredOutput<E[K]>; }>;
|
|
48
66
|
/** The DSL's user-property reference table: `u.name`, plus the system `u.userId`. */
|
|
@@ -71,7 +89,9 @@ export interface Contract<E extends Record<string, ObjectSchema> = Record<string
|
|
|
71
89
|
export type ProfilePatch<P> = { [K in keyof DeclaredOutput<P>]?: DeclaredOutput<P>[K] | null; };
|
|
72
90
|
/** Payload type of one event, for producers: `EventPayload<typeof contract, 'purchase'>`. */
|
|
73
91
|
export type EventPayload<C, K extends string> = C extends Contract<infer E> ? (K extends keyof E ? core.input<E[K]> : never) : never;
|
|
74
|
-
|
|
92
|
+
/** The topics a contract declares, as a union: `Topic<typeof contract>`; `never` without any. */
|
|
93
|
+
export type Topic<C> = C extends Contract<Record<string, ObjectSchema>, ObjectSchema, infer T> ? T : never;
|
|
94
|
+
export declare function defineContract<E extends Record<string, ObjectSchema>, P extends ObjectSchema, const T extends string = never>(definition: ContractDefinition<E, P, T>): Contract<E, P, T>;
|
|
75
95
|
/**
|
|
76
96
|
* An event the contract refuses — raised for events a journey emits itself
|
|
77
97
|
* (`.sendEvent()`), which never pass the HTTP ingest: they go through the same
|
|
@@ -86,6 +106,5 @@ export declare class ContractValidationError extends Error {
|
|
|
86
106
|
/** The one check every event passes, from HTTP or from a journey: declared, and its payload parses. */
|
|
87
107
|
export declare function checkEvent(contract: Contract, name: string, payload: unknown): ContractResult<Record<string, unknown>>;
|
|
88
108
|
export declare function isContract(value: unknown): value is Contract;
|
|
89
|
-
//#endregion
|
|
90
109
|
export {};
|
|
91
110
|
//# sourceMappingURL=contract.d.mts.map
|
package/dist/contract.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.d.mts","names":[],"sources":["../src/contract.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"contract.d.mts","names":[],"sources":["../src/contract.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;KA4BK,eAAe,KAAK;;YAGb,eAAe,KACzB,UAAU,KAAK,iBAAiB,SAC5B,KAAK,mBAAmB,OAAO,KAAK;;;;;;;;;iBAWzB,wBAAwB;EACvC,iBAAiB;;iBAGF,mBACf,UAAU,eAAe,eACzB,UAAU,cACV;EAEA,QAAQ;EACR,SAAS;;EAET,gBAAgB,wBAAwB;;;iBAIzB;EACf;EACA;;YAGU,eAAe;EAAO;EAAU,MAAM;;EAAQ;EAAW,QAAQ;;iBAE5D,SACf,UAAU,eAAe,gBAAgB,eAAe,eACxD,UAAU,eAAe,cACzB;WAES;;WAEA,iBAAiB;EAC1B,QAAQ,gBAAgB,SAAS;;WAExB,QAAQ,aAAa,WAAW,IAAI,eAAe,EAAE;;WAErD,MAAM,SAAS,eAAe;IAAO;;;WAErC;aAAoB,QAAQ;aAAY,SAAS;;;WAEjD,4BAA4B;EACrC,QAAQ,eAAe,cAAc;;EAErC,WAAW,gBAAgB,YACzB,MAAM,GACN,mBACC,eAAe,KAAK,OAAO,EAAE;;;;;;;EAOhC,kBAAkB,iBAAiB,eAAe,aAAa;;;YAIrD,aAAa,QAAQ,WAAW,eAAe,MAAM,eAAe,GAAG;;YAGvE,aAAa,GAAG,oBAC1B,UAAU,eAAe,MAAM,gBAAgB,IAAI,KAAK,MAAM,EAAE;;YAGtD,MAAM,KAChB,UAAU,SAAS,eAAe,eAAe,oBAAoB,KAAK;wBAK5D,eACd,UAAU,eAAe,eACzB,UAAU,oBACJ,0BACN,YAAY,mBAAmB,GAAG,GAAG,KAAK,SAAS,GAAG,GAAG;;;;;;qBA4E9C,gCAAgC;WAGhC;WACA,QAAQ;WAHD;EAClB,YACW,eACA,QAAQ;;;wBAWL,WACd,UAAU,UACV,cACA,mBACC,eAAe;wBAUF,WAAW,iBAAiB,SAAS"}
|
package/dist/contract.mjs
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import { event, user } from "./dsl/refs.mjs";
|
|
2
2
|
import "./dsl/index.mjs";
|
|
3
3
|
import * as z from "zod/mini";
|
|
4
|
-
|
|
4
|
+
// src/contract.ts
|
|
5
|
+
/** A topic id: what goes in a URL, a JSON key and a database column, so no spaces or specials. */
|
|
6
|
+
const TOPIC_ID = /^[A-Za-z0-9][A-Za-z0-9_.-]*$/;
|
|
5
7
|
function defineContract(definition) {
|
|
8
|
+
const topics = [...definition.subscriptions?.topics ?? []];
|
|
9
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10
|
+
for (const topic of topics) {
|
|
11
|
+
if (typeof topic !== "string" || !TOPIC_ID.test(topic)) throw new TypeError(`contract subscriptions.topics: '${topic}' is not a topic id (letters, digits, '_', '-' and '.', starting with a letter or digit)`);
|
|
12
|
+
if (seen.has(topic)) throw new TypeError(`contract subscriptions.topics lists '${topic}' twice`);
|
|
13
|
+
seen.add(topic);
|
|
14
|
+
}
|
|
6
15
|
for (const [name, schema] of Object.entries(definition.events)) {
|
|
7
16
|
if (name.startsWith("$")) throw new TypeError(`contract event '${name}': names starting with $ are reserved`);
|
|
8
17
|
assertJsonSchema(schema, `event '${name}'`, true);
|
|
@@ -21,6 +30,8 @@ function defineContract(definition) {
|
|
|
21
30
|
profile: definition.profile
|
|
22
31
|
},
|
|
23
32
|
eventNames: names,
|
|
33
|
+
topics,
|
|
34
|
+
isTopic: (topic) => seen.has(topic),
|
|
24
35
|
isEvent: (name) => Object.hasOwn(definition.events, name),
|
|
25
36
|
parseEvent(name, payload) {
|
|
26
37
|
if (!Object.hasOwn(definition.events, name)) return {
|
|
@@ -200,7 +211,6 @@ function assertFlatScalar(schema, where) {
|
|
|
200
211
|
if (def.type === "optional") def = def.innerType._zod.def;
|
|
201
212
|
if (!SCALARS.has(def.type)) throw new TypeError(`contract ${where}: profile fields must be flat scalars (string, number, boolean, enum, literal), got ${def.type}`);
|
|
202
213
|
}
|
|
203
|
-
//#endregion
|
|
204
214
|
export { ContractValidationError, checkEvent, defineContract, isContract };
|
|
205
215
|
|
|
206
216
|
//# sourceMappingURL=contract.mjs.map
|
package/dist/contract.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.mjs","names":[],"sources":["../src/contract.ts"],"sourcesContent":["import * as z from 'zod/mini';\nimport type * as core from 'zod/v4/core';\nimport { type EventRefs, type UserRefs, event, user } from './dsl';\n\n/**\n * `defineContract({ events, profile })` — the producer/engine contract as Zod\n * schemas, written once in a package both sides import. From it:\n *\n * - `contract.events` / `contract.user` are the DSL's typed reference tables\n * (what `event<E>()` / `user<U>()` used to be, now derived from the schemas);\n * - `JourneyClient({ contract })` types `track` / `identify` by event name and\n * checks payloads before sending;\n * - `journeyWorker({ contract })` re-validates every inbound `/events` and\n * `/identify` — never trusting the caller used the SDK — and refuses a bad\n * one with a 4xx before anything is stored, woken or started.\n *\n * Supported schemas (v1), synchronous and JSON-shaped: event payloads are\n * object schemas (`z.looseObject` keeps a producer's extra analytics fields,\n * `z.strictObject` refuses them) and may nest objects and arrays; the profile\n * is a flat object of string / number / boolean / enum / literal fields. Input\n * and output must mean the same thing, so defaults, transforms, coercion,\n * custom refinements and async checks are refused when the contract is\n * defined. The Zod instances stay in the host; nothing here changes the IR.\n */\n\ntype ObjectSchema = core.$ZodObject;\n\n/** The declared fields of an object schema — without a loose object's `[key: string]: unknown`, so the DSL only offers fields that were written down. */\nexport type DeclaredOutput<S> =\n S extends core.$ZodObject<infer Shape>\n ? core.$InferObjectOutput<Shape, core.$strict['out']>\n : never;\n\nexport interface ContractDefinition<\n E extends Record<string, ObjectSchema>,\n P extends ObjectSchema,\n> {\n events: E;\n profile: P;\n}\n\n/** One rejected field, with the dotted path the producer can fix. */\nexport interface ContractIssue {\n path: string;\n message: string;\n}\n\nexport type ContractResult<T> = { ok: true; data: T } | { ok: false; issues: ContractIssue[] };\n\nexport interface Contract<\n E extends Record<string, ObjectSchema> = Record<string, ObjectSchema>,\n P extends ObjectSchema = ObjectSchema,\n> {\n readonly kind: 'journey-contract';\n /** The DSL's event reference table: `e.purchase` — payload refs from the declared fields only. */\n readonly events: EventRefs<{ [K in keyof E]: DeclaredOutput<E[K]> }>;\n /** The DSL's user-property reference table: `u.name`, plus the system `u.userId`. */\n readonly user: UserRefs<DeclaredOutput<P> & { userId: string }>;\n /** The raw schemas, for hosts that need them (validation, docs). */\n readonly schemas: { readonly events: E; readonly profile: P };\n /** Every event name the contract declares — what a producer forwards. */\n readonly eventNames: readonly (keyof E & string)[];\n isEvent(name: string): name is keyof E & string;\n /** Validate one event's payload; the parsed value is what may be stored. */\n parseEvent<K extends keyof E & string>(\n name: K,\n payload: unknown\n ): ContractResult<core.output<E[K]>>;\n /**\n * Validate an `/identify` merge patch: omitted fields stay as they are,\n * `null` removes a field, every provided value is checked against its field\n * schema, and an undeclared field is refused — a patch, never a full parse\n * that would fill defaults over fields the caller did not send.\n */\n parseProfilePatch(patch: unknown): ContractResult<ProfilePatch<P>>;\n}\n\n/** An `/identify` patch over the profile: any subset of fields, `null` to remove one. */\nexport type ProfilePatch<P> = { [K in keyof DeclaredOutput<P>]?: DeclaredOutput<P>[K] | null };\n\n/** Payload type of one event, for producers: `EventPayload<typeof contract, 'purchase'>`. */\nexport type EventPayload<C, K extends string> =\n C extends Contract<infer E> ? (K extends keyof E ? core.input<E[K]> : never) : never;\n\nexport function defineContract<E extends Record<string, ObjectSchema>, P extends ObjectSchema>(\n definition: ContractDefinition<E, P>\n): Contract<E, P> {\n for (const [name, schema] of Object.entries(definition.events)) {\n if (name.startsWith('$')) {\n throw new TypeError(`contract event '${name}': names starting with $ are reserved`);\n }\n assertJsonSchema(schema, `event '${name}'`, true);\n }\n assertJsonSchema(definition.profile, 'profile', true);\n const profileShape = (definition.profile._zod.def as core.$ZodObjectDef).shape;\n for (const [field, schema] of Object.entries(profileShape)) {\n assertFlatScalar(schema, `profile.${field}`);\n }\n const strictProfile = definition.profile._zod.def.catchall?._zod.def.type === 'never';\n\n const names = Object.keys(definition.events) as (keyof E & string)[];\n return {\n kind: 'journey-contract',\n events: event() as never,\n user: user() as never,\n schemas: { events: definition.events, profile: definition.profile },\n eventNames: names,\n isEvent: (name): name is keyof E & string => Object.hasOwn(definition.events, name),\n parseEvent(name, payload) {\n if (!Object.hasOwn(definition.events, name)) {\n return { ok: false, issues: [{ path: '', message: `unknown event '${name}'` }] };\n }\n return toResult(z.safeParse(definition.events[name], payload));\n },\n parseProfilePatch(patch) {\n if (typeof patch !== 'object' || patch === null || Array.isArray(patch)) {\n return { ok: false, issues: [{ path: '', message: 'props must be an object' }] };\n }\n const issues: ContractIssue[] = [];\n const data: Record<string, unknown> = {};\n for (const [field, value] of Object.entries(patch)) {\n if (!Object.hasOwn(profileShape, field)) {\n // userId is the request's identity, not a profile field\n if (strictProfile || field === 'userId') {\n issues.push({ path: field, message: 'not a declared profile field' });\n } else {\n data[field] = value;\n }\n continue;\n }\n if (value === null) {\n data[field] = null;\n continue;\n }\n const result = z.safeParse(profileShape[field], value);\n if (result.success) data[field] = result.data;\n else issues.push(...prefixed(field, result.error));\n }\n return issues.length > 0 ? { ok: false, issues } : { ok: true, data: data as never };\n },\n };\n}\n\n/**\n * An event the contract refuses — raised for events a journey emits itself\n * (`.sendEvent()`), which never pass the HTTP ingest: they go through the same\n * check, and a refusal is deterministic, so it is not retried.\n */\nexport class ContractValidationError extends Error {\n override readonly name = 'ContractValidationError';\n constructor(\n readonly event: string,\n readonly issues: ContractIssue[]\n ) {\n super(\n `event '${event}' refused by the contract: ${issues\n .map((i) => `${i.path || '(root)'} ${i.message}`)\n .join('; ')}`\n );\n }\n}\n\n/** The one check every event passes, from HTTP or from a journey: declared, and its payload parses. */\nexport function checkEvent(\n contract: Contract,\n name: string,\n payload: unknown\n): ContractResult<Record<string, unknown>> {\n if (!contract.isEvent(name)) {\n return {\n ok: false,\n issues: [{ path: '', message: `'${String(name)}' is not in the contract` }],\n };\n }\n return contract.parseEvent(name, payload);\n}\n\nexport function isContract(value: unknown): value is Contract {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { kind?: unknown }).kind === 'journey-contract'\n );\n}\n\nfunction toResult<T>(result: core.util.SafeParseResult<T>): ContractResult<T> {\n return result.success\n ? { ok: true, data: result.data }\n : { ok: false, issues: prefixed('', result.error) };\n}\n\nfunction prefixed(prefix: string, error: core.$ZodError): ContractIssue[] {\n return error.issues.map((issue) => ({\n path: [prefix, ...issue.path.map(String)].filter((p) => p !== '').join('.'),\n message: issue.message,\n }));\n}\n\n/* ---------------------------- supported schemas ---------------------------- */\n\n/**\n * The schema types a contract may use — JSON shapes that parse synchronously\n * and mean the same before and after parsing. Anything not listed (default,\n * transform, pipe, lazy, promise, custom, date, bigint, …) is refused, and\n * every container is walked so nothing unsupported hides inside one.\n */\nconst ALLOWED = new Set([\n 'object',\n 'array',\n 'tuple',\n 'union',\n 'intersection',\n 'record',\n 'optional',\n 'nullable',\n 'readonly',\n 'nonoptional',\n 'string',\n 'number',\n 'boolean',\n 'null',\n 'enum',\n 'literal',\n 'unknown',\n 'any',\n 'never',\n]);\n\n/** Checks that change the value (`overwrite`: `.trim()`, `.toLowerCase()`, …) or run arbitrary code (`custom`: `.refine()`, async or not). */\nconst REFUSED_CHECKS = new Set(['custom', 'overwrite']);\n\nfunction assertJsonSchema(schema: unknown, where: string, top = false): void {\n const def = (schema as { _zod?: { def?: core.$ZodTypeDef } } | null | undefined)?._zod?.def;\n if (def === undefined) throw new TypeError(`contract ${where}: not a Zod schema`);\n if (top && def.type !== 'object') {\n throw new TypeError(\n `contract ${where}: must be an object schema (z.looseObject / z.strictObject)`\n );\n }\n if (!ALLOWED.has(def.type)) {\n throw new TypeError(\n `contract ${where}: ${def.type} schemas are not supported (input and output must mean the same JSON, parsed synchronously)`\n );\n }\n if ((def as { coerce?: boolean }).coerce === true) {\n throw new TypeError(`contract ${where}: coercion (z.coerce) is not supported`);\n }\n for (const check of def.checks ?? []) {\n if (REFUSED_CHECKS.has(check._zod.def.check)) {\n throw new TypeError(\n `contract ${where}: ${check._zod.def.check === 'overwrite' ? 'value-changing checks (.overwrite, .trim, …)' : 'custom refinements'} are not supported`\n );\n }\n }\n const children: [string, unknown][] = [];\n switch (def.type) {\n case 'object': {\n const object = def as core.$ZodObjectDef;\n for (const [k, v] of Object.entries(object.shape)) children.push([`${where}.${k}`, v]);\n if (object.catchall !== undefined) children.push([`${where}.*`, object.catchall]);\n break;\n }\n case 'optional':\n case 'nullable':\n case 'readonly':\n case 'nonoptional':\n children.push([where, (def as unknown as { innerType: unknown }).innerType]);\n break;\n case 'array':\n children.push([`${where}[]`, (def as core.$ZodArrayDef).element]);\n break;\n case 'tuple': {\n const tuple = def as core.$ZodTupleDef;\n tuple.items.forEach((item, index) => children.push([`${where}[${index}]`, item]));\n if (tuple.rest !== null) children.push([`${where}[…]`, tuple.rest]);\n break;\n }\n case 'union':\n for (const option of (def as core.$ZodUnionDef).options) children.push([where, option]);\n break;\n case 'intersection': {\n const intersection = def as core.$ZodIntersectionDef;\n children.push([where, intersection.left], [where, intersection.right]);\n break;\n }\n case 'record': {\n const record = def as core.$ZodRecordDef;\n children.push([`${where}{key}`, record.keyType], [`${where}{}`, record.valueType]);\n break;\n }\n default:\n break;\n }\n for (const [path, child] of children) assertJsonSchema(child, path);\n}\n\nconst SCALARS = new Set(['string', 'number', 'boolean', 'enum', 'literal']);\n\nfunction assertFlatScalar(schema: unknown, where: string): void {\n let def = (schema as core.$ZodType)._zod.def;\n if (def.type === 'optional') def = (def as core.$ZodOptionalDef).innerType._zod.def;\n if (!SCALARS.has(def.type)) {\n throw new TypeError(\n `contract ${where}: profile fields must be flat scalars (string, number, boolean, enum, literal), got ${def.type}`\n );\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"contract.mjs","names":[],"sources":["../src/contract.ts"],"sourcesContent":["import * as z from 'zod/mini';\nimport type * as core from 'zod/v4/core';\nimport { type EventRefs, type UserRefs, event, user } from './dsl';\n\n/**\n * `defineContract({ events, profile, subscriptions? })` — the producer/engine contract as Zod\n * schemas, written once in a package both sides import. From it:\n *\n * - `contract.events` / `contract.user` are the DSL's typed reference tables\n * (what `event<E>()` / `user<U>()` used to be, now derived from the schemas);\n * - `contract.topics` are the subscription topics: every email and push template names one (or\n * is transactional), a user opts out of one at a time per channel, and the compile refuses a\n * topic the contract does not declare;\n * - `JourneyClient({ contract })` types `track` / `identify` by event name and\n * checks payloads before sending, and `getSubscriptions` / `setSubscriptions` by topic;\n * - `journeyWorker({ contract })` re-validates every inbound `/events`,\n * `/identify` and `/subscriptions` — never trusting the caller used the SDK — and refuses a bad\n * one with a 4xx before anything is stored, woken or started.\n *\n * Supported schemas (v1), synchronous and JSON-shaped: event payloads are\n * object schemas (`z.looseObject` keeps a producer's extra analytics fields,\n * `z.strictObject` refuses them) and may nest objects and arrays; the profile\n * is a flat object of string / number / boolean / enum / literal fields. Input\n * and output must mean the same thing, so defaults, transforms, coercion,\n * custom refinements and async checks are refused when the contract is\n * defined. The Zod instances stay in the host; nothing here changes the IR.\n */\n\ntype ObjectSchema = core.$ZodObject;\n\n/** The declared fields of an object schema — without a loose object's `[key: string]: unknown`, so the DSL only offers fields that were written down. */\nexport type DeclaredOutput<S> =\n S extends core.$ZodObject<infer Shape>\n ? core.$InferObjectOutput<Shape, core.$strict['out']>\n : never;\n\n/**\n * The subscription topics a producer and the engine agree on: content categories ('offers',\n * 'tips'), not channels — an email or push template declares one (`emailTemplate({ topic })`,\n * `pushTemplate({ topic })`), and a user opts out of a topic per channel (the preference page is\n * topic × channel). Ids only — display names belong to the app's i18n. A plain array literal\n * is enough: `defineContract` keeps the literals (no `as const` needed), and the union they form\n * types the templates, the config and `JourneyClient.setSubscriptions`.\n */\nexport interface SubscriptionsDefinition<T extends string> {\n topics: readonly T[];\n}\n\nexport interface ContractDefinition<\n E extends Record<string, ObjectSchema>,\n P extends ObjectSchema,\n T extends string = never,\n> {\n events: E;\n profile: P;\n /** The topics, e.g. `{ topics: ['promotions', 'newsletter'] }`; none without it (transactional email and push only). */\n subscriptions?: SubscriptionsDefinition<T>;\n}\n\n/** One rejected field, with the dotted path the producer can fix. */\nexport interface ContractIssue {\n path: string;\n message: string;\n}\n\nexport type ContractResult<T> = { ok: true; data: T } | { ok: false; issues: ContractIssue[] };\n\nexport interface Contract<\n E extends Record<string, ObjectSchema> = Record<string, ObjectSchema>,\n P extends ObjectSchema = ObjectSchema,\n T extends string = string,\n> {\n readonly kind: 'journey-contract';\n /** The declared subscription topics (`subscriptions.topics`), empty when none were declared. */\n readonly topics: readonly T[];\n isTopic(topic: string): topic is T;\n /** The DSL's event reference table: `e.purchase` — payload refs from the declared fields only. */\n readonly events: EventRefs<{ [K in keyof E]: DeclaredOutput<E[K]> }>;\n /** The DSL's user-property reference table: `u.name`, plus the system `u.userId`. */\n readonly user: UserRefs<DeclaredOutput<P> & { userId: string }>;\n /** The raw schemas, for hosts that need them (validation, docs). */\n readonly schemas: { readonly events: E; readonly profile: P };\n /** Every event name the contract declares — what a producer forwards. */\n readonly eventNames: readonly (keyof E & string)[];\n isEvent(name: string): name is keyof E & string;\n /** Validate one event's payload; the parsed value is what may be stored. */\n parseEvent<K extends keyof E & string>(\n name: K,\n payload: unknown\n ): ContractResult<core.output<E[K]>>;\n /**\n * Validate an `/identify` merge patch: omitted fields stay as they are,\n * `null` removes a field, every provided value is checked against its field\n * schema, and an undeclared field is refused — a patch, never a full parse\n * that would fill defaults over fields the caller did not send.\n */\n parseProfilePatch(patch: unknown): ContractResult<ProfilePatch<P>>;\n}\n\n/** An `/identify` patch over the profile: any subset of fields, `null` to remove one. */\nexport type ProfilePatch<P> = { [K in keyof DeclaredOutput<P>]?: DeclaredOutput<P>[K] | null };\n\n/** Payload type of one event, for producers: `EventPayload<typeof contract, 'purchase'>`. */\nexport type EventPayload<C, K extends string> =\n C extends Contract<infer E> ? (K extends keyof E ? core.input<E[K]> : never) : never;\n\n/** The topics a contract declares, as a union: `Topic<typeof contract>`; `never` without any. */\nexport type Topic<C> =\n C extends Contract<Record<string, ObjectSchema>, ObjectSchema, infer T> ? T : never;\n\n/** A topic id: what goes in a URL, a JSON key and a database column, so no spaces or specials. */\nconst TOPIC_ID = /^[A-Za-z0-9][A-Za-z0-9_.-]*$/;\n\nexport function defineContract<\n E extends Record<string, ObjectSchema>,\n P extends ObjectSchema,\n const T extends string = never,\n>(definition: ContractDefinition<E, P, T>): Contract<E, P, T> {\n const topics = [...(definition.subscriptions?.topics ?? [])];\n const seen = new Set<string>();\n for (const topic of topics) {\n if (typeof topic !== 'string' || !TOPIC_ID.test(topic)) {\n throw new TypeError(\n `contract subscriptions.topics: '${topic as string}' is not a topic id (letters, digits, '_', '-' and '.', starting with a letter or digit)`\n );\n }\n if (seen.has(topic))\n throw new TypeError(`contract subscriptions.topics lists '${topic}' twice`);\n seen.add(topic);\n }\n for (const [name, schema] of Object.entries(definition.events)) {\n if (name.startsWith('$')) {\n throw new TypeError(`contract event '${name}': names starting with $ are reserved`);\n }\n assertJsonSchema(schema, `event '${name}'`, true);\n }\n assertJsonSchema(definition.profile, 'profile', true);\n const profileShape = (definition.profile._zod.def as core.$ZodObjectDef).shape;\n for (const [field, schema] of Object.entries(profileShape)) {\n assertFlatScalar(schema, `profile.${field}`);\n }\n const strictProfile = definition.profile._zod.def.catchall?._zod.def.type === 'never';\n\n const names = Object.keys(definition.events) as (keyof E & string)[];\n return {\n kind: 'journey-contract',\n events: event() as never,\n user: user() as never,\n schemas: { events: definition.events, profile: definition.profile },\n eventNames: names,\n topics,\n isTopic: (topic): topic is T => seen.has(topic),\n isEvent: (name): name is keyof E & string => Object.hasOwn(definition.events, name),\n parseEvent(name, payload) {\n if (!Object.hasOwn(definition.events, name)) {\n return { ok: false, issues: [{ path: '', message: `unknown event '${name}'` }] };\n }\n return toResult(z.safeParse(definition.events[name], payload));\n },\n parseProfilePatch(patch) {\n if (typeof patch !== 'object' || patch === null || Array.isArray(patch)) {\n return { ok: false, issues: [{ path: '', message: 'props must be an object' }] };\n }\n const issues: ContractIssue[] = [];\n const data: Record<string, unknown> = {};\n for (const [field, value] of Object.entries(patch)) {\n if (!Object.hasOwn(profileShape, field)) {\n // userId is the request's identity, not a profile field\n if (strictProfile || field === 'userId') {\n issues.push({ path: field, message: 'not a declared profile field' });\n } else {\n data[field] = value;\n }\n continue;\n }\n if (value === null) {\n data[field] = null;\n continue;\n }\n const result = z.safeParse(profileShape[field], value);\n if (result.success) data[field] = result.data;\n else issues.push(...prefixed(field, result.error));\n }\n return issues.length > 0 ? { ok: false, issues } : { ok: true, data: data as never };\n },\n };\n}\n\n/**\n * An event the contract refuses — raised for events a journey emits itself\n * (`.sendEvent()`), which never pass the HTTP ingest: they go through the same\n * check, and a refusal is deterministic, so it is not retried.\n */\nexport class ContractValidationError extends Error {\n override readonly name = 'ContractValidationError';\n constructor(\n readonly event: string,\n readonly issues: ContractIssue[]\n ) {\n super(\n `event '${event}' refused by the contract: ${issues\n .map((i) => `${i.path || '(root)'} ${i.message}`)\n .join('; ')}`\n );\n }\n}\n\n/** The one check every event passes, from HTTP or from a journey: declared, and its payload parses. */\nexport function checkEvent(\n contract: Contract,\n name: string,\n payload: unknown\n): ContractResult<Record<string, unknown>> {\n if (!contract.isEvent(name)) {\n return {\n ok: false,\n issues: [{ path: '', message: `'${String(name)}' is not in the contract` }],\n };\n }\n return contract.parseEvent(name, payload);\n}\n\nexport function isContract(value: unknown): value is Contract {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { kind?: unknown }).kind === 'journey-contract'\n );\n}\n\nfunction toResult<T>(result: core.util.SafeParseResult<T>): ContractResult<T> {\n return result.success\n ? { ok: true, data: result.data }\n : { ok: false, issues: prefixed('', result.error) };\n}\n\nfunction prefixed(prefix: string, error: core.$ZodError): ContractIssue[] {\n return error.issues.map((issue) => ({\n path: [prefix, ...issue.path.map(String)].filter((p) => p !== '').join('.'),\n message: issue.message,\n }));\n}\n\n/* ---------------------------- supported schemas ---------------------------- */\n\n/**\n * The schema types a contract may use — JSON shapes that parse synchronously\n * and mean the same before and after parsing. Anything not listed (default,\n * transform, pipe, lazy, promise, custom, date, bigint, …) is refused, and\n * every container is walked so nothing unsupported hides inside one.\n */\nconst ALLOWED = new Set([\n 'object',\n 'array',\n 'tuple',\n 'union',\n 'intersection',\n 'record',\n 'optional',\n 'nullable',\n 'readonly',\n 'nonoptional',\n 'string',\n 'number',\n 'boolean',\n 'null',\n 'enum',\n 'literal',\n 'unknown',\n 'any',\n 'never',\n]);\n\n/** Checks that change the value (`overwrite`: `.trim()`, `.toLowerCase()`, …) or run arbitrary code (`custom`: `.refine()`, async or not). */\nconst REFUSED_CHECKS = new Set(['custom', 'overwrite']);\n\nfunction assertJsonSchema(schema: unknown, where: string, top = false): void {\n const def = (schema as { _zod?: { def?: core.$ZodTypeDef } } | null | undefined)?._zod?.def;\n if (def === undefined) throw new TypeError(`contract ${where}: not a Zod schema`);\n if (top && def.type !== 'object') {\n throw new TypeError(\n `contract ${where}: must be an object schema (z.looseObject / z.strictObject)`\n );\n }\n if (!ALLOWED.has(def.type)) {\n throw new TypeError(\n `contract ${where}: ${def.type} schemas are not supported (input and output must mean the same JSON, parsed synchronously)`\n );\n }\n if ((def as { coerce?: boolean }).coerce === true) {\n throw new TypeError(`contract ${where}: coercion (z.coerce) is not supported`);\n }\n for (const check of def.checks ?? []) {\n if (REFUSED_CHECKS.has(check._zod.def.check)) {\n throw new TypeError(\n `contract ${where}: ${check._zod.def.check === 'overwrite' ? 'value-changing checks (.overwrite, .trim, …)' : 'custom refinements'} are not supported`\n );\n }\n }\n const children: [string, unknown][] = [];\n switch (def.type) {\n case 'object': {\n const object = def as core.$ZodObjectDef;\n for (const [k, v] of Object.entries(object.shape)) children.push([`${where}.${k}`, v]);\n if (object.catchall !== undefined) children.push([`${where}.*`, object.catchall]);\n break;\n }\n case 'optional':\n case 'nullable':\n case 'readonly':\n case 'nonoptional':\n children.push([where, (def as unknown as { innerType: unknown }).innerType]);\n break;\n case 'array':\n children.push([`${where}[]`, (def as core.$ZodArrayDef).element]);\n break;\n case 'tuple': {\n const tuple = def as core.$ZodTupleDef;\n tuple.items.forEach((item, index) => children.push([`${where}[${index}]`, item]));\n if (tuple.rest !== null) children.push([`${where}[…]`, tuple.rest]);\n break;\n }\n case 'union':\n for (const option of (def as core.$ZodUnionDef).options) children.push([where, option]);\n break;\n case 'intersection': {\n const intersection = def as core.$ZodIntersectionDef;\n children.push([where, intersection.left], [where, intersection.right]);\n break;\n }\n case 'record': {\n const record = def as core.$ZodRecordDef;\n children.push([`${where}{key}`, record.keyType], [`${where}{}`, record.valueType]);\n break;\n }\n default:\n break;\n }\n for (const [path, child] of children) assertJsonSchema(child, path);\n}\n\nconst SCALARS = new Set(['string', 'number', 'boolean', 'enum', 'literal']);\n\nfunction assertFlatScalar(schema: unknown, where: string): void {\n let def = (schema as core.$ZodType)._zod.def;\n if (def.type === 'optional') def = (def as core.$ZodOptionalDef).innerType._zod.def;\n if (!SCALARS.has(def.type)) {\n throw new TypeError(\n `contract ${where}: profile fields must be flat scalars (string, number, boolean, enum, literal), got ${def.type}`\n );\n }\n}\n"],"mappings":"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"}
|
package/dist/dsl/action.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
|
|
2
|
+
// src/dsl/action.ts
|
|
3
3
|
/**
|
|
4
4
|
* Define a custom action. The generic `A` types the args `.run()` accepts —
|
|
5
5
|
* the only type injection point, mirroring `template.sms<P>()`:
|
|
@@ -20,7 +20,6 @@ function action(name, handler) {
|
|
|
20
20
|
handler
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
|
-
//#endregion
|
|
24
23
|
exports.action = action;
|
|
25
24
|
|
|
26
25
|
//# sourceMappingURL=action.cjs.map
|
package/dist/dsl/action.d.cts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//#region src/dsl/action.d.ts
|
|
2
1
|
/**
|
|
3
2
|
* Custom actions — the escape hatch for side effects the built-in nodes do not
|
|
4
3
|
* cover (CRM sync, coupon issuing, outbound webhooks with custom auth, …).
|
|
@@ -61,5 +60,4 @@ export interface ActionRef<A extends object = object> {
|
|
|
61
60
|
* two definitions cannot share a name by accident.
|
|
62
61
|
*/
|
|
63
62
|
export declare function action<A extends object = Record<string, never>>(name: string, handler: ActionHandler<A>): ActionRef<A>;
|
|
64
|
-
//#endregion
|
|
65
63
|
//# sourceMappingURL=action.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action.d.cts","names":[],"sources":["../../src/dsl/action.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"action.d.cts","names":[],"sources":["../../src/dsl/action.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;iBAsBiB;EACf;;;;;;;EAOA;;;;;;;;YASU,cAAc,qBAAqB,MAAM,GAAG,KAAK,kBAAkB;;;;;iBAM9D,UAAU;WAChB;;EAET,QAAQ,MAAM,GAAG,KAAK,gBAAgB;;;;;;;;;;;;;;;;wBAiBxB,OAAO,mBAAmB,uBACxC,cACA,SAAS,cAAc,KACtB,UAAU"}
|
package/dist/dsl/action.d.mts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//#region src/dsl/action.d.ts
|
|
2
1
|
/**
|
|
3
2
|
* Custom actions — the escape hatch for side effects the built-in nodes do not
|
|
4
3
|
* cover (CRM sync, coupon issuing, outbound webhooks with custom auth, …).
|
|
@@ -61,5 +60,4 @@ export interface ActionRef<A extends object = object> {
|
|
|
61
60
|
* two definitions cannot share a name by accident.
|
|
62
61
|
*/
|
|
63
62
|
export declare function action<A extends object = Record<string, never>>(name: string, handler: ActionHandler<A>): ActionRef<A>;
|
|
64
|
-
//#endregion
|
|
65
63
|
//# sourceMappingURL=action.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action.d.mts","names":[],"sources":["../../src/dsl/action.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"action.d.mts","names":[],"sources":["../../src/dsl/action.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;iBAsBiB;EACf;;;;;;;EAOA;;;;;;;;YASU,cAAc,qBAAqB,MAAM,GAAG,KAAK,kBAAkB;;;;;iBAM9D,UAAU;WAChB;;EAET,QAAQ,MAAM,GAAG,KAAK,gBAAgB;;;;;;;;;;;;;;;;wBAiBxB,OAAO,mBAAmB,uBACxC,cACA,SAAS,cAAc,KACtB,UAAU"}
|
package/dist/dsl/action.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/dsl/action.ts
|
|
2
2
|
/**
|
|
3
3
|
* Define a custom action. The generic `A` types the args `.run()` accepts —
|
|
4
4
|
* the only type injection point, mirroring `template.sms<P>()`:
|
|
@@ -19,7 +19,6 @@ function action(name, handler) {
|
|
|
19
19
|
handler
|
|
20
20
|
};
|
|
21
21
|
}
|
|
22
|
-
//#endregion
|
|
23
22
|
export { action };
|
|
24
23
|
|
|
25
24
|
//# sourceMappingURL=action.mjs.map
|
package/dist/dsl/base.cjs
CHANGED
|
@@ -2,7 +2,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
2
2
|
const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
|
|
3
3
|
let ms = require("ms");
|
|
4
4
|
ms = require_runtime.__toESM(ms, 1);
|
|
5
|
-
|
|
5
|
+
// src/dsl/base.ts
|
|
6
6
|
const TIME_OF_DAY = /^([01]\d|2[0-3]):([0-5]\d)$/;
|
|
7
7
|
/**
|
|
8
8
|
* Parse an 'HH:mm' literal to minutes since midnight, throwing on anything the
|
|
@@ -27,7 +27,6 @@ function durationIR(value) {
|
|
|
27
27
|
ms: millis
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
|
-
//#endregion
|
|
31
30
|
exports.durationIR = durationIR;
|
|
32
31
|
exports.timeOfDayMinutes = timeOfDayMinutes;
|
|
33
32
|
|
package/dist/dsl/base.d.cts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { DurationIR } from "../ir.cjs";
|
|
2
|
-
//#region src/dsl/base.d.ts
|
|
3
2
|
/**
|
|
4
3
|
* Base vocabulary shared by the whole DSL: the schema constraints an app's
|
|
5
4
|
* types must satisfy, and the literal types (durations, weekdays, clock times)
|
|
@@ -41,5 +40,4 @@ export declare function timeOfDayMinutes(value: TimeOfDay): number;
|
|
|
41
40
|
* @internal shared by condition/flow/workflow, not part of the public API
|
|
42
41
|
*/
|
|
43
42
|
export declare function durationIR(value: Duration): DurationIR;
|
|
44
|
-
//#endregion
|
|
45
43
|
//# sourceMappingURL=base.d.cts.map
|
package/dist/dsl/base.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.d.cts","names":[],"sources":["../../src/dsl/base.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"base.d.cts","names":[],"sources":["../../src/dsl/base.ts"],"mappings":";;;;;;;;;;;;;;;YAkBY;;;;;;iBAOK;EACf;EACA;;;YAIU;YAYA;;YAGA;;;;;;wBASI,iBAAiB,OAAO;;;;;;wBAWxB,WAAW,OAAO,WAAW"}
|
package/dist/dsl/base.d.mts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { DurationIR } from "../ir.mjs";
|
|
2
|
-
//#region src/dsl/base.d.ts
|
|
3
2
|
/**
|
|
4
3
|
* Base vocabulary shared by the whole DSL: the schema constraints an app's
|
|
5
4
|
* types must satisfy, and the literal types (durations, weekdays, clock times)
|
|
@@ -41,5 +40,4 @@ export declare function timeOfDayMinutes(value: TimeOfDay): number;
|
|
|
41
40
|
* @internal shared by condition/flow/workflow, not part of the public API
|
|
42
41
|
*/
|
|
43
42
|
export declare function durationIR(value: Duration): DurationIR;
|
|
44
|
-
//#endregion
|
|
45
43
|
//# sourceMappingURL=base.d.mts.map
|
package/dist/dsl/base.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.d.mts","names":[],"sources":["../../src/dsl/base.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"base.d.mts","names":[],"sources":["../../src/dsl/base.ts"],"mappings":";;;;;;;;;;;;;;;YAkBY;;;;;;iBAOK;EACf;EACA;;;YAIU;YAYA;;YAGA;;;;;;wBASI,iBAAiB,OAAO;;;;;;wBAWxB,WAAW,OAAO,WAAW"}
|
package/dist/dsl/base.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import ms from "ms";
|
|
2
|
-
|
|
2
|
+
// src/dsl/base.ts
|
|
3
3
|
const TIME_OF_DAY = /^([01]\d|2[0-3]):([0-5]\d)$/;
|
|
4
4
|
/**
|
|
5
5
|
* Parse an 'HH:mm' literal to minutes since midnight, throwing on anything the
|
|
@@ -24,7 +24,6 @@ function durationIR(value) {
|
|
|
24
24
|
ms: millis
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
|
-
//#endregion
|
|
28
27
|
export { durationIR, timeOfDayMinutes };
|
|
29
28
|
|
|
30
29
|
//# sourceMappingURL=base.mjs.map
|
package/dist/dsl/bundle.cjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_templates = require("../templates.cjs");
|
|
3
|
-
const require_dsl_flow = require("./flow.cjs");
|
|
4
2
|
const require_hash = require("../hash.cjs");
|
|
5
3
|
const require_ir = require("../ir.cjs");
|
|
6
|
-
|
|
4
|
+
const require_templates = require("../templates.cjs");
|
|
5
|
+
const require_dsl_flow = require("./flow.cjs");
|
|
6
|
+
// src/dsl/bundle.ts
|
|
7
7
|
/**
|
|
8
8
|
* The deployment unit — compileBundle gathers workflows, segments and the
|
|
9
9
|
* template manifest into one BundleIR snapshot (terraform-apply mental model);
|
|
@@ -49,7 +49,8 @@ function compileBundle(input) {
|
|
|
49
49
|
workflows,
|
|
50
50
|
segments,
|
|
51
51
|
templates,
|
|
52
|
-
actions
|
|
52
|
+
actions,
|
|
53
|
+
subscriptions: { topics: [...input.subscriptions?.topics ?? []] }
|
|
53
54
|
});
|
|
54
55
|
}
|
|
55
56
|
function assertUniqueNames(names, kind) {
|
|
@@ -206,7 +207,6 @@ function assertNodeTrees(nodes, owner) {
|
|
|
206
207
|
case "cohort": for (const arm of node.arms) assertNodeTrees(arm.flow, owner);
|
|
207
208
|
}
|
|
208
209
|
}
|
|
209
|
-
//#endregion
|
|
210
210
|
exports.compileBundle = compileBundle;
|
|
211
211
|
|
|
212
212
|
//# sourceMappingURL=bundle.cjs.map
|
package/dist/dsl/bundle.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bundle.cjs","names":["semanticHash","CHANNEL_OF","BundleIRSchema","actionUses"],"sources":["../../src/dsl/bundle.ts"],"sourcesContent":["import { semanticHash } from '../hash';\nimport {\n type BundleIR,\n BundleIR as BundleIRSchema,\n type ConditionIR,\n IR_VERSION,\n type NodeIR,\n type SegmentIR,\n type WorkflowIR,\n} from '../ir';\nimport { type AnyTemplate, CHANNEL_OF } from '../templates';\nimport type { ActionRef } from './action';\nimport type { SegmentInternal, SegmentRef } from './condition';\nimport { actionUses } from './flow';\nimport type { WorkflowBuilder } from './workflow';\n\n/**\n * The deployment unit — compileBundle gathers workflows, segments and the\n * template manifest into one BundleIR snapshot (terraform-apply mental model);\n * the server diffs each definition by contentHash (see plan.ts).\n */\nexport function compileBundle(input: {\n workflows: readonly WorkflowBuilder[];\n segments?: readonly SegmentRef[];\n /** The template manifest: these definitions, by channel and id. */\n templates?: readonly AnyTemplate[];\n actions?: readonly ActionRef[];\n}): BundleIR {\n const segments = (input.segments ?? []).map((segment) => {\n const { id, definition, loc, meta } = segment as SegmentInternal;\n const compiled: SegmentIR = {\n irVersion: IR_VERSION,\n id,\n // Labels and provenance both live in meta, which stripMeta drops before hashing\n contentHash: semanticHash(definition),\n condition: definition,\n };\n const merged = {\n ...(meta?.name !== undefined ? { name: meta.name } : {}),\n ...(meta?.description !== undefined ? { description: meta.description } : {}),\n ...(loc !== undefined ? { loc } : {}),\n };\n if (Object.keys(merged).length > 0) compiled.meta = merged;\n return compiled;\n });\n\n const templates = (input.templates ?? []).map((template) => ({\n irVersion: IR_VERSION,\n key: template.id,\n channel: CHANNEL_OF[template.kind],\n }));\n\n // Manifest only — names; the implementation ships in the Worker bundle, never in IR\n const actions = (input.actions ?? []).map((a) => ({ irVersion: IR_VERSION, name: a.name }));\n\n const workflows = input.workflows.map((builder) => builder.toIR());\n\n // Names are wire identity (routing tables, entry ledger, by-name refs):\n // duplicates would silently last-write-win on deploy, so fail the compile.\n assertUniqueNames(\n workflows.map((w) => w.id),\n 'workflow'\n );\n assertUniqueNames(\n segments.map((s) => s.id),\n 'segment'\n );\n // A message node names its template by channel and key, so the key is unique per channel:\n // an email and a push of one nudge may share it\n assertUniqueNames(\n templates.map((t) => `${t.channel}:${t.key}`),\n 'template'\n );\n assertUniqueNames(\n actions.map((a) => a.name),\n 'action'\n );\n\n // Deploy replaces the whole segment table, so every by-name reference must\n // resolve inside this bundle — catching it here turns a runtime \"Unknown\n // segment\" in some user's journey into a compile error.\n assertSegmentRefsResolve(workflows, segments);\n\n // A segment is evaluated by evaluating the segments it names; a cycle among\n // them has no evaluation order and would recurse until the stack ran out —\n // at run time, in some user's journey. Caught here, with the cycle spelled out.\n assertNoSegmentCycles(segments);\n\n // Payload predicates and profile predicates share the Condition type, so\n // placement is checked here: payload only inside where-trees, and where-trees\n // contain nothing but payload predicates and combinators.\n assertConditionPlacement(workflows, segments);\n\n // Same rationale as segments: the runtime registry is looked up by name, so\n // an action a workflow runs must be in the bundle — and it must be the very\n // definition listed there, or two same-named definitions are competing for\n // one identity (the IR names it; only the compile can tell them apart).\n assertActionRefsResolve(workflows, input.workflows, input.actions ?? []);\n\n return BundleIRSchema.parse({ irVersion: IR_VERSION, workflows, segments, templates, actions });\n}\n\nfunction assertUniqueNames(names: readonly string[], kind: string): void {\n const seen = new Set<string>();\n for (const name of names) {\n if (seen.has(name)) throw new Error(`compileBundle: duplicate ${kind} name '${name}'`);\n seen.add(name);\n }\n}\n\nfunction assertSegmentRefsResolve(\n workflows: readonly WorkflowIR[],\n segments: readonly SegmentIR[]\n): void {\n const defined = new Set(segments.map((s) => s.id));\n const check = (refs: ReadonlySet<string>, owner: string): void => {\n for (const ref of refs) {\n if (!defined.has(ref)) {\n throw new Error(\n `compileBundle: ${owner} references segment '${ref}' that is not in the bundle — pass it in 'segments'`\n );\n }\n }\n };\n for (const workflow of workflows) {\n check(referencedSegments(workflow), `workflow '${workflow.id}'`);\n }\n // Segments may reference other segments; those must resolve too\n for (const segment of segments) {\n const refs = new Set<string>();\n collectConditionRefs(segment.condition, refs);\n check(refs, `segment '${segment.id}'`);\n }\n}\n\nfunction referencedSegments(workflow: WorkflowIR): Set<string> {\n const refs = new Set<string>();\n if (workflow.trigger.type === 'segment') refs.add(workflow.trigger.segment);\n if (workflow.trigger.type === 'event' && workflow.trigger.filter !== undefined) {\n collectConditionRefs(workflow.trigger.filter, refs);\n }\n if (workflow.goal !== undefined) collectConditionRefs(workflow.goal.condition, refs);\n if (workflow.exitWhen !== undefined) collectConditionRefs(workflow.exitWhen, refs);\n collectNodeRefs(workflow.flow, refs);\n return refs;\n}\n\nfunction collectConditionRefs(condition: ConditionIR, into: Set<string>): void {\n switch (condition.type) {\n case 'and':\n case 'or':\n for (const child of condition.conditions) collectConditionRefs(child, into);\n break;\n case 'not':\n collectConditionRefs(condition.condition, into);\n break;\n case 'segment':\n into.add(condition.segment);\n break;\n default:\n break;\n }\n}\n\nfunction collectNodeRefs(nodes: readonly NodeIR[], into: Set<string>): void {\n for (const node of nodes) {\n switch (node.type) {\n case 'wait_until':\n collectConditionRefs(node.condition, into);\n if (Array.isArray(node.onTimeout)) collectNodeRefs(node.onTimeout, into);\n break;\n case 'branch':\n for (const branchCase of node.cases) {\n collectConditionRefs(branchCase.condition, into);\n collectNodeRefs(branchCase.flow, into);\n }\n if (node.otherwise) collectNodeRefs(node.otherwise, into);\n break;\n case 'filter':\n collectConditionRefs(node.condition, into);\n break;\n case 'cohort':\n for (const arm of node.arms) collectNodeRefs(arm.flow, into);\n break;\n default:\n break;\n }\n }\n}\n\nfunction assertActionRefsResolve(\n workflows: readonly WorkflowIR[],\n builders: readonly WorkflowBuilder[],\n actions: readonly ActionRef[]\n): void {\n const listed = new Map(actions.map((a) => [a.name, a]));\n workflows.forEach((workflow, i) => {\n for (const used of actionUses(builders[i])) {\n const manifest = listed.get(used.name);\n if (manifest === undefined) {\n throw new Error(\n `compileBundle: workflow '${workflow.id}' runs action '${used.name}' that is not in the bundle — pass it in 'actions'`\n );\n }\n // Identity, not source text: the printed handler depends on the bundler.\n // Every use is checked, not one per name — with two definitions under one\n // name, whichever is listed, the *other* node would run the wrong code.\n if (manifest !== used && manifest.handler !== used.handler) {\n throw new Error(\n `compileBundle: workflow '${workflow.id}' runs action '${used.name}' that is not the definition listed in 'actions' — two definitions are sharing that name`\n );\n }\n }\n });\n}\n\n/** Walk segment → segment references depth-first; the first back edge is reported as the cycle path. */\nfunction assertNoSegmentCycles(segments: readonly SegmentIR[]): void {\n const refsOf = new Map<string, Set<string>>();\n for (const segment of segments) {\n const refs = new Set<string>();\n collectConditionRefs(segment.condition, refs);\n refsOf.set(segment.id, refs);\n }\n const done = new Set<string>();\n const path: string[] = [];\n const visit = (id: string): void => {\n const at = path.indexOf(id);\n if (at !== -1) {\n const cycle = [...path.slice(at), id].join(' → ');\n throw new Error(`compileBundle: segments reference each other in a cycle: ${cycle}`);\n }\n if (done.has(id)) return;\n path.push(id);\n for (const ref of refsOf.get(id) ?? []) visit(ref);\n path.pop();\n done.add(id);\n };\n for (const id of refsOf.keys()) visit(id);\n}\n\n/* --------------------- payload / profile placement checks --------------------- */\n\nfunction assertConditionPlacement(\n workflows: readonly WorkflowIR[],\n segments: readonly SegmentIR[]\n): void {\n for (const workflow of workflows) {\n if (workflow.trigger.type === 'event') {\n if (workflow.trigger.where !== undefined) {\n assertWhereTree(workflow.trigger.where, `workflow '${workflow.id}' trigger where`);\n }\n if (workflow.trigger.filter !== undefined) {\n assertProfileTree(workflow.trigger.filter, `workflow '${workflow.id}' trigger filter`);\n }\n }\n if (workflow.goal !== undefined) {\n assertProfileTree(workflow.goal.condition, `workflow '${workflow.id}' goal`);\n }\n if (workflow.exitWhen !== undefined) {\n assertProfileTree(workflow.exitWhen, `workflow '${workflow.id}' exitWhen`);\n }\n assertNodeTrees(workflow.flow, `workflow '${workflow.id}'`);\n }\n for (const segment of segments) {\n assertProfileTree(segment.condition, `segment '${segment.id}'`);\n }\n}\n\n/** A profile-context tree: payload predicates may appear only under a performed.where. */\nfunction assertProfileTree(condition: ConditionIR, owner: string): void {\n switch (condition.type) {\n case 'and':\n case 'or':\n for (const child of condition.conditions) assertProfileTree(child, owner);\n break;\n case 'not':\n assertProfileTree(condition.condition, owner);\n break;\n case 'performed':\n if (condition.where !== undefined) assertWhereTree(condition.where, owner);\n break;\n case 'payload':\n throw new Error(\n `compileBundle: ${owner} uses a payload predicate outside a where clause — payload refs are only valid inside performed({ where }) or trigger.event({ where })`\n );\n default:\n break; // property / segment\n }\n}\n\n/** A where tree: payload predicates and combinators only — no profile facts in event scope. */\nfunction assertWhereTree(condition: ConditionIR, owner: string): void {\n switch (condition.type) {\n case 'and':\n case 'or':\n for (const child of condition.conditions) assertWhereTree(child, owner);\n break;\n case 'not':\n assertWhereTree(condition.condition, owner);\n break;\n case 'payload':\n break;\n default:\n throw new Error(\n `compileBundle: ${owner} where clause may only contain payload predicates and and/or/not, found '${condition.type}'`\n );\n }\n}\n\nfunction assertNodeTrees(nodes: readonly NodeIR[], owner: string): void {\n for (const node of nodes) {\n switch (node.type) {\n case 'wait_until':\n assertProfileTree(node.condition, owner);\n if (Array.isArray(node.onTimeout)) assertNodeTrees(node.onTimeout, owner);\n break;\n case 'branch':\n for (const branchCase of node.cases) {\n assertProfileTree(branchCase.condition, owner);\n assertNodeTrees(branchCase.flow, owner);\n }\n if (node.otherwise) assertNodeTrees(node.otherwise, owner);\n break;\n case 'filter':\n assertProfileTree(node.condition, owner);\n break;\n case 'cohort':\n for (const arm of node.arms) assertNodeTrees(arm.flow, owner);\n break;\n default:\n break;\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"bundle.cjs","names":["semanticHash","CHANNEL_OF","BundleIRSchema","actionUses"],"sources":["../../src/dsl/bundle.ts"],"sourcesContent":["import { semanticHash } from '../hash';\nimport {\n type BundleIR,\n BundleIR as BundleIRSchema,\n type ConditionIR,\n IR_VERSION,\n type NodeIR,\n type SegmentIR,\n type WorkflowIR,\n} from '../ir';\nimport { type AnyTemplate, CHANNEL_OF } from '../templates';\nimport type { ActionRef } from './action';\nimport type { SegmentInternal, SegmentRef } from './condition';\nimport { actionUses } from './flow';\nimport type { WorkflowBuilder } from './workflow';\n\n/**\n * The deployment unit — compileBundle gathers workflows, segments and the\n * template manifest into one BundleIR snapshot (terraform-apply mental model);\n * the server diffs each definition by contentHash (see plan.ts).\n */\nexport function compileBundle(input: {\n workflows: readonly WorkflowBuilder[];\n segments?: readonly SegmentRef[];\n /** The template manifest: these definitions, by channel and id. */\n templates?: readonly AnyTemplate[];\n actions?: readonly ActionRef[];\n /** The contract's subscription topics, carried for the engine (`compileConfig` passes them). */\n subscriptions?: { topics: readonly string[] };\n}): BundleIR {\n const segments = (input.segments ?? []).map((segment) => {\n const { id, definition, loc, meta } = segment as SegmentInternal;\n const compiled: SegmentIR = {\n irVersion: IR_VERSION,\n id,\n // Labels and provenance both live in meta, which stripMeta drops before hashing\n contentHash: semanticHash(definition),\n condition: definition,\n };\n const merged = {\n ...(meta?.name !== undefined ? { name: meta.name } : {}),\n ...(meta?.description !== undefined ? { description: meta.description } : {}),\n ...(loc !== undefined ? { loc } : {}),\n };\n if (Object.keys(merged).length > 0) compiled.meta = merged;\n return compiled;\n });\n\n const templates = (input.templates ?? []).map((template) => ({\n irVersion: IR_VERSION,\n key: template.id,\n channel: CHANNEL_OF[template.kind],\n }));\n\n // Manifest only — names; the implementation ships in the Worker bundle, never in IR\n const actions = (input.actions ?? []).map((a) => ({ irVersion: IR_VERSION, name: a.name }));\n\n const workflows = input.workflows.map((builder) => builder.toIR());\n\n // Names are wire identity (routing tables, entry ledger, by-name refs):\n // duplicates would silently last-write-win on deploy, so fail the compile.\n assertUniqueNames(\n workflows.map((w) => w.id),\n 'workflow'\n );\n assertUniqueNames(\n segments.map((s) => s.id),\n 'segment'\n );\n // A message node names its template by channel and key, so the key is unique per channel:\n // an email and a push of one nudge may share it\n assertUniqueNames(\n templates.map((t) => `${t.channel}:${t.key}`),\n 'template'\n );\n assertUniqueNames(\n actions.map((a) => a.name),\n 'action'\n );\n\n // Deploy replaces the whole segment table, so every by-name reference must\n // resolve inside this bundle — catching it here turns a runtime \"Unknown\n // segment\" in some user's journey into a compile error.\n assertSegmentRefsResolve(workflows, segments);\n\n // A segment is evaluated by evaluating the segments it names; a cycle among\n // them has no evaluation order and would recurse until the stack ran out —\n // at run time, in some user's journey. Caught here, with the cycle spelled out.\n assertNoSegmentCycles(segments);\n\n // Payload predicates and profile predicates share the Condition type, so\n // placement is checked here: payload only inside where-trees, and where-trees\n // contain nothing but payload predicates and combinators.\n assertConditionPlacement(workflows, segments);\n\n // Same rationale as segments: the runtime registry is looked up by name, so\n // an action a workflow runs must be in the bundle — and it must be the very\n // definition listed there, or two same-named definitions are competing for\n // one identity (the IR names it; only the compile can tell them apart).\n assertActionRefsResolve(workflows, input.workflows, input.actions ?? []);\n\n return BundleIRSchema.parse({\n irVersion: IR_VERSION,\n workflows,\n segments,\n templates,\n actions,\n subscriptions: { topics: [...(input.subscriptions?.topics ?? [])] },\n });\n}\n\nfunction assertUniqueNames(names: readonly string[], kind: string): void {\n const seen = new Set<string>();\n for (const name of names) {\n if (seen.has(name)) throw new Error(`compileBundle: duplicate ${kind} name '${name}'`);\n seen.add(name);\n }\n}\n\nfunction assertSegmentRefsResolve(\n workflows: readonly WorkflowIR[],\n segments: readonly SegmentIR[]\n): void {\n const defined = new Set(segments.map((s) => s.id));\n const check = (refs: ReadonlySet<string>, owner: string): void => {\n for (const ref of refs) {\n if (!defined.has(ref)) {\n throw new Error(\n `compileBundle: ${owner} references segment '${ref}' that is not in the bundle — pass it in 'segments'`\n );\n }\n }\n };\n for (const workflow of workflows) {\n check(referencedSegments(workflow), `workflow '${workflow.id}'`);\n }\n // Segments may reference other segments; those must resolve too\n for (const segment of segments) {\n const refs = new Set<string>();\n collectConditionRefs(segment.condition, refs);\n check(refs, `segment '${segment.id}'`);\n }\n}\n\nfunction referencedSegments(workflow: WorkflowIR): Set<string> {\n const refs = new Set<string>();\n if (workflow.trigger.type === 'segment') refs.add(workflow.trigger.segment);\n if (workflow.trigger.type === 'event' && workflow.trigger.filter !== undefined) {\n collectConditionRefs(workflow.trigger.filter, refs);\n }\n if (workflow.goal !== undefined) collectConditionRefs(workflow.goal.condition, refs);\n if (workflow.exitWhen !== undefined) collectConditionRefs(workflow.exitWhen, refs);\n collectNodeRefs(workflow.flow, refs);\n return refs;\n}\n\nfunction collectConditionRefs(condition: ConditionIR, into: Set<string>): void {\n switch (condition.type) {\n case 'and':\n case 'or':\n for (const child of condition.conditions) collectConditionRefs(child, into);\n break;\n case 'not':\n collectConditionRefs(condition.condition, into);\n break;\n case 'segment':\n into.add(condition.segment);\n break;\n default:\n break;\n }\n}\n\nfunction collectNodeRefs(nodes: readonly NodeIR[], into: Set<string>): void {\n for (const node of nodes) {\n switch (node.type) {\n case 'wait_until':\n collectConditionRefs(node.condition, into);\n if (Array.isArray(node.onTimeout)) collectNodeRefs(node.onTimeout, into);\n break;\n case 'branch':\n for (const branchCase of node.cases) {\n collectConditionRefs(branchCase.condition, into);\n collectNodeRefs(branchCase.flow, into);\n }\n if (node.otherwise) collectNodeRefs(node.otherwise, into);\n break;\n case 'filter':\n collectConditionRefs(node.condition, into);\n break;\n case 'cohort':\n for (const arm of node.arms) collectNodeRefs(arm.flow, into);\n break;\n default:\n break;\n }\n }\n}\n\nfunction assertActionRefsResolve(\n workflows: readonly WorkflowIR[],\n builders: readonly WorkflowBuilder[],\n actions: readonly ActionRef[]\n): void {\n const listed = new Map(actions.map((a) => [a.name, a]));\n workflows.forEach((workflow, i) => {\n for (const used of actionUses(builders[i])) {\n const manifest = listed.get(used.name);\n if (manifest === undefined) {\n throw new Error(\n `compileBundle: workflow '${workflow.id}' runs action '${used.name}' that is not in the bundle — pass it in 'actions'`\n );\n }\n // Identity, not source text: the printed handler depends on the bundler.\n // Every use is checked, not one per name — with two definitions under one\n // name, whichever is listed, the *other* node would run the wrong code.\n if (manifest !== used && manifest.handler !== used.handler) {\n throw new Error(\n `compileBundle: workflow '${workflow.id}' runs action '${used.name}' that is not the definition listed in 'actions' — two definitions are sharing that name`\n );\n }\n }\n });\n}\n\n/** Walk segment → segment references depth-first; the first back edge is reported as the cycle path. */\nfunction assertNoSegmentCycles(segments: readonly SegmentIR[]): void {\n const refsOf = new Map<string, Set<string>>();\n for (const segment of segments) {\n const refs = new Set<string>();\n collectConditionRefs(segment.condition, refs);\n refsOf.set(segment.id, refs);\n }\n const done = new Set<string>();\n const path: string[] = [];\n const visit = (id: string): void => {\n const at = path.indexOf(id);\n if (at !== -1) {\n const cycle = [...path.slice(at), id].join(' → ');\n throw new Error(`compileBundle: segments reference each other in a cycle: ${cycle}`);\n }\n if (done.has(id)) return;\n path.push(id);\n for (const ref of refsOf.get(id) ?? []) visit(ref);\n path.pop();\n done.add(id);\n };\n for (const id of refsOf.keys()) visit(id);\n}\n\n/* --------------------- payload / profile placement checks --------------------- */\n\nfunction assertConditionPlacement(\n workflows: readonly WorkflowIR[],\n segments: readonly SegmentIR[]\n): void {\n for (const workflow of workflows) {\n if (workflow.trigger.type === 'event') {\n if (workflow.trigger.where !== undefined) {\n assertWhereTree(workflow.trigger.where, `workflow '${workflow.id}' trigger where`);\n }\n if (workflow.trigger.filter !== undefined) {\n assertProfileTree(workflow.trigger.filter, `workflow '${workflow.id}' trigger filter`);\n }\n }\n if (workflow.goal !== undefined) {\n assertProfileTree(workflow.goal.condition, `workflow '${workflow.id}' goal`);\n }\n if (workflow.exitWhen !== undefined) {\n assertProfileTree(workflow.exitWhen, `workflow '${workflow.id}' exitWhen`);\n }\n assertNodeTrees(workflow.flow, `workflow '${workflow.id}'`);\n }\n for (const segment of segments) {\n assertProfileTree(segment.condition, `segment '${segment.id}'`);\n }\n}\n\n/** A profile-context tree: payload predicates may appear only under a performed.where. */\nfunction assertProfileTree(condition: ConditionIR, owner: string): void {\n switch (condition.type) {\n case 'and':\n case 'or':\n for (const child of condition.conditions) assertProfileTree(child, owner);\n break;\n case 'not':\n assertProfileTree(condition.condition, owner);\n break;\n case 'performed':\n if (condition.where !== undefined) assertWhereTree(condition.where, owner);\n break;\n case 'payload':\n throw new Error(\n `compileBundle: ${owner} uses a payload predicate outside a where clause — payload refs are only valid inside performed({ where }) or trigger.event({ where })`\n );\n default:\n break; // property / segment\n }\n}\n\n/** A where tree: payload predicates and combinators only — no profile facts in event scope. */\nfunction assertWhereTree(condition: ConditionIR, owner: string): void {\n switch (condition.type) {\n case 'and':\n case 'or':\n for (const child of condition.conditions) assertWhereTree(child, owner);\n break;\n case 'not':\n assertWhereTree(condition.condition, owner);\n break;\n case 'payload':\n break;\n default:\n throw new Error(\n `compileBundle: ${owner} where clause may only contain payload predicates and and/or/not, found '${condition.type}'`\n );\n }\n}\n\nfunction assertNodeTrees(nodes: readonly NodeIR[], owner: string): void {\n for (const node of nodes) {\n switch (node.type) {\n case 'wait_until':\n assertProfileTree(node.condition, owner);\n if (Array.isArray(node.onTimeout)) assertNodeTrees(node.onTimeout, owner);\n break;\n case 'branch':\n for (const branchCase of node.cases) {\n assertProfileTree(branchCase.condition, owner);\n assertNodeTrees(branchCase.flow, owner);\n }\n if (node.otherwise) assertNodeTrees(node.otherwise, owner);\n break;\n case 'filter':\n assertProfileTree(node.condition, owner);\n break;\n case 'cohort':\n for (const arm of node.arms) assertNodeTrees(arm.flow, owner);\n break;\n default:\n break;\n }\n }\n}\n"],"mappings":"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"}
|
package/dist/dsl/bundle.d.cts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { BundleIR } from "../ir.cjs";
|
|
2
|
-
import { AnyTemplate } from "../templates.cjs";
|
|
3
2
|
import { ActionRef } from "./action.cjs";
|
|
4
3
|
import { SegmentRef } from "./condition.cjs";
|
|
5
4
|
import { WorkflowBuilder } from "./workflow.cjs";
|
|
6
|
-
|
|
5
|
+
import { AnyTemplate } from "../templates.cjs";
|
|
7
6
|
/**
|
|
8
7
|
* The deployment unit — compileBundle gathers workflows, segments and the
|
|
9
8
|
* template manifest into one BundleIR snapshot (terraform-apply mental model);
|
|
@@ -15,6 +14,9 @@ export declare function compileBundle(input: {
|
|
|
15
14
|
/** The template manifest: these definitions, by channel and id. */
|
|
16
15
|
templates?: readonly AnyTemplate[];
|
|
17
16
|
actions?: readonly ActionRef[];
|
|
17
|
+
/** The contract's subscription topics, carried for the engine (`compileConfig` passes them). */
|
|
18
|
+
subscriptions?: {
|
|
19
|
+
topics: readonly string[];
|
|
20
|
+
};
|
|
18
21
|
}): BundleIR;
|
|
19
|
-
//#endregion
|
|
20
22
|
//# sourceMappingURL=bundle.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bundle.d.cts","names":[],"sources":["../../src/dsl/bundle.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"bundle.d.cts","names":[],"sources":["../../src/dsl/bundle.ts"],"mappings":";;;;;;;;;;wBAqBgB,cAAc;EAC5B,oBAAoB;EACpB,oBAAoB;;EAEpB,qBAAqB;EACrB,mBAAmB;;EAEnB;IAAkB;;IAChB"}
|
package/dist/dsl/bundle.d.mts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { BundleIR } from "../ir.mjs";
|
|
2
|
-
import { AnyTemplate } from "../templates.mjs";
|
|
3
2
|
import { ActionRef } from "./action.mjs";
|
|
4
3
|
import { SegmentRef } from "./condition.mjs";
|
|
5
4
|
import { WorkflowBuilder } from "./workflow.mjs";
|
|
6
|
-
|
|
5
|
+
import { AnyTemplate } from "../templates.mjs";
|
|
7
6
|
/**
|
|
8
7
|
* The deployment unit — compileBundle gathers workflows, segments and the
|
|
9
8
|
* template manifest into one BundleIR snapshot (terraform-apply mental model);
|
|
@@ -15,6 +14,9 @@ export declare function compileBundle(input: {
|
|
|
15
14
|
/** The template manifest: these definitions, by channel and id. */
|
|
16
15
|
templates?: readonly AnyTemplate[];
|
|
17
16
|
actions?: readonly ActionRef[];
|
|
17
|
+
/** The contract's subscription topics, carried for the engine (`compileConfig` passes them). */
|
|
18
|
+
subscriptions?: {
|
|
19
|
+
topics: readonly string[];
|
|
20
|
+
};
|
|
18
21
|
}): BundleIR;
|
|
19
|
-
//#endregion
|
|
20
22
|
//# sourceMappingURL=bundle.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bundle.d.mts","names":[],"sources":["../../src/dsl/bundle.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"bundle.d.mts","names":[],"sources":["../../src/dsl/bundle.ts"],"mappings":";;;;;;;;;;wBAqBgB,cAAc;EAC5B,oBAAoB;EACpB,oBAAoB;;EAEpB,qBAAqB;EACrB,mBAAmB;;EAEnB;IAAkB;;IAChB"}
|
package/dist/dsl/bundle.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { CHANNEL_OF } from "../templates.mjs";
|
|
2
|
-
import { actionUses } from "./flow.mjs";
|
|
3
1
|
import { semanticHash } from "../hash.mjs";
|
|
4
2
|
import { BundleIR } from "../ir.mjs";
|
|
5
|
-
|
|
3
|
+
import { CHANNEL_OF } from "../templates.mjs";
|
|
4
|
+
import { actionUses } from "./flow.mjs";
|
|
5
|
+
// src/dsl/bundle.ts
|
|
6
6
|
/**
|
|
7
7
|
* The deployment unit — compileBundle gathers workflows, segments and the
|
|
8
8
|
* template manifest into one BundleIR snapshot (terraform-apply mental model);
|
|
@@ -48,7 +48,8 @@ function compileBundle(input) {
|
|
|
48
48
|
workflows,
|
|
49
49
|
segments,
|
|
50
50
|
templates,
|
|
51
|
-
actions
|
|
51
|
+
actions,
|
|
52
|
+
subscriptions: { topics: [...input.subscriptions?.topics ?? []] }
|
|
52
53
|
});
|
|
53
54
|
}
|
|
54
55
|
function assertUniqueNames(names, kind) {
|
|
@@ -205,7 +206,6 @@ function assertNodeTrees(nodes, owner) {
|
|
|
205
206
|
case "cohort": for (const arm of node.arms) assertNodeTrees(arm.flow, owner);
|
|
206
207
|
}
|
|
207
208
|
}
|
|
208
|
-
//#endregion
|
|
209
209
|
export { compileBundle };
|
|
210
210
|
|
|
211
211
|
//# sourceMappingURL=bundle.mjs.map
|