footprintjs 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 (251) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +658 -0
  3. package/dist/advanced.js +84 -0
  4. package/dist/esm/advanced.js +26 -0
  5. package/dist/esm/index.js +30 -0
  6. package/dist/esm/lib/builder/FlowChartBuilder.js +809 -0
  7. package/dist/esm/lib/builder/index.js +12 -0
  8. package/dist/esm/lib/builder/types.js +9 -0
  9. package/dist/esm/lib/engine/graph/StageNode.js +38 -0
  10. package/dist/esm/lib/engine/graph/index.js +2 -0
  11. package/dist/esm/lib/engine/handlers/ChildrenExecutor.js +116 -0
  12. package/dist/esm/lib/engine/handlers/ContinuationResolver.js +121 -0
  13. package/dist/esm/lib/engine/handlers/DeciderHandler.js +78 -0
  14. package/dist/esm/lib/engine/handlers/ExtractorRunner.js +117 -0
  15. package/dist/esm/lib/engine/handlers/NodeResolver.js +72 -0
  16. package/dist/esm/lib/engine/handlers/RuntimeStructureManager.js +154 -0
  17. package/dist/esm/lib/engine/handlers/SelectorHandler.js +79 -0
  18. package/dist/esm/lib/engine/handlers/StageRunner.js +86 -0
  19. package/dist/esm/lib/engine/handlers/SubflowExecutor.js +322 -0
  20. package/dist/esm/lib/engine/handlers/SubflowInputMapper.js +113 -0
  21. package/dist/esm/lib/engine/handlers/index.js +23 -0
  22. package/dist/esm/lib/engine/index.js +18 -0
  23. package/dist/esm/lib/engine/narrative/CombinedNarrativeBuilder.js +158 -0
  24. package/dist/esm/lib/engine/narrative/ControlFlowNarrativeGenerator.js +90 -0
  25. package/dist/esm/lib/engine/narrative/NullControlFlowNarrativeGenerator.js +24 -0
  26. package/dist/esm/lib/engine/narrative/index.js +4 -0
  27. package/dist/esm/lib/engine/narrative/types.js +11 -0
  28. package/dist/esm/lib/engine/traversal/FlowchartTraverser.js +419 -0
  29. package/dist/esm/lib/engine/traversal/index.js +2 -0
  30. package/dist/esm/lib/engine/types.js +16 -0
  31. package/dist/esm/lib/memory/DiagnosticCollector.js +44 -0
  32. package/dist/esm/lib/memory/EventLog.js +44 -0
  33. package/dist/esm/lib/memory/SharedMemory.js +53 -0
  34. package/dist/esm/lib/memory/StageContext.js +221 -0
  35. package/dist/esm/lib/memory/TransactionBuffer.js +66 -0
  36. package/dist/esm/lib/memory/index.js +16 -0
  37. package/dist/esm/lib/memory/types.js +7 -0
  38. package/dist/esm/lib/memory/utils.js +155 -0
  39. package/dist/esm/lib/runner/ExecutionRuntime.js +64 -0
  40. package/dist/esm/lib/runner/FlowChartExecutor.js +108 -0
  41. package/dist/esm/lib/runner/index.js +3 -0
  42. package/dist/esm/lib/scope/ScopeFacade.js +175 -0
  43. package/dist/esm/lib/scope/index.js +19 -0
  44. package/dist/esm/lib/scope/protection/createProtectedScope.js +59 -0
  45. package/dist/esm/lib/scope/protection/index.js +3 -0
  46. package/dist/esm/lib/scope/protection/types.js +8 -0
  47. package/dist/esm/lib/scope/providers/baseStateCompatible.js +29 -0
  48. package/dist/esm/lib/scope/providers/guards.js +43 -0
  49. package/dist/esm/lib/scope/providers/index.js +7 -0
  50. package/dist/esm/lib/scope/providers/providers.js +18 -0
  51. package/dist/esm/lib/scope/providers/registry.js +36 -0
  52. package/dist/esm/lib/scope/providers/resolve.js +11 -0
  53. package/dist/esm/lib/scope/providers/types.js +8 -0
  54. package/dist/esm/lib/scope/recorders/DebugRecorder.js +81 -0
  55. package/dist/esm/lib/scope/recorders/MetricRecorder.js +81 -0
  56. package/dist/esm/lib/scope/recorders/NarrativeRecorder.js +167 -0
  57. package/dist/esm/lib/scope/recorders/index.js +4 -0
  58. package/dist/esm/lib/scope/state/installResolvers.js +15 -0
  59. package/dist/esm/lib/scope/state/zod/defineScopeFromZod.js +14 -0
  60. package/dist/esm/lib/scope/state/zod/index.js +5 -0
  61. package/dist/esm/lib/scope/state/zod/resolver.js +28 -0
  62. package/dist/esm/lib/scope/state/zod/schema/builder.js +16 -0
  63. package/dist/esm/lib/scope/state/zod/scopeFactory.js +156 -0
  64. package/dist/esm/lib/scope/state/zod/utils/validateHelper.js +97 -0
  65. package/dist/esm/lib/scope/types.js +9 -0
  66. package/dist/esm/types/advanced.d.ts +35 -0
  67. package/dist/esm/types/index.d.ts +22 -0
  68. package/dist/esm/types/lib/builder/FlowChartBuilder.d.ts +121 -0
  69. package/dist/esm/types/lib/builder/index.d.ts +9 -0
  70. package/dist/esm/types/lib/builder/types.d.ts +152 -0
  71. package/dist/esm/types/lib/engine/graph/StageNode.d.ts +78 -0
  72. package/dist/esm/types/lib/engine/graph/index.d.ts +2 -0
  73. package/dist/esm/types/lib/engine/handlers/ChildrenExecutor.d.ts +33 -0
  74. package/dist/esm/types/lib/engine/handlers/ContinuationResolver.d.ts +57 -0
  75. package/dist/esm/types/lib/engine/handlers/DeciderHandler.d.ts +34 -0
  76. package/dist/esm/types/lib/engine/handlers/ExtractorRunner.d.ts +41 -0
  77. package/dist/esm/types/lib/engine/handlers/NodeResolver.d.ts +26 -0
  78. package/dist/esm/types/lib/engine/handlers/RuntimeStructureManager.d.ts +36 -0
  79. package/dist/esm/types/lib/engine/handlers/SelectorHandler.d.ts +26 -0
  80. package/dist/esm/types/lib/engine/handlers/StageRunner.d.ts +17 -0
  81. package/dist/esm/types/lib/engine/handlers/SubflowExecutor.d.ts +57 -0
  82. package/dist/esm/types/lib/engine/handlers/SubflowInputMapper.d.ts +40 -0
  83. package/dist/esm/types/lib/engine/handlers/index.d.ts +15 -0
  84. package/dist/esm/types/lib/engine/index.d.ts +16 -0
  85. package/dist/esm/types/lib/engine/narrative/CombinedNarrativeBuilder.d.ts +33 -0
  86. package/dist/esm/types/lib/engine/narrative/ControlFlowNarrativeGenerator.d.ts +29 -0
  87. package/dist/esm/types/lib/engine/narrative/NullControlFlowNarrativeGenerator.d.ts +21 -0
  88. package/dist/esm/types/lib/engine/narrative/index.d.ts +5 -0
  89. package/dist/esm/types/lib/engine/narrative/types.d.ts +33 -0
  90. package/dist/esm/types/lib/engine/traversal/FlowchartTraverser.d.ts +87 -0
  91. package/dist/esm/types/lib/engine/traversal/index.d.ts +2 -0
  92. package/dist/esm/types/lib/engine/types.d.ts +208 -0
  93. package/dist/esm/types/lib/memory/DiagnosticCollector.d.ts +33 -0
  94. package/dist/esm/types/lib/memory/EventLog.d.ts +27 -0
  95. package/dist/esm/types/lib/memory/SharedMemory.d.ts +34 -0
  96. package/dist/esm/types/lib/memory/StageContext.d.ts +74 -0
  97. package/dist/esm/types/lib/memory/TransactionBuffer.d.ts +38 -0
  98. package/dist/esm/types/lib/memory/index.d.ts +13 -0
  99. package/dist/esm/types/lib/memory/types.d.ts +60 -0
  100. package/dist/esm/types/lib/memory/utils.d.ts +67 -0
  101. package/dist/esm/types/lib/runner/ExecutionRuntime.d.ts +39 -0
  102. package/dist/esm/types/lib/runner/FlowChartExecutor.d.ts +34 -0
  103. package/dist/esm/types/lib/runner/index.d.ts +3 -0
  104. package/dist/esm/types/lib/scope/ScopeFacade.d.ts +47 -0
  105. package/dist/esm/types/lib/scope/index.d.ts +23 -0
  106. package/dist/esm/types/lib/scope/protection/createProtectedScope.d.ts +9 -0
  107. package/dist/esm/types/lib/scope/protection/index.d.ts +2 -0
  108. package/dist/esm/types/lib/scope/protection/types.d.ts +13 -0
  109. package/dist/esm/types/lib/scope/providers/baseStateCompatible.d.ts +26 -0
  110. package/dist/esm/types/lib/scope/providers/guards.d.ts +14 -0
  111. package/dist/esm/types/lib/scope/providers/index.d.ts +6 -0
  112. package/dist/esm/types/lib/scope/providers/providers.d.ts +8 -0
  113. package/dist/esm/types/lib/scope/providers/registry.d.ts +11 -0
  114. package/dist/esm/types/lib/scope/providers/resolve.d.ts +8 -0
  115. package/dist/esm/types/lib/scope/providers/types.d.ts +40 -0
  116. package/dist/esm/types/lib/scope/recorders/DebugRecorder.d.ts +35 -0
  117. package/dist/esm/types/lib/scope/recorders/MetricRecorder.d.ts +36 -0
  118. package/dist/esm/types/lib/scope/recorders/NarrativeRecorder.d.ts +45 -0
  119. package/dist/esm/types/lib/scope/recorders/index.d.ts +7 -0
  120. package/dist/esm/types/lib/scope/state/installResolvers.d.ts +4 -0
  121. package/dist/esm/types/lib/scope/state/zod/defineScopeFromZod.d.ts +9 -0
  122. package/dist/esm/types/lib/scope/state/zod/index.d.ts +5 -0
  123. package/dist/esm/types/lib/scope/state/zod/resolver.d.ts +5 -0
  124. package/dist/esm/types/lib/scope/state/zod/schema/builder.d.ts +12 -0
  125. package/dist/esm/types/lib/scope/state/zod/scopeFactory.d.ts +9 -0
  126. package/dist/esm/types/lib/scope/state/zod/utils/validateHelper.d.ts +10 -0
  127. package/dist/esm/types/lib/scope/types.d.ts +53 -0
  128. package/dist/index.js +42 -0
  129. package/dist/lib/builder/FlowChartBuilder.js +817 -0
  130. package/dist/lib/builder/index.js +20 -0
  131. package/dist/lib/builder/types.js +10 -0
  132. package/dist/lib/engine/graph/StageNode.js +42 -0
  133. package/dist/lib/engine/graph/index.js +6 -0
  134. package/dist/lib/engine/handlers/ChildrenExecutor.js +120 -0
  135. package/dist/lib/engine/handlers/ContinuationResolver.js +125 -0
  136. package/dist/lib/engine/handlers/DeciderHandler.js +82 -0
  137. package/dist/lib/engine/handlers/ExtractorRunner.js +121 -0
  138. package/dist/lib/engine/handlers/NodeResolver.js +76 -0
  139. package/dist/lib/engine/handlers/RuntimeStructureManager.js +159 -0
  140. package/dist/lib/engine/handlers/SelectorHandler.js +83 -0
  141. package/dist/lib/engine/handlers/StageRunner.js +90 -0
  142. package/dist/lib/engine/handlers/SubflowExecutor.js +326 -0
  143. package/dist/lib/engine/handlers/SubflowInputMapper.js +121 -0
  144. package/dist/lib/engine/handlers/index.js +42 -0
  145. package/dist/lib/engine/index.js +40 -0
  146. package/dist/lib/engine/narrative/CombinedNarrativeBuilder.js +162 -0
  147. package/dist/lib/engine/narrative/ControlFlowNarrativeGenerator.js +94 -0
  148. package/dist/lib/engine/narrative/NullControlFlowNarrativeGenerator.js +28 -0
  149. package/dist/lib/engine/narrative/index.js +10 -0
  150. package/dist/lib/engine/narrative/types.js +12 -0
  151. package/dist/lib/engine/traversal/FlowchartTraverser.js +423 -0
  152. package/dist/lib/engine/traversal/index.js +6 -0
  153. package/dist/lib/engine/types.js +19 -0
  154. package/dist/lib/memory/DiagnosticCollector.js +48 -0
  155. package/dist/lib/memory/EventLog.js +48 -0
  156. package/dist/lib/memory/SharedMemory.js +60 -0
  157. package/dist/lib/memory/StageContext.js +225 -0
  158. package/dist/lib/memory/TransactionBuffer.js +73 -0
  159. package/dist/lib/memory/index.js +34 -0
  160. package/dist/lib/memory/types.js +8 -0
  161. package/dist/lib/memory/utils.js +170 -0
  162. package/dist/lib/runner/ExecutionRuntime.js +68 -0
  163. package/dist/lib/runner/FlowChartExecutor.js +112 -0
  164. package/dist/lib/runner/index.js +8 -0
  165. package/dist/lib/scope/ScopeFacade.js +179 -0
  166. package/dist/lib/scope/index.js +44 -0
  167. package/dist/lib/scope/protection/createProtectedScope.js +64 -0
  168. package/dist/lib/scope/protection/index.js +8 -0
  169. package/dist/lib/scope/protection/types.js +9 -0
  170. package/dist/lib/scope/providers/baseStateCompatible.js +33 -0
  171. package/dist/lib/scope/providers/guards.js +49 -0
  172. package/dist/lib/scope/providers/index.js +21 -0
  173. package/dist/lib/scope/providers/providers.js +23 -0
  174. package/dist/lib/scope/providers/registry.js +42 -0
  175. package/dist/lib/scope/providers/resolve.js +16 -0
  176. package/dist/lib/scope/providers/types.js +9 -0
  177. package/dist/lib/scope/recorders/DebugRecorder.js +85 -0
  178. package/dist/lib/scope/recorders/MetricRecorder.js +85 -0
  179. package/dist/lib/scope/recorders/NarrativeRecorder.js +171 -0
  180. package/dist/lib/scope/recorders/index.js +10 -0
  181. package/dist/lib/scope/state/installResolvers.js +19 -0
  182. package/dist/lib/scope/state/zod/defineScopeFromZod.js +18 -0
  183. package/dist/lib/scope/state/zod/index.js +13 -0
  184. package/dist/lib/scope/state/zod/resolver.js +31 -0
  185. package/dist/lib/scope/state/zod/schema/builder.js +21 -0
  186. package/dist/lib/scope/state/zod/scopeFactory.js +160 -0
  187. package/dist/lib/scope/state/zod/utils/validateHelper.js +104 -0
  188. package/dist/lib/scope/types.js +10 -0
  189. package/dist/types/advanced.d.ts +35 -0
  190. package/dist/types/index.d.ts +22 -0
  191. package/dist/types/lib/builder/FlowChartBuilder.d.ts +121 -0
  192. package/dist/types/lib/builder/index.d.ts +9 -0
  193. package/dist/types/lib/builder/types.d.ts +152 -0
  194. package/dist/types/lib/engine/graph/StageNode.d.ts +78 -0
  195. package/dist/types/lib/engine/graph/index.d.ts +2 -0
  196. package/dist/types/lib/engine/handlers/ChildrenExecutor.d.ts +33 -0
  197. package/dist/types/lib/engine/handlers/ContinuationResolver.d.ts +57 -0
  198. package/dist/types/lib/engine/handlers/DeciderHandler.d.ts +34 -0
  199. package/dist/types/lib/engine/handlers/ExtractorRunner.d.ts +41 -0
  200. package/dist/types/lib/engine/handlers/NodeResolver.d.ts +26 -0
  201. package/dist/types/lib/engine/handlers/RuntimeStructureManager.d.ts +36 -0
  202. package/dist/types/lib/engine/handlers/SelectorHandler.d.ts +26 -0
  203. package/dist/types/lib/engine/handlers/StageRunner.d.ts +17 -0
  204. package/dist/types/lib/engine/handlers/SubflowExecutor.d.ts +57 -0
  205. package/dist/types/lib/engine/handlers/SubflowInputMapper.d.ts +40 -0
  206. package/dist/types/lib/engine/handlers/index.d.ts +15 -0
  207. package/dist/types/lib/engine/index.d.ts +16 -0
  208. package/dist/types/lib/engine/narrative/CombinedNarrativeBuilder.d.ts +33 -0
  209. package/dist/types/lib/engine/narrative/ControlFlowNarrativeGenerator.d.ts +29 -0
  210. package/dist/types/lib/engine/narrative/NullControlFlowNarrativeGenerator.d.ts +21 -0
  211. package/dist/types/lib/engine/narrative/index.d.ts +5 -0
  212. package/dist/types/lib/engine/narrative/types.d.ts +33 -0
  213. package/dist/types/lib/engine/traversal/FlowchartTraverser.d.ts +87 -0
  214. package/dist/types/lib/engine/traversal/index.d.ts +2 -0
  215. package/dist/types/lib/engine/types.d.ts +208 -0
  216. package/dist/types/lib/memory/DiagnosticCollector.d.ts +33 -0
  217. package/dist/types/lib/memory/EventLog.d.ts +27 -0
  218. package/dist/types/lib/memory/SharedMemory.d.ts +34 -0
  219. package/dist/types/lib/memory/StageContext.d.ts +74 -0
  220. package/dist/types/lib/memory/TransactionBuffer.d.ts +38 -0
  221. package/dist/types/lib/memory/index.d.ts +13 -0
  222. package/dist/types/lib/memory/types.d.ts +60 -0
  223. package/dist/types/lib/memory/utils.d.ts +67 -0
  224. package/dist/types/lib/runner/ExecutionRuntime.d.ts +39 -0
  225. package/dist/types/lib/runner/FlowChartExecutor.d.ts +34 -0
  226. package/dist/types/lib/runner/index.d.ts +3 -0
  227. package/dist/types/lib/scope/ScopeFacade.d.ts +47 -0
  228. package/dist/types/lib/scope/index.d.ts +23 -0
  229. package/dist/types/lib/scope/protection/createProtectedScope.d.ts +9 -0
  230. package/dist/types/lib/scope/protection/index.d.ts +2 -0
  231. package/dist/types/lib/scope/protection/types.d.ts +13 -0
  232. package/dist/types/lib/scope/providers/baseStateCompatible.d.ts +26 -0
  233. package/dist/types/lib/scope/providers/guards.d.ts +14 -0
  234. package/dist/types/lib/scope/providers/index.d.ts +6 -0
  235. package/dist/types/lib/scope/providers/providers.d.ts +8 -0
  236. package/dist/types/lib/scope/providers/registry.d.ts +11 -0
  237. package/dist/types/lib/scope/providers/resolve.d.ts +8 -0
  238. package/dist/types/lib/scope/providers/types.d.ts +40 -0
  239. package/dist/types/lib/scope/recorders/DebugRecorder.d.ts +35 -0
  240. package/dist/types/lib/scope/recorders/MetricRecorder.d.ts +36 -0
  241. package/dist/types/lib/scope/recorders/NarrativeRecorder.d.ts +45 -0
  242. package/dist/types/lib/scope/recorders/index.d.ts +7 -0
  243. package/dist/types/lib/scope/state/installResolvers.d.ts +4 -0
  244. package/dist/types/lib/scope/state/zod/defineScopeFromZod.d.ts +9 -0
  245. package/dist/types/lib/scope/state/zod/index.d.ts +5 -0
  246. package/dist/types/lib/scope/state/zod/resolver.d.ts +5 -0
  247. package/dist/types/lib/scope/state/zod/schema/builder.d.ts +12 -0
  248. package/dist/types/lib/scope/state/zod/scopeFactory.d.ts +9 -0
  249. package/dist/types/lib/scope/state/zod/utils/validateHelper.d.ts +10 -0
  250. package/dist/types/lib/scope/types.d.ts +53 -0
  251. package/package.json +148 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-present Sanjay Krishna Anbalagan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,658 @@
1
+ <p align="center">
2
+ <h1 align="center">FootPrint</h1>
3
+ <p align="center">
4
+ <strong>Turn your whiteboard flowchart into running code &mdash; with automatic causal traces.</strong>
5
+ </p>
6
+ </p>
7
+
8
+ <p align="center">
9
+ <a href="https://github.com/footprintjs/footPrint/actions"><img src="https://github.com/footprintjs/footPrint/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
10
+ <a href="https://www.npmjs.com/package/footprint"><img src="https://img.shields.io/npm/v/footprint.svg?style=flat" alt="npm version"></a>
11
+ <a href="https://github.com/footprintjs/footPrint/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT License"></a>
12
+ <a href="https://www.npmjs.com/package/footprint"><img src="https://img.shields.io/npm/dm/footprint.svg" alt="Downloads"></a>
13
+ <a href="https://footprintjs.github.io/footprint-playground/"><img src="https://img.shields.io/badge/Try_it-Interactive_Playground-6366f1?style=flat&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJ3aGl0ZSI+PHBhdGggZD0iTTggNXYxNGwxMS03eiIvPjwvc3ZnPg==" alt="Interactive Playground"></a>
14
+ </p>
15
+
16
+ <br>
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.
19
+
20
+ ```bash
21
+ npm install footprint
22
+ ```
23
+
24
+ ---
25
+
26
+ ## Why causal traces?
27
+
28
+ A loan application pipeline rejects Bob. The user asks: **"Why was I rejected?"**
29
+
30
+ Without FootPrint, the LLM must reconstruct the reasoning from disconnected logs &mdash; expensive, slow, and unreliable. With FootPrint, the runtime produces this trace automatically from what the code actually did:
31
+
32
+ ```
33
+ Stage 1: The process began with ReceiveApplication.
34
+ Step 1: Write app = {applicantName, annualIncome, monthlyDebts, creditScore, …}
35
+ Stage 2: Next, it moved on to PullCreditReport.
36
+ Step 1: Read app = {applicantName, annualIncome, monthlyDebts, creditScore, …}
37
+ Step 2: Write creditTier = "fair"
38
+ Step 3: Write creditFlags = (1 item)
39
+ Stage 3: Next, it moved on to CalculateDTI.
40
+ Step 1: Read app = {applicantName, annualIncome, monthlyDebts, creditScore, …}
41
+ Step 2: Write dtiRatio = 0.6
42
+ Step 3: Write dtiPercent = 60
43
+ Step 4: Write dtiStatus = "excessive"
44
+ Step 5: Write dtiFlags = (1 item)
45
+ Stage 4: Next, it moved on to VerifyEmployment.
46
+ Step 1: Read app = {applicantName, annualIncome, monthlyDebts, creditScore, …}
47
+ Step 2: Write employmentVerified = true
48
+ Step 3: Write employmentFlags = (1 item)
49
+ Stage 5: Next, it moved on to AssessRisk.
50
+ Step 1: Read creditTier = "fair"
51
+ Step 2: Read dtiStatus = "excessive"
52
+ Step 3: Read employmentVerified = true
53
+ Step 4: Write riskTier = "high"
54
+ Step 5: Write riskFactors = (1 item)
55
+ [Condition]: A decision was made, and the path taken was RejectApplication.
56
+ ```
57
+
58
+ The LLM backtracks through the trace: `riskTier="high"` &larr; `dtiStatus="excessive"` &larr; `dtiPercent=60` &larr; `app.monthlyDebts=2100`. Every variable links to its cause. Cheaper model, fewer tokens, no hallucination:
59
+
60
+ > **LLM:** "Your application was rejected because your credit score of 580 falls in the 'fair' tier, your debt-to-income ratio of 60% exceeds the 43% maximum, and your self-employment tenure of 1 year is below the 2-year minimum. These factors combined placed you in the 'high' risk tier."
61
+
62
+ That answer came from the trace &mdash; not from the LLM's imagination.
63
+
64
+ ---
65
+
66
+ ## The code that produced it
67
+
68
+ No one wrote those trace sentences. Stage functions just read and write scope &mdash; the narrative builds itself:
69
+
70
+ ```typescript
71
+ import {
72
+ FlowChartBuilder, FlowChartExecutor, ScopeFacade,
73
+ NarrativeRecorder, CombinedNarrativeBuilder,
74
+ } from 'footprint';
75
+
76
+ // ── Stage functions: just do the work, no descriptions needed ──────────
77
+
78
+ const receiveApplication = async (scope: ScopeFacade) => {
79
+ scope.setValue('app', app); // objects, arrays, nested — all supported
80
+ };
81
+
82
+ const pullCreditReport = async (scope: ScopeFacade) => {
83
+ const { creditScore } = scope.getValue('app') as any;
84
+ const tier = creditScore >= 740 ? 'excellent' : creditScore >= 670 ? 'good'
85
+ : creditScore >= 580 ? 'fair' : 'poor';
86
+ scope.setValue('creditTier', tier);
87
+ scope.setValue('creditFlags', tier === 'fair' ? ['below-average credit'] : []);
88
+ };
89
+
90
+ const calculateDTI = async (scope: ScopeFacade) => {
91
+ const { annualIncome, monthlyDebts } = scope.getValue('app') as any;
92
+ const dtiRatio = Math.round((monthlyDebts / (annualIncome / 12)) * 100) / 100;
93
+ scope.setValue('dtiRatio', dtiRatio);
94
+ scope.setValue('dtiPercent', Math.round(dtiRatio * 100));
95
+ scope.setValue('dtiStatus', dtiRatio > 0.43 ? 'excessive' : 'healthy');
96
+ scope.setValue('dtiFlags',
97
+ dtiRatio > 0.43 ? [`DTI at ${Math.round(dtiRatio * 100)}% exceeds 43%`] : []);
98
+ };
99
+
100
+ const assessRisk = async (scope: ScopeFacade) => {
101
+ const creditTier = scope.getValue('creditTier') as string;
102
+ const dtiStatus = scope.getValue('dtiStatus') as string;
103
+ const verified = scope.getValue('employmentVerified') as boolean;
104
+ const riskTier = (!verified || dtiStatus === 'excessive' || creditTier === 'poor')
105
+ ? 'high' : 'low';
106
+ scope.setValue('riskTier', riskTier);
107
+ scope.setValue('riskFactors', [/* collected flags */]);
108
+ };
109
+
110
+ const loanDecider = (scope: ScopeFacade): string => {
111
+ const tier = scope.getValue('riskTier') as string;
112
+ return tier === 'low' ? 'approved' : tier === 'high' ? 'rejected' : 'manual-review';
113
+ };
114
+
115
+ // ── Build the flow ─────────────────────────────────────────────────────
116
+
117
+ const chart = new FlowChartBuilder()
118
+ .setEnableNarrative()
119
+ .start('ReceiveApplication', receiveApplication)
120
+ .addFunction('PullCreditReport', pullCreditReport)
121
+ .addFunction('CalculateDTI', calculateDTI)
122
+ .addFunction('VerifyEmployment', verifyEmployment)
123
+ .addFunction('AssessRisk', assessRisk)
124
+ .addDeciderFunction('LoanDecision', loanDecider as any)
125
+ .addFunctionBranch('approved', 'ApproveApplication', approveFn)
126
+ .addFunctionBranch('rejected', 'RejectApplication', rejectFn)
127
+ .addFunctionBranch('manual-review', 'ManualReview', reviewFn)
128
+ .setDefault('manual-review')
129
+ .end()
130
+ .build();
131
+
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
+ // ── Run and get the narrative ──────────────────────────────────────────
143
+
144
+ const executor = new FlowChartExecutor(chart, scopeFactory);
145
+ await executor.run();
146
+
147
+ const flowNarrative = executor.getNarrative(); // control flow sentences
148
+ const combined = new CombinedNarrativeBuilder();
149
+ const narrative = combined.build(flowNarrative, recorder); // ← the trace above
150
+ ```
151
+
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.
153
+
154
+ ---
155
+
156
+ ## Two narratives, both auto-generated
157
+
158
+ ### Build-time: tool description for LLM tool selection
159
+
160
+ When you call `.build()`, FootPrint auto-generates `chart.description` &mdash; a structural summary of what the pipeline does:
161
+
162
+ ```
163
+ FlowChart: ReceiveApplication
164
+ Steps:
165
+ 1. ReceiveApplication
166
+ 2. PullCreditReport
167
+ 3. CalculateDTI
168
+ 4. VerifyEmployment
169
+ 5. AssessRisk
170
+ 6. LoanDecision — Decides between: approved, rejected, manual-review
171
+ ```
172
+
173
+ Register this as a **tool description**. When an LLM agent has multiple tools to choose from, it can read each tool's description and pick the right one &mdash; without you writing tool descriptions by hand:
174
+
175
+ ```typescript
176
+ // Each flowchart's auto-generated description becomes the tool description
177
+ const tools = [
178
+ {
179
+ name: 'process-loan',
180
+ description: loanChart.description,
181
+ // FlowChart: ReceiveApplication
182
+ // Steps:
183
+ // 1. ReceiveApplication
184
+ // 2. PullCreditReport ...
185
+ // 6. LoanDecision — Decides between: approved, rejected, manual-review
186
+ handler: (input) => runWithChart(loanChart, input),
187
+ },
188
+ {
189
+ name: 'check-fraud',
190
+ description: fraudChart.description,
191
+ // FlowChart: AnalyzeTransaction
192
+ // Steps:
193
+ // 1. AnalyzeTransaction
194
+ // 2. CheckVelocity
195
+ // 3. CheckGeolocation
196
+ // 4. FraudDecision — Decides between: allow, block, review
197
+ handler: (input) => runWithChart(fraudChart, input),
198
+ },
199
+ ];
200
+
201
+ // The LLM sees exactly what each tool does — enough to pick the right one.
202
+ // No manual description writing. The structure IS the description.
203
+ ```
204
+
205
+ ### Runtime: causal trace for LLM explanation
206
+
207
+ 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.
208
+
209
+ **Build-time tells the LLM which tool to use. Runtime tells the LLM what the tool did.**
210
+
211
+ ---
212
+
213
+ ## How it works
214
+
215
+ FootPrint has three moving parts:
216
+
217
+ 1. **Scope** &mdash; Transactional state shared across stages. Writes are buffered and committed atomically. Recorders observe every read/write without modifying behavior.
218
+ 2. **Builder** &mdash; Fluent API that compiles your flowchart into a traversable node tree.
219
+ 3. **Engine** &mdash; DFS traversal that executes stages, manages state, and generates narrative.
220
+
221
+ ```
222
+ ┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐
223
+ │ FlowChartBuilder │─────>│ FlowChart │─────>│ FlowChartExecutor │
224
+ │ (Build-time DSL) │ │ (Compiled Tree) │ │ (Runtime Engine) │
225
+ └─────────────────────┘ └─────────────────────┘ └─────────────────────┘
226
+ │ │ │
227
+ │ .start() │ .build() │ .run()
228
+ │ .addFunction() │ .description │ .getNarrative()
229
+ │ .addDeciderFunction() │ .stageDescriptions │ .getSnapshot()
230
+ │ .addSubFlowChart() │ │
231
+ └────────────────────────────┴────────────────────────────┘
232
+ ```
233
+
234
+ ---
235
+
236
+ ## Patterns
237
+
238
+ ### Linear
239
+
240
+ ```typescript
241
+ import { flowChart } from 'footprint';
242
+
243
+ flowChart('A', fnA)
244
+ .addFunction('B', fnB)
245
+ .addFunction('C', fnC)
246
+ .build();
247
+ ```
248
+
249
+ ### Parallel (Fork)
250
+
251
+ ```typescript
252
+ flowChart('Fetch', fetchFn)
253
+ .addListOfFunction([
254
+ { id: 'html', name: 'ParseHTML', fn: parseHTML },
255
+ { id: 'css', name: 'ParseCSS', fn: parseCSS },
256
+ { id: 'js', name: 'ParseJS', fn: parseJS },
257
+ ])
258
+ .addFunction('Merge', mergeFn)
259
+ .build();
260
+ ```
261
+
262
+ ### Conditional (Decider)
263
+
264
+ A decider reads from scope and returns the ID of exactly one branch to execute:
265
+
266
+ ```typescript
267
+ flowChart('Classify', classifyFn)
268
+ .addDeciderFunction('Route', (scope) => {
269
+ const type = scope.getValue('fulfillmentType');
270
+ return type === 'digital' ? 'digital' : 'physical';
271
+ })
272
+ .addFunctionBranch('digital', 'DigitalDelivery', digitalFn)
273
+ .addFunctionBranch('physical', 'ShipPackage', shipFn)
274
+ .setDefault('physical')
275
+ .end()
276
+ .build();
277
+ ```
278
+
279
+ ### Subflow Composition
280
+
281
+ Mount entire flowcharts as nodes in a larger workflow:
282
+
283
+ ```typescript
284
+ const faqFlow = flowChart('FAQ_Entry', faqEntryFn)
285
+ .addFunction('FAQ_Answer', faqAnswerFn)
286
+ .build();
287
+
288
+ const ragFlow = flowChart('RAG_Entry', ragEntryFn)
289
+ .addFunction('RAG_Retrieve', ragRetrieveFn)
290
+ .addFunction('RAG_Answer', ragAnswerFn)
291
+ .build();
292
+
293
+ const mainChart = flowChart('Router', routerFn)
294
+ .addSubFlowChart('faq', faqFlow, 'FAQ Handler')
295
+ .addSubFlowChart('rag', ragFlow, 'RAG Handler')
296
+ .addFunction('Aggregate', aggregateFn)
297
+ .build();
298
+ ```
299
+
300
+ ### Streaming (LLM)
301
+
302
+ ```typescript
303
+ const chart = flowChart('PreparePrompt', prepareFn)
304
+ .addStreamingFunction('AskLLM', 'llm-stream', askLLMFn)
305
+ .onStream((streamId, token) => process.stdout.write(token))
306
+ .onStreamEnd((streamId, fullText) => console.log('\nDone:', fullText))
307
+ .addFunction('ProcessResponse', processFn)
308
+ .build();
309
+ ```
310
+
311
+ ### Execution Control
312
+
313
+ Every stage receives `(scope, breakFn)`. Three levels of control:
314
+
315
+ ```typescript
316
+ // 1. breakFn() — Graceful stop: complete this stage, skip remaining stages
317
+ const validateInput = async (scope: ScopeFacade, breakFn: () => void) => {
318
+ const amount = scope.getValue('loanAmount') as number;
319
+ if (amount > 50_000) {
320
+ scope.setValue('rejection', 'Exceeds maximum loan amount');
321
+ breakFn(); // stage output is returned, no error — pipeline just stops
322
+ }
323
+ };
324
+
325
+ // 2. throw — Hard abort: stop immediately, propagate error to caller
326
+ const callExternalAPI = async (scope: ScopeFacade) => {
327
+ const response = await fetch(scope.getValue('apiUrl') as string);
328
+ if (response.status === 403) {
329
+ throw new Error('Access denied — cannot continue'); // executor.run() rejects
330
+ }
331
+ };
332
+
333
+ // 3. AbortSignal — External cancellation (see Cancellation & Timeout section)
334
+ await executor.run({ timeoutMs: 30_000 });
335
+ ```
336
+
337
+ | Mechanism | Trigger | Stage completes? | Returns |
338
+ |-----------|---------|-----------------|---------|
339
+ | `breakFn()` | Inside stage | Yes | Stage output (no error) |
340
+ | `throw` | Inside stage | No | Error propagates |
341
+ | `AbortSignal` | Outside pipeline | Races async | Error propagates |
342
+
343
+ ### Loops
344
+
345
+ ```typescript
346
+ flowChart('Init', initFn)
347
+ .addFunction('AskLLM', askFn, 'ask-llm')
348
+ .addFunction('ParseResponse', parseFn)
349
+ .addDeciderFunction('HasToolCalls', deciderFn)
350
+ .addFunctionBranch('yes', 'ExecuteTools', toolsFn)
351
+ .addFunctionBranch('no', 'Finalize', finalizeFn)
352
+ .end()
353
+ .loopTo('ask-llm') // loop back until no more tool calls
354
+ .build();
355
+ ```
356
+
357
+ ---
358
+
359
+ ## Scope
360
+
361
+ 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.
362
+
363
+ ### Typed Scope (Recommended)
364
+
365
+ Extend `ScopeFacade` with domain-specific getters for type-safe reads:
366
+
367
+ ```typescript
368
+ import { ScopeFacade } from 'footprint';
369
+
370
+ class LoanScope extends ScopeFacade {
371
+ get creditScore(): number {
372
+ return this.getValue('creditScore') as number;
373
+ }
374
+ get riskTier(): string {
375
+ return this.getValue('riskTier') as string;
376
+ }
377
+ get dtiStatus(): string {
378
+ return this.getValue('dtiStatus') as string;
379
+ }
380
+ }
381
+
382
+ const scopeFactory = (ctx: any, stageName: string) => new LoanScope(ctx, stageName);
383
+
384
+ // In stage functions:
385
+ const assessRisk = async (scope: LoanScope) => {
386
+ if (scope.creditScore < 600 || scope.dtiStatus === 'excessive') {
387
+ scope.setValue('riskTier', 'high'); // writes go through setValue
388
+ }
389
+ };
390
+ ```
391
+
392
+ > **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.
393
+
394
+ ### Raw Scope (Low-level)
395
+
396
+ ```typescript
397
+ scope.setValue('total', 79.98); // overwrite
398
+ scope.updateValue('config', { retries: 3 }); // deep merge
399
+ const total = scope.getValue('total'); // read
400
+ ```
401
+
402
+ ### Validated Scope (Zod)
403
+
404
+ ```typescript
405
+ import { z } from 'zod';
406
+ import { defineScopeFromZod } from 'footprint';
407
+
408
+ const schema = z.object({
409
+ creditScore: z.number(),
410
+ riskTier: z.string().optional(),
411
+ });
412
+
413
+ const scopeFactory = defineScopeFromZod(schema);
414
+ // Proxy-based: validates writes against the schema at runtime
415
+ ```
416
+
417
+ ---
418
+
419
+ ## Observability
420
+
421
+ ### Recorders
422
+
423
+ Recorders observe scope operations without modifying them. Attach multiple for different concerns:
424
+
425
+ ```typescript
426
+ import {
427
+ ScopeFacade, DebugRecorder, MetricRecorder, NarrativeRecorder,
428
+ } from 'footprint';
429
+
430
+ const scopeFactory = (ctx: any, stageName: string) => {
431
+ const scope = new ScopeFacade(ctx, stageName);
432
+ scope.attachRecorder(new DebugRecorder({ verbosity: 'verbose' }));
433
+ scope.attachRecorder(new MetricRecorder());
434
+ scope.attachRecorder(new NarrativeRecorder({ id: 'trace', detail: 'full' }));
435
+ return scope;
436
+ };
437
+ ```
438
+
439
+ 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
+
441
+ ### Custom Recorders
442
+
443
+ Implement any subset of six hooks: `onRead`, `onWrite`, `onCommit`, `onError`, `onStageStart`, `onStageEnd`.
444
+
445
+ ```typescript
446
+ import { Recorder, WriteEvent } from 'footprint';
447
+
448
+ class AuditRecorder implements Recorder {
449
+ readonly id = 'audit';
450
+ private writes: Array<{ stage: string; key: string; value: unknown }> = [];
451
+
452
+ onWrite(event: WriteEvent) {
453
+ this.writes.push({ stage: event.stageName, key: event.key, value: event.value });
454
+ }
455
+ getWrites() { return [...this.writes]; }
456
+ }
457
+ ```
458
+
459
+ ---
460
+
461
+ ## Key Features
462
+
463
+ | Feature | Description |
464
+ |---------|-------------|
465
+ | **Causal Traces** | Every read/write captured &mdash; LLMs backtrack through variables to find causes |
466
+ | **Auto Narrative** | Build-time descriptions for tool selection, runtime traces for explanation |
467
+ | **Not a DAG** | Supports loops, re-entry, and partial/resumed execution |
468
+ | **Parallel Fan-Out/In** | Fork pattern with automatic result aggregation (optional fail-fast) |
469
+ | **Cancellation** | AbortSignal and timeout support for long-running LLM calls |
470
+ | **Early Termination** | `breakFn()` stops the pipeline after the current stage |
471
+ | **Patch-Based State** | Atomic commits, safe merges, no race conditions |
472
+ | **Composable Subflows** | Mount entire flowcharts as nodes in larger workflows |
473
+ | **Streaming** | Built-in streaming stages for LLM token emission |
474
+ | **Pluggable Recorders** | DebugRecorder, MetricRecorder, NarrativeRecorder &mdash; or bring your own |
475
+
476
+ ---
477
+
478
+ ## Cancellation & Timeout
479
+
480
+ For LLM pipelines where API calls can hang, `FlowChartExecutor.run()` supports cooperative cancellation:
481
+
482
+ ```typescript
483
+ // Timeout: auto-abort after 30 seconds
484
+ const result = await executor.run({ timeoutMs: 30_000 });
485
+
486
+ // AbortSignal: cancel from outside
487
+ const controller = new AbortController();
488
+ setTimeout(() => controller.abort(), 10_000); // cancel after 10s
489
+ const result = await executor.run({ signal: controller.signal });
490
+ ```
491
+
492
+ The signal is checked before each stage starts and raced against async stage functions. Aborted executions throw with the signal's reason.
493
+
494
+ ## Fail-Fast Forks
495
+
496
+ By default, parallel children run to completion even if some fail (errors captured as `{ isError: true }`). For cases where you want immediate failure:
497
+
498
+ ```typescript
499
+ flowChart('Fetch', fetchFn)
500
+ .addListOfFunction([
501
+ { id: 'api1', name: 'CallAPI1', fn: api1Fn },
502
+ { id: 'api2', name: 'CallAPI2', fn: api2Fn },
503
+ ], { failFast: true }) // first child error rejects the whole fork
504
+ .build();
505
+ ```
506
+
507
+ ## Design: Error Handling
508
+
509
+ FootPrint's error handling is designed around one principle: **the trace must capture everything that happened, including failures**.
510
+
511
+ ### Who is responsible for what
512
+
513
+ | Layer | Responsibility |
514
+ |-------|---------------|
515
+ | **Stage function** | Business logic. Throws errors when invariants break. |
516
+ | **Engine** | Infrastructure. Catches errors, commits the trace, records error metadata, then re-throws. |
517
+ | **Consumer** | Wraps `executor.run()` in try/catch. Inspects `getSnapshot()` after failure for debugging. |
518
+
519
+ ### Commit-on-error: why it matters
520
+
521
+ When a stage throws, the engine calls `context.commit()` *before* re-throwing. This means:
522
+
523
+ ```typescript
524
+ const executor = new FlowChartExecutor(chart, scopeFactory);
525
+ try {
526
+ await executor.run();
527
+ } catch (error) {
528
+ // The snapshot captures everything up to the failure point
529
+ const snapshot = executor.getSnapshot();
530
+ // commitLog has entries for every stage that ran (including the one that failed)
531
+ // executionTree shows scope writes, error metadata, and flow decisions
532
+ // An LLM can use this to explain WHY the error happened
533
+ }
534
+ ```
535
+
536
+ 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:
537
+
538
+ - **Scope writes** made before the throw are preserved
539
+ - **Error metadata** (`stageExecutionError`) is recorded in the execution tree
540
+ - **Narrative** includes the error event (`"An error occurred at validate: ..."`)
541
+ - **Commit log** has an entry for the failed stage's state
542
+
543
+ ### Error narrative in practice
544
+
545
+ A validation pipeline fails. The trace tells the story:
546
+
547
+ ```
548
+ Stage 1: The process began with FetchData.
549
+ Step 1: Write rawPayload = {name: "Bob", age: -5}
550
+ Stage 2: Next, it moved on to Validate.
551
+ Step 1: Read rawPayload = {name: "Bob", age: -5}
552
+ An error occurred at Validate: Validation failed: age must be positive.
553
+ ```
554
+
555
+ 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.
556
+
557
+ ### What consumers can do
558
+
559
+ - **Retry with modifications**: Inspect the snapshot, fix inputs, re-run
560
+ - **Partial results**: Fork children that succeed still return results (default mode)
561
+ - **Fail-fast**: Opt into `failFast: true` when any child error should abort the whole fork
562
+ - **Timeout/cancel**: Use `timeoutMs` or `AbortSignal` for external cancellation
563
+ - **Post-mortem**: Feed the narrative + snapshot to an LLM for root-cause analysis
564
+
565
+ ---
566
+
567
+ ## API Reference
568
+
569
+ ### Builder
570
+
571
+ | Method | Description |
572
+ |--------|-------------|
573
+ | `start(name, fn?)` | Define root stage |
574
+ | `addFunction(name, fn?)` | Add linear next stage |
575
+ | `addListOfFunction(specs, opts?)` | Add parallel children (fork). Options: `{ failFast? }` |
576
+ | `addDeciderFunction(name, fn)` | Single-choice branching (returns one branch ID) |
577
+ | `addSelectorFunction(name, fn)` | Multi-choice branching (returns multiple branch IDs) |
578
+ | `addSubFlowChart(id, flow)` | Mount subflow as parallel child |
579
+ | `addSubFlowChartNext(id, flow)` | Mount subflow as linear next |
580
+ | `addStreamingFunction(name, streamId?, fn?)` | Add streaming stage |
581
+ | `addTraversalExtractor(fn)` | Register per-stage data extractor |
582
+ | `setEnableNarrative()` | Enable runtime narrative generation |
583
+ | `loopTo(stageId)` | Loop back to earlier stage |
584
+ | `build()` | Compile to FlowChart |
585
+ | `execute(scopeFactory)` | Build + run (convenience) |
586
+ | `toSpec()` | Export pure JSON (no functions) |
587
+ | `toMermaid()` | Generate Mermaid diagram |
588
+
589
+ ### Executor
590
+
591
+ | Method | Description |
592
+ |--------|-------------|
593
+ | `run(options?)` | Execute the flowchart. Options: `{ signal?, timeoutMs? }` |
594
+ | `getNarrative()` | Control-flow narrative sentences |
595
+ | `getSnapshot()` | Full execution tree + state |
596
+ | `getExtractedResults()` | Extractor results map |
597
+ | `getEnrichedResults()` | Enriched snapshots (scope state, debug info, output) |
598
+ | `getSubflowResults()` | Nested subflow execution data |
599
+ | `getRuntimeStructure()` | Serialized pipeline for visualization |
600
+
601
+ ---
602
+
603
+ ## How FootPrint Compares
604
+
605
+ | Aspect | async/await | FootPrint | Temporal / Step Functions |
606
+ |--------|-------------|-----------|--------------------------|
607
+ | **Control Flow** | Implicit in code | Explicit flowchart | External orchestrator |
608
+ | **State** | Manual/global | Scoped & transactional | Durable storage |
609
+ | **Debugging** | Stack traces | Time-travel replay | Event history |
610
+ | **LLM Narrative** | None | Automatic from operations | None |
611
+ | **Tool Descriptions** | Manual | Auto-generated from structure | Manual |
612
+ | **Complexity** | Low | Medium | High |
613
+
614
+ ---
615
+
616
+ ## Performance
617
+
618
+ Measured on Node v22, Apple Silicon. Run `npm run bench` to reproduce.
619
+
620
+ | Benchmark | Time | Detail |
621
+ |-----------|------|--------|
622
+ | **Write 1K keys** | 811µs | ~1.2M ops/s |
623
+ | **Write 10K keys** | 5.4ms | ~1.8M ops/s |
624
+ | **Read 100K keys** | 8.7ms | ~11.5M ops/s |
625
+ | **10 stages (linear)** | 106µs | 0.011ms/stage |
626
+ | **200 stages (linear)** | 4.7ms | 0.023ms/stage |
627
+ | **500 stages (linear)** | 20ms | 0.040ms/stage |
628
+ | **100 concurrent pipelines** | 2.3ms | 3-stage each |
629
+ | **1,000 concurrent pipelines** | 24ms | 3-stage each |
630
+ | **structuredClone 1KB** | 2µs | per call |
631
+ | **structuredClone 100KB** | 76µs | per call |
632
+ | **structuredClone 1MB** | 2.5ms | per call |
633
+ | **Time-travel 100 commits** | 75µs | 0.001ms/commit |
634
+ | **Time-travel 500 commits** | 385µs | 0.001ms/commit |
635
+ | **Commit with 100 writes** | 375µs | single stage |
636
+
637
+ **Bottom line:** A 200-stage pipeline completes in under 5ms. The primary cost at scale is `structuredClone` — keep state objects under 100KB per stage for sub-millisecond commit overhead.
638
+
639
+ ---
640
+
641
+ ## Architecture
642
+
643
+ FootPrint is five independent libraries, each usable standalone:
644
+
645
+ ```
646
+ src/lib/
647
+ ├── memory/ Transactional state (SharedMemory, StageContext, EventLog, TransactionBuffer)
648
+ ├── builder/ Fluent flowchart DSL (FlowChartBuilder, DeciderList, SelectorFnList)
649
+ ├── scope/ Scope facades, recorders, protection, Zod integration
650
+ ├── engine/ DFS traversal, handlers, narrative generators
651
+ └── runner/ Execution convenience (FlowChartExecutor, ExecutionRuntime)
652
+ ```
653
+
654
+ ---
655
+
656
+ ## License
657
+
658
+ [MIT](./LICENSE) &copy; [Sanjay Krishna Anbalagan](https://github.com/sanjay1909)