@jarenjs/linq 0.67.0 → 0.72.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.
package/README.md CHANGED
@@ -13,7 +13,8 @@ it by hand, as JSON, with nothing checking it until it ran.
13
13
 
14
14
  This package removes the price. You write the query, the schema, the
15
15
  model, the migration, the contract, the stylesheet, the state machine,
16
- the dataflow, the application or the form as ordinary typed JavaScript,
16
+ the dataflow, the application, the form, chart, project, text template,
17
+ message catalog or AI program as ordinary typed JavaScript,
17
18
  and what comes back is the document — exactly the one its engine
18
19
  already takes, byte for byte, with its types derived beside it. The
19
20
  chain is the pen; the document is the deliverable.
@@ -46,9 +47,11 @@ SQL — and nothing is hidden: the chain's `explain()` shows the document
46
47
  it sent, and the store's `explain(document)` shows what that became,
47
48
  `SELECT … FROM "User" WHERE "age" > ? ORDER BY "name"`.
48
49
 
50
+ Coverage: <!--fact:coverage.pens-->14 public pen/client subpaths beside the chain; 69/69 owned schema keywords have dedicated emission routes.<!--/fact-->
51
+
49
52
  ## What you gain
50
53
 
51
- - **One idiom for ten kinds of document.** `(u) => u.age.gt(21)` is a
54
+ - **One idiom across document formats.** `(u) => u.age.gt(21)` is a
52
55
  query predicate. The same shape is a schema's cross-field `check()`,
53
56
  a form's `assert`, a state machine's guard, a stylesheet rule's body,
54
57
  an app action's patch. Every callback is recorded by one proxy over
@@ -95,6 +98,11 @@ it sent, and the store's `explain(document)` shows what that became,
95
98
  | a state machine, a dataflow | `@jarenjs/linq/flow` | `@jarenjs/flow` | [flow pen](#by-code-the-flow-pen) · [FLOW-PEN](docs/FLOW-PEN.md) |
96
99
  | an application | `@jarenjs/linq/app` | `@jarenjs/app` | [app pen](#by-code-the-app-pen) · [APP-PEN](docs/APP-PEN.md) |
97
100
  | a form | `@jarenjs/linq/forms` | `@jarenjs/forms` | [forms pen](#by-code-the-forms-pen) · [FORMS-PEN](docs/FORMS-PEN.md) |
101
+ | an AI action program | `@jarenjs/linq/ai` | `@jarenjs/ai` | [AI-PEN](docs/AI-PEN.md) |
102
+ | a message catalog | `@jarenjs/linq/messages` | `@jarenjs/core/message` | [MESSAGES-PEN](docs/MESSAGES-PEN.md) |
103
+ | a text template | `@jarenjs/linq/jtlt` | `@jarenjs/json/jtlt` | [JTLT-PEN](docs/JTLT-PEN.md) |
104
+ | a Studio project | `@jarenjs/linq/project` | `@jarenjs/studio` | [PROJECT-PEN](docs/PROJECT-PEN.md) |
105
+ | a chart | `@jarenjs/linq/charts` | `@jarenjs/charts` | [CHARTS-PEN](docs/CHARTS-PEN.md) |
98
106
  | typed handles on the store | `@jarenjs/linq/db` | the front door, not a pen | [the front door](#the-front-door-jarenjslinqdb) · [DB-CLIENT](docs/DB-CLIENT.md) |
99
107
 
100
108
  The rules every pen keeps — what a pen may spell, what it must refuse,
@@ -716,4 +724,9 @@ Every subpath a consumer can import, derived from the manifest by
716
724
  | `@jarenjs/linq/forms` | JavaScript | declared |
717
725
  | `@jarenjs/linq/db` | JavaScript | declared |
718
726
  | `@jarenjs/linq/package.json` | metadata | — |
727
+ | `@jarenjs/linq/charts` | JavaScript | declared |
728
+ | `@jarenjs/linq/project` | JavaScript | declared |
729
+ | `@jarenjs/linq/jtlt` | JavaScript | declared |
730
+ | `@jarenjs/linq/messages` | JavaScript | declared |
731
+ | `@jarenjs/linq/ai` | JavaScript | declared |
719
732
  <!--/fact-->
package/docs/AI-PEN.md ADDED
@@ -0,0 +1,98 @@
1
+ # The AI program pen
2
+
3
+ > `./ai` — the public action program over environment slots. **Read it when**
4
+ > you want typed fixtures or host-authored programs without a model client.
5
+
6
+ ## 1. What it writes
7
+
8
+ The pen emits exactly `{ steps: [...] }`, the document consumed by the AI
9
+ program schema, compiler and runner. It imports no AI engine or environment.
10
+ `.schema` is an independent, deeply frozen JSON snapshot; `JSON.stringify()`
11
+ writes that document. Updates return a new builder.
12
+
13
+ `program(['corpus'])` declares input names for TypeScript; those names never
14
+ become extra document members or an embedded registry. The compiler checks the
15
+ real environment. Queries can be raw JSON or callbacks recorded over the input
16
+ document, using the same capture as other pens and no runtime query engine.
17
+
18
+ ## 2. The mapping table
19
+
20
+ Factories return frozen public steps. The same method on a program appends that
21
+ step. Every non-answer step writes `op`, `from` and `as` before its options.
22
+
23
+ | Factory / method | Emits | Type | Status |
24
+ |---|---|---|---|
25
+ | `program(slots?)` | empty `steps` | declared input names | native |
26
+ | `chunk(from, as, options?)`, `.chunk(...)` | chunk; strategy and size | result family | native |
27
+ | `grep(from, as, options)`, `.grep(...)` | grep; pattern, flags, limit | match-list slot | native |
28
+ | `select(from, as, query)`, `.select(...)` | select; query JSON | result slot | native |
29
+ | `stat(from, as)`, `.stat(...)` | stat | result slot | native |
30
+ | `peek(from, as)`, `.peek(...)` | peek | result slot | native |
31
+ | `map(from, as, prompt)`, `.map(...)` | map; bounded instruction | result family | native |
32
+ | `reduce(from, as, query, options?)`, `.reduce(...)` | reduce; query JSON and optional `outputSchema` | result slot | native |
33
+ | `answer(from, options?)`, `.answer(...)` | answer; optional chars, no as | terminal program | native |
34
+ | `.step(step)` | appends a public step | tracks its input/result names | native |
35
+ | `from(document)` | raw program | no binding-order inference | native |
36
+ | `.schema`, `.toJSON()` | frozen public JSON | program document | native |
37
+
38
+ `chunk` options are `strategy: 'size' | 'line' | 'separator'` and `size`.
39
+ `grep` requires `pattern`, with optional `flags: 'i' | 'm' | 'im' | ''` and
40
+ `limit`. `answer` takes `chars`. The schema owns numeric and string bounds.
41
+
42
+ ## 3. Worked examples
43
+
44
+ ```js
45
+ import { program } from '@jarenjs/linq/ai';
46
+ export const plan = program(['corpus'])
47
+ .chunk('corpus', 'pieces', { strategy: 'line', size: 200 })
48
+ .map('pieces', 'found', 'Return the number in this piece as JSON')
49
+ .reduce('found', 'count', { $count: '$[*]' })
50
+ .answer('count', { chars: 50 });
51
+ ```
52
+ ```json
53
+ {"steps":[{"op":"chunk","from":"corpus","as":"pieces","strategy":"line","size":200},{"op":"map","from":"pieces","as":"found","prompt":"Return the number in this piece as JSON"},{"op":"reduce","from":"found","as":"count","query":{"$count":"$[*]"}},{"op":"answer","from":"count","chars":50}]}
54
+ ```
55
+
56
+ Validate with `PROGRAM_SCHEMA` (or `programSchema({ queryRef })`), then
57
+ `compileProgram`/`programGate` with the query compiler and known environment
58
+ names. `createProgramRunner` does both shape and compile checks before execution.
59
+ The standalone compile gate checks semantics; it does not replace the shape
60
+ schema's numeric ranges, member closure or step-count cap.
61
+
62
+ ## 4. Refusals
63
+
64
+ | Code | Condition |
65
+ |---|---|
66
+ | `JL0101` | invalid slot/result name shape, unknown option/operation, non-JSON input, or a malformed input-name list |
67
+ | `JL0102` | appending a step after answer |
68
+ | `JL0104` | a query callback reads an external; program queries bind only their input document |
69
+
70
+ The schema/compiler own unknown environment names, duplicate result bindings,
71
+ missing answers, limits, query validity and family/slot semantics. `from()`
72
+ preserves raw programs for those checks rather than carrying a second compiler.
73
+
74
+ ## 5. The types
75
+
76
+ `ProgramBuilder<Bindings, Done>` tracks names and whether answer has closed the
77
+ program. `ProgramDocument`, `Step`, `StepDocument`, `StepOptions`, `Operation`,
78
+ `Query`, `ChunkOptions`, `GrepOptions` and `AnswerOptions` are types only.
79
+
80
+ A later step may read only a declared input or previous result. `select` and
81
+ `answer` read individual slots, not chunk/map families; `reduce` reads map
82
+ results. A result cannot be rebound, although an input slot can be shadowed as
83
+ in the compiler. Standalone step factories retain literal names and `.step()`
84
+ checks them on insertion. Raw documents claim neither binding order nor a
85
+ terminal state. No inferred data schema, budget or successful-run guarantee is
86
+ attached to a program.
87
+
88
+ ## 6. What it cannot spell
89
+
90
+ This is the action document, not a prompt/client wrapper. It neither pastes
91
+ corpus contents into a private envelope nor runs model calls in query expressions.
92
+ Only the existing runner's map step calls a model. Cancellation, concurrency,
93
+ sub-call budgets, storage and result interpretation remain runner concerns.
94
+
95
+ ## 7. Cost
96
+
97
+ The isolated AI program pen costs **<!--fact:bundle.ai-->16,904<!--/fact--> bytes**.
98
+ Its tree probe excludes AI, other target engines and the query chain.
package/docs/APP-PEN.md CHANGED
@@ -1123,7 +1123,7 @@ not look for them:
1123
1123
 
1124
1124
  ## 7. Cost
1125
1125
 
1126
- `@jarenjs/linq/app` builds to **<!--fact:bundle.app-->47,444<!--/fact--> bytes** as a minified,
1126
+ `@jarenjs/linq/app` builds to **<!--fact:bundle.app-->51,006<!--/fact--> bytes** as a minified,
1127
1127
  tree-shaken ESM bundle — the figure `scripts/check-tree-shaking.js`
1128
1128
  measures and `npm run test:tree-shaking` reports, published rounded
1129
1129
  beside the other nine subpath prices in
@@ -1133,8 +1133,8 @@ pen and the JSLT pen (state, and views), and no chain module, no
1133
1133
 
1134
1134
  It is the second-largest pen bundle after the client, and the two pens
1135
1135
  it carries are most of it. The three figures the same probe measures,
1136
- side by side: `@jarenjs/linq/schema` <!--fact:bundle.schema-->33,156<!--/fact--> bytes,
1137
- `@jarenjs/linq/jslt` <!--fact:bundle.jslt-->19,856<!--/fact-->, `@jarenjs/linq/app` <!--fact:bundle.app-->47,444<!--/fact-->. The subpath sums do not add — all
1136
+ side by side: `@jarenjs/linq/schema` <!--fact:bundle.schema-->36,717<!--/fact--> bytes,
1137
+ `@jarenjs/linq/jslt` <!--fact:bundle.jslt-->19,856<!--/fact-->, `@jarenjs/linq/app` <!--fact:bundle.app-->51,006<!--/fact-->. The subpath sums do not add — all
1138
1138
  three carry the capture, the expression lowering and the JSON boundary,
1139
1139
  which each bundle counts once — so what the app pen costs a consumer who
1140
1140
  already imports the schema pen is the difference the numbers do state:
@@ -0,0 +1,94 @@
1
+ # The chart pen
2
+
3
+ > `./charts` — chart-definition documents for every chart kind. **Read it when**
4
+ > you want typed chart data and presentation options that `compileChart` consumes.
5
+
6
+ ## 1. What it writes
7
+
8
+ Import the kind you need from `@jarenjs/linq/charts`. Each factory starts a
9
+ public chart definition with its literal `type`. Fluent methods replace one
10
+ member; `options()` replaces several. `.schema` is an independent, deeply
11
+ frozen JSON document, and `JSON.stringify(builder)` serializes that document.
12
+
13
+ The pen imports no chart renderer. Its declarations and known member vocabulary
14
+ are derived from the public chart grammar with `@jarenjs/emit`. Run
15
+ `node scripts/generate-chart-pen.js` after a grammar change; the drift gate
16
+ compares both generated files with that grammar.
17
+
18
+ ## 2. The mapping table
19
+
20
+ The factory determines the available methods and their argument types. Every
21
+ method below preserves that discriminator and replaces a previous value.
22
+ Nested series and axis fragments use the shapes shared by the public grammar.
23
+
24
+ | Method | Emits | Type | Status |
25
+ |---|---|---|---|
26
+ | `pie(options?)`, `bar(options?)`, `line(options?)`, `scatter(options?)` | the named `type` and options | corresponding chart | native |
27
+ | `candlestick(options?)`, `radar(options?)`, `gauge(options?)`, `boxplot(options?)` | the named `type` and options | corresponding chart | native |
28
+ | `heatmap(options?)`, `treemap(options?)`, `streamgraph(options?)`, `sankey(options?)`, `map(options?)` | the named `type` and options | corresponding chart | native |
29
+ | `.options(object)` | supplied known members | same kind | native |
30
+ | `.title(text)`, `.stream(spec)` | `title`, `stream` | same kind | native |
31
+ | `.donut(value)`, `.slices(values)` | pie members | pie | native |
32
+ | `.stacked(value)`, `.orient(value)`, `.categories(values)` | bar members | bar | native |
33
+ | `.log(value)`, `.catLabel(text)`, `.valLabel(text)` | members on kinds that declare them | same kind | native |
34
+ | `.series(values)` | the kind's series shape | same kind | native |
35
+ | `.x(value)`, `.markers(value)`, `.sampling(value)` | line members | line | native |
36
+ | `.xLabel(text)`, `.yLabel(text)`, `.domain(spec)`, `.dateNames(names)`, `.timeFormats(formats)` | axis members on their declared kinds | same kind | native |
37
+ | `.xLog(value)`, `.yLog(value)`, `.refY(value)`, `.refLabel(text)` | scatter members | scatter | native |
38
+ | `.points(values)`, `.candles(values)` | the kind's point/candle data | same kind | native |
39
+ | `.axes(values)`, `.max(value)`, `.min(value)` | radar/gauge members | same kind | native |
40
+ | `.value(value)`, `.unit(text)`, `.tone(value)` | gauge values; map `value` is a property name | same kind | native |
41
+ | `.boxes(values)`, `.xLabels(values)`, `.yLabels(values)`, `.values(values)` | boxplot/heatmap members | same kind | native |
42
+ | `.aspect(value)`, `.items(values)`, `.xs(values)` | treemap/map/streamgraph members | same kind | native |
43
+ | `.nodes(values)`, `.links(values)` | sankey members | sankey | native |
44
+ | `.label(text)`, `.simplify(value)`, `.features(values)` | map members | map | native |
45
+ | `from(document)` | raw document, including extensions | declared chart shape | native |
46
+ | `.schema`, `.toJSON()` | frozen public document | chart definition | native |
47
+
48
+ ## 3. Worked examples
49
+
50
+ ```js
51
+ import { bar } from '@jarenjs/linq/charts';
52
+ export const chart = bar().categories(['A', 'B'])
53
+ .series([{ name: 'Sales', values: [3, 5] }]).title('Sales');
54
+ ```
55
+ ```json
56
+ {"type":"bar","categories":["A","B"],"series":[{"name":"Sales","values":[3,5]}],"title":"Sales"}
57
+ ```
58
+
59
+ Pass `chart.schema` to `compileChart` from `@jarenjs/charts`, then call
60
+ `toVnode()` or `toSvgString()` on the compiled result. Streaming consumers may
61
+ omit data members and supply data through the chart engine's separate argument.
62
+
63
+ ## 4. Refusals
64
+
65
+ | Code | Condition |
66
+ |---|---|
67
+ | `JL0101` | a non-JSON value, non-plain option map, unknown kind or option, or a fluent member on a kind that does not declare it |
68
+
69
+ Numeric ranges and relationships among data members remain the chart grammar's
70
+ and renderer's responsibility. For extension fields use `from(document)`.
71
+
72
+ ## 5. The types
73
+
74
+ `ChartBuilder` is the exported runtime class. `ChartPen<K>` adds precisely the
75
+ methods permitted by kind `K`; `ChartKind`, `ChartDefinitions`, `ChartOptions`
76
+ and the individual chart/fragment interfaces are types only. A bar series takes
77
+ `values`; a line series takes `points`. A gauge's `value` is numeric, while a
78
+ map's `value` names a feature property. `.options()` preserves the kind too.
79
+
80
+ The runtime tests enumerate every schema kind and top-level member, validate
81
+ each emitted document and compare its AST and SVG with the hand-written input.
82
+ The consumer type pins reject cross-kind methods and malformed series values.
83
+
84
+ ## 6. What it cannot spell
85
+
86
+ Functions, engine instances and callbacks are not JSON chart definitions.
87
+ The pen does not compile geometry, validate longitude ranges, supply themes or
88
+ render tooltips; those responsibilities remain in the chart engine.
89
+
90
+ ## 7. Cost
91
+
92
+ The isolated chart pen costs **<!--fact:bundle.charts-->17,014<!--/fact--> bytes**.
93
+ The tree-shaking gate refuses chart engine, other target engine and chain
94
+ modules in this bundle, and checks that the chain and schema pen do not import it.
@@ -1188,10 +1188,10 @@ to write, and reaching them means one import of `@jarenjs/contract` over
1188
1188
 
1189
1189
  ## 7. Cost
1190
1190
 
1191
- `@jarenjs/linq/contract` builds to **<!--fact:bundle.contract-->45,298<!--/fact--> bytes** as a minified,
1191
+ `@jarenjs/linq/contract` builds to **<!--fact:bundle.contract-->48,859<!--/fact--> bytes** as a minified,
1192
1192
  tree-shaken ESM bundle — the figure `scripts/check-tree-shaking.js`
1193
1193
  measures and `npm run test:tree-shaking` reports, published rounded
1194
- (<!--fact:bundle.contract.kb-->45<!--/fact--> kB) beside the other nine subpath prices in
1194
+ (<!--fact:bundle.contract.kb-->49<!--/fact--> kB) beside the other nine subpath prices in
1195
1195
  [docs/CONSUMING.md](../../../docs/CONSUMING.md).
1196
1196
 
1197
1197
  The probe is a gate, not a report: building a one-operation contract as a
@@ -1200,7 +1200,7 @@ them:
1200
1200
 
1201
1201
  - **the schema pen is included, and that is the ceiling.** A contract's
1202
1202
  inputs and outputs are schemas, so the two are measured together and
1203
- the bundle carries <!--fact:bundle.schema-->33,156<!--/fact--> of its <!--fact:bundle.contract-->45,298<!--/fact--> bytes as the schema pen's own.
1203
+ the bundle carries <!--fact:bundle.schema-->36,717<!--/fact--> of its <!--fact:bundle.contract-->48,859<!--/fact--> bytes as the schema pen's own.
1204
1204
  The contract pen's own share is the remaining ~12 kB, most of it the
1205
1205
  refusal messages §4 lists;
1206
1206
  - **no chain module** — none of `sequence.js`, `document.js`, `async.js`,
@@ -1216,6 +1216,6 @@ them:
1216
1216
  A consumer who writes a contract and also compiles it pays both prices
1217
1217
  and they add rather than overlap. That is the shape the separation is
1218
1218
  for: a browser bundle that only needs the TYPES a contract implies —
1219
- `typedClient` over an HTTP binding, say — ships the pen's <!--fact:bundle.contract.kb-->45<!--/fact--> kB and none
1219
+ `typedClient` over an HTTP binding, say — ships the pen's <!--fact:bundle.contract.kb-->49<!--/fact--> kB and none
1220
1220
  of the compiler, while the server that serves the contract imports
1221
1221
  `@jarenjs/contract` and does not need the pen at all.
package/docs/DB-CLIENT.md CHANGED
@@ -826,10 +826,10 @@ never builds one; the migration between two of them is
826
826
 
827
827
  ## 7. Cost
828
828
 
829
- `@jarenjs/linq/db` builds to **<!--fact:bundle.db-->623,994<!--/fact--> bytes** as a minified,
829
+ `@jarenjs/linq/db` builds to **<!--fact:bundle.db-->625,827<!--/fact--> bytes** as a minified,
830
830
  tree-shaken ESM bundle — the figure `scripts/check-tree-shaking.js`
831
831
  measures and `npm run test:tree-shaking` reports, published rounded
832
- (<!--fact:bundle.db.kb-->624<!--/fact--> kB) beside the other nine subpath prices in
832
+ (<!--fact:bundle.db.kb-->626<!--/fact--> kB) beside the other nine subpath prices in
833
833
  [docs/CONSUMING.md](../../../docs/CONSUMING.md).
834
834
 
835
835
  It is by far the largest of the ten, and the reason is §1.1's edge rather
@@ -856,7 +856,7 @@ What the probe asserts, and fails the build on:
856
856
  asserts the same exclusion.
857
857
 
858
858
  A consumer who wants the model pen's types without the store pays
859
- `./model`'s <!--fact:bundle.model-->41,582<!--/fact--> bytes and installs no peer; one who wants to run
859
+ `./model`'s <!--fact:bundle.model-->45,143<!--/fact--> bytes and installs no peer; one who wants to run
860
860
  queries against an array rather than a database pays the chain's price
861
861
  (§17 of [QUERY-PEN.md](QUERY-PEN.md)) and installs no peer. `./db` is
862
862
  the one subpath whose `package.json` entry carries an optional peer at
package/docs/FORMS-PEN.md CHANGED
@@ -925,7 +925,7 @@ for this pen at all.
925
925
 
926
926
  ## 7. Cost
927
927
 
928
- `@jarenjs/linq/forms` builds to **<!--fact:bundle.forms-->37,312<!--/fact--> bytes** as a minified,
928
+ `@jarenjs/linq/forms` builds to **<!--fact:bundle.forms-->40,873<!--/fact--> bytes** as a minified,
929
929
  tree-shaken ESM bundle — the figure `scripts/check-tree-shaking.js`
930
930
  measures and `npm run test:tree-shaking` reports, published rounded
931
931
  beside the other nine subpath prices in
@@ -934,7 +934,7 @@ pen it subclasses, and no chain module, no `@jarenjs/forms` byte and no
934
934
  model pen.
935
935
 
936
936
  Most of that figure is the schema pen: `@jarenjs/linq/schema` alone
937
- is <!--fact:bundle.schema-->33,156<!--/fact--> bytes, so the whole `x-form` vocabulary — the mixin, the rule
937
+ is <!--fact:bundle.schema-->36,717<!--/fact--> bytes, so the whole `x-form` vocabulary — the mixin, the rule
938
938
  capture, the submit transform and their refusal messages — is about 4 kB
939
939
  on top of a pen a form-shaped consumer usually already carries. A
940
940
  consumer importing both subpaths pays the schema pen once.
@@ -0,0 +1,83 @@
1
+ # The JTLT pen
2
+
3
+ > `./jtlt` — text templates with JSLT dispatch and query expressions. **Read it
4
+ > when** you want to author Markdown, XML or source text as portable JSON.
5
+
6
+ ## 1. What it writes
7
+
8
+ The pen emits the public JTLT 0.1 envelope or its bare rule-array shorthand.
9
+ `.schema` is a deeply frozen JSON snapshot; serializing a builder writes exactly
10
+ that document. The published latest and draft-07 grammars came first and are
11
+ held to the existing renderer's complete fixture corpus.
12
+
13
+ The pen captures expressions but imports no query engine or JTLT renderer.
14
+ Pass its document to `compileJtltStylesheet` or `validateJtltTemplate` from
15
+ `@jarenjs/json/jtlt`. The [format](../../json/docs/JTLT-FORMAT.md) defines
16
+ escaping, dispatch, matching, output and the structural/compiler boundary.
17
+
18
+ ## 2. The mapping table
19
+
20
+ | Method | Emits | Type | Status |
21
+ |---|---|---|---|
22
+ | `text(string)` | literal text; doubles a leading `$` | string segment | native |
23
+ | `query(expression, options?)` | raw or captured query string/object | interpolated segment | native |
24
+ | `raw(expression, options?)` | `{ $raw: expression }` | unescaped interpolation | native |
25
+ | `json(expression, options?)` | `{ $json: expression }` | JSON serialization | native |
26
+ | `apply(selector, mode?, options?)` | `$apply` selector or selector/mode list | dispatch splice | native |
27
+ | `rule(body?, options?)` | body and optional match/mode/priority | rule | native |
28
+ | `.body(segments)`, `.match(spec)`, `.mode(string)`, `.priority(number)` | replacement rule member | same rule | native |
29
+ | `stylesheet(rules?, options?)` | `$jtlt`, optional output, rules | envelope | native |
30
+ | `bare(rules?)` | public rule array | shorthand | native |
31
+ | `.rules(rules)`, `.rule(rule)` | replace or append rules | preserves envelope/shorthand | native |
32
+ | `.output(method)` | output method; wraps a bare list in an envelope | text or XML | native |
33
+ | `from(document)` | raw template with member order preserved | template | native |
34
+ | `.schema`, `.toJSON()` | frozen public JSON | document | native |
35
+
36
+ Expression callbacks receive the matched value and declared externals. Supply
37
+ `{ externals: ['rate'] }` as options to name a user parameter; `root` and `path`
38
+ are already bound by JTLT. Captured `.get()` paths use bracket notation, as in
39
+ other pens. Raw query documents retain the author's spelling. Nested arrays
40
+ remain nested segment lists, and literal containers belong inside `json()`.
41
+
42
+ ## 3. Worked examples
43
+
44
+ ```js
45
+ import { stylesheet, rule, apply, text } from '@jarenjs/linq/jtlt';
46
+ export const template = stylesheet([
47
+ rule([text('# Books\n'), apply('$.books[*]')], { match: '$' }),
48
+ rule(['- ', '$.title', '\n'], { match: '$.books[*]' }),
49
+ ]);
50
+ ```
51
+ ```json
52
+ {"$jtlt":"0.1","rules":[{"match":"$","body":["# Books\n",{"$apply":"$.books[*]"}]},{"match":"$.books[*]","body":["- ","$.title","\n"]}]}
53
+ ```
54
+
55
+ ## 4. Refusals
56
+
57
+ | Code | Condition |
58
+ |---|---|
59
+ | `JL0101` | non-JSON input, unknown option, invalid list/text/output/mode argument, invalid or repeated external declaration, or a query segment that is neither a string nor query object |
60
+ | `JL0104` | a capture callback reads an undeclared external |
61
+
62
+ The shared capture also enforces the binder's expression rules. The JTLT compiler
63
+ checks match syntax, query arguments, hooks and the reserved priority band;
64
+ rendering checks input-dependent serialization and recursion. No type claims
65
+ that a template will render every possible input.
66
+
67
+ ## 5. The types
68
+
69
+ `RuleBuilder` and `TemplateBuilder` are runtime classes. `RuleDocument`,
70
+ `TemplateDocument`, `Segment`, `Match`, `Output`, `QueryInput` and `QueryOptions`
71
+ are types. The output is text; no inferred schema for rendered content is claimed.
72
+ The external-name phantom catches misspellings without adding a runtime registry.
73
+
74
+ ## 6. What it cannot spell
75
+
76
+ Functions and engine objects cannot enter documents. Callbacks are recorded into
77
+ query JSON before emission. The pen does not escape an entire finished document,
78
+ validate XML well-formedness, render text, or add a second dispatch language.
79
+
80
+ ## 7. Cost
81
+
82
+ The isolated JTLT pen costs **<!--fact:bundle.jtlt-->16,725<!--/fact--> bytes**.
83
+ Its tree probe excludes target engines and the query chain.