@shware/workflow 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +154 -2
- package/dist/_virtual/_rolldown/runtime.cjs +23 -0
- package/dist/apns-relay.cjs +105 -0
- package/dist/apns-relay.cjs.map +1 -0
- package/dist/apns-relay.d.cts +28 -0
- package/dist/apns-relay.d.cts.map +1 -0
- package/dist/apns-relay.d.mts +28 -0
- package/dist/apns-relay.d.mts.map +1 -0
- package/dist/apns-relay.mjs +104 -0
- package/dist/apns-relay.mjs.map +1 -0
- package/dist/apns.cjs +121 -0
- package/dist/apns.cjs.map +1 -0
- package/dist/apns.d.cts +63 -0
- package/dist/apns.d.cts.map +1 -0
- package/dist/apns.d.mts +63 -0
- package/dist/apns.d.mts.map +1 -0
- package/dist/apns.mjs +116 -0
- package/dist/apns.mjs.map +1 -0
- package/dist/client.cjs +125 -0
- package/dist/client.cjs.map +1 -0
- package/dist/client.d.cts +108 -0
- package/dist/client.d.cts.map +1 -0
- package/dist/client.d.mts +108 -0
- package/dist/client.d.mts.map +1 -0
- package/dist/client.mjs +122 -0
- package/dist/client.mjs.map +1 -0
- package/dist/cloudflare/bindings.cjs +28 -0
- package/dist/cloudflare/bindings.cjs.map +1 -0
- package/dist/cloudflare/bindings.d.cts +144 -0
- package/dist/cloudflare/bindings.d.cts.map +1 -0
- package/dist/cloudflare/bindings.d.mts +144 -0
- package/dist/cloudflare/bindings.d.mts.map +1 -0
- package/dist/cloudflare/bindings.mjs +25 -0
- package/dist/cloudflare/bindings.mjs.map +1 -0
- package/dist/cloudflare/channels.cjs +164 -0
- package/dist/cloudflare/channels.cjs.map +1 -0
- package/dist/cloudflare/channels.d.cts +29 -0
- package/dist/cloudflare/channels.d.cts.map +1 -0
- package/dist/cloudflare/channels.d.mts +29 -0
- package/dist/cloudflare/channels.d.mts.map +1 -0
- package/dist/cloudflare/channels.mjs +161 -0
- package/dist/cloudflare/channels.mjs.map +1 -0
- package/dist/cloudflare/d1-env.cjs +17 -0
- package/dist/cloudflare/d1-env.cjs.map +1 -0
- package/dist/cloudflare/d1-env.d.cts +12 -0
- package/dist/cloudflare/d1-env.d.cts.map +1 -0
- package/dist/cloudflare/d1-env.d.mts +12 -0
- package/dist/cloudflare/d1-env.d.mts.map +1 -0
- package/dist/cloudflare/d1-env.mjs +16 -0
- package/dist/cloudflare/d1-env.mjs.map +1 -0
- package/dist/cloudflare/index.cjs +54 -0
- package/dist/cloudflare/index.d.cts +19 -0
- package/dist/cloudflare/index.d.mts +19 -0
- package/dist/cloudflare/index.mjs +16 -0
- package/dist/cloudflare/router.cjs +516 -0
- package/dist/cloudflare/router.cjs.map +1 -0
- package/dist/cloudflare/router.d.cts +157 -0
- package/dist/cloudflare/router.d.cts.map +1 -0
- package/dist/cloudflare/router.d.mts +157 -0
- package/dist/cloudflare/router.d.mts.map +1 -0
- package/dist/cloudflare/router.mjs +503 -0
- package/dist/cloudflare/router.mjs.map +1 -0
- package/dist/cloudflare/runner.cjs +225 -0
- package/dist/cloudflare/runner.cjs.map +1 -0
- package/dist/cloudflare/runner.d.cts +73 -0
- package/dist/cloudflare/runner.d.cts.map +1 -0
- package/dist/cloudflare/runner.d.mts +73 -0
- package/dist/cloudflare/runner.d.mts.map +1 -0
- package/dist/cloudflare/runner.mjs +224 -0
- package/dist/cloudflare/runner.mjs.map +1 -0
- package/dist/cloudflare/schema.sql +127 -0
- package/dist/cloudflare/senders.cjs +114 -0
- package/dist/cloudflare/senders.cjs.map +1 -0
- package/dist/cloudflare/senders.d.cts +68 -0
- package/dist/cloudflare/senders.d.cts.map +1 -0
- package/dist/cloudflare/senders.d.mts +68 -0
- package/dist/cloudflare/senders.d.mts.map +1 -0
- package/dist/cloudflare/senders.mjs +108 -0
- package/dist/cloudflare/senders.mjs.map +1 -0
- package/dist/cloudflare/worker.cjs +228 -0
- package/dist/cloudflare/worker.cjs.map +1 -0
- package/dist/cloudflare/worker.d.cts +92 -0
- package/dist/cloudflare/worker.d.cts.map +1 -0
- package/dist/cloudflare/worker.d.mts +92 -0
- package/dist/cloudflare/worker.d.mts.map +1 -0
- package/dist/cloudflare/worker.mjs +224 -0
- package/dist/cloudflare/worker.mjs.map +1 -0
- package/dist/cloudflare/workers.d.cjs +0 -0
- package/dist/cloudflare/workers.d.cts +44 -0
- package/dist/cloudflare/workers.d.cts.map +1 -0
- package/dist/cloudflare/workers.d.mts +44 -0
- package/dist/cloudflare/workers.d.mts.map +1 -0
- package/dist/config.cjs +151 -0
- package/dist/config.cjs.map +1 -0
- package/dist/config.d.cts +149 -0
- package/dist/config.d.cts.map +1 -0
- package/dist/config.d.mts +149 -0
- package/dist/config.d.mts.map +1 -0
- package/dist/config.mjs +143 -0
- package/dist/config.mjs.map +1 -0
- package/dist/contract.cjs +212 -0
- package/dist/contract.cjs.map +1 -0
- package/dist/contract.d.cts +90 -0
- package/dist/contract.d.cts.map +1 -0
- package/dist/contract.d.mts +90 -0
- package/dist/contract.d.mts.map +1 -0
- package/dist/contract.mjs +206 -0
- package/dist/contract.mjs.map +1 -0
- package/dist/dsl/action.cjs +29 -0
- package/dist/dsl/action.cjs.map +1 -0
- package/dist/dsl/action.d.cts +64 -0
- package/dist/dsl/action.d.cts.map +1 -0
- package/dist/dsl/action.d.mts +64 -0
- package/dist/dsl/action.d.mts.map +1 -0
- package/dist/dsl/action.mjs +28 -0
- package/dist/dsl/action.mjs.map +1 -0
- package/dist/dsl/base.cjs +34 -0
- package/dist/dsl/base.cjs.map +1 -0
- package/dist/dsl/base.d.cts +45 -0
- package/dist/dsl/base.d.cts.map +1 -0
- package/dist/dsl/base.d.mts +45 -0
- package/dist/dsl/base.d.mts.map +1 -0
- package/dist/dsl/base.mjs +30 -0
- package/dist/dsl/base.mjs.map +1 -0
- package/dist/dsl/bundle.cjs +207 -0
- package/dist/dsl/bundle.cjs.map +1 -0
- package/dist/dsl/bundle.d.cts +19 -0
- package/dist/dsl/bundle.d.cts.map +1 -0
- package/dist/dsl/bundle.d.mts +19 -0
- package/dist/dsl/bundle.d.mts.map +1 -0
- package/dist/dsl/bundle.mjs +206 -0
- package/dist/dsl/bundle.mjs.map +1 -0
- package/dist/dsl/condition.cjs +177 -0
- package/dist/dsl/condition.cjs.map +1 -0
- package/dist/dsl/condition.d.cts +140 -0
- package/dist/dsl/condition.d.cts.map +1 -0
- package/dist/dsl/condition.d.mts +140 -0
- package/dist/dsl/condition.d.mts.map +1 -0
- package/dist/dsl/condition.mjs +156 -0
- package/dist/dsl/condition.mjs.map +1 -0
- package/dist/dsl/flow.cjs +228 -0
- package/dist/dsl/flow.cjs.map +1 -0
- package/dist/dsl/flow.d.cts +223 -0
- package/dist/dsl/flow.d.cts.map +1 -0
- package/dist/dsl/flow.d.mts +223 -0
- package/dist/dsl/flow.d.mts.map +1 -0
- package/dist/dsl/flow.mjs +226 -0
- package/dist/dsl/flow.mjs.map +1 -0
- package/dist/dsl/index.cjs +38 -0
- package/dist/dsl/index.d.cts +10 -0
- package/dist/dsl/index.d.mts +10 -0
- package/dist/dsl/index.mjs +9 -0
- package/dist/dsl/refs.cjs +59 -0
- package/dist/dsl/refs.cjs.map +1 -0
- package/dist/dsl/refs.d.cts +88 -0
- package/dist/dsl/refs.d.cts.map +1 -0
- package/dist/dsl/refs.d.mts +88 -0
- package/dist/dsl/refs.d.mts.map +1 -0
- package/dist/dsl/refs.mjs +55 -0
- package/dist/dsl/refs.mjs.map +1 -0
- package/dist/dsl/template.cjs +65 -0
- package/dist/dsl/template.cjs.map +1 -0
- package/dist/dsl/template.d.cts +75 -0
- package/dist/dsl/template.d.cts.map +1 -0
- package/dist/dsl/template.d.mts +75 -0
- package/dist/dsl/template.d.mts.map +1 -0
- package/dist/dsl/template.mjs +63 -0
- package/dist/dsl/template.mjs.map +1 -0
- package/dist/dsl/trigger.cjs +40 -0
- package/dist/dsl/trigger.cjs.map +1 -0
- package/dist/dsl/trigger.d.cts +65 -0
- package/dist/dsl/trigger.d.cts.map +1 -0
- package/dist/dsl/trigger.d.mts +65 -0
- package/dist/dsl/trigger.d.mts.map +1 -0
- package/dist/dsl/trigger.mjs +39 -0
- package/dist/dsl/trigger.mjs.map +1 -0
- package/dist/dsl/workflow.cjs +121 -0
- package/dist/dsl/workflow.cjs.map +1 -0
- package/dist/dsl/workflow.d.cts +59 -0
- package/dist/dsl/workflow.d.cts.map +1 -0
- package/dist/dsl/workflow.d.mts +59 -0
- package/dist/dsl/workflow.d.mts.map +1 -0
- package/dist/dsl/workflow.mjs +120 -0
- package/dist/dsl/workflow.mjs.map +1 -0
- package/dist/engine/actions.cjs +27 -0
- package/dist/engine/actions.cjs.map +1 -0
- package/dist/engine/actions.d.cts +40 -0
- package/dist/engine/actions.d.cts.map +1 -0
- package/dist/engine/actions.d.mts +40 -0
- package/dist/engine/actions.d.mts.map +1 -0
- package/dist/engine/actions.mjs +26 -0
- package/dist/engine/actions.mjs.map +1 -0
- package/dist/engine/bucket.cjs +72 -0
- package/dist/engine/bucket.cjs.map +1 -0
- package/dist/engine/bucket.d.cts +32 -0
- package/dist/engine/bucket.d.cts.map +1 -0
- package/dist/engine/bucket.d.mts +32 -0
- package/dist/engine/bucket.d.mts.map +1 -0
- package/dist/engine/bucket.mjs +69 -0
- package/dist/engine/bucket.mjs.map +1 -0
- package/dist/engine/condition.cjs +166 -0
- package/dist/engine/condition.cjs.map +1 -0
- package/dist/engine/condition.d.cts +52 -0
- package/dist/engine/condition.d.cts.map +1 -0
- package/dist/engine/condition.d.mts +52 -0
- package/dist/engine/condition.d.mts.map +1 -0
- package/dist/engine/condition.mjs +161 -0
- package/dist/engine/condition.mjs.map +1 -0
- package/dist/engine/index.cjs +20 -0
- package/dist/engine/index.d.cts +8 -0
- package/dist/engine/index.d.mts +8 -0
- package/dist/engine/index.mjs +7 -0
- package/dist/engine/interpreter.cjs +428 -0
- package/dist/engine/interpreter.cjs.map +1 -0
- package/dist/engine/interpreter.d.cts +6 -0
- package/dist/engine/interpreter.d.cts.map +1 -0
- package/dist/engine/interpreter.d.mts +6 -0
- package/dist/engine/interpreter.d.mts.map +1 -0
- package/dist/engine/interpreter.mjs +427 -0
- package/dist/engine/interpreter.mjs.map +1 -0
- package/dist/engine/ports.cjs +0 -0
- package/dist/engine/ports.d.cts +252 -0
- package/dist/engine/ports.d.cts.map +1 -0
- package/dist/engine/ports.d.mts +252 -0
- package/dist/engine/ports.d.mts.map +1 -0
- package/dist/engine/ports.mjs +1 -0
- package/dist/engine/recipient.cjs +23 -0
- package/dist/engine/recipient.cjs.map +1 -0
- package/dist/engine/recipient.d.cts +13 -0
- package/dist/engine/recipient.d.cts.map +1 -0
- package/dist/engine/recipient.d.mts +13 -0
- package/dist/engine/recipient.d.mts.map +1 -0
- package/dist/engine/recipient.mjs +22 -0
- package/dist/engine/recipient.mjs.map +1 -0
- package/dist/engine/time-scale.cjs +54 -0
- package/dist/engine/time-scale.cjs.map +1 -0
- package/dist/engine/time-scale.d.cts +31 -0
- package/dist/engine/time-scale.d.cts.map +1 -0
- package/dist/engine/time-scale.d.mts +31 -0
- package/dist/engine/time-scale.d.mts.map +1 -0
- package/dist/engine/time-scale.mjs +52 -0
- package/dist/engine/time-scale.mjs.map +1 -0
- package/dist/engine/time-window.cjs +149 -0
- package/dist/engine/time-window.cjs.map +1 -0
- package/dist/engine/time-window.d.cts +22 -0
- package/dist/engine/time-window.d.cts.map +1 -0
- package/dist/engine/time-window.d.mts +22 -0
- package/dist/engine/time-window.d.mts.map +1 -0
- package/dist/engine/time-window.mjs +147 -0
- package/dist/engine/time-window.mjs.map +1 -0
- package/dist/fcm.cjs +125 -0
- package/dist/fcm.cjs.map +1 -0
- package/dist/fcm.d.cts +48 -0
- package/dist/fcm.d.cts.map +1 -0
- package/dist/fcm.d.mts +48 -0
- package/dist/fcm.d.mts.map +1 -0
- package/dist/fcm.mjs +121 -0
- package/dist/fcm.mjs.map +1 -0
- package/dist/hash.cjs +245 -0
- package/dist/hash.cjs.map +1 -0
- package/dist/hash.d.cts +30 -0
- package/dist/hash.d.cts.map +1 -0
- package/dist/hash.d.mts +30 -0
- package/dist/hash.d.mts.map +1 -0
- package/dist/hash.mjs +239 -0
- package/dist/hash.mjs.map +1 -0
- package/dist/index.cjs +100 -0
- package/dist/index.d.cts +18 -0
- package/dist/index.d.mts +18 -0
- package/dist/index.mjs +17 -0
- package/dist/ir.cjs +366 -0
- package/dist/ir.cjs.map +1 -0
- package/dist/ir.d.cts +486 -0
- package/dist/ir.d.cts.map +1 -0
- package/dist/ir.d.mts +486 -0
- package/dist/ir.d.mts.map +1 -0
- package/dist/ir.mjs +344 -0
- package/dist/ir.mjs.map +1 -0
- package/dist/jwt.cjs +22 -0
- package/dist/jwt.cjs.map +1 -0
- package/dist/jwt.d.cts +9 -0
- package/dist/jwt.d.cts.map +1 -0
- package/dist/jwt.d.mts +9 -0
- package/dist/jwt.d.mts.map +1 -0
- package/dist/jwt.mjs +20 -0
- package/dist/jwt.mjs.map +1 -0
- package/dist/plan.cjs +223 -0
- package/dist/plan.cjs.map +1 -0
- package/dist/plan.d.cts +68 -0
- package/dist/plan.d.cts.map +1 -0
- package/dist/plan.d.mts +68 -0
- package/dist/plan.d.mts.map +1 -0
- package/dist/plan.mjs +221 -0
- package/dist/plan.mjs.map +1 -0
- package/dist/postgres-js.cjs +42 -0
- package/dist/postgres-js.cjs.map +1 -0
- package/dist/postgres-js.d.cts +28 -0
- package/dist/postgres-js.d.cts.map +1 -0
- package/dist/postgres-js.d.mts +28 -0
- package/dist/postgres-js.d.mts.map +1 -0
- package/dist/postgres-js.mjs +38 -0
- package/dist/postgres-js.mjs.map +1 -0
- package/dist/provenance.cjs +67 -0
- package/dist/provenance.cjs.map +1 -0
- package/dist/provenance.d.cts +41 -0
- package/dist/provenance.d.cts.map +1 -0
- package/dist/provenance.d.mts +41 -0
- package/dist/provenance.d.mts.map +1 -0
- package/dist/provenance.mjs +65 -0
- package/dist/provenance.mjs.map +1 -0
- package/dist/push.cjs +14 -0
- package/dist/push.cjs.map +1 -0
- package/dist/push.d.cts +16 -0
- package/dist/push.d.cts.map +1 -0
- package/dist/push.d.mts +16 -0
- package/dist/push.d.mts.map +1 -0
- package/dist/push.mjs +13 -0
- package/dist/push.mjs.map +1 -0
- package/dist/react-email.cjs +48 -0
- package/dist/react-email.cjs.map +1 -0
- package/dist/react-email.d.cts +7 -0
- package/dist/react-email.d.cts.map +1 -0
- package/dist/react-email.d.mts +7 -0
- package/dist/react-email.d.mts.map +1 -0
- package/dist/react-email.mjs +47 -0
- package/dist/react-email.mjs.map +1 -0
- package/dist/reporting/classify.cjs +42 -0
- package/dist/reporting/classify.cjs.map +1 -0
- package/dist/reporting/classify.d.cts +60 -0
- package/dist/reporting/classify.d.cts.map +1 -0
- package/dist/reporting/classify.d.mts +60 -0
- package/dist/reporting/classify.d.mts.map +1 -0
- package/dist/reporting/classify.mjs +38 -0
- package/dist/reporting/classify.mjs.map +1 -0
- package/dist/reporting/observer.cjs +63 -0
- package/dist/reporting/observer.cjs.map +1 -0
- package/dist/reporting/observer.d.cts +18 -0
- package/dist/reporting/observer.d.cts.map +1 -0
- package/dist/reporting/observer.d.mts +18 -0
- package/dist/reporting/observer.d.mts.map +1 -0
- package/dist/reporting/observer.mjs +62 -0
- package/dist/reporting/observer.mjs.map +1 -0
- package/dist/reporting/tracking.cjs +153 -0
- package/dist/reporting/tracking.cjs.map +1 -0
- package/dist/reporting/tracking.d.cts +43 -0
- package/dist/reporting/tracking.d.cts.map +1 -0
- package/dist/reporting/tracking.d.mts +43 -0
- package/dist/reporting/tracking.d.mts.map +1 -0
- package/dist/reporting/tracking.mjs +151 -0
- package/dist/reporting/tracking.mjs.map +1 -0
- package/dist/runtime.cjs +19 -0
- package/dist/runtime.cjs.map +1 -0
- package/dist/runtime.d.cts +30 -0
- package/dist/runtime.d.cts.map +1 -0
- package/dist/runtime.d.mts +30 -0
- package/dist/runtime.d.mts.map +1 -0
- package/dist/runtime.mjs +18 -0
- package/dist/runtime.mjs.map +1 -0
- package/dist/store/d1.cjs +185 -0
- package/dist/store/d1.cjs.map +1 -0
- package/dist/store/d1.d.cts +55 -0
- package/dist/store/d1.d.cts.map +1 -0
- package/dist/store/d1.d.mts +55 -0
- package/dist/store/d1.d.mts.map +1 -0
- package/dist/store/d1.mjs +184 -0
- package/dist/store/d1.mjs.map +1 -0
- package/dist/store/facts.cjs +60 -0
- package/dist/store/facts.cjs.map +1 -0
- package/dist/store/facts.d.cts +38 -0
- package/dist/store/facts.d.cts.map +1 -0
- package/dist/store/facts.d.mts +38 -0
- package/dist/store/facts.d.mts.map +1 -0
- package/dist/store/facts.mjs +58 -0
- package/dist/store/facts.mjs.map +1 -0
- package/dist/store/index.cjs +31 -0
- package/dist/store/index.cjs.map +1 -0
- package/dist/store/index.d.cts +291 -0
- package/dist/store/index.d.cts.map +1 -0
- package/dist/store/index.d.mts +291 -0
- package/dist/store/index.d.mts.map +1 -0
- package/dist/store/index.mjs +22 -0
- package/dist/store/index.mjs.map +1 -0
- package/dist/store/postgres-reporting.cjs +213 -0
- package/dist/store/postgres-reporting.cjs.map +1 -0
- package/dist/store/postgres-reporting.d.cts +63 -0
- package/dist/store/postgres-reporting.d.cts.map +1 -0
- package/dist/store/postgres-reporting.d.mts +63 -0
- package/dist/store/postgres-reporting.d.mts.map +1 -0
- package/dist/store/postgres-reporting.mjs +212 -0
- package/dist/store/postgres-reporting.mjs.map +1 -0
- package/dist/store/postgres.cjs +309 -0
- package/dist/store/postgres.cjs.map +1 -0
- package/dist/store/postgres.d.cts +77 -0
- package/dist/store/postgres.d.cts.map +1 -0
- package/dist/store/postgres.d.mts +77 -0
- package/dist/store/postgres.d.mts.map +1 -0
- package/dist/store/postgres.mjs +306 -0
- package/dist/store/postgres.mjs.map +1 -0
- package/dist/store/sql.cjs +24 -0
- package/dist/store/sql.cjs.map +1 -0
- package/dist/store/sql.d.cts +25 -0
- package/dist/store/sql.d.cts.map +1 -0
- package/dist/store/sql.d.mts +25 -0
- package/dist/store/sql.d.mts.map +1 -0
- package/dist/store/sql.mjs +23 -0
- package/dist/store/sql.mjs.map +1 -0
- package/dist/store/tables.cjs +404 -0
- package/dist/store/tables.cjs.map +1 -0
- package/dist/store/tables.d.cts +81 -0
- package/dist/store/tables.d.cts.map +1 -0
- package/dist/store/tables.d.mts +81 -0
- package/dist/store/tables.d.mts.map +1 -0
- package/dist/store/tables.mjs +399 -0
- package/dist/store/tables.mjs.map +1 -0
- package/dist/templates.cjs +247 -0
- package/dist/templates.cjs.map +1 -0
- package/dist/templates.d.cts +187 -0
- package/dist/templates.d.cts.map +1 -0
- package/dist/templates.d.mts +187 -0
- package/dist/templates.d.mts.map +1 -0
- package/dist/templates.mjs +230 -0
- package/dist/templates.mjs.map +1 -0
- package/dist/webhooks.cjs +96 -0
- package/dist/webhooks.cjs.map +1 -0
- package/dist/webhooks.d.cts +25 -0
- package/dist/webhooks.d.cts.map +1 -0
- package/dist/webhooks.d.mts +25 -0
- package/dist/webhooks.d.mts.map +1 -0
- package/dist/webhooks.mjs +94 -0
- package/dist/webhooks.mjs.map +1 -0
- package/dist/webpush.cjs +180 -0
- package/dist/webpush.cjs.map +1 -0
- package/dist/webpush.d.cts +53 -0
- package/dist/webpush.d.cts.map +1 -0
- package/dist/webpush.d.mts +53 -0
- package/dist/webpush.d.mts.map +1 -0
- package/dist/webpush.mjs +175 -0
- package/dist/webpush.mjs.map +1 -0
- package/package.json +189 -3
- package/src/cloudflare/schema.sql +127 -0
- package/src/cloudflare/workers.d.ts +44 -0
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { ChannelIR, ConditionIR, ScalarIR } from "../ir.cjs";
|
|
2
|
+
//#region src/engine/ports.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Engine ports: the only shape of the outside world the interpreter depends on.
|
|
5
|
+
*
|
|
6
|
+
* Design constraints (AWS-proof, see the engine design notes):
|
|
7
|
+
* - A wake-up is only a hint — "**at least one wake-up, re-evaluate after
|
|
8
|
+
* waking**". No event buffering or de-duplication is promised (Cloudflare
|
|
9
|
+
* buffers, AWS callback tokens do not — the port takes the intersection).
|
|
10
|
+
* The truth of a condition is always re-read by the caller once awake.
|
|
11
|
+
* - subscribe / waitForWake / unsubscribe are split so the caller can register
|
|
12
|
+
* interest *before* its first condition check — an event landing between the
|
|
13
|
+
* check and the registration would otherwise be missed until the timeout.
|
|
14
|
+
* - Every source of non-determinism (randomness, current time, external IO)
|
|
15
|
+
* must happen inside step.do, so its result is persisted and replay lines up.
|
|
16
|
+
*/
|
|
17
|
+
export interface EngineStep {
|
|
18
|
+
/** Durable step: fn's return value is checkpointed; on replay the result is reused without re-running. */
|
|
19
|
+
do<T>(name: string, fn: () => Promise<T>): Promise<T>;
|
|
20
|
+
/** Durable sleep: suspended time is not billed. */
|
|
21
|
+
sleep(name: string, ms: number): Promise<void>;
|
|
22
|
+
/** Sleep until an absolute instant (used by time_window). */
|
|
23
|
+
sleepUntil(name: string, timestampMs: number): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Durable: register interest in the named events (Cloudflare: write the
|
|
26
|
+
* subscription table; AWS: create a callback and store its id). Must
|
|
27
|
+
* complete before the caller's condition check for that attempt.
|
|
28
|
+
*
|
|
29
|
+
* Contract: one subscribe arms **at least one** wake-up — adapters whose
|
|
30
|
+
* wake handle is one-shot (AWS durable-function callbacks are consumed on
|
|
31
|
+
* delivery) satisfy it with exactly one. Callers therefore re-subscribe on
|
|
32
|
+
* every wait attempt; adapters where registrations persist (Cloudflare rows)
|
|
33
|
+
* make the repeat call idempotent.
|
|
34
|
+
*/
|
|
35
|
+
subscribe(name: string, events: readonly string[]): Promise<void>;
|
|
36
|
+
/** Durable: drop every registration this instance holds. */
|
|
37
|
+
unsubscribe(name: string): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Wait for a wake-up or a timeout. A registration from `subscribe` must
|
|
40
|
+
* already exist — this call only parks the instance.
|
|
41
|
+
*/
|
|
42
|
+
waitForWake(name: string, timeoutMs: number): Promise<'event' | 'timeout'>;
|
|
43
|
+
}
|
|
44
|
+
/** One device a push can go to: the provider token (APNs device token, FCM registration token) and the platform that picks the provider. */
|
|
45
|
+
export interface PushDevice {
|
|
46
|
+
token: string;
|
|
47
|
+
platform: string;
|
|
48
|
+
}
|
|
49
|
+
/** Facts from a single user's point of view: the read interface condition evaluation uses (D1 and in-memory share one evaluator). */
|
|
50
|
+
export interface FactSource {
|
|
51
|
+
/**
|
|
52
|
+
* How many times an event occurred. `sinceMs` bounds the window (within /
|
|
53
|
+
* anchoring); `where` is a payload-only condition tree — count only events
|
|
54
|
+
* whose payload matches (evaluate with matchesWhere from engine/condition).
|
|
55
|
+
*/
|
|
56
|
+
countEvents(event: string, opts?: {
|
|
57
|
+
sinceMs?: number;
|
|
58
|
+
untilMs?: number;
|
|
59
|
+
where?: ConditionIR;
|
|
60
|
+
}): Promise<number>;
|
|
61
|
+
getProperty(path: string): Promise<ScalarIR | undefined>;
|
|
62
|
+
/** Resolve a segment definition by id (stored with the bundle at deploy time). */
|
|
63
|
+
getSegmentCondition(name: string): Promise<ConditionIR | undefined>;
|
|
64
|
+
/** The user's push devices, most recently registered first (empty when the user registered none). */
|
|
65
|
+
listDevices(): Promise<PushDevice[]>;
|
|
66
|
+
/** The user opted out of the channel (an email unsubscribe): nothing is sent there. */
|
|
67
|
+
isSuppressed?(channel: ChannelIR): Promise<boolean>;
|
|
68
|
+
}
|
|
69
|
+
export interface OutboundMessage {
|
|
70
|
+
/** Where the copy comes from: the workflow, its instance and the message node. */
|
|
71
|
+
workflow: string;
|
|
72
|
+
instanceId: string;
|
|
73
|
+
nodeId: string;
|
|
74
|
+
channel: ChannelIR;
|
|
75
|
+
template: string;
|
|
76
|
+
/** Already resolved: user_property references have been replaced by actual values. */
|
|
77
|
+
props: Record<string, ScalarIR | undefined>;
|
|
78
|
+
userId: string;
|
|
79
|
+
/**
|
|
80
|
+
* The channel's recipient address, resolved by the engine — so senders never
|
|
81
|
+
* read the database: a profile property for most channels, the device token
|
|
82
|
+
* for push (one message per device).
|
|
83
|
+
*/
|
|
84
|
+
recipient?: string | undefined;
|
|
85
|
+
/** Push only: the device this copy goes to — its platform picks the provider. */
|
|
86
|
+
device?: PushDevice | undefined;
|
|
87
|
+
/**
|
|
88
|
+
* `${instanceId}:${nodeId}` (plus `:${token}` for a push copy) — one per copy. Senders
|
|
89
|
+
* de-duplicate on it, making replays and retries safe, and it is the copy's identity for
|
|
90
|
+
* tracking: a push carries it in its payload, so the app can report the tap against it.
|
|
91
|
+
*/
|
|
92
|
+
idempotencyKey: string;
|
|
93
|
+
/** What the host's observer asked the sender to add for measuring this copy (see `MessageTracking`). */
|
|
94
|
+
tracking?: MessageTracking | undefined;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Measuring a copy, as the built-in senders apply it: an email's links are routed through
|
|
98
|
+
* `link` and `pixel` is appended as a 1×1 image; a push carries `data` in its payload.
|
|
99
|
+
* Each is optional, and a sender ignores what its channel has no use for.
|
|
100
|
+
*/
|
|
101
|
+
export interface MessageTracking {
|
|
102
|
+
/** The original `http(s)` URL in, the URL that records the click and redirects out. */
|
|
103
|
+
link?: (url: string) => string;
|
|
104
|
+
/** Loaded when the email is opened. */
|
|
105
|
+
pixel?: string;
|
|
106
|
+
/** Extra string keys in the push payload. */
|
|
107
|
+
data?: Record<string, string>;
|
|
108
|
+
/**
|
|
109
|
+
* Called by the sender once the links are rewritten and before the transport takes the copy:
|
|
110
|
+
* what the tracking URLs resolve to is stored before anyone can click one. A throw stops the
|
|
111
|
+
* send, and the step retries it.
|
|
112
|
+
*/
|
|
113
|
+
commit?: () => Promise<void>;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* How a copy ended at the transport. A sender returns nothing when the transport accepted it;
|
|
117
|
+
* `{ delivered: false }` when it settled the copy without delivering — a dead push token, which
|
|
118
|
+
* is not retried. A transient failure throws instead, and the step retries.
|
|
119
|
+
*/
|
|
120
|
+
export interface SendResult {
|
|
121
|
+
delivered: false;
|
|
122
|
+
reason: string;
|
|
123
|
+
}
|
|
124
|
+
export interface MessageSender {
|
|
125
|
+
send(message: OutboundMessage): Promise<void | SendResult>;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* What the host learns about each journey as it runs, for reporting: where every instance
|
|
129
|
+
* stands, every copy it sends and how the transport took it, and how it ended. Storage is the
|
|
130
|
+
* host's — the engine only reports. Every method is optional and must be idempotent: a step
|
|
131
|
+
* that fails after its report ran is retried and reports again (keyed by the instance and node,
|
|
132
|
+
* or the copy's idempotency key).
|
|
133
|
+
*/
|
|
134
|
+
export interface JourneyObserver {
|
|
135
|
+
/** The instance entered, before its entry guards: `at` is the trigger's time, the entry anchor. */
|
|
136
|
+
started?(event: {
|
|
137
|
+
workflow: string;
|
|
138
|
+
instanceId: string;
|
|
139
|
+
userId: string;
|
|
140
|
+
at: number;
|
|
141
|
+
}): Promise<void>;
|
|
142
|
+
/** The instance reached a node: it sits there until the next one (a delay, a wait), or passes through. */
|
|
143
|
+
nodeReached?(event: {
|
|
144
|
+
workflow: string;
|
|
145
|
+
instanceId: string;
|
|
146
|
+
userId: string;
|
|
147
|
+
nodeId: string;
|
|
148
|
+
at: number;
|
|
149
|
+
}): Promise<void>;
|
|
150
|
+
/** A copy is about to go out; the tracking returned travels with it to the sender. */
|
|
151
|
+
messagePrepared?(message: OutboundMessage): Promise<MessageTracking | undefined>;
|
|
152
|
+
/** The transport took the copy (`delivered: true`), or settled it undelivered; a thrown send is not reported. */
|
|
153
|
+
messageSent?(message: OutboundMessage, result: {
|
|
154
|
+
delivered: true;
|
|
155
|
+
} | SendResult): Promise<void>;
|
|
156
|
+
/** The instance ended: its outcome (`goal` is a conversion), or `failed` when an error ended it. */
|
|
157
|
+
finished?(event: {
|
|
158
|
+
workflow: string;
|
|
159
|
+
instanceId: string;
|
|
160
|
+
userId: string;
|
|
161
|
+
status: JourneyOutcome['status'] | 'failed';
|
|
162
|
+
at: number;
|
|
163
|
+
/** When it converted, if it did — set whatever the status (see JourneyOutcome). */
|
|
164
|
+
convertedAt?: number;
|
|
165
|
+
}): Promise<void>;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Message preparation: checks a message's resolved props against its template
|
|
169
|
+
* before any sender sees it (and, for a push, before the first device), and
|
|
170
|
+
* returns the props to send — the parsed value. Throwing stops the send; the
|
|
171
|
+
* interpreter adds the instance and node to the error. The Cloudflare host
|
|
172
|
+
* assembles one from the template registries (`registryValidator`).
|
|
173
|
+
*/
|
|
174
|
+
export interface MessageValidator {
|
|
175
|
+
validate(message: {
|
|
176
|
+
channel: ChannelIR;
|
|
177
|
+
template: string;
|
|
178
|
+
props: Record<string, ScalarIR | undefined>;
|
|
179
|
+
}): Record<string, ScalarIR | undefined>;
|
|
180
|
+
}
|
|
181
|
+
/** send_event's outlet: feeds back into ingest (the event edge between workflows). */
|
|
182
|
+
export interface EventSink {
|
|
183
|
+
/**
|
|
184
|
+
* `id` identifies the occurrence, and sending the same one twice must record
|
|
185
|
+
* it once. The interpreter passes `${instanceId}:${nodeId}` — the same
|
|
186
|
+
* identity senders and actions de-duplicate on — because the emit is wrapped
|
|
187
|
+
* in a step, and a step body is retried after a failure that may have
|
|
188
|
+
* happened *after* the event was already written.
|
|
189
|
+
*/
|
|
190
|
+
emit(event: string, payload: Record<string, ScalarIR | undefined>, id: string): Promise<void>;
|
|
191
|
+
}
|
|
192
|
+
/** One action node's resolved call, ready for the registry. */
|
|
193
|
+
export interface ActionInvocation {
|
|
194
|
+
/** Registry name; the code itself lives in the deployed bundle (dual-plane model, see dsl/action.ts). */
|
|
195
|
+
action: string;
|
|
196
|
+
/** Code-identity pin from the IR the journey is running; adapters compare it against the registered implementation. */
|
|
197
|
+
codeHash?: string | undefined;
|
|
198
|
+
/** Already resolved: user_property references have been replaced by actual values (same contract as message props). */
|
|
199
|
+
args: Record<string, ScalarIR | undefined>;
|
|
200
|
+
userId: string;
|
|
201
|
+
/** `${instanceId}:${nodeId}` — handlers with external side effects de-duplicate on it, like senders do. */
|
|
202
|
+
idempotencyKey: string;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Custom-action outlet. The interpreter already wraps the call in step.do, so
|
|
206
|
+
* an implementation may just look up and run the handler — a throw is retried
|
|
207
|
+
* by the runtime, a success is checkpointed.
|
|
208
|
+
*/
|
|
209
|
+
export interface ActionInvoker {
|
|
210
|
+
invoke(invocation: ActionInvocation): Promise<void>;
|
|
211
|
+
}
|
|
212
|
+
export interface JourneyContext {
|
|
213
|
+
userId: string;
|
|
214
|
+
/** Instance identity: feeds the idempotency key and the logs. */
|
|
215
|
+
instanceId: string;
|
|
216
|
+
/**
|
|
217
|
+
* When the user entered the workflow (ms epoch) — the baseline for the
|
|
218
|
+
* goal's attribution window (`goal.within`). Must be replay-stable: the
|
|
219
|
+
* adapter derives it from the trigger event's timestamp, never from
|
|
220
|
+
* Date.now() at run time.
|
|
221
|
+
*/
|
|
222
|
+
enteredAtMs: number;
|
|
223
|
+
step: EngineStep;
|
|
224
|
+
facts: FactSource;
|
|
225
|
+
messages: MessageSender;
|
|
226
|
+
events: EventSink;
|
|
227
|
+
/** Optional: only journeys whose IR contains action nodes need one; running such a node without it is a runtime error. */
|
|
228
|
+
actions?: ActionInvoker | undefined;
|
|
229
|
+
/** Template props validation before every send; without one, props go out as resolved. */
|
|
230
|
+
validator?: MessageValidator | undefined;
|
|
231
|
+
/** Reporting hooks (positions and copies); without one, nothing is reported. */
|
|
232
|
+
observer?: JourneyObserver | undefined;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* How an instance ended, and — apart from that — whether and when it converted: `goal` ends it
|
|
236
|
+
* at the conversion, while with `exitOnMatch: false` a converted instance ends like any other
|
|
237
|
+
* and only `convertedAt` tells.
|
|
238
|
+
*/
|
|
239
|
+
export type JourneyOutcome = ({
|
|
240
|
+
status: 'completed';
|
|
241
|
+
} | {
|
|
242
|
+
status: 'exited';
|
|
243
|
+
reason?: string;
|
|
244
|
+
} | {
|
|
245
|
+
status: 'goal';
|
|
246
|
+
} | {
|
|
247
|
+
status: 'exit_when';
|
|
248
|
+
}) & {
|
|
249
|
+
convertedAt?: number;
|
|
250
|
+
};
|
|
251
|
+
//#endregion
|
|
252
|
+
//# sourceMappingURL=ports.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ports.d.cts","names":[],"sources":["../../src/engine/ports.ts"],"mappings":";;;;;;;;;;;;;;;;iBAiBiB;;EAEf,GAAG,GAAG,cAAc,UAAU,QAAQ,KAAK,QAAQ;;EAGnD,MAAM,cAAc,aAAa;;EAGjC,WAAW,cAAc,sBAAsB;;;;;;;;;;;;EAa/C,UAAU,cAAc,4BAA4B;;EAGpD,YAAY,eAAe;;;;;EAM3B,YAAY,cAAc,oBAAoB;;;iBAI/B;EACf;EACA;;;iBAIe;;;;;;EAMf,YACE,eACA;IAAS;IAAkB;IAAkB,QAAQ;MACpD;EACH,YAAY,eAAe,QAAQ;;EAEnC,oBAAoB,eAAe,QAAQ;;EAE3C,eAAe,QAAQ;;EAEvB,cAAc,SAAS,YAAY;;iBAGpB;;EAEf;EACA;EACA;EACA,SAAS;EACT;;EAEA,OAAO,eAAe;EACtB;;;;;;EAMA;;EAEA,SAAS;;;;;;EAMT;;EAEA,WAAW;;;;;;;iBAQI;;EAEf,QAAQ;;EAER;;EAEA,OAAO;;;;;;EAMP,eAAe;;;;;;;iBAQA;EACf;EACA;;iBAGe;EACf,KAAK,SAAS,kBAAkB,eAAe;;;;;;;;;iBAUhC;;EAEf,SAAS;IACP;IACA;IACA;IACA;MACE;;EAEJ,aAAa;IACX;IACA;IACA;IACA;IACA;MACE;;EAEJ,iBAAiB,SAAS,kBAAkB,QAAQ;;EAEpD,aAAa,SAAS,iBAAiB;IAAU;MAAoB,aAAa;;EAElF,UAAU;IACR;IACA;IACA;IACA,QAAQ;IACR;;IAEA;MACE;;;;;;;;;iBAUW;EACf,SAAS;IACP,SAAS;IACT;IACA,OAAO,eAAe;MACpB,eAAe;;;iBAIJ;;;;;;;;EAQf,KAAK,eAAe,SAAS,eAAe,uBAAuB,aAAa;;;iBAIjE;;EAEf;;EAEA;;EAEA,MAAM,eAAe;EACrB;;EAEA;;;;;;;iBAQe;EACf,OAAO,YAAY,mBAAmB;;iBAGvB;EACf;;EAEA;;;;;;;EAOA;EACA,MAAM;EACN,OAAO;EACP,UAAU;EACV,QAAQ;;EAER,UAAU;;EAEV,YAAY;;EAEZ,WAAW;;;;;;;YAQD;EACN;;EACA;EAAkB;;EAClB;;EACA;;EACA"}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { ChannelIR, ConditionIR, ScalarIR } from "../ir.mjs";
|
|
2
|
+
//#region src/engine/ports.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Engine ports: the only shape of the outside world the interpreter depends on.
|
|
5
|
+
*
|
|
6
|
+
* Design constraints (AWS-proof, see the engine design notes):
|
|
7
|
+
* - A wake-up is only a hint — "**at least one wake-up, re-evaluate after
|
|
8
|
+
* waking**". No event buffering or de-duplication is promised (Cloudflare
|
|
9
|
+
* buffers, AWS callback tokens do not — the port takes the intersection).
|
|
10
|
+
* The truth of a condition is always re-read by the caller once awake.
|
|
11
|
+
* - subscribe / waitForWake / unsubscribe are split so the caller can register
|
|
12
|
+
* interest *before* its first condition check — an event landing between the
|
|
13
|
+
* check and the registration would otherwise be missed until the timeout.
|
|
14
|
+
* - Every source of non-determinism (randomness, current time, external IO)
|
|
15
|
+
* must happen inside step.do, so its result is persisted and replay lines up.
|
|
16
|
+
*/
|
|
17
|
+
export interface EngineStep {
|
|
18
|
+
/** Durable step: fn's return value is checkpointed; on replay the result is reused without re-running. */
|
|
19
|
+
do<T>(name: string, fn: () => Promise<T>): Promise<T>;
|
|
20
|
+
/** Durable sleep: suspended time is not billed. */
|
|
21
|
+
sleep(name: string, ms: number): Promise<void>;
|
|
22
|
+
/** Sleep until an absolute instant (used by time_window). */
|
|
23
|
+
sleepUntil(name: string, timestampMs: number): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Durable: register interest in the named events (Cloudflare: write the
|
|
26
|
+
* subscription table; AWS: create a callback and store its id). Must
|
|
27
|
+
* complete before the caller's condition check for that attempt.
|
|
28
|
+
*
|
|
29
|
+
* Contract: one subscribe arms **at least one** wake-up — adapters whose
|
|
30
|
+
* wake handle is one-shot (AWS durable-function callbacks are consumed on
|
|
31
|
+
* delivery) satisfy it with exactly one. Callers therefore re-subscribe on
|
|
32
|
+
* every wait attempt; adapters where registrations persist (Cloudflare rows)
|
|
33
|
+
* make the repeat call idempotent.
|
|
34
|
+
*/
|
|
35
|
+
subscribe(name: string, events: readonly string[]): Promise<void>;
|
|
36
|
+
/** Durable: drop every registration this instance holds. */
|
|
37
|
+
unsubscribe(name: string): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Wait for a wake-up or a timeout. A registration from `subscribe` must
|
|
40
|
+
* already exist — this call only parks the instance.
|
|
41
|
+
*/
|
|
42
|
+
waitForWake(name: string, timeoutMs: number): Promise<'event' | 'timeout'>;
|
|
43
|
+
}
|
|
44
|
+
/** One device a push can go to: the provider token (APNs device token, FCM registration token) and the platform that picks the provider. */
|
|
45
|
+
export interface PushDevice {
|
|
46
|
+
token: string;
|
|
47
|
+
platform: string;
|
|
48
|
+
}
|
|
49
|
+
/** Facts from a single user's point of view: the read interface condition evaluation uses (D1 and in-memory share one evaluator). */
|
|
50
|
+
export interface FactSource {
|
|
51
|
+
/**
|
|
52
|
+
* How many times an event occurred. `sinceMs` bounds the window (within /
|
|
53
|
+
* anchoring); `where` is a payload-only condition tree — count only events
|
|
54
|
+
* whose payload matches (evaluate with matchesWhere from engine/condition).
|
|
55
|
+
*/
|
|
56
|
+
countEvents(event: string, opts?: {
|
|
57
|
+
sinceMs?: number;
|
|
58
|
+
untilMs?: number;
|
|
59
|
+
where?: ConditionIR;
|
|
60
|
+
}): Promise<number>;
|
|
61
|
+
getProperty(path: string): Promise<ScalarIR | undefined>;
|
|
62
|
+
/** Resolve a segment definition by id (stored with the bundle at deploy time). */
|
|
63
|
+
getSegmentCondition(name: string): Promise<ConditionIR | undefined>;
|
|
64
|
+
/** The user's push devices, most recently registered first (empty when the user registered none). */
|
|
65
|
+
listDevices(): Promise<PushDevice[]>;
|
|
66
|
+
/** The user opted out of the channel (an email unsubscribe): nothing is sent there. */
|
|
67
|
+
isSuppressed?(channel: ChannelIR): Promise<boolean>;
|
|
68
|
+
}
|
|
69
|
+
export interface OutboundMessage {
|
|
70
|
+
/** Where the copy comes from: the workflow, its instance and the message node. */
|
|
71
|
+
workflow: string;
|
|
72
|
+
instanceId: string;
|
|
73
|
+
nodeId: string;
|
|
74
|
+
channel: ChannelIR;
|
|
75
|
+
template: string;
|
|
76
|
+
/** Already resolved: user_property references have been replaced by actual values. */
|
|
77
|
+
props: Record<string, ScalarIR | undefined>;
|
|
78
|
+
userId: string;
|
|
79
|
+
/**
|
|
80
|
+
* The channel's recipient address, resolved by the engine — so senders never
|
|
81
|
+
* read the database: a profile property for most channels, the device token
|
|
82
|
+
* for push (one message per device).
|
|
83
|
+
*/
|
|
84
|
+
recipient?: string | undefined;
|
|
85
|
+
/** Push only: the device this copy goes to — its platform picks the provider. */
|
|
86
|
+
device?: PushDevice | undefined;
|
|
87
|
+
/**
|
|
88
|
+
* `${instanceId}:${nodeId}` (plus `:${token}` for a push copy) — one per copy. Senders
|
|
89
|
+
* de-duplicate on it, making replays and retries safe, and it is the copy's identity for
|
|
90
|
+
* tracking: a push carries it in its payload, so the app can report the tap against it.
|
|
91
|
+
*/
|
|
92
|
+
idempotencyKey: string;
|
|
93
|
+
/** What the host's observer asked the sender to add for measuring this copy (see `MessageTracking`). */
|
|
94
|
+
tracking?: MessageTracking | undefined;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Measuring a copy, as the built-in senders apply it: an email's links are routed through
|
|
98
|
+
* `link` and `pixel` is appended as a 1×1 image; a push carries `data` in its payload.
|
|
99
|
+
* Each is optional, and a sender ignores what its channel has no use for.
|
|
100
|
+
*/
|
|
101
|
+
export interface MessageTracking {
|
|
102
|
+
/** The original `http(s)` URL in, the URL that records the click and redirects out. */
|
|
103
|
+
link?: (url: string) => string;
|
|
104
|
+
/** Loaded when the email is opened. */
|
|
105
|
+
pixel?: string;
|
|
106
|
+
/** Extra string keys in the push payload. */
|
|
107
|
+
data?: Record<string, string>;
|
|
108
|
+
/**
|
|
109
|
+
* Called by the sender once the links are rewritten and before the transport takes the copy:
|
|
110
|
+
* what the tracking URLs resolve to is stored before anyone can click one. A throw stops the
|
|
111
|
+
* send, and the step retries it.
|
|
112
|
+
*/
|
|
113
|
+
commit?: () => Promise<void>;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* How a copy ended at the transport. A sender returns nothing when the transport accepted it;
|
|
117
|
+
* `{ delivered: false }` when it settled the copy without delivering — a dead push token, which
|
|
118
|
+
* is not retried. A transient failure throws instead, and the step retries.
|
|
119
|
+
*/
|
|
120
|
+
export interface SendResult {
|
|
121
|
+
delivered: false;
|
|
122
|
+
reason: string;
|
|
123
|
+
}
|
|
124
|
+
export interface MessageSender {
|
|
125
|
+
send(message: OutboundMessage): Promise<void | SendResult>;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* What the host learns about each journey as it runs, for reporting: where every instance
|
|
129
|
+
* stands, every copy it sends and how the transport took it, and how it ended. Storage is the
|
|
130
|
+
* host's — the engine only reports. Every method is optional and must be idempotent: a step
|
|
131
|
+
* that fails after its report ran is retried and reports again (keyed by the instance and node,
|
|
132
|
+
* or the copy's idempotency key).
|
|
133
|
+
*/
|
|
134
|
+
export interface JourneyObserver {
|
|
135
|
+
/** The instance entered, before its entry guards: `at` is the trigger's time, the entry anchor. */
|
|
136
|
+
started?(event: {
|
|
137
|
+
workflow: string;
|
|
138
|
+
instanceId: string;
|
|
139
|
+
userId: string;
|
|
140
|
+
at: number;
|
|
141
|
+
}): Promise<void>;
|
|
142
|
+
/** The instance reached a node: it sits there until the next one (a delay, a wait), or passes through. */
|
|
143
|
+
nodeReached?(event: {
|
|
144
|
+
workflow: string;
|
|
145
|
+
instanceId: string;
|
|
146
|
+
userId: string;
|
|
147
|
+
nodeId: string;
|
|
148
|
+
at: number;
|
|
149
|
+
}): Promise<void>;
|
|
150
|
+
/** A copy is about to go out; the tracking returned travels with it to the sender. */
|
|
151
|
+
messagePrepared?(message: OutboundMessage): Promise<MessageTracking | undefined>;
|
|
152
|
+
/** The transport took the copy (`delivered: true`), or settled it undelivered; a thrown send is not reported. */
|
|
153
|
+
messageSent?(message: OutboundMessage, result: {
|
|
154
|
+
delivered: true;
|
|
155
|
+
} | SendResult): Promise<void>;
|
|
156
|
+
/** The instance ended: its outcome (`goal` is a conversion), or `failed` when an error ended it. */
|
|
157
|
+
finished?(event: {
|
|
158
|
+
workflow: string;
|
|
159
|
+
instanceId: string;
|
|
160
|
+
userId: string;
|
|
161
|
+
status: JourneyOutcome['status'] | 'failed';
|
|
162
|
+
at: number;
|
|
163
|
+
/** When it converted, if it did — set whatever the status (see JourneyOutcome). */
|
|
164
|
+
convertedAt?: number;
|
|
165
|
+
}): Promise<void>;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Message preparation: checks a message's resolved props against its template
|
|
169
|
+
* before any sender sees it (and, for a push, before the first device), and
|
|
170
|
+
* returns the props to send — the parsed value. Throwing stops the send; the
|
|
171
|
+
* interpreter adds the instance and node to the error. The Cloudflare host
|
|
172
|
+
* assembles one from the template registries (`registryValidator`).
|
|
173
|
+
*/
|
|
174
|
+
export interface MessageValidator {
|
|
175
|
+
validate(message: {
|
|
176
|
+
channel: ChannelIR;
|
|
177
|
+
template: string;
|
|
178
|
+
props: Record<string, ScalarIR | undefined>;
|
|
179
|
+
}): Record<string, ScalarIR | undefined>;
|
|
180
|
+
}
|
|
181
|
+
/** send_event's outlet: feeds back into ingest (the event edge between workflows). */
|
|
182
|
+
export interface EventSink {
|
|
183
|
+
/**
|
|
184
|
+
* `id` identifies the occurrence, and sending the same one twice must record
|
|
185
|
+
* it once. The interpreter passes `${instanceId}:${nodeId}` — the same
|
|
186
|
+
* identity senders and actions de-duplicate on — because the emit is wrapped
|
|
187
|
+
* in a step, and a step body is retried after a failure that may have
|
|
188
|
+
* happened *after* the event was already written.
|
|
189
|
+
*/
|
|
190
|
+
emit(event: string, payload: Record<string, ScalarIR | undefined>, id: string): Promise<void>;
|
|
191
|
+
}
|
|
192
|
+
/** One action node's resolved call, ready for the registry. */
|
|
193
|
+
export interface ActionInvocation {
|
|
194
|
+
/** Registry name; the code itself lives in the deployed bundle (dual-plane model, see dsl/action.ts). */
|
|
195
|
+
action: string;
|
|
196
|
+
/** Code-identity pin from the IR the journey is running; adapters compare it against the registered implementation. */
|
|
197
|
+
codeHash?: string | undefined;
|
|
198
|
+
/** Already resolved: user_property references have been replaced by actual values (same contract as message props). */
|
|
199
|
+
args: Record<string, ScalarIR | undefined>;
|
|
200
|
+
userId: string;
|
|
201
|
+
/** `${instanceId}:${nodeId}` — handlers with external side effects de-duplicate on it, like senders do. */
|
|
202
|
+
idempotencyKey: string;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Custom-action outlet. The interpreter already wraps the call in step.do, so
|
|
206
|
+
* an implementation may just look up and run the handler — a throw is retried
|
|
207
|
+
* by the runtime, a success is checkpointed.
|
|
208
|
+
*/
|
|
209
|
+
export interface ActionInvoker {
|
|
210
|
+
invoke(invocation: ActionInvocation): Promise<void>;
|
|
211
|
+
}
|
|
212
|
+
export interface JourneyContext {
|
|
213
|
+
userId: string;
|
|
214
|
+
/** Instance identity: feeds the idempotency key and the logs. */
|
|
215
|
+
instanceId: string;
|
|
216
|
+
/**
|
|
217
|
+
* When the user entered the workflow (ms epoch) — the baseline for the
|
|
218
|
+
* goal's attribution window (`goal.within`). Must be replay-stable: the
|
|
219
|
+
* adapter derives it from the trigger event's timestamp, never from
|
|
220
|
+
* Date.now() at run time.
|
|
221
|
+
*/
|
|
222
|
+
enteredAtMs: number;
|
|
223
|
+
step: EngineStep;
|
|
224
|
+
facts: FactSource;
|
|
225
|
+
messages: MessageSender;
|
|
226
|
+
events: EventSink;
|
|
227
|
+
/** Optional: only journeys whose IR contains action nodes need one; running such a node without it is a runtime error. */
|
|
228
|
+
actions?: ActionInvoker | undefined;
|
|
229
|
+
/** Template props validation before every send; without one, props go out as resolved. */
|
|
230
|
+
validator?: MessageValidator | undefined;
|
|
231
|
+
/** Reporting hooks (positions and copies); without one, nothing is reported. */
|
|
232
|
+
observer?: JourneyObserver | undefined;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* How an instance ended, and — apart from that — whether and when it converted: `goal` ends it
|
|
236
|
+
* at the conversion, while with `exitOnMatch: false` a converted instance ends like any other
|
|
237
|
+
* and only `convertedAt` tells.
|
|
238
|
+
*/
|
|
239
|
+
export type JourneyOutcome = ({
|
|
240
|
+
status: 'completed';
|
|
241
|
+
} | {
|
|
242
|
+
status: 'exited';
|
|
243
|
+
reason?: string;
|
|
244
|
+
} | {
|
|
245
|
+
status: 'goal';
|
|
246
|
+
} | {
|
|
247
|
+
status: 'exit_when';
|
|
248
|
+
}) & {
|
|
249
|
+
convertedAt?: number;
|
|
250
|
+
};
|
|
251
|
+
//#endregion
|
|
252
|
+
//# sourceMappingURL=ports.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ports.d.mts","names":[],"sources":["../../src/engine/ports.ts"],"mappings":";;;;;;;;;;;;;;;;iBAiBiB;;EAEf,GAAG,GAAG,cAAc,UAAU,QAAQ,KAAK,QAAQ;;EAGnD,MAAM,cAAc,aAAa;;EAGjC,WAAW,cAAc,sBAAsB;;;;;;;;;;;;EAa/C,UAAU,cAAc,4BAA4B;;EAGpD,YAAY,eAAe;;;;;EAM3B,YAAY,cAAc,oBAAoB;;;iBAI/B;EACf;EACA;;;iBAIe;;;;;;EAMf,YACE,eACA;IAAS;IAAkB;IAAkB,QAAQ;MACpD;EACH,YAAY,eAAe,QAAQ;;EAEnC,oBAAoB,eAAe,QAAQ;;EAE3C,eAAe,QAAQ;;EAEvB,cAAc,SAAS,YAAY;;iBAGpB;;EAEf;EACA;EACA;EACA,SAAS;EACT;;EAEA,OAAO,eAAe;EACtB;;;;;;EAMA;;EAEA,SAAS;;;;;;EAMT;;EAEA,WAAW;;;;;;;iBAQI;;EAEf,QAAQ;;EAER;;EAEA,OAAO;;;;;;EAMP,eAAe;;;;;;;iBAQA;EACf;EACA;;iBAGe;EACf,KAAK,SAAS,kBAAkB,eAAe;;;;;;;;;iBAUhC;;EAEf,SAAS;IACP;IACA;IACA;IACA;MACE;;EAEJ,aAAa;IACX;IACA;IACA;IACA;IACA;MACE;;EAEJ,iBAAiB,SAAS,kBAAkB,QAAQ;;EAEpD,aAAa,SAAS,iBAAiB;IAAU;MAAoB,aAAa;;EAElF,UAAU;IACR;IACA;IACA;IACA,QAAQ;IACR;;IAEA;MACE;;;;;;;;;iBAUW;EACf,SAAS;IACP,SAAS;IACT;IACA,OAAO,eAAe;MACpB,eAAe;;;iBAIJ;;;;;;;;EAQf,KAAK,eAAe,SAAS,eAAe,uBAAuB,aAAa;;;iBAIjE;;EAEf;;EAEA;;EAEA,MAAM,eAAe;EACrB;;EAEA;;;;;;;iBAQe;EACf,OAAO,YAAY,mBAAmB;;iBAGvB;EACf;;EAEA;;;;;;;EAOA;EACA,MAAM;EACN,OAAO;EACP,UAAU;EACV,QAAQ;;EAER,UAAU;;EAEV,YAAY;;EAEZ,WAAW;;;;;;;YAQD;EACN;;EACA;EAAkB;;EAClB;;EACA;;EACA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region src/engine/recipient.ts
|
|
3
|
+
/**
|
|
4
|
+
* Channel → the user property holding its recipient address. The engine
|
|
5
|
+
* resolves it before sending, which keeps senders stateless. Push is absent on
|
|
6
|
+
* purpose: its recipients are devices, kept in the store. Slack and Discord post to a team
|
|
7
|
+
* channel through the template's webhook, so they have no per-user address either.
|
|
8
|
+
* TODO: let the app override this table (today it is a convention, with
|
|
9
|
+
* UserPropertyBase guaranteeing `email` exists).
|
|
10
|
+
*/
|
|
11
|
+
const RECIPIENT_PROPERTY = {
|
|
12
|
+
email: "email",
|
|
13
|
+
sms: "phone",
|
|
14
|
+
push: void 0,
|
|
15
|
+
slack: void 0,
|
|
16
|
+
discord: void 0,
|
|
17
|
+
survey: "email",
|
|
18
|
+
in_app: void 0
|
|
19
|
+
};
|
|
20
|
+
//#endregion
|
|
21
|
+
exports.RECIPIENT_PROPERTY = RECIPIENT_PROPERTY;
|
|
22
|
+
|
|
23
|
+
//# sourceMappingURL=recipient.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recipient.cjs","names":[],"sources":["../../src/engine/recipient.ts"],"sourcesContent":["import type { ChannelIR } from '../ir';\n\n/**\n * Channel → the user property holding its recipient address. The engine\n * resolves it before sending, which keeps senders stateless. Push is absent on\n * purpose: its recipients are devices, kept in the store. Slack and Discord post to a team\n * channel through the template's webhook, so they have no per-user address either.\n * TODO: let the app override this table (today it is a convention, with\n * UserPropertyBase guaranteeing `email` exists).\n */\nexport const RECIPIENT_PROPERTY: Record<ChannelIR, string | undefined> = {\n email: 'email',\n sms: 'phone',\n push: undefined,\n slack: undefined,\n discord: undefined,\n survey: 'email',\n in_app: undefined,\n};\n"],"mappings":";;;;;;;;;;AAUA,MAAa,qBAA4D;CACvE,OAAO;CACP,KAAK;CACL,MAAM,KAAA;CACN,OAAO,KAAA;CACP,SAAS,KAAA;CACT,QAAQ;CACR,QAAQ,KAAA;AACV"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ChannelIR } from "../ir.cjs";
|
|
2
|
+
//#region src/engine/recipient.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Channel → the user property holding its recipient address. The engine
|
|
5
|
+
* resolves it before sending, which keeps senders stateless. Push is absent on
|
|
6
|
+
* purpose: its recipients are devices, kept in the store. Slack and Discord post to a team
|
|
7
|
+
* channel through the template's webhook, so they have no per-user address either.
|
|
8
|
+
* TODO: let the app override this table (today it is a convention, with
|
|
9
|
+
* UserPropertyBase guaranteeing `email` exists).
|
|
10
|
+
*/
|
|
11
|
+
export declare const RECIPIENT_PROPERTY: Record<ChannelIR, string | undefined>;
|
|
12
|
+
//#endregion
|
|
13
|
+
//# sourceMappingURL=recipient.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recipient.d.cts","names":[],"sources":["../../src/engine/recipient.ts"],"mappings":";;;;;;;;;;qBAUa,oBAAoB,OAAO"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ChannelIR } from "../ir.mjs";
|
|
2
|
+
//#region src/engine/recipient.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Channel → the user property holding its recipient address. The engine
|
|
5
|
+
* resolves it before sending, which keeps senders stateless. Push is absent on
|
|
6
|
+
* purpose: its recipients are devices, kept in the store. Slack and Discord post to a team
|
|
7
|
+
* channel through the template's webhook, so they have no per-user address either.
|
|
8
|
+
* TODO: let the app override this table (today it is a convention, with
|
|
9
|
+
* UserPropertyBase guaranteeing `email` exists).
|
|
10
|
+
*/
|
|
11
|
+
export declare const RECIPIENT_PROPERTY: Record<ChannelIR, string | undefined>;
|
|
12
|
+
//#endregion
|
|
13
|
+
//# sourceMappingURL=recipient.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recipient.d.mts","names":[],"sources":["../../src/engine/recipient.ts"],"mappings":";;;;;;;;;;qBAUa,oBAAoB,OAAO"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//#region src/engine/recipient.ts
|
|
2
|
+
/**
|
|
3
|
+
* Channel → the user property holding its recipient address. The engine
|
|
4
|
+
* resolves it before sending, which keeps senders stateless. Push is absent on
|
|
5
|
+
* purpose: its recipients are devices, kept in the store. Slack and Discord post to a team
|
|
6
|
+
* channel through the template's webhook, so they have no per-user address either.
|
|
7
|
+
* TODO: let the app override this table (today it is a convention, with
|
|
8
|
+
* UserPropertyBase guaranteeing `email` exists).
|
|
9
|
+
*/
|
|
10
|
+
const RECIPIENT_PROPERTY = {
|
|
11
|
+
email: "email",
|
|
12
|
+
sms: "phone",
|
|
13
|
+
push: void 0,
|
|
14
|
+
slack: void 0,
|
|
15
|
+
discord: void 0,
|
|
16
|
+
survey: "email",
|
|
17
|
+
in_app: void 0
|
|
18
|
+
};
|
|
19
|
+
//#endregion
|
|
20
|
+
export { RECIPIENT_PROPERTY };
|
|
21
|
+
|
|
22
|
+
//# sourceMappingURL=recipient.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recipient.mjs","names":[],"sources":["../../src/engine/recipient.ts"],"sourcesContent":["import type { ChannelIR } from '../ir';\n\n/**\n * Channel → the user property holding its recipient address. The engine\n * resolves it before sending, which keeps senders stateless. Push is absent on\n * purpose: its recipients are devices, kept in the store. Slack and Discord post to a team\n * channel through the template's webhook, so they have no per-user address either.\n * TODO: let the app override this table (today it is a convention, with\n * UserPropertyBase guaranteeing `email` exists).\n */\nexport const RECIPIENT_PROPERTY: Record<ChannelIR, string | undefined> = {\n email: 'email',\n sms: 'phone',\n push: undefined,\n slack: undefined,\n discord: undefined,\n survey: 'email',\n in_app: undefined,\n};\n"],"mappings":";;;;;;;;;AAUA,MAAa,qBAA4D;CACvE,OAAO;CACP,KAAK;CACL,MAAM,KAAA;CACN,OAAO,KAAA;CACP,SAAS,KAAA;CACT,QAAQ;CACR,QAAQ,KAAA;AACV"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region src/engine/time-scale.ts
|
|
3
|
+
/**
|
|
4
|
+
* Debug time scale: divide every duration in a workflow's IR by a factor so a
|
|
5
|
+
* journey written in days plays out in seconds — with a factor of 3600, a
|
|
6
|
+
* `delay('2 days')` sleeps 48 s, a `waitUntil(..., { timeout: '3 days' })`
|
|
7
|
+
* gives up after 72 s, and a goal `within: '30 days'` closes after 12 min.
|
|
8
|
+
*
|
|
9
|
+
* Everything that is a Duration scales together (delays, random delays, wait
|
|
10
|
+
* timeouts, goal and `performed(... within)` windows inside the workflow), so
|
|
11
|
+
* the journey keeps its shape, just faster. What does not scale: wall-clock
|
|
12
|
+
* `timeWindow`s (they are times of day, not durations). Segment definitions
|
|
13
|
+
* live in the store rather than the workflow body; `scaleSegmentWindows`
|
|
14
|
+
* scales them as they are read, so a segment's `within` keeps pace too.
|
|
15
|
+
*
|
|
16
|
+
* Durations are recognised structurally — an object whose only keys are
|
|
17
|
+
* `value` (the source text) and `ms` — which is the DurationIR shape and
|
|
18
|
+
* nothing else in the IR. The text is kept so the studio still shows what was
|
|
19
|
+
* written.
|
|
20
|
+
*/
|
|
21
|
+
function scaleDurations(ir, factor) {
|
|
22
|
+
if (!(factor > 0) || factor === 1) return ir;
|
|
23
|
+
return walk(ir, factor);
|
|
24
|
+
}
|
|
25
|
+
function walk(value, factor) {
|
|
26
|
+
if (Array.isArray(value)) return value.map((item) => walk(item, factor));
|
|
27
|
+
if (value === null || typeof value !== "object") return value;
|
|
28
|
+
const record = value;
|
|
29
|
+
const keys = Object.keys(record);
|
|
30
|
+
if (keys.length === 2 && typeof record.value === "string" && typeof record.ms === "number") return {
|
|
31
|
+
value: record.value,
|
|
32
|
+
ms: Math.max(0, Math.round(record.ms / factor))
|
|
33
|
+
};
|
|
34
|
+
return Object.fromEntries(keys.map((key) => [key, walk(record[key], factor)]));
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* A store whose segment definitions come back scaled by `factor` — what journeyWorker reads
|
|
38
|
+
* through in fast-forward, so a segment's windows (`performed(e.login, { within: '7 days' })`)
|
|
39
|
+
* shrink with the journeys that reference it, and with the segment triggers it routes. Every
|
|
40
|
+
* other method is the store's own.
|
|
41
|
+
*/
|
|
42
|
+
function scaleSegmentWindows(store, factor) {
|
|
43
|
+
if (!(factor > 0) || factor === 1) return store;
|
|
44
|
+
return new Proxy(store, { get(target, property) {
|
|
45
|
+
if (property === "getSegmentCondition") return async (name) => scaleDurations(await target.getSegmentCondition(name), factor);
|
|
46
|
+
const value = Reflect.get(target, property, target);
|
|
47
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
48
|
+
} });
|
|
49
|
+
}
|
|
50
|
+
//#endregion
|
|
51
|
+
exports.scaleDurations = scaleDurations;
|
|
52
|
+
exports.scaleSegmentWindows = scaleSegmentWindows;
|
|
53
|
+
|
|
54
|
+
//# sourceMappingURL=time-scale.cjs.map
|