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.
- package/README.md +43 -34
- 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
|
-
|
|
74
|
-
|
|
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 =
|
|
79
|
-
scope.setValue('app', app);
|
|
88
|
+
const receiveApplication = (scope: ScopeFacade) => {
|
|
89
|
+
scope.setValue('app', app);
|
|
80
90
|
};
|
|
81
91
|
|
|
82
|
-
const pullCreditReport =
|
|
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 =
|
|
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
|
|
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',
|
|
126
|
-
.addFunctionBranch('rejected', 'RejectApplication',
|
|
127
|
-
.addFunctionBranch('manual-review', 'ManualReview',
|
|
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,
|
|
152
|
+
const executor = new FlowChartExecutor(chart, toScopeFactory(ScopeFacade));
|
|
145
153
|
await executor.run();
|
|
146
154
|
|
|
147
|
-
const
|
|
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
|
-
|
|
158
|
+
`enableNarrative()` auto-instruments every scope. The executor captures stage transitions, decisions, reads, and writes — 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 '
|
|
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** — 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 '
|
|
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 '
|
|
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,
|
|
428
|
-
} from '
|
|
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 '
|
|
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()` |
|
|
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