@shware/workflow 5.0.1 → 6.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +224 -53
- package/dist/aws/index.cjs +1 -1
- package/dist/aws/index.mjs +1 -1
- package/dist/client.cjs +68 -4
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +39 -3
- package/dist/client.d.cts.map +1 -1
- package/dist/client.d.mts +39 -3
- package/dist/client.d.mts.map +1 -1
- package/dist/client.mjs +68 -4
- package/dist/client.mjs.map +1 -1
- package/dist/cloudflare/index.cjs +4 -4
- package/dist/cloudflare/index.mjs +4 -4
- package/dist/cloudflare/runner.cjs +1 -1
- package/dist/cloudflare/runner.d.cts +1 -1
- package/dist/cloudflare/runner.d.mts +1 -1
- package/dist/cloudflare/runner.mjs +1 -1
- package/dist/config.cjs +24 -3
- package/dist/config.cjs.map +1 -1
- package/dist/config.d.cts +15 -9
- package/dist/config.d.cts.map +1 -1
- package/dist/config.d.mts +15 -9
- package/dist/config.d.mts.map +1 -1
- package/dist/config.mjs +25 -4
- package/dist/config.mjs.map +1 -1
- package/dist/contract.cjs +11 -0
- package/dist/contract.cjs.map +1 -1
- package/dist/contract.d.cts +28 -7
- package/dist/contract.d.cts.map +1 -1
- package/dist/contract.d.mts +28 -7
- package/dist/contract.d.mts.map +1 -1
- package/dist/contract.mjs +11 -0
- package/dist/contract.mjs.map +1 -1
- package/dist/dsl/bundle.cjs +4 -3
- package/dist/dsl/bundle.cjs.map +1 -1
- package/dist/dsl/bundle.d.cts +5 -1
- package/dist/dsl/bundle.d.cts.map +1 -1
- package/dist/dsl/bundle.d.mts +5 -1
- package/dist/dsl/bundle.d.mts.map +1 -1
- package/dist/dsl/bundle.mjs +4 -3
- package/dist/dsl/bundle.mjs.map +1 -1
- package/dist/dsl/flow.cjs +2 -1
- package/dist/dsl/flow.cjs.map +1 -1
- package/dist/dsl/flow.d.cts +1 -1
- package/dist/dsl/flow.d.cts.map +1 -1
- package/dist/dsl/flow.d.mts +1 -1
- package/dist/dsl/flow.d.mts.map +1 -1
- package/dist/dsl/flow.mjs +2 -1
- package/dist/dsl/flow.mjs.map +1 -1
- package/dist/dsl/workflow.cjs +2 -2
- package/dist/dsl/workflow.mjs +2 -2
- package/dist/email-sender.cjs +3 -2
- package/dist/email-sender.cjs.map +1 -1
- package/dist/email-sender.d.cts +5 -4
- package/dist/email-sender.d.cts.map +1 -1
- package/dist/email-sender.d.mts +5 -4
- package/dist/email-sender.d.mts.map +1 -1
- package/dist/email-sender.mjs +3 -2
- package/dist/email-sender.mjs.map +1 -1
- package/dist/email.cjs +66 -11
- package/dist/email.cjs.map +1 -1
- package/dist/email.d.cts +72 -12
- package/dist/email.d.cts.map +1 -1
- package/dist/email.d.mts +72 -12
- package/dist/email.d.mts.map +1 -1
- package/dist/email.mjs +63 -12
- package/dist/email.mjs.map +1 -1
- package/dist/engine/condition.cjs.map +1 -1
- package/dist/engine/condition.d.cts.map +1 -1
- package/dist/engine/condition.d.mts.map +1 -1
- package/dist/engine/condition.mjs.map +1 -1
- package/dist/engine/index.cjs +1 -1
- package/dist/engine/index.mjs +1 -1
- package/dist/engine/interpreter.cjs +21 -4
- package/dist/engine/interpreter.cjs.map +1 -1
- package/dist/engine/interpreter.mjs +21 -4
- package/dist/engine/interpreter.mjs.map +1 -1
- package/dist/engine/ports.d.cts +14 -2
- package/dist/engine/ports.d.cts.map +1 -1
- package/dist/engine/ports.d.mts +14 -2
- package/dist/engine/ports.d.mts.map +1 -1
- package/dist/host/channels.cjs +1 -1
- package/dist/host/channels.mjs +1 -1
- package/dist/host/execute.cjs +2 -2
- package/dist/host/execute.d.cts +1 -1
- package/dist/host/execute.d.mts +1 -1
- package/dist/host/execute.mjs +2 -2
- package/dist/host/http.cjs +75 -5
- package/dist/host/http.cjs.map +1 -1
- package/dist/host/http.d.cts +2 -2
- package/dist/host/http.d.cts.map +1 -1
- package/dist/host/http.d.mts +2 -2
- package/dist/host/http.d.mts.map +1 -1
- package/dist/host/http.mjs +76 -6
- package/dist/host/http.mjs.map +1 -1
- package/dist/host/ingest.cjs +173 -4
- package/dist/host/ingest.cjs.map +1 -1
- package/dist/host/ingest.d.cts +99 -0
- package/dist/host/ingest.d.cts.map +1 -1
- package/dist/host/ingest.d.mts +99 -0
- package/dist/host/ingest.d.mts.map +1 -1
- package/dist/host/ingest.mjs +167 -5
- package/dist/host/ingest.mjs.map +1 -1
- package/dist/host/journey-host.cjs +10 -8
- package/dist/host/journey-host.cjs.map +1 -1
- package/dist/host/journey-host.mjs +10 -8
- package/dist/host/journey-host.mjs.map +1 -1
- package/dist/index.cjs +8 -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 +11 -2
- package/dist/ir.cjs.map +1 -1
- package/dist/ir.d.cts +17 -0
- package/dist/ir.d.cts.map +1 -1
- package/dist/ir.d.mts +17 -0
- package/dist/ir.d.mts.map +1 -1
- package/dist/ir.mjs +11 -3
- package/dist/ir.mjs.map +1 -1
- package/dist/react-email.cjs +6 -5
- package/dist/react-email.cjs.map +1 -1
- package/dist/react-email.d.cts.map +1 -1
- package/dist/react-email.d.mts.map +1 -1
- package/dist/react-email.mjs +6 -5
- package/dist/react-email.mjs.map +1 -1
- package/dist/reporting/observer.cjs +4 -1
- package/dist/reporting/observer.cjs.map +1 -1
- package/dist/reporting/observer.d.cts +2 -1
- package/dist/reporting/observer.d.cts.map +1 -1
- package/dist/reporting/observer.d.mts +2 -1
- package/dist/reporting/observer.d.mts.map +1 -1
- package/dist/reporting/observer.mjs +4 -1
- package/dist/reporting/observer.mjs.map +1 -1
- package/dist/reporting/tracking.cjs +31 -10
- package/dist/reporting/tracking.cjs.map +1 -1
- package/dist/reporting/tracking.d.cts +15 -6
- package/dist/reporting/tracking.d.cts.map +1 -1
- package/dist/reporting/tracking.d.mts +15 -6
- package/dist/reporting/tracking.d.mts.map +1 -1
- package/dist/reporting/tracking.mjs +31 -11
- package/dist/reporting/tracking.mjs.map +1 -1
- package/dist/store/facts.cjs +3 -3
- package/dist/store/facts.cjs.map +1 -1
- package/dist/store/facts.d.cts +2 -2
- package/dist/store/facts.d.cts.map +1 -1
- package/dist/store/facts.d.mts +2 -2
- package/dist/store/facts.d.mts.map +1 -1
- package/dist/store/facts.mjs +3 -3
- package/dist/store/facts.mjs.map +1 -1
- package/dist/store/index.cjs +1 -1
- package/dist/store/index.cjs.map +1 -1
- package/dist/store/index.d.cts +44 -13
- package/dist/store/index.d.cts.map +1 -1
- package/dist/store/index.d.mts +44 -13
- package/dist/store/index.d.mts.map +1 -1
- package/dist/store/index.mjs +1 -1
- package/dist/store/index.mjs.map +1 -1
- package/dist/store/postgres-reporting.cjs +30 -10
- package/dist/store/postgres-reporting.cjs.map +1 -1
- package/dist/store/postgres-reporting.d.cts +4 -2
- package/dist/store/postgres-reporting.d.cts.map +1 -1
- package/dist/store/postgres-reporting.d.mts +4 -2
- package/dist/store/postgres-reporting.d.mts.map +1 -1
- package/dist/store/postgres-reporting.mjs +30 -10
- package/dist/store/postgres-reporting.mjs.map +1 -1
- package/dist/store/postgres.cjs +39 -3
- package/dist/store/postgres.cjs.map +1 -1
- package/dist/store/postgres.d.cts +4 -2
- package/dist/store/postgres.d.cts.map +1 -1
- package/dist/store/postgres.d.mts +4 -2
- package/dist/store/postgres.d.mts.map +1 -1
- package/dist/store/postgres.mjs +40 -4
- package/dist/store/postgres.mjs.map +1 -1
- package/dist/store/tables.cjs +75 -11
- package/dist/store/tables.cjs.map +1 -1
- package/dist/store/tables.d.cts +17 -1
- package/dist/store/tables.d.cts.map +1 -1
- package/dist/store/tables.d.mts +17 -1
- package/dist/store/tables.d.mts.map +1 -1
- package/dist/store/tables.mjs +74 -12
- package/dist/store/tables.mjs.map +1 -1
- package/dist/templates.cjs +60 -7
- package/dist/templates.cjs.map +1 -1
- package/dist/templates.d.cts +101 -13
- package/dist/templates.d.cts.map +1 -1
- package/dist/templates.d.mts +101 -13
- package/dist/templates.d.mts.map +1 -1
- package/dist/templates.mjs +61 -9
- package/dist/templates.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"journey-host.mjs","names":[],"sources":["../../src/host/journey-host.ts"],"sourcesContent":["import * as z from 'zod/mini';\nimport type * as core from 'zod/v4/core';\nimport { type JourneyConfig, compileConfig } from '../config';\nimport { type Contract, ContractValidationError, checkEvent } from '../contract';\nimport { assertEmailConfig } from '../email';\nimport type { EmailTransport } from '../email-sender';\nimport { RegistryActionInvoker } from '../engine/actions';\nimport { profileUpdatedEvent } from '../engine/condition';\nimport type { JourneyObserver, MessageSender } from '../engine/ports';\nimport { RECIPIENT_PROPERTY } from '../engine/recipient';\nimport { scaleSegmentWindows } from '../engine/time-scale';\nimport type { BundleIR, NodeIR } from '../ir';\nimport { registryRenderer } from '../react-email';\nimport { reportingObserver } from '../reporting/observer';\nimport type { TrackingOptions } from '../reporting/tracking';\nimport type { JourneyRuntimeOptions } from '../runtime';\nimport type { BundleVersion, JourneyStore, StoreLease } from '../store/index';\nimport { DEFAULT_SCHEMA } from '../store/tables';\nimport { indexTemplates, registryValidator } from '../templates';\nimport type { Authorize } from './auth';\nimport { channelsFromConfig, createProviderCache, runtimeFromConfig } from './channels';\nimport type { JourneyRunParts } from './execute';\nimport { serve } from './http';\nimport {\n type SweepResult,\n deployBundle,\n ingestEvent,\n sweepSegments,\n wakeSubscribers,\n} from './ingest';\nimport type { JourneyEnv, JourneyParams } from './platform';\n\n/**\n * What a journey host is, apart from the runtime it runs on: the validated environment, the\n * store lease, the outlets a run is wired to, the HTTP API (`http.ts`, with the host's parts\n * plugged in) and the bundle that goes live with the code. `journeyWorker` (Cloudflare) and\n * `journeyLambda` (AWS) are this plus how their platform starts, wakes and stops instances.\n */\n\nexport interface JourneyHostOptions<Env extends JourneyEnv> {\n /**\n * The project's `workflow.config.ts` (`defineConfig`): what the host deploys (`GET /bundle`,\n * `POST /bundle/deploy` serve `compileConfig`), the template registries the senders, the\n * validator and `/preview` render from, the contract every `/events` and `/identify` is\n * checked against, the email defaults, actions, and the runtime / APNs / FCM settings.\n * Secrets come from the env under the names `JourneyEnv` documents.\n */\n config: JourneyConfig;\n /**\n * The data plane, opened per request and per workflow run. Return a `StoreLease` when the\n * client must be released afterwards (a Postgres connection). `database` is the config's\n * (`schema` defaulting to `workflow`):\n *\n * store: (env, database) => postgresJsStore(env.DATABASE_URL, database)\n */\n store: (env: Env, database: { schema: string }) => JourneyStore | StoreLease;\n /**\n * Where journey emails go: a transport built from the validated env — `sesEmailTransport(...)`,\n * `cloudflareEmailTransport(...)`, or your own. Which one is independent of where the workflow\n * runs. Without it the email channel fails its sends, saying so.\n */\n email?: (env: Env) => EmailTransport;\n /**\n * Reporting hooks: node arrivals, every copy sent (with the tracking it should carry — click\n * links and an open pixel for email, payload data for a push), and how each instance ended.\n * By default the store records them itself, and the host serves the email tracking routes\n * (`/t/...`, on `WORKFLOW_ORIGIN`); override to report elsewhere.\n */\n observer?: (env: Env, store: JourneyStore) => JourneyObserver;\n /** Message outlet override (tests, a custom gateway): replaces every channel, `email` included. */\n messages?: (env: Env, store: JourneyStore) => MessageSender;\n /** Per-instance runtime options override (a tester in fast-forward); by default `config.runtime` for `ENVIRONMENT`. */\n runtime?: (env: Env, params: JourneyParams) => JourneyRuntimeOptions;\n /**\n * The host's environment, as a zod schema over its vars and secrets (bindings pass through\n * untouched): checked the first time the host runs, so a missing or malformed variable fails\n * there — every problem listed — not at the send that needs it. Webhook templates read their\n * URL from the parsed values: `webhook: (env: Env) => env.WEBHOOK_DISCORD_ALERTS`.\n */\n env?: core.$ZodType<object>;\n /** 'enforce' (default) answers an event or patch the contract refuses with a 400; 'report' only logs it. */\n contractMode?: 'enforce' | 'report';\n /**\n * Who may call the API routes, in whatever form the host chooses: an HMAC over the body, a\n * JWT, a header its proxy adds. Receives a copy of the request and the env; true lets it\n * through, false answers 401, a throw answers 500. The tracking routes and `/health` stay\n * public. Without it, the env's `API_TOKEN` as a bearer (`bearerMatches`), which production\n * then requires.\n *\n * authorize: async (request, env) =>\n * timingSafeEqual(request.headers.get('x-signature') ?? '', await hmac(env.SECRET, await request.text()))\n */\n authorize?: Authorize<Env>;\n}\n\n/** The host's environment failed its schema (`env` option). */\nexport class EnvValidationError extends Error {\n override readonly name = 'EnvValidationError';\n constructor(readonly issues: readonly string[]) {\n super(`the host's environment is invalid:\\n ${issues.join('\\n ')}`);\n }\n}\n\nexport interface JourneyHost<Env extends JourneyEnv> {\n config: JourneyConfig;\n /** The env, parsed by the schema once per env object (throws EnvValidationError). */\n checkEnv(env: Env): Env;\n /** A store for one request or run, its segment windows scaled with the runtime. */\n lease(env: Env): Required<StoreLease>;\n /** The bundle compiled into this code. */\n bundle(): BundleIR;\n /** The outlets one run is wired to (the store is the run's). */\n runParts(env: Env, store: JourneyStore, params: JourneyParams): JourneyRunParts;\n /** The HTTP API over one store: the ingest API plus tracking, the sweep, the bundle and previews. */\n handle(request: Request, env: Env, store: JourneyStore): Promise<Response>;\n /** The scheduled sweep (`sweepSegments`) on a store of its own. */\n sweep(env: Env): Promise<SweepResult>;\n /**\n * Make the bundle compiled into `version` live, once per process, only over an older live\n * version (the store records it). Undefined when there is nothing to do; a failure is logged\n * and retried a minute later.\n */\n applyOwnBundle(env: Env, version: BundleVersion | undefined): Promise<void> | undefined;\n}\n\n/** What the platform adapter itself contributes. */\nexport interface JourneyPlatform {\n /** An HTTP/2 fetch for APNs, where the runtime's own fetch is HTTP/1.1 (Node). */\n apnsFetch?: typeof fetch;\n}\n\nfunction sendsEmail(nodes: readonly NodeIR[]): boolean {\n return nodes.some((node) => {\n switch (node.type) {\n case 'message':\n return node.channel === 'email';\n case 'wait_until':\n return Array.isArray(node.onTimeout) && sendsEmail(node.onTimeout);\n case 'branch':\n return (\n node.cases.some((branch) => sendsEmail(branch.flow)) || sendsEmail(node.otherwise ?? [])\n );\n case 'cohort':\n return node.arms.some((arm) => sendsEmail(arm.flow));\n default:\n return false;\n }\n });\n}\n\nconst describeIssue = (issue: { path: PropertyKey[]; message: string }): string =>\n `${issue.path.join('.') || '(root)'}: ${issue.message}`;\n\n/** An event the host emits itself, checked against the contract like any ingest. */\nfunction checkedPayload(\n contract: Contract | undefined,\n event: string,\n payload: Record<string, unknown>\n): Record<string, unknown> {\n if (contract === undefined) return payload;\n const verdict = checkEvent(contract, event, payload);\n if (!verdict.ok) throw new ContractValidationError(event, verdict.issues);\n return verdict.data;\n}\n\nexport function createJourneyHost<Env extends JourneyEnv>(\n options: JourneyHostOptions<Env>,\n platform: JourneyPlatform = {}\n): JourneyHost<Env> {\n const { config } = options;\n const emailIds = indexTemplates('email', config.templates?.email);\n // A config still on `emails` (its sender would be silently dropped) fails here, not at a send\n assertEmailConfig(config, emailIds.values());\n let compiled: BundleIR | undefined;\n const bundle = () => (compiled ??= compileConfig(config));\n // A journey that emails with nowhere to send fails here too, not at its first email\n const emailing = bundle().workflows.filter((workflow) => sendsEmail(workflow.flow));\n if (options.email === undefined && options.messages === undefined && emailing.length > 0) {\n throw new TypeError(\n `workflows ${emailing.map((w) => `'${w.id}'`).join(', ')} send email, but the host has no ` +\n '`email` transport: pass `email: (env) => cloudflareEmailTransport(...)` or `sesEmailTransport(...)`'\n );\n }\n // Parsed once per env object: the runtime hands the same one to every request of an isolate\n const checked = new WeakMap<object, Env>();\n const checkEnv = (env: Env): Env => {\n if (options.env === undefined) return env;\n const cached = checked.get(env);\n if (cached !== undefined) return cached;\n const result = z.safeParse(options.env, env);\n // Names and reasons only: the values are secrets\n if (!result.success) throw new EnvValidationError(result.error.issues.map(describeIssue));\n const merged: Env = { ...env, ...result.data };\n checked.set(env, merged);\n return merged;\n };\n const emails = config.templates?.email;\n const database = { schema: config.database?.schema ?? DEFAULT_SCHEMA };\n // In fast-forward, segment windows scale with the journeys reading them (time-scale.ts)\n const lease = (env: Env): Required<StoreLease> => {\n const opened = options.store(env, database);\n const noop = async () => {};\n const factor = runtimeFromConfig(config, env).timeScale;\n return 'store' in opened\n ? { store: scaleSegmentWindows(opened.store, factor), close: opened.close ?? noop }\n : { store: scaleSegmentWindows(opened, factor), close: noop };\n };\n\n // The provider clients (APNs JWT, FCM access token) live with the host, not with a run: a\n // run's env is a fresh object on every Lambda invocation, so nothing keyed on it would be reused\n const providers = createProviderCache();\n const messages = (env: Env, store: JourneyStore): MessageSender => {\n if (options.messages !== undefined) return options.messages(env, store);\n return channelsFromConfig(config, env, {\n store,\n email: options.email?.(env),\n apnsFetch: platform.apnsFetch,\n providers,\n });\n };\n\n const observer = (env: Env, store: JourneyStore): JourneyObserver => {\n if (options.observer !== undefined) return options.observer(env, store);\n return reportingObserver(store.reporting, env.WORKFLOW_ORIGIN);\n };\n\n const runParts = (env: Env, store: JourneyStore, params: JourneyParams): JourneyRunParts => ({\n store,\n env,\n runtime:\n options.runtime === undefined ? runtimeFromConfig(config, env) : options.runtime(env, params),\n messages: messages(env, store),\n // An action ref is a registered action structurally: the same object carries name, code and hash\n actions: new RegistryActionInvoker(config.actions ?? []),\n contract: config.contract,\n validator: registryValidator(config.templates ?? {}),\n observer: observer(env, store),\n });\n\n const preview = emails === undefined ? undefined : registryRenderer(emails, config.email);\n\n /**\n * The recipients' routes (`/t/*`): a push tapped open is recorded as the config's\n * `webPush.openedEvent` — keyed by the copy, so a second tap of the same notification is the\n * same event — and an unsubscribe wakes the waits reading email reachability (`reachable\n * ('email')` subscribes to the email property's channel), so an `exitWhen:\n * not(reachable('email'))` takes effect without waiting for a timeout.\n */\n const tracking = (env: Env, store: JourneyStore): TrackingOptions => {\n const opened = config.webPush?.openedEvent;\n return {\n product: config.title,\n quickMs: config.email?.tracking?.quickClickMs,\n pushOpened:\n opened === undefined\n ? undefined\n : async ({ messageId, userId, at }) => {\n const payload = checkedPayload(config.contract, opened, { message_id: messageId });\n const input = {\n id: `${opened}:${messageId}`,\n userId,\n event: opened,\n payload,\n ts: at,\n };\n await ingestEvent(env, input, store);\n },\n onUnsubscribed: async ({ userId }) => {\n const email = profileUpdatedEvent(RECIPIENT_PROPERTY.email ?? 'email');\n await wakeSubscribers(env, store, userId, [email]);\n },\n };\n };\n\n const handle = (request: Request, env: Env, store: JourneyStore): Promise<Response> =>\n serve(request, {\n env,\n store,\n validation: { contract: config.contract, mode: options.contractMode },\n authorize: options.authorize as Authorize<JourneyEnv> | undefined,\n host: {\n bundle,\n preview:\n preview === undefined ? undefined : { has: (id) => emailIds.has(id), render: preview },\n tracking: tracking(env, store),\n sweep: () => sweepSegments(env, store),\n },\n });\n\n const sweep = async (env: Env) => {\n const { store, close } = lease(env);\n try {\n return await sweepSegments(env, store);\n } finally {\n await close();\n }\n };\n\n // The bundle compiled into this version goes live on its own, once per process: the first\n // request or scheduled run reads which version's bundle is live (one row) and applies this one\n // only over an older version, so one still serving during a rollout can never put its routes\n // back. A failure (say the migrations have not run) is logged and retried a minute later. Only\n // the request that starts the check gets its promise back: a request context cannot wait on\n // another's I/O.\n let applying: Promise<void> | undefined;\n let retryAt = 0;\n const applyOwnBundle = (env: Env, version: BundleVersion | undefined) => {\n if (version === undefined || version.id === '' || Number.isNaN(version.createdAt)) {\n return undefined;\n }\n if (applying !== undefined || Date.now() < retryAt) return undefined;\n applying = (async () => {\n const { store, close } = lease(env);\n try {\n const live = await store.liveBundleVersion();\n if (live !== undefined && (live.id === version.id || live.createdAt >= version.createdAt))\n return;\n const result = await deployBundle(env, bundle(), store, version);\n if (result.applied) console.log(`[workflow] version ${version.id} is live`);\n } finally {\n await close();\n }\n })().catch((error: unknown) => {\n applying = undefined;\n retryAt = Date.now() + 60_000;\n console.error(`[workflow] could not make version ${version.id} live`, error);\n });\n return applying;\n };\n\n return { config, checkEnv, lease, bundle, runParts, handle, sweep, applyOwnBundle };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"journey-host.mjs","names":[],"sources":["../../src/host/journey-host.ts"],"sourcesContent":["import * as z from 'zod/mini';\nimport type * as core from 'zod/v4/core';\nimport { type JourneyConfig, compileConfig } from '../config';\nimport { type Contract, ContractValidationError, checkEvent } from '../contract';\nimport { assertEmailConfig } from '../email';\nimport type { EmailTransport } from '../email-sender';\nimport { RegistryActionInvoker } from '../engine/actions';\nimport { profileUpdatedEvent } from '../engine/condition';\nimport type { JourneyObserver, MessageSender } from '../engine/ports';\nimport { RECIPIENT_PROPERTY } from '../engine/recipient';\nimport { scaleSegmentWindows } from '../engine/time-scale';\nimport type { BundleIR, NodeIR } from '../ir';\nimport { registryRenderer } from '../react-email';\nimport { reportingObserver } from '../reporting/observer';\nimport type { TrackingOptions } from '../reporting/tracking';\nimport type { JourneyRuntimeOptions } from '../runtime';\nimport type { BundleVersion, JourneyStore, StoreLease } from '../store/index';\nimport { DEFAULT_SCHEMA } from '../store/tables';\nimport { indexTemplates, registryValidator } from '../templates';\nimport type { Authorize } from './auth';\nimport { channelsFromConfig, createProviderCache, runtimeFromConfig } from './channels';\nimport type { JourneyRunParts } from './execute';\nimport { serve } from './http';\nimport {\n type SweepResult,\n deployBundle,\n ingestEvent,\n sweepSegments,\n wakeSubscribers,\n} from './ingest';\nimport type { JourneyEnv, JourneyParams } from './platform';\n\n/**\n * What a journey host is, apart from the runtime it runs on: the validated environment, the\n * store lease, the outlets a run is wired to, the HTTP API (`http.ts`, with the host's parts\n * plugged in) and the bundle that goes live with the code. `journeyWorker` (Cloudflare) and\n * `journeyLambda` (AWS) are this plus how their platform starts, wakes and stops instances.\n */\n\nexport interface JourneyHostOptions<Env extends JourneyEnv> {\n /**\n * The project's `workflow.config.ts` (`defineConfig`): what the host deploys (`GET /bundle`,\n * `POST /bundle/deploy` serve `compileConfig`), the template registries the senders, the\n * validator and `/preview` render from, the contract every `/events` and `/identify` is\n * checked against, the email defaults, actions, and the runtime / APNs / FCM settings.\n * Secrets come from the env under the names `JourneyEnv` documents.\n */\n config: JourneyConfig;\n /**\n * The data plane, opened per request and per workflow run. Return a `StoreLease` when the\n * client must be released afterwards (a Postgres connection). `database` is the config's\n * (`schema` defaulting to `workflow`):\n *\n * store: (env, database) => postgresJsStore(env.DATABASE_URL, database)\n */\n store: (env: Env, database: { schema: string }) => JourneyStore | StoreLease;\n /**\n * Where journey emails go: a transport built from the validated env — `sesEmailTransport(...)`,\n * `cloudflareEmailTransport(...)`, or your own. Which one is independent of where the workflow\n * runs. Without it the email channel fails its sends, saying so.\n */\n email?: (env: Env) => EmailTransport;\n /**\n * Reporting hooks: node arrivals, every copy sent (with the tracking it should carry — click\n * links and an open pixel for email, payload data for a push), and how each instance ended.\n * By default the store records them itself, and the host serves the email tracking routes\n * (`/t/...`, on `WORKFLOW_ORIGIN`); override to report elsewhere.\n */\n observer?: (env: Env, store: JourneyStore) => JourneyObserver;\n /** Message outlet override (tests, a custom gateway): replaces every channel, `email` included. */\n messages?: (env: Env, store: JourneyStore) => MessageSender;\n /** Per-instance runtime options override (a tester in fast-forward); by default `config.runtime` for `ENVIRONMENT`. */\n runtime?: (env: Env, params: JourneyParams) => JourneyRuntimeOptions;\n /**\n * The host's environment, as a zod schema over its vars and secrets (bindings pass through\n * untouched): checked the first time the host runs, so a missing or malformed variable fails\n * there — every problem listed — not at the send that needs it. Webhook templates read their\n * URL from the parsed values: `webhook: (env: Env) => env.WEBHOOK_DISCORD_ALERTS`.\n */\n env?: core.$ZodType<object>;\n /** 'enforce' (default) answers an event or patch the contract refuses with a 400; 'report' only logs it. */\n contractMode?: 'enforce' | 'report';\n /**\n * Who may call the API routes, in whatever form the host chooses: an HMAC over the body, a\n * JWT, a header its proxy adds. Receives a copy of the request and the env; true lets it\n * through, false answers 401, a throw answers 500. The tracking routes and `/health` stay\n * public. Without it, the env's `API_TOKEN` as a bearer (`bearerMatches`), which production\n * then requires.\n *\n * authorize: async (request, env) =>\n * timingSafeEqual(request.headers.get('x-signature') ?? '', await hmac(env.SECRET, await request.text()))\n */\n authorize?: Authorize<Env>;\n}\n\n/** The host's environment failed its schema (`env` option). */\nexport class EnvValidationError extends Error {\n override readonly name = 'EnvValidationError';\n constructor(readonly issues: readonly string[]) {\n super(`the host's environment is invalid:\\n ${issues.join('\\n ')}`);\n }\n}\n\nexport interface JourneyHost<Env extends JourneyEnv> {\n config: JourneyConfig;\n /** The env, parsed by the schema once per env object (throws EnvValidationError). */\n checkEnv(env: Env): Env;\n /** A store for one request or run, its segment windows scaled with the runtime. */\n lease(env: Env): Required<StoreLease>;\n /** The bundle compiled into this code. */\n bundle(): BundleIR;\n /** The outlets one run is wired to (the store is the run's). */\n runParts(env: Env, store: JourneyStore, params: JourneyParams): JourneyRunParts;\n /** The HTTP API over one store: the ingest API plus tracking, the sweep, the bundle and previews. */\n handle(request: Request, env: Env, store: JourneyStore): Promise<Response>;\n /** The scheduled sweep (`sweepSegments`) on a store of its own. */\n sweep(env: Env): Promise<SweepResult>;\n /**\n * Make the bundle compiled into `version` live, once per process, only over an older live\n * version (the store records it). Undefined when there is nothing to do; a failure is logged\n * and retried a minute later.\n */\n applyOwnBundle(env: Env, version: BundleVersion | undefined): Promise<void> | undefined;\n}\n\n/** What the platform adapter itself contributes. */\nexport interface JourneyPlatform {\n /** An HTTP/2 fetch for APNs, where the runtime's own fetch is HTTP/1.1 (Node). */\n apnsFetch?: typeof fetch;\n}\n\nfunction sendsEmail(nodes: readonly NodeIR[]): boolean {\n return nodes.some((node) => {\n switch (node.type) {\n case 'message':\n return node.channel === 'email';\n case 'wait_until':\n return Array.isArray(node.onTimeout) && sendsEmail(node.onTimeout);\n case 'branch':\n return (\n node.cases.some((branch) => sendsEmail(branch.flow)) || sendsEmail(node.otherwise ?? [])\n );\n case 'cohort':\n return node.arms.some((arm) => sendsEmail(arm.flow));\n default:\n return false;\n }\n });\n}\n\nconst describeIssue = (issue: { path: PropertyKey[]; message: string }): string =>\n `${issue.path.join('.') || '(root)'}: ${issue.message}`;\n\n/** An event the host emits itself, checked against the contract like any ingest. */\nfunction checkedPayload(\n contract: Contract | undefined,\n event: string,\n payload: Record<string, unknown>\n): Record<string, unknown> {\n if (contract === undefined) return payload;\n const verdict = checkEvent(contract, event, payload);\n if (!verdict.ok) throw new ContractValidationError(event, verdict.issues);\n return verdict.data;\n}\n\nexport function createJourneyHost<Env extends JourneyEnv>(\n options: JourneyHostOptions<Env>,\n platform: JourneyPlatform = {}\n): JourneyHost<Env> {\n const { config } = options;\n const emailIds = indexTemplates('email', config.templates?.email);\n // A config still on `emails` (its sender would be silently dropped) fails here, not at a send\n assertEmailConfig(config, emailIds.values());\n let compiled: BundleIR | undefined;\n const bundle = () => (compiled ??= compileConfig(config));\n // A journey that emails with nowhere to send fails here too, not at its first email\n const emailing = bundle().workflows.filter((workflow) => sendsEmail(workflow.flow));\n if (options.email === undefined && options.messages === undefined && emailing.length > 0) {\n throw new TypeError(\n `workflows ${emailing.map((w) => `'${w.id}'`).join(', ')} send email, but the host has no ` +\n '`email` transport: pass `email: (env) => cloudflareEmailTransport(...)` or `sesEmailTransport(...)`'\n );\n }\n // Parsed once per env object: the runtime hands the same one to every request of an isolate\n const checked = new WeakMap<object, Env>();\n const checkEnv = (env: Env): Env => {\n if (options.env === undefined) return env;\n const cached = checked.get(env);\n if (cached !== undefined) return cached;\n const result = z.safeParse(options.env, env);\n // Names and reasons only: the values are secrets\n if (!result.success) throw new EnvValidationError(result.error.issues.map(describeIssue));\n const merged: Env = { ...env, ...result.data };\n checked.set(env, merged);\n return merged;\n };\n const emails = config.templates?.email;\n const database = { schema: config.database?.schema ?? DEFAULT_SCHEMA };\n // In fast-forward, segment windows scale with the journeys reading them (time-scale.ts)\n const lease = (env: Env): Required<StoreLease> => {\n const opened = options.store(env, database);\n const noop = async () => {};\n const factor = runtimeFromConfig(config, env).timeScale;\n return 'store' in opened\n ? { store: scaleSegmentWindows(opened.store, factor), close: opened.close ?? noop }\n : { store: scaleSegmentWindows(opened, factor), close: noop };\n };\n\n // The provider clients (APNs JWT, FCM access token) live with the host, not with a run: a\n // run's env is a fresh object on every Lambda invocation, so nothing keyed on it would be reused\n const providers = createProviderCache();\n const messages = (env: Env, store: JourneyStore): MessageSender => {\n if (options.messages !== undefined) return options.messages(env, store);\n return channelsFromConfig(config, env, {\n store,\n email: options.email?.(env),\n apnsFetch: platform.apnsFetch,\n providers,\n });\n };\n\n const observer = (env: Env, store: JourneyStore): JourneyObserver => {\n if (options.observer !== undefined) return options.observer(env, store);\n return reportingObserver(store.reporting, env.WORKFLOW_ORIGIN);\n };\n\n const runParts = (env: Env, store: JourneyStore, params: JourneyParams): JourneyRunParts => ({\n store,\n env,\n runtime:\n options.runtime === undefined ? runtimeFromConfig(config, env) : options.runtime(env, params),\n messages: messages(env, store),\n // An action ref is a registered action structurally: the same object carries name, code and hash\n actions: new RegistryActionInvoker(config.actions ?? []),\n contract: config.contract,\n validator: registryValidator(config.templates ?? {}),\n observer: observer(env, store),\n });\n\n const preview = emails === undefined ? undefined : registryRenderer(emails, config.email);\n\n /**\n * The recipients' routes (`/t/*`): a push tapped open is recorded as the config's\n * `webPush.openedEvent` — keyed by the copy, so a second tap of the same notification is the\n * same event — and an unsubscribe from all email wakes the waits reading email reachability\n * (`reachable('email')` subscribes to the email property's channel), so an `exitWhen:\n * not(reachable('email'))` takes effect without waiting for a timeout. An unsubscribe from one\n * topic wakes nothing: the user is still reachable.\n */\n const tracking = (env: Env, store: JourneyStore): TrackingOptions => {\n const opened = config.webPush?.openedEvent;\n return {\n product: config.title,\n quickMs: config.email?.tracking?.quickClickMs,\n pushOpened:\n opened === undefined\n ? undefined\n : async ({ messageId, userId, at }) => {\n const payload = checkedPayload(config.contract, opened, { message_id: messageId });\n const input = {\n id: `${opened}:${messageId}`,\n userId,\n event: opened,\n payload,\n ts: at,\n };\n await ingestEvent(env, input, store);\n },\n onUnsubscribed: async ({ userId, all }) => {\n // A topic opt-out leaves the user reachable by email: only the channel-wide one wakes\n if (!all) return;\n const email = profileUpdatedEvent(RECIPIENT_PROPERTY.email ?? 'email');\n await wakeSubscribers(env, store, userId, [email]);\n },\n };\n };\n\n const handle = (request: Request, env: Env, store: JourneyStore): Promise<Response> =>\n serve(request, {\n env,\n store,\n validation: { contract: config.contract, mode: options.contractMode },\n authorize: options.authorize as Authorize<JourneyEnv> | undefined,\n host: {\n bundle,\n preview:\n preview === undefined ? undefined : { has: (id) => emailIds.has(id), render: preview },\n tracking: tracking(env, store),\n sweep: () => sweepSegments(env, store),\n },\n });\n\n const sweep = async (env: Env) => {\n const { store, close } = lease(env);\n try {\n return await sweepSegments(env, store);\n } finally {\n await close();\n }\n };\n\n // The bundle compiled into this version goes live on its own, once per process: the first\n // request or scheduled run reads which version's bundle is live (one row) and applies this one\n // only over an older version, so one still serving during a rollout can never put its routes\n // back. A failure (say the migrations have not run) is logged and retried a minute later. Only\n // the request that starts the check gets its promise back: a request context cannot wait on\n // another's I/O.\n let applying: Promise<void> | undefined;\n let retryAt = 0;\n const applyOwnBundle = (env: Env, version: BundleVersion | undefined) => {\n if (version === undefined || version.id === '' || Number.isNaN(version.createdAt)) {\n return undefined;\n }\n if (applying !== undefined || Date.now() < retryAt) return undefined;\n applying = (async () => {\n const { store, close } = lease(env);\n try {\n const live = await store.liveBundleVersion();\n if (live !== undefined && (live.id === version.id || live.createdAt >= version.createdAt))\n return;\n const result = await deployBundle(env, bundle(), store, version);\n if (result.applied) console.log(`[workflow] version ${version.id} is live`);\n } finally {\n await close();\n }\n })().catch((error: unknown) => {\n applying = undefined;\n retryAt = Date.now() + 60_000;\n console.error(`[workflow] could not make version ${version.id} live`, error);\n });\n return applying;\n };\n\n return { config, checkEnv, lease, bundle, runParts, handle, sweep, applyOwnBundle };\n}\n"],"mappings":"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"}
|
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_hash = require("./hash.cjs");
|
|
3
|
+
const require_ir = require("./ir.cjs");
|
|
2
4
|
const require_dsl_action = require("./dsl/action.cjs");
|
|
3
5
|
const require_dsl_refs = require("./dsl/refs.cjs");
|
|
4
6
|
const require_dsl_template = require("./dsl/template.cjs");
|
|
@@ -7,8 +9,6 @@ const require_email = require("./email.cjs");
|
|
|
7
9
|
const require_templates = require("./templates.cjs");
|
|
8
10
|
const require_dsl_flow = require("./dsl/flow.cjs");
|
|
9
11
|
const require_dsl_trigger = require("./dsl/trigger.cjs");
|
|
10
|
-
const require_hash = require("./hash.cjs");
|
|
11
|
-
const require_ir = require("./ir.cjs");
|
|
12
12
|
const require_dsl_workflow = require("./dsl/workflow.cjs");
|
|
13
13
|
const require_dsl_bundle = require("./dsl/bundle.cjs");
|
|
14
14
|
require("./dsl/index.cjs");
|
|
@@ -29,11 +29,13 @@ exports.IR_VERSION = require_ir.IR_VERSION;
|
|
|
29
29
|
exports.MetaIR = require_ir.MetaIR;
|
|
30
30
|
exports.NodeIR = require_ir.NodeIR;
|
|
31
31
|
exports.NodeMetaIR = require_ir.NodeMetaIR;
|
|
32
|
+
exports.PREVIEW_MESSAGE_ID = require_email.PREVIEW_MESSAGE_ID;
|
|
32
33
|
exports.PropValueIR = require_ir.PropValueIR;
|
|
33
34
|
exports.PropertyOperatorIR = require_ir.PropertyOperatorIR;
|
|
34
35
|
exports.ScalarIR = require_ir.ScalarIR;
|
|
35
36
|
exports.SegmentIR = require_ir.SegmentIR;
|
|
36
37
|
exports.SourceLocIR = require_ir.SourceLocIR;
|
|
38
|
+
exports.SubscriptionsIR = require_ir.SubscriptionsIR;
|
|
37
39
|
exports.TemplateIR = require_ir.TemplateIR;
|
|
38
40
|
exports.TemplateValidationError = require_templates.TemplateValidationError;
|
|
39
41
|
exports.TriggerIR = require_ir.TriggerIR;
|
|
@@ -46,7 +48,9 @@ exports.apnsEnvironment = require_config.apnsEnvironment;
|
|
|
46
48
|
exports.assertEmailAddress = require_email.assertEmailAddress;
|
|
47
49
|
exports.assertEmailConfig = require_email.assertEmailConfig;
|
|
48
50
|
exports.assertEmailMetadata = require_email.assertEmailMetadata;
|
|
51
|
+
exports.assertHttpUrl = require_email.assertHttpUrl;
|
|
49
52
|
exports.assertSupportedSchema = require_templates.assertSupportedSchema;
|
|
53
|
+
exports.assertTemplateTopic = require_email.assertTemplateTopic;
|
|
50
54
|
exports.assertUnsubscribeUrl = require_email.assertUnsubscribeUrl;
|
|
51
55
|
exports.between = require_dsl_condition.between;
|
|
52
56
|
exports.canonicalJSON = require_hash.canonicalJSON;
|
|
@@ -91,6 +95,7 @@ exports.or = require_dsl_condition.or;
|
|
|
91
95
|
exports.parseTemplateProps = require_templates.parseTemplateProps;
|
|
92
96
|
exports.performed = require_dsl_condition.performed;
|
|
93
97
|
exports.plan = require_plan.plan;
|
|
98
|
+
exports.preferencesLink = require_email.preferencesLink;
|
|
94
99
|
exports.pushTemplate = require_templates.pushTemplate;
|
|
95
100
|
exports.reachable = require_dsl_condition.reachable;
|
|
96
101
|
exports.registryValidator = require_templates.registryValidator;
|
|
@@ -106,6 +111,7 @@ exports.slackTemplate = require_templates.slackTemplate;
|
|
|
106
111
|
exports.stripMeta = require_hash.stripMeta;
|
|
107
112
|
exports.stripProvenance = require_hash.stripProvenance;
|
|
108
113
|
exports.template = require_dsl_template.template;
|
|
114
|
+
exports.templatesFor = require_templates.templatesFor;
|
|
109
115
|
exports.trigger = require_dsl_trigger.trigger;
|
|
110
116
|
exports.user = require_dsl_refs.user;
|
|
111
117
|
exports.workflow = require_dsl_workflow.workflow;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import { ActionIR, BundleIR, ChannelIR, ConditionIR, DurationIR, GoalIR, IR_VERSION, MetaIR, NodeIR, NodeMetaIR, PropValueIR, PropertyOperatorIR, ScalarIR, SegmentIR, SourceLocIR, TemplateIR, TriggerIR, UserPropertyRefIR, WeekdayIR, WorkflowIR } from "./ir.cjs";
|
|
2
|
-
import { EmailAddress, EmailConfig, ResolvedEmail, assertEmailAddress, assertEmailConfig, assertEmailMetadata, assertUnsubscribeUrl, formatEmailAddress, isUnsubscribeLink, mimeAddress, resolveEmail } from "./email.cjs";
|
|
3
|
-
import { AnyTemplate, CHANNEL_OF, ChannelTemplate, DiscordContent, DiscordTemplate, EmailMetadata, EmailTemplate, EmailTemplateDefinition, PropsSchema, PushContent, PushTemplate, PushTemplateDefinition, ResolvedProps, SlackContent, SlackTemplate, TemplateChannel, TemplateIssue, TemplateRegistry, TemplateValidationError, WebhookTemplateDefinition, assertSupportedSchema, discordTemplate, emailTemplate, indexTemplates, isDiscordTemplate, isEmailTemplate, isPushTemplate, isSlackTemplate, isTemplate, lookupTemplate, parseTemplateProps, pushTemplate, registryValidator, renderDiscordContent, renderPushContent, renderSlackContent, slackTemplate } from "./templates.cjs";
|
|
1
|
+
import { ActionIR, BundleIR, ChannelIR, ConditionIR, DurationIR, GoalIR, IR_VERSION, MetaIR, NodeIR, NodeMetaIR, PropValueIR, PropertyOperatorIR, ScalarIR, SegmentIR, SourceLocIR, SubscriptionsIR, TemplateIR, TriggerIR, UserPropertyRefIR, WeekdayIR, WorkflowIR } from "./ir.cjs";
|
|
4
2
|
import { ActionContext, ActionHandler, ActionRef, action } from "./dsl/action.cjs";
|
|
5
3
|
import { Duration, EventMap, TimeOfDay, UserPropertyBase, Weekday } from "./dsl/base.cjs";
|
|
6
4
|
import { EventRef, EventRefs, MessageArgs, PayloadRef, PayloadRefNode, PayloadRefs, PropInput, PropsInput, UserPropertyRef, UserRefs, event, user } from "./dsl/refs.cjs";
|
|
@@ -11,9 +9,11 @@ import { DateTime, EventTriggerOptions, TriggerFactory, TriggerRef, trigger } fr
|
|
|
11
9
|
import { GoalOptions, WorkflowBuilder, WorkflowDefinition, workflow } from "./dsl/workflow.cjs";
|
|
12
10
|
import { compileBundle } from "./dsl/bundle.cjs";
|
|
13
11
|
import "./dsl/index.cjs";
|
|
14
|
-
import { Contract, ContractDefinition, ContractIssue, ContractResult, ContractValidationError, DeclaredOutput, EventPayload, ProfilePatch, checkEvent, defineContract, isContract } from "./contract.cjs";
|
|
12
|
+
import { Contract, ContractDefinition, ContractIssue, ContractResult, ContractValidationError, DeclaredOutput, EventPayload, ProfilePatch, SubscriptionsDefinition, Topic, checkEvent, defineContract, isContract } from "./contract.cjs";
|
|
13
|
+
import { EmailAddress, EmailConfig, EmailRenderOptions, PREVIEW_MESSAGE_ID, ResolvedEmail, assertEmailAddress, assertEmailConfig, assertEmailMetadata, assertHttpUrl, assertTemplateTopic, assertUnsubscribeUrl, formatEmailAddress, isUnsubscribeLink, mimeAddress, preferencesLink, resolveEmail } from "./email.cjs";
|
|
14
|
+
import { AnyTemplate, CHANNEL_OF, ChannelTemplate, DiscordContent, DiscordTemplate, EmailMetadata, EmailTemplate, EmailTemplateBase, EmailTemplateDefinition, EmailTemplateTopic, PropsSchema, PushContent, PushTemplate, PushTemplateBase, PushTemplateDefinition, ResolvedProps, SlackContent, SlackTemplate, TemplateChannel, TemplateIssue, TemplateRegistry, TemplateTopic, TemplateValidationError, WebhookTemplateDefinition, assertSupportedSchema, discordTemplate, emailTemplate, indexTemplates, isDiscordTemplate, isEmailTemplate, isPushTemplate, isSlackTemplate, isTemplate, lookupTemplate, parseTemplateProps, pushTemplate, registryValidator, renderDiscordContent, renderPushContent, renderSlackContent, slackTemplate, templatesFor } from "./templates.cjs";
|
|
15
15
|
import { JourneyMode, JourneyRuntimeConfig, JourneyRuntimeOptions, resolveRuntime } from "./runtime.cjs";
|
|
16
16
|
import { ApnsConfig, ConfigCompileError, DatabaseConfig, FcmConfig, JourneyConfig, JourneyConfigDefinition, WebPushConfig, apnsEnvironment, compileConfig, defineConfig, isConfig } from "./config.cjs";
|
|
17
17
|
import { canonicalJSON, fullHash, semanticHash, sha256Hex, stripMeta, stripProvenance } from "./hash.cjs";
|
|
18
18
|
import { BundlePlan, ChangeStatus, NodeChange, ResourceChange, WorkflowChange, isSemanticChange, plan } from "./plan.cjs";
|
|
19
|
-
export { type ActionContext, type ActionHandler, ActionIR, type ActionRef, AnyTemplate, ApnsConfig, type BranchArm, type BranchCase, BundleIR, BundlePlan, CHANNEL_OF, ChangeStatus, type Channel, ChannelIR, ChannelTemplate, type Condition, ConditionIR, ConfigCompileError, Contract, ContractDefinition, ContractIssue, ContractResult, ContractValidationError, DatabaseConfig, type DateTime, DeclaredOutput, DiscordContent, DiscordTemplate, type Duration, DurationIR, EmailAddress, EmailConfig, EmailMetadata, EmailTemplate, EmailTemplateDefinition, type EmitArgs, type EmitPayload, type EmptyProps, type EventMap, EventPayload, type EventRef, type EventRefs, type EventTriggerOptions, FcmConfig, type Flow, type FlowBuilder, GoalIR, type GoalOptions, IR_VERSION, JourneyConfig, JourneyConfigDefinition, JourneyMode, JourneyRuntimeConfig, JourneyRuntimeOptions, type MessageArgs, MetaIR, NodeChange, NodeIR, NodeMetaIR, type PayloadRef, type PayloadRefNode, type PayloadRefs, type PayloadWhere, type PerformedOptions, type PreferOptions, ProfilePatch, type PropInput, PropValueIR, PropertyOperatorIR, type PropsInput, type PropsOf, PropsSchema, PushContent, type PushOptions, PushTemplate, PushTemplateDefinition, ResolvedEmail, ResolvedProps, ResourceChange, ScalarIR, type SegmentDefinition, SegmentIR, type SegmentRef, SlackContent, SlackTemplate, SourceLocIR, type SubFlow, TemplateChannel, type TemplateFactory, TemplateIR, TemplateIssue, type TemplateRef, TemplateRegistry, TemplateValidationError, type TimeOfDay, type TriggerFactory, TriggerIR, type TriggerRef, type UserPropertyBase, type UserPropertyRef, UserPropertyRefIR, type UserRefs, WebPushConfig, WebhookTemplateDefinition, type Weekday, WeekdayIR, type WorkflowBuilder, WorkflowChange, type WorkflowDefinition, WorkflowIR, action, and, apnsEnvironment, assertEmailAddress, assertEmailConfig, assertEmailMetadata, assertSupportedSchema, assertUnsubscribeUrl, between, canonicalJSON, checkEvent, compileBundle, compileConfig, contains, defineConfig, defineContract, discordTemplate, emailTemplate, eq, event, exists, flow, formatEmailAddress, fullHash, gt, gte, inArray, indexTemplates, isConfig, isContract, isDiscordTemplate, isEmailTemplate, isPushTemplate, isSemanticChange, isSlackTemplate, isTemplate, isUnsubscribeLink, lookupTemplate, lt, lte, mimeAddress, ne, not, notBetween, notContains, notExists, notInArray, or, parseTemplateProps, performed, plan, pushTemplate, reachable, registryValidator, renderDiscordContent, renderPushContent, renderSlackContent, resolveEmail, resolveRuntime, segment, semanticHash, sha256Hex, slackTemplate, stripMeta, stripProvenance, template, trigger, user, workflow };
|
|
19
|
+
export { type ActionContext, type ActionHandler, ActionIR, type ActionRef, AnyTemplate, ApnsConfig, type BranchArm, type BranchCase, BundleIR, BundlePlan, CHANNEL_OF, ChangeStatus, type Channel, ChannelIR, ChannelTemplate, type Condition, ConditionIR, ConfigCompileError, Contract, ContractDefinition, ContractIssue, ContractResult, ContractValidationError, DatabaseConfig, type DateTime, DeclaredOutput, DiscordContent, DiscordTemplate, type Duration, DurationIR, EmailAddress, EmailConfig, EmailMetadata, EmailRenderOptions, EmailTemplate, EmailTemplateBase, EmailTemplateDefinition, EmailTemplateTopic, type EmitArgs, type EmitPayload, type EmptyProps, type EventMap, EventPayload, type EventRef, type EventRefs, type EventTriggerOptions, FcmConfig, type Flow, type FlowBuilder, GoalIR, type GoalOptions, IR_VERSION, JourneyConfig, JourneyConfigDefinition, JourneyMode, JourneyRuntimeConfig, JourneyRuntimeOptions, type MessageArgs, MetaIR, NodeChange, NodeIR, NodeMetaIR, PREVIEW_MESSAGE_ID, type PayloadRef, type PayloadRefNode, type PayloadRefs, type PayloadWhere, type PerformedOptions, type PreferOptions, ProfilePatch, type PropInput, PropValueIR, PropertyOperatorIR, type PropsInput, type PropsOf, PropsSchema, PushContent, type PushOptions, PushTemplate, PushTemplateBase, PushTemplateDefinition, ResolvedEmail, ResolvedProps, ResourceChange, ScalarIR, type SegmentDefinition, SegmentIR, type SegmentRef, SlackContent, SlackTemplate, SourceLocIR, type SubFlow, SubscriptionsDefinition, SubscriptionsIR, TemplateChannel, type TemplateFactory, TemplateIR, TemplateIssue, type TemplateRef, TemplateRegistry, TemplateTopic, TemplateValidationError, type TimeOfDay, Topic, type TriggerFactory, TriggerIR, type TriggerRef, type UserPropertyBase, type UserPropertyRef, UserPropertyRefIR, type UserRefs, WebPushConfig, WebhookTemplateDefinition, type Weekday, WeekdayIR, type WorkflowBuilder, WorkflowChange, type WorkflowDefinition, WorkflowIR, action, and, apnsEnvironment, assertEmailAddress, assertEmailConfig, assertEmailMetadata, assertHttpUrl, assertSupportedSchema, assertTemplateTopic, assertUnsubscribeUrl, between, canonicalJSON, checkEvent, compileBundle, compileConfig, contains, defineConfig, defineContract, discordTemplate, emailTemplate, eq, event, exists, flow, formatEmailAddress, fullHash, gt, gte, inArray, indexTemplates, isConfig, isContract, isDiscordTemplate, isEmailTemplate, isPushTemplate, isSemanticChange, isSlackTemplate, isTemplate, isUnsubscribeLink, lookupTemplate, lt, lte, mimeAddress, ne, not, notBetween, notContains, notExists, notInArray, or, parseTemplateProps, performed, plan, preferencesLink, pushTemplate, reachable, registryValidator, renderDiscordContent, renderPushContent, renderSlackContent, resolveEmail, resolveRuntime, segment, semanticHash, sha256Hex, slackTemplate, stripMeta, stripProvenance, template, templatesFor, trigger, user, workflow };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import { ActionIR, BundleIR, ChannelIR, ConditionIR, DurationIR, GoalIR, IR_VERSION, MetaIR, NodeIR, NodeMetaIR, PropValueIR, PropertyOperatorIR, ScalarIR, SegmentIR, SourceLocIR, TemplateIR, TriggerIR, UserPropertyRefIR, WeekdayIR, WorkflowIR } from "./ir.mjs";
|
|
2
|
-
import { EmailAddress, EmailConfig, ResolvedEmail, assertEmailAddress, assertEmailConfig, assertEmailMetadata, assertUnsubscribeUrl, formatEmailAddress, isUnsubscribeLink, mimeAddress, resolveEmail } from "./email.mjs";
|
|
3
|
-
import { AnyTemplate, CHANNEL_OF, ChannelTemplate, DiscordContent, DiscordTemplate, EmailMetadata, EmailTemplate, EmailTemplateDefinition, PropsSchema, PushContent, PushTemplate, PushTemplateDefinition, ResolvedProps, SlackContent, SlackTemplate, TemplateChannel, TemplateIssue, TemplateRegistry, TemplateValidationError, WebhookTemplateDefinition, assertSupportedSchema, discordTemplate, emailTemplate, indexTemplates, isDiscordTemplate, isEmailTemplate, isPushTemplate, isSlackTemplate, isTemplate, lookupTemplate, parseTemplateProps, pushTemplate, registryValidator, renderDiscordContent, renderPushContent, renderSlackContent, slackTemplate } from "./templates.mjs";
|
|
1
|
+
import { ActionIR, BundleIR, ChannelIR, ConditionIR, DurationIR, GoalIR, IR_VERSION, MetaIR, NodeIR, NodeMetaIR, PropValueIR, PropertyOperatorIR, ScalarIR, SegmentIR, SourceLocIR, SubscriptionsIR, TemplateIR, TriggerIR, UserPropertyRefIR, WeekdayIR, WorkflowIR } from "./ir.mjs";
|
|
4
2
|
import { ActionContext, ActionHandler, ActionRef, action } from "./dsl/action.mjs";
|
|
5
3
|
import { Duration, EventMap, TimeOfDay, UserPropertyBase, Weekday } from "./dsl/base.mjs";
|
|
6
4
|
import { EventRef, EventRefs, MessageArgs, PayloadRef, PayloadRefNode, PayloadRefs, PropInput, PropsInput, UserPropertyRef, UserRefs, event, user } from "./dsl/refs.mjs";
|
|
@@ -11,9 +9,11 @@ import { DateTime, EventTriggerOptions, TriggerFactory, TriggerRef, trigger } fr
|
|
|
11
9
|
import { GoalOptions, WorkflowBuilder, WorkflowDefinition, workflow } from "./dsl/workflow.mjs";
|
|
12
10
|
import { compileBundle } from "./dsl/bundle.mjs";
|
|
13
11
|
import "./dsl/index.mjs";
|
|
14
|
-
import { Contract, ContractDefinition, ContractIssue, ContractResult, ContractValidationError, DeclaredOutput, EventPayload, ProfilePatch, checkEvent, defineContract, isContract } from "./contract.mjs";
|
|
12
|
+
import { Contract, ContractDefinition, ContractIssue, ContractResult, ContractValidationError, DeclaredOutput, EventPayload, ProfilePatch, SubscriptionsDefinition, Topic, checkEvent, defineContract, isContract } from "./contract.mjs";
|
|
13
|
+
import { EmailAddress, EmailConfig, EmailRenderOptions, PREVIEW_MESSAGE_ID, ResolvedEmail, assertEmailAddress, assertEmailConfig, assertEmailMetadata, assertHttpUrl, assertTemplateTopic, assertUnsubscribeUrl, formatEmailAddress, isUnsubscribeLink, mimeAddress, preferencesLink, resolveEmail } from "./email.mjs";
|
|
14
|
+
import { AnyTemplate, CHANNEL_OF, ChannelTemplate, DiscordContent, DiscordTemplate, EmailMetadata, EmailTemplate, EmailTemplateBase, EmailTemplateDefinition, EmailTemplateTopic, PropsSchema, PushContent, PushTemplate, PushTemplateBase, PushTemplateDefinition, ResolvedProps, SlackContent, SlackTemplate, TemplateChannel, TemplateIssue, TemplateRegistry, TemplateTopic, TemplateValidationError, WebhookTemplateDefinition, assertSupportedSchema, discordTemplate, emailTemplate, indexTemplates, isDiscordTemplate, isEmailTemplate, isPushTemplate, isSlackTemplate, isTemplate, lookupTemplate, parseTemplateProps, pushTemplate, registryValidator, renderDiscordContent, renderPushContent, renderSlackContent, slackTemplate, templatesFor } from "./templates.mjs";
|
|
15
15
|
import { JourneyMode, JourneyRuntimeConfig, JourneyRuntimeOptions, resolveRuntime } from "./runtime.mjs";
|
|
16
16
|
import { ApnsConfig, ConfigCompileError, DatabaseConfig, FcmConfig, JourneyConfig, JourneyConfigDefinition, WebPushConfig, apnsEnvironment, compileConfig, defineConfig, isConfig } from "./config.mjs";
|
|
17
17
|
import { canonicalJSON, fullHash, semanticHash, sha256Hex, stripMeta, stripProvenance } from "./hash.mjs";
|
|
18
18
|
import { BundlePlan, ChangeStatus, NodeChange, ResourceChange, WorkflowChange, isSemanticChange, plan } from "./plan.mjs";
|
|
19
|
-
export { type ActionContext, type ActionHandler, ActionIR, type ActionRef, AnyTemplate, ApnsConfig, type BranchArm, type BranchCase, BundleIR, BundlePlan, CHANNEL_OF, ChangeStatus, type Channel, ChannelIR, ChannelTemplate, type Condition, ConditionIR, ConfigCompileError, Contract, ContractDefinition, ContractIssue, ContractResult, ContractValidationError, DatabaseConfig, type DateTime, DeclaredOutput, DiscordContent, DiscordTemplate, type Duration, DurationIR, EmailAddress, EmailConfig, EmailMetadata, EmailTemplate, EmailTemplateDefinition, type EmitArgs, type EmitPayload, type EmptyProps, type EventMap, EventPayload, type EventRef, type EventRefs, type EventTriggerOptions, FcmConfig, type Flow, type FlowBuilder, GoalIR, type GoalOptions, IR_VERSION, JourneyConfig, JourneyConfigDefinition, JourneyMode, JourneyRuntimeConfig, JourneyRuntimeOptions, type MessageArgs, MetaIR, NodeChange, NodeIR, NodeMetaIR, type PayloadRef, type PayloadRefNode, type PayloadRefs, type PayloadWhere, type PerformedOptions, type PreferOptions, ProfilePatch, type PropInput, PropValueIR, PropertyOperatorIR, type PropsInput, type PropsOf, PropsSchema, PushContent, type PushOptions, PushTemplate, PushTemplateDefinition, ResolvedEmail, ResolvedProps, ResourceChange, ScalarIR, type SegmentDefinition, SegmentIR, type SegmentRef, SlackContent, SlackTemplate, SourceLocIR, type SubFlow, TemplateChannel, type TemplateFactory, TemplateIR, TemplateIssue, type TemplateRef, TemplateRegistry, TemplateValidationError, type TimeOfDay, type TriggerFactory, TriggerIR, type TriggerRef, type UserPropertyBase, type UserPropertyRef, UserPropertyRefIR, type UserRefs, WebPushConfig, WebhookTemplateDefinition, type Weekday, WeekdayIR, type WorkflowBuilder, WorkflowChange, type WorkflowDefinition, WorkflowIR, action, and, apnsEnvironment, assertEmailAddress, assertEmailConfig, assertEmailMetadata, assertSupportedSchema, assertUnsubscribeUrl, between, canonicalJSON, checkEvent, compileBundle, compileConfig, contains, defineConfig, defineContract, discordTemplate, emailTemplate, eq, event, exists, flow, formatEmailAddress, fullHash, gt, gte, inArray, indexTemplates, isConfig, isContract, isDiscordTemplate, isEmailTemplate, isPushTemplate, isSemanticChange, isSlackTemplate, isTemplate, isUnsubscribeLink, lookupTemplate, lt, lte, mimeAddress, ne, not, notBetween, notContains, notExists, notInArray, or, parseTemplateProps, performed, plan, pushTemplate, reachable, registryValidator, renderDiscordContent, renderPushContent, renderSlackContent, resolveEmail, resolveRuntime, segment, semanticHash, sha256Hex, slackTemplate, stripMeta, stripProvenance, template, trigger, user, workflow };
|
|
19
|
+
export { type ActionContext, type ActionHandler, ActionIR, type ActionRef, AnyTemplate, ApnsConfig, type BranchArm, type BranchCase, BundleIR, BundlePlan, CHANNEL_OF, ChangeStatus, type Channel, ChannelIR, ChannelTemplate, type Condition, ConditionIR, ConfigCompileError, Contract, ContractDefinition, ContractIssue, ContractResult, ContractValidationError, DatabaseConfig, type DateTime, DeclaredOutput, DiscordContent, DiscordTemplate, type Duration, DurationIR, EmailAddress, EmailConfig, EmailMetadata, EmailRenderOptions, EmailTemplate, EmailTemplateBase, EmailTemplateDefinition, EmailTemplateTopic, type EmitArgs, type EmitPayload, type EmptyProps, type EventMap, EventPayload, type EventRef, type EventRefs, type EventTriggerOptions, FcmConfig, type Flow, type FlowBuilder, GoalIR, type GoalOptions, IR_VERSION, JourneyConfig, JourneyConfigDefinition, JourneyMode, JourneyRuntimeConfig, JourneyRuntimeOptions, type MessageArgs, MetaIR, NodeChange, NodeIR, NodeMetaIR, PREVIEW_MESSAGE_ID, type PayloadRef, type PayloadRefNode, type PayloadRefs, type PayloadWhere, type PerformedOptions, type PreferOptions, ProfilePatch, type PropInput, PropValueIR, PropertyOperatorIR, type PropsInput, type PropsOf, PropsSchema, PushContent, type PushOptions, PushTemplate, PushTemplateBase, PushTemplateDefinition, ResolvedEmail, ResolvedProps, ResourceChange, ScalarIR, type SegmentDefinition, SegmentIR, type SegmentRef, SlackContent, SlackTemplate, SourceLocIR, type SubFlow, SubscriptionsDefinition, SubscriptionsIR, TemplateChannel, type TemplateFactory, TemplateIR, TemplateIssue, type TemplateRef, TemplateRegistry, TemplateTopic, TemplateValidationError, type TimeOfDay, Topic, type TriggerFactory, TriggerIR, type TriggerRef, type UserPropertyBase, type UserPropertyRef, UserPropertyRefIR, type UserRefs, WebPushConfig, WebhookTemplateDefinition, type Weekday, WeekdayIR, type WorkflowBuilder, WorkflowChange, type WorkflowDefinition, WorkflowIR, action, and, apnsEnvironment, assertEmailAddress, assertEmailConfig, assertEmailMetadata, assertHttpUrl, assertSupportedSchema, assertTemplateTopic, assertUnsubscribeUrl, between, canonicalJSON, checkEvent, compileBundle, compileConfig, contains, defineConfig, defineContract, discordTemplate, emailTemplate, eq, event, exists, flow, formatEmailAddress, fullHash, gt, gte, inArray, indexTemplates, isConfig, isContract, isDiscordTemplate, isEmailTemplate, isPushTemplate, isSemanticChange, isSlackTemplate, isTemplate, isUnsubscribeLink, lookupTemplate, lt, lte, mimeAddress, ne, not, notBetween, notContains, notExists, notInArray, or, parseTemplateProps, performed, plan, preferencesLink, pushTemplate, reachable, registryValidator, renderDiscordContent, renderPushContent, renderSlackContent, resolveEmail, resolveRuntime, segment, semanticHash, sha256Hex, slackTemplate, stripMeta, stripProvenance, template, templatesFor, trigger, user, workflow };
|
package/dist/index.mjs
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
+
import { canonicalJSON, fullHash, semanticHash, sha256Hex, stripMeta, stripProvenance } from "./hash.mjs";
|
|
2
|
+
import { ActionIR, BundleIR, ChannelIR, ConditionIR, DurationIR, GoalIR, IR_VERSION, MetaIR, NodeIR, NodeMetaIR, PropValueIR, PropertyOperatorIR, ScalarIR, SegmentIR, SourceLocIR, SubscriptionsIR, TemplateIR, TriggerIR, UserPropertyRefIR, WeekdayIR, WorkflowIR } from "./ir.mjs";
|
|
1
3
|
import { action } from "./dsl/action.mjs";
|
|
2
4
|
import { event, user } from "./dsl/refs.mjs";
|
|
3
5
|
import { template } from "./dsl/template.mjs";
|
|
4
6
|
import { and, between, contains, eq, exists, gt, gte, inArray, lt, lte, ne, not, notBetween, notContains, notExists, notInArray, or, performed, reachable, segment } from "./dsl/condition.mjs";
|
|
5
|
-
import { assertEmailAddress, assertEmailConfig, assertEmailMetadata, assertUnsubscribeUrl, formatEmailAddress, isUnsubscribeLink, mimeAddress, resolveEmail } from "./email.mjs";
|
|
6
|
-
import { CHANNEL_OF, TemplateValidationError, assertSupportedSchema, discordTemplate, emailTemplate, indexTemplates, isDiscordTemplate, isEmailTemplate, isPushTemplate, isSlackTemplate, isTemplate, lookupTemplate, parseTemplateProps, pushTemplate, registryValidator, renderDiscordContent, renderPushContent, renderSlackContent, slackTemplate } from "./templates.mjs";
|
|
7
|
+
import { PREVIEW_MESSAGE_ID, assertEmailAddress, assertEmailConfig, assertEmailMetadata, assertHttpUrl, assertTemplateTopic, assertUnsubscribeUrl, formatEmailAddress, isUnsubscribeLink, mimeAddress, preferencesLink, resolveEmail } from "./email.mjs";
|
|
8
|
+
import { CHANNEL_OF, TemplateValidationError, assertSupportedSchema, discordTemplate, emailTemplate, indexTemplates, isDiscordTemplate, isEmailTemplate, isPushTemplate, isSlackTemplate, isTemplate, lookupTemplate, parseTemplateProps, pushTemplate, registryValidator, renderDiscordContent, renderPushContent, renderSlackContent, slackTemplate, templatesFor } from "./templates.mjs";
|
|
7
9
|
import { flow } from "./dsl/flow.mjs";
|
|
8
10
|
import { trigger } from "./dsl/trigger.mjs";
|
|
9
|
-
import { canonicalJSON, fullHash, semanticHash, sha256Hex, stripMeta, stripProvenance } from "./hash.mjs";
|
|
10
|
-
import { ActionIR, BundleIR, ChannelIR, ConditionIR, DurationIR, GoalIR, IR_VERSION, MetaIR, NodeIR, NodeMetaIR, PropValueIR, PropertyOperatorIR, ScalarIR, SegmentIR, SourceLocIR, TemplateIR, TriggerIR, UserPropertyRefIR, WeekdayIR, WorkflowIR } from "./ir.mjs";
|
|
11
11
|
import { workflow } from "./dsl/workflow.mjs";
|
|
12
12
|
import { compileBundle } from "./dsl/bundle.mjs";
|
|
13
13
|
import "./dsl/index.mjs";
|
|
@@ -15,4 +15,4 @@ import { ConfigCompileError, apnsEnvironment, compileConfig, defineConfig, isCon
|
|
|
15
15
|
import { ContractValidationError, checkEvent, defineContract, isContract } from "./contract.mjs";
|
|
16
16
|
import { isSemanticChange, plan } from "./plan.mjs";
|
|
17
17
|
import { resolveRuntime } from "./runtime.mjs";
|
|
18
|
-
export { ActionIR, BundleIR, CHANNEL_OF, ChannelIR, ConditionIR, ConfigCompileError, ContractValidationError, DurationIR, GoalIR, IR_VERSION, MetaIR, NodeIR, NodeMetaIR, PropValueIR, PropertyOperatorIR, ScalarIR, SegmentIR, SourceLocIR, TemplateIR, TemplateValidationError, TriggerIR, UserPropertyRefIR, WeekdayIR, WorkflowIR, action, and, apnsEnvironment, assertEmailAddress, assertEmailConfig, assertEmailMetadata, assertSupportedSchema, assertUnsubscribeUrl, between, canonicalJSON, checkEvent, compileBundle, compileConfig, contains, defineConfig, defineContract, discordTemplate, emailTemplate, eq, event, exists, flow, formatEmailAddress, fullHash, gt, gte, inArray, indexTemplates, isConfig, isContract, isDiscordTemplate, isEmailTemplate, isPushTemplate, isSemanticChange, isSlackTemplate, isTemplate, isUnsubscribeLink, lookupTemplate, lt, lte, mimeAddress, ne, not, notBetween, notContains, notExists, notInArray, or, parseTemplateProps, performed, plan, pushTemplate, reachable, registryValidator, renderDiscordContent, renderPushContent, renderSlackContent, resolveEmail, resolveRuntime, segment, semanticHash, sha256Hex, slackTemplate, stripMeta, stripProvenance, template, trigger, user, workflow };
|
|
18
|
+
export { ActionIR, BundleIR, CHANNEL_OF, ChannelIR, ConditionIR, ConfigCompileError, ContractValidationError, DurationIR, GoalIR, IR_VERSION, MetaIR, NodeIR, NodeMetaIR, PREVIEW_MESSAGE_ID, PropValueIR, PropertyOperatorIR, ScalarIR, SegmentIR, SourceLocIR, SubscriptionsIR, TemplateIR, TemplateValidationError, TriggerIR, UserPropertyRefIR, WeekdayIR, WorkflowIR, action, and, apnsEnvironment, assertEmailAddress, assertEmailConfig, assertEmailMetadata, assertHttpUrl, assertSupportedSchema, assertTemplateTopic, assertUnsubscribeUrl, between, canonicalJSON, checkEvent, compileBundle, compileConfig, contains, defineConfig, defineContract, discordTemplate, emailTemplate, eq, event, exists, flow, formatEmailAddress, fullHash, gt, gte, inArray, indexTemplates, isConfig, isContract, isDiscordTemplate, isEmailTemplate, isPushTemplate, isSemanticChange, isSlackTemplate, isTemplate, isUnsubscribeLink, lookupTemplate, lt, lte, mimeAddress, ne, not, notBetween, notContains, notExists, notInArray, or, parseTemplateProps, performed, plan, preferencesLink, pushTemplate, reachable, registryValidator, renderDiscordContent, renderPushContent, renderSlackContent, resolveEmail, resolveRuntime, segment, semanticHash, sha256Hex, slackTemplate, stripMeta, stripProvenance, template, templatesFor, trigger, user, workflow };
|
package/dist/ir.cjs
CHANGED
|
@@ -196,7 +196,8 @@ const NodeIR = zod_mini.lazy(() => zod_mini.discriminatedUnion("type", [
|
|
|
196
196
|
channel: ChannelIR,
|
|
197
197
|
template: zod_mini.string(),
|
|
198
198
|
props: zod_mini.record(zod_mini.string(), PropValueIR),
|
|
199
|
-
devices: zod_mini.optional(zod_mini.literal("latest"))
|
|
199
|
+
devices: zod_mini.optional(zod_mini.literal("latest")),
|
|
200
|
+
topic: zod_mini.optional(zod_mini.nullable(zod_mini.string()))
|
|
200
201
|
}),
|
|
201
202
|
zod_mini.object({
|
|
202
203
|
...nodeBase,
|
|
@@ -332,12 +333,19 @@ const ActionIR = zod_mini.object({
|
|
|
332
333
|
* apply, same mental model). The server diffs each definition's contentHash to
|
|
333
334
|
* decide publish / skip / retire.
|
|
334
335
|
*/
|
|
336
|
+
/**
|
|
337
|
+
* The subscription topics the contract declares, carried by the bundle so the engine validates
|
|
338
|
+
* `/subscriptions` and the templates' topics against the deployed list, not the SDK's word.
|
|
339
|
+
*/
|
|
340
|
+
const SubscriptionsIR = zod_mini.object({ topics: zod_mini.array(zod_mini.string()) });
|
|
335
341
|
const BundleIR = zod_mini.object({
|
|
336
342
|
irVersion: zod_mini.literal(2),
|
|
337
343
|
workflows: zod_mini.array(WorkflowIR),
|
|
338
344
|
segments: zod_mini.array(SegmentIR),
|
|
339
345
|
templates: zod_mini.array(TemplateIR),
|
|
340
|
-
actions: zod_mini.array(ActionIR)
|
|
346
|
+
actions: zod_mini.array(ActionIR),
|
|
347
|
+
/** Absent in a bundle compiled before topics existed: no topics. */
|
|
348
|
+
subscriptions: zod_mini.optional(SubscriptionsIR)
|
|
341
349
|
});
|
|
342
350
|
exports.ActionIR = ActionIR;
|
|
343
351
|
exports.BundleIR = BundleIR;
|
|
@@ -354,6 +362,7 @@ exports.PropertyOperatorIR = PropertyOperatorIR;
|
|
|
354
362
|
exports.ScalarIR = ScalarIR;
|
|
355
363
|
exports.SegmentIR = SegmentIR;
|
|
356
364
|
exports.SourceLocIR = SourceLocIR;
|
|
365
|
+
exports.SubscriptionsIR = SubscriptionsIR;
|
|
357
366
|
exports.TemplateIR = TemplateIR;
|
|
358
367
|
exports.TriggerIR = TriggerIR;
|
|
359
368
|
exports.UserPropertyRefIR = UserPropertyRefIR;
|
package/dist/ir.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ir.cjs","names":["z"],"sources":["../src/ir.ts"],"sourcesContent":["import * as z from 'zod/mini';\n\n/**\n * IR (Intermediate Representation) — what the DSL compiles to: serializable\n * JSON. It is the three-way contract between the engine that interprets it,\n * the UI (react-flow) that renders it, and diff / audit tooling.\n *\n * Validation strategy: the zod/mini schemas are the authoritative definition.\n * TS types are either inferred from a schema or hand-written under the same\n * name (zod cannot infer recursive types; the hand-written type is anchored\n * with a ZodMiniType<T> annotation, so any drift fails to compile). IR crosses\n * process boundaries (database, network, UI), so runtime validation is a must.\n *\n * ## Versioning (two independent axes)\n *\n * 1. IR format version (irVersion): evolution of the IR schema itself. Adding\n * optional fields does not bump it; only structurally incompatible changes\n * do (changing a discriminant, dropping a field, altering semantics).\n * Readers dispatch on irVersion to the matching schema/migrator; writers\n * always write the current version.\n * 2. Content version (contentHash): a content-addressed id for a single\n * definition's **execution semantics** — the canonical-JSON hash after\n * metadata (meta / label / reason) is stripped, see hash.ts. It is used to:\n * - pin a version when a user enters, so an in-flight journey runs the\n * pinned version to completion (the default policy; compatible hot\n * updates are a future explicit-migration topic). Known exception:\n * segment conditions are resolved **by id at evaluation time** — a\n * segment redeploy changes behaviour of in-flight journeys that\n * reference it. Pinning covers the workflow's own structure; per-hash\n * segment snapshots are future work;\n * - diff on deploy: same hash means unchanged, skip publishing;\n * - audit: answer \"which version was this user on\" at any point.\n *\n * ## Node ids\n *\n * Derived from the structural path at compile time — stable and deterministic:\n * root level '0', '1', …; the j-th child of a branch's i-th case is\n * '{branchId}.c{i}.{j}', the default arm '{branchId}.o.{j}'; cohort arms use\n * the arm name '{cohortId}.{armName}.{j}'; a waitUntil timeout flow '{id}.t.{j}'.\n * Editing a node's parameters keeps its id; inserting a node shifts the ids of\n * its later siblings (a known trade-off — locating in-flight users relies on\n * the pinned version rather than cross-version id alignment, and cross-version\n * mapping is an explicit migrator's job).\n */\n\nexport const IR_VERSION = 2;\n\n/* -------------------------------- Base values ------------------------------- */\n\nexport const ScalarIR = z.union([z.string(), z.number(), z.boolean()]);\nexport type ScalarIR = z.infer<typeof ScalarIR>;\n\n/** Duration: `value` keeps the DSL literal ('23 hours', for display), `ms` is what the engine uses. */\n/*\n * TODO: `value` is hashed alongside `ms`, so '7 days' / '1 week' / '168 hours'\n * — identical to the engine, which only reads ms — produce three different\n * contentHashes. Rewording a duration therefore reports as a semantic change in\n * plan and repins in-flight users. Moving `value` out of the hash (it is a\n * display string) would fix it; deferred because it changes existing hashes.\n */\nexport const DurationIR = z.object({ value: z.string(), ms: z.number() });\nexport type DurationIR = z.infer<typeof DurationIR>;\n\n/** User-property reference — isomorphic to the DSL's UserPropertyRef once serialized (phantom type dropped). */\nexport const UserPropertyRefIR = z.object({\n type: z.literal('user_property'),\n path: z.string(),\n});\nexport type UserPropertyRefIR = z.infer<typeof UserPropertyRefIR>;\n\n/** A message prop / event payload value: either a literal or a user-property reference. */\nexport const PropValueIR = z.union([ScalarIR, UserPropertyRefIR]);\nexport type PropValueIR = z.infer<typeof PropValueIR>;\n\nexport const ChannelIR = z.enum(['email', 'sms', 'push', 'in_app', 'slack', 'discord', 'survey']);\nexport type ChannelIR = z.infer<typeof ChannelIR>;\n\nexport const WeekdayIR = z.enum(['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']);\nexport type WeekdayIR = z.infer<typeof WeekdayIR>;\n\n/* --------------------------------- Provenance -------------------------------- */\n\n/**\n * Source location of the builder call that produced a definition — the IR's\n * source map. Lives under `meta`, so it is **excluded from contentHash**\n * (see hash.ts): provenance never affects execution identity or plan.\n * `file` is cwd-relative when compiled under Node, a URL under Vite dev.\n */\nexport const SourceLocIR = z.object({\n file: z.string(),\n line: z.number(),\n column: z.number(),\n});\nexport type SourceLocIR = z.infer<typeof SourceLocIR>;\n\n/** Node-level metadata: provenance only, for now. Excluded from contentHash. */\nexport const NodeMetaIR = z.object({\n loc: z.optional(SourceLocIR),\n});\nexport type NodeMetaIR = z.infer<typeof NodeMetaIR>;\n\n/* -------------------------------- Conditions -------------------------------- */\n\nexport const PropertyOperatorIR = z.enum([\n 'eq',\n 'ne',\n 'gt',\n 'gte',\n 'lt',\n 'lte',\n 'between',\n 'not_between',\n 'in_array',\n 'not_in_array',\n 'exists',\n 'not_exists',\n 'contains',\n 'not_contains',\n]);\nexport type PropertyOperatorIR = z.infer<typeof PropertyOperatorIR>;\n\n/**\n * Condition expression tree. The shape of value/values follows `op`:\n * eq/ne/gt/gte/lt/lte/contains/not_contains → value; in_array/not_in_array →\n * values; between/not_between → values = [min, max]; exists/not_exists → neither.\n *\n * `payload` predicates test the payload of an event (dotted paths reach nested\n * fields). They are only valid inside a `where` subtree — `performed.where`\n * and the event trigger's `where` — where an event row is in scope; the\n * compiler (compileBundle) and the evaluator both enforce the placement.\n * TODO: runtime refinement narrowing by op.\n */\n/**\n * Provenance rides on conditions too, so the studio can point at the exact\n * `eq(...)` / `and(...)` that produced a node — same rule as nodes: `meta` is\n * dropped before hashing, so carrying it never affects execution identity.\n */\ninterface ConditionBaseIR {\n meta?: NodeMetaIR | undefined;\n}\n\nexport type ConditionIR =\n | (ConditionBaseIR & { type: 'and'; conditions: ConditionIR[] })\n | (ConditionBaseIR & { type: 'or'; conditions: ConditionIR[] })\n | (ConditionBaseIR & { type: 'not'; condition: ConditionIR })\n /** Reference a segment by id (defined in SegmentIR, versioned separately). */\n | (ConditionBaseIR & { type: 'segment'; segment: string })\n | (ConditionBaseIR & {\n type: 'property';\n path: string;\n op: PropertyOperatorIR;\n value?: ScalarIR | undefined;\n values?: ScalarIR[] | undefined;\n })\n | (ConditionBaseIR & {\n /** Event payload predicate; `path` is dotted for nested fields ('tags.utm_source'). */\n type: 'payload';\n path: string;\n op: PropertyOperatorIR;\n value?: ScalarIR | undefined;\n values?: ScalarIR[] | undefined;\n })\n | (ConditionBaseIR & {\n type: 'performed';\n event: string;\n /** Payload filter: only events matching it count (a payload-only subtree). */\n where?: ConditionIR | undefined;\n within?: DurationIR | undefined;\n count?: number | undefined;\n })\n /**\n * The user can be reached on the channel right now: for push, the host's\n * device directory lists at least one device; for the others, the channel's\n * recipient property (email, phone, …) is set.\n */\n | (ConditionBaseIR & { type: 'reachable'; channel: ChannelIR });\n\nconst conditionBase = { meta: z.optional(NodeMetaIR) };\n\nexport const ConditionIR: z.ZodMiniType<ConditionIR> = z.lazy(() =>\n z.discriminatedUnion('type', [\n z.object({ ...conditionBase, type: z.literal('and'), conditions: z.array(ConditionIR) }),\n z.object({ ...conditionBase, type: z.literal('or'), conditions: z.array(ConditionIR) }),\n z.object({ ...conditionBase, type: z.literal('not'), condition: ConditionIR }),\n z.object({ ...conditionBase, type: z.literal('segment'), segment: z.string() }),\n z.object({\n ...conditionBase,\n type: z.literal('property'),\n path: z.string(),\n op: PropertyOperatorIR,\n value: z.optional(ScalarIR),\n values: z.optional(z.array(ScalarIR)),\n }),\n z.object({\n ...conditionBase,\n type: z.literal('payload'),\n path: z.string(),\n op: PropertyOperatorIR,\n value: z.optional(ScalarIR),\n values: z.optional(z.array(ScalarIR)),\n }),\n z.object({\n ...conditionBase,\n type: z.literal('performed'),\n event: z.string(),\n where: z.optional(ConditionIR),\n within: z.optional(DurationIR),\n count: z.optional(z.number()),\n }),\n z.object({ ...conditionBase, type: z.literal('reachable'), channel: ChannelIR }),\n ])\n);\n\n/* --------------------------------- Triggers --------------------------------- */\n\n/**\n * Trigger shapes. The event trigger has two independent gates: `where` tests\n * the triggering event's payload (payload-only subtree, evaluated against the\n * incoming event before anything else), `filter` tests the user's profile.\n */\nexport const TriggerIR = z.discriminatedUnion('type', [\n z.object({\n type: z.literal('event'),\n event: z.string(),\n where: z.optional(ConditionIR),\n filter: z.optional(ConditionIR),\n }),\n z.object({ type: z.literal('segment'), segment: z.string() }),\n z.object({ type: z.literal('date'), at: z.string() }),\n z.object({ type: z.literal('webhook') }),\n]);\nexport type TriggerIR = z.infer<typeof TriggerIR>;\n\n/* ----------------------------------- Nodes ---------------------------------- */\n\ninterface NodeBaseIR {\n /** Stable id derived from the structural path (rules in the file header). */\n id: string;\n /** Optional node name: UI title / observability handle (branch's optional first argument lands here). */\n label?: string | undefined;\n /** Metadata (provenance); excluded from contentHash. */\n meta?: NodeMetaIR | undefined;\n}\n\nexport type NodeIR =\n | (NodeBaseIR & {\n type: 'message';\n channel: ChannelIR;\n template: string;\n props: Record<string, PropValueIR>;\n /**\n * Push only: which of the user's devices get a copy. Absent means every\n * device the directory lists; 'latest' only the most recently registered.\n */\n devices?: 'latest' | undefined;\n })\n | (NodeBaseIR & { type: 'delay'; duration: DurationIR })\n | (NodeBaseIR & { type: 'random_delay'; min: DurationIR; max: DurationIR })\n | (NodeBaseIR & {\n type: 'time_window';\n days: WeekdayIR[];\n between: [string, string];\n tz: string;\n })\n | (NodeBaseIR & {\n type: 'wait_until';\n condition: ConditionIR;\n timeout: DurationIR;\n onTimeout: 'continue' | 'exit' | NodeIR[];\n })\n | (NodeBaseIR & {\n type: 'branch';\n cases: { label?: string | undefined; condition: ConditionIR; flow: NodeIR[] }[];\n /** Default arm (the DSL's bare tail argument). Absent = fall through to the main line. */\n otherwise?: NodeIR[] | undefined;\n })\n | (NodeBaseIR & {\n type: 'filter';\n /** Audit-log line for the users this gate drops. Metadata: excluded from contentHash. */\n reason?: string | undefined;\n condition: ConditionIR;\n })\n | (NodeBaseIR & {\n type: 'cohort';\n /**\n * Explicit experiment key: bucketing hashes `userId:key` instead of the\n * structural node id, so the assignment survives node insertion/moves and\n * the experiment keeps one identity across workflow versions. Absent →\n * the node id is used (fine for one-off splits).\n */\n key?: string | undefined;\n /**\n * Arms in authoring order — the order the interpreter accumulates weights\n * in, so it decides which bucket range each arm owns. The DSL keeps that\n * promise by rejecting integer-like arm names, which JavaScript would\n * hoist to the front of the object in numeric order. Weights sum to 100.\n */\n arms: { name: string; weight: number; flow: NodeIR[] }[];\n })\n | (NodeBaseIR & {\n /** `reason` is the audit-log line. Metadata: excluded from contentHash. */\n type: 'exit';\n reason?: string | undefined;\n })\n | (NodeBaseIR & { type: 'send_event'; event: string; payload: Record<string, PropValueIR> })\n | (NodeBaseIR & {\n type: 'action';\n /** Registry name of the custom action; the code itself lives in the deployed Worker bundle. */\n action: string;\n /** Argument values; user_property references are resolved at run time, like message props. */\n args: Record<string, PropValueIR>;\n });\n\nconst nodeBase = { id: z.string(), label: z.optional(z.string()), meta: z.optional(NodeMetaIR) };\n\nexport const NodeIR: z.ZodMiniType<NodeIR> = z.lazy(() =>\n z.discriminatedUnion('type', [\n z.object({\n ...nodeBase,\n type: z.literal('message'),\n channel: ChannelIR,\n template: z.string(),\n props: z.record(z.string(), PropValueIR),\n devices: z.optional(z.literal('latest')),\n }),\n z.object({ ...nodeBase, type: z.literal('delay'), duration: DurationIR }),\n z.object({ ...nodeBase, type: z.literal('random_delay'), min: DurationIR, max: DurationIR }),\n z.object({\n ...nodeBase,\n type: z.literal('time_window'),\n days: z.array(WeekdayIR),\n between: z.tuple([z.string(), z.string()]),\n tz: z.string(),\n }),\n z.object({\n ...nodeBase,\n type: z.literal('wait_until'),\n condition: ConditionIR,\n timeout: DurationIR,\n onTimeout: z.union([z.literal('continue'), z.literal('exit'), z.array(NodeIR)]),\n }),\n z.object({\n ...nodeBase,\n type: z.literal('branch'),\n cases: z.array(\n z.object({\n label: z.optional(z.string()),\n condition: ConditionIR,\n flow: z.array(NodeIR),\n })\n ),\n otherwise: z.optional(z.array(NodeIR)),\n }),\n z.object({\n ...nodeBase,\n type: z.literal('filter'),\n condition: ConditionIR,\n reason: z.optional(z.string()),\n }),\n z.object({\n ...nodeBase,\n type: z.literal('cohort'),\n key: z.optional(z.string()),\n arms: z.array(z.object({ name: z.string(), weight: z.number(), flow: z.array(NodeIR) })),\n }),\n z.object({ ...nodeBase, type: z.literal('exit'), reason: z.optional(z.string()) }),\n z.object({\n ...nodeBase,\n type: z.literal('send_event'),\n event: z.string(),\n payload: z.record(z.string(), PropValueIR),\n }),\n z.object({\n ...nodeBase,\n type: z.literal('action'),\n action: z.string(),\n args: z.record(z.string(), PropValueIR),\n }),\n ])\n);\n\n/* ---------------------- Top-level definitions and bundle --------------------- */\n\nexport const GoalIR = z.object({\n condition: ConditionIR,\n within: z.optional(DurationIR),\n exitOnMatch: z.boolean(),\n});\nexport type GoalIR = z.infer<typeof GoalIR>;\n\n/**\n * Human-facing metadata: **excluded from contentHash** (see hash.ts).\n * Editing a name or description does not touch execution semantics, so it never\n * invalidates the version in-flight users are pinned to, nor shows up in plan.\n */\nexport const MetaIR = z.object({\n /**\n * Human label. The asset's `id` is its identity — a wire key that routing\n * tables and the entry ledger are built on — so it is not something to\n * rename for readability. This is: it is metadata, dropped before hashing,\n * and the studio shows it in place of the key wherever it is set.\n */\n name: z.optional(z.string()),\n description: z.optional(z.string()),\n loc: z.optional(SourceLocIR),\n});\nexport type MetaIR = z.infer<typeof MetaIR>;\n\nexport const WorkflowIR = z.object({\n irVersion: z.literal(IR_VERSION),\n /** Wire identity (`workflow({ id })`): routing tables, the entry ledger and reporting are keyed on it. */\n id: z.string(),\n contentHash: z.string(),\n meta: z.optional(MetaIR),\n trigger: TriggerIR,\n goal: z.optional(GoalIR),\n exitWhen: z.optional(ConditionIR),\n flow: z.array(NodeIR),\n});\nexport type WorkflowIR = z.infer<typeof WorkflowIR>;\n\nexport const SegmentIR = z.object({\n irVersion: z.literal(IR_VERSION),\n /** Wire identity (`segment({ id })`): segment references and the membership store are keyed on it. */\n id: z.string(),\n contentHash: z.string(),\n /** Provenance plus the human label — MetaIR, same as a workflow's. */\n meta: z.optional(MetaIR),\n condition: ConditionIR,\n});\nexport type SegmentIR = z.infer<typeof SegmentIR>;\n\n/** Template manifest entry: IR records only the reference; the body lives in the template system. */\nexport const TemplateIR = z.object({\n irVersion: z.literal(IR_VERSION),\n key: z.string(),\n channel: ChannelIR,\n});\nexport type TemplateIR = z.infer<typeof TemplateIR>;\n\n/**\n * Action manifest entry: IR records the name; the implementation solidifies\n * into the Worker bundle at deploy time, never into IR (custom code stays out\n * of the data plane by design).\n */\nexport const ActionIR = z.object({\n irVersion: z.literal(IR_VERSION),\n name: z.string(),\n});\nexport type ActionIR = z.infer<typeof ActionIR>;\n\n/**\n * Deployment unit: the complete snapshot one `deploy` produces (terraform\n * apply, same mental model). The server diffs each definition's contentHash to\n * decide publish / skip / retire.\n */\nexport const BundleIR = z.object({\n irVersion: z.literal(IR_VERSION),\n workflows: z.array(WorkflowIR),\n segments: z.array(SegmentIR),\n templates: z.array(TemplateIR),\n actions: z.array(ActionIR),\n});\nexport type BundleIR = z.infer<typeof BundleIR>;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ir.cjs","names":["z"],"sources":["../src/ir.ts"],"sourcesContent":["import * as z from 'zod/mini';\n\n/**\n * IR (Intermediate Representation) — what the DSL compiles to: serializable\n * JSON. It is the three-way contract between the engine that interprets it,\n * the UI (react-flow) that renders it, and diff / audit tooling.\n *\n * Validation strategy: the zod/mini schemas are the authoritative definition.\n * TS types are either inferred from a schema or hand-written under the same\n * name (zod cannot infer recursive types; the hand-written type is anchored\n * with a ZodMiniType<T> annotation, so any drift fails to compile). IR crosses\n * process boundaries (database, network, UI), so runtime validation is a must.\n *\n * ## Versioning (two independent axes)\n *\n * 1. IR format version (irVersion): evolution of the IR schema itself. Adding\n * optional fields does not bump it; only structurally incompatible changes\n * do (changing a discriminant, dropping a field, altering semantics).\n * Readers dispatch on irVersion to the matching schema/migrator; writers\n * always write the current version.\n * 2. Content version (contentHash): a content-addressed id for a single\n * definition's **execution semantics** — the canonical-JSON hash after\n * metadata (meta / label / reason) is stripped, see hash.ts. It is used to:\n * - pin a version when a user enters, so an in-flight journey runs the\n * pinned version to completion (the default policy; compatible hot\n * updates are a future explicit-migration topic). Known exception:\n * segment conditions are resolved **by id at evaluation time** — a\n * segment redeploy changes behaviour of in-flight journeys that\n * reference it. Pinning covers the workflow's own structure; per-hash\n * segment snapshots are future work;\n * - diff on deploy: same hash means unchanged, skip publishing;\n * - audit: answer \"which version was this user on\" at any point.\n *\n * ## Node ids\n *\n * Derived from the structural path at compile time — stable and deterministic:\n * root level '0', '1', …; the j-th child of a branch's i-th case is\n * '{branchId}.c{i}.{j}', the default arm '{branchId}.o.{j}'; cohort arms use\n * the arm name '{cohortId}.{armName}.{j}'; a waitUntil timeout flow '{id}.t.{j}'.\n * Editing a node's parameters keeps its id; inserting a node shifts the ids of\n * its later siblings (a known trade-off — locating in-flight users relies on\n * the pinned version rather than cross-version id alignment, and cross-version\n * mapping is an explicit migrator's job).\n */\n\nexport const IR_VERSION = 2;\n\n/* -------------------------------- Base values ------------------------------- */\n\nexport const ScalarIR = z.union([z.string(), z.number(), z.boolean()]);\nexport type ScalarIR = z.infer<typeof ScalarIR>;\n\n/** Duration: `value` keeps the DSL literal ('23 hours', for display), `ms` is what the engine uses. */\n/*\n * TODO: `value` is hashed alongside `ms`, so '7 days' / '1 week' / '168 hours'\n * — identical to the engine, which only reads ms — produce three different\n * contentHashes. Rewording a duration therefore reports as a semantic change in\n * plan and repins in-flight users. Moving `value` out of the hash (it is a\n * display string) would fix it; deferred because it changes existing hashes.\n */\nexport const DurationIR = z.object({ value: z.string(), ms: z.number() });\nexport type DurationIR = z.infer<typeof DurationIR>;\n\n/** User-property reference — isomorphic to the DSL's UserPropertyRef once serialized (phantom type dropped). */\nexport const UserPropertyRefIR = z.object({\n type: z.literal('user_property'),\n path: z.string(),\n});\nexport type UserPropertyRefIR = z.infer<typeof UserPropertyRefIR>;\n\n/** A message prop / event payload value: either a literal or a user-property reference. */\nexport const PropValueIR = z.union([ScalarIR, UserPropertyRefIR]);\nexport type PropValueIR = z.infer<typeof PropValueIR>;\n\nexport const ChannelIR = z.enum(['email', 'sms', 'push', 'in_app', 'slack', 'discord', 'survey']);\nexport type ChannelIR = z.infer<typeof ChannelIR>;\n\nexport const WeekdayIR = z.enum(['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']);\nexport type WeekdayIR = z.infer<typeof WeekdayIR>;\n\n/* --------------------------------- Provenance -------------------------------- */\n\n/**\n * Source location of the builder call that produced a definition — the IR's\n * source map. Lives under `meta`, so it is **excluded from contentHash**\n * (see hash.ts): provenance never affects execution identity or plan.\n * `file` is cwd-relative when compiled under Node, a URL under Vite dev.\n */\nexport const SourceLocIR = z.object({\n file: z.string(),\n line: z.number(),\n column: z.number(),\n});\nexport type SourceLocIR = z.infer<typeof SourceLocIR>;\n\n/** Node-level metadata: provenance only, for now. Excluded from contentHash. */\nexport const NodeMetaIR = z.object({\n loc: z.optional(SourceLocIR),\n});\nexport type NodeMetaIR = z.infer<typeof NodeMetaIR>;\n\n/* -------------------------------- Conditions -------------------------------- */\n\nexport const PropertyOperatorIR = z.enum([\n 'eq',\n 'ne',\n 'gt',\n 'gte',\n 'lt',\n 'lte',\n 'between',\n 'not_between',\n 'in_array',\n 'not_in_array',\n 'exists',\n 'not_exists',\n 'contains',\n 'not_contains',\n]);\nexport type PropertyOperatorIR = z.infer<typeof PropertyOperatorIR>;\n\n/**\n * Condition expression tree. The shape of value/values follows `op`:\n * eq/ne/gt/gte/lt/lte/contains/not_contains → value; in_array/not_in_array →\n * values; between/not_between → values = [min, max]; exists/not_exists → neither.\n *\n * `payload` predicates test the payload of an event (dotted paths reach nested\n * fields). They are only valid inside a `where` subtree — `performed.where`\n * and the event trigger's `where` — where an event row is in scope; the\n * compiler (compileBundle) and the evaluator both enforce the placement.\n * TODO: runtime refinement narrowing by op.\n */\n/**\n * Provenance rides on conditions too, so the studio can point at the exact\n * `eq(...)` / `and(...)` that produced a node — same rule as nodes: `meta` is\n * dropped before hashing, so carrying it never affects execution identity.\n */\ninterface ConditionBaseIR {\n meta?: NodeMetaIR | undefined;\n}\n\nexport type ConditionIR =\n | (ConditionBaseIR & { type: 'and'; conditions: ConditionIR[] })\n | (ConditionBaseIR & { type: 'or'; conditions: ConditionIR[] })\n | (ConditionBaseIR & { type: 'not'; condition: ConditionIR })\n /** Reference a segment by id (defined in SegmentIR, versioned separately). */\n | (ConditionBaseIR & { type: 'segment'; segment: string })\n | (ConditionBaseIR & {\n type: 'property';\n path: string;\n op: PropertyOperatorIR;\n value?: ScalarIR | undefined;\n values?: ScalarIR[] | undefined;\n })\n | (ConditionBaseIR & {\n /** Event payload predicate; `path` is dotted for nested fields ('tags.utm_source'). */\n type: 'payload';\n path: string;\n op: PropertyOperatorIR;\n value?: ScalarIR | undefined;\n values?: ScalarIR[] | undefined;\n })\n | (ConditionBaseIR & {\n type: 'performed';\n event: string;\n /** Payload filter: only events matching it count (a payload-only subtree). */\n where?: ConditionIR | undefined;\n within?: DurationIR | undefined;\n count?: number | undefined;\n })\n /**\n * The user can be reached on the channel right now: for push, the host's\n * device directory lists at least one device; for the others, the channel's\n * recipient property (email, phone, …) is set.\n */\n | (ConditionBaseIR & { type: 'reachable'; channel: ChannelIR });\n\nconst conditionBase = { meta: z.optional(NodeMetaIR) };\n\nexport const ConditionIR: z.ZodMiniType<ConditionIR> = z.lazy(() =>\n z.discriminatedUnion('type', [\n z.object({ ...conditionBase, type: z.literal('and'), conditions: z.array(ConditionIR) }),\n z.object({ ...conditionBase, type: z.literal('or'), conditions: z.array(ConditionIR) }),\n z.object({ ...conditionBase, type: z.literal('not'), condition: ConditionIR }),\n z.object({ ...conditionBase, type: z.literal('segment'), segment: z.string() }),\n z.object({\n ...conditionBase,\n type: z.literal('property'),\n path: z.string(),\n op: PropertyOperatorIR,\n value: z.optional(ScalarIR),\n values: z.optional(z.array(ScalarIR)),\n }),\n z.object({\n ...conditionBase,\n type: z.literal('payload'),\n path: z.string(),\n op: PropertyOperatorIR,\n value: z.optional(ScalarIR),\n values: z.optional(z.array(ScalarIR)),\n }),\n z.object({\n ...conditionBase,\n type: z.literal('performed'),\n event: z.string(),\n where: z.optional(ConditionIR),\n within: z.optional(DurationIR),\n count: z.optional(z.number()),\n }),\n z.object({ ...conditionBase, type: z.literal('reachable'), channel: ChannelIR }),\n ])\n);\n\n/* --------------------------------- Triggers --------------------------------- */\n\n/**\n * Trigger shapes. The event trigger has two independent gates: `where` tests\n * the triggering event's payload (payload-only subtree, evaluated against the\n * incoming event before anything else), `filter` tests the user's profile.\n */\nexport const TriggerIR = z.discriminatedUnion('type', [\n z.object({\n type: z.literal('event'),\n event: z.string(),\n where: z.optional(ConditionIR),\n filter: z.optional(ConditionIR),\n }),\n z.object({ type: z.literal('segment'), segment: z.string() }),\n z.object({ type: z.literal('date'), at: z.string() }),\n z.object({ type: z.literal('webhook') }),\n]);\nexport type TriggerIR = z.infer<typeof TriggerIR>;\n\n/* ----------------------------------- Nodes ---------------------------------- */\n\ninterface NodeBaseIR {\n /** Stable id derived from the structural path (rules in the file header). */\n id: string;\n /** Optional node name: UI title / observability handle (branch's optional first argument lands here). */\n label?: string | undefined;\n /** Metadata (provenance); excluded from contentHash. */\n meta?: NodeMetaIR | undefined;\n}\n\nexport type NodeIR =\n | (NodeBaseIR & {\n type: 'message';\n channel: ChannelIR;\n template: string;\n props: Record<string, PropValueIR>;\n /**\n * Push only: which of the user's devices get a copy. Absent means every\n * device the directory lists; 'latest' only the most recently registered.\n */\n devices?: 'latest' | undefined;\n /**\n * Email and push: the template's subscription topic, or null for a transactional message\n * (sent whatever the user opted out of). Absent for other channels, and in IR compiled\n * before topics existed: such a step checks the channel-level opt-out only, as it always did.\n */\n topic?: string | null | undefined;\n })\n | (NodeBaseIR & { type: 'delay'; duration: DurationIR })\n | (NodeBaseIR & { type: 'random_delay'; min: DurationIR; max: DurationIR })\n | (NodeBaseIR & {\n type: 'time_window';\n days: WeekdayIR[];\n between: [string, string];\n tz: string;\n })\n | (NodeBaseIR & {\n type: 'wait_until';\n condition: ConditionIR;\n timeout: DurationIR;\n onTimeout: 'continue' | 'exit' | NodeIR[];\n })\n | (NodeBaseIR & {\n type: 'branch';\n cases: { label?: string | undefined; condition: ConditionIR; flow: NodeIR[] }[];\n /** Default arm (the DSL's bare tail argument). Absent = fall through to the main line. */\n otherwise?: NodeIR[] | undefined;\n })\n | (NodeBaseIR & {\n type: 'filter';\n /** Audit-log line for the users this gate drops. Metadata: excluded from contentHash. */\n reason?: string | undefined;\n condition: ConditionIR;\n })\n | (NodeBaseIR & {\n type: 'cohort';\n /**\n * Explicit experiment key: bucketing hashes `userId:key` instead of the\n * structural node id, so the assignment survives node insertion/moves and\n * the experiment keeps one identity across workflow versions. Absent →\n * the node id is used (fine for one-off splits).\n */\n key?: string | undefined;\n /**\n * Arms in authoring order — the order the interpreter accumulates weights\n * in, so it decides which bucket range each arm owns. The DSL keeps that\n * promise by rejecting integer-like arm names, which JavaScript would\n * hoist to the front of the object in numeric order. Weights sum to 100.\n */\n arms: { name: string; weight: number; flow: NodeIR[] }[];\n })\n | (NodeBaseIR & {\n /** `reason` is the audit-log line. Metadata: excluded from contentHash. */\n type: 'exit';\n reason?: string | undefined;\n })\n | (NodeBaseIR & { type: 'send_event'; event: string; payload: Record<string, PropValueIR> })\n | (NodeBaseIR & {\n type: 'action';\n /** Registry name of the custom action; the code itself lives in the deployed Worker bundle. */\n action: string;\n /** Argument values; user_property references are resolved at run time, like message props. */\n args: Record<string, PropValueIR>;\n });\n\nconst nodeBase = { id: z.string(), label: z.optional(z.string()), meta: z.optional(NodeMetaIR) };\n\nexport const NodeIR: z.ZodMiniType<NodeIR> = z.lazy(() =>\n z.discriminatedUnion('type', [\n z.object({\n ...nodeBase,\n type: z.literal('message'),\n channel: ChannelIR,\n template: z.string(),\n props: z.record(z.string(), PropValueIR),\n devices: z.optional(z.literal('latest')),\n topic: z.optional(z.nullable(z.string())),\n }),\n z.object({ ...nodeBase, type: z.literal('delay'), duration: DurationIR }),\n z.object({ ...nodeBase, type: z.literal('random_delay'), min: DurationIR, max: DurationIR }),\n z.object({\n ...nodeBase,\n type: z.literal('time_window'),\n days: z.array(WeekdayIR),\n between: z.tuple([z.string(), z.string()]),\n tz: z.string(),\n }),\n z.object({\n ...nodeBase,\n type: z.literal('wait_until'),\n condition: ConditionIR,\n timeout: DurationIR,\n onTimeout: z.union([z.literal('continue'), z.literal('exit'), z.array(NodeIR)]),\n }),\n z.object({\n ...nodeBase,\n type: z.literal('branch'),\n cases: z.array(\n z.object({\n label: z.optional(z.string()),\n condition: ConditionIR,\n flow: z.array(NodeIR),\n })\n ),\n otherwise: z.optional(z.array(NodeIR)),\n }),\n z.object({\n ...nodeBase,\n type: z.literal('filter'),\n condition: ConditionIR,\n reason: z.optional(z.string()),\n }),\n z.object({\n ...nodeBase,\n type: z.literal('cohort'),\n key: z.optional(z.string()),\n arms: z.array(z.object({ name: z.string(), weight: z.number(), flow: z.array(NodeIR) })),\n }),\n z.object({ ...nodeBase, type: z.literal('exit'), reason: z.optional(z.string()) }),\n z.object({\n ...nodeBase,\n type: z.literal('send_event'),\n event: z.string(),\n payload: z.record(z.string(), PropValueIR),\n }),\n z.object({\n ...nodeBase,\n type: z.literal('action'),\n action: z.string(),\n args: z.record(z.string(), PropValueIR),\n }),\n ])\n);\n\n/* ---------------------- Top-level definitions and bundle --------------------- */\n\nexport const GoalIR = z.object({\n condition: ConditionIR,\n within: z.optional(DurationIR),\n exitOnMatch: z.boolean(),\n});\nexport type GoalIR = z.infer<typeof GoalIR>;\n\n/**\n * Human-facing metadata: **excluded from contentHash** (see hash.ts).\n * Editing a name or description does not touch execution semantics, so it never\n * invalidates the version in-flight users are pinned to, nor shows up in plan.\n */\nexport const MetaIR = z.object({\n /**\n * Human label. The asset's `id` is its identity — a wire key that routing\n * tables and the entry ledger are built on — so it is not something to\n * rename for readability. This is: it is metadata, dropped before hashing,\n * and the studio shows it in place of the key wherever it is set.\n */\n name: z.optional(z.string()),\n description: z.optional(z.string()),\n loc: z.optional(SourceLocIR),\n});\nexport type MetaIR = z.infer<typeof MetaIR>;\n\nexport const WorkflowIR = z.object({\n irVersion: z.literal(IR_VERSION),\n /** Wire identity (`workflow({ id })`): routing tables, the entry ledger and reporting are keyed on it. */\n id: z.string(),\n contentHash: z.string(),\n meta: z.optional(MetaIR),\n trigger: TriggerIR,\n goal: z.optional(GoalIR),\n exitWhen: z.optional(ConditionIR),\n flow: z.array(NodeIR),\n});\nexport type WorkflowIR = z.infer<typeof WorkflowIR>;\n\nexport const SegmentIR = z.object({\n irVersion: z.literal(IR_VERSION),\n /** Wire identity (`segment({ id })`): segment references and the membership store are keyed on it. */\n id: z.string(),\n contentHash: z.string(),\n /** Provenance plus the human label — MetaIR, same as a workflow's. */\n meta: z.optional(MetaIR),\n condition: ConditionIR,\n});\nexport type SegmentIR = z.infer<typeof SegmentIR>;\n\n/** Template manifest entry: IR records only the reference; the body lives in the template system. */\nexport const TemplateIR = z.object({\n irVersion: z.literal(IR_VERSION),\n key: z.string(),\n channel: ChannelIR,\n});\nexport type TemplateIR = z.infer<typeof TemplateIR>;\n\n/**\n * Action manifest entry: IR records the name; the implementation solidifies\n * into the Worker bundle at deploy time, never into IR (custom code stays out\n * of the data plane by design).\n */\nexport const ActionIR = z.object({\n irVersion: z.literal(IR_VERSION),\n name: z.string(),\n});\nexport type ActionIR = z.infer<typeof ActionIR>;\n\n/**\n * Deployment unit: the complete snapshot one `deploy` produces (terraform\n * apply, same mental model). The server diffs each definition's contentHash to\n * decide publish / skip / retire.\n */\n/**\n * The subscription topics the contract declares, carried by the bundle so the engine validates\n * `/subscriptions` and the templates' topics against the deployed list, not the SDK's word.\n */\nexport const SubscriptionsIR = z.object({\n topics: z.array(z.string()),\n});\nexport type SubscriptionsIR = z.infer<typeof SubscriptionsIR>;\n\nexport const BundleIR = z.object({\n irVersion: z.literal(IR_VERSION),\n workflows: z.array(WorkflowIR),\n segments: z.array(SegmentIR),\n templates: z.array(TemplateIR),\n actions: z.array(ActionIR),\n /** Absent in a bundle compiled before topics existed: no topics. */\n subscriptions: z.optional(SubscriptionsIR),\n});\nexport type BundleIR = z.infer<typeof BundleIR>;\n"],"mappings":"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"}
|
package/dist/ir.d.cts
CHANGED
|
@@ -222,6 +222,12 @@ export type NodeIR = (NodeBaseIR & {
|
|
|
222
222
|
* device the directory lists; 'latest' only the most recently registered.
|
|
223
223
|
*/
|
|
224
224
|
devices?: 'latest' | undefined;
|
|
225
|
+
/**
|
|
226
|
+
* Email and push: the template's subscription topic, or null for a transactional message
|
|
227
|
+
* (sent whatever the user opted out of). Absent for other channels, and in IR compiled
|
|
228
|
+
* before topics existed: such a step checks the channel-level opt-out only, as it always did.
|
|
229
|
+
*/
|
|
230
|
+
topic?: string | null | undefined;
|
|
225
231
|
}) | (NodeBaseIR & {
|
|
226
232
|
type: 'delay';
|
|
227
233
|
duration: DurationIR;
|
|
@@ -398,6 +404,14 @@ export type ActionIR = z.infer<typeof ActionIR>;
|
|
|
398
404
|
* apply, same mental model). The server diffs each definition's contentHash to
|
|
399
405
|
* decide publish / skip / retire.
|
|
400
406
|
*/
|
|
407
|
+
/**
|
|
408
|
+
* The subscription topics the contract declares, carried by the bundle so the engine validates
|
|
409
|
+
* `/subscriptions` and the templates' topics against the deployed list, not the SDK's word.
|
|
410
|
+
*/
|
|
411
|
+
export declare const SubscriptionsIR: z.ZodMiniObject<{
|
|
412
|
+
topics: z.ZodMiniArray<z.ZodMiniString<string>>;
|
|
413
|
+
}, z.core.$strip>;
|
|
414
|
+
export type SubscriptionsIR = z.infer<typeof SubscriptionsIR>;
|
|
401
415
|
export declare const BundleIR: z.ZodMiniObject<{
|
|
402
416
|
irVersion: z.ZodMiniLiteral<2>;
|
|
403
417
|
workflows: z.ZodMiniArray<z.ZodMiniObject<{
|
|
@@ -470,6 +484,9 @@ export declare const BundleIR: z.ZodMiniObject<{
|
|
|
470
484
|
irVersion: z.ZodMiniLiteral<2>;
|
|
471
485
|
name: z.ZodMiniString<string>;
|
|
472
486
|
}, z.core.$strip>>;
|
|
487
|
+
subscriptions: z.ZodMiniOptional<z.ZodMiniObject<{
|
|
488
|
+
topics: z.ZodMiniArray<z.ZodMiniString<string>>;
|
|
489
|
+
}, z.core.$strip>>;
|
|
473
490
|
}, z.core.$strip>;
|
|
474
491
|
export type BundleIR = z.infer<typeof BundleIR>;
|
|
475
492
|
export {};
|
package/dist/ir.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ir.d.cts","names":[],"sources":["../src/ir.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBA6Ca;qBAIA,UAAQ,EAAA,uBAAA,EAAA,uBAAA,EAAA,uBAAA,EAAA;YACT,WAAW,EAAE,aAAa;;qBAUzB,YAAU,EAAA;;;GAAkD,EAAA,KAAA;YAC7D,aAAa,EAAE,aAAa;;qBAG3B,mBAAiB,EAAA;;;GAG5B,EAAA,KAAA;YACU,oBAAoB,EAAE,aAAa;;qBAGlC,aAAW,EAAA,uBAAA,EAAA,uBAAA,EAAA,uBAAA,EAAA,uBAAA,EAAA,2BAAA,EAAA;;;GAAyC,EAAA,KAAA;YACrD,cAAc,EAAE,aAAa;qBAE5B,WAAS,EAAA;;;;;;;;;YACV,YAAY,EAAE,aAAa;qBAE1B,WAAS,EAAA;;;;;;;;;YACV,YAAY,EAAE,aAAa;;;;;;;qBAU1B,aAAW,EAAA;;;;GAItB,EAAA,KAAA;YACU,cAAc,EAAE,aAAa;;qBAG5B,YAAU,EAAA;;;;;;GAErB,EAAA,KAAA;YACU,aAAa,EAAE,aAAa;qBAI3B,oBAAkB,EAAA;;;;;;;;;;;;;;;;YAgBnB,qBAAqB,EAAE,aAAa;;;;;;;;;;;;;;;;;UAkBtC;EACR,OAAO;;YAGG,eACP;EAAoB;EAAa,YAAY;MAC7C;EAAoB;EAAY,YAAY;MAC5C;EAAoB;EAAa,WAAW;;;CAE5C;EAAoB;EAAiB;MACrC;EACC;EACA;EACA,IAAI;EACJ,QAAQ;EACR,SAAS;MAEV;;EAEC;EACA;EACA,IAAI;EACJ,QAAQ;EACR,SAAS;MAEV;EACC;EACA;;EAEA,QAAQ;EACR,SAAS;EACT;;;;;;;CAOD;EAAoB;EAAmB,SAAS;;qBAIxC,aAAa,EAAE,YAAY;;;;;;qBAyC3B,WAAS,EAAA,2BAAA,EAAA;;;;;;;;;;;;;GAUpB,EAAA,KAAA;YACU,YAAY,EAAE,aAAa;UAI7B;;EAER;;EAEA;;EAEA,OAAO;;YAGG,UACP;EACC;EACA,SAAS;EACT;EACA,OAAO,eAAe;;;;;EAKtB;MAED;EAAe;EAAe,UAAU;MACxC;EAAe;EAAsB,KAAK;EAAY,KAAK;MAC3D;EACC;EACA,MAAM;EACN;EACA;MAED;EACC;EACA,WAAW;EACX,SAAS;EACT,iCAAiC;MAElC;EACC;EACA;IAAS;IAA4B,WAAW;IAAa,MAAM;;;EAEnE,YAAY;MAEb;EACC;;EAEA;EACA,WAAW;MAEZ;EACC;;;;;;;EAOA;;;;;;;EAOA;IAAQ;IAAc;IAAgB,MAAM;;MAE7C;;EAEC;EACA;MAED;EAAe;EAAoB;EAAe,SAAS,eAAe;MAC1E;EACC;;EAEA;;EAEA,MAAM,eAAe;;qBAKd,QAAQ,EAAE,YAAY;
|
|
1
|
+
{"version":3,"file":"ir.d.cts","names":[],"sources":["../src/ir.ts"],"mappings":"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"}
|
package/dist/ir.d.mts
CHANGED
|
@@ -222,6 +222,12 @@ export type NodeIR = (NodeBaseIR & {
|
|
|
222
222
|
* device the directory lists; 'latest' only the most recently registered.
|
|
223
223
|
*/
|
|
224
224
|
devices?: 'latest' | undefined;
|
|
225
|
+
/**
|
|
226
|
+
* Email and push: the template's subscription topic, or null for a transactional message
|
|
227
|
+
* (sent whatever the user opted out of). Absent for other channels, and in IR compiled
|
|
228
|
+
* before topics existed: such a step checks the channel-level opt-out only, as it always did.
|
|
229
|
+
*/
|
|
230
|
+
topic?: string | null | undefined;
|
|
225
231
|
}) | (NodeBaseIR & {
|
|
226
232
|
type: 'delay';
|
|
227
233
|
duration: DurationIR;
|
|
@@ -398,6 +404,14 @@ export type ActionIR = z.infer<typeof ActionIR>;
|
|
|
398
404
|
* apply, same mental model). The server diffs each definition's contentHash to
|
|
399
405
|
* decide publish / skip / retire.
|
|
400
406
|
*/
|
|
407
|
+
/**
|
|
408
|
+
* The subscription topics the contract declares, carried by the bundle so the engine validates
|
|
409
|
+
* `/subscriptions` and the templates' topics against the deployed list, not the SDK's word.
|
|
410
|
+
*/
|
|
411
|
+
export declare const SubscriptionsIR: z.ZodMiniObject<{
|
|
412
|
+
topics: z.ZodMiniArray<z.ZodMiniString<string>>;
|
|
413
|
+
}, z.core.$strip>;
|
|
414
|
+
export type SubscriptionsIR = z.infer<typeof SubscriptionsIR>;
|
|
401
415
|
export declare const BundleIR: z.ZodMiniObject<{
|
|
402
416
|
irVersion: z.ZodMiniLiteral<2>;
|
|
403
417
|
workflows: z.ZodMiniArray<z.ZodMiniObject<{
|
|
@@ -470,6 +484,9 @@ export declare const BundleIR: z.ZodMiniObject<{
|
|
|
470
484
|
irVersion: z.ZodMiniLiteral<2>;
|
|
471
485
|
name: z.ZodMiniString<string>;
|
|
472
486
|
}, z.core.$strip>>;
|
|
487
|
+
subscriptions: z.ZodMiniOptional<z.ZodMiniObject<{
|
|
488
|
+
topics: z.ZodMiniArray<z.ZodMiniString<string>>;
|
|
489
|
+
}, z.core.$strip>>;
|
|
473
490
|
}, z.core.$strip>;
|
|
474
491
|
export type BundleIR = z.infer<typeof BundleIR>;
|
|
475
492
|
export {};
|
package/dist/ir.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ir.d.mts","names":[],"sources":["../src/ir.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBA6Ca;qBAIA,UAAQ,EAAA,uBAAA,EAAA,uBAAA,EAAA,uBAAA,EAAA;YACT,WAAW,EAAE,aAAa;;qBAUzB,YAAU,EAAA;;;GAAkD,EAAA,KAAA;YAC7D,aAAa,EAAE,aAAa;;qBAG3B,mBAAiB,EAAA;;;GAG5B,EAAA,KAAA;YACU,oBAAoB,EAAE,aAAa;;qBAGlC,aAAW,EAAA,uBAAA,EAAA,uBAAA,EAAA,uBAAA,EAAA,uBAAA,EAAA,2BAAA,EAAA;;;GAAyC,EAAA,KAAA;YACrD,cAAc,EAAE,aAAa;qBAE5B,WAAS,EAAA;;;;;;;;;YACV,YAAY,EAAE,aAAa;qBAE1B,WAAS,EAAA;;;;;;;;;YACV,YAAY,EAAE,aAAa;;;;;;;qBAU1B,aAAW,EAAA;;;;GAItB,EAAA,KAAA;YACU,cAAc,EAAE,aAAa;;qBAG5B,YAAU,EAAA;;;;;;GAErB,EAAA,KAAA;YACU,aAAa,EAAE,aAAa;qBAI3B,oBAAkB,EAAA;;;;;;;;;;;;;;;;YAgBnB,qBAAqB,EAAE,aAAa;;;;;;;;;;;;;;;;;UAkBtC;EACR,OAAO;;YAGG,eACP;EAAoB;EAAa,YAAY;MAC7C;EAAoB;EAAY,YAAY;MAC5C;EAAoB;EAAa,WAAW;;;CAE5C;EAAoB;EAAiB;MACrC;EACC;EACA;EACA,IAAI;EACJ,QAAQ;EACR,SAAS;MAEV;;EAEC;EACA;EACA,IAAI;EACJ,QAAQ;EACR,SAAS;MAEV;EACC;EACA;;EAEA,QAAQ;EACR,SAAS;EACT;;;;;;;CAOD;EAAoB;EAAmB,SAAS;;qBAIxC,aAAa,EAAE,YAAY;;;;;;qBAyC3B,WAAS,EAAA,2BAAA,EAAA;;;;;;;;;;;;;GAUpB,EAAA,KAAA;YACU,YAAY,EAAE,aAAa;UAI7B;;EAER;;EAEA;;EAEA,OAAO;;YAGG,UACP;EACC;EACA,SAAS;EACT;EACA,OAAO,eAAe;;;;;EAKtB;MAED;EAAe;EAAe,UAAU;MACxC;EAAe;EAAsB,KAAK;EAAY,KAAK;MAC3D;EACC;EACA,MAAM;EACN;EACA;MAED;EACC;EACA,WAAW;EACX,SAAS;EACT,iCAAiC;MAElC;EACC;EACA;IAAS;IAA4B,WAAW;IAAa,MAAM;;;EAEnE,YAAY;MAEb;EACC;;EAEA;EACA,WAAW;MAEZ;EACC;;;;;;;EAOA;;;;;;;EAOA;IAAQ;IAAc;IAAgB,MAAM;;MAE7C;;EAEC;EACA;MAED;EAAe;EAAoB;EAAe,SAAS,eAAe;MAC1E;EACC;;EAEA;;EAEA,MAAM,eAAe;;qBAKd,QAAQ,EAAE,YAAY;
|
|
1
|
+
{"version":3,"file":"ir.d.mts","names":[],"sources":["../src/ir.ts"],"mappings":"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"}
|
package/dist/ir.mjs
CHANGED
|
@@ -193,7 +193,8 @@ const NodeIR = z.lazy(() => z.discriminatedUnion("type", [
|
|
|
193
193
|
channel: ChannelIR,
|
|
194
194
|
template: z.string(),
|
|
195
195
|
props: z.record(z.string(), PropValueIR),
|
|
196
|
-
devices: z.optional(z.literal("latest"))
|
|
196
|
+
devices: z.optional(z.literal("latest")),
|
|
197
|
+
topic: z.optional(z.nullable(z.string()))
|
|
197
198
|
}),
|
|
198
199
|
z.object({
|
|
199
200
|
...nodeBase,
|
|
@@ -329,13 +330,20 @@ const ActionIR = z.object({
|
|
|
329
330
|
* apply, same mental model). The server diffs each definition's contentHash to
|
|
330
331
|
* decide publish / skip / retire.
|
|
331
332
|
*/
|
|
333
|
+
/**
|
|
334
|
+
* The subscription topics the contract declares, carried by the bundle so the engine validates
|
|
335
|
+
* `/subscriptions` and the templates' topics against the deployed list, not the SDK's word.
|
|
336
|
+
*/
|
|
337
|
+
const SubscriptionsIR = z.object({ topics: z.array(z.string()) });
|
|
332
338
|
const BundleIR = z.object({
|
|
333
339
|
irVersion: z.literal(2),
|
|
334
340
|
workflows: z.array(WorkflowIR),
|
|
335
341
|
segments: z.array(SegmentIR),
|
|
336
342
|
templates: z.array(TemplateIR),
|
|
337
|
-
actions: z.array(ActionIR)
|
|
343
|
+
actions: z.array(ActionIR),
|
|
344
|
+
/** Absent in a bundle compiled before topics existed: no topics. */
|
|
345
|
+
subscriptions: z.optional(SubscriptionsIR)
|
|
338
346
|
});
|
|
339
|
-
export { ActionIR, BundleIR, ChannelIR, ConditionIR, DurationIR, GoalIR, IR_VERSION, MetaIR, NodeIR, NodeMetaIR, PropValueIR, PropertyOperatorIR, ScalarIR, SegmentIR, SourceLocIR, TemplateIR, TriggerIR, UserPropertyRefIR, WeekdayIR, WorkflowIR };
|
|
347
|
+
export { ActionIR, BundleIR, ChannelIR, ConditionIR, DurationIR, GoalIR, IR_VERSION, MetaIR, NodeIR, NodeMetaIR, PropValueIR, PropertyOperatorIR, ScalarIR, SegmentIR, SourceLocIR, SubscriptionsIR, TemplateIR, TriggerIR, UserPropertyRefIR, WeekdayIR, WorkflowIR };
|
|
340
348
|
|
|
341
349
|
//# sourceMappingURL=ir.mjs.map
|