@jarenjs/linq 0.83.3 → 0.84.3

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
@@ -16,7 +16,7 @@ it by hand, as JSON, with nothing checking it until it ran.
16
16
  This package removes the price. You write the query, the schema, the
17
17
  model, the migration, the contract, the stylesheet, the state machine,
18
18
  the dataflow, the application, the form, chart, project, text template,
19
- message catalog or AI program as ordinary typed JavaScript,
19
+ message catalog as ordinary typed JavaScript,
20
20
  and what comes back is the document — exactly the one its engine
21
21
  already takes, byte for byte, with its types derived beside it. The
22
22
  chain is the pen; the document is the deliverable.
@@ -49,7 +49,7 @@ SQL — and nothing is hidden: the chain's `explain()` shows the document
49
49
  it sent, and the store's `explain(document)` shows what that became,
50
50
  `SELECT … FROM "User" WHERE "age" > ? ORDER BY "name"`.
51
51
 
52
- Coverage: <!--fact:coverage.pens-->15 public pen/client subpaths beside the chain; 69/69 owned schema keywords have dedicated emission routes.<!--/fact-->
52
+ Coverage: <!--fact:coverage.pens-->15 public pen/client/authoring subpaths beside the chain; 69/69 owned schema keywords have dedicated emission routes.<!--/fact-->
53
53
 
54
54
  ## What you gain
55
55
 
@@ -100,7 +100,6 @@ Coverage: <!--fact:coverage.pens-->15 public pen/client subpaths beside the chai
100
100
  | a state machine, a dataflow | `@jarenjs/linq/flow` | `@jarenjs/flow` | [flow pen](#by-code-the-flow-pen) · [FLOW-PEN](docs/FLOW-PEN.md) |
101
101
  | an application | `@jarenjs/linq/app` | `@jarenjs/app` | [app pen](#by-code-the-app-pen) · [APP-PEN](docs/APP-PEN.md) |
102
102
  | a form | `@jarenjs/linq/forms` | `@jarenjs/forms` | [forms pen](#by-code-the-forms-pen) · [FORMS-PEN](docs/FORMS-PEN.md) |
103
- | an AI action program | `@jarenjs/linq/ai` | `@jarenjs/ai` | [AI-PEN](docs/AI-PEN.md) |
104
103
  | a message catalog | `@jarenjs/linq/messages` | `@jarenjs/core/message` | [MESSAGES-PEN](docs/MESSAGES-PEN.md) |
105
104
  | a text template | `@jarenjs/linq/jtlt` | `@jarenjs/json/jtlt` | [JTLT-PEN](docs/JTLT-PEN.md) |
106
105
  | a Studio project | `@jarenjs/linq/project` | `@jarenjs/studio` | [PROJECT-PEN](docs/PROJECT-PEN.md) |
@@ -730,8 +729,8 @@ Every subpath a consumer can import, derived from the manifest by
730
729
  | `@jarenjs/linq/project` | JavaScript | declared |
731
730
  | `@jarenjs/linq/jtlt` | JavaScript | declared |
732
731
  | `@jarenjs/linq/messages` | JavaScript | declared |
733
- | `@jarenjs/linq/ai` | JavaScript | declared |
734
732
  | `@jarenjs/linq/formula` | JavaScript | declared |
733
+ | `@jarenjs/linq/authoring` | JavaScript | declared |
735
734
  <!--/fact-->
736
735
 
737
736
  Explicit cross-provider joins use `federate({ sources, maxRows, maxBytes,
@@ -744,3 +743,13 @@ planning, buffering limits and cleanup guarantees.
744
743
  ## Saved formula documents
745
744
 
746
745
  `@jarenjs/linq/formula` exports `defineFormula` for immutable JSON Query profiles with revision, bindings and schema/helper references. See [FORMULA-PEN](docs/FORMULA-PEN.md); execution remains in json/formula.
746
+
747
+ ## Authoring custom documents
748
+
749
+ `@jarenjs/linq/authoring` exposes `DocumentBuilder`, `snapshot`, `optionsOf` and
750
+ `captureQuery` for pens that produce ordinary JSON documents. Builder snapshots
751
+ are deeply frozen; `with()` creates a new instance of the same subclass.
752
+ `optionsOf` enforces a closed set of option keys. `captureQuery` uses the same
753
+ expression capture as the built-in pens; external names are explicit and an
754
+ undeclared name retains the `JL0104` refusal. Compilation remains the consumer's
755
+ responsibility. Use expression `get()` for field names that overlap methods.
package/docs/JSLT-PEN.md CHANGED
@@ -893,7 +893,7 @@ non-judgement is itself gated.
893
893
  ### 6.6 When not to reach for this pen
894
894
 
895
895
  - **The stylesheet is data.** A `$jslt` document loaded from a file,
896
- authored by a model ([@jarenjs/ai](../../ai/README.md)'s authoring
896
+ authored by a model ([Tangle authoring](https://github.com/jklarenbeek/tangleai/blob/main/packages/jaren/docs/AUTHORING.md)'s authoring
897
897
  profile writes them) or edited in the studio is a value;
898
898
  `compileJsltStylesheet` takes it directly and nothing here has to be in
899
899
  the path.
@@ -16,9 +16,9 @@ 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
- (<!--fact:coverage.subpaths-->`./ai`, `./app`, `./charts`, `./contract`, `./db`, `./flow`, `./forms`, `./formula`, `./jslt`, `./jtlt`, `./messages`, `./migration`, `./model`, `./project`, `./schema`<!--/fact-->); `.` stays the chain.
19
+ (<!--fact:coverage.subpaths-->`./app`, `./authoring`, `./charts`, `./contract`, `./db`, `./flow`, `./forms`, `./formula`, `./jslt`, `./jtlt`, `./messages`, `./migration`, `./model`, `./project`, `./schema`<!--/fact-->); `.` stays the chain.
20
20
 
21
- Coverage: <!--fact:coverage.pens-->15 public pen/client subpaths beside the chain; 69/69 owned schema keywords have dedicated emission routes.<!--/fact-->
21
+ Coverage: <!--fact:coverage.pens-->15 public pen/client/authoring subpaths beside the chain; 69/69 owned schema keywords have dedicated emission routes.<!--/fact-->
22
22
 
23
23
  **This document is the family's normative reference**: §1 states the
24
24
  rules every pen keeps, §1.3 the error codes they share, and §4–§7 the
@@ -36,7 +36,7 @@ is the index of those guides, and it is how a reader reaches any of them.
36
36
  <!--fact:pens.index-->
37
37
  | Document | Lines | What it writes, and when to open it |
38
38
  |---|---:|---|
39
- | [LINQ-FORMAT.md](LINQ-FORMAT.md) | 965 | 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 |
39
+ | [LINQ-FORMAT.md](LINQ-FORMAT.md) | 944 | 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
40
  | [QUERY-PEN.md](QUERY-PEN.md) | 1,752 | 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
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 |
42
42
  | [MODEL-PEN.md](MODEL-PEN.md) | 1,116 | `./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 |
@@ -46,7 +46,6 @@ is the index of those guides, and it is how a reader reaches any of them.
46
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 |
47
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 |
48
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
49
  | [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
50
  | [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
51
  | [PROJECT-PEN.md](PROJECT-PEN.md) | 81 | `./project` — Studio projects with named, typed files. **Read it when** you want a portable editor workspace containing documents written by several pens. |
@@ -95,7 +94,6 @@ data rather than for a program has no compile step to be faithful to.
95
94
  | `$fsm` 0.1, `$dag` 0.1 (`jaren-fsm`, `jaren-dag`) | `@jarenjs/flow` | **`./flow`** | [FLOW-PEN.md](FLOW-PEN.md) |
96
95
  | `jaren-app` 0.1 | `@jarenjs/app` | **`./app`** | [APP-PEN.md](APP-PEN.md) |
97
96
  | `x-form` on JSON Schema | `@jarenjs/forms` | **`./forms`** | [FORMS-PEN.md](FORMS-PEN.md) |
98
- | AI action program | `@jarenjs/ai` | **`./ai`** | [AI-PEN.md](AI-PEN.md) |
99
97
  | message catalogs / MessageSpec | `@jarenjs/core`, validate/forms/contract | **`./messages`** | [MESSAGES-PEN.md](MESSAGES-PEN.md) |
100
98
  | `$jtlt` 0.1 (`jaren-jtlt`) | `@jarenjs/json` | **`./jtlt`** | [JTLT-PEN.md](JTLT-PEN.md) |
101
99
  | `jaren-project` | `@jarenjs/studio` | **`./project`** | [PROJECT-PEN.md](PROJECT-PEN.md) |
@@ -205,7 +203,7 @@ and the bundle is the byte count the tree-shaking probe builds.
205
203
  <!--fact:pens.census-->
206
204
  | Document | Subpath | Lines | Mapping rows | Worked examples | Refusals | Bundle |
207
205
  |---|---|---:|---:|---:|---:|---:|
208
- | [LINQ-FORMAT.md](LINQ-FORMAT.md) | — | 965 | — | — | — | — |
206
+ | [LINQ-FORMAT.md](LINQ-FORMAT.md) | — | 944 | — | — | — | — |
209
207
  | [QUERY-PEN.md](QUERY-PEN.md) | `.` | 1,752 | 34 | 8 | 15 | 175,221 B |
210
208
  | [SCHEMA-PEN.md](SCHEMA-PEN.md) | `./schema` | 1,205 | 82 | 10 | 4 | 35,285 B |
211
209
  | [MODEL-PEN.md](MODEL-PEN.md) | `./model` | 1,116 | 30 | 6 | 3 | 44,143 B |
@@ -215,14 +213,13 @@ and the bundle is the byte count the tree-shaking probe builds.
215
213
  | [FLOW-PEN.md](FLOW-PEN.md) | `./flow` | 1,033 | 16 | 7 | 3 | 18,478 B |
216
214
  | [APP-PEN.md](APP-PEN.md) | `./app` | 1,143 | 22 | 7 | 3 | 49,574 B |
217
215
  | [FORMS-PEN.md](FORMS-PEN.md) | `./forms` | 940 | 18 | 6 | 3 | 39,441 B |
218
- | [AI-PEN.md](AI-PEN.md) | `./ai` | 98 | 12 | 1 | 3 | 15,472 B |
219
216
  | [MESSAGES-PEN.md](MESSAGES-PEN.md) | `./messages` | 105 | 9 | 2 | 1 | 16,931 B |
220
217
  | [JTLT-PEN.md](JTLT-PEN.md) | `./jtlt` | 83 | 13 | 1 | 2 | 15,293 B |
221
218
  | [PROJECT-PEN.md](PROJECT-PEN.md) | `./project` | 81 | 9 | 1 | 1 | 13,793 B |
222
219
  | [CHARTS-PEN.md](CHARTS-PEN.md) | `./charts` | 94 | 21 | 1 | 1 | 15,582 B |
223
220
  | [DB-CLIENT.md](DB-CLIENT.md) | `./db` | 1,085 | 46 | 4 | 2 | 675,752 B |
224
221
  | [FORMULA-PEN.md](FORMULA-PEN.md) | `./formula` | 41 | 2 | — | — | 14,850 B |
225
- | **17 documents** | | **12,698** | **380** | **73** | | |
222
+ | **16 documents** | | **12,579** | **368** | **72** | | |
226
223
  <!--/fact-->
227
224
 
228
225
  A pen whose mapping rows are far below its worked examples is a pen
@@ -241,10 +238,10 @@ throws, so this table moves when a pen's source does.
241
238
  <!--fact:pens.codes-->
242
239
  | Code | Raised by |
243
240
  |---|---|
244
- | `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) |
245
- | `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) |
241
+ | `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), [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) |
242
+ | `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) |
246
243
  | `JL0103` | [SCHEMA-PEN.md](SCHEMA-PEN.md), [CONTRACT-PEN.md](CONTRACT-PEN.md) |
247
- | `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) |
244
+ | `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), [JTLT-PEN.md](JTLT-PEN.md) |
248
245
  | `JL0105` | [QUERY-PEN.md](QUERY-PEN.md) |
249
246
  | `JL0106` | [MIGRATION-PEN.md](MIGRATION-PEN.md) |
250
247
  | `JL0107` | [DB-CLIENT.md](DB-CLIENT.md) |
@@ -273,7 +270,6 @@ it and each document publishes it. The rounded column is what
273
270
  | `@jarenjs/linq/flow` | [FLOW-PEN.md](FLOW-PEN.md) | 18,478 B | 18 kB |
274
271
  | `@jarenjs/linq/app` | [APP-PEN.md](APP-PEN.md) | 49,574 B | 50 kB |
275
272
  | `@jarenjs/linq/forms` | [FORMS-PEN.md](FORMS-PEN.md) | 39,441 B | 39 kB |
276
- | `@jarenjs/linq/ai` | [AI-PEN.md](AI-PEN.md) | 15,472 B | 15 kB |
277
273
  | `@jarenjs/linq/messages` | [MESSAGES-PEN.md](MESSAGES-PEN.md) | 16,931 B | 17 kB |
278
274
  | `@jarenjs/linq/jtlt` | [JTLT-PEN.md](JTLT-PEN.md) | 15,293 B | 15 kB |
279
275
  | `@jarenjs/linq/project` | [PROJECT-PEN.md](PROJECT-PEN.md) | 13,793 B | 14 kB |
@@ -796,23 +792,6 @@ it says.
796
792
  | `computed` | query | `x-form.computed` | the field's derived value, mapped to plain JSON. A failure leaves the value absent |
797
793
  | `message` | string or MessageSpec | `x-form.message`, verbatim | what an `assert` failure renders — an inline template, or `{ $msgid, message?, params? }` for the catalog |
798
794
 
799
- ### The AI program pen — [AI-PEN.md §2](AI-PEN.md)
800
-
801
- | Factory / method | Emits | Type | Status |
802
- |---|---|---|---|
803
- | `program(slots?)` | empty `steps` | declared input names | native |
804
- | `chunk(from, as, options?)`, `.chunk(...)` | chunk; strategy and size | result family | native |
805
- | `grep(from, as, options)`, `.grep(...)` | grep; pattern, flags, limit | match-list slot | native |
806
- | `select(from, as, query)`, `.select(...)` | select; query JSON | result slot | native |
807
- | `stat(from, as)`, `.stat(...)` | stat | result slot | native |
808
- | `peek(from, as)`, `.peek(...)` | peek | result slot | native |
809
- | `map(from, as, prompt)`, `.map(...)` | map; bounded instruction | result family | native |
810
- | `reduce(from, as, query, options?)`, `.reduce(...)` | reduce; query JSON and optional `outputSchema` | result slot | native |
811
- | `answer(from, options?)`, `.answer(...)` | answer; optional chars, no as | terminal program | native |
812
- | `.step(step)` | appends a public step | tracks its input/result names | native |
813
- | `from(document)` | raw program | no binding-order inference | native |
814
- | `.schema`, `.toJSON()` | frozen public JSON | program document | native |
815
-
816
795
  ### The messages pen — [MESSAGES-PEN.md §2](MESSAGES-PEN.md)
817
796
 
818
797
  | Method | Emits | Type | Status |
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@jarenjs/linq",
3
3
  "private": false,
4
- "version": "0.83.3",
4
+ "version": "0.84.3",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
7
7
  "types": "./types/index.d.ts",
@@ -64,13 +64,13 @@
64
64
  "types": "./types/messages.d.ts",
65
65
  "default": "./src/messages/index.js"
66
66
  },
67
- "./ai": {
68
- "types": "./types/ai.d.ts",
69
- "default": "./src/ai/index.js"
70
- },
71
67
  "./formula": {
72
68
  "types": "./types/formula.d.ts",
73
69
  "default": "./src/formula/index.js"
70
+ },
71
+ "./authoring": {
72
+ "types": "./types/authoring.d.ts",
73
+ "default": "./src/authoring.js"
74
74
  }
75
75
  },
76
76
  "files": [
@@ -108,13 +108,13 @@
108
108
  "prepack": "npm run build:types"
109
109
  },
110
110
  "dependencies": {
111
- "@jarenjs/core": "^0.83.3",
112
- "@jarenjs/json": "^0.83.3"
111
+ "@jarenjs/core": "^0.84.3",
112
+ "@jarenjs/json": "^0.84.3"
113
113
  },
114
114
  "peerDependencies": {
115
- "@jarenjs/db": "^0.83.3",
116
- "@jarenjs/formats": "^0.83.3",
117
- "@jarenjs/validate": "^0.83.3"
115
+ "@jarenjs/db": "^0.84.3",
116
+ "@jarenjs/formats": "^0.84.3",
117
+ "@jarenjs/validate": "^0.84.3"
118
118
  },
119
119
  "peerDependenciesMeta": {
120
120
  "@jarenjs/db": {
@@ -0,0 +1,4 @@
1
+ //@ts-check
2
+ /** Immutable document building and query capture for authored-format pens. */
3
+ export { DocumentBuilder, optionsOf, snapshot } from './authored.js';
4
+ export { captureQuery } from './capture-root.js';
@@ -0,0 +1,17 @@
1
+ import type { Expr } from './index.js';
2
+
3
+ /** A captured immutable JSON document; replacement preserves subclasses. */
4
+ export class DocumentBuilder<T extends object = Record<string, unknown>> {
5
+ constructor(document: T);
6
+ readonly schema: Readonly<T>;
7
+ toJSON(): Readonly<T>;
8
+ with(patch: Partial<T>): this;
9
+ }
10
+ /** Snapshot and freeze a JSON value; invalid JSON retains LinqBuildError codes. */
11
+ export function snapshot<T>(value: T): Readonly<T>;
12
+ /** Reject options outside the named closed boundary, then snapshot. */
13
+ export function optionsOf<T extends object>(value: T, keys: readonly string[], what: string): Readonly<T>;
14
+ /** Capture a query; undeclared external names retain JL0104 errors. */
15
+ export function captureQuery<T, E extends string>(what: string, externals: readonly E[],
16
+ rule: (value: Expr<T>, externals: Record<E, unknown>) => unknown,
17
+ options?: { advice?: (name: string) => string; fold?: boolean; noun?: string }): unknown;
@@ -408,7 +408,7 @@ export type ToolName<S extends string> =
408
408
  S extends `${infer A}.${infer B}` ? `${A}_${ToolName<B>}` : S;
409
409
 
410
410
  /** One tool definition, typed by the operation it invokes — the
411
- * `ToolDef` shape `@jarenjs/ai`'s `createToolbox().add` takes. */
411
+ * `ToolDef` shape browser or host operation registries takes. */
412
412
  export type TypedTool<C> = {
413
413
  [K in keyof InvokableOf<C>]: {
414
414
  name: ToolName<Extract<K, string>>;
package/docs/AI-PEN.md DELETED
@@ -1,98 +0,0 @@
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-->15,472<!--/fact--> bytes**.
98
- Its tree probe excludes AI, other target engines and the query chain.
package/src/ai/index.js DELETED
@@ -1,76 +0,0 @@
1
- //@ts-check
2
- /** The AI action document, authored without an AI client, environment or engine. */
3
- import { DocumentBuilder, optionsOf, snapshot } from '../authored.js';
4
- import { captureQuery } from '../capture-root.js';
5
- import { LinqBuildError } from '../errors.js';
6
-
7
- const FIELDS = {
8
- chunk: ['strategy', 'size'], grep: ['pattern', 'flags', 'limit'], select: ['query'],
9
- stat: [], peek: [], map: ['prompt'], reduce: ['query', 'outputSchema'], answer: ['chars'],
10
- };
11
- function make(op, from, as, options) {
12
- if (typeof from !== 'string' || !from || (op !== 'answer' && (typeof as !== 'string' || !as)))
13
- throw new LinqBuildError('JL0101', 'A step needs a source name and, except answer, a result name');
14
- return snapshot({ op, from, ...(op === 'answer' ? {} : { as }), ...optionsOf(options, FIELDS[op], `${op}()`) });
15
- }
16
- function expression(value) {
17
- // The program query receives only its input document: an undeclared external
18
- // is the shared capture's JL0104 refusal, not an invented environment lookup.
19
- return typeof value === 'function' ? captureQuery('program query', [], value, { fold: false }) : value;
20
- }
21
- /** Split a slot into a family of pieces. */
22
- export function chunk(from, as, options = {}) { return make('chunk', from, as, options); }
23
- /** Find matching slots with a bounded pattern and options. */
24
- export function grep(from, as, options) { return make('grep', from, as, options); }
25
- /** Query one JSON slot; a callback is captured as ordinary query JSON. */
26
- export function select(from, as, query) { return make('select', from, as, { query: expression(query) }); }
27
- /** Inspect counts and shape. */
28
- export function stat(from, as) { return make('stat', from, as, {}); }
29
- /** Inspect a slot's metadata and excerpt. */
30
- export function peek(from, as) { return make('peek', from, as, {}); }
31
- /** Ask one bounded prompt per member of a slot/family. */
32
- export function map(from, as, prompt) { return make('map', from, as, { prompt }); }
33
- /** Query the collected results of an earlier map. */
34
- export function reduce(from, as, query, options = {}) {
35
- return make('reduce', from, as, { query: expression(query), ...optionsOf(options, ['outputSchema'], 'reduce()') });
36
- }
37
- /** Declare the final answer slot and optional excerpt bound. */
38
- export function answer(from, options = {}) { return make('answer', from, undefined, options); }
39
-
40
- /** An immutable ordered program; binding types are phantoms. */
41
- export class ProgramBuilder extends DocumentBuilder {
42
- /** Append a public step. An answer closes this builder. @param {any} value */
43
- step(value) {
44
- if (this.schema.steps.at(-1)?.op === 'answer')
45
- throw new LinqBuildError('JL0102', 'answer is terminal; start another program to append work');
46
- if (value === null || typeof value !== 'object' || !Object.hasOwn(FIELDS, value.op))
47
- throw new LinqBuildError('JL0101', 'step() needs a known program operation');
48
- const step = optionsOf(value, ['op', 'from', ...(value.op === 'answer' ? [] : ['as']), ...FIELDS[value.op]], 'step()');
49
- return this.with({ steps: [...this.schema.steps, step] });
50
- }
51
- /** Append a chunk step. */
52
- chunk(from, as, options = {}) { return this.step(chunk(from, as, options)); }
53
- /** Append a grep step. */
54
- grep(from, as, options) { return this.step(grep(from, as, options)); }
55
- /** Append a select step. */
56
- select(from, as, query) { return this.step(select(from, as, query)); }
57
- /** Append a stat step. */
58
- stat(from, as) { return this.step(stat(from, as)); }
59
- /** Append a peek step. */
60
- peek(from, as) { return this.step(peek(from, as)); }
61
- /** Append a map step. */
62
- map(from, as, prompt) { return this.step(map(from, as, prompt)); }
63
- /** Append a reduce step. */
64
- reduce(from, as, query, options = {}) { return this.step(reduce(from, as, query, options)); }
65
- /** Append the terminal answer. */
66
- answer(from, options = {}) { return this.step(answer(from, options)); }
67
- }
68
-
69
- /** Declare input slot names for TypeScript; they never become document members. @param {readonly string[]} slots */
70
- export function program(slots = []) {
71
- if (!Array.isArray(slots) || slots.some((name) => typeof name !== 'string' || !name))
72
- throw new LinqBuildError('JL0101', 'program() takes input slot names');
73
- return new ProgramBuilder({ steps: [] });
74
- }
75
- /** A raw public program. Compilation remains authoritative for names and semantics. @param {any} document */
76
- export function from(document) { return new ProgramBuilder(document); }
package/types/ai.d.ts DELETED
@@ -1,56 +0,0 @@
1
- import type { Json } from './schema.js';
2
- import type { ExprBase, UnknownExpr } from './index.js';
3
- export type Query = Json | ((value: UnknownExpr) => ExprBase<unknown> | Json);
4
- export interface ChunkOptions { readonly strategy?: 'size' | 'line' | 'separator'; readonly size?: number }
5
- export interface GrepOptions { readonly pattern: string; readonly flags?: 'i' | 'm' | 'im' | ''; readonly limit?: number }
6
- export interface AnswerOptions { readonly chars?: number }
7
- export interface ReduceOptions { readonly outputSchema?: Json }
8
- export interface StepOptions {
9
- chunk: ChunkOptions; grep: GrepOptions; select: { readonly query: Json };
10
- stat: {}; peek: {}; map: { readonly prompt: string }; reduce: { readonly query: Json } & ReduceOptions; answer: AnswerOptions;
11
- }
12
- export type Operation = keyof StepOptions;
13
- export type Step<K extends Operation, F extends string = string, N extends string = string> = {
14
- readonly op: K; readonly from: F;
15
- } & (K extends 'answer' ? {} : { readonly as: N }) & StepOptions[K];
16
- export type StepDocument = { [K in Operation]: Step<K> }[Operation];
17
- export interface ProgramDocument { readonly steps: readonly StepDocument[] }
18
- export function chunk<const F extends string, const N extends string>(from: F, as: N, options?: ChunkOptions): Step<'chunk', F, N>;
19
- export function grep<const F extends string, const N extends string>(from: F, as: N, options: GrepOptions): Step<'grep', F, N>;
20
- export function select<const F extends string, const N extends string>(from: F, as: N, query: Query): Step<'select', F, N>;
21
- export function stat<const F extends string, const N extends string>(from: F, as: N): Step<'stat', F, N>;
22
- export function peek<const F extends string, const N extends string>(from: F, as: N): Step<'peek', F, N>;
23
- export function map<const F extends string, const N extends string>(from: F, as: N, prompt: string): Step<'map', F, N>;
24
- export function reduce<const F extends string, const N extends string>(from: F, as: N, query: Query, options?: ReduceOptions): Step<'reduce', F, N>;
25
- export function answer<const F extends string>(from: F, options?: AnswerOptions): Step<'answer', F>;
26
-
27
- type Kind = Exclude<Operation, 'answer'> | 'slot';
28
- type Bindings = Record<string, Kind>;
29
- type Names<B, K extends Kind = Kind> = { [N in keyof B]: B[N] extends K ? N : never }[keyof B] & string;
30
- type Single<B> = Names<B, Exclude<Kind, 'chunk' | 'map'>>;
31
- type Fresh<B, N extends string> = N extends keyof B ? B[N] extends 'slot' ? N : never : N;
32
- type Add<B, N extends string, K extends Kind> = Omit<B, N> & Record<N, K>;
33
- type InputFor<B, K extends Operation> = K extends 'reduce' ? Names<B, 'map'> : K extends 'select' | 'answer' ? Single<B> : Names<B>;
34
- type StepKeys<K extends Operation> = 'op' | 'from' | (K extends 'answer' ? never : 'as') | keyof StepOptions[K];
35
- type CheckStep<B, T extends StepDocument> = Exclude<keyof T, StepKeys<T['op']>> extends never ? T['from'] extends InputFor<B, T['op']>
36
- ? T extends { readonly as: infer N extends string } ? N extends Fresh<B, N> ? unknown : never : unknown : never : never;
37
- type AfterStep<B, T extends StepDocument> = T extends { readonly as: infer N extends string; readonly op: infer K extends Kind } ? Add<B, N, K> : B;
38
- export class ProgramBuilder<B extends Bindings = {}, Done extends boolean = false> {
39
- protected constructor();
40
- readonly __bindings: B;
41
- readonly __done: Done;
42
- readonly schema: ProgramDocument;
43
- toJSON(): ProgramDocument;
44
- step<const T extends StepDocument>(this: ProgramBuilder<B, false>, step: T & CheckStep<B, T>): ProgramBuilder<AfterStep<B, T>, T['op'] extends 'answer' ? true : false>;
45
- chunk<N extends string>(this: ProgramBuilder<B, false>, from: Names<B>, as: Fresh<B, N>, options?: ChunkOptions): ProgramBuilder<Add<B, N, 'chunk'>>;
46
- grep<N extends string>(this: ProgramBuilder<B, false>, from: Names<B>, as: Fresh<B, N>, options: GrepOptions): ProgramBuilder<Add<B, N, 'grep'>>;
47
- select<N extends string>(this: ProgramBuilder<B, false>, from: Single<B>, as: Fresh<B, N>, query: Query): ProgramBuilder<Add<B, N, 'select'>>;
48
- stat<N extends string>(this: ProgramBuilder<B, false>, from: Names<B>, as: Fresh<B, N>): ProgramBuilder<Add<B, N, 'stat'>>;
49
- peek<N extends string>(this: ProgramBuilder<B, false>, from: Names<B>, as: Fresh<B, N>): ProgramBuilder<Add<B, N, 'peek'>>;
50
- map<N extends string>(this: ProgramBuilder<B, false>, from: Names<B>, as: Fresh<B, N>, prompt: string): ProgramBuilder<Add<B, N, 'map'>>;
51
- reduce<N extends string>(this: ProgramBuilder<B, false>, from: Names<B, 'map'>, as: Fresh<B, N>, query: Query, options?: ReduceOptions): ProgramBuilder<Add<B, N, 'reduce'>>;
52
- answer(this: ProgramBuilder<B, false>, from: Single<B>, options?: AnswerOptions): ProgramBuilder<B, true>;
53
- }
54
- export function program<const S extends readonly string[] = []>(slots?: S): ProgramBuilder<Record<S[number], 'slot'>>;
55
- /** Raw documents make no binding-order or terminal-state inference. */
56
- export function from(document: ProgramDocument): ProgramBuilder<Record<string, Kind>, boolean>;