@voyant-travel/workflows 0.107.10

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 (120) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +52 -0
  3. package/README.md +79 -0
  4. package/dist/auth/index.d.ts +125 -0
  5. package/dist/auth/index.d.ts.map +1 -0
  6. package/dist/auth/index.js +352 -0
  7. package/dist/bindings.d.ts +119 -0
  8. package/dist/bindings.d.ts.map +1 -0
  9. package/dist/bindings.js +19 -0
  10. package/dist/client.d.ts +135 -0
  11. package/dist/client.d.ts.map +1 -0
  12. package/dist/client.js +305 -0
  13. package/dist/conditions.d.ts +29 -0
  14. package/dist/conditions.d.ts.map +1 -0
  15. package/dist/conditions.js +5 -0
  16. package/dist/config.d.ts +93 -0
  17. package/dist/config.d.ts.map +1 -0
  18. package/dist/config.js +7 -0
  19. package/dist/driver.d.ts +237 -0
  20. package/dist/driver.d.ts.map +1 -0
  21. package/dist/driver.js +53 -0
  22. package/dist/errors.d.ts +58 -0
  23. package/dist/errors.d.ts.map +1 -0
  24. package/dist/errors.js +76 -0
  25. package/dist/events/compile.d.ts +34 -0
  26. package/dist/events/compile.d.ts.map +1 -0
  27. package/dist/events/compile.js +204 -0
  28. package/dist/events/index.d.ts +8 -0
  29. package/dist/events/index.d.ts.map +1 -0
  30. package/dist/events/index.js +11 -0
  31. package/dist/events/input-mapper.d.ts +24 -0
  32. package/dist/events/input-mapper.d.ts.map +1 -0
  33. package/dist/events/input-mapper.js +169 -0
  34. package/dist/events/manifest-builder.d.ts +42 -0
  35. package/dist/events/manifest-builder.d.ts.map +1 -0
  36. package/dist/events/manifest-builder.js +313 -0
  37. package/dist/events/payload-hash.d.ts +46 -0
  38. package/dist/events/payload-hash.d.ts.map +1 -0
  39. package/dist/events/payload-hash.js +98 -0
  40. package/dist/events/predicate.d.ts +77 -0
  41. package/dist/events/predicate.d.ts.map +1 -0
  42. package/dist/events/predicate.js +347 -0
  43. package/dist/events/registry.d.ts +37 -0
  44. package/dist/events/registry.d.ts.map +1 -0
  45. package/dist/events/registry.js +47 -0
  46. package/dist/handler/index.d.ts +114 -0
  47. package/dist/handler/index.d.ts.map +1 -0
  48. package/dist/handler/index.js +267 -0
  49. package/dist/handler/resume.d.ts +41 -0
  50. package/dist/handler/resume.d.ts.map +1 -0
  51. package/dist/handler/resume.js +44 -0
  52. package/dist/http-ingest.d.ts +54 -0
  53. package/dist/http-ingest.d.ts.map +1 -0
  54. package/dist/http-ingest.js +214 -0
  55. package/dist/index.d.ts +6 -0
  56. package/dist/index.d.ts.map +1 -0
  57. package/dist/index.js +10 -0
  58. package/dist/protocol/index.d.ts +345 -0
  59. package/dist/protocol/index.d.ts.map +1 -0
  60. package/dist/protocol/index.js +110 -0
  61. package/dist/rate-limit/index.d.ts +40 -0
  62. package/dist/rate-limit/index.d.ts.map +1 -0
  63. package/dist/rate-limit/index.js +139 -0
  64. package/dist/runtime/ctx.d.ts +111 -0
  65. package/dist/runtime/ctx.d.ts.map +1 -0
  66. package/dist/runtime/ctx.js +624 -0
  67. package/dist/runtime/determinism.d.ts +19 -0
  68. package/dist/runtime/determinism.d.ts.map +1 -0
  69. package/dist/runtime/determinism.js +61 -0
  70. package/dist/runtime/errors.d.ts +21 -0
  71. package/dist/runtime/errors.d.ts.map +1 -0
  72. package/dist/runtime/errors.js +45 -0
  73. package/dist/runtime/executor.d.ts +166 -0
  74. package/dist/runtime/executor.d.ts.map +1 -0
  75. package/dist/runtime/executor.js +226 -0
  76. package/dist/runtime/journal.d.ts +56 -0
  77. package/dist/runtime/journal.d.ts.map +1 -0
  78. package/dist/runtime/journal.js +28 -0
  79. package/dist/testing/index.d.ts +117 -0
  80. package/dist/testing/index.d.ts.map +1 -0
  81. package/dist/testing/index.js +599 -0
  82. package/dist/trigger.d.ts +37 -0
  83. package/dist/trigger.d.ts.map +1 -0
  84. package/dist/trigger.js +11 -0
  85. package/dist/types.d.ts +63 -0
  86. package/dist/types.d.ts.map +1 -0
  87. package/dist/types.js +3 -0
  88. package/dist/workflow.d.ts +222 -0
  89. package/dist/workflow.d.ts.map +1 -0
  90. package/dist/workflow.js +55 -0
  91. package/package.json +120 -0
  92. package/src/auth/index.ts +398 -0
  93. package/src/bindings.ts +135 -0
  94. package/src/client.ts +498 -0
  95. package/src/conditions.ts +43 -0
  96. package/src/config.ts +114 -0
  97. package/src/driver.ts +277 -0
  98. package/src/errors.ts +109 -0
  99. package/src/events/compile.ts +268 -0
  100. package/src/events/index.ts +42 -0
  101. package/src/events/input-mapper.ts +201 -0
  102. package/src/events/manifest-builder.ts +372 -0
  103. package/src/events/payload-hash.ts +110 -0
  104. package/src/events/predicate.ts +390 -0
  105. package/src/events/registry.ts +86 -0
  106. package/src/handler/index.ts +413 -0
  107. package/src/handler/resume.ts +100 -0
  108. package/src/http-ingest.ts +299 -0
  109. package/src/index.ts +18 -0
  110. package/src/protocol/index.ts +483 -0
  111. package/src/rate-limit/index.ts +181 -0
  112. package/src/runtime/ctx.ts +876 -0
  113. package/src/runtime/determinism.ts +75 -0
  114. package/src/runtime/errors.ts +58 -0
  115. package/src/runtime/executor.ts +442 -0
  116. package/src/runtime/journal.ts +80 -0
  117. package/src/testing/index.ts +796 -0
  118. package/src/trigger.ts +63 -0
  119. package/src/types.ts +80 -0
  120. package/src/workflow.ts +328 -0
@@ -0,0 +1,299 @@
1
+ // Optional HTTP ingest adapter — mounts `/api/manifests` and `/api/events`
2
+ // on a Hono-shaped app, forwarding into a `WorkflowDriver`.
3
+ //
4
+ // Self-host Mode 2 deployments mount this when external emitters need to
5
+ // fire events into the runtime (storefront BFF, third-party webhooks,
6
+ // sibling-process pairs across machines). voyant-cloud always mounts it
7
+ // at its HTTP boundary.
8
+ //
9
+ // Transport-agnostic: takes a minimal `HttpAppLike` interface so the SDK
10
+ // stays a leaf package (no `hono` dep). `@voyant-travel/voyant-hono`'s `Hono`
11
+ // instance satisfies the shape via TypeScript structural compat.
12
+ //
13
+ // Architecture: docs/architecture/workflows-runtime-architecture.md §15.4.
14
+
15
+ import type { IngestEventArgs, WorkflowDriver } from "./driver.js"
16
+ import type { EnvironmentName } from "./types.js"
17
+
18
+ const ALLOWED_ENVS = new Set<EnvironmentName>(["production", "preview", "development"])
19
+
20
+ // ---- Public types ----
21
+
22
+ /**
23
+ * Minimum interface a Hono-shaped app exposes that we use. `app.post(...)`
24
+ * and `app.get(...)` register handlers; the handler signature mirrors
25
+ * Hono's `Context`-style callback for portability — we only read the
26
+ * request body and request params via the framework's response helpers.
27
+ */
28
+ export interface HttpAppLike {
29
+ post(path: string, handler: HttpHandler): unknown
30
+ get(path: string, handler: HttpHandler): unknown
31
+ }
32
+
33
+ /**
34
+ * Minimum context shape we read off Hono. Restricted to body parsing,
35
+ * route params, and JSON response helpers.
36
+ */
37
+ export interface HttpContextLike {
38
+ req: {
39
+ json(): Promise<unknown>
40
+ param(name: string): string | undefined
41
+ header(name: string): string | undefined
42
+ raw: Request
43
+ }
44
+ json(body: unknown, status?: number): Response
45
+ text(body: string, status?: number): Response
46
+ status(code: number): unknown
47
+ }
48
+
49
+ export type HttpHandler = (ctx: HttpContextLike) => Promise<Response> | Response
50
+
51
+ export interface MountHttpIngestAdapterOptions {
52
+ /**
53
+ * Driver the adapter forwards into. Typically the same instance
54
+ * `createApp({ workflows: { driver } })` constructed.
55
+ */
56
+ driver: WorkflowDriver
57
+ /** Mount path. Defaults to `"/api/workflows"`. */
58
+ basePath?: string
59
+ /**
60
+ * Optional auth check. Receives the original `Request` and returns
61
+ * `void` on success / throws on failure. Reuse
62
+ * `createBearerVerifier(...)` from `@voyant-travel/workflows/auth` for the
63
+ * canonical bearer-token shape.
64
+ */
65
+ verifyRequest?: (req: Request) => void | Promise<void>
66
+ }
67
+
68
+ // ---- Mount ----
69
+
70
+ /**
71
+ * Mount the adapter onto a Hono-shaped app. Registers:
72
+ *
73
+ * POST {basePath}/events → driver.ingestEvent
74
+ * POST {basePath}/manifests → driver.registerManifest
75
+ * GET {basePath}/manifests/:env → driver.getManifest
76
+ *
77
+ * Returns the mounted base path so callers can log it.
78
+ */
79
+ export function mountHttpIngestAdapter(
80
+ app: HttpAppLike,
81
+ opts: MountHttpIngestAdapterOptions,
82
+ ): string {
83
+ const base = (opts.basePath ?? "/api/workflows").replace(/\/$/, "")
84
+
85
+ app.post(`${base}/events`, async (ctx) => {
86
+ if (opts.verifyRequest) {
87
+ try {
88
+ await opts.verifyRequest(ctx.req.raw)
89
+ } catch (err) {
90
+ return ctx.json({ error: "unauthorized", message: errMessage(err) }, 401)
91
+ }
92
+ }
93
+
94
+ let raw: unknown
95
+ try {
96
+ raw = await ctx.req.json()
97
+ } catch (err) {
98
+ return ctx.json({ error: "invalid_json", message: errMessage(err) }, 400)
99
+ }
100
+ const validation = validateIngestBody(raw)
101
+ if (!validation.ok) return ctx.json(validation.error, 400)
102
+
103
+ const args: IngestEventArgs = {
104
+ environment: validation.body.environment,
105
+ envelope: validation.body.envelope,
106
+ idempotencyKey: validation.body.idempotencyKey,
107
+ }
108
+ const result = await opts.driver.ingestEvent(args)
109
+ if (!result.ok && result.reason === "manifest_not_registered") {
110
+ return ctx.json(result, 200)
111
+ }
112
+ if (!result.ok) {
113
+ return ctx.json(result, 502)
114
+ }
115
+ return ctx.json(result, 200)
116
+ })
117
+
118
+ app.post(`${base}/manifests`, async (ctx) => {
119
+ if (opts.verifyRequest) {
120
+ try {
121
+ await opts.verifyRequest(ctx.req.raw)
122
+ } catch (err) {
123
+ return ctx.json({ error: "unauthorized", message: errMessage(err) }, 401)
124
+ }
125
+ }
126
+ let raw: unknown
127
+ try {
128
+ raw = await ctx.req.json()
129
+ } catch (err) {
130
+ return ctx.json({ error: "invalid_json", message: errMessage(err) }, 400)
131
+ }
132
+ const validation = validateRegisterBody(raw)
133
+ if (!validation.ok) return ctx.json(validation.error, 400)
134
+ try {
135
+ const result = await opts.driver.registerManifest({
136
+ environment: validation.body.environment,
137
+ manifest: validation.body.manifest as never, // structurally compatible
138
+ })
139
+ return ctx.json({ ok: true, versionId: result.versionId }, 200)
140
+ } catch (err) {
141
+ return ctx.json({ error: "register_failed", message: errMessage(err) }, 500)
142
+ }
143
+ })
144
+
145
+ app.get(`${base}/manifests/:env`, async (ctx) => {
146
+ if (opts.verifyRequest) {
147
+ try {
148
+ await opts.verifyRequest(ctx.req.raw)
149
+ } catch (err) {
150
+ return ctx.json({ error: "unauthorized", message: errMessage(err) }, 401)
151
+ }
152
+ }
153
+ const env = ctx.req.param("env")
154
+ if (!env || !ALLOWED_ENVS.has(env as EnvironmentName)) {
155
+ return ctx.json(
156
+ {
157
+ error: "invalid_environment",
158
+ message: `environment must be one of ${[...ALLOWED_ENVS].join(", ")}`,
159
+ },
160
+ 400,
161
+ )
162
+ }
163
+ const manifest = await opts.driver.getManifest({ environment: env as EnvironmentName })
164
+ if (!manifest) {
165
+ return ctx.json({ error: "not_found", environment: env }, 404)
166
+ }
167
+ return ctx.json({ environment: env, versionId: manifest.versionId, manifest }, 200)
168
+ })
169
+
170
+ return base
171
+ }
172
+
173
+ // ---- Validation ----
174
+
175
+ interface IngestBody {
176
+ environment: EnvironmentName
177
+ envelope: {
178
+ name: string
179
+ data: unknown
180
+ metadata?: Record<string, unknown> & { eventId?: string }
181
+ emittedAt: string
182
+ }
183
+ idempotencyKey?: string
184
+ }
185
+
186
+ function validateIngestBody(
187
+ raw: unknown,
188
+ ): { ok: true; body: IngestBody } | { ok: false; error: { error: string; message: string } } {
189
+ if (typeof raw !== "object" || raw === null) {
190
+ return { ok: false, error: { error: "invalid_body", message: "expected JSON object" } }
191
+ }
192
+ const r = raw as Record<string, unknown>
193
+ if (typeof r.environment !== "string" || !ALLOWED_ENVS.has(r.environment as EnvironmentName)) {
194
+ return {
195
+ ok: false,
196
+ error: {
197
+ error: "invalid_body",
198
+ message: `"environment" must be one of ${[...ALLOWED_ENVS].join(", ")}`,
199
+ },
200
+ }
201
+ }
202
+ if (typeof r.envelope !== "object" || r.envelope === null) {
203
+ return { ok: false, error: { error: "invalid_body", message: '"envelope" must be an object' } }
204
+ }
205
+ const envelope = r.envelope as Record<string, unknown>
206
+ if (typeof envelope.name !== "string" || envelope.name.length === 0) {
207
+ return {
208
+ ok: false,
209
+ error: { error: "invalid_body", message: '"envelope.name" must be a non-empty string' },
210
+ }
211
+ }
212
+ if (typeof envelope.emittedAt !== "string" || envelope.emittedAt.length === 0) {
213
+ return {
214
+ ok: false,
215
+ error: {
216
+ error: "invalid_body",
217
+ message: '"envelope.emittedAt" must be an ISO timestamp string',
218
+ },
219
+ }
220
+ }
221
+ if (
222
+ envelope.metadata !== undefined &&
223
+ (typeof envelope.metadata !== "object" || envelope.metadata === null)
224
+ ) {
225
+ return {
226
+ ok: false,
227
+ error: {
228
+ error: "invalid_body",
229
+ message: '"envelope.metadata" must be an object when supplied',
230
+ },
231
+ }
232
+ }
233
+ if (r.idempotencyKey !== undefined && typeof r.idempotencyKey !== "string") {
234
+ return {
235
+ ok: false,
236
+ error: { error: "invalid_body", message: '"idempotencyKey" must be a string when supplied' },
237
+ }
238
+ }
239
+ return {
240
+ ok: true,
241
+ body: {
242
+ environment: r.environment as EnvironmentName,
243
+ envelope: {
244
+ name: envelope.name,
245
+ data: envelope.data,
246
+ metadata: envelope.metadata as Record<string, unknown> | undefined,
247
+ emittedAt: envelope.emittedAt,
248
+ },
249
+ idempotencyKey: r.idempotencyKey as string | undefined,
250
+ },
251
+ }
252
+ }
253
+
254
+ interface RegisterBody {
255
+ environment: EnvironmentName
256
+ manifest: Record<string, unknown> & { versionId: string }
257
+ }
258
+
259
+ function validateRegisterBody(
260
+ raw: unknown,
261
+ ): { ok: true; body: RegisterBody } | { ok: false; error: { error: string; message: string } } {
262
+ if (typeof raw !== "object" || raw === null) {
263
+ return { ok: false, error: { error: "invalid_body", message: "expected JSON object" } }
264
+ }
265
+ const r = raw as Record<string, unknown>
266
+ if (typeof r.environment !== "string" || !ALLOWED_ENVS.has(r.environment as EnvironmentName)) {
267
+ return {
268
+ ok: false,
269
+ error: {
270
+ error: "invalid_body",
271
+ message: `"environment" must be one of ${[...ALLOWED_ENVS].join(", ")}`,
272
+ },
273
+ }
274
+ }
275
+ if (typeof r.manifest !== "object" || r.manifest === null) {
276
+ return { ok: false, error: { error: "invalid_body", message: '"manifest" must be an object' } }
277
+ }
278
+ const manifest = r.manifest as Record<string, unknown>
279
+ if (typeof manifest.versionId !== "string" || manifest.versionId.length === 0) {
280
+ return {
281
+ ok: false,
282
+ error: {
283
+ error: "invalid_body",
284
+ message: '"manifest.versionId" must be a non-empty string',
285
+ },
286
+ }
287
+ }
288
+ return {
289
+ ok: true,
290
+ body: {
291
+ environment: r.environment as EnvironmentName,
292
+ manifest: manifest as Record<string, unknown> & { versionId: string },
293
+ },
294
+ }
295
+ }
296
+
297
+ function errMessage(err: unknown): string {
298
+ return err instanceof Error ? err.message : String(err)
299
+ }
package/src/index.ts ADDED
@@ -0,0 +1,18 @@
1
+ // @voyant-travel/workflows
2
+ //
3
+ // Authoring SDK for Voyant Workflows. Full contract in:
4
+ // docs/sdk-surface.md §2–§8
5
+ // docs/design.md §3–§4
6
+
7
+ export * from "./conditions.js"
8
+ export {
9
+ FatalError,
10
+ HookConflictError,
11
+ QuotaExceededError,
12
+ RetryableError,
13
+ TimeoutError,
14
+ ValidationError,
15
+ } from "./errors.js"
16
+ export * from "./trigger.js"
17
+ export * from "./types.js"
18
+ export * from "./workflow.js"