@shware/workflow 0.0.0-stage → 0.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/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
package/dist/client.mjs
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
//#region src/client.ts
|
|
2
|
+
var JourneyClientError = class extends Error {
|
|
3
|
+
path;
|
|
4
|
+
status;
|
|
5
|
+
body;
|
|
6
|
+
constructor(path, status, body) {
|
|
7
|
+
super(`journey ${path} failed: ${status} ${body}`);
|
|
8
|
+
this.path = path;
|
|
9
|
+
this.status = status;
|
|
10
|
+
this.body = body;
|
|
11
|
+
this.name = "JourneyClientError";
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
/** A payload or profile patch the contract refuses — raised before the request, with the field paths. */
|
|
15
|
+
var JourneyValidationError = class extends Error {
|
|
16
|
+
path;
|
|
17
|
+
event;
|
|
18
|
+
issues;
|
|
19
|
+
constructor(path, event, issues) {
|
|
20
|
+
super(`journey ${path}${event === void 0 ? "" : ` '${event}'`} refused by the contract: ${issues.map((i) => `${i.path || "(root)"} ${i.message}`).join("; ")}`);
|
|
21
|
+
this.path = path;
|
|
22
|
+
this.event = event;
|
|
23
|
+
this.issues = issues;
|
|
24
|
+
this.name = "JourneyValidationError";
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
var JourneyClient = class {
|
|
28
|
+
options;
|
|
29
|
+
constructor(options) {
|
|
30
|
+
this.options = options;
|
|
31
|
+
}
|
|
32
|
+
/** Merge profile properties (`null` clears one). Every property a journey reads must be identified before the event that starts it. */
|
|
33
|
+
identify(userId, props) {
|
|
34
|
+
const contract = this.options.contract;
|
|
35
|
+
if (contract !== void 0) {
|
|
36
|
+
const result = contract.parseProfilePatch(props);
|
|
37
|
+
if (!result.ok) return Promise.reject(new JourneyValidationError("/identify", void 0, result.issues));
|
|
38
|
+
}
|
|
39
|
+
return this.post("/identify", {
|
|
40
|
+
userId,
|
|
41
|
+
props
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Record an event; the Worker starts every journey whose trigger matches it
|
|
46
|
+
* and wakes waits on it. A caller that retries should pass the same `id`
|
|
47
|
+
* each time: the Worker stores one occurrence per id, so a retry after a
|
|
48
|
+
* timeout cannot double-count the event. `ts` defaults to the Worker's clock.
|
|
49
|
+
*/
|
|
50
|
+
track(userId, event, ...args) {
|
|
51
|
+
const [payload = {}, options = {}] = args;
|
|
52
|
+
const contract = this.options.contract;
|
|
53
|
+
if (contract !== void 0) {
|
|
54
|
+
const result = contract.parseEvent(event, payload);
|
|
55
|
+
if (!result.ok) return Promise.reject(new JourneyValidationError("/events", event, result.issues));
|
|
56
|
+
}
|
|
57
|
+
return this.post("/events", {
|
|
58
|
+
userId,
|
|
59
|
+
event,
|
|
60
|
+
payload,
|
|
61
|
+
...options.id === void 0 ? {} : { id: options.id },
|
|
62
|
+
...options.ts === void 0 ? {} : { ts: options.ts }
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* A push token the app registered for the user (call it on every registration: a token moves
|
|
67
|
+
* to the latest user). The engine keeps devices itself — pushes go to them, a token APNs / FCM
|
|
68
|
+
* reports as gone is dropped — and journeys waiting on `reachable('push')` wake.
|
|
69
|
+
*/
|
|
70
|
+
registerDevice(userId, device) {
|
|
71
|
+
return this.post("/devices", {
|
|
72
|
+
userId,
|
|
73
|
+
...device
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* A journey push tapped open: the `message_id` the push carried, reported for the user who
|
|
78
|
+
* tapped it. Recorded as a human open on the copy (an email's opens are tracked by the Worker).
|
|
79
|
+
*/
|
|
80
|
+
messageOpened(userId, messageId) {
|
|
81
|
+
return this.post("/messages/opened", {
|
|
82
|
+
userId,
|
|
83
|
+
messageId
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* A deleted account: the engine records the erasure, stops the user's running journeys, then
|
|
88
|
+
* erases everything it keeps about them (profile, devices, events, reporting). Once this
|
|
89
|
+
* resolves the request is durable: `erased: false` means an instance would not stop yet, and
|
|
90
|
+
* the Worker's scheduled sweep finishes the job.
|
|
91
|
+
*/
|
|
92
|
+
forgetUser(userId) {
|
|
93
|
+
return this.post("/users/forget", { userId });
|
|
94
|
+
}
|
|
95
|
+
/** The user signed out on that device: the token stays registered, bound to nobody. */
|
|
96
|
+
unbindDevice(token) {
|
|
97
|
+
return this.post("/devices/unbind", { token });
|
|
98
|
+
}
|
|
99
|
+
/** Forget a push token for good (an uninstall the app learned of, a token it replaced). */
|
|
100
|
+
removeDevice(token) {
|
|
101
|
+
return this.post("/devices/remove", { token });
|
|
102
|
+
}
|
|
103
|
+
/** Deploy a compiled bundle (BundleIR). */
|
|
104
|
+
deploy(bundle) {
|
|
105
|
+
return this.post("/deploy", bundle);
|
|
106
|
+
}
|
|
107
|
+
async post(path, body) {
|
|
108
|
+
const headers = { "content-type": "application/json" };
|
|
109
|
+
if (this.options.token !== void 0) headers.authorization = `Bearer ${this.options.token}`;
|
|
110
|
+
const response = await (this.options.fetch ?? fetch)(`${this.options.origin.replace(/\/$/, "")}${path}`, {
|
|
111
|
+
method: "POST",
|
|
112
|
+
headers,
|
|
113
|
+
body: JSON.stringify(body)
|
|
114
|
+
});
|
|
115
|
+
if (!response.ok) throw new JourneyClientError(path, response.status, await response.text());
|
|
116
|
+
return await response.json();
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
//#endregion
|
|
120
|
+
export { JourneyClient, JourneyClientError, JourneyValidationError };
|
|
121
|
+
|
|
122
|
+
//# sourceMappingURL=client.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.mjs","names":[],"sources":["../src/client.ts"],"sourcesContent":["import type * as core from 'zod/v4/core';\nimport type { DeployResult, IdentifyResult, IngestResult } from './cloudflare';\nimport type { Contract, ContractIssue, ProfilePatch } from './contract';\nimport type { ProfileProps } from './store';\n\n/**\n * `@shware/workflow/client` — the producer side of the ingest API, for the\n * service that knows what users do (an api server, a webhook handler). Plain\n * fetch, no dependencies; errors carry the status and body so a caller can\n * log them without failing the request that triggered the report.\n */\n\nexport interface JourneyClientOptions<C extends Contract | undefined = undefined> {\n /**\n * The shared `defineContract(...)`: types `track` (the event name picks the\n * payload type) and `identify`, and checks both before anything is sent.\n */\n contract?: C;\n /** The journey Worker's origin, e.g. `https://workflow.example.com`. */\n origin: string;\n /** Its API_TOKEN, sent as a bearer; omit against an open local instance. */\n token?: string;\n /** Fetch implementation (defaults to the global one). */\n fetch?: typeof fetch;\n}\n\nexport class JourneyClientError extends Error {\n constructor(\n readonly path: string,\n readonly status: number,\n readonly body: string\n ) {\n super(`journey ${path} failed: ${status} ${body}`);\n this.name = 'JourneyClientError';\n }\n}\n\n/** A payload or profile patch the contract refuses — raised before the request, with the field paths. */\nexport class JourneyValidationError extends Error {\n constructor(\n readonly path: string,\n readonly event: string | undefined,\n readonly issues: ContractIssue[]\n ) {\n super(\n `journey ${path}${event === undefined ? '' : ` '${event}'`} refused by the contract: ${issues\n .map((i) => `${i.path || '(root)'} ${i.message}`)\n .join('; ')}`\n );\n this.name = 'JourneyValidationError';\n }\n}\n\ntype EventsOf<C> = C extends Contract<infer E> ? E : never;\n/** Event names: the contract's, or any string without one. */\nexport type ClientEventName<C> = [C] extends [undefined] ? string : keyof EventsOf<C> & string;\ntype TrackArgs<C, K> = [C] extends [undefined]\n ? [payload?: Record<string, unknown>, options?: TrackOptions]\n : K extends keyof EventsOf<C>\n ? [payload: core.input<EventsOf<C>[K]>, options?: TrackOptions]\n : never;\ntype IdentifyProps<C> = [C] extends [undefined]\n ? ProfileProps\n : C extends Contract<Record<string, core.$ZodObject>, infer P>\n ? ProfilePatch<P>\n : never;\n\nexport interface TrackOptions {\n /** The occurrence's identity: a retry with the same id is recorded once. */\n id?: string;\n /** When it happened (epoch ms); the Worker's clock when omitted. */\n ts?: number;\n}\n\nexport class JourneyClient<C extends Contract | undefined = undefined> {\n constructor(private readonly options: JourneyClientOptions<C>) {}\n\n /** Merge profile properties (`null` clears one). Every property a journey reads must be identified before the event that starts it. */\n identify(userId: string, props: IdentifyProps<C>): Promise<IdentifyResult> {\n const contract = this.options.contract;\n if (contract !== undefined) {\n const result = contract.parseProfilePatch(props);\n if (!result.ok)\n return Promise.reject(new JourneyValidationError('/identify', undefined, result.issues));\n }\n return this.post<IdentifyResult>('/identify', { userId, props });\n }\n\n /**\n * Record an event; the Worker starts every journey whose trigger matches it\n * and wakes waits on it. A caller that retries should pass the same `id`\n * each time: the Worker stores one occurrence per id, so a retry after a\n * timeout cannot double-count the event. `ts` defaults to the Worker's clock.\n */\n track<K extends ClientEventName<C>>(\n userId: string,\n event: K,\n ...args: TrackArgs<C, K>\n ): Promise<IngestResult> {\n const [payload = {}, options = {}] = args as [Record<string, unknown>?, TrackOptions?];\n const contract = this.options.contract;\n if (contract !== undefined) {\n const result = contract.parseEvent(event, payload);\n if (!result.ok)\n return Promise.reject(new JourneyValidationError('/events', event, result.issues));\n }\n return this.post<IngestResult>('/events', {\n userId,\n event,\n payload,\n ...(options.id === undefined ? {} : { id: options.id }),\n ...(options.ts === undefined ? {} : { ts: options.ts }),\n });\n }\n\n /**\n * A push token the app registered for the user (call it on every registration: a token moves\n * to the latest user). The engine keeps devices itself — pushes go to them, a token APNs / FCM\n * reports as gone is dropped — and journeys waiting on `reachable('push')` wake.\n */\n registerDevice(\n userId: string,\n device: { token: string; platform: 'ios' | 'android' | 'web' }\n ): Promise<{ ok: true; woke: number }> {\n return this.post('/devices', { userId, ...device });\n }\n\n /**\n * A journey push tapped open: the `message_id` the push carried, reported for the user who\n * tapped it. Recorded as a human open on the copy (an email's opens are tracked by the Worker).\n */\n messageOpened(userId: string, messageId: string): Promise<{ ok: true; recorded: boolean }> {\n return this.post('/messages/opened', { userId, messageId });\n }\n\n /**\n * A deleted account: the engine records the erasure, stops the user's running journeys, then\n * erases everything it keeps about them (profile, devices, events, reporting). Once this\n * resolves the request is durable: `erased: false` means an instance would not stop yet, and\n * the Worker's scheduled sweep finishes the job.\n */\n forgetUser(userId: string): Promise<{ ok: true; erased: boolean; pending: string[] }> {\n return this.post('/users/forget', { userId });\n }\n\n /** The user signed out on that device: the token stays registered, bound to nobody. */\n unbindDevice(token: string): Promise<{ ok: true; woke: number }> {\n return this.post('/devices/unbind', { token });\n }\n\n /** Forget a push token for good (an uninstall the app learned of, a token it replaced). */\n removeDevice(token: string): Promise<{ ok: true; woke: number }> {\n return this.post('/devices/remove', { token });\n }\n\n /** Deploy a compiled bundle (BundleIR). */\n deploy(bundle: unknown): Promise<DeployResult> {\n return this.post<DeployResult>('/deploy', bundle);\n }\n\n private async post<T>(path: string, body: unknown): Promise<T> {\n const headers: Record<string, string> = { 'content-type': 'application/json' };\n if (this.options.token !== undefined) headers.authorization = `Bearer ${this.options.token}`;\n const doFetch = this.options.fetch ?? fetch;\n const response = await doFetch(`${this.options.origin.replace(/\\/$/, '')}${path}`, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n });\n if (!response.ok) throw new JourneyClientError(path, response.status, await response.text());\n return (await response.json()) as T;\n }\n}\n"],"mappings":";AA0BA,IAAa,qBAAb,cAAwC,MAAM;CAEjC;CACA;CACA;CAHX,YACE,MACA,QACA,MACA;EACA,MAAM,WAAW,KAAK,WAAW,OAAO,GAAG,MAAM;EAJxC,KAAA,OAAA;EACA,KAAA,SAAA;EACA,KAAA,OAAA;EAGT,KAAK,OAAO;CACd;AACF;;AAGA,IAAa,yBAAb,cAA4C,MAAM;CAErC;CACA;CACA;CAHX,YACE,MACA,OACA,QACA;EACA,MACE,WAAW,OAAO,UAAU,KAAA,IAAY,KAAK,KAAK,MAAM,GAAG,4BAA4B,OACpF,KAAK,MAAM,GAAG,EAAE,QAAQ,SAAS,GAAG,EAAE,SAAS,CAAC,CAChD,KAAK,IAAI,GACd;EARS,KAAA,OAAA;EACA,KAAA,QAAA;EACA,KAAA,SAAA;EAOT,KAAK,OAAO;CACd;AACF;AAuBA,IAAa,gBAAb,MAAuE;CACxC;CAA7B,YAAY,SAAmD;EAAlC,KAAA,UAAA;CAAmC;;CAGhE,SAAS,QAAgB,OAAkD;EACzE,MAAM,WAAW,KAAK,QAAQ;EAC9B,IAAI,aAAa,KAAA,GAAW;GAC1B,MAAM,SAAS,SAAS,kBAAkB,KAAK;GAC/C,IAAI,CAAC,OAAO,IACV,OAAO,QAAQ,OAAO,IAAI,uBAAuB,aAAa,KAAA,GAAW,OAAO,MAAM,CAAC;EAC3F;EACA,OAAO,KAAK,KAAqB,aAAa;GAAE;GAAQ;EAAM,CAAC;CACjE;;;;;;;CAQA,MACE,QACA,OACA,GAAG,MACoB;EACvB,MAAM,CAAC,UAAU,CAAC,GAAG,UAAU,CAAC,KAAK;EACrC,MAAM,WAAW,KAAK,QAAQ;EAC9B,IAAI,aAAa,KAAA,GAAW;GAC1B,MAAM,SAAS,SAAS,WAAW,OAAO,OAAO;GACjD,IAAI,CAAC,OAAO,IACV,OAAO,QAAQ,OAAO,IAAI,uBAAuB,WAAW,OAAO,OAAO,MAAM,CAAC;EACrF;EACA,OAAO,KAAK,KAAmB,WAAW;GACxC;GACA;GACA;GACA,GAAI,QAAQ,OAAO,KAAA,IAAY,CAAC,IAAI,EAAE,IAAI,QAAQ,GAAG;GACrD,GAAI,QAAQ,OAAO,KAAA,IAAY,CAAC,IAAI,EAAE,IAAI,QAAQ,GAAG;EACvD,CAAC;CACH;;;;;;CAOA,eACE,QACA,QACqC;EACrC,OAAO,KAAK,KAAK,YAAY;GAAE;GAAQ,GAAG;EAAO,CAAC;CACpD;;;;;CAMA,cAAc,QAAgB,WAA6D;EACzF,OAAO,KAAK,KAAK,oBAAoB;GAAE;GAAQ;EAAU,CAAC;CAC5D;;;;;;;CAQA,WAAW,QAA2E;EACpF,OAAO,KAAK,KAAK,iBAAiB,EAAE,OAAO,CAAC;CAC9C;;CAGA,aAAa,OAAoD;EAC/D,OAAO,KAAK,KAAK,mBAAmB,EAAE,MAAM,CAAC;CAC/C;;CAGA,aAAa,OAAoD;EAC/D,OAAO,KAAK,KAAK,mBAAmB,EAAE,MAAM,CAAC;CAC/C;;CAGA,OAAO,QAAwC;EAC7C,OAAO,KAAK,KAAmB,WAAW,MAAM;CAClD;CAEA,MAAc,KAAQ,MAAc,MAA2B;EAC7D,MAAM,UAAkC,EAAE,gBAAgB,mBAAmB;EAC7E,IAAI,KAAK,QAAQ,UAAU,KAAA,GAAW,QAAQ,gBAAgB,UAAU,KAAK,QAAQ;EAErF,MAAM,WAAW,OADD,KAAK,QAAQ,SAAS,MAAA,CACP,GAAG,KAAK,QAAQ,OAAO,QAAQ,OAAO,EAAE,IAAI,QAAQ;GACjF,QAAQ;GACR;GACA,MAAM,KAAK,UAAU,IAAI;EAC3B,CAAC;EACD,IAAI,CAAC,SAAS,IAAI,MAAM,IAAI,mBAAmB,MAAM,SAAS,QAAQ,MAAM,SAAS,KAAK,CAAC;EAC3F,OAAQ,MAAM,SAAS,KAAK;CAC9B;AACF"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region src/cloudflare/bindings.ts
|
|
3
|
+
/** The event type the router uses to wake waiting instances (sendEvent's `type`). */
|
|
4
|
+
const WAKE_EVENT_TYPE = "wake";
|
|
5
|
+
/**
|
|
6
|
+
* Slack on the deadline when deciding whether a thrown wait was a timeout: the
|
|
7
|
+
* platform may surface the expiry a moment early, and a wait shorter than this
|
|
8
|
+
* has no room to tell the two apart anyway.
|
|
9
|
+
*/
|
|
10
|
+
const WAKE_TIMEOUT_TOLERANCE_MS = 5e3;
|
|
11
|
+
/**
|
|
12
|
+
* Did a wait that threw actually reach its deadline?
|
|
13
|
+
*
|
|
14
|
+
* CF signals a wait timeout by throwing, and so does every other failure in
|
|
15
|
+
* there — a broken binding, an evicted instance, a malformed timeout. The
|
|
16
|
+
* adapter used to read them all as timeouts, which made an infrastructure
|
|
17
|
+
* fault indistinguishable from "the user never did it": the journey took its
|
|
18
|
+
* onTimeout branch, early and silently. Only the clock can tell them apart.
|
|
19
|
+
*/
|
|
20
|
+
function wakeExpired(startedAt, timeoutMs, now) {
|
|
21
|
+
return now - startedAt >= Math.max(0, timeoutMs - WAKE_TIMEOUT_TOLERANCE_MS);
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
exports.WAKE_EVENT_TYPE = WAKE_EVENT_TYPE;
|
|
25
|
+
exports.WAKE_TIMEOUT_TOLERANCE_MS = WAKE_TIMEOUT_TOLERANCE_MS;
|
|
26
|
+
exports.wakeExpired = wakeExpired;
|
|
27
|
+
|
|
28
|
+
//# sourceMappingURL=bindings.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bindings.cjs","names":[],"sources":["../../src/cloudflare/bindings.ts"],"sourcesContent":["import type { EmailAddress } from '../config';\n\n/**\n * A minimal structural interface over the Cloudflare bindings.\n *\n * @cloudflare/workers-types is deliberately not used: the global types it\n * injects clash with the DOM lib this package's react side needs. A structural\n * subset plus the ambient cloudflare:workers declaration is enough to compile,\n * and the real bindings satisfy it structurally at runtime.\n */\n\nexport interface KVNamespaceLike {\n get(key: string): Promise<string | null>;\n put(key: string, value: string): Promise<void>;\n}\n\n/** Structural subset of D1Result: `meta.changes` is how INSERT OR IGNORE reports whether it inserted. */\nexport interface D1RunResultLike {\n meta?: { changes?: number };\n}\n\nexport interface D1PreparedStatementLike {\n bind(...values: unknown[]): D1PreparedStatementLike;\n first<T = Record<string, unknown>>(): Promise<T | null>;\n all<T = Record<string, unknown>>(): Promise<{ results: T[] }>;\n run(): Promise<D1RunResultLike>;\n}\n\nexport interface D1DatabaseLike {\n prepare(sql: string): D1PreparedStatementLike;\n /** Statements run in order inside one transaction (real D1 semantics) — deploy relies on the atomicity. */\n batch(statements: D1PreparedStatementLike[]): Promise<unknown>;\n}\n\nexport interface WorkflowInstanceLike {\n id: string;\n sendEvent(event: { type: string; payload?: unknown }): Promise<void>;\n /** Stop the instance for good (Cloudflare Workflows' `terminate`). */\n terminate?(): Promise<void>;\n /** Where it stands (`complete`, `errored`, `terminated`, `running`, …). */\n status?(): Promise<{ status: string }>;\n}\n\nexport interface WorkflowBindingLike {\n create(options: { id: string; params: unknown }): Promise<WorkflowInstanceLike>;\n get(id: string): Promise<WorkflowInstanceLike>;\n}\n\n/**\n * Cloudflare Email Service's send_email binding (structural subset).\n *\n * `idempotencyKey` is part of the call because the send happens inside a\n * `step.do`: a step body re-runs after its fn resolved but before the\n * checkpoint committed, so a binding that drops the key mails the user twice.\n * The binding is app-supplied glue (nothing here can de-duplicate — there is\n * no state between two runs of the same step), which is why the port makes the\n * key impossible to miss rather than merely available.\n */\nexport interface EmailBindingLike {\n send(message: {\n from: EmailAddress;\n to: EmailAddress;\n replyTo?: EmailAddress;\n subject: string;\n html: string;\n /** `${instanceId}:${nodeId}`: stable across replays and retries — drop a send whose key was already delivered. */\n idempotencyKey: string;\n }): Promise<unknown>;\n}\n\n/** Every binding the journey engine needs (names match the wrangler config). */\nexport interface JourneyEnv {\n /**\n * BundleIR storage for the default D1 store (`wf:${contentHash}` → WorkflowIR JSON). Optional:\n * a Worker on another JourneyStore (Postgres, say) does not declare it.\n */\n WORKFLOW_KV?: KVNamespaceLike;\n /** Tables of the default D1 store (events / profiles / segments / triggers / entries / subscriptions). Optional, as above. */\n DB?: D1DatabaseLike;\n /** JourneyRunner's workflow binding (creating instances and waking them). */\n JOURNEY: WorkflowBindingLike;\n /** Optional message-delivery webhook (defaults to the console logging sender). */\n MESSAGE_WEBHOOK_URL?: string;\n /**\n * Bearer token required on every mutating HTTP endpoint (`Authorization:\n * Bearer <token>`). Unset = open — acceptable only for local dev; /deploy in\n * particular rewrites the whole routing table, so production must set this.\n */\n API_TOKEN?: string;\n /**\n * This Worker's public origin. With a store that keeps reporting (Postgres), journey emails'\n * links and open pixel point at its `/t/...` routes; without it emails go out untracked.\n */\n WORKFLOW_ORIGIN?: string;\n /** Which `workflow.config.ts` entry applies (`development`, `production`, …); production when unset. */\n ENVIRONMENT?: string;\n /** Cloudflare Email Sending, when the config has `emails`. */\n EMAIL?: EmailBindingLike;\n /** The APNs auth key (`.p8`, base64) named by the config's `apns.keyId`. */\n APNS_PRIVATE_KEY_BASE64?: string;\n /** The Firebase service account (base64 JSON) the config's `fcm` sends with. */\n GOOGLE_APPLICATION_CREDENTIALS_BASE64?: string;\n /** The VAPID key pair web pushes are signed with (`generateVapidKeys`); the config's `webPush` turns the channel on. */\n VAPID_PUBLIC_KEY?: string;\n VAPID_PRIVATE_KEY?: string;\n /** Development only: the HTTP/2 relay `wrangler dev` sends APNs requests through (see `@shware/workflow/apns-relay`). */\n APNS_RELAY_ORIGIN?: string;\n}\n\n/** The event type the router uses to wake waiting instances (sendEvent's `type`). */\nexport const WAKE_EVENT_TYPE = 'wake';\n\n/** Journey instance parameters, passed by the ingest router when it creates one. */\nexport interface JourneyParams {\n workflowId: string;\n /** The IR version pinned at entry. */\n contentHash: string;\n userId: string;\n trigger: { event: string; payload: Record<string, unknown> };\n [key: string]: unknown;\n}\n\n/**\n * Slack on the deadline when deciding whether a thrown wait was a timeout: the\n * platform may surface the expiry a moment early, and a wait shorter than this\n * has no room to tell the two apart anyway.\n */\nexport const WAKE_TIMEOUT_TOLERANCE_MS = 5_000;\n\n/**\n * Did a wait that threw actually reach its deadline?\n *\n * CF signals a wait timeout by throwing, and so does every other failure in\n * there — a broken binding, an evicted instance, a malformed timeout. The\n * adapter used to read them all as timeouts, which made an infrastructure\n * fault indistinguishable from \"the user never did it\": the journey took its\n * onTimeout branch, early and silently. Only the clock can tell them apart.\n */\nexport function wakeExpired(startedAt: number, timeoutMs: number, now: number): boolean {\n return now - startedAt >= Math.max(0, timeoutMs - WAKE_TIMEOUT_TOLERANCE_MS);\n}\n"],"mappings":";;;AA8GA,MAAa,kBAAkB;;;;;;AAiB/B,MAAa,4BAA4B;;;;;;;;;;AAWzC,SAAgB,YAAY,WAAmB,WAAmB,KAAsB;CACtF,OAAO,MAAM,aAAa,KAAK,IAAI,GAAG,YAAY,yBAAyB;AAC7E"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { EmailAddress } from "../config.cjs";
|
|
2
|
+
//#region src/cloudflare/bindings.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* A minimal structural interface over the Cloudflare bindings.
|
|
5
|
+
*
|
|
6
|
+
* @cloudflare/workers-types is deliberately not used: the global types it
|
|
7
|
+
* injects clash with the DOM lib this package's react side needs. A structural
|
|
8
|
+
* subset plus the ambient cloudflare:workers declaration is enough to compile,
|
|
9
|
+
* and the real bindings satisfy it structurally at runtime.
|
|
10
|
+
*/
|
|
11
|
+
export interface KVNamespaceLike {
|
|
12
|
+
get(key: string): Promise<string | null>;
|
|
13
|
+
put(key: string, value: string): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
/** Structural subset of D1Result: `meta.changes` is how INSERT OR IGNORE reports whether it inserted. */
|
|
16
|
+
export interface D1RunResultLike {
|
|
17
|
+
meta?: {
|
|
18
|
+
changes?: number;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export interface D1PreparedStatementLike {
|
|
22
|
+
bind(...values: unknown[]): D1PreparedStatementLike;
|
|
23
|
+
first<T = Record<string, unknown>>(): Promise<T | null>;
|
|
24
|
+
all<T = Record<string, unknown>>(): Promise<{
|
|
25
|
+
results: T[];
|
|
26
|
+
}>;
|
|
27
|
+
run(): Promise<D1RunResultLike>;
|
|
28
|
+
}
|
|
29
|
+
export interface D1DatabaseLike {
|
|
30
|
+
prepare(sql: string): D1PreparedStatementLike;
|
|
31
|
+
/** Statements run in order inside one transaction (real D1 semantics) — deploy relies on the atomicity. */
|
|
32
|
+
batch(statements: D1PreparedStatementLike[]): Promise<unknown>;
|
|
33
|
+
}
|
|
34
|
+
export interface WorkflowInstanceLike {
|
|
35
|
+
id: string;
|
|
36
|
+
sendEvent(event: {
|
|
37
|
+
type: string;
|
|
38
|
+
payload?: unknown;
|
|
39
|
+
}): Promise<void>;
|
|
40
|
+
/** Stop the instance for good (Cloudflare Workflows' `terminate`). */
|
|
41
|
+
terminate?(): Promise<void>;
|
|
42
|
+
/** Where it stands (`complete`, `errored`, `terminated`, `running`, …). */
|
|
43
|
+
status?(): Promise<{
|
|
44
|
+
status: string;
|
|
45
|
+
}>;
|
|
46
|
+
}
|
|
47
|
+
export interface WorkflowBindingLike {
|
|
48
|
+
create(options: {
|
|
49
|
+
id: string;
|
|
50
|
+
params: unknown;
|
|
51
|
+
}): Promise<WorkflowInstanceLike>;
|
|
52
|
+
get(id: string): Promise<WorkflowInstanceLike>;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Cloudflare Email Service's send_email binding (structural subset).
|
|
56
|
+
*
|
|
57
|
+
* `idempotencyKey` is part of the call because the send happens inside a
|
|
58
|
+
* `step.do`: a step body re-runs after its fn resolved but before the
|
|
59
|
+
* checkpoint committed, so a binding that drops the key mails the user twice.
|
|
60
|
+
* The binding is app-supplied glue (nothing here can de-duplicate — there is
|
|
61
|
+
* no state between two runs of the same step), which is why the port makes the
|
|
62
|
+
* key impossible to miss rather than merely available.
|
|
63
|
+
*/
|
|
64
|
+
export interface EmailBindingLike {
|
|
65
|
+
send(message: {
|
|
66
|
+
from: EmailAddress;
|
|
67
|
+
to: EmailAddress;
|
|
68
|
+
replyTo?: EmailAddress;
|
|
69
|
+
subject: string;
|
|
70
|
+
html: string;
|
|
71
|
+
/** `${instanceId}:${nodeId}`: stable across replays and retries — drop a send whose key was already delivered. */
|
|
72
|
+
idempotencyKey: string;
|
|
73
|
+
}): Promise<unknown>;
|
|
74
|
+
}
|
|
75
|
+
/** Every binding the journey engine needs (names match the wrangler config). */
|
|
76
|
+
export interface JourneyEnv {
|
|
77
|
+
/**
|
|
78
|
+
* BundleIR storage for the default D1 store (`wf:${contentHash}` → WorkflowIR JSON). Optional:
|
|
79
|
+
* a Worker on another JourneyStore (Postgres, say) does not declare it.
|
|
80
|
+
*/
|
|
81
|
+
WORKFLOW_KV?: KVNamespaceLike;
|
|
82
|
+
/** Tables of the default D1 store (events / profiles / segments / triggers / entries / subscriptions). Optional, as above. */
|
|
83
|
+
DB?: D1DatabaseLike;
|
|
84
|
+
/** JourneyRunner's workflow binding (creating instances and waking them). */
|
|
85
|
+
JOURNEY: WorkflowBindingLike;
|
|
86
|
+
/** Optional message-delivery webhook (defaults to the console logging sender). */
|
|
87
|
+
MESSAGE_WEBHOOK_URL?: string;
|
|
88
|
+
/**
|
|
89
|
+
* Bearer token required on every mutating HTTP endpoint (`Authorization:
|
|
90
|
+
* Bearer <token>`). Unset = open — acceptable only for local dev; /deploy in
|
|
91
|
+
* particular rewrites the whole routing table, so production must set this.
|
|
92
|
+
*/
|
|
93
|
+
API_TOKEN?: string;
|
|
94
|
+
/**
|
|
95
|
+
* This Worker's public origin. With a store that keeps reporting (Postgres), journey emails'
|
|
96
|
+
* links and open pixel point at its `/t/...` routes; without it emails go out untracked.
|
|
97
|
+
*/
|
|
98
|
+
WORKFLOW_ORIGIN?: string;
|
|
99
|
+
/** Which `workflow.config.ts` entry applies (`development`, `production`, …); production when unset. */
|
|
100
|
+
ENVIRONMENT?: string;
|
|
101
|
+
/** Cloudflare Email Sending, when the config has `emails`. */
|
|
102
|
+
EMAIL?: EmailBindingLike;
|
|
103
|
+
/** The APNs auth key (`.p8`, base64) named by the config's `apns.keyId`. */
|
|
104
|
+
APNS_PRIVATE_KEY_BASE64?: string;
|
|
105
|
+
/** The Firebase service account (base64 JSON) the config's `fcm` sends with. */
|
|
106
|
+
GOOGLE_APPLICATION_CREDENTIALS_BASE64?: string;
|
|
107
|
+
/** The VAPID key pair web pushes are signed with (`generateVapidKeys`); the config's `webPush` turns the channel on. */
|
|
108
|
+
VAPID_PUBLIC_KEY?: string;
|
|
109
|
+
VAPID_PRIVATE_KEY?: string;
|
|
110
|
+
/** Development only: the HTTP/2 relay `wrangler dev` sends APNs requests through (see `@shware/workflow/apns-relay`). */
|
|
111
|
+
APNS_RELAY_ORIGIN?: string;
|
|
112
|
+
}
|
|
113
|
+
/** The event type the router uses to wake waiting instances (sendEvent's `type`). */
|
|
114
|
+
export declare const WAKE_EVENT_TYPE = "wake";
|
|
115
|
+
/** Journey instance parameters, passed by the ingest router when it creates one. */
|
|
116
|
+
export interface JourneyParams {
|
|
117
|
+
workflowId: string;
|
|
118
|
+
/** The IR version pinned at entry. */
|
|
119
|
+
contentHash: string;
|
|
120
|
+
userId: string;
|
|
121
|
+
trigger: {
|
|
122
|
+
event: string;
|
|
123
|
+
payload: Record<string, unknown>;
|
|
124
|
+
};
|
|
125
|
+
[key: string]: unknown;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Slack on the deadline when deciding whether a thrown wait was a timeout: the
|
|
129
|
+
* platform may surface the expiry a moment early, and a wait shorter than this
|
|
130
|
+
* has no room to tell the two apart anyway.
|
|
131
|
+
*/
|
|
132
|
+
export declare const WAKE_TIMEOUT_TOLERANCE_MS = 5000;
|
|
133
|
+
/**
|
|
134
|
+
* Did a wait that threw actually reach its deadline?
|
|
135
|
+
*
|
|
136
|
+
* CF signals a wait timeout by throwing, and so does every other failure in
|
|
137
|
+
* there — a broken binding, an evicted instance, a malformed timeout. The
|
|
138
|
+
* adapter used to read them all as timeouts, which made an infrastructure
|
|
139
|
+
* fault indistinguishable from "the user never did it": the journey took its
|
|
140
|
+
* onTimeout branch, early and silently. Only the clock can tell them apart.
|
|
141
|
+
*/
|
|
142
|
+
export declare function wakeExpired(startedAt: number, timeoutMs: number, now: number): boolean;
|
|
143
|
+
//#endregion
|
|
144
|
+
//# sourceMappingURL=bindings.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bindings.d.cts","names":[],"sources":["../../src/cloudflare/bindings.ts"],"mappings":";;;;;;;;;;iBAWiB;EACf,IAAI,cAAc;EAClB,IAAI,aAAa,gBAAgB;;;iBAIlB;EACf;IAAS;;;iBAGM;EACf,QAAQ,oBAAoB;EAC5B,MAAM,IAAI,4BAA4B,QAAQ;EAC9C,IAAI,IAAI,4BAA4B;IAAU,SAAS;;EACvD,OAAO,QAAQ;;iBAGA;EACf,QAAQ,cAAc;;EAEtB,MAAM,YAAY,4BAA4B;;iBAG/B;EACf;EACA,UAAU;IAAS;IAAc;MAAsB;;EAEvD,cAAc;;EAEd,WAAW;IAAU;;;iBAGN;EACf,OAAO;IAAW;IAAY;MAAoB,QAAQ;EAC1D,IAAI,aAAa,QAAQ;;;;;;;;;;;;iBAaV;EACf,KAAK;IACH,MAAM;IACN,IAAI;IACJ,UAAU;IACV;IACA;;IAEA;MACE;;;iBAIW;;;;;EAKf,cAAc;;EAEd,KAAK;;EAEL,SAAS;;EAET;;;;;;EAMA;;;;;EAKA;;EAEA;;EAEA,QAAQ;;EAER;;EAEA;;EAEA;EACA;;EAEA;;;qBAIW;;iBAGI;EACf;;EAEA;EACA;EACA;IAAW;IAAe,SAAS;;GAClC;;;;;;;qBAQU;;;;;;;;;;wBAWG,YAAY,mBAAmB,mBAAmB"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { EmailAddress } from "../config.mjs";
|
|
2
|
+
//#region src/cloudflare/bindings.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* A minimal structural interface over the Cloudflare bindings.
|
|
5
|
+
*
|
|
6
|
+
* @cloudflare/workers-types is deliberately not used: the global types it
|
|
7
|
+
* injects clash with the DOM lib this package's react side needs. A structural
|
|
8
|
+
* subset plus the ambient cloudflare:workers declaration is enough to compile,
|
|
9
|
+
* and the real bindings satisfy it structurally at runtime.
|
|
10
|
+
*/
|
|
11
|
+
export interface KVNamespaceLike {
|
|
12
|
+
get(key: string): Promise<string | null>;
|
|
13
|
+
put(key: string, value: string): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
/** Structural subset of D1Result: `meta.changes` is how INSERT OR IGNORE reports whether it inserted. */
|
|
16
|
+
export interface D1RunResultLike {
|
|
17
|
+
meta?: {
|
|
18
|
+
changes?: number;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export interface D1PreparedStatementLike {
|
|
22
|
+
bind(...values: unknown[]): D1PreparedStatementLike;
|
|
23
|
+
first<T = Record<string, unknown>>(): Promise<T | null>;
|
|
24
|
+
all<T = Record<string, unknown>>(): Promise<{
|
|
25
|
+
results: T[];
|
|
26
|
+
}>;
|
|
27
|
+
run(): Promise<D1RunResultLike>;
|
|
28
|
+
}
|
|
29
|
+
export interface D1DatabaseLike {
|
|
30
|
+
prepare(sql: string): D1PreparedStatementLike;
|
|
31
|
+
/** Statements run in order inside one transaction (real D1 semantics) — deploy relies on the atomicity. */
|
|
32
|
+
batch(statements: D1PreparedStatementLike[]): Promise<unknown>;
|
|
33
|
+
}
|
|
34
|
+
export interface WorkflowInstanceLike {
|
|
35
|
+
id: string;
|
|
36
|
+
sendEvent(event: {
|
|
37
|
+
type: string;
|
|
38
|
+
payload?: unknown;
|
|
39
|
+
}): Promise<void>;
|
|
40
|
+
/** Stop the instance for good (Cloudflare Workflows' `terminate`). */
|
|
41
|
+
terminate?(): Promise<void>;
|
|
42
|
+
/** Where it stands (`complete`, `errored`, `terminated`, `running`, …). */
|
|
43
|
+
status?(): Promise<{
|
|
44
|
+
status: string;
|
|
45
|
+
}>;
|
|
46
|
+
}
|
|
47
|
+
export interface WorkflowBindingLike {
|
|
48
|
+
create(options: {
|
|
49
|
+
id: string;
|
|
50
|
+
params: unknown;
|
|
51
|
+
}): Promise<WorkflowInstanceLike>;
|
|
52
|
+
get(id: string): Promise<WorkflowInstanceLike>;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Cloudflare Email Service's send_email binding (structural subset).
|
|
56
|
+
*
|
|
57
|
+
* `idempotencyKey` is part of the call because the send happens inside a
|
|
58
|
+
* `step.do`: a step body re-runs after its fn resolved but before the
|
|
59
|
+
* checkpoint committed, so a binding that drops the key mails the user twice.
|
|
60
|
+
* The binding is app-supplied glue (nothing here can de-duplicate — there is
|
|
61
|
+
* no state between two runs of the same step), which is why the port makes the
|
|
62
|
+
* key impossible to miss rather than merely available.
|
|
63
|
+
*/
|
|
64
|
+
export interface EmailBindingLike {
|
|
65
|
+
send(message: {
|
|
66
|
+
from: EmailAddress;
|
|
67
|
+
to: EmailAddress;
|
|
68
|
+
replyTo?: EmailAddress;
|
|
69
|
+
subject: string;
|
|
70
|
+
html: string;
|
|
71
|
+
/** `${instanceId}:${nodeId}`: stable across replays and retries — drop a send whose key was already delivered. */
|
|
72
|
+
idempotencyKey: string;
|
|
73
|
+
}): Promise<unknown>;
|
|
74
|
+
}
|
|
75
|
+
/** Every binding the journey engine needs (names match the wrangler config). */
|
|
76
|
+
export interface JourneyEnv {
|
|
77
|
+
/**
|
|
78
|
+
* BundleIR storage for the default D1 store (`wf:${contentHash}` → WorkflowIR JSON). Optional:
|
|
79
|
+
* a Worker on another JourneyStore (Postgres, say) does not declare it.
|
|
80
|
+
*/
|
|
81
|
+
WORKFLOW_KV?: KVNamespaceLike;
|
|
82
|
+
/** Tables of the default D1 store (events / profiles / segments / triggers / entries / subscriptions). Optional, as above. */
|
|
83
|
+
DB?: D1DatabaseLike;
|
|
84
|
+
/** JourneyRunner's workflow binding (creating instances and waking them). */
|
|
85
|
+
JOURNEY: WorkflowBindingLike;
|
|
86
|
+
/** Optional message-delivery webhook (defaults to the console logging sender). */
|
|
87
|
+
MESSAGE_WEBHOOK_URL?: string;
|
|
88
|
+
/**
|
|
89
|
+
* Bearer token required on every mutating HTTP endpoint (`Authorization:
|
|
90
|
+
* Bearer <token>`). Unset = open — acceptable only for local dev; /deploy in
|
|
91
|
+
* particular rewrites the whole routing table, so production must set this.
|
|
92
|
+
*/
|
|
93
|
+
API_TOKEN?: string;
|
|
94
|
+
/**
|
|
95
|
+
* This Worker's public origin. With a store that keeps reporting (Postgres), journey emails'
|
|
96
|
+
* links and open pixel point at its `/t/...` routes; without it emails go out untracked.
|
|
97
|
+
*/
|
|
98
|
+
WORKFLOW_ORIGIN?: string;
|
|
99
|
+
/** Which `workflow.config.ts` entry applies (`development`, `production`, …); production when unset. */
|
|
100
|
+
ENVIRONMENT?: string;
|
|
101
|
+
/** Cloudflare Email Sending, when the config has `emails`. */
|
|
102
|
+
EMAIL?: EmailBindingLike;
|
|
103
|
+
/** The APNs auth key (`.p8`, base64) named by the config's `apns.keyId`. */
|
|
104
|
+
APNS_PRIVATE_KEY_BASE64?: string;
|
|
105
|
+
/** The Firebase service account (base64 JSON) the config's `fcm` sends with. */
|
|
106
|
+
GOOGLE_APPLICATION_CREDENTIALS_BASE64?: string;
|
|
107
|
+
/** The VAPID key pair web pushes are signed with (`generateVapidKeys`); the config's `webPush` turns the channel on. */
|
|
108
|
+
VAPID_PUBLIC_KEY?: string;
|
|
109
|
+
VAPID_PRIVATE_KEY?: string;
|
|
110
|
+
/** Development only: the HTTP/2 relay `wrangler dev` sends APNs requests through (see `@shware/workflow/apns-relay`). */
|
|
111
|
+
APNS_RELAY_ORIGIN?: string;
|
|
112
|
+
}
|
|
113
|
+
/** The event type the router uses to wake waiting instances (sendEvent's `type`). */
|
|
114
|
+
export declare const WAKE_EVENT_TYPE = "wake";
|
|
115
|
+
/** Journey instance parameters, passed by the ingest router when it creates one. */
|
|
116
|
+
export interface JourneyParams {
|
|
117
|
+
workflowId: string;
|
|
118
|
+
/** The IR version pinned at entry. */
|
|
119
|
+
contentHash: string;
|
|
120
|
+
userId: string;
|
|
121
|
+
trigger: {
|
|
122
|
+
event: string;
|
|
123
|
+
payload: Record<string, unknown>;
|
|
124
|
+
};
|
|
125
|
+
[key: string]: unknown;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Slack on the deadline when deciding whether a thrown wait was a timeout: the
|
|
129
|
+
* platform may surface the expiry a moment early, and a wait shorter than this
|
|
130
|
+
* has no room to tell the two apart anyway.
|
|
131
|
+
*/
|
|
132
|
+
export declare const WAKE_TIMEOUT_TOLERANCE_MS = 5000;
|
|
133
|
+
/**
|
|
134
|
+
* Did a wait that threw actually reach its deadline?
|
|
135
|
+
*
|
|
136
|
+
* CF signals a wait timeout by throwing, and so does every other failure in
|
|
137
|
+
* there — a broken binding, an evicted instance, a malformed timeout. The
|
|
138
|
+
* adapter used to read them all as timeouts, which made an infrastructure
|
|
139
|
+
* fault indistinguishable from "the user never did it": the journey took its
|
|
140
|
+
* onTimeout branch, early and silently. Only the clock can tell them apart.
|
|
141
|
+
*/
|
|
142
|
+
export declare function wakeExpired(startedAt: number, timeoutMs: number, now: number): boolean;
|
|
143
|
+
//#endregion
|
|
144
|
+
//# sourceMappingURL=bindings.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bindings.d.mts","names":[],"sources":["../../src/cloudflare/bindings.ts"],"mappings":";;;;;;;;;;iBAWiB;EACf,IAAI,cAAc;EAClB,IAAI,aAAa,gBAAgB;;;iBAIlB;EACf;IAAS;;;iBAGM;EACf,QAAQ,oBAAoB;EAC5B,MAAM,IAAI,4BAA4B,QAAQ;EAC9C,IAAI,IAAI,4BAA4B;IAAU,SAAS;;EACvD,OAAO,QAAQ;;iBAGA;EACf,QAAQ,cAAc;;EAEtB,MAAM,YAAY,4BAA4B;;iBAG/B;EACf;EACA,UAAU;IAAS;IAAc;MAAsB;;EAEvD,cAAc;;EAEd,WAAW;IAAU;;;iBAGN;EACf,OAAO;IAAW;IAAY;MAAoB,QAAQ;EAC1D,IAAI,aAAa,QAAQ;;;;;;;;;;;;iBAaV;EACf,KAAK;IACH,MAAM;IACN,IAAI;IACJ,UAAU;IACV;IACA;;IAEA;MACE;;;iBAIW;;;;;EAKf,cAAc;;EAEd,KAAK;;EAEL,SAAS;;EAET;;;;;;EAMA;;;;;EAKA;;EAEA;;EAEA,QAAQ;;EAER;;EAEA;;EAEA;EACA;;EAEA;;;qBAIW;;iBAGI;EACf;;EAEA;EACA;EACA;IAAW;IAAe,SAAS;;GAClC;;;;;;;qBAQU;;;;;;;;;;wBAWG,YAAY,mBAAmB,mBAAmB"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//#region src/cloudflare/bindings.ts
|
|
2
|
+
/** The event type the router uses to wake waiting instances (sendEvent's `type`). */
|
|
3
|
+
const WAKE_EVENT_TYPE = "wake";
|
|
4
|
+
/**
|
|
5
|
+
* Slack on the deadline when deciding whether a thrown wait was a timeout: the
|
|
6
|
+
* platform may surface the expiry a moment early, and a wait shorter than this
|
|
7
|
+
* has no room to tell the two apart anyway.
|
|
8
|
+
*/
|
|
9
|
+
const WAKE_TIMEOUT_TOLERANCE_MS = 5e3;
|
|
10
|
+
/**
|
|
11
|
+
* Did a wait that threw actually reach its deadline?
|
|
12
|
+
*
|
|
13
|
+
* CF signals a wait timeout by throwing, and so does every other failure in
|
|
14
|
+
* there — a broken binding, an evicted instance, a malformed timeout. The
|
|
15
|
+
* adapter used to read them all as timeouts, which made an infrastructure
|
|
16
|
+
* fault indistinguishable from "the user never did it": the journey took its
|
|
17
|
+
* onTimeout branch, early and silently. Only the clock can tell them apart.
|
|
18
|
+
*/
|
|
19
|
+
function wakeExpired(startedAt, timeoutMs, now) {
|
|
20
|
+
return now - startedAt >= Math.max(0, timeoutMs - WAKE_TIMEOUT_TOLERANCE_MS);
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
export { WAKE_EVENT_TYPE, WAKE_TIMEOUT_TOLERANCE_MS, wakeExpired };
|
|
24
|
+
|
|
25
|
+
//# sourceMappingURL=bindings.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bindings.mjs","names":[],"sources":["../../src/cloudflare/bindings.ts"],"sourcesContent":["import type { EmailAddress } from '../config';\n\n/**\n * A minimal structural interface over the Cloudflare bindings.\n *\n * @cloudflare/workers-types is deliberately not used: the global types it\n * injects clash with the DOM lib this package's react side needs. A structural\n * subset plus the ambient cloudflare:workers declaration is enough to compile,\n * and the real bindings satisfy it structurally at runtime.\n */\n\nexport interface KVNamespaceLike {\n get(key: string): Promise<string | null>;\n put(key: string, value: string): Promise<void>;\n}\n\n/** Structural subset of D1Result: `meta.changes` is how INSERT OR IGNORE reports whether it inserted. */\nexport interface D1RunResultLike {\n meta?: { changes?: number };\n}\n\nexport interface D1PreparedStatementLike {\n bind(...values: unknown[]): D1PreparedStatementLike;\n first<T = Record<string, unknown>>(): Promise<T | null>;\n all<T = Record<string, unknown>>(): Promise<{ results: T[] }>;\n run(): Promise<D1RunResultLike>;\n}\n\nexport interface D1DatabaseLike {\n prepare(sql: string): D1PreparedStatementLike;\n /** Statements run in order inside one transaction (real D1 semantics) — deploy relies on the atomicity. */\n batch(statements: D1PreparedStatementLike[]): Promise<unknown>;\n}\n\nexport interface WorkflowInstanceLike {\n id: string;\n sendEvent(event: { type: string; payload?: unknown }): Promise<void>;\n /** Stop the instance for good (Cloudflare Workflows' `terminate`). */\n terminate?(): Promise<void>;\n /** Where it stands (`complete`, `errored`, `terminated`, `running`, …). */\n status?(): Promise<{ status: string }>;\n}\n\nexport interface WorkflowBindingLike {\n create(options: { id: string; params: unknown }): Promise<WorkflowInstanceLike>;\n get(id: string): Promise<WorkflowInstanceLike>;\n}\n\n/**\n * Cloudflare Email Service's send_email binding (structural subset).\n *\n * `idempotencyKey` is part of the call because the send happens inside a\n * `step.do`: a step body re-runs after its fn resolved but before the\n * checkpoint committed, so a binding that drops the key mails the user twice.\n * The binding is app-supplied glue (nothing here can de-duplicate — there is\n * no state between two runs of the same step), which is why the port makes the\n * key impossible to miss rather than merely available.\n */\nexport interface EmailBindingLike {\n send(message: {\n from: EmailAddress;\n to: EmailAddress;\n replyTo?: EmailAddress;\n subject: string;\n html: string;\n /** `${instanceId}:${nodeId}`: stable across replays and retries — drop a send whose key was already delivered. */\n idempotencyKey: string;\n }): Promise<unknown>;\n}\n\n/** Every binding the journey engine needs (names match the wrangler config). */\nexport interface JourneyEnv {\n /**\n * BundleIR storage for the default D1 store (`wf:${contentHash}` → WorkflowIR JSON). Optional:\n * a Worker on another JourneyStore (Postgres, say) does not declare it.\n */\n WORKFLOW_KV?: KVNamespaceLike;\n /** Tables of the default D1 store (events / profiles / segments / triggers / entries / subscriptions). Optional, as above. */\n DB?: D1DatabaseLike;\n /** JourneyRunner's workflow binding (creating instances and waking them). */\n JOURNEY: WorkflowBindingLike;\n /** Optional message-delivery webhook (defaults to the console logging sender). */\n MESSAGE_WEBHOOK_URL?: string;\n /**\n * Bearer token required on every mutating HTTP endpoint (`Authorization:\n * Bearer <token>`). Unset = open — acceptable only for local dev; /deploy in\n * particular rewrites the whole routing table, so production must set this.\n */\n API_TOKEN?: string;\n /**\n * This Worker's public origin. With a store that keeps reporting (Postgres), journey emails'\n * links and open pixel point at its `/t/...` routes; without it emails go out untracked.\n */\n WORKFLOW_ORIGIN?: string;\n /** Which `workflow.config.ts` entry applies (`development`, `production`, …); production when unset. */\n ENVIRONMENT?: string;\n /** Cloudflare Email Sending, when the config has `emails`. */\n EMAIL?: EmailBindingLike;\n /** The APNs auth key (`.p8`, base64) named by the config's `apns.keyId`. */\n APNS_PRIVATE_KEY_BASE64?: string;\n /** The Firebase service account (base64 JSON) the config's `fcm` sends with. */\n GOOGLE_APPLICATION_CREDENTIALS_BASE64?: string;\n /** The VAPID key pair web pushes are signed with (`generateVapidKeys`); the config's `webPush` turns the channel on. */\n VAPID_PUBLIC_KEY?: string;\n VAPID_PRIVATE_KEY?: string;\n /** Development only: the HTTP/2 relay `wrangler dev` sends APNs requests through (see `@shware/workflow/apns-relay`). */\n APNS_RELAY_ORIGIN?: string;\n}\n\n/** The event type the router uses to wake waiting instances (sendEvent's `type`). */\nexport const WAKE_EVENT_TYPE = 'wake';\n\n/** Journey instance parameters, passed by the ingest router when it creates one. */\nexport interface JourneyParams {\n workflowId: string;\n /** The IR version pinned at entry. */\n contentHash: string;\n userId: string;\n trigger: { event: string; payload: Record<string, unknown> };\n [key: string]: unknown;\n}\n\n/**\n * Slack on the deadline when deciding whether a thrown wait was a timeout: the\n * platform may surface the expiry a moment early, and a wait shorter than this\n * has no room to tell the two apart anyway.\n */\nexport const WAKE_TIMEOUT_TOLERANCE_MS = 5_000;\n\n/**\n * Did a wait that threw actually reach its deadline?\n *\n * CF signals a wait timeout by throwing, and so does every other failure in\n * there — a broken binding, an evicted instance, a malformed timeout. The\n * adapter used to read them all as timeouts, which made an infrastructure\n * fault indistinguishable from \"the user never did it\": the journey took its\n * onTimeout branch, early and silently. Only the clock can tell them apart.\n */\nexport function wakeExpired(startedAt: number, timeoutMs: number, now: number): boolean {\n return now - startedAt >= Math.max(0, timeoutMs - WAKE_TIMEOUT_TOLERANCE_MS);\n}\n"],"mappings":";;AA8GA,MAAa,kBAAkB;;;;;;AAiB/B,MAAa,4BAA4B;;;;;;;;;;AAWzC,SAAgB,YAAY,WAAmB,WAAmB,KAAsB;CACtF,OAAO,MAAM,aAAa,KAAK,IAAI,GAAG,YAAY,yBAAyB;AAC7E"}
|