footprintjs 0.2.0 → 0.2.2

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 (2) hide show
  1. package/README.md +43 -34
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -69,17 +69,27 @@ No one wrote those trace sentences. Stage functions just read and write scope &m
69
69
 
70
70
  ```typescript
71
71
  import {
72
- FlowChartBuilder, FlowChartExecutor, ScopeFacade,
73
- NarrativeRecorder, CombinedNarrativeBuilder,
74
- } from 'footprint';
72
+ FlowChartBuilder, FlowChartExecutor, ScopeFacade, toScopeFactory,
73
+ } from 'footprintjs';
74
+
75
+ // ── The application data ──────────────────────────────────────────────
76
+
77
+ const app = {
78
+ applicantName: 'Bob',
79
+ annualIncome: 42_000,
80
+ monthlyDebts: 2_100,
81
+ creditScore: 580,
82
+ employmentType: 'self-employed',
83
+ employmentYears: 1,
84
+ };
75
85
 
76
86
  // ── Stage functions: just do the work, no descriptions needed ──────────
77
87
 
78
- const receiveApplication = async (scope: ScopeFacade) => {
79
- scope.setValue('app', app); // objects, arrays, nested — all supported
88
+ const receiveApplication = (scope: ScopeFacade) => {
89
+ scope.setValue('app', app);
80
90
  };
81
91
 
82
- const pullCreditReport = async (scope: ScopeFacade) => {
92
+ const pullCreditReport = (scope: ScopeFacade) => {
83
93
  const { creditScore } = scope.getValue('app') as any;
84
94
  const tier = creditScore >= 740 ? 'excellent' : creditScore >= 670 ? 'good'
85
95
  : creditScore >= 580 ? 'fair' : 'poor';
@@ -87,7 +97,7 @@ const pullCreditReport = async (scope: ScopeFacade) => {
87
97
  scope.setValue('creditFlags', tier === 'fair' ? ['below-average credit'] : []);
88
98
  };
89
99
 
90
- const calculateDTI = async (scope: ScopeFacade) => {
100
+ const calculateDTI = (scope: ScopeFacade) => {
91
101
  const { annualIncome, monthlyDebts } = scope.getValue('app') as any;
92
102
  const dtiRatio = Math.round((monthlyDebts / (annualIncome / 12)) * 100) / 100;
93
103
  scope.setValue('dtiRatio', dtiRatio);
@@ -97,7 +107,15 @@ const calculateDTI = async (scope: ScopeFacade) => {
97
107
  dtiRatio > 0.43 ? [`DTI at ${Math.round(dtiRatio * 100)}% exceeds 43%`] : []);
98
108
  };
99
109
 
100
- const assessRisk = async (scope: ScopeFacade) => {
110
+ const verifyEmployment = (scope: ScopeFacade) => {
111
+ const { employmentType, employmentYears } = scope.getValue('app') as any;
112
+ const verified = employmentType !== 'self-employed' || employmentYears >= 2;
113
+ scope.setValue('employmentVerified', verified);
114
+ scope.setValue('employmentFlags',
115
+ !verified ? [`${employmentType}, ${employmentYears}yr < 2yr minimum`] : []);
116
+ };
117
+
118
+ const assessRisk = (scope: ScopeFacade) => {
101
119
  const creditTier = scope.getValue('creditTier') as string;
102
120
  const dtiStatus = scope.getValue('dtiStatus') as string;
103
121
  const verified = scope.getValue('employmentVerified') as boolean;
@@ -122,34 +140,22 @@ const chart = new FlowChartBuilder()
122
140
  .addFunction('VerifyEmployment', verifyEmployment)
123
141
  .addFunction('AssessRisk', assessRisk)
124
142
  .addDeciderFunction('LoanDecision', loanDecider as any)
125
- .addFunctionBranch('approved', 'ApproveApplication', approveFn)
126
- .addFunctionBranch('rejected', 'RejectApplication', rejectFn)
127
- .addFunctionBranch('manual-review', 'ManualReview', reviewFn)
143
+ .addFunctionBranch('approved', 'ApproveApplication', () => {})
144
+ .addFunctionBranch('rejected', 'RejectApplication', () => {})
145
+ .addFunctionBranch('manual-review', 'ManualReview', () => {})
128
146
  .setDefault('manual-review')
129
147
  .end()
130
148
  .build();
131
149
 
132
- // ── Instrument scope with NarrativeRecorder ────────────────────────────
133
-
134
- const recorder = new NarrativeRecorder({ id: 'loan', detail: 'full' });
135
-
136
- const scopeFactory = (ctx: any, stageName: string) => {
137
- const scope = new ScopeFacade(ctx, stageName);
138
- scope.attachRecorder(recorder);
139
- return scope;
140
- };
141
-
142
150
  // ── Run and get the narrative ──────────────────────────────────────────
143
151
 
144
- const executor = new FlowChartExecutor(chart, scopeFactory);
152
+ const executor = new FlowChartExecutor(chart, toScopeFactory(ScopeFacade));
145
153
  await executor.run();
146
154
 
147
- const flowNarrative = executor.getNarrative(); // control flow sentences
148
- const combined = new CombinedNarrativeBuilder();
149
- const narrative = combined.build(flowNarrative, recorder); // ← the trace above
155
+ const narrative = executor.getNarrative(); // the trace above
150
156
  ```
151
157
 
152
- The NarrativeRecorder observes every `getValue`/`setValue` call. The ControlFlowNarrativeGenerator captures stage transitions and decisions. CombinedNarrativeBuilder merges both into the trace. No descriptions were written by hand.
158
+ `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.
153
159
 
154
160
  ---
155
161
 
@@ -238,7 +244,7 @@ FootPrint has three moving parts:
238
244
  ### Linear
239
245
 
240
246
  ```typescript
241
- import { flowChart } from 'footprint';
247
+ import { flowChart } from 'footprintjs';
242
248
 
243
249
  flowChart('A', fnA)
244
250
  .addFunction('B', fnB)
@@ -365,7 +371,7 @@ Each stage receives a **scope** &mdash; a transactional interface to shared stat
365
371
  Extend `ScopeFacade` with domain-specific getters for type-safe reads:
366
372
 
367
373
  ```typescript
368
- import { ScopeFacade } from 'footprint';
374
+ import { ScopeFacade } from 'footprintjs';
369
375
 
370
376
  class LoanScope extends ScopeFacade {
371
377
  get creditScore(): number {
@@ -403,7 +409,7 @@ const total = scope.getValue('total'); // read
403
409
 
404
410
  ```typescript
405
411
  import { z } from 'zod';
406
- import { defineScopeFromZod } from 'footprint';
412
+ import { defineScopeFromZod } from 'footprintjs';
407
413
 
408
414
  const schema = z.object({
409
415
  creditScore: z.number(),
@@ -424,18 +430,19 @@ Recorders observe scope operations without modifying them. Attach multiple for d
424
430
 
425
431
  ```typescript
426
432
  import {
427
- ScopeFacade, DebugRecorder, MetricRecorder, NarrativeRecorder,
428
- } from 'footprint';
433
+ ScopeFacade, DebugRecorder, MetricRecorder,
434
+ } from 'footprintjs';
429
435
 
430
436
  const scopeFactory = (ctx: any, stageName: string) => {
431
437
  const scope = new ScopeFacade(ctx, stageName);
432
438
  scope.attachRecorder(new DebugRecorder({ verbosity: 'verbose' }));
433
439
  scope.attachRecorder(new MetricRecorder());
434
- scope.attachRecorder(new NarrativeRecorder({ id: 'trace', detail: 'full' }));
435
440
  return scope;
436
441
  };
437
442
  ```
438
443
 
444
+ > **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.
445
+
439
446
  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.
440
447
 
441
448
  ### Custom Recorders
@@ -443,7 +450,7 @@ Error isolation is built in: if a recorder throws, the error is routed to `onErr
443
450
  Implement any subset of six hooks: `onRead`, `onWrite`, `onCommit`, `onError`, `onStageStart`, `onStageEnd`.
444
451
 
445
452
  ```typescript
446
- import { Recorder, WriteEvent } from 'footprint';
453
+ import { Recorder, WriteEvent } from 'footprintjs';
447
454
 
448
455
  class AuditRecorder implements Recorder {
449
456
  readonly id = 'audit';
@@ -591,7 +598,9 @@ An LLM reading this trace can immediately explain: *"The validation failed becau
591
598
  | Method | Description |
592
599
  |--------|-------------|
593
600
  | `run(options?)` | Execute the flowchart. Options: `{ signal?, timeoutMs? }` |
594
- | `getNarrative()` | Control-flow narrative sentences |
601
+ | `getNarrative()` | Combined narrative (flow + data) with ScopeFacade; flow-only otherwise |
602
+ | `getFlowNarrative()` | Flow-only narrative sentences |
603
+ | `getNarrativeEntries()` | Structured `CombinedNarrativeEntry[]` for programmatic use |
595
604
  | `getSnapshot()` | Full execution tree + state |
596
605
  | `getExtractedResults()` | Extractor results map |
597
606
  | `getEnrichedResults()` | Enriched snapshots (scope state, debug info, output) |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "footprintjs",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Turn your whiteboard flowchart into running code — with automatic causal traces for LLM reasoning",
5
5
  "license": "MIT",
6
6
  "author": "Sanjay Krishna Anbalagan",