footprintjs 0.3.0 → 0.4.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 (74) hide show
  1. package/README.md +107 -329
  2. package/dist/esm/index.js +10 -4
  3. package/dist/esm/lib/contract/openapi.js +6 -6
  4. package/dist/esm/lib/contract/schema.js +4 -4
  5. package/dist/esm/lib/contract/types.js +1 -1
  6. package/dist/esm/lib/engine/index.js +12 -1
  7. package/dist/esm/lib/engine/narrative/FlowRecorderDispatcher.js +187 -0
  8. package/dist/esm/lib/engine/narrative/NarrativeFlowRecorder.js +89 -0
  9. package/dist/esm/lib/engine/narrative/index.js +12 -1
  10. package/dist/esm/lib/engine/narrative/recorders/AdaptiveNarrativeFlowRecorder.js +48 -0
  11. package/dist/esm/lib/engine/narrative/recorders/MilestoneNarrativeFlowRecorder.js +41 -0
  12. package/dist/esm/lib/engine/narrative/recorders/ProgressiveNarrativeFlowRecorder.js +54 -0
  13. package/dist/esm/lib/engine/narrative/recorders/RLENarrativeFlowRecorder.js +74 -0
  14. package/dist/esm/lib/engine/narrative/recorders/SeparateNarrativeFlowRecorder.js +55 -0
  15. package/dist/esm/lib/engine/narrative/recorders/SilentNarrativeFlowRecorder.js +50 -0
  16. package/dist/esm/lib/engine/narrative/recorders/WindowedNarrativeFlowRecorder.js +82 -0
  17. package/dist/esm/lib/engine/narrative/recorders/index.js +9 -0
  18. package/dist/esm/lib/engine/narrative/types.js +1 -1
  19. package/dist/esm/lib/engine/traversal/FlowchartTraverser.js +28 -6
  20. package/dist/esm/lib/runner/FlowChartExecutor.js +21 -1
  21. package/dist/esm/types/index.d.ts +10 -1
  22. package/dist/esm/types/lib/contract/types.d.ts +2 -2
  23. package/dist/esm/types/lib/engine/index.d.ts +10 -0
  24. package/dist/esm/types/lib/engine/narrative/FlowRecorderDispatcher.d.ts +38 -0
  25. package/dist/esm/types/lib/engine/narrative/NarrativeFlowRecorder.d.ts +31 -0
  26. package/dist/esm/types/lib/engine/narrative/index.d.ts +10 -0
  27. package/dist/esm/types/lib/engine/narrative/recorders/AdaptiveNarrativeFlowRecorder.d.ts +25 -0
  28. package/dist/esm/types/lib/engine/narrative/recorders/MilestoneNarrativeFlowRecorder.d.ts +24 -0
  29. package/dist/esm/types/lib/engine/narrative/recorders/ProgressiveNarrativeFlowRecorder.d.ts +30 -0
  30. package/dist/esm/types/lib/engine/narrative/recorders/RLENarrativeFlowRecorder.d.ts +25 -0
  31. package/dist/esm/types/lib/engine/narrative/recorders/SeparateNarrativeFlowRecorder.d.ts +32 -0
  32. package/dist/esm/types/lib/engine/narrative/recorders/SilentNarrativeFlowRecorder.d.ts +25 -0
  33. package/dist/esm/types/lib/engine/narrative/recorders/WindowedNarrativeFlowRecorder.d.ts +29 -0
  34. package/dist/esm/types/lib/engine/narrative/recorders/index.d.ts +7 -0
  35. package/dist/esm/types/lib/engine/narrative/types.d.ts +79 -0
  36. package/dist/esm/types/lib/engine/traversal/FlowchartTraverser.d.ts +7 -0
  37. package/dist/esm/types/lib/runner/FlowChartExecutor.d.ts +12 -0
  38. package/dist/index.js +19 -5
  39. package/dist/lib/contract/openapi.js +6 -6
  40. package/dist/lib/contract/schema.js +4 -4
  41. package/dist/lib/contract/types.js +1 -1
  42. package/dist/lib/engine/index.js +22 -2
  43. package/dist/lib/engine/narrative/FlowRecorderDispatcher.js +191 -0
  44. package/dist/lib/engine/narrative/NarrativeFlowRecorder.js +93 -0
  45. package/dist/lib/engine/narrative/index.js +22 -2
  46. package/dist/lib/engine/narrative/recorders/AdaptiveNarrativeFlowRecorder.js +52 -0
  47. package/dist/lib/engine/narrative/recorders/MilestoneNarrativeFlowRecorder.js +45 -0
  48. package/dist/lib/engine/narrative/recorders/ProgressiveNarrativeFlowRecorder.js +58 -0
  49. package/dist/lib/engine/narrative/recorders/RLENarrativeFlowRecorder.js +78 -0
  50. package/dist/lib/engine/narrative/recorders/SeparateNarrativeFlowRecorder.js +59 -0
  51. package/dist/lib/engine/narrative/recorders/SilentNarrativeFlowRecorder.js +54 -0
  52. package/dist/lib/engine/narrative/recorders/WindowedNarrativeFlowRecorder.js +86 -0
  53. package/dist/lib/engine/narrative/recorders/index.js +19 -0
  54. package/dist/lib/engine/narrative/types.js +1 -1
  55. package/dist/lib/engine/traversal/FlowchartTraverser.js +28 -6
  56. package/dist/lib/runner/FlowChartExecutor.js +21 -1
  57. package/dist/types/index.d.ts +10 -1
  58. package/dist/types/lib/contract/types.d.ts +2 -2
  59. package/dist/types/lib/engine/index.d.ts +10 -0
  60. package/dist/types/lib/engine/narrative/FlowRecorderDispatcher.d.ts +38 -0
  61. package/dist/types/lib/engine/narrative/NarrativeFlowRecorder.d.ts +31 -0
  62. package/dist/types/lib/engine/narrative/index.d.ts +10 -0
  63. package/dist/types/lib/engine/narrative/recorders/AdaptiveNarrativeFlowRecorder.d.ts +25 -0
  64. package/dist/types/lib/engine/narrative/recorders/MilestoneNarrativeFlowRecorder.d.ts +24 -0
  65. package/dist/types/lib/engine/narrative/recorders/ProgressiveNarrativeFlowRecorder.d.ts +30 -0
  66. package/dist/types/lib/engine/narrative/recorders/RLENarrativeFlowRecorder.d.ts +25 -0
  67. package/dist/types/lib/engine/narrative/recorders/SeparateNarrativeFlowRecorder.d.ts +32 -0
  68. package/dist/types/lib/engine/narrative/recorders/SilentNarrativeFlowRecorder.d.ts +25 -0
  69. package/dist/types/lib/engine/narrative/recorders/WindowedNarrativeFlowRecorder.d.ts +29 -0
  70. package/dist/types/lib/engine/narrative/recorders/index.d.ts +7 -0
  71. package/dist/types/lib/engine/narrative/types.d.ts +79 -0
  72. package/dist/types/lib/engine/traversal/FlowchartTraverser.d.ts +7 -0
  73. package/dist/types/lib/runner/FlowChartExecutor.d.ts +12 -0
  74. package/package.json +1 -1
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  <p align="center">
2
2
  <h1 align="center">FootPrint</h1>
3
3
  <p align="center">
4
- <strong>Turn your whiteboard flowchart into running code &mdash; with automatic causal traces.</strong>
4
+ <strong>The flowchart pattern for backend code &mdash; self-explainable systems that AI can reason about.</strong>
5
5
  </p>
6
6
  </p>
7
7
 
@@ -15,7 +15,9 @@
15
15
 
16
16
  <br>
17
17
 
18
- FootPrint is a runtime for building **flowchart pipelines** where each node is just a function. It produces **causal traces** as a byproduct of execution &mdash; so any LLM can explain what happened and why, without reconstructing from logs.
18
+ **MVC is a pattern for structuring backends. FootPrint is a different pattern &mdash; the flowchart pattern &mdash; where your business logic is organized as a graph of functions with transactional state.** The code becomes self-explainable: AI can read the structure, trace every decision, and explain what happened without reconstructing from logs.
19
+
20
+ > FootPrint is **not** a workflow engine, pipeline builder, or orchestrator. It's a code pattern &mdash; like how React changed how we build UIs, FootPrint changes how we structure backend logic to be AI-native.
19
21
 
20
22
  ```bash
21
23
  npm install footprintjs
@@ -49,18 +51,26 @@ console.log(executor.getNarrative());
49
51
 
50
52
  > **[Try it in the browser](https://footprintjs.github.io/footprint-playground/)** &mdash; no install needed
51
53
  >
52
- > **[Browse 20+ examples](https://github.com/footprintjs/footPrint-samples)** &mdash; features, flowchart patterns, and a full loan underwriting demo
54
+ > **[Browse 25+ examples](https://github.com/footprintjs/footPrint-samples)** &mdash; features, flowchart patterns, flow recorder strategies, and a full loan underwriting demo
53
55
 
54
56
  ---
55
57
 
56
- ## Why FootPrint?
58
+ ## Why a new pattern?
59
+
60
+ **MVC** separates concerns into Model, View, Controller. It works, but the code is opaque to AI &mdash; an LLM can't trace why a request produced a specific result without parsing scattered logs.
61
+
62
+ **The flowchart pattern** structures the same logic as a graph of named functions with managed state. This gives you two things MVC can't:
57
63
 
58
- | | Without FootPrint | With FootPrint |
64
+ 1. **Self-describing code** &mdash; The structure auto-generates tool descriptions for LLM agents. No hand-written descriptions that drift from reality.
65
+ 2. **Self-explaining execution** &mdash; Every run produces a causal trace showing what happened and why. An LLM reads the trace and explains decisions accurately &mdash; no hallucination.
66
+
67
+ | | MVC / Traditional | Flowchart Pattern (FootPrint) |
59
68
  |---|---|---|
60
- | **LLM explains a decision** | Reconstruct from scattered logs; expensive, slow, hallucination-prone | Read the causal trace; cheap model, fewer tokens, zero hallucination |
61
- | **Tool descriptions** | Write and maintain them by hand | Auto-generated from the flowchart structure |
62
- | **Debugging** | `console.log` + guesswork | Time-travel replay to any stage |
69
+ | **Code structure** | Controllers with implicit flow | Explicit graph of named functions |
70
+ | **LLM explains a decision** | Reconstruct from scattered logs | Read the causal trace directly |
71
+ | **Tool descriptions for agents** | Write and maintain by hand | Auto-generated from the graph |
63
72
  | **State management** | Global/manual, race-prone | Transactional scope with atomic commits |
73
+ | **Debugging** | `console.log` + guesswork | Time-travel replay to any stage |
64
74
 
65
75
  ### Example: Loan rejection
66
76
 
@@ -104,7 +114,7 @@ That answer came from the trace &mdash; not from the LLM's imagination.
104
114
 
105
115
  ## The code that produced it
106
116
 
107
- No one wrote those trace sentences. Stage functions just read and write scope &mdash; the narrative builds itself:
117
+ This is regular backend code &mdash; just structured as a flowchart instead of a controller. No one wrote those trace sentences. The functions just read and write scope; the pattern produces the narrative automatically:
108
118
 
109
119
  ```typescript
110
120
  import {
@@ -189,15 +199,17 @@ await executor.run();
189
199
  const narrative = executor.getNarrative(); // ← the trace above
190
200
  ```
191
201
 
192
- `enableNarrative()` auto-instruments every scope. The executor captures stage transitions, decisions, reads, and writes &mdash; then merges them into the combined trace. No descriptions were written by hand.
202
+ The functions are ordinary TypeScript &mdash; no decorators, no special annotations. The flowchart pattern captures stage transitions, decisions, reads, and writes automatically. That's the difference from MVC: in MVC, this trace doesn't exist. In the flowchart pattern, it's a byproduct of the structure.
193
203
 
194
204
  ---
195
205
 
196
- ## Two narratives, both auto-generated
206
+ ## What makes it self-explainable
207
+
208
+ The flowchart pattern produces two AI-readable outputs automatically &mdash; no extra code needed:
197
209
 
198
- ### Build-time: tool description for LLM tool selection
210
+ ### Build-time: tool description for LLM agents
199
211
 
200
- When you call `.build()`, FootPrint auto-generates `chart.description` &mdash; a structural summary of what the pipeline does:
212
+ When you call `.build()`, the structure auto-generates `chart.description` &mdash; a complete description of what the code does:
201
213
 
202
214
  ```
203
215
  FlowChart: ReceiveApplication
@@ -244,19 +256,19 @@ const tools = [
244
256
 
245
257
  ### Runtime: causal trace for LLM explanation
246
258
 
247
- After `.run()`, the combined narrative shows what the code *actually did* &mdash; every value read, every value written, every decision made. This is the trace shown at the top of this page. Ship it alongside the result so any follow-up LLM call can explain what happened and why.
259
+ After `.run()`, the trace shows what the code *actually did* &mdash; every value read, every value written, every decision made. Ship it alongside the result so any LLM can explain what happened and why.
248
260
 
249
- **Build-time tells the LLM which tool to use. Runtime tells the LLM what the tool did.**
261
+ **Build-time tells the LLM what the code does. Runtime tells the LLM what the code did.** That's what makes it self-explainable &mdash; the pattern produces both automatically.
250
262
 
251
263
  ---
252
264
 
253
- ## How it works
265
+ ## How the pattern works
254
266
 
255
- FootPrint has three moving parts:
267
+ The flowchart pattern has three parts &mdash; the same way MVC has Model, View, Controller:
256
268
 
257
- 1. **Scope** &mdash; Transactional state shared across stages. Writes are buffered and committed atomically. Recorders observe every read/write without modifying behavior.
258
- 2. **Builder** &mdash; Fluent API that compiles your flowchart into a traversable node tree.
259
- 3. **Engine** &mdash; DFS traversal that executes stages, manages state, and generates narrative.
269
+ 1. **Builder** &mdash; Define your business logic as a graph of named functions. Like how a controller defines routes, but the structure is an explicit flowchart.
270
+ 2. **Scope** &mdash; Transactional state shared across stages. Writes are buffered and committed atomically. This replaces scattered state management.
271
+ 3. **Engine** &mdash; Executes the graph and auto-generates the causal trace. You write functions; the pattern produces the explanation.
260
272
 
261
273
  ```
262
274
  ┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐
@@ -273,229 +285,91 @@ FootPrint has three moving parts:
273
285
 
274
286
  ---
275
287
 
276
- ## Patterns
288
+ ## Documentation
277
289
 
278
- ### Linear
290
+ | Guide | What it covers |
291
+ |-------|---------------|
292
+ | **[Patterns](docs/guides/patterns.md)** | All 7 flowchart patterns with diagrams |
293
+ | **[Scope](docs/guides/scope.md)** | Typed, raw, and Zod scope; recorders; protection |
294
+ | **[Execution Control](docs/guides/execution.md)** | breakFn, cancellation, timeout, fail-fast, loops |
295
+ | **[Error Handling](docs/guides/error-handling.md)** | Commit-on-error, debug recorder, post-mortem |
296
+ | **[Flow Recorders](docs/guides/flow-recorders.md)** | Pluggable observers for control flow narrative — 7 built-in strategies |
297
+ | **[Contracts](docs/guides/contracts.md)** | defineContract, OpenAPI 3.1, Zod vs JSON Schema |
298
+ | **[Internals](docs/internals/)** | Architecture deep-dives for each library |
279
299
 
280
- ```typescript
281
- import { flowChart } from 'footprintjs';
300
+ ---
282
301
 
283
- flowChart('A', fnA)
284
- .addFunction('B', fnB)
285
- .addFunction('C', fnC)
286
- .build();
287
- ```
302
+ ## Patterns
288
303
 
289
- ### Parallel (Fork)
304
+ Seven composition patterns &mdash; linear, parallel, conditional, multi-select, subflow, streaming, and loops:
290
305
 
291
306
  ```typescript
307
+ // Linear: A → B → C
308
+ flowChart('A', fnA).addFunction('B', fnB).addFunction('C', fnC).build();
309
+
310
+ // Parallel fork
292
311
  flowChart('Fetch', fetchFn)
293
312
  .addListOfFunction([
294
313
  { id: 'html', name: 'ParseHTML', fn: parseHTML },
295
314
  { id: 'css', name: 'ParseCSS', fn: parseCSS },
296
- { id: 'js', name: 'ParseJS', fn: parseJS },
297
315
  ])
298
316
  .addFunction('Merge', mergeFn)
299
317
  .build();
300
- ```
301
-
302
- ### Conditional (Decider)
303
318
 
304
- A decider reads from scope and returns the ID of exactly one branch to execute:
305
-
306
- ```typescript
319
+ // Conditional branching (decider)
307
320
  flowChart('Classify', classifyFn)
308
- .addDeciderFunction('Route', (scope) => {
309
- const type = scope.getValue('fulfillmentType');
310
- return type === 'digital' ? 'digital' : 'physical';
311
- })
321
+ .addDeciderFunction('Route', routeFn)
312
322
  .addFunctionBranch('digital', 'DigitalDelivery', digitalFn)
313
323
  .addFunctionBranch('physical', 'ShipPackage', shipFn)
314
324
  .setDefault('physical')
315
325
  .end()
316
326
  .build();
317
- ```
318
-
319
- ### Subflow Composition
320
-
321
- Mount entire flowcharts as nodes in a larger workflow:
322
-
323
- ```typescript
324
- const faqFlow = flowChart('FAQ_Entry', faqEntryFn)
325
- .addFunction('FAQ_Answer', faqAnswerFn)
326
- .build();
327
-
328
- const ragFlow = flowChart('RAG_Entry', ragEntryFn)
329
- .addFunction('RAG_Retrieve', ragRetrieveFn)
330
- .addFunction('RAG_Answer', ragAnswerFn)
331
- .build();
332
327
 
333
- const mainChart = flowChart('Router', routerFn)
328
+ // Subflow composition
329
+ flowChart('Router', routerFn)
334
330
  .addSubFlowChart('faq', faqFlow, 'FAQ Handler')
335
331
  .addSubFlowChart('rag', ragFlow, 'RAG Handler')
336
- .addFunction('Aggregate', aggregateFn)
337
332
  .build();
338
- ```
339
333
 
340
- ### Streaming (LLM)
341
-
342
- ```typescript
343
- const chart = flowChart('PreparePrompt', prepareFn)
344
- .addStreamingFunction('AskLLM', 'llm-stream', askLLMFn)
345
- .onStream((streamId, token) => process.stdout.write(token))
346
- .onStreamEnd((streamId, fullText) => console.log('\nDone:', fullText))
347
- .addFunction('ProcessResponse', processFn)
348
- .build();
349
- ```
350
-
351
- ### Execution Control
352
-
353
- Every stage receives `(scope, breakFn)`. Three levels of control:
354
-
355
- ```typescript
356
- // 1. breakFn() — Graceful stop: complete this stage, skip remaining stages
357
- const validateInput = async (scope: ScopeFacade, breakFn: () => void) => {
358
- const amount = scope.getValue('loanAmount') as number;
359
- if (amount > 50_000) {
360
- scope.setValue('rejection', 'Exceeds maximum loan amount');
361
- breakFn(); // stage output is returned, no error — pipeline just stops
362
- }
363
- };
364
-
365
- // 2. throw — Hard abort: stop immediately, propagate error to caller
366
- const callExternalAPI = async (scope: ScopeFacade) => {
367
- const response = await fetch(scope.getValue('apiUrl') as string);
368
- if (response.status === 403) {
369
- throw new Error('Access denied — cannot continue'); // executor.run() rejects
370
- }
371
- };
372
-
373
- // 3. AbortSignal — External cancellation (see Cancellation & Timeout section)
374
- await executor.run({ timeoutMs: 30_000 });
375
- ```
376
-
377
- | Mechanism | Trigger | Stage completes? | Returns |
378
- |-----------|---------|-----------------|---------|
379
- | `breakFn()` | Inside stage | Yes | Stage output (no error) |
380
- | `throw` | Inside stage | No | Error propagates |
381
- | `AbortSignal` | Outside pipeline | Races async | Error propagates |
382
-
383
- ### Loops
384
-
385
- ```typescript
334
+ // Loops
386
335
  flowChart('Init', initFn)
387
- .addFunction('AskLLM', askFn, 'ask-llm')
388
- .addFunction('ParseResponse', parseFn)
389
- .addDeciderFunction('HasToolCalls', deciderFn)
390
- .addFunctionBranch('yes', 'ExecuteTools', toolsFn)
391
- .addFunctionBranch('no', 'Finalize', finalizeFn)
336
+ .addFunction('Retry', retryFn, 'retry')
337
+ .addDeciderFunction('Check', checkFn)
338
+ .addFunctionBranch('again', 'Process', processFn)
339
+ .addFunctionBranch('done', 'Finish', finishFn)
392
340
  .end()
393
- .loopTo('ask-llm') // loop back until no more tool calls
341
+ .loopTo('retry')
394
342
  .build();
395
343
  ```
396
344
 
345
+ **[Full patterns guide &rarr;](docs/guides/patterns.md)** &mdash; all seven patterns with diagrams and composition examples
346
+
397
347
  ---
398
348
 
399
349
  ## Scope
400
350
 
401
- Each stage receives a **scope** &mdash; a transactional interface to shared state. Writes are buffered and committed atomically after each stage. Recorders can observe every operation.
402
-
403
- ### Typed Scope (Recommended)
404
-
405
- Extend `ScopeFacade` with domain-specific getters for type-safe reads:
351
+ Each stage receives a **scope** &mdash; a transactional interface to shared state:
406
352
 
407
353
  ```typescript
408
- import { ScopeFacade } from 'footprintjs';
409
-
354
+ // Typed scope (recommended)
410
355
  class LoanScope extends ScopeFacade {
411
- get creditScore(): number {
412
- return this.getValue('creditScore') as number;
413
- }
414
- get riskTier(): string {
415
- return this.getValue('riskTier') as string;
416
- }
417
- get dtiStatus(): string {
418
- return this.getValue('dtiStatus') as string;
419
- }
356
+ get creditScore(): number { return this.getValue('creditScore') as number; }
420
357
  }
421
358
 
422
- const scopeFactory = (ctx: any, stageName: string) => new LoanScope(ctx, stageName);
423
-
424
- // In stage functions:
425
- const assessRisk = async (scope: LoanScope) => {
426
- if (scope.creditScore < 600 || scope.dtiStatus === 'excessive') {
427
- scope.setValue('riskTier', 'high'); // writes go through setValue
428
- }
429
- };
430
- ```
431
-
432
- > **Why `getValue`/`setValue` instead of direct properties?** Scope protection blocks `scope.foo = bar` &mdash; those writes bypass transactional buffering and recorder hooks. Typed getters give you clean reads; `setValue` gives you tracked writes.
433
-
434
- ### Raw Scope (Low-level)
435
-
436
- ```typescript
437
- scope.setValue('total', 79.98); // overwrite
438
- scope.updateValue('config', { retries: 3 }); // deep merge
439
- const total = scope.getValue('total'); // read
440
- ```
441
-
442
- ### Validated Scope (Zod)
443
-
444
- ```typescript
445
- import { z } from 'zod';
446
- import { defineScopeFromZod } from 'footprintjs';
447
-
448
- const schema = z.object({
359
+ // Validated scope (Zod)
360
+ const scopeFactory = defineScopeFromZod(z.object({
449
361
  creditScore: z.number(),
450
362
  riskTier: z.string().optional(),
451
- });
363
+ }));
452
364
 
453
- const scopeFactory = defineScopeFromZod(schema);
454
- // Proxy-based: validates writes against the schema at runtime
365
+ // Raw scope
366
+ scope.setValue('total', 79.98);
367
+ scope.updateValue('config', { retries: 3 });
455
368
  ```
456
369
 
457
- ---
458
-
459
- ## Observability
460
-
461
- ### Recorders
462
-
463
- Recorders observe scope operations without modifying them. Attach multiple for different concerns:
464
-
465
- ```typescript
466
- import {
467
- ScopeFacade, DebugRecorder, MetricRecorder,
468
- } from 'footprintjs';
469
-
470
- const scopeFactory = (ctx: any, stageName: string) => {
471
- const scope = new ScopeFacade(ctx, stageName);
472
- scope.attachRecorder(new DebugRecorder({ verbosity: 'verbose' }));
473
- scope.attachRecorder(new MetricRecorder());
474
- return scope;
475
- };
476
- ```
477
-
478
- > **Note:** `NarrativeRecorder` is attached automatically when narrative is enabled via `setEnableNarrative()` or `executor.enableNarrative()`. You only need to attach it manually if you need custom options.
479
-
480
- Error isolation is built in: if a recorder throws, the error is routed to `onError` hooks of other recorders, and the scope operation continues normally.
481
-
482
- ### Custom Recorders
483
-
484
- Implement any subset of six hooks: `onRead`, `onWrite`, `onCommit`, `onError`, `onStageStart`, `onStageEnd`.
485
-
486
- ```typescript
487
- import { Recorder, WriteEvent } from 'footprintjs';
370
+ Pluggable recorders observe every operation: `DebugRecorder`, `MetricRecorder`, `NarrativeRecorder`, or bring your own.
488
371
 
489
- class AuditRecorder implements Recorder {
490
- readonly id = 'audit';
491
- private writes: Array<{ stage: string; key: string; value: unknown }> = [];
492
-
493
- onWrite(event: WriteEvent) {
494
- this.writes.push({ stage: event.stageName, key: event.key, value: event.value });
495
- }
496
- getWrites() { return [...this.writes]; }
497
- }
498
- ```
372
+ **[Full scope guide &rarr;](docs/guides/scope.md)** &mdash; typed/raw/Zod scope, recorders, protection, provider system
499
373
 
500
374
  ---
501
375
 
@@ -513,95 +387,30 @@ class AuditRecorder implements Recorder {
513
387
  | **Composable Subflows** | Mount entire flowcharts as nodes in larger workflows |
514
388
  | **Streaming** | Built-in streaming stages for LLM token emission |
515
389
  | **Pluggable Recorders** | DebugRecorder, MetricRecorder, NarrativeRecorder &mdash; or bring your own |
390
+ | **Flow Recorders** | 7 narrative strategies for loop summarization &mdash; Windowed, Silent, Adaptive, Progressive, Milestone, RLE, Separate &mdash; or build custom ([examples](https://github.com/footprintjs/footPrint-samples/tree/main/examples/flow-recorders)) |
516
391
 
517
392
  ---
518
393
 
519
- ## Cancellation & Timeout
520
-
521
- For LLM pipelines where API calls can hang, `FlowChartExecutor.run()` supports cooperative cancellation:
522
-
523
- ```typescript
524
- // Timeout: auto-abort after 30 seconds
525
- const result = await executor.run({ timeoutMs: 30_000 });
526
-
527
- // AbortSignal: cancel from outside
528
- const controller = new AbortController();
529
- setTimeout(() => controller.abort(), 10_000); // cancel after 10s
530
- const result = await executor.run({ signal: controller.signal });
531
- ```
532
-
533
- The signal is checked before each stage starts and raced against async stage functions. Aborted executions throw with the signal's reason.
534
-
535
- ## Fail-Fast Forks
394
+ ## Execution Control & Error Handling
536
395
 
537
- By default, parallel children run to completion even if some fail (errors captured as `{ isError: true }`). For cases where you want immediate failure:
396
+ Three levels of control: `breakFn()` (graceful stop), `throw` (hard abort), `AbortSignal` (external cancellation):
538
397
 
539
398
  ```typescript
540
- flowChart('Fetch', fetchFn)
541
- .addListOfFunction([
542
- { id: 'api1', name: 'CallAPI1', fn: api1Fn },
543
- { id: 'api2', name: 'CallAPI2', fn: api2Fn },
544
- ], { failFast: true }) // first child error rejects the whole fork
545
- .build();
546
- ```
547
-
548
- ## Design: Error Handling
549
-
550
- FootPrint's error handling is designed around one principle: **the trace must capture everything that happened, including failures**.
551
-
552
- ### Who is responsible for what
553
-
554
- | Layer | Responsibility |
555
- |-------|---------------|
556
- | **Stage function** | Business logic. Throws errors when invariants break. |
557
- | **Engine** | Infrastructure. Catches errors, commits the trace, records error metadata, then re-throws. |
558
- | **Consumer** | Wraps `executor.run()` in try/catch. Inspects `getSnapshot()` after failure for debugging. |
559
-
560
- ### Commit-on-error: why it matters
561
-
562
- When a stage throws, the engine calls `context.commit()` *before* re-throwing. This means:
563
-
564
- ```typescript
565
- const executor = new FlowChartExecutor(chart, scopeFactory);
566
- try {
567
- await executor.run();
568
- } catch (error) {
569
- // The snapshot captures everything up to the failure point
570
- const snapshot = executor.getSnapshot();
571
- // commitLog has entries for every stage that ran (including the one that failed)
572
- // executionTree shows scope writes, error metadata, and flow decisions
573
- // An LLM can use this to explain WHY the error happened
574
- }
575
- ```
576
-
577
- Without commit-on-error, a failed stage's partial writes would be lost. The trace would end at the last successful stage, hiding the context of the failure. Commit-on-error gives consumers complete visibility:
578
-
579
- - **Scope writes** made before the throw are preserved
580
- - **Error metadata** (`stageExecutionError`) is recorded in the execution tree
581
- - **Narrative** includes the error event (`"An error occurred at validate: ..."`)
582
- - **Commit log** has an entry for the failed stage's state
583
-
584
- ### Error narrative in practice
585
-
586
- A validation pipeline fails. The trace tells the story:
399
+ // Graceful stop — complete this stage, skip remaining
400
+ const validate = async (scope: ScopeFacade, breakFn: () => void) => {
401
+ if (scope.getValue('amount') > 50_000) { breakFn(); }
402
+ };
587
403
 
588
- ```
589
- Stage 1: The process began with FetchData.
590
- Step 1: Write rawPayload = {name: "Bob", age: -5}
591
- Stage 2: Next, it moved on to Validate.
592
- Step 1: Read rawPayload = {name: "Bob", age: -5}
593
- An error occurred at Validate: Validation failed: age must be positive.
404
+ // Timeout / external cancellation
405
+ await executor.run({ timeoutMs: 30_000 });
406
+ await executor.run({ signal: controller.signal });
594
407
  ```
595
408
 
596
- An LLM reading this trace can immediately explain: *"The validation failed because the age field was -5, which was provided in the raw payload from FetchData. Age must be a positive number."* No log reconstruction needed.
409
+ When a stage throws, the engine commits the trace *before* re-throwing &mdash; so `getSnapshot()` captures everything up to the failure point, including partial writes and error metadata.
597
410
 
598
- ### What consumers can do
411
+ **[Execution control guide &rarr;](docs/guides/execution.md)** &mdash; breakFn, cancellation, timeout, fail-fast forks, loops
599
412
 
600
- - **Retry with modifications**: Inspect the snapshot, fix inputs, re-run
601
- - **Partial results**: Fork children that succeed still return results (default mode)
602
- - **Fail-fast**: Opt into `failFast: true` when any child error should abort the whole fork
603
- - **Timeout/cancel**: Use `timeoutMs` or `AbortSignal` for external cancellation
604
- - **Post-mortem**: Feed the narrative + snapshot to an LLM for root-cause analysis
413
+ **[Error handling guide &rarr;](docs/guides/error-handling.md)** &mdash; commit-on-error, debug recorder, error narrative, post-mortem
605
414
 
606
415
  ---
607
416
 
@@ -634,6 +443,8 @@ An LLM reading this trace can immediately explain: *"The validation failed becau
634
443
  | `run(options?)` | Execute the flowchart. Options: `{ signal?, timeoutMs? }` |
635
444
  | `getNarrative()` | Combined narrative (flow + data) with ScopeFacade; flow-only otherwise |
636
445
  | `getFlowNarrative()` | Flow-only narrative sentences |
446
+ | `attachFlowRecorder(recorder)` | Attach a FlowRecorder for pluggable narrative control |
447
+ | `detachFlowRecorder(id)` | Detach a FlowRecorder by id |
637
448
  | `getNarrativeEntries()` | Structured `CombinedNarrativeEntry[]` for programmatic use |
638
449
  | `getSnapshot()` | Full execution tree + state |
639
450
  | `getExtractedResults()` | Extractor results map |
@@ -645,14 +456,18 @@ An LLM reading this trace can immediately explain: *"The validation failed becau
645
456
 
646
457
  ## How FootPrint Compares
647
458
 
648
- | Aspect | async/await | FootPrint | Temporal / Step Functions |
649
- |--------|-------------|-----------|--------------------------|
650
- | **Control Flow** | Implicit in code | Explicit flowchart | External orchestrator |
651
- | **State** | Manual/global | Scoped & transactional | Durable storage |
459
+ FootPrint is a **code pattern**, not an orchestrator. It runs in your process, not as a separate service.
460
+
461
+ | Aspect | MVC / async-await | FootPrint (flowchart pattern) | Temporal / Step Functions |
462
+ |--------|-------------------|-------------------------------|--------------------------|
463
+ | **What it is** | Code pattern | Code pattern | External orchestrator |
464
+ | **Runs where** | In your process | In your process | Separate service |
465
+ | **Control flow** | Implicit in code | Explicit graph of functions | External state machine |
466
+ | **State** | Manual / global | Transactional scope | Durable storage |
467
+ | **AI explains decisions** | Parse logs (hallucination-prone) | Read the causal trace (accurate) | Parse event history |
468
+ | **Tool descriptions** | Write by hand | Auto-generated from structure | Write by hand |
652
469
  | **Debugging** | Stack traces | Time-travel replay | Event history |
653
- | **LLM Narrative** | None | Automatic from operations | None |
654
- | **Tool Descriptions** | Manual | Auto-generated from structure | Manual |
655
- | **Complexity** | Low | Medium | High |
470
+ | **Complexity** | Low | Low-medium | High |
656
471
 
657
472
  ---
658
473
 
@@ -683,68 +498,29 @@ Measured on Node v22, Apple Silicon. Run `npm run bench` to reproduce.
683
498
 
684
499
  ## Contract & OpenAPI
685
500
 
686
- Define I/O schemas on your flowchart and auto-generate OpenAPI 3.1 specs:
501
+ Define I/O schemas (Zod or raw JSON Schema) and auto-generate OpenAPI 3.1 specs:
687
502
 
688
503
  ```typescript
689
504
  import { flowChart, defineContract } from 'footprintjs';
690
- import { z } from 'zod'; // optional — raw JSON Schema also works
691
-
692
- const chart = flowChart('ProcessLoan', receiveFn)
693
- .addFunction('Assess', assessFn)
694
- .addDeciderFunction('Decide', deciderFn)
695
- .addFunctionBranch('approved', 'Approve', approveFn)
696
- .addFunctionBranch('rejected', 'Reject', rejectFn)
697
- .end()
698
- .build();
505
+ import { z } from 'zod';
699
506
 
700
- // Define the contract — accepts Zod or raw JSON Schema
701
507
  const contract = defineContract(chart, {
702
- inputSchema: z.object({
703
- applicantName: z.string(),
704
- creditScore: z.number(),
705
- }),
706
- outputSchema: z.object({
707
- decision: z.enum(['approved', 'rejected']),
708
- reason: z.string(),
709
- }),
710
- outputMapper: (scope) => ({
711
- decision: scope.decision as string,
712
- reason: scope.reason as string,
713
- }),
508
+ inputSchema: z.object({ applicantName: z.string(), creditScore: z.number() }),
509
+ outputSchema: z.object({ decision: z.enum(['approved', 'rejected']) }),
714
510
  });
715
511
 
716
- // Auto-generate OpenAPI 3.1 spec
717
512
  const spec = contract.toOpenAPI({ version: '1.0.0', basePath: '/api' });
718
- // → { openapi: '3.1.0', paths: { '/api/processloan': { post: { ... } } }, ... }
719
- ```
720
-
721
- Schemas can also be set at build time:
722
-
723
- ```typescript
724
- const chart = flowChart('Greet', greetFn)
725
- .setInputSchema(z.object({ name: z.string() }))
726
- .setOutputSchema(z.object({ greeting: z.string() }))
727
- .setOutputMapper((scope) => ({ greeting: scope.message as string }))
728
- .build();
729
513
  ```
730
514
 
731
- Zod is an **optional peer dependency** zero bundle impact if not used. Pass raw JSON Schema instead:
515
+ Zod is an **optional peer dependency** &mdash; zero bundle impact if not used.
732
516
 
733
- ```typescript
734
- const contract = defineContract(chart, {
735
- inputSchema: {
736
- type: 'object',
737
- properties: { name: { type: 'string' } },
738
- required: ['name'],
739
- },
740
- });
741
- ```
517
+ **[Full contracts guide &rarr;](docs/guides/contracts.md)** &mdash; defineContract, OpenAPI generation, Zod vs JSON Schema, builder-level schemas
742
518
 
743
519
  ---
744
520
 
745
521
  ## Architecture
746
522
 
747
- FootPrint is six independent libraries, each usable standalone:
523
+ FootPrint is the reference implementation of the flowchart pattern &mdash; six independent libraries, each usable standalone:
748
524
 
749
525
  ```
750
526
  src/lib/
@@ -756,6 +532,8 @@ src/lib/
756
532
  └── contract/ I/O schemas, Zod→JSON Schema, OpenAPI 3.1 generation
757
533
  ```
758
534
 
535
+ **[Architecture deep-dives &rarr;](docs/internals/)** &mdash; each library has its own README with primitives, design decisions, and dependency graphs
536
+
759
537
  ---
760
538
 
761
539
  ## License
package/dist/esm/index.js CHANGED
@@ -24,10 +24,16 @@ export { NarrativeRecorder } from './lib/scope';
24
24
  // Zod-based scope definitions
25
25
  export { defineScopeFromZod } from './lib/scope';
26
26
  export { CombinedNarrativeBuilder } from './lib/engine';
27
- // ============================================================================
28
- // ContractI/O boundary, schemas, and OpenAPI generation
29
- // ============================================================================
27
+ export { NarrativeFlowRecorder } from './lib/engine';
28
+ // Built-in FlowRecorder strategies (tree-shakeable import only what you use)
29
+ export { AdaptiveNarrativeFlowRecorder } from './lib/engine';
30
+ export { MilestoneNarrativeFlowRecorder } from './lib/engine';
31
+ export { ProgressiveNarrativeFlowRecorder } from './lib/engine';
32
+ export { RLENarrativeFlowRecorder } from './lib/engine';
33
+ export { SeparateNarrativeFlowRecorder } from './lib/engine';
34
+ export { SilentNarrativeFlowRecorder } from './lib/engine';
35
+ export { WindowedNarrativeFlowRecorder } from './lib/engine';
30
36
  export { defineContract } from './lib/contract';
31
37
  export { normalizeSchema, zodToJsonSchema } from './lib/contract';
32
38
  export { generateOpenAPI } from './lib/contract';
33
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7OztHQVNHO0FBT0gsT0FBTyxFQUFFLFNBQVMsRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUU1RCwrRUFBK0U7QUFDL0UsdUNBQXVDO0FBQ3ZDLCtFQUErRTtBQUUvRSxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFFakQsK0VBQStFO0FBQy9FLDBDQUEwQztBQUMxQywrRUFBK0U7QUFFL0UsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUUxQyxZQUFZO0FBQ1osT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUM3QyxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQzVDLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUtoRCw4QkFBOEI7QUFDOUIsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sYUFBYSxDQUFDO0FBT2pELE9BQU8sRUFBRSx3QkFBd0IsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQVN4RCwrRUFBK0U7QUFDL0UsMkRBQTJEO0FBQzNELCtFQUErRTtBQUUvRSxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDaEQsT0FBTyxFQUFFLGVBQWUsRUFBRSxlQUFlLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUNsRSxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sZ0JBQWdCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEZvb3RQcmludCDigJQgUHVibGljIEFQSVxuICpcbiAqIENvbm5lY3RlZCBjYXVzYWwgdHJhY2UgbGlicmFyeSBmb3IgTExNIHBpcGVsaW5lcy5cbiAqIEJ1aWxkcyBmbG93Y2hhcnRzLCBleGVjdXRlcyB0aGVtIHZpYSBERlMgdHJhdmVyc2FsLCBhbmQgY2FwdHVyZXNcbiAqIGV2ZXJ5IHN0YWdlJ3MgY29udGV4dCAoc3RhdGUsIGRlY2lzaW9ucywgZXJyb3JzKSBpbiBhbiBhdWRpdGFibGUgdHJhY2UuXG4gKlxuICogRm9yIGFkdmFuY2VkL2ludGVybmFsIEFQSXMgKG1lbW9yeSBwcmltaXRpdmVzLCBlbmdpbmUgaGFuZGxlcnMsIHByb3ZpZGVycyksXG4gKiBpbXBvcnQgZnJvbSAnZm9vdHByaW50L2FkdmFuY2VkJy5cbiAqL1xuXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4vLyBCdWlsZGVyIOKAlCBGbG93Y2hhcnQgY29uc3RydWN0aW9uXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbmV4cG9ydCB0eXBlIHsgRmxvd0NoYXJ0LCBQaXBlbGluZVN0YWdlRnVuY3Rpb24gYXMgU3RhZ2VIYW5kbGVyLCBTdHJlYW1IYW5kbGVycyB9IGZyb20gJy4vbGliL2J1aWxkZXInO1xuZXhwb3J0IHsgZmxvd0NoYXJ0LCBGbG93Q2hhcnRCdWlsZGVyIH0gZnJvbSAnLi9saWIvYnVpbGRlcic7XG5cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbi8vIFJ1bm5lciDigJQgRXhlY3V0aW9uIGNvbnZlbmllbmNlIGxheWVyXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbmV4cG9ydCB7IEZsb3dDaGFydEV4ZWN1dG9yIH0gZnJvbSAnLi9saWIvcnVubmVyJztcblxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuLy8gU2NvcGUg4oCUIFBlci1zdGFnZSBmYWNhZGVzIGFuZCByZWNvcmRlcnNcbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuZXhwb3J0IHsgU2NvcGVGYWNhZGUgfSBmcm9tICcuL2xpYi9zY29wZSc7XG5cbi8vIFJlY29yZGVyc1xuZXhwb3J0IHsgTWV0cmljUmVjb3JkZXIgfSBmcm9tICcuL2xpYi9zY29wZSc7XG5leHBvcnQgeyBEZWJ1Z1JlY29yZGVyIH0gZnJvbSAnLi9saWIvc2NvcGUnO1xuZXhwb3J0IHsgTmFycmF0aXZlUmVjb3JkZXIgfSBmcm9tICcuL2xpYi9zY29wZSc7XG5cbi8vIFJlY29yZGVyIGludGVyZmFjZSBhbmQgY29yZSBldmVudCB0eXBlcyAobmVlZGVkIHRvIGltcGxlbWVudCBjdXN0b20gUmVjb3JkZXIpXG5leHBvcnQgdHlwZSB7IENvbW1pdEV2ZW50LCBFcnJvckV2ZW50LCBSZWFkRXZlbnQsIFJlY29yZGVyLCBXcml0ZUV2ZW50IH0gZnJvbSAnLi9saWIvc2NvcGUnO1xuXG4vLyBab2QtYmFzZWQgc2NvcGUgZGVmaW5pdGlvbnNcbmV4cG9ydCB7IGRlZmluZVNjb3BlRnJvbVpvZCB9IGZyb20gJy4vbGliL3Njb3BlJztcblxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuLy8gRW5naW5lIOKAlCBOYXJyYXRpdmUgKGNvbW1vbmx5IHVzZWQpXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbmV4cG9ydCB0eXBlIHsgQ29tYmluZWROYXJyYXRpdmVFbnRyeSB9IGZyb20gJy4vbGliL2VuZ2luZSc7XG5leHBvcnQgeyBDb21iaW5lZE5hcnJhdGl2ZUJ1aWxkZXIgfSBmcm9tICcuL2xpYi9lbmdpbmUnO1xuXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4vLyBNZW1vcnkg4oCUIFNjb3BlRmFjdG9yeSB0eXBlIChuZWVkZWQgZm9yIEZsb3dDaGFydEV4ZWN1dG9yIGNvbnN0cnVjdG9yKVxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG5leHBvcnQgdHlwZSB7IFJ1bk9wdGlvbnMgfSBmcm9tICcuL2xpYi9lbmdpbmUnO1xuZXhwb3J0IHR5cGUgeyBTY29wZUZhY3RvcnkgfSBmcm9tICcuL2xpYi9tZW1vcnknO1xuXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4vLyBDb250cmFjdCDigJQgSS9PIGJvdW5kYXJ5LCBzY2hlbWFzLCBhbmQgT3BlbkFQSSBnZW5lcmF0aW9uXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbmV4cG9ydCB7IGRlZmluZUNvbnRyYWN0IH0gZnJvbSAnLi9saWIvY29udHJhY3QnO1xuZXhwb3J0IHsgbm9ybWFsaXplU2NoZW1hLCB6b2RUb0pzb25TY2hlbWEgfSBmcm9tICcuL2xpYi9jb250cmFjdCc7XG5leHBvcnQgeyBnZW5lcmF0ZU9wZW5BUEkgfSBmcm9tICcuL2xpYi9jb250cmFjdCc7XG5leHBvcnQgdHlwZSB7XG4gIEZsb3dDaGFydENvbnRyYWN0LFxuICBGbG93Q2hhcnRDb250cmFjdE9wdGlvbnMsXG4gIEpzb25TY2hlbWEsXG4gIE9wZW5BUElPcHRpb25zLFxuICBPcGVuQVBJU3BlYyxcbn0gZnJvbSAnLi9saWIvY29udHJhY3QnO1xuIl19
39
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7OztHQVNHO0FBT0gsT0FBTyxFQUFFLFNBQVMsRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUU1RCwrRUFBK0U7QUFDL0UsdUNBQXVDO0FBQ3ZDLCtFQUErRTtBQUUvRSxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFFakQsK0VBQStFO0FBQy9FLDBDQUEwQztBQUMxQywrRUFBK0U7QUFFL0UsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUUxQyxZQUFZO0FBQ1osT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUM3QyxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQzVDLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUtoRCw4QkFBOEI7QUFDOUIsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sYUFBYSxDQUFDO0FBT2pELE9BQU8sRUFBRSx3QkFBd0IsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUl4RCxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFFckQsK0VBQStFO0FBQy9FLE9BQU8sRUFBRSw2QkFBNkIsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUM3RCxPQUFPLEVBQUUsOEJBQThCLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFDOUQsT0FBTyxFQUFFLGdDQUFnQyxFQUFFLE1BQU0sY0FBYyxDQUFDO0FBQ2hFLE9BQU8sRUFBRSx3QkFBd0IsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUN4RCxPQUFPLEVBQUUsNkJBQTZCLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFDN0QsT0FBTyxFQUFFLDJCQUEyQixFQUFFLE1BQU0sY0FBYyxDQUFDO0FBQzNELE9BQU8sRUFBRSw2QkFBNkIsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQW9CN0QsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQ2hELE9BQU8sRUFBRSxlQUFlLEVBQUUsZUFBZSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDbEUsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLGdCQUFnQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBGb290UHJpbnQg4oCUIFB1YmxpYyBBUElcbiAqXG4gKiBDb25uZWN0ZWQgY2F1c2FsIHRyYWNlIGxpYnJhcnkgZm9yIExMTSBwaXBlbGluZXMuXG4gKiBCdWlsZHMgZmxvd2NoYXJ0cywgZXhlY3V0ZXMgdGhlbSB2aWEgREZTIHRyYXZlcnNhbCwgYW5kIGNhcHR1cmVzXG4gKiBldmVyeSBzdGFnZSdzIGNvbnRleHQgKHN0YXRlLCBkZWNpc2lvbnMsIGVycm9ycykgaW4gYW4gYXVkaXRhYmxlIHRyYWNlLlxuICpcbiAqIEZvciBhZHZhbmNlZC9pbnRlcm5hbCBBUElzIChtZW1vcnkgcHJpbWl0aXZlcywgZW5naW5lIGhhbmRsZXJzLCBwcm92aWRlcnMpLFxuICogaW1wb3J0IGZyb20gJ2Zvb3RwcmludC9hZHZhbmNlZCcuXG4gKi9cblxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuLy8gQnVpbGRlciDigJQgRmxvd2NoYXJ0IGNvbnN0cnVjdGlvblxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG5leHBvcnQgdHlwZSB7IEZsb3dDaGFydCwgUGlwZWxpbmVTdGFnZUZ1bmN0aW9uIGFzIFN0YWdlSGFuZGxlciwgU3RyZWFtSGFuZGxlcnMgfSBmcm9tICcuL2xpYi9idWlsZGVyJztcbmV4cG9ydCB7IGZsb3dDaGFydCwgRmxvd0NoYXJ0QnVpbGRlciB9IGZyb20gJy4vbGliL2J1aWxkZXInO1xuXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4vLyBSdW5uZXIg4oCUIEV4ZWN1dGlvbiBjb252ZW5pZW5jZSBsYXllclxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG5leHBvcnQgeyBGbG93Q2hhcnRFeGVjdXRvciB9IGZyb20gJy4vbGliL3J1bm5lcic7XG5cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbi8vIFNjb3BlIOKAlCBQZXItc3RhZ2UgZmFjYWRlcyBhbmQgcmVjb3JkZXJzXG4vLyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbmV4cG9ydCB7IFNjb3BlRmFjYWRlIH0gZnJvbSAnLi9saWIvc2NvcGUnO1xuXG4vLyBSZWNvcmRlcnNcbmV4cG9ydCB7IE1ldHJpY1JlY29yZGVyIH0gZnJvbSAnLi9saWIvc2NvcGUnO1xuZXhwb3J0IHsgRGVidWdSZWNvcmRlciB9IGZyb20gJy4vbGliL3Njb3BlJztcbmV4cG9ydCB7IE5hcnJhdGl2ZVJlY29yZGVyIH0gZnJvbSAnLi9saWIvc2NvcGUnO1xuXG4vLyBSZWNvcmRlciBpbnRlcmZhY2UgYW5kIGNvcmUgZXZlbnQgdHlwZXMgKG5lZWRlZCB0byBpbXBsZW1lbnQgY3VzdG9tIFJlY29yZGVyKVxuZXhwb3J0IHR5cGUgeyBDb21taXRFdmVudCwgRXJyb3JFdmVudCwgUmVhZEV2ZW50LCBSZWNvcmRlciwgV3JpdGVFdmVudCB9IGZyb20gJy4vbGliL3Njb3BlJztcblxuLy8gWm9kLWJhc2VkIHNjb3BlIGRlZmluaXRpb25zXG5leHBvcnQgeyBkZWZpbmVTY29wZUZyb21ab2QgfSBmcm9tICcuL2xpYi9zY29wZSc7XG5cbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbi8vIEVuZ2luZSDigJQgTmFycmF0aXZlIChjb21tb25seSB1c2VkKVxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG5leHBvcnQgdHlwZSB7IENvbWJpbmVkTmFycmF0aXZlRW50cnkgfSBmcm9tICcuL2xpYi9lbmdpbmUnO1xuZXhwb3J0IHsgQ29tYmluZWROYXJyYXRpdmVCdWlsZGVyIH0gZnJvbSAnLi9saWIvZW5naW5lJztcblxuLy8gRmxvd1JlY29yZGVyIOKAlCBQbHVnZ2FibGUgb2JzZXJ2ZXIgZm9yIGNvbnRyb2wgZmxvdyBldmVudHMgKG1pcnJvcnMgc2NvcGUgUmVjb3JkZXIpXG5leHBvcnQgdHlwZSB7IEZsb3dMb29wRXZlbnQsIEZsb3dSZWNvcmRlciB9IGZyb20gJy4vbGliL2VuZ2luZSc7XG5leHBvcnQgeyBOYXJyYXRpdmVGbG93UmVjb3JkZXIgfSBmcm9tICcuL2xpYi9lbmdpbmUnO1xuXG4vLyBCdWlsdC1pbiBGbG93UmVjb3JkZXIgc3RyYXRlZ2llcyAodHJlZS1zaGFrZWFibGUg4oCUIGltcG9ydCBvbmx5IHdoYXQgeW91IHVzZSlcbmV4cG9ydCB7IEFkYXB0aXZlTmFycmF0aXZlRmxvd1JlY29yZGVyIH0gZnJvbSAnLi9saWIvZW5naW5lJztcbmV4cG9ydCB7IE1pbGVzdG9uZU5hcnJhdGl2ZUZsb3dSZWNvcmRlciB9IGZyb20gJy4vbGliL2VuZ2luZSc7XG5leHBvcnQgeyBQcm9ncmVzc2l2ZU5hcnJhdGl2ZUZsb3dSZWNvcmRlciB9IGZyb20gJy4vbGliL2VuZ2luZSc7XG5leHBvcnQgeyBSTEVOYXJyYXRpdmVGbG93UmVjb3JkZXIgfSBmcm9tICcuL2xpYi9lbmdpbmUnO1xuZXhwb3J0IHsgU2VwYXJhdGVOYXJyYXRpdmVGbG93UmVjb3JkZXIgfSBmcm9tICcuL2xpYi9lbmdpbmUnO1xuZXhwb3J0IHsgU2lsZW50TmFycmF0aXZlRmxvd1JlY29yZGVyIH0gZnJvbSAnLi9saWIvZW5naW5lJztcbmV4cG9ydCB7IFdpbmRvd2VkTmFycmF0aXZlRmxvd1JlY29yZGVyIH0gZnJvbSAnLi9saWIvZW5naW5lJztcblxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuLy8gTWVtb3J5IOKAlCBTY29wZUZhY3RvcnkgdHlwZSAobmVlZGVkIGZvciBGbG93Q2hhcnRFeGVjdXRvciBjb25zdHJ1Y3Rvcilcbi8vID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuZXhwb3J0IHR5cGUgeyBSdW5PcHRpb25zIH0gZnJvbSAnLi9saWIvZW5naW5lJztcbmV4cG9ydCB0eXBlIHsgU2NvcGVGYWN0b3J5IH0gZnJvbSAnLi9saWIvbWVtb3J5JztcblxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuLy8gQ29udHJhY3Qg4oCUIEkvTyBib3VuZGFyeSwgc2NoZW1hcywgYW5kIE9wZW5BUEkgZ2VuZXJhdGlvblxuLy8gPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG5leHBvcnQgdHlwZSB7XG4gIEZsb3dDaGFydENvbnRyYWN0LFxuICBGbG93Q2hhcnRDb250cmFjdE9wdGlvbnMsXG4gIEpzb25TY2hlbWEsXG4gIE9wZW5BUElPcHRpb25zLFxuICBPcGVuQVBJU3BlYyxcbn0gZnJvbSAnLi9saWIvY29udHJhY3QnO1xuZXhwb3J0IHsgZGVmaW5lQ29udHJhY3QgfSBmcm9tICcuL2xpYi9jb250cmFjdCc7XG5leHBvcnQgeyBub3JtYWxpemVTY2hlbWEsIHpvZFRvSnNvblNjaGVtYSB9IGZyb20gJy4vbGliL2NvbnRyYWN0JztcbmV4cG9ydCB7IGdlbmVyYXRlT3BlbkFQSSB9IGZyb20gJy4vbGliL2NvbnRyYWN0JztcbiJdfQ==