@funkai/agents 0.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.
Files changed (153) hide show
  1. package/.generated/req.txt +1 -0
  2. package/.turbo/turbo-build.log +21 -0
  3. package/.turbo/turbo-test$colon$coverage.log +109 -0
  4. package/.turbo/turbo-test.log +141 -0
  5. package/.turbo/turbo-typecheck.log +4 -0
  6. package/CHANGELOG.md +16 -0
  7. package/ISSUES.md +540 -0
  8. package/LICENSE +21 -0
  9. package/README.md +128 -0
  10. package/banner.svg +97 -0
  11. package/coverage/lcov-report/base.css +224 -0
  12. package/coverage/lcov-report/block-navigation.js +87 -0
  13. package/coverage/lcov-report/core/agents/base/agent.ts.html +1705 -0
  14. package/coverage/lcov-report/core/agents/base/index.html +146 -0
  15. package/coverage/lcov-report/core/agents/base/output.ts.html +256 -0
  16. package/coverage/lcov-report/core/agents/base/utils.ts.html +694 -0
  17. package/coverage/lcov-report/core/agents/flow/engine.ts.html +928 -0
  18. package/coverage/lcov-report/core/agents/flow/flow-agent.ts.html +1462 -0
  19. package/coverage/lcov-report/core/agents/flow/index.html +146 -0
  20. package/coverage/lcov-report/core/agents/flow/messages.ts.html +508 -0
  21. package/coverage/lcov-report/core/agents/flow/steps/factory.ts.html +1975 -0
  22. package/coverage/lcov-report/core/agents/flow/steps/index.html +116 -0
  23. package/coverage/lcov-report/core/index.html +131 -0
  24. package/coverage/lcov-report/core/logger.ts.html +541 -0
  25. package/coverage/lcov-report/core/models/providers/index.html +116 -0
  26. package/coverage/lcov-report/core/models/providers/openai.ts.html +337 -0
  27. package/coverage/lcov-report/core/provider/index.html +131 -0
  28. package/coverage/lcov-report/core/provider/provider.ts.html +346 -0
  29. package/coverage/lcov-report/core/provider/usage.ts.html +376 -0
  30. package/coverage/lcov-report/core/tool.ts.html +577 -0
  31. package/coverage/lcov-report/favicon.png +0 -0
  32. package/coverage/lcov-report/index.html +221 -0
  33. package/coverage/lcov-report/lib/hooks.ts.html +262 -0
  34. package/coverage/lcov-report/lib/index.html +161 -0
  35. package/coverage/lcov-report/lib/middleware.ts.html +274 -0
  36. package/coverage/lcov-report/lib/runnable.ts.html +151 -0
  37. package/coverage/lcov-report/lib/trace.ts.html +520 -0
  38. package/coverage/lcov-report/prettify.css +1 -0
  39. package/coverage/lcov-report/prettify.js +2 -0
  40. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  41. package/coverage/lcov-report/sorter.js +210 -0
  42. package/coverage/lcov-report/utils/attempt.ts.html +199 -0
  43. package/coverage/lcov-report/utils/error.ts.html +421 -0
  44. package/coverage/lcov-report/utils/index.html +176 -0
  45. package/coverage/lcov-report/utils/resolve.ts.html +208 -0
  46. package/coverage/lcov-report/utils/result.ts.html +538 -0
  47. package/coverage/lcov-report/utils/zod.ts.html +178 -0
  48. package/coverage/lcov.info +1566 -0
  49. package/dist/index.d.mts +2883 -0
  50. package/dist/index.d.mts.map +1 -0
  51. package/dist/index.mjs +2312 -0
  52. package/dist/index.mjs.map +1 -0
  53. package/docs/core/agent.md +231 -0
  54. package/docs/core/hooks.md +95 -0
  55. package/docs/core/overview.md +87 -0
  56. package/docs/core/step.md +279 -0
  57. package/docs/core/tools.md +98 -0
  58. package/docs/core/workflow.md +235 -0
  59. package/docs/guides/create-agent.md +224 -0
  60. package/docs/guides/create-tool.md +137 -0
  61. package/docs/guides/create-workflow.md +374 -0
  62. package/docs/overview.md +244 -0
  63. package/docs/provider/models.md +55 -0
  64. package/docs/provider/overview.md +106 -0
  65. package/docs/provider/usage.md +100 -0
  66. package/docs/research/experimental-context.md +167 -0
  67. package/docs/research/gap-analysis.md +86 -0
  68. package/docs/research/prepare-step-and-active-tools.md +138 -0
  69. package/docs/research/sub-agent-model.md +249 -0
  70. package/docs/troubleshooting.md +60 -0
  71. package/logo.svg +17 -0
  72. package/models.config.json +18 -0
  73. package/package.json +60 -0
  74. package/scripts/generate-models.ts +324 -0
  75. package/src/core/agents/base/agent.test.ts +1522 -0
  76. package/src/core/agents/base/agent.ts +547 -0
  77. package/src/core/agents/base/output.test.ts +93 -0
  78. package/src/core/agents/base/output.ts +57 -0
  79. package/src/core/agents/base/types.test-d.ts +69 -0
  80. package/src/core/agents/base/types.ts +503 -0
  81. package/src/core/agents/base/utils.test.ts +397 -0
  82. package/src/core/agents/base/utils.ts +197 -0
  83. package/src/core/agents/flow/engine.test.ts +452 -0
  84. package/src/core/agents/flow/engine.ts +281 -0
  85. package/src/core/agents/flow/flow-agent.test.ts +1027 -0
  86. package/src/core/agents/flow/flow-agent.ts +473 -0
  87. package/src/core/agents/flow/messages.test.ts +198 -0
  88. package/src/core/agents/flow/messages.ts +141 -0
  89. package/src/core/agents/flow/steps/agent.test.ts +280 -0
  90. package/src/core/agents/flow/steps/agent.ts +87 -0
  91. package/src/core/agents/flow/steps/all.test.ts +300 -0
  92. package/src/core/agents/flow/steps/all.ts +73 -0
  93. package/src/core/agents/flow/steps/builder.ts +124 -0
  94. package/src/core/agents/flow/steps/each.test.ts +257 -0
  95. package/src/core/agents/flow/steps/each.ts +61 -0
  96. package/src/core/agents/flow/steps/factory.test-d.ts +50 -0
  97. package/src/core/agents/flow/steps/factory.test.ts +1025 -0
  98. package/src/core/agents/flow/steps/factory.ts +645 -0
  99. package/src/core/agents/flow/steps/map.test.ts +273 -0
  100. package/src/core/agents/flow/steps/map.ts +75 -0
  101. package/src/core/agents/flow/steps/race.test.ts +290 -0
  102. package/src/core/agents/flow/steps/race.ts +59 -0
  103. package/src/core/agents/flow/steps/reduce.test.ts +310 -0
  104. package/src/core/agents/flow/steps/reduce.ts +73 -0
  105. package/src/core/agents/flow/steps/result.ts +27 -0
  106. package/src/core/agents/flow/steps/step.test.ts +402 -0
  107. package/src/core/agents/flow/steps/step.ts +51 -0
  108. package/src/core/agents/flow/steps/while.test.ts +283 -0
  109. package/src/core/agents/flow/steps/while.ts +75 -0
  110. package/src/core/agents/flow/types.ts +348 -0
  111. package/src/core/logger.test.ts +163 -0
  112. package/src/core/logger.ts +152 -0
  113. package/src/core/models/index.test.ts +137 -0
  114. package/src/core/models/index.ts +152 -0
  115. package/src/core/models/providers/openai.ts +84 -0
  116. package/src/core/provider/provider.test.ts +128 -0
  117. package/src/core/provider/provider.ts +99 -0
  118. package/src/core/provider/types.ts +98 -0
  119. package/src/core/provider/usage.test.ts +304 -0
  120. package/src/core/provider/usage.ts +97 -0
  121. package/src/core/tool.test.ts +65 -0
  122. package/src/core/tool.ts +164 -0
  123. package/src/core/types.ts +66 -0
  124. package/src/index.ts +95 -0
  125. package/src/lib/context.test.ts +86 -0
  126. package/src/lib/context.ts +49 -0
  127. package/src/lib/hooks.test.ts +102 -0
  128. package/src/lib/hooks.ts +59 -0
  129. package/src/lib/middleware.test.ts +122 -0
  130. package/src/lib/middleware.ts +63 -0
  131. package/src/lib/runnable.test.ts +41 -0
  132. package/src/lib/runnable.ts +22 -0
  133. package/src/lib/trace.test.ts +291 -0
  134. package/src/lib/trace.ts +145 -0
  135. package/src/models/index.ts +123 -0
  136. package/src/models/providers/index.ts +15 -0
  137. package/src/models/providers/openai.ts +84 -0
  138. package/src/testing/context.ts +32 -0
  139. package/src/testing/index.ts +2 -0
  140. package/src/testing/logger.ts +19 -0
  141. package/src/utils/attempt.test.ts +127 -0
  142. package/src/utils/attempt.ts +38 -0
  143. package/src/utils/error.test.ts +179 -0
  144. package/src/utils/error.ts +112 -0
  145. package/src/utils/resolve.test.ts +38 -0
  146. package/src/utils/resolve.ts +41 -0
  147. package/src/utils/result.test.ts +79 -0
  148. package/src/utils/result.ts +151 -0
  149. package/src/utils/zod.test.ts +69 -0
  150. package/src/utils/zod.ts +31 -0
  151. package/tsconfig.json +25 -0
  152. package/tsdown.config.ts +15 -0
  153. package/vitest.config.ts +46 -0
@@ -0,0 +1,452 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { z } from "zod";
3
+
4
+ import { createFlowEngine } from "@/core/agents/flow/engine.js";
5
+ import { createMockLogger } from "@/testing/index.js";
6
+
7
+ // ---------------------------------------------------------------------------
8
+ // Helpers
9
+ // ---------------------------------------------------------------------------
10
+
11
+ const Input = z.object({ x: z.number() });
12
+ const Output = z.object({ y: z.number() });
13
+
14
+ function defaultConfig(overrides?: Record<string, unknown>) {
15
+ return {
16
+ name: "test-flow",
17
+ input: Input,
18
+ output: Output,
19
+ logger: createMockLogger(),
20
+ ...overrides,
21
+ };
22
+ }
23
+
24
+ // ---------------------------------------------------------------------------
25
+ // Engine with no custom steps (plain flow agent)
26
+ // ---------------------------------------------------------------------------
27
+
28
+ describe("createFlowEngine", () => {
29
+ it("works as a plain flow agent when no custom steps are defined", async () => {
30
+ const engine = createFlowEngine({});
31
+
32
+ const fa = engine(defaultConfig(), async ({ input }) => ({ y: input.x * 2 }));
33
+
34
+ const result = await fa.generate({ x: 5 });
35
+
36
+ expect(result.ok).toBe(true);
37
+ if (!result.ok) return;
38
+ expect(result.output).toEqual({ y: 10 });
39
+ expect(result.duration).toBeGreaterThanOrEqual(0);
40
+ expect(result.trace).toBeDefined();
41
+ });
42
+
43
+ // ---------------------------------------------------------------------------
44
+ // Custom steps on $
45
+ // ---------------------------------------------------------------------------
46
+
47
+ describe("custom steps", () => {
48
+ it("custom steps defined in $ are available on $ in the handler", async () => {
49
+ const engine = createFlowEngine({
50
+ $: {
51
+ double: async ({ config }: { config: { value: number } }) => config.value * 2,
52
+ },
53
+ });
54
+
55
+ const fa = engine(defaultConfig(), async ({ input, $ }) => {
56
+ const doubled = await $.double({ value: input.x });
57
+ return { y: doubled };
58
+ });
59
+
60
+ const result = await fa.generate({ x: 7 });
61
+
62
+ expect(result.ok).toBe(true);
63
+ if (!result.ok) return;
64
+ expect(result.output).toEqual({ y: 14 });
65
+ });
66
+
67
+ it("custom step factories receive correct ctx and config", async () => {
68
+ const factorySpy = vi.fn(
69
+ async ({
70
+ ctx,
71
+ config,
72
+ }: {
73
+ ctx: { signal: AbortSignal; log: unknown };
74
+ config: { value: number };
75
+ }) => {
76
+ expect(ctx.signal).toBeDefined();
77
+ expect(ctx.log).toBeDefined();
78
+ return config.value + 1;
79
+ },
80
+ );
81
+
82
+ const engine = createFlowEngine({
83
+ $: {
84
+ increment: factorySpy,
85
+ },
86
+ });
87
+
88
+ const fa = engine(defaultConfig(), async ({ input, $ }) => {
89
+ const result = await $.increment({ value: input.x });
90
+ return { y: result };
91
+ });
92
+
93
+ await fa.generate({ x: 10 });
94
+
95
+ expect(factorySpy).toHaveBeenCalledTimes(1);
96
+ const firstCall = factorySpy.mock.calls[0];
97
+ if (!firstCall) return;
98
+ const call = firstCall[0];
99
+ if (!call) return;
100
+ expect(call.config).toEqual({ value: 10 });
101
+ expect(call.ctx).toHaveProperty("signal");
102
+ expect(call.ctx).toHaveProperty("log");
103
+ expect(call.ctx).not.toHaveProperty("trace");
104
+ });
105
+
106
+ it("multiple custom steps work independently", async () => {
107
+ const engine = createFlowEngine({
108
+ $: {
109
+ add: async ({ config }: { config: { a: number; b: number } }) => config.a + config.b,
110
+ multiply: async ({ config }: { config: { a: number; b: number } }) => config.a * config.b,
111
+ },
112
+ });
113
+
114
+ const SumProduct = z.object({ sum: z.number(), product: z.number() });
115
+ const fa = engine(
116
+ { name: "test-flow", input: Input, output: SumProduct, logger: createMockLogger() },
117
+ async ({ input, $ }) => {
118
+ const sum = await $.add({ a: input.x, b: 3 });
119
+ const product = await $.multiply({ a: input.x, b: 3 });
120
+ return { sum, product };
121
+ },
122
+ );
123
+
124
+ const result = await fa.generate({ x: 4 });
125
+
126
+ expect(result.ok).toBe(true);
127
+ if (!result.ok) return;
128
+ expect(result.output).toEqual({ sum: 7, product: 12 });
129
+ });
130
+
131
+ it("built-in $ steps remain available alongside custom steps", async () => {
132
+ const engine = createFlowEngine({
133
+ $: {
134
+ noop: async (_params: { config: undefined }) => "noop",
135
+ },
136
+ });
137
+
138
+ const fa = engine(defaultConfig(), async ({ input, $ }) => {
139
+ const stepResult = await $.step({
140
+ id: "built-in",
141
+ execute: async () => ({ v: input.x }),
142
+ });
143
+
144
+ await $.noop(undefined);
145
+
146
+ if (!stepResult.ok) {
147
+ return { y: 0 };
148
+ }
149
+ return { y: stepResult.value.v };
150
+ });
151
+
152
+ const result = await fa.generate({ x: 42 });
153
+
154
+ expect(result.ok).toBe(true);
155
+ if (!result.ok) return;
156
+ expect(result.output).toEqual({ y: 42 });
157
+ });
158
+ });
159
+
160
+ // ---------------------------------------------------------------------------
161
+ // Hook merging
162
+ // ---------------------------------------------------------------------------
163
+
164
+ describe("hook merging", () => {
165
+ it("engine hooks fire before flow agent hooks (engine onStart -> flow onStart)", async () => {
166
+ const order: string[] = [];
167
+
168
+ const engine = createFlowEngine({
169
+ onStart: () => {
170
+ order.push("engine:onStart");
171
+ },
172
+ onFinish: () => {
173
+ order.push("engine:onFinish");
174
+ },
175
+ });
176
+
177
+ const fa = engine(
178
+ defaultConfig({
179
+ onStart: () => {
180
+ order.push("flow:onStart");
181
+ },
182
+ onFinish: () => {
183
+ order.push("flow:onFinish");
184
+ },
185
+ }),
186
+ async ({ input }) => ({ y: input.x }),
187
+ );
188
+
189
+ await fa.generate({ x: 1 });
190
+
191
+ expect(order).toEqual(["engine:onStart", "flow:onStart", "engine:onFinish", "flow:onFinish"]);
192
+ });
193
+
194
+ it("engine onError fires before flow agent onError", async () => {
195
+ const order: string[] = [];
196
+
197
+ const engine = createFlowEngine({
198
+ onError: () => {
199
+ order.push("engine:onError");
200
+ },
201
+ });
202
+
203
+ const fa = engine(
204
+ defaultConfig({
205
+ onError: () => {
206
+ order.push("flow:onError");
207
+ },
208
+ }),
209
+ async () => {
210
+ throw new Error("boom");
211
+ },
212
+ );
213
+
214
+ const result = await fa.generate({ x: 1 });
215
+
216
+ expect(result.ok).toBe(false);
217
+ expect(order).toEqual(["engine:onError", "flow:onError"]);
218
+ });
219
+
220
+ it("engine onStepStart fires before flow agent onStepStart", async () => {
221
+ const order: string[] = [];
222
+
223
+ const engine = createFlowEngine({
224
+ onStepStart: () => {
225
+ order.push("engine:onStepStart");
226
+ },
227
+ onStepFinish: () => {
228
+ order.push("engine:onStepFinish");
229
+ },
230
+ });
231
+
232
+ const fa = engine(
233
+ defaultConfig({
234
+ onStepStart: () => {
235
+ order.push("flow:onStepStart");
236
+ },
237
+ onStepFinish: () => {
238
+ order.push("flow:onStepFinish");
239
+ },
240
+ }),
241
+ async ({ $, input }) => {
242
+ await $.step({
243
+ id: "my-step",
244
+ execute: async () => ({ v: 1 }),
245
+ });
246
+ return { y: input.x };
247
+ },
248
+ );
249
+
250
+ await fa.generate({ x: 1 });
251
+
252
+ expect(order).toEqual([
253
+ "engine:onStepStart",
254
+ "flow:onStepStart",
255
+ "engine:onStepFinish",
256
+ "flow:onStepFinish",
257
+ ]);
258
+ });
259
+
260
+ it("if only engine has a hook (flow does not), it fires", async () => {
261
+ const engineOnStart = vi.fn();
262
+ const engineOnFinish = vi.fn();
263
+
264
+ const engine = createFlowEngine({
265
+ onStart: engineOnStart,
266
+ onFinish: engineOnFinish,
267
+ });
268
+
269
+ const fa = engine(defaultConfig(), async ({ input }) => ({ y: input.x }));
270
+
271
+ await fa.generate({ x: 5 });
272
+
273
+ expect(engineOnStart).toHaveBeenCalledTimes(1);
274
+ expect(engineOnFinish).toHaveBeenCalledTimes(1);
275
+ });
276
+
277
+ it("if only flow has a hook (engine does not), it fires", async () => {
278
+ const flowOnStart = vi.fn();
279
+ const flowOnFinish = vi.fn();
280
+
281
+ const engine = createFlowEngine({});
282
+
283
+ const fa = engine(
284
+ defaultConfig({
285
+ onStart: flowOnStart,
286
+ onFinish: flowOnFinish,
287
+ }),
288
+ async ({ input }) => ({ y: input.x }),
289
+ );
290
+
291
+ await fa.generate({ x: 5 });
292
+
293
+ expect(flowOnStart).toHaveBeenCalledTimes(1);
294
+ expect(flowOnFinish).toHaveBeenCalledTimes(1);
295
+ });
296
+ });
297
+
298
+ // ---------------------------------------------------------------------------
299
+ // Combined: custom steps + hooks
300
+ // ---------------------------------------------------------------------------
301
+
302
+ describe("custom steps with hooks", () => {
303
+ it("custom steps work alongside hook merging", async () => {
304
+ const order: string[] = [];
305
+
306
+ const engine = createFlowEngine({
307
+ $: {
308
+ triple: async ({ config }: { config: { value: number } }) => {
309
+ order.push("custom:execute");
310
+ return config.value * 3;
311
+ },
312
+ },
313
+ onStart: () => {
314
+ order.push("engine:onStart");
315
+ },
316
+ onFinish: () => {
317
+ order.push("engine:onFinish");
318
+ },
319
+ });
320
+
321
+ const fa = engine(
322
+ defaultConfig({
323
+ onStart: () => {
324
+ order.push("flow:onStart");
325
+ },
326
+ onFinish: () => {
327
+ order.push("flow:onFinish");
328
+ },
329
+ }),
330
+ async ({ input, $ }) => {
331
+ const tripled = await $.triple({ value: input.x });
332
+ return { y: tripled };
333
+ },
334
+ );
335
+
336
+ const result = await fa.generate({ x: 3 });
337
+
338
+ expect(result.ok).toBe(true);
339
+ if (!result.ok) return;
340
+ expect(result.output).toEqual({ y: 9 });
341
+ expect(order).toEqual([
342
+ "engine:onStart",
343
+ "flow:onStart",
344
+ "custom:execute",
345
+ "engine:onFinish",
346
+ "flow:onFinish",
347
+ ]);
348
+ });
349
+ });
350
+
351
+ // ---------------------------------------------------------------------------
352
+ // Edge cases
353
+ // ---------------------------------------------------------------------------
354
+
355
+ describe("edge cases", () => {
356
+ it("returns validation error for invalid input", async () => {
357
+ const engine = createFlowEngine({});
358
+
359
+ const fa = engine(defaultConfig(), async ({ input }) => ({ y: input.x }));
360
+
361
+ const result = await fa.generate({ x: "not-a-number" } as unknown as { x: number });
362
+
363
+ expect(result.ok).toBe(false);
364
+ if (result.ok) return;
365
+ expect(result.error.code).toBe("VALIDATION_ERROR");
366
+ });
367
+
368
+ it("falls back to createDefaultLogger when logger is omitted from config", async () => {
369
+ const engine = createFlowEngine({});
370
+
371
+ const fa = engine(
372
+ { name: "no-logger-flow", input: Input, output: Output },
373
+ async ({ input }) => ({ y: input.x + 1 }),
374
+ );
375
+
376
+ const result = await fa.generate({ x: 10 });
377
+
378
+ expect(result.ok).toBe(true);
379
+ if (!result.ok) return;
380
+ expect(result.output).toEqual({ y: 11 });
381
+ });
382
+
383
+ it("returns FLOW_AGENT_ERROR when handler throws", async () => {
384
+ const engine = createFlowEngine({});
385
+
386
+ const fa = engine(defaultConfig(), async () => {
387
+ throw new Error("handler failed");
388
+ });
389
+
390
+ const result = await fa.generate({ x: 1 });
391
+
392
+ expect(result.ok).toBe(false);
393
+ if (result.ok) return;
394
+ expect(result.error.code).toBe("FLOW_AGENT_ERROR");
395
+ expect(result.error.message).toBe("handler failed");
396
+ });
397
+
398
+ it("engine can create multiple independent flow agents", async () => {
399
+ const engine = createFlowEngine({
400
+ $: {
401
+ add: async ({ config }: { config: { a: number; b: number } }) => config.a + config.b,
402
+ },
403
+ });
404
+
405
+ const fa1 = engine({ ...defaultConfig(), name: "fa-1" }, async ({ input, $ }) => ({
406
+ y: await $.add({ a: input.x, b: 1 }),
407
+ }));
408
+
409
+ const fa2 = engine({ ...defaultConfig(), name: "fa-2" }, async ({ input, $ }) => ({
410
+ y: await $.add({ a: input.x, b: 100 }),
411
+ }));
412
+
413
+ const [r1, r2] = await Promise.all([fa1.generate({ x: 5 }), fa2.generate({ x: 5 })]);
414
+
415
+ expect(r1.ok).toBe(true);
416
+ expect(r2.ok).toBe(true);
417
+ if (!r1.ok || !r2.ok) return;
418
+ expect(r1.output).toEqual({ y: 6 });
419
+ expect(r2.output).toEqual({ y: 105 });
420
+ });
421
+ });
422
+
423
+ // ---------------------------------------------------------------------------
424
+ // Custom step name validation
425
+ // ---------------------------------------------------------------------------
426
+
427
+ describe("custom step name validation", () => {
428
+ it("throws when a custom step name conflicts with a reserved name", () => {
429
+ expect(() =>
430
+ createFlowEngine({
431
+ $: {
432
+ step: async () => "conflict",
433
+ },
434
+ }),
435
+ ).toThrow();
436
+ });
437
+
438
+ it("throws for each reserved name", () => {
439
+ for (const reserved of ["step", "agent", "map", "each", "reduce", "while", "all", "race"]) {
440
+ expect(
441
+ () =>
442
+ createFlowEngine({
443
+ $: {
444
+ [reserved]: async () => "conflict",
445
+ },
446
+ }),
447
+ `Expected createFlowEngine to throw for reserved name "${reserved}"`,
448
+ ).toThrow();
449
+ }
450
+ });
451
+ });
452
+ });