@voyantjs/workflows 0.28.3 → 0.29.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.
Files changed (59) hide show
  1. package/dist/driver.d.ts +237 -0
  2. package/dist/driver.d.ts.map +1 -0
  3. package/dist/driver.js +53 -0
  4. package/dist/events/compile.d.ts +34 -0
  5. package/dist/events/compile.d.ts.map +1 -0
  6. package/dist/events/compile.js +204 -0
  7. package/dist/events/index.d.ts +8 -0
  8. package/dist/events/index.d.ts.map +1 -0
  9. package/dist/events/index.js +11 -0
  10. package/dist/events/input-mapper.d.ts +24 -0
  11. package/dist/events/input-mapper.d.ts.map +1 -0
  12. package/dist/events/input-mapper.js +169 -0
  13. package/dist/events/manifest-builder.d.ts +32 -0
  14. package/dist/events/manifest-builder.d.ts.map +1 -0
  15. package/dist/events/manifest-builder.js +66 -0
  16. package/dist/events/payload-hash.d.ts +46 -0
  17. package/dist/events/payload-hash.d.ts.map +1 -0
  18. package/dist/events/payload-hash.js +98 -0
  19. package/dist/events/predicate.d.ts +77 -0
  20. package/dist/events/predicate.d.ts.map +1 -0
  21. package/dist/events/predicate.js +347 -0
  22. package/dist/events/registry.d.ts +37 -0
  23. package/dist/events/registry.d.ts.map +1 -0
  24. package/dist/events/registry.js +47 -0
  25. package/dist/handler/index.d.ts +8 -0
  26. package/dist/handler/index.d.ts.map +1 -1
  27. package/dist/handler/index.js +1 -0
  28. package/dist/http-ingest.d.ts +54 -0
  29. package/dist/http-ingest.d.ts.map +1 -0
  30. package/dist/http-ingest.js +214 -0
  31. package/dist/protocol/index.d.ts +17 -2
  32. package/dist/protocol/index.d.ts.map +1 -1
  33. package/dist/runtime/ctx.d.ts +9 -0
  34. package/dist/runtime/ctx.d.ts.map +1 -1
  35. package/dist/runtime/ctx.js +17 -0
  36. package/dist/runtime/executor.d.ts +7 -0
  37. package/dist/runtime/executor.d.ts.map +1 -1
  38. package/dist/runtime/executor.js +1 -0
  39. package/dist/trigger.d.ts +28 -14
  40. package/dist/trigger.d.ts.map +1 -1
  41. package/dist/trigger.js +4 -4
  42. package/dist/workflow.d.ts +10 -0
  43. package/dist/workflow.d.ts.map +1 -1
  44. package/package.json +14 -2
  45. package/src/driver.ts +277 -0
  46. package/src/events/compile.ts +268 -0
  47. package/src/events/index.ts +42 -0
  48. package/src/events/input-mapper.ts +201 -0
  49. package/src/events/manifest-builder.ts +97 -0
  50. package/src/events/payload-hash.ts +110 -0
  51. package/src/events/predicate.ts +390 -0
  52. package/src/events/registry.ts +88 -0
  53. package/src/handler/index.ts +9 -0
  54. package/src/http-ingest.ts +299 -0
  55. package/src/protocol/index.ts +17 -2
  56. package/src/runtime/ctx.ts +29 -0
  57. package/src/runtime/executor.ts +8 -0
  58. package/src/trigger.ts +31 -15
  59. package/src/workflow.ts +11 -0
@@ -0,0 +1,237 @@
1
+ import type { WorkflowManifest } from "./protocol/index.js";
2
+ import type { ListRunsOptions, Run, RunDetail, RunSummary, TriggerOptions } from "./trigger.js";
3
+ import type { EnvironmentName, WaitpointKind } from "./types.js";
4
+ import type { WorkflowDefinition } from "./workflow.js";
5
+ /**
6
+ * Read-only view of a service container. Step bodies resolve services via
7
+ * `ctx.services.resolve(...)`. The framework's `ModuleContainer`
8
+ * (in `@voyantjs/core`) satisfies this shape structurally; we don't import
9
+ * it directly to keep `@voyantjs/workflows` a leaf package.
10
+ */
11
+ export interface ServiceResolver {
12
+ resolve<T>(name: string): T;
13
+ has(name: string): boolean;
14
+ }
15
+ /**
16
+ * Structural shape of an event envelope as ingested by a driver. Matches
17
+ * `EventEnvelope` from `@voyantjs/core` (`name`, `data`, `metadata?`, `emittedAt`),
18
+ * declared structurally so the SDK package doesn't import core.
19
+ */
20
+ export interface IngestEventEnvelope<TData = unknown> {
21
+ /** Event name in `<resource>.<pastTenseAction>` form. */
22
+ name: string;
23
+ /** Business payload. */
24
+ data: TData;
25
+ /** Optional metadata. `metadata.eventId` is the canonical idempotency seed
26
+ * (a fresh ULID, stamped by the framework's EventBus forwarder). External
27
+ * callers may supply their own. See architecture doc §15.2. */
28
+ metadata?: Record<string, unknown> & {
29
+ eventId?: string;
30
+ };
31
+ /** ISO timestamp string. */
32
+ emittedAt: string;
33
+ }
34
+ /**
35
+ * Minimal logger contract drivers can rely on. Matches the framework logger's
36
+ * call signature without taking a hard dependency on it.
37
+ */
38
+ export type DriverLogger = (level: "debug" | "info" | "warn" | "error", msg: string, data?: object) => void;
39
+ /**
40
+ * Deps the framework injects into a `DriverFactory` at boot. Driver factories
41
+ * close over their environment-specific options (DO bindings, DB pool, etc.)
42
+ * and read framework deps from this argument.
43
+ */
44
+ export interface DriverFactoryDeps {
45
+ /** Read-only view of the framework's `ModuleContainer`. */
46
+ services: ServiceResolver;
47
+ /** Framework logger. */
48
+ logger: DriverLogger;
49
+ /** Injectable clock — defaults to `() => Date.now()`. */
50
+ now?: () => number;
51
+ }
52
+ /**
53
+ * Concrete driver factories return this. `createApp()` calls it once, after
54
+ * the container is built, to obtain the `WorkflowDriver`.
55
+ */
56
+ export type DriverFactory = (deps: DriverFactoryDeps) => WorkflowDriver;
57
+ /**
58
+ * Argument to `driver.ingestEvent(...)`. The framework's EventBus forwarder
59
+ * builds this from the core `EventEnvelope`; external HTTP callers (the
60
+ * optional ingest adapter, voyant-cloud) build it from a wire payload.
61
+ */
62
+ export interface IngestEventArgs {
63
+ environment: EnvironmentName;
64
+ envelope: IngestEventEnvelope;
65
+ /** Optional caller-supplied idempotency override. When absent, the driver
66
+ * derives a key per match from `metadata.eventId` (or a content hash
67
+ * fallback). See architecture doc §15.2. */
68
+ idempotencyKey?: string;
69
+ }
70
+ /** Per-filter outcome from a single `ingestEvent` call. */
71
+ export type IngestMatch = {
72
+ filterId: string;
73
+ targetWorkflowId: string;
74
+ runId: string;
75
+ idempotencyKey: string;
76
+ status: "queued";
77
+ } | {
78
+ filterId: string;
79
+ status: "skipped";
80
+ reason: "where_eval_error" | "input_projection_error" | "input_schema_violation";
81
+ details?: string;
82
+ } | {
83
+ filterId: string;
84
+ targetWorkflowId: string;
85
+ status: "error";
86
+ reason: string;
87
+ };
88
+ export type IngestEventResponse = {
89
+ ok: true;
90
+ eventId: string;
91
+ matches: IngestMatch[];
92
+ } | {
93
+ ok: false;
94
+ reason: "manifest_not_registered" | "environment_mismatch" | "payload_too_large" | "trigger_failed_for_all_matches";
95
+ message?: string;
96
+ };
97
+ /**
98
+ * The mandatory driver contract. Every concrete driver — InMemory, Mode 2
99
+ * (Postgres), Mode 1 (CF edge) — implements all five methods. The compliance
100
+ * test suite (`driver-compliance.test.ts`) is the contract.
101
+ */
102
+ export interface WorkflowDriver {
103
+ /**
104
+ * Idempotent. Same manifest body returns the same `versionId` across calls.
105
+ * Failures are NOT swallowed — `createApp()`'s bootstrap surfaces rejections
106
+ * (see architecture doc §21.22).
107
+ */
108
+ registerManifest(args: {
109
+ environment: EnvironmentName;
110
+ manifest: WorkflowManifest;
111
+ }): Promise<{
112
+ versionId: string;
113
+ }>;
114
+ /**
115
+ * Trigger a workflow run by id or definition handle. Honors
116
+ * `opts.idempotencyKey` for dedup (returns the existing run on conflict).
117
+ */
118
+ trigger<TIn, TOut>(workflow: WorkflowDefinition<TIn, TOut> | string, input: TIn, opts?: TriggerOptions): Promise<Run<TOut>>;
119
+ /**
120
+ * Ingest one event. Synchronously (from the caller's POV) loads the
121
+ * registered manifest, evaluates `where`, projects `input`, and triggers
122
+ * one run per match. See architecture doc §15.
123
+ */
124
+ ingestEvent(args: IngestEventArgs): Promise<IngestEventResponse>;
125
+ /**
126
+ * Read the registered manifest. Used at boot for version-mismatch detection
127
+ * and by the dashboard for filter inspection.
128
+ */
129
+ getManifest(args: {
130
+ environment: EnvironmentName;
131
+ }): Promise<WorkflowManifest | null>;
132
+ /**
133
+ * Optional. Drains in-flight steps, refuses new triggers, awaits time-wheel
134
+ * exit. Default `gracefulMs` is 30_000.
135
+ */
136
+ shutdown?(opts?: {
137
+ gracefulMs?: number;
138
+ }): Promise<void>;
139
+ /**
140
+ * Optional read-side surface. Drivers that can support listings, run detail,
141
+ * and journal streams declare this; consumers (the dashboard) duck-type on
142
+ * `driver.admin`. See architecture doc §6.2.
143
+ */
144
+ admin?: WorkflowAdmin | Partial<WorkflowAdmin>;
145
+ }
146
+ /**
147
+ * Read-side operations. Implemented natively by the Mode 2 driver against
148
+ * Postgres, partially by Mode 1 (single-run reads only — `listRuns` is not
149
+ * implemented in self-host Mode 1; voyant-cloud provides an index layer).
150
+ * See architecture doc §6.2 + §8.3.
151
+ */
152
+ export interface WorkflowAdmin {
153
+ listRuns(opts?: ListRunsOptions): Promise<{
154
+ runs: RunSummary[];
155
+ nextCursor?: string;
156
+ }>;
157
+ getRun(runId: string): Promise<RunDetail | null>;
158
+ cancelRun(runId: string, opts?: {
159
+ reason?: string;
160
+ compensate?: boolean;
161
+ }): Promise<void>;
162
+ /**
163
+ * Subscribe to journal events for a run. The async-iterable shape lets
164
+ * dashboards stream live without polling.
165
+ */
166
+ streamRun(runId: string): AsyncIterable<AdminStreamEvent>;
167
+ }
168
+ /**
169
+ * The admin streaming surface re-uses the protocol's `StreamEvent` for
170
+ * step/waitpoint/log events; this type is its alias to keep the import
171
+ * surface narrow.
172
+ */
173
+ export type AdminStreamEvent = {
174
+ kind: "run.snapshot";
175
+ at: number;
176
+ status: string;
177
+ metadata: Record<string, unknown>;
178
+ } | {
179
+ kind: "step.started";
180
+ at: number;
181
+ stepId: string;
182
+ runtime: "edge" | "node";
183
+ } | {
184
+ kind: "step.ok";
185
+ at: number;
186
+ stepId: string;
187
+ durationMs: number;
188
+ } | {
189
+ kind: "step.err";
190
+ at: number;
191
+ stepId: string;
192
+ error: {
193
+ code: string;
194
+ message: string;
195
+ };
196
+ } | {
197
+ kind: "waitpoint.registered";
198
+ at: number;
199
+ waitpointId: string;
200
+ waitpointKind: WaitpointKind;
201
+ } | {
202
+ kind: "waitpoint.resolved";
203
+ at: number;
204
+ waitpointId: string;
205
+ } | {
206
+ kind: "log";
207
+ at: number;
208
+ level: "info" | "warn" | "error";
209
+ message: string;
210
+ } | {
211
+ kind: "run.finished";
212
+ at: number;
213
+ status: string;
214
+ };
215
+ /**
216
+ * Base class for typed driver errors. Concrete subclasses below; consumers
217
+ * `instanceof` to handle specific cases.
218
+ */
219
+ export declare class WorkflowDriverError extends Error {
220
+ readonly code: string;
221
+ readonly cause?: unknown;
222
+ constructor(code: string, message: string, opts?: {
223
+ cause?: unknown;
224
+ });
225
+ }
226
+ export declare class ManifestNotRegisteredError extends WorkflowDriverError {
227
+ constructor(environment: EnvironmentName);
228
+ }
229
+ export declare class EventTooLargeError extends WorkflowDriverError {
230
+ readonly bytes: number;
231
+ readonly limit: number;
232
+ constructor(bytes: number, limit?: number);
233
+ }
234
+ export declare class EnvironmentMismatchError extends WorkflowDriverError {
235
+ constructor(eventEnv: EnvironmentName, manifestEnv: EnvironmentName);
236
+ }
237
+ //# sourceMappingURL=driver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"driver.d.ts","sourceRoot":"","sources":["../src/driver.ts"],"names":[],"mappings":"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"}
package/dist/driver.js ADDED
@@ -0,0 +1,53 @@
1
+ // The WorkflowDriver contract.
2
+ //
3
+ // A driver is the runtime-side object that backs `createApp({ workflows })`:
4
+ // it owns manifest registration, run triggering, event ingest, and (optionally)
5
+ // admin reads. Concrete drivers live in downstream packages
6
+ // (`@voyantjs/workflows-orchestrator` for InMemory, `-node` for Mode 2 / Postgres,
7
+ // `-cloudflare` for Mode 1 / DO+KV).
8
+ //
9
+ // Drivers are constructed via *factories* — `DriverFactory` is a function the
10
+ // framework invokes after `createApp()` has assembled its `ModuleContainer`.
11
+ // This lets concrete factories accept their environment-specific options
12
+ // (DO namespaces, DB pool, etc.) at user-call time and receive framework
13
+ // deps (services, logger, …) at boot time, without a setter API.
14
+ //
15
+ // Authoritative architecture: docs/architecture/workflows-runtime-architecture.md §6.
16
+ // ---- Errors ----
17
+ /**
18
+ * Base class for typed driver errors. Concrete subclasses below; consumers
19
+ * `instanceof` to handle specific cases.
20
+ */
21
+ export class WorkflowDriverError extends Error {
22
+ code;
23
+ cause;
24
+ constructor(code, message, opts) {
25
+ super(message);
26
+ this.name = "WorkflowDriverError";
27
+ this.code = code;
28
+ this.cause = opts?.cause;
29
+ }
30
+ }
31
+ export class ManifestNotRegisteredError extends WorkflowDriverError {
32
+ constructor(environment) {
33
+ super("manifest_not_registered", `No manifest is registered for environment "${environment}". ` +
34
+ `createApp() must complete its bootstrap before driver.ingestEvent(...) can fire.`);
35
+ this.name = "ManifestNotRegisteredError";
36
+ }
37
+ }
38
+ export class EventTooLargeError extends WorkflowDriverError {
39
+ bytes;
40
+ limit;
41
+ constructor(bytes, limit = 256 * 1024) {
42
+ super("payload_too_large", `Event payload is ${bytes} bytes, exceeding the ${limit}-byte cap.`);
43
+ this.name = "EventTooLargeError";
44
+ this.bytes = bytes;
45
+ this.limit = limit;
46
+ }
47
+ }
48
+ export class EnvironmentMismatchError extends WorkflowDriverError {
49
+ constructor(eventEnv, manifestEnv) {
50
+ super("environment_mismatch", `Event environment "${eventEnv}" does not match the registered manifest's environment "${manifestEnv}".`);
51
+ this.name = "EnvironmentMismatchError";
52
+ }
53
+ }
@@ -0,0 +1,34 @@
1
+ import type { EventFilterDeclaration } from "../trigger.js";
2
+ import { type EventFilterRuntimeEntry } from "./registry.js";
3
+ export declare class EventFilterCompileError extends Error {
4
+ readonly errors: string[];
5
+ constructor(message: string, errors?: string[]);
6
+ }
7
+ /**
8
+ * Compile an authored declaration into a runtime entry. Throws
9
+ * `EventFilterCompileError` on shape errors so authoring problems fail
10
+ * at module-load time.
11
+ */
12
+ export declare function compileEventFilter<T>(eventType: string, declaration: EventFilterDeclaration<T>): Promise<EventFilterRuntimeEntry>;
13
+ /**
14
+ * Compile + register in one step. The synchronous wrapper for `trigger.on()`
15
+ * that user code calls — validates the declaration, computes the
16
+ * content-derived id, registers the entry, and returns it.
17
+ *
18
+ * Returning the full {@link EventFilterRuntimeEntry} is what makes the
19
+ * authoring shape from the architecture doc work directly — modules can
20
+ * write `eventFilters: [trigger.on(...)]` without a registry round-trip:
21
+ * the entry already structurally satisfies `EventFilterDescriptor`
22
+ * (matching `id` + `eventType` fields) and carries the `manifest` payload
23
+ * `createApp()`'s wireWorkflowRuntime needs to register with the driver.
24
+ */
25
+ export declare function compileAndRegister<T>(eventType: string, declaration: EventFilterDeclaration<T>): EventFilterRuntimeEntry;
26
+ /**
27
+ * Synchronous compile — uses a deterministic but non-cryptographic hash
28
+ * over the canonical JSON. Suitable for the registry id (we just need
29
+ * stable + collision-resistant-enough across realistic registration
30
+ * counts). The crypto-grade `sha256(...)` is used at *manifest build*
31
+ * time when async is fine.
32
+ */
33
+ export declare function compileEventFilterSync<T>(eventType: string, declaration: EventFilterDeclaration<T>): EventFilterRuntimeEntry;
34
+ //# sourceMappingURL=compile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../src/events/compile.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAA;AAI3D,OAAO,EAAE,KAAK,uBAAuB,EAA0B,MAAM,eAAe,CAAA;AAEpF,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAA;gBAEb,OAAO,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,EAAO;CAKnD;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,CAAC,EACxC,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,sBAAsB,CAAC,CAAC,CAAC,GACrC,OAAO,CAAC,uBAAuB,CAAC,CAyFlC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAClC,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,sBAAsB,CAAC,CAAC,CAAC,GACrC,uBAAuB,CAOzB;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,EACtC,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,sBAAsB,CAAC,CAAC,CAAC,GACrC,uBAAuB,CAqFzB"}
@@ -0,0 +1,204 @@
1
+ // Compile an `EventFilterDeclaration` (authored in user code) into an
2
+ // `EventFilterRuntimeEntry`. Validates the predicate + mapper at
3
+ // registration time so authoring errors fail fast; computes the
4
+ // content-derived `id` / `payloadHash` so the manifest is deterministic.
5
+ //
6
+ // Called from `trigger.on(...)` in `../trigger.js`.
7
+ import { validateInputMapper } from "./input-mapper.js";
8
+ import { canonicalJson, sha256 } from "./payload-hash.js";
9
+ import { validatePredicate } from "./predicate.js";
10
+ import { getEventFilterRegistry } from "./registry.js";
11
+ export class EventFilterCompileError extends Error {
12
+ errors;
13
+ constructor(message, errors = []) {
14
+ super(message);
15
+ this.name = "EventFilterCompileError";
16
+ this.errors = errors;
17
+ }
18
+ }
19
+ /**
20
+ * Compile an authored declaration into a runtime entry. Throws
21
+ * `EventFilterCompileError` on shape errors so authoring problems fail
22
+ * at module-load time.
23
+ */
24
+ export async function compileEventFilter(eventType, declaration) {
25
+ if (typeof eventType !== "string" || eventType.length === 0) {
26
+ throw new EventFilterCompileError(`trigger.on(eventType, ...): eventType must be a non-empty string`);
27
+ }
28
+ if (typeof declaration !== "object" || declaration === null) {
29
+ throw new EventFilterCompileError(`trigger.on("${eventType}", ...): declaration must be an object`);
30
+ }
31
+ if (!declaration.target || typeof declaration.target !== "object") {
32
+ throw new EventFilterCompileError(`trigger.on("${eventType}", ...): "target" must be a workflow definition (got ${typeof declaration.target})`);
33
+ }
34
+ const targetWorkflowId = typeof declaration.target.id === "string"
35
+ ? declaration.target.id
36
+ : "";
37
+ if (targetWorkflowId.length === 0) {
38
+ throw new EventFilterCompileError(`trigger.on("${eventType}", ...): "target.id" must be a non-empty string`);
39
+ }
40
+ // Validate `where` if supplied.
41
+ const where = declaration.where;
42
+ if (where !== undefined) {
43
+ const result = validatePredicate(where);
44
+ if (!result.ok) {
45
+ throw new EventFilterCompileError(`trigger.on("${eventType}", target=${targetWorkflowId}): invalid where clause`, result.errors);
46
+ }
47
+ }
48
+ // Validate `input` if supplied.
49
+ const input = declaration.input;
50
+ if (input !== undefined) {
51
+ const result = validateInputMapper(input);
52
+ if (!result.ok) {
53
+ throw new EventFilterCompileError(`trigger.on("${eventType}", target=${targetWorkflowId}): invalid input mapper`, result.errors);
54
+ }
55
+ }
56
+ // Reject the legacy `match` callback explicitly so authoring errors are obvious.
57
+ if (typeof declaration.match === "function") {
58
+ throw new EventFilterCompileError(`trigger.on("${eventType}"): the "match" callback is no longer supported. ` +
59
+ `Use the structured "where" predicate instead. See architecture doc §12.`);
60
+ }
61
+ // Canonical content-derived id. Stable across re-deploys because the
62
+ // canonicalized JSON of the declaration is identical for byte-equivalent
63
+ // sources.
64
+ const canonicalDeclaration = {
65
+ eventType,
66
+ where: where ?? null,
67
+ input: input ?? null,
68
+ targetWorkflowId,
69
+ };
70
+ const fullHash = await sha256(canonicalDeclaration);
71
+ const payloadHash = fullHash;
72
+ // Filter id seeds from the same hash but stays human-friendly in logs.
73
+ const id = `ef_${fullHash.slice(0, 16)}`;
74
+ const manifest = {
75
+ id,
76
+ eventType,
77
+ payloadHash,
78
+ targetWorkflowId,
79
+ ...(where !== undefined ? { where } : {}),
80
+ ...(input !== undefined ? { input } : {}),
81
+ };
82
+ const entry = {
83
+ id,
84
+ eventType,
85
+ manifest,
86
+ declaration: declaration,
87
+ targetWorkflowId,
88
+ };
89
+ return entry;
90
+ }
91
+ /**
92
+ * Compile + register in one step. The synchronous wrapper for `trigger.on()`
93
+ * that user code calls — validates the declaration, computes the
94
+ * content-derived id, registers the entry, and returns it.
95
+ *
96
+ * Returning the full {@link EventFilterRuntimeEntry} is what makes the
97
+ * authoring shape from the architecture doc work directly — modules can
98
+ * write `eventFilters: [trigger.on(...)]` without a registry round-trip:
99
+ * the entry already structurally satisfies `EventFilterDescriptor`
100
+ * (matching `id` + `eventType` fields) and carries the `manifest` payload
101
+ * `createApp()`'s wireWorkflowRuntime needs to register with the driver.
102
+ */
103
+ export function compileAndRegister(eventType, declaration) {
104
+ // Run validation + compile synchronously where possible. The async
105
+ // boundary is the SHA-256 digest from Web Crypto; we bridge via a
106
+ // synchronous canonical-JSON hash so trigger.on() can stay sync.
107
+ const entry = compileEventFilterSync(eventType, declaration);
108
+ getEventFilterRegistry().add(entry);
109
+ return entry;
110
+ }
111
+ /**
112
+ * Synchronous compile — uses a deterministic but non-cryptographic hash
113
+ * over the canonical JSON. Suitable for the registry id (we just need
114
+ * stable + collision-resistant-enough across realistic registration
115
+ * counts). The crypto-grade `sha256(...)` is used at *manifest build*
116
+ * time when async is fine.
117
+ */
118
+ export function compileEventFilterSync(eventType, declaration) {
119
+ if (typeof eventType !== "string" || eventType.length === 0) {
120
+ throw new EventFilterCompileError(`trigger.on(eventType, ...): eventType must be a non-empty string`);
121
+ }
122
+ if (typeof declaration !== "object" || declaration === null) {
123
+ throw new EventFilterCompileError(`trigger.on("${eventType}", ...): declaration must be an object`);
124
+ }
125
+ if (!declaration.target || typeof declaration.target !== "object") {
126
+ throw new EventFilterCompileError(`trigger.on("${eventType}", ...): "target" must be a workflow definition (got ${typeof declaration.target})`);
127
+ }
128
+ const targetWorkflowId = typeof declaration.target.id === "string"
129
+ ? declaration.target.id
130
+ : "";
131
+ if (targetWorkflowId.length === 0) {
132
+ throw new EventFilterCompileError(`trigger.on("${eventType}", ...): "target.id" must be a non-empty string`);
133
+ }
134
+ const where = declaration.where;
135
+ if (where !== undefined) {
136
+ const result = validatePredicate(where);
137
+ if (!result.ok) {
138
+ throw new EventFilterCompileError(`trigger.on("${eventType}", target=${targetWorkflowId}): invalid where clause`, result.errors);
139
+ }
140
+ }
141
+ const input = declaration.input;
142
+ if (input !== undefined) {
143
+ const result = validateInputMapper(input);
144
+ if (!result.ok) {
145
+ throw new EventFilterCompileError(`trigger.on("${eventType}", target=${targetWorkflowId}): invalid input mapper`, result.errors);
146
+ }
147
+ }
148
+ if (typeof declaration.match === "function") {
149
+ throw new EventFilterCompileError(`trigger.on("${eventType}"): the "match" callback is no longer supported. ` +
150
+ `Use the structured "where" predicate instead. See architecture doc §12.`);
151
+ }
152
+ const canonicalDeclaration = {
153
+ eventType,
154
+ where: where ?? null,
155
+ input: input ?? null,
156
+ targetWorkflowId,
157
+ };
158
+ const json = canonicalJson(canonicalDeclaration);
159
+ const shortId = nonCryptoHash16(json);
160
+ const id = `ef_${shortId}`;
161
+ const manifest = {
162
+ id,
163
+ eventType,
164
+ // payloadHash on the manifest is stable (same canonical JSON) but
165
+ // upgraded to the crypto-grade sha256 at manifest-build time. Until
166
+ // then it carries the same short hash so consumers that read it pre-
167
+ // build (e.g. dashboards in dev mode) still see something sensible.
168
+ payloadHash: shortId,
169
+ targetWorkflowId,
170
+ ...(where !== undefined ? { where } : {}),
171
+ ...(input !== undefined ? { input } : {}),
172
+ };
173
+ return {
174
+ id,
175
+ eventType,
176
+ manifest,
177
+ declaration: declaration,
178
+ targetWorkflowId,
179
+ };
180
+ }
181
+ /**
182
+ * Deterministic 16-hex-char hash for synchronous registration. Uses FNV-1a
183
+ * 64-bit folded into hex — collision-resistant enough at the cardinality of
184
+ * "filters per project" (low thousands at most). The cryptographic SHA-256
185
+ * is the source of truth for manifest-level identity; this is just enough
186
+ * to be a stable id for the in-process registry.
187
+ */
188
+ function nonCryptoHash16(text) {
189
+ // FNV-1a 64-bit, computed as two 32-bit halves to avoid bigint overhead
190
+ // in tight loops. Output: 16 hex chars (concatenation of the two halves).
191
+ let h1 = 0xcbf29ce4;
192
+ let h2 = 0x84222325;
193
+ for (let i = 0; i < text.length; i++) {
194
+ const c = text.charCodeAt(i);
195
+ h1 = (h1 ^ c) >>> 0;
196
+ h2 = (h2 ^ c) >>> 0;
197
+ // Multiply by 0x100000001b3 split into the two halves.
198
+ const lo = h2 * 0x01b3;
199
+ const hi = h1 * 0x01b3 + Math.floor(lo / 0x100000000);
200
+ h2 = (lo & 0xffffffff) >>> 0;
201
+ h1 = hi >>> 0;
202
+ }
203
+ return h1.toString(16).padStart(8, "0") + h2.toString(16).padStart(8, "0");
204
+ }
@@ -0,0 +1,8 @@
1
+ export { compileAndRegister, compileEventFilterSync, EventFilterCompileError, } from "./compile.js";
2
+ export type { InputMapper } from "./input-mapper.js";
3
+ export { type InputMapperValidationResult, projectInput, validateInputMapper, } from "./input-mapper.js";
4
+ export { type BuildManifestArgs, buildManifest, } from "./manifest-builder.js";
5
+ export { canonicalize, canonicalJson, deriveStableEventId, sha256, shortHash, } from "./payload-hash.js";
6
+ export { evaluatePredicate, type PathOrLit, type PredicateEnvelope, type PredicateExpr, type PredicateValidationResult, resolvePath, validatePredicate, } from "./predicate.js";
7
+ export { __resetEventFilterRegistry, type EventFilterRuntimeEntry, getEventFilterRegistry, } from "./registry.js";
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/events/index.ts"],"names":[],"mappings":"AAMA,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,cAAc,CAAA;AACrB,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACpD,OAAO,EACL,KAAK,2BAA2B,EAChC,YAAY,EACZ,mBAAmB,GACpB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EACL,KAAK,iBAAiB,EACtB,aAAa,GACd,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EACL,YAAY,EACZ,aAAa,EACb,mBAAmB,EACnB,MAAM,EACN,SAAS,GACV,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EACL,iBAAiB,EACjB,KAAK,SAAS,EACd,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,yBAAyB,EAC9B,WAAW,EACX,iBAAiB,GAClB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EACL,0BAA0B,EAC1B,KAAK,uBAAuB,EAC5B,sBAAsB,GACvB,MAAM,eAAe,CAAA"}
@@ -0,0 +1,11 @@
1
+ // `@voyantjs/workflows/events` — the events runtime: structured `where`
2
+ // predicate + `input` mapper, event-filter registry, manifest builder.
3
+ //
4
+ // Authoritative architecture: docs/architecture/workflows-runtime-architecture.md
5
+ // §12 (trigger.on runtime), §13 (DSLs), §14 (manifest lifecycle).
6
+ export { compileAndRegister, compileEventFilterSync, EventFilterCompileError, } from "./compile.js";
7
+ export { projectInput, validateInputMapper, } from "./input-mapper.js";
8
+ export { buildManifest, } from "./manifest-builder.js";
9
+ export { canonicalize, canonicalJson, deriveStableEventId, sha256, shortHash, } from "./payload-hash.js";
10
+ export { evaluatePredicate, resolvePath, validatePredicate, } from "./predicate.js";
11
+ export { __resetEventFilterRegistry, getEventFilterRegistry, } from "./registry.js";
@@ -0,0 +1,24 @@
1
+ import { type PathOrLit, type PredicateEnvelope } from "./predicate.js";
2
+ export type InputMapper = undefined | {
3
+ passthrough: true;
4
+ } | {
5
+ path: string;
6
+ } | {
7
+ object: Record<string, InputMapper | PathOrLit>;
8
+ };
9
+ /**
10
+ * Project a workflow input from an event envelope. Mirrors the predicate
11
+ * evaluator's no-throw contract — missing paths produce `undefined` in the
12
+ * output, registration-time linting catches structural errors.
13
+ *
14
+ * Throws `InputMapperError` only on unexpected shape errors (the mapper
15
+ * itself was constructed wrong). Drivers catch and surface this as
16
+ * `IngestMatch.status === "skipped"` with reason `"input_projection_error"`.
17
+ */
18
+ export declare function projectInput(mapper: InputMapper, envelope: PredicateEnvelope): unknown;
19
+ export interface InputMapperValidationResult {
20
+ ok: boolean;
21
+ errors: string[];
22
+ }
23
+ export declare function validateInputMapper(mapper: InputMapper): InputMapperValidationResult;
24
+ //# sourceMappingURL=input-mapper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input-mapper.d.ts","sourceRoot":"","sources":["../../src/events/input-mapper.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,iBAAiB,EAAe,MAAM,gBAAgB,CAAA;AAIpF,MAAM,MAAM,WAAW,GACnB,SAAS,GACT;IAAE,WAAW,EAAE,IAAI,CAAA;CAAE,GACrB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,CAAC,CAAA;CAAE,CAAA;AAIvD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAgCtF;AAID,MAAM,WAAW,2BAA2B;IAC1C,EAAE,EAAE,OAAO,CAAA;IACX,MAAM,EAAE,MAAM,EAAE,CAAA;CACjB;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,WAAW,GAAG,2BAA2B,CAIpF"}