@jarenjs/linq 0.67.0 → 0.72.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,8 +2,8 @@
2
2
 
3
3
  > this file, the binder and the family's **normative reference**: what a
4
4
  > pen is, the rules all of them keep, the shared `JL01xx` table, and the
5
- > cross-pen views derived from the ten guides it indexes. **Read it when**
6
- > you want a rule that is true of every pen, an index of the eleven
5
+ > cross-pen views derived from the guides it indexes. **Read it when**
6
+ > you want a rule that is true of every pen, an index of the
7
7
  > documents, or one place to look up a method without knowing which pen
8
8
  > owns it
9
9
 
@@ -16,28 +16,29 @@ A **pen** is a by-code front-end to one of the suite's document formats:
16
16
  named functions that build a standard document — a JSON Schema, a
17
17
  `$model`, a `$jslt` stylesheet — the way the chain builds a query
18
18
  document. `@jarenjs/linq` exports each pen under its own subpath
19
- (`@jarenjs/linq/schema`, `/model`, `/jslt`, `/migration`, `/contract`,
20
- `/flow`, `/app` and `/forms`); `.` stays the chain.
19
+ (<!--fact:coverage.subpaths-->`./ai`, `./app`, `./charts`, `./contract`, `./db`, `./flow`, `./forms`, `./jslt`, `./jtlt`, `./messages`, `./migration`, `./model`, `./project`, `./schema`<!--/fact-->); `.` stays the chain.
20
+
21
+ Coverage: <!--fact:coverage.pens-->14 public pen/client subpaths beside the chain; 69/69 owned schema keywords have dedicated emission routes.<!--/fact-->
21
22
 
22
23
  **This document is the family's normative reference**: §1 states the
23
24
  rules every pen keeps, §1.3 the error codes they share, and §4–§7 the
24
25
  cross-pen views — the census, the refusal map, the measured price of each
25
26
  subpath, and every pen's mapping table in one place — none of which is
26
- written here, all of it derived from the ten documents beside it. Those
27
- ten are **guides**: each opens with the problem its pen solves, builds one
27
+ written here, all of it derived from the documents beside it. Those
28
+ are **guides**: each opens with the problem its pen solves, builds one
28
29
  document across its sections, and carries exactly one normative section
29
30
  of its own, the mapping table its rows here come from. So the two
30
31
  questions have two homes and neither is a copy of the other — "what is
31
32
  true of every pen, and where do I look this method up" is answered here;
32
33
  "how do I write one of these documents" is answered there. This section
33
- is the index of the ten, and it is how a reader reaches any of them.
34
+ is the index of those guides, and it is how a reader reaches any of them.
34
35
 
35
36
  <!--fact:pens.index-->
36
37
  | Document | Lines | What it writes, and when to open it |
37
38
  |---|---:|---|
38
- | [LINQ-FORMAT.md](LINQ-FORMAT.md) | 820 | this file, the binder and the family's **normative reference**: what a pen is, the rules all of them keep, the shared `JL01xx` table, and the cross-pen views derived from the ten guides it indexes. **Read it when** you want a rule that is true of every pen, an index of the eleven documents, or one place to look up a method without knowing which pen owns it |
39
- | [QUERY-PEN.md](QUERY-PEN.md) | 1,724 | the chain, `.` — query documents (`jaren-query`) and the provider seam. **Read it when** you are querying data, or implementing a provider that answers a query document |
40
- | [SCHEMA-PEN.md](SCHEMA-PEN.md) | 1,218 | `./schema` — JSON Schema 2020-12: the structural keywords, the constraints and the annotations, each with a method of its own, plus `$query`, `$defs`/`$ref` recursion and the normalizer's per-field predicates. **Read it when** you are describing the shape of data — for validation, for a form, or as the base of an entity |
39
+ | [LINQ-FORMAT.md](LINQ-FORMAT.md) | 948 | this file, the binder and the family's **normative reference**: what a pen is, the rules all of them keep, the shared `JL01xx` table, and the cross-pen views derived from the guides it indexes. **Read it when** you want a rule that is true of every pen, an index of the documents, or one place to look up a method without knowing which pen owns it |
40
+ | [QUERY-PEN.md](QUERY-PEN.md) | 1,725 | the chain, `.` — query documents (`jaren-query`) and the provider seam. **Read it when** you are querying data, or implementing a provider that answers a query document |
41
+ | [SCHEMA-PEN.md](SCHEMA-PEN.md) | 1,205 | `./schema` — JSON Schema 2020-12: the structural keywords, the constraints and the annotations, each with a method of its own, plus `$query`, `$defs`/`$ref` recursion and the normalizer's per-field predicates. **Read it when** you are describing the shape of data — for validation, for a form, or as the base of an entity |
41
42
  | [MODEL-PEN.md](MODEL-PEN.md) | 1,092 | `./model` — the `x-entity` vocabulary on JSON Schema, and the `$model` 0.1 document `openStore` accepts unchanged. **Read it when** you are declaring a store's entities, their keys and their relations |
42
43
  | [JSLT-PEN.md](JSLT-PEN.md) | 955 | `./jslt` — `$jslt` 0.1 stylesheets: the envelope and its rules, whose bodies are captured over the matched value. **Read it when** you are transforming one document into another |
43
44
  | [MIGRATION-PEN.md](MIGRATION-PEN.md) | 781 | `./migration` — `$migration` 0.1 documents: the two shape hashes and the ordered steps the runner takes. **Read it when** you are moving a store from one model to the next |
@@ -45,13 +46,17 @@ is the index of the ten, and it is how a reader reaches any of them.
45
46
  | [FLOW-PEN.md](FLOW-PEN.md) | 1,033 | `./flow` — `jaren-fsm` 0.1 machines and `jaren-dag` 0.1 dataflows, every query-valued member captured. **Read it when** you are declaring a state machine or a dependency graph of tasks |
46
47
  | [APP-PEN.md](APP-PEN.md) | 1,143 | `./app` — the `jaren-app` 0.1 document `createApp` runs, and the JSON Schema of its state beside it. **Read it when** you are declaring a whole application: state, view, actions, effects |
47
48
  | [FORMS-PEN.md](FORMS-PEN.md) | 940 | `./forms` — the `x-form` vocabulary on JSON Schema, and `assertOnSubmit()`, the same rules' layer-3 `$query` twin. **Read it when** you are turning a schema into a form |
49
+ | [AI-PEN.md](AI-PEN.md) | 98 | `./ai` — the public action program over environment slots. **Read it when** you want typed fixtures or host-authored programs without a model client. |
50
+ | [MESSAGES-PEN.md](MESSAGES-PEN.md) | 105 | `./messages` — JSON message catalogs and message references. **Read it when** you want checked translation keys, placeholders and explicit completeness. |
51
+ | [JTLT-PEN.md](JTLT-PEN.md) | 83 | `./jtlt` — text templates with JSLT dispatch and query expressions. **Read it when** you want to author Markdown, XML or source text as portable JSON. |
52
+ | [PROJECT-PEN.md](PROJECT-PEN.md) | 75 | `./project` — Studio projects with named, typed files. **Read it when** you want a portable editor workspace containing documents written by several pens. |
53
+ | [CHARTS-PEN.md](CHARTS-PEN.md) | 94 | `./charts` — chart-definition documents for every chart kind. **Read it when** you want typed chart data and presentation options that `compileChart` consumes. |
48
54
  | [DB-CLIENT.md](DB-CLIENT.md) | 893 | `./db` — the client: the store's typed front door, not a pen, and the package's one runtime edge. **Read it when** you are reading or writing rows: `load`, `include`, `link`/`unlink`, `live` |
49
55
  <!--/fact-->
50
56
 
51
57
  Every row of that table is derived, and none of it is written here: the
52
58
  sentence is the document's own opening line, the length is the file's,
53
- and `npm run docs:derive` writes the table out of the eleven documents
54
- beside this one. The line counts are not decoration — they are what tells
59
+ and `npm run docs:derive` writes the table out of the documents in this directory. The line counts are not decoration — they are what tells
55
60
  a reader whether the document they are about to open is a ten-minute read
56
61
  or an afternoon — and `test/docs/format-citations.test.js` holds each one
57
62
  equal to the file it names, independently of the derivation.
@@ -61,7 +66,7 @@ the website opens these files through the binder and only through the
61
66
  binder, so the index IS the directory listing, and a pen added to
62
67
  `packages/linq/docs/` appears here the next time the derivation runs.
63
68
 
64
- §2 to §5 are derived the same way, from the same eleven documents. They
69
+ §2 to §5 are derived the same way, from the same documents. They
65
70
  exist so that a reader with a cross-pen question — which pens raise
66
71
  `JL0104`, what a subpath costs, which pen has a `named()` — has one
67
72
  place to look, and so that the answer is never a second copy anybody has
@@ -89,17 +94,15 @@ data rather than for a program has no compile step to be faithful to.
89
94
  | `$fsm` 0.1, `$dag` 0.1 (`jaren-fsm`, `jaren-dag`) | `@jarenjs/flow` | **`./flow`** | [FLOW-PEN.md](FLOW-PEN.md) |
90
95
  | `jaren-app` 0.1 | `@jarenjs/app` | **`./app`** | [APP-PEN.md](APP-PEN.md) |
91
96
  | `x-form` on JSON Schema | `@jarenjs/forms` | **`./forms`** | [FORMS-PEN.md](FORMS-PEN.md) |
97
+ | AI action program | `@jarenjs/ai` | **`./ai`** | [AI-PEN.md](AI-PEN.md) |
98
+ | message catalogs / MessageSpec | `@jarenjs/core`, validate/forms/contract | **`./messages`** | [MESSAGES-PEN.md](MESSAGES-PEN.md) |
99
+ | `$jtlt` 0.1 (`jaren-jtlt`) | `@jarenjs/json` | **`./jtlt`** | [JTLT-PEN.md](JTLT-PEN.md) |
100
+ | `jaren-project` | `@jarenjs/studio` | **`./project`** | [PROJECT-PEN.md](PROJECT-PEN.md) |
101
+ | `chart-definition` | `@jarenjs/charts` | **`./charts`** | [CHARTS-PEN.md](CHARTS-PEN.md) |
92
102
 
93
- Authored formats without a pen **yet** the contract generalises to
94
- each, and each is its own piece of work rather than a silent extension
95
- of the set above: `chart-definition` (`@jarenjs/charts`),
96
- `jaren-project` (`@jarenjs/studio` — low value alone, since the studio
97
- authors projects; its worth is round-tripping pen output INTO the
98
- studio), the JTLT template document (`@jarenjs/json` — blocked first on
99
- a published `jaren-jtlt` grammar, which is a format decision), message
100
- catalogs (`@jarenjs/contract` and `@jarenjs/locales`), and the AI action
101
- language (`@jarenjs/ai` — a model authors those documents; a pen's worth
102
- there is fixtures and tests).
103
+ The authored-document formats above share the public JSON boundary. Message
104
+ catalogs preserve template strings; locale render functions remain executable
105
+ code at the existing renderer boundary, as MESSAGES-PEN documents.
103
106
 
104
107
  Formats with **no pen, by decision**, each for the reason its row gives:
105
108
 
@@ -191,7 +194,7 @@ being assembled where one exists (`/properties/lines/items`).
191
194
 
192
195
  ## 2. The census
193
196
 
194
- What each of the eleven documents covers, counted from the document
197
+ What each document covers, counted from the document
195
198
  itself. Every column has a gate behind it in a different file: the
196
199
  mapping rows are held equal to the subpath's callable names, the worked
197
200
  examples are executed against the JSON beside them, the refusal count is
@@ -201,18 +204,23 @@ and the bundle is the byte count the tree-shaking probe builds.
201
204
  <!--fact:pens.census-->
202
205
  | Document | Subpath | Lines | Mapping rows | Worked examples | Refusals | Bundle |
203
206
  |---|---|---:|---:|---:|---:|---:|
204
- | [LINQ-FORMAT.md](LINQ-FORMAT.md) | — | 820 | — | — | — | — |
205
- | [QUERY-PEN.md](QUERY-PEN.md) | `.` | 1,724 | 34 | 8 | 15 | 174,264 B |
206
- | [SCHEMA-PEN.md](SCHEMA-PEN.md) | `./schema` | 1,218 | 66 | 10 | 4 | 33,156 B |
207
- | [MODEL-PEN.md](MODEL-PEN.md) | `./model` | 1,092 | 28 | 6 | 3 | 41,582 B |
207
+ | [LINQ-FORMAT.md](LINQ-FORMAT.md) | — | 948 | — | — | — | — |
208
+ | [QUERY-PEN.md](QUERY-PEN.md) | `.` | 1,725 | 34 | 8 | 15 | 174,188 B |
209
+ | [SCHEMA-PEN.md](SCHEMA-PEN.md) | `./schema` | 1,205 | 82 | 10 | 4 | 36,717 B |
210
+ | [MODEL-PEN.md](MODEL-PEN.md) | `./model` | 1,092 | 28 | 6 | 3 | 45,143 B |
208
211
  | [JSLT-PEN.md](JSLT-PEN.md) | `./jslt` | 955 | 17 | 8 | 3 | 19,856 B |
209
212
  | [MIGRATION-PEN.md](MIGRATION-PEN.md) | `./migration` | 781 | 11 | 5 | 4 | 24,259 B |
210
- | [CONTRACT-PEN.md](CONTRACT-PEN.md) | `./contract` | 1,221 | 38 | 6 | 3 | 45,298 B |
213
+ | [CONTRACT-PEN.md](CONTRACT-PEN.md) | `./contract` | 1,221 | 38 | 6 | 3 | 48,859 B |
211
214
  | [FLOW-PEN.md](FLOW-PEN.md) | `./flow` | 1,033 | 16 | 7 | 3 | 19,910 B |
212
- | [APP-PEN.md](APP-PEN.md) | `./app` | 1,143 | 22 | 7 | 3 | 47,444 B |
213
- | [FORMS-PEN.md](FORMS-PEN.md) | `./forms` | 940 | 18 | 6 | 3 | 37,312 B |
214
- | [DB-CLIENT.md](DB-CLIENT.md) | `./db` | 893 | 41 | 4 | 2 | 623,994 B |
215
- | **eleven documents** | | **11,820** | **291** | **67** | | |
215
+ | [APP-PEN.md](APP-PEN.md) | `./app` | 1,143 | 22 | 7 | 3 | 51,006 B |
216
+ | [FORMS-PEN.md](FORMS-PEN.md) | `./forms` | 940 | 18 | 6 | 3 | 40,873 B |
217
+ | [AI-PEN.md](AI-PEN.md) | `./ai` | 98 | 12 | 1 | 3 | 16,904 B |
218
+ | [MESSAGES-PEN.md](MESSAGES-PEN.md) | `./messages` | 105 | 9 | 2 | 1 | 18,337 B |
219
+ | [JTLT-PEN.md](JTLT-PEN.md) | `./jtlt` | 83 | 13 | 1 | 2 | 16,725 B |
220
+ | [PROJECT-PEN.md](PROJECT-PEN.md) | `./project` | 75 | 9 | 1 | 1 | 15,132 B |
221
+ | [CHARTS-PEN.md](CHARTS-PEN.md) | `./charts` | 94 | 21 | 1 | 1 | 17,014 B |
222
+ | [DB-CLIENT.md](DB-CLIENT.md) | `./db` | 893 | 41 | 4 | 2 | 627,770 B |
223
+ | **16 documents** | | **12,391** | **371** | **73** | | |
216
224
  <!--/fact-->
217
225
 
218
226
  A pen whose mapping rows are far below its worked examples is a pen
@@ -231,10 +239,10 @@ throws, so this table moves when a pen's source does.
231
239
  <!--fact:pens.codes-->
232
240
  | Code | Raised by |
233
241
  |---|---|
234
- | `JL0101` | [SCHEMA-PEN.md](SCHEMA-PEN.md), [MODEL-PEN.md](MODEL-PEN.md), [JSLT-PEN.md](JSLT-PEN.md), [MIGRATION-PEN.md](MIGRATION-PEN.md), [CONTRACT-PEN.md](CONTRACT-PEN.md), [FLOW-PEN.md](FLOW-PEN.md), [APP-PEN.md](APP-PEN.md), [FORMS-PEN.md](FORMS-PEN.md), [DB-CLIENT.md](DB-CLIENT.md) |
235
- | `JL0102` | [SCHEMA-PEN.md](SCHEMA-PEN.md), [MODEL-PEN.md](MODEL-PEN.md), [JSLT-PEN.md](JSLT-PEN.md), [MIGRATION-PEN.md](MIGRATION-PEN.md), [CONTRACT-PEN.md](CONTRACT-PEN.md), [FLOW-PEN.md](FLOW-PEN.md), [APP-PEN.md](APP-PEN.md), [FORMS-PEN.md](FORMS-PEN.md) |
242
+ | `JL0101` | [SCHEMA-PEN.md](SCHEMA-PEN.md), [MODEL-PEN.md](MODEL-PEN.md), [JSLT-PEN.md](JSLT-PEN.md), [MIGRATION-PEN.md](MIGRATION-PEN.md), [CONTRACT-PEN.md](CONTRACT-PEN.md), [FLOW-PEN.md](FLOW-PEN.md), [APP-PEN.md](APP-PEN.md), [FORMS-PEN.md](FORMS-PEN.md), [AI-PEN.md](AI-PEN.md), [MESSAGES-PEN.md](MESSAGES-PEN.md), [JTLT-PEN.md](JTLT-PEN.md), [PROJECT-PEN.md](PROJECT-PEN.md), [CHARTS-PEN.md](CHARTS-PEN.md), [DB-CLIENT.md](DB-CLIENT.md) |
243
+ | `JL0102` | [SCHEMA-PEN.md](SCHEMA-PEN.md), [MODEL-PEN.md](MODEL-PEN.md), [JSLT-PEN.md](JSLT-PEN.md), [MIGRATION-PEN.md](MIGRATION-PEN.md), [CONTRACT-PEN.md](CONTRACT-PEN.md), [FLOW-PEN.md](FLOW-PEN.md), [APP-PEN.md](APP-PEN.md), [FORMS-PEN.md](FORMS-PEN.md), [AI-PEN.md](AI-PEN.md) |
236
244
  | `JL0103` | [SCHEMA-PEN.md](SCHEMA-PEN.md), [CONTRACT-PEN.md](CONTRACT-PEN.md) |
237
- | `JL0104` | [SCHEMA-PEN.md](SCHEMA-PEN.md), [MODEL-PEN.md](MODEL-PEN.md), [JSLT-PEN.md](JSLT-PEN.md), [MIGRATION-PEN.md](MIGRATION-PEN.md), [FLOW-PEN.md](FLOW-PEN.md), [APP-PEN.md](APP-PEN.md), [FORMS-PEN.md](FORMS-PEN.md) |
245
+ | `JL0104` | [SCHEMA-PEN.md](SCHEMA-PEN.md), [MODEL-PEN.md](MODEL-PEN.md), [JSLT-PEN.md](JSLT-PEN.md), [MIGRATION-PEN.md](MIGRATION-PEN.md), [FLOW-PEN.md](FLOW-PEN.md), [APP-PEN.md](APP-PEN.md), [FORMS-PEN.md](FORMS-PEN.md), [AI-PEN.md](AI-PEN.md), [JTLT-PEN.md](JTLT-PEN.md) |
238
246
  | `JL0105` | [QUERY-PEN.md](QUERY-PEN.md) |
239
247
  | `JL0106` | [MIGRATION-PEN.md](MIGRATION-PEN.md) |
240
248
  | `JL0107` | [DB-CLIENT.md](DB-CLIENT.md) |
@@ -254,16 +262,21 @@ it and each document publishes it. The rounded column is what
254
262
  <!--fact:pens.cost-->
255
263
  | Subpath | Document | Bundle | Rounded |
256
264
  |---|---|---:|---:|
257
- | `@jarenjs/linq` | [QUERY-PEN.md](QUERY-PEN.md) | 174,264 B | 174 kB |
258
- | `@jarenjs/linq/schema` | [SCHEMA-PEN.md](SCHEMA-PEN.md) | 33,156 B | 33 kB |
259
- | `@jarenjs/linq/model` | [MODEL-PEN.md](MODEL-PEN.md) | 41,582 B | 42 kB |
265
+ | `@jarenjs/linq` | [QUERY-PEN.md](QUERY-PEN.md) | 174,188 B | 174 kB |
266
+ | `@jarenjs/linq/schema` | [SCHEMA-PEN.md](SCHEMA-PEN.md) | 36,717 B | 37 kB |
267
+ | `@jarenjs/linq/model` | [MODEL-PEN.md](MODEL-PEN.md) | 45,143 B | 45 kB |
260
268
  | `@jarenjs/linq/jslt` | [JSLT-PEN.md](JSLT-PEN.md) | 19,856 B | 20 kB |
261
269
  | `@jarenjs/linq/migration` | [MIGRATION-PEN.md](MIGRATION-PEN.md) | 24,259 B | 24 kB |
262
- | `@jarenjs/linq/contract` | [CONTRACT-PEN.md](CONTRACT-PEN.md) | 45,298 B | 45 kB |
270
+ | `@jarenjs/linq/contract` | [CONTRACT-PEN.md](CONTRACT-PEN.md) | 48,859 B | 49 kB |
263
271
  | `@jarenjs/linq/flow` | [FLOW-PEN.md](FLOW-PEN.md) | 19,910 B | 20 kB |
264
- | `@jarenjs/linq/app` | [APP-PEN.md](APP-PEN.md) | 47,444 B | 47 kB |
265
- | `@jarenjs/linq/forms` | [FORMS-PEN.md](FORMS-PEN.md) | 37,312 B | 37 kB |
266
- | `@jarenjs/linq/db` | [DB-CLIENT.md](DB-CLIENT.md) | 623,994 B | 624 kB |
272
+ | `@jarenjs/linq/app` | [APP-PEN.md](APP-PEN.md) | 51,006 B | 51 kB |
273
+ | `@jarenjs/linq/forms` | [FORMS-PEN.md](FORMS-PEN.md) | 40,873 B | 41 kB |
274
+ | `@jarenjs/linq/ai` | [AI-PEN.md](AI-PEN.md) | 16,904 B | 17 kB |
275
+ | `@jarenjs/linq/messages` | [MESSAGES-PEN.md](MESSAGES-PEN.md) | 18,337 B | 18 kB |
276
+ | `@jarenjs/linq/jtlt` | [JTLT-PEN.md](JTLT-PEN.md) | 16,725 B | 17 kB |
277
+ | `@jarenjs/linq/project` | [PROJECT-PEN.md](PROJECT-PEN.md) | 15,132 B | 15 kB |
278
+ | `@jarenjs/linq/charts` | [CHARTS-PEN.md](CHARTS-PEN.md) | 17,014 B | 17 kB |
279
+ | `@jarenjs/linq/db` | [DB-CLIENT.md](DB-CLIENT.md) | 627,770 B | 628 kB |
267
280
  <!--/fact-->
268
281
 
269
282
  Read these as prices, not as scores. `./db` is the largest by an order of
@@ -468,6 +481,32 @@ it says.
468
481
  | `requireJson(value, what)` | nothing: the JSON boundary every value entering a document crosses, exported so a pen built over this one uses the same door | `T` | native; a non-JSON value is `JL0101` |
469
482
  | `createFactories(classes)` | nothing: the named factories above, built for one SET of builder classes. `@jarenjs/linq/model` and `@jarenjs/linq/forms` call it with their subclasses, which is why the wiring exists exactly once and no subpath patches another's prototype | the factory record | native |
470
483
 
484
+ **Content, containment and resource identity**
485
+
486
+ | Method | Emits | `Infer` / `Input` | Status |
487
+ |---|---|---|---|
488
+ | `.id(uri)` | `$id` | `this` | native; string required |
489
+ | array `.minContains(n)`, `.maxContains(n)` | `minContains`, `maxContains` | `this` | native; non-negative integers |
490
+ | string `.contentEncoding(text)` | `contentEncoding` | `this` | native |
491
+ | string `.contentMediaType(text)` | `contentMediaType` | `this` | native |
492
+ | string `.contentSchema(builder)` | `contentSchema` | `this` | native; named definitions hoist |
493
+ | string `.formatMinimum(text)`, `.formatMaximum(text)` | `formatMinimum`, `formatMaximum` | `this` | native |
494
+ | string `.formatExclusiveMinimum(text)`, `.formatExclusiveMaximum(text)` | `formatExclusiveMinimum`, `formatExclusiveMaximum` | `this` | native |
495
+
496
+ **Applicators, references and legacy keywords**
497
+
498
+ | Method | Emits | `Infer` / `Input` | Status |
499
+ |---|---|---|---|
500
+ | `.not(builder)` | `not` | `this` | native |
501
+ | `.unevaluatedProperties(builder)`, `.unevaluatedItems(builder)` | `unevaluatedProperties`, `unevaluatedItems` | `this` | native |
502
+ | `.dependentSchemas(map)`, `.dependencies(map)` | `dependentSchemas`, `dependencies` | `this` | native; legacy dependencies also accept arrays of distinct member names |
503
+ | `.anchor(name)`, `.vocabulary(map)` | `$anchor`, `$vocabulary` | `this` | native |
504
+ | `.dynamicRef(uri)`, `.dynamicAnchor(name)` | `$dynamicRef`, `$dynamicAnchor` | `this` | native; no inferred reference identity |
505
+ | `.recursiveRef(uri)`, `.recursiveAnchor(boolean)` | `$recursiveRef`, `$recursiveAnchor` | `this` | native |
506
+ | `.definitions(map)`, `.additionalItems(builder)` | `definitions`, `additionalItems` | `this` | native; legacy vocabulary |
507
+ | `.dollarData(pointer)`, `.data(map)` | `$data`, `data` | `this` | native |
508
+ | `.legacyNullable(boolean)` | `nullable` | `unknown` | native; distinct from `.nullable()` |
509
+
471
510
  ### The Jaren model pen — [MODEL-PEN.md §2](MODEL-PEN.md)
472
511
 
473
512
  **The `x-entity` vocabulary**
@@ -752,6 +791,95 @@ it says.
752
791
  | `computed` | query | `x-form.computed` | the field's derived value, mapped to plain JSON. A failure leaves the value absent |
753
792
  | `message` | string or MessageSpec | `x-form.message`, verbatim | what an `assert` failure renders — an inline template, or `{ $msgid, message?, params? }` for the catalog |
754
793
 
794
+ ### The AI program pen — [AI-PEN.md §2](AI-PEN.md)
795
+
796
+ | Factory / method | Emits | Type | Status |
797
+ |---|---|---|---|
798
+ | `program(slots?)` | empty `steps` | declared input names | native |
799
+ | `chunk(from, as, options?)`, `.chunk(...)` | chunk; strategy and size | result family | native |
800
+ | `grep(from, as, options)`, `.grep(...)` | grep; pattern, flags, limit | match-list slot | native |
801
+ | `select(from, as, query)`, `.select(...)` | select; query JSON | result slot | native |
802
+ | `stat(from, as)`, `.stat(...)` | stat | result slot | native |
803
+ | `peek(from, as)`, `.peek(...)` | peek | result slot | native |
804
+ | `map(from, as, prompt)`, `.map(...)` | map; bounded instruction | result family | native |
805
+ | `reduce(from, as, query, options?)`, `.reduce(...)` | reduce; query JSON and optional `outputSchema` | result slot | native |
806
+ | `answer(from, options?)`, `.answer(...)` | answer; optional chars, no as | terminal program | native |
807
+ | `.step(step)` | appends a public step | tracks its input/result names | native |
808
+ | `from(document)` | raw program | no binding-order inference | native |
809
+ | `.schema`, `.toJSON()` | frozen public JSON | program document | native |
810
+
811
+ ### The messages pen — [MESSAGES-PEN.md §2](MESSAGES-PEN.md)
812
+
813
+ | Method | Emits | Type | Status |
814
+ |---|---|---|---|
815
+ | `catalog(source?, locale?)` | starts an empty draft; defaults to all/en | source key space | native |
816
+ | `.entry(id, template)` | adds or replaces one string entry | adds present id | native |
817
+ | `.entries(map)` | adds or replaces several entries | adds literal keys | native |
818
+ | `.complete()` | complete frozen catalog | every source id | native |
819
+ | `.partial()` | frozen subset | explicitly present ids | native |
820
+ | `.toJSON()` | complete frozen catalog | requires completeness | native |
821
+ | `from(document, options?)` | raw draft; options choose source/locale | literal keys where known | native |
822
+ | `inline(template)` | inline MessageSpec string | string | native |
823
+ | `message(id, options?)` | `{ $msgid, params?, message? }` | known id/parameter names | native |
824
+
825
+ ### The JTLT pen — [JTLT-PEN.md §2](JTLT-PEN.md)
826
+
827
+ | Method | Emits | Type | Status |
828
+ |---|---|---|---|
829
+ | `text(string)` | literal text; doubles a leading `$` | string segment | native |
830
+ | `query(expression, options?)` | raw or captured query string/object | interpolated segment | native |
831
+ | `raw(expression, options?)` | `{ $raw: expression }` | unescaped interpolation | native |
832
+ | `json(expression, options?)` | `{ $json: expression }` | JSON serialization | native |
833
+ | `apply(selector, mode?, options?)` | `$apply` selector or selector/mode list | dispatch splice | native |
834
+ | `rule(body?, options?)` | body and optional match/mode/priority | rule | native |
835
+ | `.body(segments)`, `.match(spec)`, `.mode(string)`, `.priority(number)` | replacement rule member | same rule | native |
836
+ | `stylesheet(rules?, options?)` | `$jtlt`, optional output, rules | envelope | native |
837
+ | `bare(rules?)` | public rule array | shorthand | native |
838
+ | `.rules(rules)`, `.rule(rule)` | replace or append rules | preserves envelope/shorthand | native |
839
+ | `.output(method)` | output method; wraps a bare list in an envelope | text or XML | native |
840
+ | `from(document)` | raw template with member order preserved | template | native |
841
+ | `.schema`, `.toJSON()` | frozen public JSON | document | native |
842
+
843
+ ### The project pen — [PROJECT-PEN.md §2](PROJECT-PEN.md)
844
+
845
+ | Method | Emits | Type | Status |
846
+ |---|---|---|---|
847
+ | `file(name, kind, text)` | `{ name, kind, text }`, preserving text | literal name/kind | native |
848
+ | `jsonFile(name, kind, document)` | same file, with serialized JSON text | literal name/kind | native |
849
+ | `defineProject(files?, options?)` | version, files, optional active/layout | names from files | native |
850
+ | `.files(files)` | replacement file list | replaces known names | native |
851
+ | `.file(file)` | appended file | adds its name | native |
852
+ | `.active(name)` | requested active name | existing name | native |
853
+ | `.layout(options)` | replacement layout | mode, ratio, autorun | native |
854
+ | `from(document)` | raw project envelope | arbitrary names | native |
855
+ | `.schema`, `.toJSON()` | frozen public document | project document | native |
856
+
857
+ ### The chart pen — [CHARTS-PEN.md §2](CHARTS-PEN.md)
858
+
859
+ | Method | Emits | Type | Status |
860
+ |---|---|---|---|
861
+ | `pie(options?)`, `bar(options?)`, `line(options?)`, `scatter(options?)` | the named `type` and options | corresponding chart | native |
862
+ | `candlestick(options?)`, `radar(options?)`, `gauge(options?)`, `boxplot(options?)` | the named `type` and options | corresponding chart | native |
863
+ | `heatmap(options?)`, `treemap(options?)`, `streamgraph(options?)`, `sankey(options?)`, `map(options?)` | the named `type` and options | corresponding chart | native |
864
+ | `.options(object)` | supplied known members | same kind | native |
865
+ | `.title(text)`, `.stream(spec)` | `title`, `stream` | same kind | native |
866
+ | `.donut(value)`, `.slices(values)` | pie members | pie | native |
867
+ | `.stacked(value)`, `.orient(value)`, `.categories(values)` | bar members | bar | native |
868
+ | `.log(value)`, `.catLabel(text)`, `.valLabel(text)` | members on kinds that declare them | same kind | native |
869
+ | `.series(values)` | the kind's series shape | same kind | native |
870
+ | `.x(value)`, `.markers(value)`, `.sampling(value)` | line members | line | native |
871
+ | `.xLabel(text)`, `.yLabel(text)`, `.domain(spec)`, `.dateNames(names)`, `.timeFormats(formats)` | axis members on their declared kinds | same kind | native |
872
+ | `.xLog(value)`, `.yLog(value)`, `.refY(value)`, `.refLabel(text)` | scatter members | scatter | native |
873
+ | `.points(values)`, `.candles(values)` | the kind's point/candle data | same kind | native |
874
+ | `.axes(values)`, `.max(value)`, `.min(value)` | radar/gauge members | same kind | native |
875
+ | `.value(value)`, `.unit(text)`, `.tone(value)` | gauge values; map `value` is a property name | same kind | native |
876
+ | `.boxes(values)`, `.xLabels(values)`, `.yLabels(values)`, `.values(values)` | boxplot/heatmap members | same kind | native |
877
+ | `.aspect(value)`, `.items(values)`, `.xs(values)` | treemap/map/streamgraph members | same kind | native |
878
+ | `.nodes(values)`, `.links(values)` | sankey members | sankey | native |
879
+ | `.label(text)`, `.simplify(value)`, `.features(values)` | map members | map | native |
880
+ | `from(document)` | raw document, including extensions | declared chart shape | native |
881
+ | `.schema`, `.toJSON()` | frozen public document | chart definition | native |
882
+
755
883
  ### The Jaren linq client — [DB-CLIENT.md §2](DB-CLIENT.md)
756
884
 
757
885
  **What is the store's and what is the client's**
@@ -0,0 +1,105 @@
1
+ # The messages pen
2
+
3
+ > `./messages` — JSON message catalogs and message references. **Read it when**
4
+ > you want checked translation keys, placeholders and explicit completeness.
5
+
6
+ ## 1. What it writes
7
+
8
+ The pen writes a flat JSON map of msgid to template string, or the existing
9
+ MessageSpec string/object used by `errorMessage` and `x-form.message`. The
10
+ `validate`, `forms` and `contract` scopes derive from their English catalogs;
11
+ `all` combines those disjoint key spaces. The locale name is diagnostic metadata
12
+ and never appears in the emitted catalog.
13
+
14
+ A catalog draft is immutable. `complete()` emits only when every required id is
15
+ present; `partial()` explicitly emits the entries supplied so far. Both exits
16
+ check ids and placeholders. `JSON.stringify(draft)` requires completeness too.
17
+ An emitted document is an independent, deeply frozen JSON snapshot.
18
+
19
+ ## 2. The mapping table
20
+
21
+ | Method | Emits | Type | Status |
22
+ |---|---|---|---|
23
+ | `catalog(source?, locale?)` | starts an empty draft; defaults to all/en | source key space | native |
24
+ | `.entry(id, template)` | adds or replaces one string entry | adds present id | native |
25
+ | `.entries(map)` | adds or replaces several entries | adds literal keys | native |
26
+ | `.complete()` | complete frozen catalog | every source id | native |
27
+ | `.partial()` | frozen subset | explicitly present ids | native |
28
+ | `.toJSON()` | complete frozen catalog | requires completeness | native |
29
+ | `from(document, options?)` | raw draft; options choose source/locale | literal keys where known | native |
30
+ | `inline(template)` | inline MessageSpec string | string | native |
31
+ | `message(id, options?)` | `{ $msgid, params?, message? }` | known id/parameter names | native |
32
+
33
+ Catalog entries are strings. A structured MessageSpec is a **reference to** a
34
+ catalog entry, used in a schema or form; it is not itself a catalog entry.
35
+ Template placeholders are checked through the shared message compiler, including
36
+ escaped and repeated braces. Translations must preserve the English parameter
37
+ set. A MessageSpec fallback may use a subset; params may override known members.
38
+
39
+ ## 3. Worked examples
40
+
41
+ ```js
42
+ import { catalog } from '@jarenjs/linq/messages';
43
+ export const messages = catalog('contract', 'nl')
44
+ .entry('contract/body-too-large', 'Operatie {op} overschrijdt {limit} bytes')
45
+ .partial();
46
+ ```
47
+ ```json
48
+ {"contract/body-too-large":"Operatie {op} overschrijdt {limit} bytes"}
49
+ ```
50
+
51
+ ```js
52
+ import { message } from '@jarenjs/linq/messages';
53
+ export const errorMessage = message('minimum', {
54
+ params: { limit: 10 }, message: 'At least {limit}',
55
+ });
56
+ ```
57
+ ```json
58
+ {"$msgid":"minimum","params":{"limit":10},"message":"At least {limit}"}
59
+ ```
60
+
61
+ Pass an emitted catalog to `compileMessageCatalog` from `@jarenjs/core/message`.
62
+ Use the emitted MessageSpec as a schema's `errorMessage` or form message.
63
+
64
+ ## 4. Refusals
65
+
66
+ | Code | Condition |
67
+ |---|---|
68
+ | `JL0101` | unknown source/id/parameter, invalid locale/map/template, a missing or extra placeholder, a non-JSON input, or an incomplete catalog at complete()/serialization |
69
+
70
+ Placeholder diagnostics name the locale, msgid and missing/extra names. The raw
71
+ factory preserves input but does not bypass checks at either publication exit.
72
+
73
+ ## 5. The types
74
+
75
+ `CatalogBuilder<Source, Present>` carries completeness only as phantoms.
76
+ `CATALOGS` contains the generated, frozen parameter vocabulary grouped by owner.
77
+ `CatalogSource`, `CatalogIds`, `Msgid`, `MessageParameters`, `CatalogDocument`
78
+ and `MessageSpec` are types. `complete()` is unavailable on a known incomplete
79
+ draft. Dynamic string-indexed maps, optional entries and a single union-valued
80
+ id do not claim compile-time completeness. A parameterless id accepts no named
81
+ parameters.
82
+ Parameter value types stay JSON; no numeric or semantic inference is invented.
83
+
84
+ ## 6. What it cannot spell
85
+
86
+ Existing locale packs also contain render functions for pluralization, localized
87
+ numbers and lists. Those functions are not JSON and are not serialized by this
88
+ pen. Every locale's string entries round-trip exactly; its function entries keep
89
+ running through the existing renderer. Contract catalogs consist entirely of
90
+ strings, so their full catalogs round-trip for every shipped locale.
91
+
92
+ Ids derive from the actual English objects. String placeholders come from the
93
+ shared compiler; function parameter names are read statically from their
94
+ JavaScript syntax using the existing development parser. An unfamiliar or opaque
95
+ function shape fails generation. This records available parameter names without
96
+ claiming to reproduce branching or formatting as a string template. An author
97
+ may deliberately supply a string replacement using that parameter vocabulary.
98
+ Run `node scripts/generate-message-pen.js` after changing English catalogs; the
99
+ gate compares generated files and all locale parameter sets with their sources.
100
+
101
+ ## 7. Cost
102
+
103
+ The isolated messages pen costs **<!--fact:bundle.messages-->18,337<!--/fact--> bytes**.
104
+ It carries the shared template compiler and generated vocabulary, with no locale,
105
+ validator, forms or contract engine and no query chain.
@@ -778,4 +778,4 @@ proxy behind it, and the canonicalizer — and the two model documents
778
778
  themselves arrive as data, deep-frozen JSON that the consumer's own model
779
779
  module built. So a consumer who ships migrations to a browser does not
780
780
  ship the model pen with them; a consumer who OPENS a store does, and pays
781
- `./model`'s <!--fact:bundle.model-->41,582<!--/fact--> bytes for it.
781
+ `./model`'s <!--fact:bundle.model-->45,143<!--/fact--> bytes for it.
package/docs/MODEL-PEN.md CHANGED
@@ -1062,10 +1062,10 @@ catch them:
1062
1062
 
1063
1063
  ## 7. Cost
1064
1064
 
1065
- `@jarenjs/linq/model` builds to **<!--fact:bundle.model-->41,582<!--/fact--> bytes** as a minified,
1065
+ `@jarenjs/linq/model` builds to **<!--fact:bundle.model-->45,143<!--/fact--> bytes** as a minified,
1066
1066
  tree-shaken ESM bundle — the figure `scripts/check-tree-shaking.js`
1067
1067
  measures and `npm run test:tree-shaking` reports, published rounded
1068
- (<!--fact:bundle.model.kb-->42<!--/fact--> kB) beside the other nine subpath prices in
1068
+ (<!--fact:bundle.model.kb-->45<!--/fact--> kB) beside the other nine subpath prices in
1069
1069
  [docs/CONSUMING.md](../../../docs/CONSUMING.md).
1070
1070
 
1071
1071
  The probe is a gate, not a report: building a two-member model as a
@@ -1083,7 +1083,7 @@ them:
1083
1083
  byte of `packages/linq/src/model/`, because the subclasses are built by
1084
1084
  this subpath rather than patched onto the base classes.
1085
1085
 
1086
- The price above the schema pen's <!--fact:bundle.schema-->33,156<!--/fact--> is about 8 kB: the mixin, the
1086
+ The price above the schema pen's <!--fact:bundle.schema-->36,717<!--/fact--> is about 8 kB: the mixin, the
1087
1087
  three relation factories, `collection()`/`index()` with their capture,
1088
1088
  `defineModel()` — and the refusal MESSAGES, which are most of what §4
1089
1089
  costs. That is a deliberate trade: naming the rule and the spelling that
@@ -0,0 +1,75 @@
1
+ # The project pen
2
+
3
+ > `./project` — Studio projects with named, typed files. **Read it when** you
4
+ > want a portable editor workspace containing documents written by several pens.
5
+
6
+ ## 1. What it writes
7
+
8
+ `@jarenjs/linq/project` writes the public `project: "0.1"` envelope. File text
9
+ stays byte-for-byte intact. `jsonFile()` serializes a JSON value once; pass a
10
+ schema or another pen's `.schema` explicitly. The pen imports no Studio engine.
11
+
12
+ Every update returns a new builder with a deeply frozen JSON snapshot. Studio's
13
+ `parseProject()` validates the envelope, resolves its active file and supplies
14
+ layout defaults. `validateFile()` checks each file through its own engine.
15
+
16
+ ## 2. The mapping table
17
+
18
+ | Method | Emits | Type | Status |
19
+ |---|---|---|---|
20
+ | `file(name, kind, text)` | `{ name, kind, text }`, preserving text | literal name/kind | native |
21
+ | `jsonFile(name, kind, document)` | same file, with serialized JSON text | literal name/kind | native |
22
+ | `defineProject(files?, options?)` | version, files, optional active/layout | names from files | native |
23
+ | `.files(files)` | replacement file list | replaces known names | native |
24
+ | `.file(file)` | appended file | adds its name | native |
25
+ | `.active(name)` | requested active name | existing name | native |
26
+ | `.layout(options)` | replacement layout | mode, ratio, autorun | native |
27
+ | `from(document)` | raw project envelope | arbitrary names | native |
28
+ | `.schema`, `.toJSON()` | frozen public document | project document | native |
29
+
30
+ ## 3. Worked examples
31
+
32
+ ```js
33
+ import { defineProject, jsonFile } from '@jarenjs/linq/project';
34
+ export const project = defineProject([jsonFile('data.json', 'data', { count: 2 })])
35
+ .active('data.json').layout({ mode: 'right', ratio: 0.4, autorun: true });
36
+ ```
37
+ ```json
38
+ {"project":"0.1","files":[{"name":"data.json","kind":"data","text":"{\"count\":2}"}],"active":"data.json","layout":{"mode":"right","ratio":0.4,"autorun":true}}
39
+ ```
40
+
41
+ Call `parseProject(project.schema)` from `@jarenjs/studio`. A parsed nonempty
42
+ project round-trips through JSON and the parser unchanged. Empty projects are
43
+ valid; the parser represents their absent active file as `null` internally.
44
+
45
+ ## 4. Refusals
46
+
47
+ | Code | Condition |
48
+ |---|---|
49
+ | `JL0101` | a missing/empty filename, unknown kind or option, non-string text/active name, non-array files, or a non-JSON input |
50
+
51
+ Studio refuses duplicate filenames with `JS0002`. A missing active filename
52
+ falls back to the first file; it is not a parser error. Layout ranges, malformed
53
+ file contents and document semantics are validated at the Studio boundary. The
54
+ pen preserves these behaviors and does not carry a second project validator.
55
+
56
+ ## 5. The types
57
+
58
+ `ProjectBuilder<Names>` tracks file names only in declarations. `FILE_KINDS`
59
+ contains the schema's file-kind vocabulary, checked against Studio's own set.
60
+ `ProjectFile<Name, Kind>`, `FileKind`, `ProjectLayout` and `ProjectDocument` are
61
+ types. `JsonInput` accepts readonly structural documents and leaves unknown
62
+ schema extension values to the runtime JSON check. `.files()` replaces the name union and `.file()` widens it. `.active()`
63
+ refuses an undeclared literal name in TypeScript. `from()` deliberately keeps
64
+ names broad. Neither duplicate detection nor numeric range proofs are claimed.
65
+
66
+ ## 6. What it cannot spell
67
+
68
+ File contents are text, not live editors or engine objects. JSON files accept
69
+ only JSON data. The envelope does not embed every file grammar: query registries,
70
+ app rendering and contract checks remain the file engines' responsibilities.
71
+
72
+ ## 7. Cost
73
+
74
+ The isolated project pen costs **<!--fact:bundle.project-->15,132<!--/fact--> bytes**.
75
+ Its tree probe excludes Studio, other target engines and the query chain.
package/docs/QUERY-PEN.md CHANGED
@@ -728,7 +728,8 @@ const rows = await fromAsync(fed.source('orders'))
728
728
  ```
729
729
 
730
730
  `fed.source(name)` is an ordinary provider source (§8) with the root
731
- `$.<name>[*]`, and all of a federation's sources share one scope which
731
+ `$.<name>[*]` (a quoted member selector for names containing punctuation,
732
+ spaces or other characters), and all of a federation's sources share one scope — which
732
733
  is exactly what admits the join. The federation is what executes it:
733
734
 
734
735
  1. each side's own document — the filters and the projection the chain
@@ -1671,14 +1672,14 @@ are shorter:
1671
1672
  ## 17. Cost
1672
1673
 
1673
1674
  A consumer importing `from` from `@jarenjs/linq` and calling one
1674
- terminal bundles **<!--fact:bundle.chain-->174,264<!--/fact--> bytes** (esbuild, ESM, minified, tree-shaken,
1675
+ terminal bundles **<!--fact:bundle.chain-->174,188<!--/fact--> bytes** (esbuild, ESM, minified, tree-shaken,
1675
1676
  `platform: 'neutral'`). The figure is measured by
1676
1677
  `scripts/check-tree-shaking.js`'s chain probe and compared with this
1677
1678
  section on every `npm run test:tree-shaking`: it is derived, never typed,
1678
1679
  and a stale one is red here rather than wrong in a document somebody
1679
1680
  reads.
1680
1681
 
1681
- Of that, **<!--fact:bundle.chain.own-->40,280<!--/fact--> bytes** are the chain's own modules — `sequence.js`,
1682
+ Of that, **<!--fact:bundle.chain.own-->40,258<!--/fact--> bytes** are the chain's own modules — `sequence.js`,
1682
1683
  `async.js`, `expression.js`, `document.js`, `provider.js`,
1683
1684
  `concurrency.js`, `errors.js` and `schema-of.js`. The remaining ~134 kB
1684
1685
  is the query ENGINE and the core it stands on: a chain's document has to
@@ -1705,7 +1706,7 @@ making:
1705
1706
  `docs/CONSUMING.md` states the rounded price of all ten subpaths in one
1706
1707
  table, each figure held equal to the same measurements. Two of its rows
1707
1708
  are the ones to read together: the chain at <!--fact:bundle.chain.kb-->174<!--/fact--> kB and
1708
- `./db` at <!--fact:bundle.db.kb-->624<!--/fact--> kB.
1709
+ `./db` at <!--fact:bundle.db.kb-->628<!--/fact--> kB.
1709
1710
  The client costs what the store costs, by construction, and the chain
1710
1711
  costs what running a query costs.
1711
1712
 
@@ -1714,7 +1715,7 @@ the reason is worth knowing: a bundler counts a shared module once, and
1714
1715
  the chain and every pen share the expression capture (`expression.js`)
1715
1716
  and the coded errors under it (`errors.js`, and `@jarenjs/core`'s error
1716
1717
  and object helpers). A consumer importing the chain AND the schema pen
1717
- bundles **<!--fact:bundle.chain.withSchemaPen-->195,342<!--/fact--> bytes** — **<!--fact:bundle.chain.shared-->12,078<!--/fact--> bytes** less than the sum of the
1718
+ bundles **<!--fact:bundle.chain.withSchemaPen-->198,858<!--/fact--> bytes** — **<!--fact:bundle.chain.shared-->12,047<!--/fact--> bytes** less than the sum of the
1718
1719
  figure above and [SCHEMA-PEN.md](SCHEMA-PEN.md#7-cost) §7's, which is
1719
1720
  what those shared modules weigh. The probe measures that pair too, so
1720
1721
  the saving is derived like everything else here. What the chain does NOT