retold 4.0.4 → 4.0.8

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 (93) hide show
  1. package/.claude/launch.json +47 -0
  2. package/.claude/settings.local.json +45 -107
  3. package/CLAUDE.md +9 -7
  4. package/README.md +9 -7
  5. package/Retold-Modules-Manifest.json +616 -0
  6. package/docs/README.md +7 -1
  7. package/docs/{cover.md → _cover.md} +1 -1
  8. package/docs/_sidebar.md +30 -3
  9. package/docs/architecture/architecture.md +5 -2
  10. package/docs/architecture/dependencies/_generate-graph.js +186 -0
  11. package/docs/architecture/dependencies/_generate-svg.js +364 -0
  12. package/docs/architecture/dependencies/in-ecosystem-dependency-graph-generation.md +97 -0
  13. package/docs/architecture/dependencies/in-ecosystem-dependency-graph.json +3168 -0
  14. package/docs/architecture/dependencies/in-ecosystem-dependency-graph.md +221 -0
  15. package/docs/architecture/dependencies/in-ecosystem-dependency-graph.svg +664 -0
  16. package/docs/architecture/documentation-style-guide.md +65 -0
  17. package/docs/architecture/example-app-style-guide.md +154 -0
  18. package/docs/architecture/modules.md +33 -12
  19. package/docs/architecture/templating/data-access.md +196 -0
  20. package/docs/architecture/templating/data-formatting.md +350 -0
  21. package/docs/architecture/templating/data-generation.md +72 -0
  22. package/docs/architecture/templating/debugging.md +181 -0
  23. package/docs/architecture/templating/entity.md +99 -0
  24. package/docs/architecture/templating/iteration.md +170 -0
  25. package/docs/architecture/templating/jellyfish-deep-dive.md +271 -0
  26. package/docs/architecture/templating/jellyfish-templates.md +476 -0
  27. package/docs/architecture/templating/logic.md +185 -0
  28. package/docs/architecture/templating/ref-breakpoint.md +38 -0
  29. package/docs/architecture/templating/ref-data.md +51 -0
  30. package/docs/architecture/templating/ref-dateonlyformat.md +43 -0
  31. package/docs/architecture/templating/ref-dateonlyymd.md +39 -0
  32. package/docs/architecture/templating/ref-datetimeformat.md +59 -0
  33. package/docs/architecture/templating/ref-datetimeymd.md +44 -0
  34. package/docs/architecture/templating/ref-dejs.md +42 -0
  35. package/docs/architecture/templating/ref-digits.md +36 -0
  36. package/docs/architecture/templating/ref-dj.md +50 -0
  37. package/docs/architecture/templating/ref-dollars.md +36 -0
  38. package/docs/architecture/templating/ref-dt.md +38 -0
  39. package/docs/architecture/templating/ref-dvbk.md +46 -0
  40. package/docs/architecture/templating/ref-dwaf.md +45 -0
  41. package/docs/architecture/templating/ref-dwtf.md +45 -0
  42. package/docs/architecture/templating/ref-entity.md +47 -0
  43. package/docs/architecture/templating/ref-hce.md +29 -0
  44. package/docs/architecture/templating/ref-hcs.md +38 -0
  45. package/docs/architecture/templating/ref-join.md +45 -0
  46. package/docs/architecture/templating/ref-joinunique.md +34 -0
  47. package/docs/architecture/templating/ref-ls.md +37 -0
  48. package/docs/architecture/templating/ref-lv.md +38 -0
  49. package/docs/architecture/templating/ref-lvt.md +33 -0
  50. package/docs/architecture/templating/ref-ne.md +40 -0
  51. package/docs/architecture/templating/ref-pascalcaseidentifier.md +41 -0
  52. package/docs/architecture/templating/ref-pict.md +42 -0
  53. package/docs/architecture/templating/ref-pluckjoinunique.md +39 -0
  54. package/docs/architecture/templating/ref-rn.md +35 -0
  55. package/docs/architecture/templating/ref-rns.md +35 -0
  56. package/docs/architecture/templating/ref-sbr.md +36 -0
  57. package/docs/architecture/templating/ref-solve.md +46 -0
  58. package/docs/architecture/templating/ref-tbda.md +41 -0
  59. package/docs/architecture/templating/ref-tbr.md +43 -0
  60. package/docs/architecture/templating/ref-tbt.md +46 -0
  61. package/docs/architecture/templating/ref-template.md +40 -0
  62. package/docs/architecture/templating/ref-tfa.md +32 -0
  63. package/docs/architecture/templating/ref-tfm.md +43 -0
  64. package/docs/architecture/templating/ref-tif.md +45 -0
  65. package/docs/architecture/templating/ref-tifabs.md +41 -0
  66. package/docs/architecture/templating/ref-ts.md +41 -0
  67. package/docs/architecture/templating/ref-tsfm.md +42 -0
  68. package/docs/architecture/templating/ref-tswp.md +45 -0
  69. package/docs/architecture/templating/ref-tvs.md +48 -0
  70. package/docs/architecture/templating/ref-view.md +40 -0
  71. package/docs/architecture/templating/ref-vrs.md +39 -0
  72. package/docs/architecture/templating/solvers.md +153 -0
  73. package/docs/architecture/templating/template-composition.md +196 -0
  74. package/docs/architecture/templating/template-expressions.md +217 -0
  75. package/docs/architecture/templating/views.md +154 -0
  76. package/docs/examples/todolist/todo-list.md +1 -1
  77. package/docs/modules/apps.md +26 -0
  78. package/docs/modules/pict.md +18 -0
  79. package/docs/modules/utility.md +23 -1
  80. package/docs/retold-catalog.json +2541 -307
  81. package/docs/retold-keyword-index.json +267578 -117399
  82. package/modules/CLAUDE.md +1 -0
  83. package/modules/Checkout.sh +1 -0
  84. package/modules/Diff.sh +86 -0
  85. package/modules/Include-Retold-Module-List.sh +4 -2
  86. package/modules/Status.sh +1 -0
  87. package/modules/Update.sh +1 -0
  88. package/modules/apps/Apps.md +1 -0
  89. package/modules/utility/Utility.md +1 -0
  90. package/package.json +9 -11
  91. package/docs/retold-building-documentation.md +0 -33
  92. package/modules/.claude/settings.local.json +0 -52
  93. package/modules/Retold-Modules.md +0 -24
@@ -0,0 +1,170 @@
1
+ # Iteration Expressions
2
+
3
+ Iteration expressions render a template once for each item in a collection. These are the primary mechanism for generating lists, tables, and repeated structures.
4
+
5
+ ## TemplateSet (TS)
6
+
7
+ Renders a registered template once for each item in an array or object. Each item becomes the `Record` inside the template.
8
+
9
+ **Tags:** `{~TemplateSet:TEMPLATE_HASH:COLLECTION_ADDRESS~}` `{~TS:TEMPLATE_HASH:COLLECTION_ADDRESS~}`
10
+
11
+ **Parameters:**
12
+
13
+ | Parameter | Description |
14
+ |-----------|-------------|
15
+ | TEMPLATE_HASH | Hash of the template to render for each item |
16
+ | COLLECTION_ADDRESS | Address of an array or object to iterate over |
17
+
18
+ **Examples:**
19
+
20
+ ```javascript
21
+ _Pict.TemplateProvider.addTemplate('ProductRow',
22
+ '<tr><td>{~D:Record.Name~}</td><td>{~Dollars:Record.Price~}</td></tr>');
23
+
24
+ _Pict.AppData.Products = [
25
+ { Name: 'Widget', Price: 9.99 },
26
+ { Name: 'Gadget', Price: 19.99 },
27
+ { Name: 'Sprocket', Price: 4.50 }
28
+ ];
29
+
30
+ _Pict.parseTemplate('<table>{~TS:ProductRow:AppData.Products~}</table>');
31
+ // '<table><tr><td>Widget</td><td>$9.99</td></tr><tr><td>Gadget</td><td>$19.99</td></tr><tr><td>Sprocket</td><td>$4.50</td></tr></table>'
32
+ ```
33
+
34
+ When iterating over an object, each value becomes Record and the iteration follows the object's key order.
35
+
36
+ TemplateSet is the workhorse of list rendering. Every table body, navigation menu, card grid, or repeated structure typically uses a TemplateSet.
37
+
38
+ ---
39
+
40
+ ## TemplateSetFromMap (TSFM)
41
+
42
+ Looks up a key in a map to get an array, then renders a template for each item in that array.
43
+
44
+ **Tags:** `{~TemplateSetFromMap:TEMPLATE_HASH:MAP_ADDRESS:KEY_ADDRESS~}` `{~TSFM:TEMPLATE_HASH:MAP_ADDRESS:KEY_ADDRESS~}`
45
+
46
+ **Parameters:**
47
+
48
+ | Parameter | Description |
49
+ |-----------|-------------|
50
+ | TEMPLATE_HASH | Hash of the template to render for each item |
51
+ | MAP_ADDRESS | Address of a map/object whose values are arrays |
52
+ | KEY_ADDRESS | Address containing the key to look up in the map |
53
+
54
+ **Examples:**
55
+
56
+ ```javascript
57
+ _Pict.AppData.TeamRoster = {
58
+ 'Engineering': [
59
+ { Name: 'Alice', Title: 'Lead' },
60
+ { Name: 'Bob', Title: 'Senior' }
61
+ ],
62
+ 'Design': [
63
+ { Name: 'Carol', Title: 'Director' },
64
+ { Name: 'Dave', Title: 'Senior' }
65
+ ]
66
+ };
67
+
68
+ _Pict.TemplateProvider.addTemplate('MemberRow',
69
+ '<li>{~D:Record.Name~} - {~D:Record.Title~}</li>');
70
+
71
+ _Pict.parseTemplate(
72
+ '<ul>{~TSFM:MemberRow:AppData.TeamRoster:Record.Department~}</ul>',
73
+ { Department: 'Engineering' });
74
+ // '<ul><li>Alice - Lead</li><li>Bob - Senior</li></ul>'
75
+ ```
76
+
77
+ This is useful when your data is organized as a map of arrays (e.g., items grouped by category, team members grouped by department) and you need to render one group at a time.
78
+
79
+ ---
80
+
81
+ ## TemplateSetWithPayload (TSWP)
82
+
83
+ Renders a template for each item in a collection, wrapping each item as `{ Data: <item>, Payload: <payloadData> }`. Inside templates, use `Record.Data` for the current item and `Record.Payload` for the extra data.
84
+
85
+ **Tags:** `{~TemplateSetWithPayload:TEMPLATE_HASH:COLLECTION_ADDRESS:PAYLOAD_ADDRESS~}` `{~TSWP:TEMPLATE_HASH:COLLECTION_ADDRESS:PAYLOAD_ADDRESS~}`
86
+
87
+ **Parameters:**
88
+
89
+ | Parameter | Description |
90
+ |-----------|-------------|
91
+ | TEMPLATE_HASH | Hash of the template to render for each item |
92
+ | COLLECTION_ADDRESS | Address of an array to iterate over |
93
+ | PAYLOAD_ADDRESS | Address of additional data to include with each item |
94
+
95
+ **Examples:**
96
+
97
+ ```javascript
98
+ _Pict.TemplateProvider.addTemplate('TaskItem',
99
+ '<div class="{~D:Record.Payload.itemClass~}">{~D:Record.Data.Title~}</div>');
100
+
101
+ _Pict.AppData.Tasks = [
102
+ { Title: 'Write tests' },
103
+ { Title: 'Fix bug' },
104
+ { Title: 'Deploy' }
105
+ ];
106
+ _Pict.AppData.DisplayConfig = { itemClass: 'task-card' };
107
+
108
+ _Pict.parseTemplate(
109
+ '{~TSWP:TaskItem:AppData.Tasks:AppData.DisplayConfig~}');
110
+ // '<div class="task-card">Write tests</div><div class="task-card">Fix bug</div><div class="task-card">Deploy</div>'
111
+ ```
112
+
113
+ The payload pattern solves a common problem: when rendering a list, each item template needs access to shared configuration (CSS classes, labels, feature flags) that is not part of the item data itself. Without payload, you would need to reference `AppData` directly for this shared data. With payload, the template is self-contained and reusable.
114
+
115
+ ---
116
+
117
+ ## TemplateValueSet (TVS)
118
+
119
+ Iterates over the values of an object or array. Each value becomes Record. For arrays, the record includes `Key`, `Value`, `Index`, and `Count` properties. For objects, keys are iterated in sorted order.
120
+
121
+ **Tags:** `{~TemplateValueSet:TEMPLATE_HASH:DATA_ADDRESS~}` `{~TVS:TEMPLATE_HASH:DATA_ADDRESS~}`
122
+
123
+ **Parameters:**
124
+
125
+ | Parameter | Description |
126
+ |-----------|-------------|
127
+ | TEMPLATE_HASH | Hash of the template to render for each value |
128
+ | DATA_ADDRESS | Address of an object or array |
129
+
130
+ **Array Record Properties:**
131
+
132
+ | Property | Description |
133
+ |----------|-------------|
134
+ | `Record.Key` | The array index |
135
+ | `Record.Value` | The value at that index |
136
+ | `Record.Index` | Same as Key (zero-based) |
137
+ | `Record.Count` | Total number of items |
138
+
139
+ **Examples:**
140
+
141
+ ```javascript
142
+ // Iterating over array values
143
+ _Pict.TemplateProvider.addTemplate('ValueItem',
144
+ '<li>{~D:Record.Value~}</li>');
145
+
146
+ _Pict.AppData.Tags = ['javascript', 'node', 'express'];
147
+
148
+ _Pict.parseTemplate('<ul>{~TVS:ValueItem:AppData.Tags~}</ul>');
149
+ // '<ul><li>javascript</li><li>node</li><li>express</li></ul>'
150
+
151
+ // Iterating over object values (sorted keys)
152
+ _Pict.TemplateProvider.addTemplate('UserEntry',
153
+ '<p>{~D:Record.Value~}</p>');
154
+
155
+ _Pict.AppData.Users = { '3': 'Charlie', '1': 'Alice', '2': 'Bob' };
156
+
157
+ _Pict.parseTemplate('{~TVS:UserEntry:AppData.Users~}');
158
+ // '<p>Alice</p><p>Bob</p><p>Charlie</p>' (sorted by key)
159
+ ```
160
+
161
+ TemplateValueSet is distinct from TemplateSet. TemplateSet expects each array item to be an object that becomes Record directly. TemplateValueSet wraps each value in a `{ Key, Value, Index, Count }` envelope. Use TemplateValueSet when iterating over simple values (strings, numbers) or when you need the index/count metadata.
162
+
163
+ ## Choosing the Right Iteration Expression
164
+
165
+ | Scenario | Expression |
166
+ |----------|------------|
167
+ | Array of objects, render each as Record | `{~TS:...~}` |
168
+ | Map of arrays, render one group | `{~TSFM:...~}` |
169
+ | Array of objects, each needs shared payload data | `{~TSWP:...~}` |
170
+ | Array of simple values, or need index/count | `{~TVS:...~}` |
@@ -0,0 +1,271 @@
1
+ # Jellyfish Deep Dive
2
+
3
+ This document covers the internals of the Jellyfish template engine -- how templates are parsed, how expressions are resolved, and how the rendering pipeline works from string input to final output.
4
+
5
+ ## Three-Layer Architecture
6
+
7
+ Jellyfish is built from three distinct layers, each responsible for a different part of template processing.
8
+
9
+ ```mermaid
10
+ graph TB
11
+ subgraph L3["Layer 3: Pict Template Expressions"]
12
+ direction TB
13
+ expressions["44 built-in expressions<br/><i>Data, Logic, Composition, Iteration, etc.</i>"]
14
+ custom["Custom expressions<br/><i>Your application-specific tags</i>"]
15
+ end
16
+
17
+ subgraph L2["Layer 2: Fable MetaTemplate"]
18
+ direction TB
19
+ meta["Pattern registration<br/>Template parsing orchestration"]
20
+ end
21
+
22
+ subgraph L1["Layer 1: Precedent"]
23
+ direction TB
24
+ trie["Word-trie pattern matching<br/>Fast string scanning"]
25
+ end
26
+
27
+ L3 --> L2
28
+ L2 --> L1
29
+
30
+ style L3 fill:#f3e5f5,stroke:#ab47bc,color:#333
31
+ style L2 fill:#fff3e0,stroke:#ffa726,color:#333
32
+ style L1 fill:#fce4ec,stroke:#ef5350,color:#333
33
+ style expressions fill:#fff,stroke:#ce93d8,color:#333
34
+ style custom fill:#fff,stroke:#ce93d8,color:#333
35
+ style meta fill:#fff,stroke:#ffcc80,color:#333
36
+ style trie fill:#fff,stroke:#ef9a9a,color:#333
37
+ ```
38
+
39
+ ### Layer 1: Precedent
40
+
41
+ [Precedent](#/doc/utility/precedent) is a word-trie-based pattern matcher. It scans strings for registered start/end delimiter pairs and extracts the content between them. The trie structure makes this efficient even for long template strings with many registered patterns.
42
+
43
+ When you register `{~D:` as a start pattern and `~}` as an end pattern, Precedent builds a trie that recognizes this pair anywhere in a string. Given the input `Hello {~D:Record.Name~}!`, Precedent identifies the match and extracts `Record.Name` as the content between delimiters.
44
+
45
+ ### Layer 2: Fable MetaTemplate
46
+
47
+ The MetaTemplate service wraps Precedent and manages the connection between pattern matches and their handlers. It lives in Fable (not Pict), which means the basic template engine is available to any Fable-based application.
48
+
49
+ MetaTemplate provides two key methods:
50
+
51
+ - `addPatternBoth(startTag, endTag, renderSync, renderAsync, context)` -- Registers a delimiter pair with both sync and async render functions.
52
+ - `parseString(templateString, data, callback, contextArray, scope, state)` -- Processes a template string, finding all matches and calling their registered handlers.
53
+
54
+ ### Layer 3: Pict Template Expressions
55
+
56
+ Each expression is a class extending `pict-template` (which extends `fable-serviceproviderbase`). An expression registers its delimiter patterns in its constructor and implements `render()` and optionally `renderAsync()`.
57
+
58
+ ```javascript
59
+ class MyExpression extends libPictTemplate
60
+ {
61
+ constructor(pFable, pOptions, pServiceHash)
62
+ {
63
+ super(pFable, pOptions, pServiceHash);
64
+ this.addPattern('{~MyTag:', '~}');
65
+ }
66
+
67
+ render(pTemplateHash, pRecord, pContextArray, pScope, pState)
68
+ {
69
+ // pTemplateHash is the content between {~MyTag: and ~}
70
+ return 'result string';
71
+ }
72
+ }
73
+ ```
74
+
75
+ ## The Rendering Pipeline
76
+
77
+ When you call `pict.parseTemplate(templateString, record)`, the following sequence occurs:
78
+
79
+ ```mermaid
80
+ graph TD
81
+ input["Template String Input<br/><code>'Hello {~D:Record.Name~}!'</code>"]
82
+ scan["Precedent Scan<br/>Find all {~TAG:...~} matches"]
83
+ extract["Extract Template Hash<br/><code>Record.Name</code> with tag <code>D</code>"]
84
+ lookup["Handler Lookup<br/>Find registered expression for <code>{~D:</code>"]
85
+ render["Expression Render<br/><code>render('Record.Name', record, ...)</code>"]
86
+ resolve["State Resolution<br/>Resolve <code>Record.Name</code> from unified state"]
87
+ replace["String Replacement<br/>Replace <code>{~D:Record.Name~}</code> with result"]
88
+ recurse["Recursive Processing<br/>Check for remaining expressions"]
89
+ output["Final Output<br/><code>'Hello Alice!'</code>"]
90
+
91
+ input --> scan
92
+ scan --> extract
93
+ extract --> lookup
94
+ lookup --> render
95
+ render --> resolve
96
+ resolve --> replace
97
+ replace --> recurse
98
+ recurse --> output
99
+
100
+ style input fill:#e8f5e9,stroke:#43a047,color:#333
101
+ style output fill:#e8f5e9,stroke:#43a047,color:#333
102
+ style scan fill:#fce4ec,stroke:#ef5350,color:#333
103
+ style resolve fill:#fff3e0,stroke:#ffa726,color:#333
104
+ style render fill:#f3e5f5,stroke:#ab47bc,color:#333
105
+ ```
106
+
107
+ The recursive step is important. Template expressions can return strings that themselves contain template expressions. For example, `{~T:SomeTemplate~}` returns the content of `SomeTemplate`, which may contain `{~D:...~}` expressions that need further resolution. The engine continues processing until no more expressions remain.
108
+
109
+ ## State Resolution
110
+
111
+ Every template expression has access to `resolveStateFromAddress()`, which resolves a dot-notation address against Pict's unified state object.
112
+
113
+ The unified state combines several namespaces into a single object:
114
+
115
+ ```javascript
116
+ {
117
+ Fable: pict, // The Fable/Pict instance
118
+ Pict: pict, // Alias for the same instance
119
+ AppData: pict.AppData, // Persistent application state
120
+ TempData: pict.TempData, // Transient caches
121
+ Bundle: pict.Bundle, // Configuration and supporting data
122
+ Context: pContextArray, // Hierarchical context array
123
+ Record: pRecord, // Current record (loop item or passed data)
124
+ Scope: pScope, // Sticky scope for carrying state
125
+ __State: pState // Internal plumbing
126
+ }
127
+ ```
128
+
129
+ Address resolution uses [Manyfest](#/doc/utility/manyfest) for safe traversal. A path like `AppData.Users[0].Name` navigates the nested structure without throwing if any intermediate key is missing.
130
+
131
+ ### Namespace Roles
132
+
133
+ **AppData** is the primary state container. Application code sets values here, and templates read them. It persists for the lifetime of the Pict instance.
134
+
135
+ ```javascript
136
+ _Pict.AppData.User = { Name: 'Alice', Role: 'admin' };
137
+ _Pict.parseTemplate('{~D:AppData.User.Name~}');
138
+ // 'Alice'
139
+ ```
140
+
141
+ **Record** is the context-specific data object. In a template set (`{~TS:...~}`), Record is the current item being iterated. When calling `parseTemplate` directly, the second argument becomes Record.
142
+
143
+ ```javascript
144
+ _Pict.parseTemplate('{~D:Record.Title~}', { Title: 'Dune' });
145
+ // 'Dune'
146
+ ```
147
+
148
+ **Bundle** holds configuration and supporting data that supplements AppData. Typically set during initialization.
149
+
150
+ **TempData** is for values that do not need to persist -- intermediate calculations, caches, or per-render scratch data.
151
+
152
+ **Context** is an array of objects accessible by index. It provides hierarchical context when templates are deeply nested.
153
+
154
+ **Scope** is a sticky state container that travels through template processing. A parent template can set scope values that child templates access. The `{~VRS:ViewHash~}` expression (View Retaining Scope) uses this to pass state into view rendering.
155
+
156
+ ## Synchronous vs Asynchronous Rendering
157
+
158
+ Most expressions are synchronous -- they resolve data from memory and return a string immediately. The Data expression, formatting expressions, logic expressions, and solvers are all synchronous.
159
+
160
+ Some expressions require asynchronous work. The Entity expression (`{~E:...~}`) fetches records from a REST API. View expressions may trigger async rendering pipelines.
161
+
162
+ The engine handles both modes:
163
+
164
+ ```javascript
165
+ // Synchronous -- no callback, returns string
166
+ let tmpResult = _Pict.parseTemplate('{~D:AppData.Title~}');
167
+
168
+ // Asynchronous -- callback receives the result
169
+ _Pict.parseTemplate('{~E:Book^42^BookCard~}', {},
170
+ (pError, pResult) =>
171
+ {
172
+ console.log(pResult);
173
+ });
174
+ ```
175
+
176
+ When a template contains a mix of sync and async expressions, the engine processes synchronous expressions immediately and queues async expressions. The callback fires when all async operations complete.
177
+
178
+ Every expression class defines both `render()` (sync) and `renderAsync()` (async). The base class default for `renderAsync` simply calls `render` and passes the result to the callback. Expressions that need true async behavior override `renderAsync`.
179
+
180
+ ## Template Registration and Storage
181
+
182
+ Named templates are stored in the **TemplateProvider** -- a service that maps string hashes to template content.
183
+
184
+ ```javascript
185
+ // Store a template
186
+ _Pict.TemplateProvider.addTemplate('BookCard',
187
+ '<div>{~D:Record.Title~}</div>');
188
+
189
+ // Retrieve it
190
+ let tmpTemplate = _Pict.TemplateProvider.getTemplate('BookCard');
191
+ // '<div>{~D:Record.Title~}</div>'
192
+ ```
193
+
194
+ Templates can also be registered through view configurations, where the `Templates` array in a view's config is automatically loaded into the TemplateProvider.
195
+
196
+ ### Default Templates
197
+
198
+ The TemplateProvider supports **default templates** -- patterns that match template hashes by prefix and postfix. This enables convention-based rendering without registering every template explicitly.
199
+
200
+ ```javascript
201
+ // Register a default template matching any hash ending in '-ListRow'
202
+ _Pict.TemplateProvider.addDefaultTemplate('', '-ListRow',
203
+ '<li>{~D:Record.Name~}</li>');
204
+
205
+ // Now both of these resolve to the default:
206
+ _Pict.parseTemplate('{~TS:Product-ListRow:AppData.Products~}');
207
+ _Pict.parseTemplate('{~TS:User-ListRow:AppData.Users~}');
208
+ ```
209
+
210
+ The TemplateProvider looks for an exact match first. If none is found, it checks default templates for a matching prefix/postfix pattern.
211
+
212
+ ## Parameter Conventions
213
+
214
+ Template expressions use two separator conventions:
215
+
216
+ - **Colon (`:`)** separates major parameters. For example, `{~T:TemplateHash:DataAddress~}` has two colon-separated parameters.
217
+ - **Caret (`^`)** separates sub-parameters within a single parameter. For example, `{~TIf:Template:Data:Left^==^Right~}` has a condition parameter with three caret-separated parts.
218
+
219
+ This distinction matters when parsing expression hashes. A TemplateIf expression receives `Template:Data:Left^==^Right` as its hash, splits on colons to get the three major parameters, then splits the third parameter on carets to get the comparison operands and operator.
220
+
221
+ ## Nesting and Recursion
222
+
223
+ Templates can nest arbitrarily deep. A Template expression renders another template, which may contain TemplateSet expressions, which render templates that contain Data expressions, and so on.
224
+
225
+ ```javascript
226
+ _Pict.TemplateProvider.addTemplate('Page',
227
+ '<main>{~T:Header~}{~TS:Section:AppData.Sections~}{~T:Footer~}</main>');
228
+
229
+ _Pict.TemplateProvider.addTemplate('Header',
230
+ '<header><h1>{~D:AppData.Title~}</h1></header>');
231
+
232
+ _Pict.TemplateProvider.addTemplate('Section',
233
+ '<section><h2>{~D:Record.Heading~}</h2><p>{~D:Record.Body~}</p></section>');
234
+
235
+ _Pict.TemplateProvider.addTemplate('Footer',
236
+ '<footer>{~D:AppData.Copyright~}</footer>');
237
+ ```
238
+
239
+ Rendering `Page` triggers a cascade: the engine processes the Template expressions, which return content containing Data expressions, which are then resolved. Each level sees the same unified state but with the appropriate Record for the current iteration context.
240
+
241
+ ## Expression Lifecycle
242
+
243
+ When Pict initializes, it calls `initializePictTemplateEngine()` which registers all 44 built-in expressions. Each expression:
244
+
245
+ 1. Is instantiated as a Fable service
246
+ 2. Registers its delimiter patterns with MetaTemplate (via `addPattern`)
247
+ 3. Becomes available for immediate use in any template string
248
+
249
+ Custom expressions follow the same lifecycle. Calling `_Pict.addTemplate(ExpressionClass)` instantiates the class and registers its patterns.
250
+
251
+ The order of registration does not matter. Precedent's trie handles overlapping patterns correctly -- `{~D:` and `{~DJ:` and `{~DWTF:` all coexist because the trie matches the longest applicable start pattern.
252
+
253
+ ## Error Handling
254
+
255
+ Jellyfish templates fail gracefully. The design principle is that a template should always produce output, even if some expressions cannot resolve.
256
+
257
+ - **Missing data:** Returns empty string. `{~D:AppData.Missing.Path~}` produces `''`.
258
+ - **Invalid template hash:** Logs a warning and returns empty string.
259
+ - **Missing named template:** The Template expression logs a warning and returns empty string.
260
+ - **Type mismatches:** Formatting expressions handle non-numeric input gracefully, typically returning the input unchanged or empty string.
261
+ - **Async errors:** Passed to the callback's error parameter without crashing the rendering pipeline.
262
+
263
+ This means a partially-populated data model still produces usable output. Missing values appear as empty strings rather than error messages or exceptions.
264
+
265
+ ## Performance Considerations
266
+
267
+ Precedent's word-trie makes pattern scanning efficient. The trie is built once when patterns are registered and reused for every parse operation.
268
+
269
+ Template strings are not pre-compiled or cached by default. Each call to `parseTemplate` scans the string fresh. For templates rendered repeatedly with different data (like inside a TemplateSet), the named template system avoids string duplication -- the TemplateProvider stores each template once and the engine references it by hash.
270
+
271
+ For high-frequency rendering scenarios, the key optimization is keeping template strings short and using composition (`{~T:...~}`) to break large templates into smaller, focused pieces. This also improves readability and maintainability.