@shware/workflow 5.0.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +165 -42
- package/dist/_virtual/_rolldown/runtime.cjs +1 -2
- package/dist/apns-relay.cjs +1 -2
- package/dist/apns-relay.d.cts +0 -2
- package/dist/apns-relay.d.cts.map +1 -1
- package/dist/apns-relay.d.mts +0 -2
- package/dist/apns-relay.d.mts.map +1 -1
- package/dist/apns-relay.mjs +1 -2
- package/dist/apns.cjs +1 -2
- package/dist/apns.d.cts +0 -2
- package/dist/apns.d.cts.map +1 -1
- package/dist/apns.d.mts +0 -2
- package/dist/apns.d.mts.map +1 -1
- package/dist/apns.mjs +1 -2
- package/dist/aws/engine-step.cjs +1 -2
- package/dist/aws/engine-step.d.cts +0 -2
- package/dist/aws/engine-step.d.cts.map +1 -1
- package/dist/aws/engine-step.d.mts +0 -2
- package/dist/aws/engine-step.d.mts.map +1 -1
- package/dist/aws/engine-step.mjs +1 -2
- package/dist/aws/function-url.cjs +1 -2
- package/dist/aws/function-url.d.cts +0 -2
- package/dist/aws/function-url.d.cts.map +1 -1
- package/dist/aws/function-url.d.mts +0 -2
- package/dist/aws/function-url.d.mts.map +1 -1
- package/dist/aws/function-url.mjs +1 -2
- package/dist/aws/index.cjs +2 -3
- package/dist/aws/index.d.cts +0 -2
- package/dist/aws/index.d.cts.map +1 -1
- package/dist/aws/index.d.mts +0 -2
- package/dist/aws/index.d.mts.map +1 -1
- package/dist/aws/index.mjs +2 -3
- package/dist/aws/instances.cjs +1 -2
- package/dist/aws/instances.d.cts +0 -2
- package/dist/aws/instances.d.cts.map +1 -1
- package/dist/aws/instances.d.mts +0 -2
- package/dist/aws/instances.d.mts.map +1 -1
- package/dist/aws/instances.mjs +1 -2
- package/dist/aws/lambda-api.cjs +1 -2
- package/dist/aws/lambda-api.d.cts +0 -2
- package/dist/aws/lambda-api.d.cts.map +1 -1
- package/dist/aws/lambda-api.d.mts +0 -2
- package/dist/aws/lambda-api.d.mts.map +1 -1
- package/dist/aws/lambda-api.mjs +1 -2
- package/dist/client.cjs +46 -6
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +25 -5
- package/dist/client.d.cts.map +1 -1
- package/dist/client.d.mts +25 -5
- package/dist/client.d.mts.map +1 -1
- package/dist/client.mjs +46 -6
- package/dist/client.mjs.map +1 -1
- package/dist/cloudflare/bindings.cjs +1 -2
- package/dist/cloudflare/bindings.d.cts +0 -2
- package/dist/cloudflare/bindings.d.cts.map +1 -1
- package/dist/cloudflare/bindings.d.mts +0 -2
- package/dist/cloudflare/bindings.d.mts.map +1 -1
- package/dist/cloudflare/bindings.mjs +1 -2
- package/dist/cloudflare/index.cjs +2 -2
- package/dist/cloudflare/index.mjs +2 -2
- package/dist/cloudflare/runner.cjs +2 -3
- package/dist/cloudflare/runner.d.cts +1 -3
- package/dist/cloudflare/runner.d.cts.map +1 -1
- package/dist/cloudflare/runner.d.mts +1 -3
- package/dist/cloudflare/runner.d.mts.map +1 -1
- package/dist/cloudflare/runner.mjs +2 -3
- package/dist/cloudflare/worker.cjs +1 -2
- package/dist/cloudflare/worker.d.cts +0 -2
- package/dist/cloudflare/worker.d.cts.map +1 -1
- package/dist/cloudflare/worker.d.mts +0 -2
- package/dist/cloudflare/worker.d.mts.map +1 -1
- package/dist/cloudflare/worker.mjs +1 -2
- package/dist/cloudflare/workers.d.cts +0 -2
- package/dist/cloudflare/workers.d.cts.map +1 -1
- package/dist/cloudflare/workers.d.mts +0 -2
- package/dist/cloudflare/workers.d.mts.map +1 -1
- package/dist/config.cjs +25 -5
- package/dist/config.cjs.map +1 -1
- package/dist/config.d.cts +15 -11
- package/dist/config.d.cts.map +1 -1
- package/dist/config.d.mts +15 -11
- package/dist/config.d.mts.map +1 -1
- package/dist/config.mjs +26 -6
- package/dist/config.mjs.map +1 -1
- package/dist/contract.cjs +12 -2
- package/dist/contract.cjs.map +1 -1
- package/dist/contract.d.cts +28 -9
- package/dist/contract.d.cts.map +1 -1
- package/dist/contract.d.mts +28 -9
- package/dist/contract.d.mts.map +1 -1
- package/dist/contract.mjs +12 -2
- package/dist/contract.mjs.map +1 -1
- package/dist/dsl/action.cjs +1 -2
- package/dist/dsl/action.d.cts +0 -2
- package/dist/dsl/action.d.cts.map +1 -1
- package/dist/dsl/action.d.mts +0 -2
- package/dist/dsl/action.d.mts.map +1 -1
- package/dist/dsl/action.mjs +1 -2
- package/dist/dsl/base.cjs +1 -2
- package/dist/dsl/base.d.cts +0 -2
- package/dist/dsl/base.d.cts.map +1 -1
- package/dist/dsl/base.d.mts +0 -2
- package/dist/dsl/base.d.mts.map +1 -1
- package/dist/dsl/base.mjs +1 -2
- package/dist/dsl/bundle.cjs +5 -5
- package/dist/dsl/bundle.cjs.map +1 -1
- package/dist/dsl/bundle.d.cts +5 -3
- package/dist/dsl/bundle.d.cts.map +1 -1
- package/dist/dsl/bundle.d.mts +5 -3
- package/dist/dsl/bundle.d.mts.map +1 -1
- package/dist/dsl/bundle.mjs +5 -5
- package/dist/dsl/bundle.mjs.map +1 -1
- package/dist/dsl/condition.cjs +1 -2
- package/dist/dsl/condition.d.cts +0 -2
- package/dist/dsl/condition.d.cts.map +1 -1
- package/dist/dsl/condition.d.mts +0 -2
- package/dist/dsl/condition.d.mts.map +1 -1
- package/dist/dsl/condition.mjs +1 -2
- package/dist/dsl/flow.cjs +3 -3
- package/dist/dsl/flow.cjs.map +1 -1
- package/dist/dsl/flow.d.cts +1 -3
- package/dist/dsl/flow.d.cts.map +1 -1
- package/dist/dsl/flow.d.mts +1 -3
- package/dist/dsl/flow.d.mts.map +1 -1
- package/dist/dsl/flow.mjs +3 -3
- package/dist/dsl/flow.mjs.map +1 -1
- package/dist/dsl/refs.cjs +1 -2
- package/dist/dsl/refs.d.cts +0 -2
- package/dist/dsl/refs.d.cts.map +1 -1
- package/dist/dsl/refs.d.mts +0 -2
- package/dist/dsl/refs.d.mts.map +1 -1
- package/dist/dsl/refs.mjs +1 -2
- package/dist/dsl/template.cjs +1 -2
- package/dist/dsl/template.d.cts +0 -2
- package/dist/dsl/template.d.cts.map +1 -1
- package/dist/dsl/template.d.mts +0 -2
- package/dist/dsl/template.d.mts.map +1 -1
- package/dist/dsl/template.mjs +1 -2
- package/dist/dsl/trigger.cjs +1 -2
- package/dist/dsl/trigger.d.cts +0 -2
- package/dist/dsl/trigger.d.cts.map +1 -1
- package/dist/dsl/trigger.d.mts +0 -2
- package/dist/dsl/trigger.d.mts.map +1 -1
- package/dist/dsl/trigger.mjs +1 -2
- package/dist/dsl/workflow.cjs +3 -4
- package/dist/dsl/workflow.d.cts +0 -2
- package/dist/dsl/workflow.d.cts.map +1 -1
- package/dist/dsl/workflow.d.mts +0 -2
- package/dist/dsl/workflow.d.mts.map +1 -1
- package/dist/dsl/workflow.mjs +3 -4
- package/dist/email-context.cjs +1 -2
- package/dist/email-context.d.cts +0 -2
- package/dist/email-context.d.cts.map +1 -1
- package/dist/email-context.d.mts +0 -2
- package/dist/email-context.d.mts.map +1 -1
- package/dist/email-context.mjs +1 -2
- package/dist/email-sender.cjs +2 -3
- package/dist/email-sender.cjs.map +1 -1
- package/dist/email-sender.d.cts +1 -3
- package/dist/email-sender.d.cts.map +1 -1
- package/dist/email-sender.d.mts +1 -3
- package/dist/email-sender.d.mts.map +1 -1
- package/dist/email-sender.mjs +2 -3
- package/dist/email-sender.mjs.map +1 -1
- package/dist/email.cjs +33 -7
- package/dist/email.cjs.map +1 -1
- package/dist/email.d.cts +38 -11
- package/dist/email.d.cts.map +1 -1
- package/dist/email.d.mts +38 -11
- package/dist/email.d.mts.map +1 -1
- package/dist/email.mjs +33 -8
- package/dist/email.mjs.map +1 -1
- package/dist/engine/actions.cjs +1 -2
- package/dist/engine/actions.d.cts +0 -2
- package/dist/engine/actions.d.cts.map +1 -1
- package/dist/engine/actions.d.mts +0 -2
- package/dist/engine/actions.d.mts.map +1 -1
- package/dist/engine/actions.mjs +1 -2
- package/dist/engine/bucket.cjs +1 -2
- package/dist/engine/bucket.d.cts +0 -2
- package/dist/engine/bucket.d.cts.map +1 -1
- package/dist/engine/bucket.d.mts +0 -2
- package/dist/engine/bucket.d.mts.map +1 -1
- package/dist/engine/bucket.mjs +1 -2
- package/dist/engine/condition.cjs +1 -2
- package/dist/engine/condition.cjs.map +1 -1
- package/dist/engine/condition.d.cts +0 -2
- package/dist/engine/condition.d.cts.map +1 -1
- package/dist/engine/condition.d.mts +0 -2
- package/dist/engine/condition.d.mts.map +1 -1
- package/dist/engine/condition.mjs +1 -2
- package/dist/engine/condition.mjs.map +1 -1
- package/dist/engine/duration.cjs +1 -2
- package/dist/engine/duration.d.cts +0 -2
- package/dist/engine/duration.d.cts.map +1 -1
- package/dist/engine/duration.d.mts +0 -2
- package/dist/engine/duration.d.mts.map +1 -1
- package/dist/engine/duration.mjs +1 -2
- package/dist/engine/index.cjs +1 -1
- package/dist/engine/index.mjs +1 -1
- package/dist/engine/interpreter.cjs +22 -6
- package/dist/engine/interpreter.cjs.map +1 -1
- package/dist/engine/interpreter.d.cts +0 -2
- package/dist/engine/interpreter.d.cts.map +1 -1
- package/dist/engine/interpreter.d.mts +0 -2
- package/dist/engine/interpreter.d.mts.map +1 -1
- package/dist/engine/interpreter.mjs +22 -6
- package/dist/engine/interpreter.mjs.map +1 -1
- package/dist/engine/ports.d.cts +8 -4
- package/dist/engine/ports.d.cts.map +1 -1
- package/dist/engine/ports.d.mts +8 -4
- package/dist/engine/ports.d.mts.map +1 -1
- package/dist/engine/recipient.cjs +1 -2
- package/dist/engine/recipient.d.cts +0 -2
- package/dist/engine/recipient.d.cts.map +1 -1
- package/dist/engine/recipient.d.mts +0 -2
- package/dist/engine/recipient.d.mts.map +1 -1
- package/dist/engine/recipient.mjs +1 -2
- package/dist/engine/retry.cjs +1 -2
- package/dist/engine/retry.d.cts +0 -2
- package/dist/engine/retry.d.cts.map +1 -1
- package/dist/engine/retry.d.mts +0 -2
- package/dist/engine/retry.d.mts.map +1 -1
- package/dist/engine/retry.mjs +1 -2
- package/dist/engine/time-scale.cjs +1 -2
- package/dist/engine/time-scale.d.cts +0 -2
- package/dist/engine/time-scale.d.cts.map +1 -1
- package/dist/engine/time-scale.d.mts +0 -2
- package/dist/engine/time-scale.d.mts.map +1 -1
- package/dist/engine/time-scale.mjs +1 -2
- package/dist/engine/time-window.cjs +1 -2
- package/dist/engine/time-window.d.cts +0 -2
- package/dist/engine/time-window.d.cts.map +1 -1
- package/dist/engine/time-window.d.mts +0 -2
- package/dist/engine/time-window.d.mts.map +1 -1
- package/dist/engine/time-window.mjs +1 -2
- package/dist/fcm.cjs +1 -2
- package/dist/fcm.d.cts +0 -2
- package/dist/fcm.d.cts.map +1 -1
- package/dist/fcm.d.mts +0 -2
- package/dist/fcm.d.mts.map +1 -1
- package/dist/fcm.mjs +1 -2
- package/dist/hash.cjs +1 -2
- package/dist/hash.d.cts +0 -2
- package/dist/hash.d.cts.map +1 -1
- package/dist/hash.d.mts +0 -2
- package/dist/hash.d.mts.map +1 -1
- package/dist/hash.mjs +1 -2
- package/dist/host/auth.cjs +1 -2
- package/dist/host/auth.d.cts +0 -2
- package/dist/host/auth.d.cts.map +1 -1
- package/dist/host/auth.d.mts +0 -2
- package/dist/host/auth.d.mts.map +1 -1
- package/dist/host/auth.mjs +1 -2
- package/dist/host/channels.cjs +2 -3
- package/dist/host/channels.d.cts +0 -2
- package/dist/host/channels.d.cts.map +1 -1
- package/dist/host/channels.d.mts +0 -2
- package/dist/host/channels.d.mts.map +1 -1
- package/dist/host/channels.mjs +2 -3
- package/dist/host/execute.cjs +3 -4
- package/dist/host/execute.d.cts +1 -3
- package/dist/host/execute.d.cts.map +1 -1
- package/dist/host/execute.d.mts +1 -3
- package/dist/host/execute.d.mts.map +1 -1
- package/dist/host/execute.mjs +3 -4
- package/dist/host/fresh-read.cjs +1 -2
- package/dist/host/fresh-read.d.cts +0 -2
- package/dist/host/fresh-read.d.cts.map +1 -1
- package/dist/host/fresh-read.d.mts +0 -2
- package/dist/host/fresh-read.d.mts.map +1 -1
- package/dist/host/fresh-read.mjs +1 -2
- package/dist/host/http.cjs +39 -5
- package/dist/host/http.cjs.map +1 -1
- package/dist/host/http.d.cts +2 -4
- package/dist/host/http.d.cts.map +1 -1
- package/dist/host/http.d.mts +2 -4
- package/dist/host/http.d.mts.map +1 -1
- package/dist/host/http.mjs +40 -6
- package/dist/host/http.mjs.map +1 -1
- package/dist/host/ingest.cjs +129 -6
- package/dist/host/ingest.cjs.map +1 -1
- package/dist/host/ingest.d.cts +71 -2
- package/dist/host/ingest.d.cts.map +1 -1
- package/dist/host/ingest.d.mts +71 -2
- package/dist/host/ingest.d.mts.map +1 -1
- package/dist/host/ingest.mjs +126 -7
- package/dist/host/ingest.mjs.map +1 -1
- package/dist/host/journey-host.cjs +11 -10
- package/dist/host/journey-host.cjs.map +1 -1
- package/dist/host/journey-host.d.cts +0 -2
- package/dist/host/journey-host.d.cts.map +1 -1
- package/dist/host/journey-host.d.mts +0 -2
- package/dist/host/journey-host.d.mts.map +1 -1
- package/dist/host/journey-host.mjs +11 -10
- package/dist/host/journey-host.mjs.map +1 -1
- package/dist/host/platform.cjs +1 -2
- package/dist/host/platform.d.cts +0 -2
- package/dist/host/platform.d.cts.map +1 -1
- package/dist/host/platform.d.mts +0 -2
- package/dist/host/platform.d.mts.map +1 -1
- package/dist/host/platform.mjs +1 -2
- package/dist/host/senders.cjs +1 -2
- package/dist/host/senders.d.cts +0 -2
- package/dist/host/senders.d.cts.map +1 -1
- package/dist/host/senders.d.mts +0 -2
- package/dist/host/senders.d.mts.map +1 -1
- package/dist/host/senders.mjs +1 -2
- package/dist/http2-client.cjs +1 -2
- package/dist/http2-client.d.cts +0 -2
- package/dist/http2-client.d.cts.map +1 -1
- package/dist/http2-client.d.mts +0 -2
- package/dist/http2-client.d.mts.map +1 -1
- package/dist/http2-client.mjs +1 -2
- package/dist/index.cjs +5 -2
- package/dist/index.d.cts +5 -5
- package/dist/index.d.mts +5 -5
- package/dist/index.mjs +5 -5
- package/dist/ir.cjs +12 -4
- package/dist/ir.cjs.map +1 -1
- package/dist/ir.d.cts +17 -2
- package/dist/ir.d.cts.map +1 -1
- package/dist/ir.d.mts +17 -2
- package/dist/ir.d.mts.map +1 -1
- package/dist/ir.mjs +12 -5
- package/dist/ir.mjs.map +1 -1
- package/dist/jwt.cjs +1 -2
- package/dist/jwt.d.cts +0 -2
- package/dist/jwt.d.cts.map +1 -1
- package/dist/jwt.d.mts +0 -2
- package/dist/jwt.d.mts.map +1 -1
- package/dist/jwt.mjs +1 -2
- package/dist/plan.cjs +1 -2
- package/dist/plan.d.cts +0 -2
- package/dist/plan.d.cts.map +1 -1
- package/dist/plan.d.mts +0 -2
- package/dist/plan.d.mts.map +1 -1
- package/dist/plan.mjs +1 -2
- package/dist/postgres-js.cjs +1 -2
- package/dist/postgres-js.d.cts +0 -2
- package/dist/postgres-js.d.cts.map +1 -1
- package/dist/postgres-js.d.mts +0 -2
- package/dist/postgres-js.d.mts.map +1 -1
- package/dist/postgres-js.mjs +1 -2
- package/dist/provenance.cjs +1 -2
- package/dist/provenance.d.cts +0 -2
- package/dist/provenance.d.cts.map +1 -1
- package/dist/provenance.d.mts +0 -2
- package/dist/provenance.d.mts.map +1 -1
- package/dist/provenance.mjs +1 -2
- package/dist/push.cjs +1 -2
- package/dist/push.d.cts +0 -2
- package/dist/push.d.cts.map +1 -1
- package/dist/push.d.mts +0 -2
- package/dist/push.d.mts.map +1 -1
- package/dist/push.mjs +1 -2
- package/dist/react-email.cjs +1 -2
- package/dist/react-email.d.cts +0 -2
- package/dist/react-email.d.cts.map +1 -1
- package/dist/react-email.d.mts +0 -2
- package/dist/react-email.d.mts.map +1 -1
- package/dist/react-email.mjs +1 -2
- package/dist/reporting/classify.cjs +1 -2
- package/dist/reporting/classify.d.cts +0 -2
- package/dist/reporting/classify.d.cts.map +1 -1
- package/dist/reporting/classify.d.mts +0 -2
- package/dist/reporting/classify.d.mts.map +1 -1
- package/dist/reporting/classify.mjs +1 -2
- package/dist/reporting/observer.cjs +2 -2
- package/dist/reporting/observer.cjs.map +1 -1
- package/dist/reporting/observer.d.cts +0 -2
- package/dist/reporting/observer.d.cts.map +1 -1
- package/dist/reporting/observer.d.mts +0 -2
- package/dist/reporting/observer.d.mts.map +1 -1
- package/dist/reporting/observer.mjs +2 -2
- package/dist/reporting/observer.mjs.map +1 -1
- package/dist/reporting/tracking.cjs +29 -12
- package/dist/reporting/tracking.cjs.map +1 -1
- package/dist/reporting/tracking.d.cts +13 -8
- package/dist/reporting/tracking.d.cts.map +1 -1
- package/dist/reporting/tracking.d.mts +13 -8
- package/dist/reporting/tracking.d.mts.map +1 -1
- package/dist/reporting/tracking.mjs +29 -12
- package/dist/reporting/tracking.mjs.map +1 -1
- package/dist/runtime.cjs +1 -2
- package/dist/runtime.d.cts +0 -2
- package/dist/runtime.d.cts.map +1 -1
- package/dist/runtime.d.mts +0 -2
- package/dist/runtime.d.mts.map +1 -1
- package/dist/runtime.mjs +1 -2
- package/dist/store/erasure.cjs +1 -2
- package/dist/store/erasure.d.cts +0 -2
- package/dist/store/erasure.d.cts.map +1 -1
- package/dist/store/erasure.d.mts +0 -2
- package/dist/store/erasure.d.mts.map +1 -1
- package/dist/store/erasure.mjs +1 -2
- package/dist/store/facts.cjs +4 -5
- package/dist/store/facts.cjs.map +1 -1
- package/dist/store/facts.d.cts +2 -4
- package/dist/store/facts.d.cts.map +1 -1
- package/dist/store/facts.d.mts +2 -4
- package/dist/store/facts.d.mts.map +1 -1
- package/dist/store/facts.mjs +4 -5
- package/dist/store/facts.mjs.map +1 -1
- package/dist/store/index.cjs +2 -3
- package/dist/store/index.cjs.map +1 -1
- package/dist/store/index.d.cts +44 -15
- package/dist/store/index.d.cts.map +1 -1
- package/dist/store/index.d.mts +44 -15
- package/dist/store/index.d.mts.map +1 -1
- package/dist/store/index.mjs +2 -3
- package/dist/store/index.mjs.map +1 -1
- package/dist/store/postgres-reporting.cjs +31 -12
- package/dist/store/postgres-reporting.cjs.map +1 -1
- package/dist/store/postgres-reporting.d.cts +4 -4
- package/dist/store/postgres-reporting.d.cts.map +1 -1
- package/dist/store/postgres-reporting.d.mts +4 -4
- package/dist/store/postgres-reporting.d.mts.map +1 -1
- package/dist/store/postgres-reporting.mjs +31 -12
- package/dist/store/postgres-reporting.mjs.map +1 -1
- package/dist/store/postgres.cjs +40 -5
- package/dist/store/postgres.cjs.map +1 -1
- package/dist/store/postgres.d.cts +4 -4
- package/dist/store/postgres.d.cts.map +1 -1
- package/dist/store/postgres.d.mts +4 -4
- package/dist/store/postgres.d.mts.map +1 -1
- package/dist/store/postgres.mjs +41 -6
- package/dist/store/postgres.mjs.map +1 -1
- package/dist/store/sql.cjs +1 -2
- package/dist/store/sql.d.cts +0 -2
- package/dist/store/sql.d.cts.map +1 -1
- package/dist/store/sql.d.mts +0 -2
- package/dist/store/sql.d.mts.map +1 -1
- package/dist/store/sql.mjs +1 -2
- package/dist/store/tables.cjs +76 -13
- package/dist/store/tables.cjs.map +1 -1
- package/dist/store/tables.d.cts +17 -3
- package/dist/store/tables.d.cts.map +1 -1
- package/dist/store/tables.d.mts +17 -3
- package/dist/store/tables.d.mts.map +1 -1
- package/dist/store/tables.mjs +75 -14
- package/dist/store/tables.mjs.map +1 -1
- package/dist/store/user-lock.cjs +1 -2
- package/dist/store/user-lock.d.cts +0 -2
- package/dist/store/user-lock.d.cts.map +1 -1
- package/dist/store/user-lock.d.mts +0 -2
- package/dist/store/user-lock.d.mts.map +1 -1
- package/dist/store/user-lock.mjs +1 -2
- package/dist/templates.cjs +61 -9
- package/dist/templates.cjs.map +1 -1
- package/dist/templates.d.cts +101 -15
- package/dist/templates.d.cts.map +1 -1
- package/dist/templates.d.mts +101 -15
- package/dist/templates.d.mts.map +1 -1
- package/dist/templates.mjs +62 -11
- package/dist/templates.mjs.map +1 -1
- package/dist/transports/cloudflare-email.cjs +1 -2
- package/dist/transports/cloudflare-email.d.cts +0 -2
- package/dist/transports/cloudflare-email.d.cts.map +1 -1
- package/dist/transports/cloudflare-email.d.mts +0 -2
- package/dist/transports/cloudflare-email.d.mts.map +1 -1
- package/dist/transports/cloudflare-email.mjs +1 -2
- package/dist/transports/ses.cjs +1 -2
- package/dist/transports/ses.d.cts +0 -2
- package/dist/transports/ses.d.cts.map +1 -1
- package/dist/transports/ses.d.mts +0 -2
- package/dist/transports/ses.d.mts.map +1 -1
- package/dist/transports/ses.mjs +1 -2
- package/dist/transports/sigv4.cjs +1 -2
- package/dist/transports/sigv4.d.cts +0 -2
- package/dist/transports/sigv4.d.cts.map +1 -1
- package/dist/transports/sigv4.d.mts +0 -2
- package/dist/transports/sigv4.d.mts.map +1 -1
- package/dist/transports/sigv4.mjs +1 -2
- package/dist/webhooks.cjs +1 -2
- package/dist/webhooks.d.cts +0 -2
- package/dist/webhooks.d.cts.map +1 -1
- package/dist/webhooks.d.mts +0 -2
- package/dist/webhooks.d.mts.map +1 -1
- package/dist/webhooks.mjs +1 -2
- package/dist/webpush.cjs +1 -2
- package/dist/webpush.d.cts +0 -2
- package/dist/webpush.d.cts.map +1 -1
- package/dist/webpush.d.mts +0 -2
- package/dist/webpush.d.mts.map +1 -1
- package/dist/webpush.mjs +1 -2
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -54,6 +54,7 @@ export const contract = defineContract({
|
|
|
54
54
|
purchase: looseObject({ transaction_id: string(), currency: string(), value: number() }),
|
|
55
55
|
},
|
|
56
56
|
profile: strictObject({ email: optional(email()), name: string() }),
|
|
57
|
+
subscriptions: { topics: ['promotions', 'newsletter', 'tips'] },
|
|
57
58
|
});
|
|
58
59
|
|
|
59
60
|
/** The DSL's reference tables: `e.login`, `u.name`. */
|
|
@@ -66,6 +67,16 @@ The profile is a flat object of string, number, boolean, enum and literal
|
|
|
66
67
|
fields. Defaults, transforms, coercion and custom refinements are refused when
|
|
67
68
|
the contract is defined, because input and output must mean the same JSON.
|
|
68
69
|
|
|
70
|
+
`subscriptions.topics` lists the subscription topics: content categories
|
|
71
|
+
("offers", "tips"), not channels — ids only (a plain token; the app's i18n gives
|
|
72
|
+
them names). A plain array literal is enough: `defineContract` keeps the
|
|
73
|
+
literals, and the union they form types every email and push template's
|
|
74
|
+
`topic`, the config and `JourneyClient.setSubscriptions`. A user opts out of a
|
|
75
|
+
topic per channel (the preference page is topic × channel), or of a whole
|
|
76
|
+
channel; the engine keeps that record and is the one source of truth for it
|
|
77
|
+
(see [Email](#email), [Push](#push) and [`JourneyClient`](#journeyclient)).
|
|
78
|
+
Leave the field out when every email and push is transactional.
|
|
79
|
+
|
|
69
80
|
### 2. Templates
|
|
70
81
|
|
|
71
82
|
One object per template, identified by its own `id` (unique per channel): the
|
|
@@ -74,14 +85,20 @@ stable and rename `title` instead. Its strict Zod `props` schema is the only
|
|
|
74
85
|
source of the props type: the DSL's arguments, the `preview` and the component
|
|
75
86
|
are all derived from it, and every send parses the props against it first.
|
|
76
87
|
|
|
88
|
+
```ts
|
|
89
|
+
// src/contract.ts (continued): the template factories bound to the contract
|
|
90
|
+
export const { emailTemplate, pushTemplate } = templatesFor(contract);
|
|
91
|
+
```
|
|
92
|
+
|
|
77
93
|
```tsx
|
|
78
94
|
// src/email/welcome.tsx
|
|
79
95
|
import { Html, Text } from '@react-email/components';
|
|
80
|
-
import { emailTemplate } from '@shware/workflow';
|
|
81
96
|
import { strictObject, string } from 'zod/mini';
|
|
97
|
+
import { emailTemplate } from '../contract';
|
|
82
98
|
|
|
83
99
|
export default emailTemplate({
|
|
84
100
|
id: 'welcome',
|
|
101
|
+
transactional: true,
|
|
85
102
|
from: { email: 'noreply@acme.test', name: 'Acme' },
|
|
86
103
|
title: 'Welcome',
|
|
87
104
|
props: strictObject({ name: string() }),
|
|
@@ -95,6 +112,17 @@ export default emailTemplate({
|
|
|
95
112
|
});
|
|
96
113
|
```
|
|
97
114
|
|
|
115
|
+
Every email and push template says what it is: `topic: 'tips'` (a topic the
|
|
116
|
+
contract declares, which the user can opt out of on that channel) or
|
|
117
|
+
`transactional: true` (a receipt, a code, an invite: sent whatever they opted
|
|
118
|
+
out of; for an email, with no unsubscribe link). Leaving both out, or giving
|
|
119
|
+
both, is a type error and a compile error, and the error says so in words.
|
|
120
|
+
`templatesFor(contract)` gives the factories with the topic typed by the
|
|
121
|
+
contract, so a topic it does not declare is an error at the template's own
|
|
122
|
+
`topic` line, in the editor and when the module loads; the bare `emailTemplate`
|
|
123
|
+
/ `pushTemplate` from `@shware/workflow` are for a project without a contract
|
|
124
|
+
(transactional templates only), and the compile checks every template again.
|
|
125
|
+
|
|
98
126
|
Gather them per channel; the workflows send the definitions and the config
|
|
99
127
|
registers the whole:
|
|
100
128
|
|
|
@@ -113,11 +141,13 @@ import { email } from './email';
|
|
|
113
141
|
export const templates = { email };
|
|
114
142
|
```
|
|
115
143
|
|
|
116
|
-
`tip.tsx` is written the same way
|
|
117
|
-
only. `.email(email.welcome, props)` checks `props`
|
|
118
|
-
schema, and a push template is not accepted where an
|
|
119
|
-
refuses an id registered twice in a channel,
|
|
120
|
-
definition other than the one registered under its id
|
|
144
|
+
`tip.tsx` is written the same way, with `topic: 'tips'`, and so is a
|
|
145
|
+
`pushTemplate`. The property names are JavaScript names only. `.email(email.welcome, props)` checks `props`
|
|
146
|
+
against that template's schema, and a push template is not accepted where an
|
|
147
|
+
email is. The compile refuses an id registered twice in a channel, a workflow
|
|
148
|
+
sending a definition other than the one registered under its id, and a topic
|
|
149
|
+
the contract does not declare (with a `contract` in the config, the editor
|
|
150
|
+
refuses it first).
|
|
121
151
|
|
|
122
152
|
### 3. A workflow
|
|
123
153
|
|
|
@@ -463,30 +493,42 @@ the compile fails.
|
|
|
463
493
|
|
|
464
494
|
### Email
|
|
465
495
|
|
|
466
|
-
`emailTemplate({ id, from, replyTo?, unsubscribeUrl?, title, description?, props, subject, preview, component })`
|
|
496
|
+
`emailTemplate({ id, topic | transactional, from, replyTo?, unsubscribeUrl?, title, description?, props, subject, preview, component })`
|
|
467
497
|
declares one. The Worker renders it with `@shware/workflow/react-email` (React's
|
|
468
498
|
streaming renderer, which waits for react-email's `<Tailwind>`) and sends it
|
|
469
499
|
through the host's email transport, from the template's own `from`:
|
|
470
500
|
|
|
471
501
|
```ts
|
|
472
|
-
// src/email/
|
|
502
|
+
// src/email/tip.tsx
|
|
473
503
|
export default emailTemplate({
|
|
474
|
-
id: '
|
|
504
|
+
id: 'tip',
|
|
505
|
+
topic: 'tips',
|
|
475
506
|
from: { email: 'noreply@acme.test', name: 'Acme' },
|
|
476
507
|
replyTo: { email: 'support@acme.test', name: 'Acme Support' }, // left out: no Reply-To
|
|
477
|
-
title: '
|
|
508
|
+
title: 'Weekly tip',
|
|
478
509
|
props: strictObject({ name: string() }),
|
|
479
|
-
subject: '
|
|
510
|
+
subject: 'One thing to try this week',
|
|
480
511
|
preview: { name: 'John Doe' },
|
|
481
512
|
component: ({ name }) => <Layout name={name}>…</Layout>,
|
|
482
513
|
});
|
|
483
514
|
```
|
|
484
515
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
`
|
|
488
|
-
|
|
489
|
-
|
|
516
|
+
Every template is one of two kinds, by type: a **topic** email names one of the
|
|
517
|
+
contract's `subscriptions.topics`, and a **transactional** email says
|
|
518
|
+
`transactional: true` (and takes no `unsubscribeUrl`). The kind decides what
|
|
519
|
+
the engine does before and after the send:
|
|
520
|
+
|
|
521
|
+
| Email | Before sending | Unsubscribe link, `List-Unsubscribe` |
|
|
522
|
+
| ------------- | -------------------------------------------------------------- | ------------------------------------- |
|
|
523
|
+
| topic | skipped when the user opted out of that topic, or of all email | yes |
|
|
524
|
+
| transactional | always sent (a receipt, a code, an invite) | none, whatever `email.unsubscribeUrl` |
|
|
525
|
+
|
|
526
|
+
There is no global sender: share addresses through a module of your own. A
|
|
527
|
+
topic email's unsubscribe link is the template's `unsubscribeUrl`, else the
|
|
528
|
+
config's `email.unsubscribeUrl`, else none; a transactional email never has one
|
|
529
|
+
(`resolveEmail` is the rule). A layout reads it from the render, never from a
|
|
530
|
+
constant, so a template's own URL reaches its HTML; the resolved `topic` is
|
|
531
|
+
there too, for a line like "you get these because you subscribed to tips":
|
|
490
532
|
|
|
491
533
|
```tsx
|
|
492
534
|
import { useEmailContext } from '@shware/workflow/email-context';
|
|
@@ -527,10 +569,23 @@ An email with an unsubscribe link also carries `List-Unsubscribe` (RFC 2369), so
|
|
|
527
569
|
a mail client's own unsubscribe button works. With reporting on (a Postgres store and
|
|
528
570
|
`WORKFLOW_ORIGIN`), it names the copy's one-click endpoint `/t/u/<message>`, together with
|
|
529
571
|
`List-Unsubscribe-Post: List-Unsubscribe=One-Click` (RFC 8058). Gmail and Yahoo require
|
|
530
|
-
one-click unsubscribe for bulk senders. The provider's POST there unsubscribes at once
|
|
531
|
-
same URL opened in a browser
|
|
532
|
-
|
|
533
|
-
|
|
572
|
+
one-click unsubscribe for bulk senders. The provider's POST there unsubscribes at once from
|
|
573
|
+
the email's topic; the same URL opened in a browser (and the email's own link) asks first, with
|
|
574
|
+
two buttons: the email's topic, and all email. Without reporting, the header names the
|
|
575
|
+
template's own unsubscribe link, with no one-click claim. Only https URLs are named, so a local
|
|
576
|
+
`http://` origin sends no header.
|
|
577
|
+
|
|
578
|
+
What a user opted out of is the engine's record, per topic and channel: a topic email is
|
|
579
|
+
skipped while its topic (on email) or all email is opted out, a transactional email never is,
|
|
580
|
+
and `reachable('email')` reads the all-email opt-out only — a user unsubscribed from one topic
|
|
581
|
+
is still reachable, so an `exitWhen: not(reachable('email'))` fires on "all email", not on a
|
|
582
|
+
topic. An email opt-out says nothing about push: the same topic is opted out per channel. The
|
|
583
|
+
app reads and sets the record through `GET` / `PUT /subscriptions/:userId`
|
|
584
|
+
(`JourneyClient.getSubscriptions` / `setSubscriptions`), which is how a signed-in preference
|
|
585
|
+
page is built: the engine holds the truth, the page shows it. The all-email opt-out and the
|
|
586
|
+
topic opt-outs are separate records: `all: false` masks every topic, whatever its own flag says,
|
|
587
|
+
and `all: true` clears only the all-email opt-out — a topic opted out on its own stays off until
|
|
588
|
+
a patch names it.
|
|
534
589
|
|
|
535
590
|
An email step refuses to send when the profile has no `email`.
|
|
536
591
|
`GET /preview/<id>?props=<JSON>` renders a template, with the subject in the
|
|
@@ -538,13 +593,14 @@ An email step refuses to send when the profile has no `email`.
|
|
|
538
593
|
|
|
539
594
|
### Push
|
|
540
595
|
|
|
541
|
-
`pushTemplate({ id, title, description?, props, preview, content })`,
|
|
542
|
-
`content(props)` returns `{ title, body, image?, url? }`: final text, never
|
|
596
|
+
`pushTemplate({ id, topic | transactional, title, description?, props, preview, content })`,
|
|
597
|
+
where `content(props)` returns `{ title, body, image?, url? }`: final text, never
|
|
543
598
|
placeholder-expanded. `url` is the page a browser push opens.
|
|
544
599
|
|
|
545
600
|
```ts
|
|
546
601
|
export default pushTemplate({
|
|
547
602
|
id: 'onboarding_tip_push',
|
|
603
|
+
topic: 'tips',
|
|
548
604
|
title: 'Onboarding tip (push)',
|
|
549
605
|
props: strictObject({ name: string() }),
|
|
550
606
|
preview: { name: 'John Doe' },
|
|
@@ -552,6 +608,18 @@ export default pushTemplate({
|
|
|
552
608
|
});
|
|
553
609
|
```
|
|
554
610
|
|
|
611
|
+
A push is a **topic** push (one of the contract's `subscriptions.topics`) or a
|
|
612
|
+
**transactional** one (`transactional: true`: a code, a receipt, a reply), like
|
|
613
|
+
an email. A topic push is skipped while the user opted out of that topic on
|
|
614
|
+
push, or of all push; a transactional push always goes. The app records the
|
|
615
|
+
opt-outs through `PUT /subscriptions/:userId` (`JourneyClient.setSubscriptions`
|
|
616
|
+
with `{ push: { all?, topics? } }`), which is the in-app switch App Store Review
|
|
617
|
+
Guideline 4.5.4 requires for marketing pushes: `push.all: false` is "no
|
|
618
|
+
promotional notifications", a topic `false` is one category off. The opt-outs are
|
|
619
|
+
per channel: a topic switched off for push is still sent by email.
|
|
620
|
+
`reachable('push')` is a device plus no all-push opt-out; a topic opt-out leaves
|
|
621
|
+
it true. Changing `push.all` wakes the waits on it, as a device change does.
|
|
622
|
+
|
|
555
623
|
Pushes go to the devices the engine keeps. The app registers each token with
|
|
556
624
|
`JourneyClient.registerDevice(userId, { token, platform })`, and a token moves
|
|
557
625
|
to the latest user that registers it. Each platform is turned on by the config,
|
|
@@ -636,20 +704,20 @@ Discord; supply anything else with `journeyWorker({ messages })`.
|
|
|
636
704
|
|
|
637
705
|
`defineConfig` takes:
|
|
638
706
|
|
|
639
|
-
| Field | Holds
|
|
640
|
-
| ----------- |
|
|
641
|
-
| `title` | The Studio title
|
|
642
|
-
| `contract` | The `defineContract(...)` every `/events` and `/identify` is validated against
|
|
643
|
-
| `workflows` | The workflows to deploy
|
|
644
|
-
| `segments` | Named segments
|
|
645
|
-
| `actions` | Custom actions `.run()` uses
|
|
646
|
-
| `templates` | The registries: `{ email?, push?, discord?, slack? }
|
|
647
|
-
| `email` | `{ unsubscribeUrl?, tracking? }`: what every email template shares
|
|
648
|
-
| `runtime` | Per mode (`development`, `production`, …): `{ timeScale?, logMessages? }`
|
|
649
|
-
| `apns` | `{ topic, keyId, teamId, environment? }`
|
|
650
|
-
| `fcm` | `{ androidChannelId? }`
|
|
651
|
-
| `webPush` | `{ subject, openedEvent? }`
|
|
652
|
-
| `database` | `{ schema? }`: the Postgres schema, `workflow` by default
|
|
707
|
+
| Field | Holds |
|
|
708
|
+
| ----------- | ----------------------------------------------------------------------------------- |
|
|
709
|
+
| `title` | The Studio title |
|
|
710
|
+
| `contract` | The `defineContract(...)` every `/events` and `/identify` is validated against |
|
|
711
|
+
| `workflows` | The workflows to deploy |
|
|
712
|
+
| `segments` | Named segments |
|
|
713
|
+
| `actions` | Custom actions `.run()` uses |
|
|
714
|
+
| `templates` | The registries: `{ email?, push?, discord?, slack? }`; topics typed by the contract |
|
|
715
|
+
| `email` | `{ unsubscribeUrl?, tracking? }`: what every topic email template shares |
|
|
716
|
+
| `runtime` | Per mode (`development`, `production`, …): `{ timeScale?, logMessages? }` |
|
|
717
|
+
| `apns` | `{ topic, keyId, teamId, environment? }` |
|
|
718
|
+
| `fcm` | `{ androidChannelId? }` |
|
|
719
|
+
| `webPush` | `{ subject, openedEvent? }` |
|
|
720
|
+
| `database` | `{ schema? }`: the Postgres schema, `workflow` by default |
|
|
653
721
|
|
|
654
722
|
The `ENVIRONMENT` var picks the `runtime` entry (production when unset).
|
|
655
723
|
`timeScale` fast-forwards every duration: 600 turns "1 day" into 2.4 minutes.
|
|
@@ -689,12 +757,39 @@ const journeys = new JourneyClient({ contract, origin, token });
|
|
|
689
757
|
| `unbindDevice(token)` | `POST /devices/unbind` | Signed out: the token stays, bound to nobody |
|
|
690
758
|
| `removeDevice(token)` | `POST /devices/remove` | Forget a token for good |
|
|
691
759
|
| `messageOpened(userId, messageId)` | `POST /messages/opened` | A journey push tapped open (the `message_id` it carried) |
|
|
760
|
+
| `getSubscriptions(userId)` | `GET /subscriptions/…` | `{ email, push }`, each `{ all, topics }`, `true` = on |
|
|
761
|
+
| `setSubscriptions(userId, patch)` | `PUT /subscriptions/…` | Change what the patch names; answers the new state |
|
|
692
762
|
| `forgetUser(userId)` | `POST /users/forget` | Stop the user's journeys and erase their data |
|
|
693
763
|
| `deploy(bundle)` | `POST /deploy` | Deploy a compiled bundle |
|
|
694
764
|
|
|
695
765
|
Pass an `id` to `track` whenever a caller may retry; see
|
|
696
766
|
[Give each event an `id`](#give-each-event-an-id).
|
|
697
767
|
|
|
768
|
+
`getSubscriptions` and `setSubscriptions` are typed by the contract's
|
|
769
|
+
`subscriptions.topics`, and a topic it does not declare is refused before the
|
|
770
|
+
request. The record is topic × channel: `email` and `push` each list every
|
|
771
|
+
topic. A preference page is a thin proxy over them:
|
|
772
|
+
|
|
773
|
+
```ts
|
|
774
|
+
const { email, push } = await journeys.getSubscriptions(userId);
|
|
775
|
+
// email: { all: false, topics: { promotions: false, newsletter: true, tips: true } }
|
|
776
|
+
// push: { all: true, topics: { promotions: true, newsletter: true, tips: true } }
|
|
777
|
+
// `all: false` masks every topic: newsletter reads true, but no email is sent until `all` is
|
|
778
|
+
// true again. Each flag is its own record, so a preference page shows `all` as the master
|
|
779
|
+
// switch and the topics as they are. Push is untouched by the email record.
|
|
780
|
+
await journeys.setSubscriptions(userId, { email: { all: true } });
|
|
781
|
+
// email: { all: true, topics: { promotions: false, newsletter: true, tips: true } }
|
|
782
|
+
// `all: true` cleared only the all-email opt-out; promotions stays off until named:
|
|
783
|
+
await journeys.setSubscriptions(userId, { email: { topics: { promotions: true } } });
|
|
784
|
+
await journeys.setSubscriptions(userId, { email: { all: false } }); // unsubscribe from everything
|
|
785
|
+
await journeys.setSubscriptions(userId, { push: { all: false } }); // the in-app "no promotional pushes"
|
|
786
|
+
await journeys.setSubscriptions(userId, { push: { topics: { promotions: false } } }); // one category off, on push only
|
|
787
|
+
```
|
|
788
|
+
|
|
789
|
+
A "subscribe to all" button sends `all: true` plus every topic `true`; an
|
|
790
|
+
"unsubscribe from all" sends `all: false` and leaves the topics as they are, so
|
|
791
|
+
turning `all` back on restores the user's own choices.
|
|
792
|
+
|
|
698
793
|
## Compiling and planning
|
|
699
794
|
|
|
700
795
|
`compileConfig(config)` returns the `BundleIR` the Worker deploys. It is
|
|
@@ -1016,14 +1111,25 @@ says `database: { schema }`, so their short names never meet the app's. The
|
|
|
1016
1111
|
host owns the migrations. `npx workflow-ui generate --out <file>` (from
|
|
1017
1112
|
`@shware/workflow-ui`) writes the tables as one drizzle file on that schema;
|
|
1018
1113
|
re-export it from your drizzle schema and let drizzle-kit write the migration.
|
|
1019
|
-
Run `--check` in CI to catch an SDK upgrade that changed them. The
|
|
1114
|
+
Run `--check` in CI to catch an SDK upgrade that changed them. The file is laid
|
|
1115
|
+
out the way a prettier-compatible formatter at 100 columns lays it out, so
|
|
1116
|
+
formatting it changes nothing and a regeneration shows only real changes. The unique index
|
|
1020
1117
|
on `event` is on `(user_id, event_id)`: regenerate and migrate before deploying —
|
|
1021
|
-
the deploy check verifies the unique indexes as well as the columns, and refuses
|
|
1118
|
+
the deploy check verifies the unique indexes and constraints as well as the columns, and refuses
|
|
1022
1119
|
to run until they match. Without drizzle,
|
|
1023
1120
|
`renderPostgresDdl()` returns the same tables as plain DDL. Nothing creates or
|
|
1024
1121
|
alters them at runtime: `POST /bundle/deploy` checks the live database first
|
|
1025
1122
|
and answers 503 with every missing table or column.
|
|
1026
1123
|
|
|
1124
|
+
**Upgrading to 6.0 (topics).** `suppression` gains a nullable `topic`
|
|
1125
|
+
column, loses its `(user_id, channel)` primary key and gets a
|
|
1126
|
+
`UNIQUE NULLS NOT DISTINCT (user_id, channel, topic)` constraint (Postgres 15+),
|
|
1127
|
+
and `message` gains a nullable `topic` (written for email and push copies).
|
|
1128
|
+
Regenerate the drizzle file and let drizzle-kit write the migration; it needs no
|
|
1129
|
+
data step. Rows from before keep their meaning: a `suppression` row whose
|
|
1130
|
+
`topic` is null is an opt-out from the whole channel, which is what every
|
|
1131
|
+
unsubscribe recorded before topics was.
|
|
1132
|
+
|
|
1027
1133
|
### Deploying is `wrangler deploy`
|
|
1028
1134
|
|
|
1029
1135
|
The journeys are compiled into the Worker, so a new version carries its own
|
|
@@ -1088,14 +1194,31 @@ On Postgres the engine also keeps:
|
|
|
1088
1194
|
- `POST /devices` takes `{ userId, token, platform, ts? }`;
|
|
1089
1195
|
`POST /devices/unbind` and `POST /devices/remove` take `{ token }`.
|
|
1090
1196
|
- `POST /messages/opened` takes `{ userId, messageId }`.
|
|
1091
|
-
- `
|
|
1197
|
+
- `GET /subscriptions/:userId` answers `{ email: { all, topics }, push: { all, topics } }`
|
|
1198
|
+
(`true` is subscribed; each channel's `topics` has one key per topic the
|
|
1199
|
+
deployed bundle declares). `PUT /subscriptions/:userId` takes a partial body
|
|
1200
|
+
`{ email?: { all?, topics? }, push?: { all?, topics? } }` and changes only
|
|
1201
|
+
what it names, per channel: `all: false` opts out of the whole channel,
|
|
1202
|
+
`topics: { newsletter: false }` of one topic on it, and `all: true` clears
|
|
1203
|
+
the channel-wide opt-out only (a topic opted out on its own stays off until
|
|
1204
|
+
named). An answer's `all: false` masks every topic of that channel, whatever
|
|
1205
|
+
the topic's own flag says. An undeclared topic, an unknown channel or a
|
|
1206
|
+
non-boolean answers 400 with the field paths; an erased user 410. Changing
|
|
1207
|
+
`email.all` wakes the waits on `reachable('email')`, `push.all` those on
|
|
1208
|
+
`reachable('push')`.
|
|
1209
|
+
- `POST /users/forget` takes `{ userId }`; the erasure takes the user's opt-outs
|
|
1210
|
+
with the rest, since nothing is ever sent to that id again.
|
|
1092
1211
|
- `POST /deploy` takes a compiled bundle and swaps the routing table atomically.
|
|
1093
1212
|
|
|
1094
1213
|
`journeyWorker` adds `GET /bundle` (the compiled config), `POST /bundle/deploy`
|
|
1095
1214
|
(deploy it), `GET /preview/<key>`, `POST /segments/sweep`, and the `/t/...`
|
|
1096
|
-
tracking routes recipients open, which need no bearer.
|
|
1097
|
-
|
|
1098
|
-
|
|
1215
|
+
tracking routes recipients open, which need no bearer. `POST /t/u/<message>`
|
|
1216
|
+
(the one-click unsubscribe, and the confirmation page's first button) opts the
|
|
1217
|
+
recipient out of that email's topic; with `?scope=all` (the second button) out
|
|
1218
|
+
of every email. A transactional email never carried the link, so every `/t/u`
|
|
1219
|
+
variant answers 404 for it. With a contract, an event or identify it refuses is
|
|
1220
|
+
answered with a 400 and the field paths, before anything is stored, woken or
|
|
1221
|
+
started.
|
|
1099
1222
|
|
|
1100
1223
|
### Give each event an `id`
|
|
1101
1224
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// \0rolldown/runtime.js
|
|
2
2
|
var __create = Object.create;
|
|
3
3
|
var __defProp = Object.defineProperty;
|
|
4
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -19,5 +19,4 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
19
19
|
value: mod,
|
|
20
20
|
enumerable: true
|
|
21
21
|
}) : target, mod));
|
|
22
|
-
//#endregion
|
|
23
22
|
exports.__toESM = __toESM;
|
package/dist/apns-relay.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_http2_client = require("./http2-client.cjs");
|
|
3
3
|
let node_http = require("node:http");
|
|
4
|
-
|
|
4
|
+
// src/apns-relay.ts
|
|
5
5
|
const DEFAULT_ALLOW = ["https://api.push.apple.com", "https://api.sandbox.push.apple.com"];
|
|
6
6
|
/** Request headers APNs reads; everything else (host, content-length, …) is the relay's own business. */
|
|
7
7
|
const FORWARDED = [
|
|
@@ -86,7 +86,6 @@ function startApnsRelay(options) {
|
|
|
86
86
|
});
|
|
87
87
|
});
|
|
88
88
|
}
|
|
89
|
-
//#endregion
|
|
90
89
|
exports.startApnsRelay = startApnsRelay;
|
|
91
90
|
|
|
92
91
|
//# sourceMappingURL=apns-relay.cjs.map
|
package/dist/apns-relay.d.cts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//#region src/apns-relay.d.ts
|
|
2
1
|
/**
|
|
3
2
|
* `@shware/workflow/apns-relay` — a development-only HTTP/1.1 → HTTP/2 relay
|
|
4
3
|
* for APNs, run under Node next to `wrangler dev`.
|
|
@@ -24,5 +23,4 @@ export interface ApnsRelay {
|
|
|
24
23
|
close(): Promise<void>;
|
|
25
24
|
}
|
|
26
25
|
export declare function startApnsRelay(options: ApnsRelayOptions): Promise<ApnsRelay>;
|
|
27
|
-
//#endregion
|
|
28
26
|
//# sourceMappingURL=apns-relay.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apns-relay.d.cts","names":[],"sources":["../src/apns-relay.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"apns-relay.d.cts","names":[],"sources":["../src/apns-relay.ts"],"mappings":";;;;;;;;;;;;;iBAiBiB;EACf;;EAEA;;EAEA;;iBAGe;EACf;EACA,SAAS;;wBAiBK,eAAe,SAAS,mBAAmB,QAAQ"}
|
package/dist/apns-relay.d.mts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//#region src/apns-relay.d.ts
|
|
2
1
|
/**
|
|
3
2
|
* `@shware/workflow/apns-relay` — a development-only HTTP/1.1 → HTTP/2 relay
|
|
4
3
|
* for APNs, run under Node next to `wrangler dev`.
|
|
@@ -24,5 +23,4 @@ export interface ApnsRelay {
|
|
|
24
23
|
close(): Promise<void>;
|
|
25
24
|
}
|
|
26
25
|
export declare function startApnsRelay(options: ApnsRelayOptions): Promise<ApnsRelay>;
|
|
27
|
-
//#endregion
|
|
28
26
|
//# sourceMappingURL=apns-relay.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apns-relay.d.mts","names":[],"sources":["../src/apns-relay.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"apns-relay.d.mts","names":[],"sources":["../src/apns-relay.ts"],"mappings":";;;;;;;;;;;;;iBAiBiB;EACf;;EAEA;;EAEA;;iBAGe;EACf;EACA,SAAS;;wBAiBK,eAAe,SAAS,mBAAmB,QAAQ"}
|
package/dist/apns-relay.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { http2Sessions } from "./http2-client.mjs";
|
|
2
2
|
import { createServer } from "node:http";
|
|
3
|
-
|
|
3
|
+
// src/apns-relay.ts
|
|
4
4
|
const DEFAULT_ALLOW = ["https://api.push.apple.com", "https://api.sandbox.push.apple.com"];
|
|
5
5
|
/** Request headers APNs reads; everything else (host, content-length, …) is the relay's own business. */
|
|
6
6
|
const FORWARDED = [
|
|
@@ -85,7 +85,6 @@ function startApnsRelay(options) {
|
|
|
85
85
|
});
|
|
86
86
|
});
|
|
87
87
|
}
|
|
88
|
-
//#endregion
|
|
89
88
|
export { startApnsRelay };
|
|
90
89
|
|
|
91
90
|
//# sourceMappingURL=apns-relay.mjs.map
|
package/dist/apns.cjs
CHANGED
|
@@ -2,7 +2,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
2
2
|
const require_engine_retry = require("./engine/retry.cjs");
|
|
3
3
|
const require_jwt = require("./jwt.cjs");
|
|
4
4
|
let node_buffer = require("node:buffer");
|
|
5
|
-
|
|
5
|
+
// src/apns.ts
|
|
6
6
|
const APNS_HOSTS = {
|
|
7
7
|
sandbox: "https://api.sandbox.push.apple.com",
|
|
8
8
|
production: "https://api.push.apple.com"
|
|
@@ -196,7 +196,6 @@ function apnsKeyFromBase64(value) {
|
|
|
196
196
|
if (!pem.includes("-----BEGIN PRIVATE KEY-----")) throw new require_engine_retry.PermanentError("APNs key is not a PKCS #8 PEM (.p8) file");
|
|
197
197
|
return pem;
|
|
198
198
|
}
|
|
199
|
-
//#endregion
|
|
200
199
|
exports.APNS_HOSTS = APNS_HOSTS;
|
|
201
200
|
exports.ApnsProviderToken = ApnsProviderToken;
|
|
202
201
|
exports.ApnsPushSender = ApnsPushSender;
|
package/dist/apns.d.cts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { MessageSender, OutboundMessage, SendResult } from "./engine/ports.cjs";
|
|
2
2
|
import "./engine/index.cjs";
|
|
3
3
|
import { PushRenderer } from "./push.cjs";
|
|
4
|
-
//#region src/apns.d.ts
|
|
5
4
|
/**
|
|
6
5
|
* `@shware/workflow/apns` — iOS push delivery straight to the Apple Push
|
|
7
6
|
* Notification service, token-based (an APNs auth key, the `.p8` file from the
|
|
@@ -105,5 +104,4 @@ export declare function apnsReason(body: string): string;
|
|
|
105
104
|
export declare function signApnsJwt(key: ApnsProviderTokenOptions, nowMs?: number): Promise<string>;
|
|
106
105
|
/** The `.p8` as it is usually shipped in a secret: base64 of the file. */
|
|
107
106
|
export declare function apnsKeyFromBase64(value: string): string;
|
|
108
|
-
//#endregion
|
|
109
107
|
//# sourceMappingURL=apns.d.cts.map
|
package/dist/apns.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apns.d.cts","names":[],"sources":["../src/apns.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"apns.d.cts","names":[],"sources":["../src/apns.ts"],"mappings":";;;;;;;;;;;;;;;;iBAoBiB;;EAEf;;EAEA;;EAEA;;iBAGe;;EAEf,aAAa;;EAEb;;;;;;;EAOA;EACA,QAAQ;;;;;;EAMR;;EAEA,eAAe;;qBAGJ;WACX;WACA;;;;;;;;;;;;;;;;;;;;;qBAsBW,oBAAoB;;;;;;;;wBAsBX,cAAc,yBAAyB;;;;;;;;qBAqBhD;mBAIkB;UAHrB;UACA;EAER,YAA6B,SAAS;;EAGhC,MAAM,eAAmB;;EAgB/B;;qBAKW,0BAA0B;mBACR;EAA7B,YAA6B,SAAS;EAEhC,KAAK,SAAS,kBAAkB,eAAe;;;wBAkFvC,WAAW;;wBAUL,YACpB,KAAK,0BACL,iBACC;;wBAoBa,kBAAkB"}
|
package/dist/apns.d.mts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { MessageSender, OutboundMessage, SendResult } from "./engine/ports.mjs";
|
|
2
2
|
import "./engine/index.mjs";
|
|
3
3
|
import { PushRenderer } from "./push.mjs";
|
|
4
|
-
//#region src/apns.d.ts
|
|
5
4
|
/**
|
|
6
5
|
* `@shware/workflow/apns` — iOS push delivery straight to the Apple Push
|
|
7
6
|
* Notification service, token-based (an APNs auth key, the `.p8` file from the
|
|
@@ -105,5 +104,4 @@ export declare function apnsReason(body: string): string;
|
|
|
105
104
|
export declare function signApnsJwt(key: ApnsProviderTokenOptions, nowMs?: number): Promise<string>;
|
|
106
105
|
/** The `.p8` as it is usually shipped in a secret: base64 of the file. */
|
|
107
106
|
export declare function apnsKeyFromBase64(value: string): string;
|
|
108
|
-
//#endregion
|
|
109
107
|
//# sourceMappingURL=apns.d.mts.map
|
package/dist/apns.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apns.d.mts","names":[],"sources":["../src/apns.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"apns.d.mts","names":[],"sources":["../src/apns.ts"],"mappings":";;;;;;;;;;;;;;;;iBAoBiB;;EAEf;;EAEA;;EAEA;;iBAGe;;EAEf,aAAa;;EAEb;;;;;;;EAOA;EACA,QAAQ;;;;;;EAMR;;EAEA,eAAe;;qBAGJ;WACX;WACA;;;;;;;;;;;;;;;;;;;;;qBAsBW,oBAAoB;;;;;;;;wBAsBX,cAAc,yBAAyB;;;;;;;;qBAqBhD;mBAIkB;UAHrB;UACA;EAER,YAA6B,SAAS;;EAGhC,MAAM,eAAmB;;EAgB/B;;qBAKW,0BAA0B;mBACR;EAA7B,YAA6B,SAAS;EAEhC,KAAK,SAAS,kBAAkB,eAAe;;;wBAkFvC,WAAW;;wBAUL,YACpB,KAAK,0BACL,iBACC;;wBAoBa,kBAAkB"}
|
package/dist/apns.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { PermanentError, isRetryableStatus } from "./engine/retry.mjs";
|
|
2
2
|
import { base64url, pemToDer } from "./jwt.mjs";
|
|
3
3
|
import { Buffer } from "node:buffer";
|
|
4
|
-
|
|
4
|
+
// src/apns.ts
|
|
5
5
|
const APNS_HOSTS = {
|
|
6
6
|
sandbox: "https://api.sandbox.push.apple.com",
|
|
7
7
|
production: "https://api.push.apple.com"
|
|
@@ -195,7 +195,6 @@ function apnsKeyFromBase64(value) {
|
|
|
195
195
|
if (!pem.includes("-----BEGIN PRIVATE KEY-----")) throw new PermanentError("APNs key is not a PKCS #8 PEM (.p8) file");
|
|
196
196
|
return pem;
|
|
197
197
|
}
|
|
198
|
-
//#endregion
|
|
199
198
|
export { APNS_HOSTS, ApnsProviderToken, ApnsPushSender, DEAD_TOKEN_REASONS, apnsIdFromKey, apnsKeyFromBase64, apnsReason, signApnsJwt };
|
|
200
199
|
|
|
201
200
|
//# sourceMappingURL=apns.mjs.map
|
package/dist/aws/engine-step.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_engine_retry = require("../engine/retry.cjs");
|
|
3
3
|
let _aws_durable_execution_sdk_js = require("@aws/durable-execution-sdk-js");
|
|
4
|
-
|
|
4
|
+
// src/aws/engine-step.ts
|
|
5
5
|
/**
|
|
6
6
|
* EngineStep over a Lambda durable execution.
|
|
7
7
|
*
|
|
@@ -96,7 +96,6 @@ var AwsEngineStep = class {
|
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
};
|
|
99
|
-
//#endregion
|
|
100
99
|
exports.AwsEngineStep = AwsEngineStep;
|
|
101
100
|
exports.stepRetry = stepRetry;
|
|
102
101
|
|
|
@@ -2,7 +2,6 @@ import { EngineStep } from "../engine/ports.cjs";
|
|
|
2
2
|
import { ExecutionRegistry, JourneyStore } from "../store/index.cjs";
|
|
3
3
|
import { LambdaApi } from "./lambda-api.cjs";
|
|
4
4
|
import { DurableContext, RetryDecision } from "@aws/durable-execution-sdk-js";
|
|
5
|
-
//#region src/aws/engine-step.d.ts
|
|
6
5
|
/**
|
|
7
6
|
* EngineStep over a Lambda durable execution.
|
|
8
7
|
*
|
|
@@ -40,5 +39,4 @@ export declare class AwsEngineStep implements EngineStep {
|
|
|
40
39
|
deadlineMs: number;
|
|
41
40
|
}): Promise<'event' | 'timeout'>;
|
|
42
41
|
}
|
|
43
|
-
//#endregion
|
|
44
42
|
//# sourceMappingURL=engine-step.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine-step.d.cts","names":[],"sources":["../../src/aws/engine-step.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"engine-step.d.cts","names":[],"sources":["../../src/aws/engine-step.ts"],"mappings":";;;;;;;;;;;;;;;wBAuBgB,UAAU,OAAO,OAAO,kBAAkB;qBAa7C,yBAAyB;mBAKjB;mBACA;mBACA;mBACA;mBACA;mBACA;UATX;UACA;EAER,YACmB,SAAS,gBACT,OAAO,cACP,YAAY,mBACZ,QAAQ,WACR,gBACA;EAGnB,GAAG,GAAG,cAAc,UAAU,QAAQ,KAAK,QAAQ;EAI7C,MAAM,cAAc,aAAa;EAMjC,UACJ,cACA,2BACA;IAAW;MACV;EAoCG,YAAY,eAAe;;;;;EAsB3B,YACJ,cACA;IAAS;IAAmB;MAC3B"}
|
|
@@ -2,7 +2,6 @@ import { EngineStep } from "../engine/ports.mjs";
|
|
|
2
2
|
import { ExecutionRegistry, JourneyStore } from "../store/index.mjs";
|
|
3
3
|
import { LambdaApi } from "./lambda-api.mjs";
|
|
4
4
|
import { DurableContext, RetryDecision } from "@aws/durable-execution-sdk-js";
|
|
5
|
-
//#region src/aws/engine-step.d.ts
|
|
6
5
|
/**
|
|
7
6
|
* EngineStep over a Lambda durable execution.
|
|
8
7
|
*
|
|
@@ -40,5 +39,4 @@ export declare class AwsEngineStep implements EngineStep {
|
|
|
40
39
|
deadlineMs: number;
|
|
41
40
|
}): Promise<'event' | 'timeout'>;
|
|
42
41
|
}
|
|
43
|
-
//#endregion
|
|
44
42
|
//# sourceMappingURL=engine-step.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine-step.d.mts","names":[],"sources":["../../src/aws/engine-step.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"engine-step.d.mts","names":[],"sources":["../../src/aws/engine-step.ts"],"mappings":";;;;;;;;;;;;;;;wBAuBgB,UAAU,OAAO,OAAO,kBAAkB;qBAa7C,yBAAyB;mBAKjB;mBACA;mBACA;mBACA;mBACA;mBACA;UATX;UACA;EAER,YACmB,SAAS,gBACT,OAAO,cACP,YAAY,mBACZ,QAAQ,WACR,gBACA;EAGnB,GAAG,GAAG,cAAc,UAAU,QAAQ,KAAK,QAAQ;EAI7C,MAAM,cAAc,aAAa;EAMjC,UACJ,cACA,2BACA;IAAW;MACV;EAoCG,YAAY,eAAe;;;;;EAsB3B,YACJ,cACA;IAAS;IAAmB;MAC3B"}
|
package/dist/aws/engine-step.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isRetryable } from "../engine/retry.mjs";
|
|
2
2
|
import { CallbackTimeoutError } from "@aws/durable-execution-sdk-js";
|
|
3
|
-
|
|
3
|
+
// src/aws/engine-step.ts
|
|
4
4
|
/**
|
|
5
5
|
* EngineStep over a Lambda durable execution.
|
|
6
6
|
*
|
|
@@ -95,7 +95,6 @@ var AwsEngineStep = class {
|
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
};
|
|
98
|
-
//#endregion
|
|
99
98
|
export { AwsEngineStep, stepRetry };
|
|
100
99
|
|
|
101
100
|
//# sourceMappingURL=engine-step.mjs.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
let node_buffer = require("node:buffer");
|
|
3
|
-
|
|
3
|
+
// src/aws/function-url.ts
|
|
4
4
|
/**
|
|
5
5
|
* An HTTP request, told apart by the event's own top-level shape — the Function URL wraps
|
|
6
6
|
* every request in `requestContext.http`, so no request body can pass for anything else.
|
|
@@ -46,7 +46,6 @@ async function fromResponse(response, method) {
|
|
|
46
46
|
isBase64Encoded: !textual
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
|
-
//#endregion
|
|
50
49
|
exports.fromResponse = fromResponse;
|
|
51
50
|
exports.isFunctionUrlEvent = isFunctionUrlEvent;
|
|
52
51
|
exports.toRequest = toRequest;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//#region src/aws/function-url.d.ts
|
|
2
1
|
/**
|
|
3
2
|
* Lambda Function URL events (payload format 2.0) as fetch `Request`s, and `Response`s back as
|
|
4
3
|
* the result Lambda turns into the HTTP response — so the host's HTTP API runs unchanged.
|
|
@@ -34,5 +33,4 @@ export interface FunctionUrlResult {
|
|
|
34
33
|
export declare function isFunctionUrlEvent(event: unknown): event is FunctionUrlEvent;
|
|
35
34
|
export declare function toRequest(event: FunctionUrlEvent): Request;
|
|
36
35
|
export declare function fromResponse(response: Response, method: string): Promise<FunctionUrlResult>;
|
|
37
|
-
//#endregion
|
|
38
36
|
//# sourceMappingURL=function-url.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"function-url.d.cts","names":[],"sources":["../../src/aws/function-url.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"function-url.d.cts","names":[],"sources":["../../src/aws/function-url.ts"],"mappings":";;;;iBAMiB;EACf;EACA;EACA;EACA,UAAU;EACV;EACA;EACA;EACA;IACE;IACA;MAAQ;MAAgB;MAAmB;;;;iBAI9B;EACf;EACA,SAAS;EACT;EACA;EACA;;;;;;wBAOc,mBAAmB,iBAAiB,SAAS;wBAoB7C,UAAU,OAAO,mBAAmB;wBA4B9B,aAAa,UAAU,UAAU,iBAAiB,QAAQ"}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//#region src/aws/function-url.d.ts
|
|
2
1
|
/**
|
|
3
2
|
* Lambda Function URL events (payload format 2.0) as fetch `Request`s, and `Response`s back as
|
|
4
3
|
* the result Lambda turns into the HTTP response — so the host's HTTP API runs unchanged.
|
|
@@ -34,5 +33,4 @@ export interface FunctionUrlResult {
|
|
|
34
33
|
export declare function isFunctionUrlEvent(event: unknown): event is FunctionUrlEvent;
|
|
35
34
|
export declare function toRequest(event: FunctionUrlEvent): Request;
|
|
36
35
|
export declare function fromResponse(response: Response, method: string): Promise<FunctionUrlResult>;
|
|
37
|
-
//#endregion
|
|
38
36
|
//# sourceMappingURL=function-url.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"function-url.d.mts","names":[],"sources":["../../src/aws/function-url.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"function-url.d.mts","names":[],"sources":["../../src/aws/function-url.ts"],"mappings":";;;;iBAMiB;EACf;EACA;EACA;EACA,UAAU;EACV;EACA;EACA;EACA;IACE;IACA;MAAQ;MAAgB;MAAmB;;;;iBAI9B;EACf;EACA,SAAS;EACT;EACA;EACA;;;;;;wBAOc,mBAAmB,iBAAiB,SAAS;wBAoB7C,UAAU,OAAO,mBAAmB;wBA4B9B,aAAa,UAAU,UAAU,iBAAiB,QAAQ"}
|