@astrale-os/sdk 0.6.0-beta.43 → 0.6.0-beta.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/README.md +6 -6
  2. package/dist/application/action/authoring.d.ts +1 -1
  3. package/dist/application/action/context.d.ts +9 -8
  4. package/dist/application/error/error.d.ts +7 -7
  5. package/dist/application/error/error.js +12 -12
  6. package/dist/application/error/index.d.ts +1 -1
  7. package/dist/application/error/index.js +1 -1
  8. package/dist/application/mutation/authoring/builder.d.ts +12 -12
  9. package/dist/application/mutation/authoring/builder.js +3 -3
  10. package/dist/application/mutation/authoring/guard.d.ts +2 -2
  11. package/dist/application/mutation/authoring/index.d.ts +1 -1
  12. package/dist/application/mutation/define.d.ts +8 -8
  13. package/dist/application/mutation/define.js +12 -12
  14. package/dist/application/mutation/execution/execute.d.ts +2 -2
  15. package/dist/application/mutation/execution/execute.js +10 -10
  16. package/dist/application/mutation/execution/executor.d.ts +2 -2
  17. package/dist/application/mutation/execution/executor.js +2 -2
  18. package/dist/application/mutation/index.d.ts +2 -2
  19. package/dist/application/mutation/legacy/v6/define.d.ts +1 -1
  20. package/dist/application/mutation/legacy/v6/mutation.d.ts +1 -1
  21. package/dist/application/mutation/mutation.d.ts +12 -12
  22. package/dist/application/query/composite/builder.js +1 -1
  23. package/dist/application/query/define.d.ts +1 -1
  24. package/dist/application/query/define.js +3 -3
  25. package/dist/application/query/defined/define.d.ts +45 -0
  26. package/dist/application/query/defined/define.js +96 -0
  27. package/dist/application/query/{recipe/recipe.d.ts → defined/query.d.ts} +21 -20
  28. package/dist/application/query/definition.d.ts +5 -5
  29. package/dist/application/query/definition.js +4 -4
  30. package/dist/application/query/execution/bounds.d.ts +5 -5
  31. package/dist/application/query/execution/bounds.js +2 -2
  32. package/dist/application/query/execution/client.d.ts +21 -0
  33. package/dist/application/query/execution/client.js +1 -0
  34. package/dist/application/query/execution/crossings.d.ts +3 -3
  35. package/dist/application/query/execution/crossings.js +23 -23
  36. package/dist/application/query/execution/error.d.ts +8 -8
  37. package/dist/application/query/execution/error.js +5 -5
  38. package/dist/application/query/execution/execute.d.ts +4 -4
  39. package/dist/application/query/execution/executor.d.ts +2 -2
  40. package/dist/application/query/execution/rows.d.ts +5 -5
  41. package/dist/application/query/execution/rows.js +42 -30
  42. package/dist/application/query/execution/{recipe.d.ts → run.d.ts} +21 -31
  43. package/dist/application/query/execution/{recipe.js → run.js} +49 -49
  44. package/dist/application/query/execution/split.d.ts +3 -3
  45. package/dist/application/query/execution/split.js +1 -1
  46. package/dist/application/query/handles/build.d.ts +1 -1
  47. package/dist/application/query/handles/domain.d.ts +1 -1
  48. package/dist/application/query/handles/handles.d.ts +14 -12
  49. package/dist/application/query/handles/plan.d.ts +10 -9
  50. package/dist/application/query/handles/plan.js +1 -1
  51. package/dist/application/query/handles/reads.js +4 -4
  52. package/dist/application/query/handles/rows.d.ts +1 -1
  53. package/dist/application/query/index.d.ts +9 -6
  54. package/dist/application/query/index.js +3 -3
  55. package/dist/application/query/language/index.d.ts +1 -1
  56. package/dist/application/query/language/query.d.ts +5 -5
  57. package/dist/application/query/legacy/v6/collection/union.d.ts +1 -1
  58. package/dist/application/query/legacy/v6/collection/union.js +3 -3
  59. package/dist/application/query/legacy/v6/define.d.ts +1 -1
  60. package/dist/application/query/legacy/v6/define.js +3 -3
  61. package/dist/application/query/legacy/v6/execution/execute.d.ts +6 -4
  62. package/dist/application/query/legacy/v6/index.d.ts +2 -1
  63. package/dist/application/query/legacy/v6/index.js +1 -1
  64. package/dist/application/query/query.d.ts +3 -3
  65. package/dist/application/routes/admission.d.ts +1 -1
  66. package/dist/application/routes/admission.js +2 -2
  67. package/dist/application/routes/index.d.ts +3 -2
  68. package/dist/application/routes/route.d.ts +1 -1
  69. package/dist/application/runtime/declarations.d.ts +6 -6
  70. package/dist/application/runtime/define.d.ts +5 -5
  71. package/dist/application/workflow/durable.d.ts +9 -9
  72. package/dist/execution/invocation/capabilities/bind.d.ts +1 -1
  73. package/dist/execution/invocation/capabilities/bind.js +8 -8
  74. package/dist/execution/invocation/dispatch/action.js +2 -2
  75. package/dist/execution/invocation/dispatch/workflow.js +2 -2
  76. package/dist/execution/invocation/result/index.d.ts +1 -1
  77. package/dist/execution/invocation/result/index.js +1 -1
  78. package/dist/execution/invocation/result/undeclared.d.ts +7 -0
  79. package/dist/execution/invocation/result/undeclared.js +18 -0
  80. package/dist/execution/workflows/failure.d.ts +7 -7
  81. package/dist/execution/workflows/failure.js +19 -18
  82. package/dist/execution/workflows/receiver.d.ts +1 -1
  83. package/dist/execution/workflows/run.js +2 -2
  84. package/dist/execution/workflows/steps.d.ts +1 -1
  85. package/dist/platform/schema/class-authoring.d.ts +2 -8
  86. package/dist/platform/schema/index.d.ts +1 -1
  87. package/dist/platform/schema/kernel-compiled.d.ts +1 -1
  88. package/dist/platform/schema/kernel-compiled.js +1 -1
  89. package/dist/platform/schema/kernel.d.ts +4 -2
  90. package/dist/testing/index.d.ts +2 -2
  91. package/dist/testing/index.js +1 -1
  92. package/dist/testing/{recipe.d.ts → query.d.ts} +5 -5
  93. package/dist/testing/{recipe.js → query.js} +9 -9
  94. package/dist/tooling/cli/orchestrate.js +7 -6
  95. package/dist/tooling/linter/adapters/typescript/{recipe-definitions.d.ts → definition-calls.d.ts} +10 -10
  96. package/dist/tooling/linter/adapters/typescript/{recipe-definitions.js → definition-calls.js} +10 -10
  97. package/dist/tooling/linter/adapters/typescript/index.d.ts +1 -1
  98. package/dist/tooling/linter/adapters/typescript/index.js +1 -1
  99. package/dist/tooling/linter/adapters/typescript/query-observation.js +6 -6
  100. package/dist/tooling/linter/implementations/source/definitions.d.ts +2 -0
  101. package/dist/tooling/linter/implementations/source/{recipes.js → definitions.js} +27 -27
  102. package/dist/tooling/linter/implementations/source/global.js +8 -6
  103. package/dist/tooling/linter/implementations/source/mutations.js +5 -5
  104. package/dist/tooling/linter/implementations/source/shared.d.ts +2 -2
  105. package/dist/tooling/linter/implementations/source/shared.js +7 -7
  106. package/dist/tooling/linter/implementations/source/verifiers.js +2 -2
  107. package/dist/tooling/linter/policy/generated.js +3 -3
  108. package/dist/tooling/linter/requirements/registry.js +4 -4
  109. package/docs/durable-workflows.md +8 -8
  110. package/docs/handler-dependencies.md +1 -1
  111. package/docs/legacy/query-v6.md +6 -6
  112. package/docs/queries.md +38 -38
  113. package/package.json +6 -6
  114. package/dist/application/query/recipe/define.d.ts +0 -44
  115. package/dist/application/query/recipe/define.js +0 -95
  116. package/dist/execution/invocation/result/recipe.d.ts +0 -6
  117. package/dist/execution/invocation/result/recipe.js +0 -17
  118. package/dist/tooling/linter/implementations/source/recipes.d.ts +0 -2
  119. /package/dist/application/query/{recipe/recipe.js → defined/query.js} +0 -0
@@ -20,12 +20,12 @@ interface Item {
20
20
  readonly target?: Item;
21
21
  readonly branches: Readonly<Record<string, unknown>>;
22
22
  }
23
- /** What renders one answer: the recipe it errs for, the Domain's Classes, the answer's facts. */
23
+ /** What renders one answer: the Query it errs for, the Domain's Classes, the answer's facts. */
24
24
  export declare class Renderer {
25
25
  #private;
26
- readonly recipe: string;
26
+ readonly query: string;
27
27
  readonly model: Model;
28
- constructor(recipe: string, model: Model, answer: QueryListAnswer<unknown, unknown>);
28
+ constructor(query: string, model: Model, answer: QueryListAnswer<unknown, unknown>);
29
29
  /** A Node row, or `undefined` when the reader may not read it and the read does not keep it. */
30
30
  node(item: Item, pattern: Pattern): Json | undefined;
31
31
  /** An Edge root row: its identity, its values and both ends by role. */
@@ -36,7 +36,7 @@ export declare class Renderer {
36
36
  rootOne(item: Item, root: NodeRootPlan | EdgeRootPlan): Json;
37
37
  }
38
38
  /** The answer of a root read with nothing to read: its verb's empty answer. */
39
- export declare function emptyResult(recipe: string, plan: ReadPlan): unknown;
39
+ export declare function emptyResult(query: string, plan: ReadPlan): unknown;
40
40
  /** A root read's result from its answer, by its verb. */
41
- export declare function rootResult(recipe: string, model: Model, plan: ReadPlan, answer: unknown): unknown;
41
+ export declare function rootResult(query: string, model: Model, plan: ReadPlan, answer: unknown): unknown;
42
42
  export {};
@@ -1,15 +1,16 @@
1
1
  import { factKey, factsOf } from '../answer/facts.js';
2
+ import { QueryValueMismatchError } from '../failure.js';
2
3
  import { ROW_KEYS } from '../handles/model.js';
3
4
  import { QueryError } from './error.js';
4
5
  const NO_TAKEN = new Set();
5
6
  const freeze = (value) => Object.freeze(value);
6
- /** What renders one answer: the recipe it errs for, the Domain's Classes, the answer's facts. */
7
+ /** What renders one answer: the Query it errs for, the Domain's Classes, the answer's facts. */
7
8
  export class Renderer {
8
- recipe;
9
+ query;
9
10
  model;
10
11
  #facts;
11
- constructor(recipe, model, answer) {
12
- this.recipe = recipe;
12
+ constructor(query, model, answer) {
13
+ this.query = query;
13
14
  this.model = model;
14
15
  this.#facts = factsOf(answer);
15
16
  }
@@ -22,7 +23,7 @@ export class Renderer {
22
23
  }
23
24
  #class(item) {
24
25
  if (item.class === undefined)
25
- throw new QueryError('UNSUPPORTED', this.recipe, { feature: 'classes' });
26
+ throw new QueryError('UNSUPPORTED', this.query, { feature: 'classes' });
26
27
  return item.class;
27
28
  }
28
29
  /**
@@ -46,11 +47,19 @@ export class Renderer {
46
47
  return (candidates.find((c) => candidates.every((o) => this.model.satisfies(c).has(o.key))) ??
47
48
  candidates[0]);
48
49
  }
49
- /** The values of a fact by the layout of a Class: flat names, then groups (F6). */
50
+ /**
51
+ * The values of a fact by the layout of a Class: flat names, then groups (F6). The row types a
52
+ * required value as present, so a fact without one breaks the Domain as a value that does not
53
+ * match its Property does.
54
+ */
50
55
  #values(fact, c, taken) {
51
56
  if (c === undefined)
52
57
  return {};
53
58
  const values = (this.#fact(fact)?.values ?? {});
59
+ for (const property of c.properties) {
60
+ if (property.definition.required && values[property.key] === undefined)
61
+ throw new QueryValueMismatchError(`${factKey(fact)} has no value for ${property.key}, which its Property requires in this Domain`);
62
+ }
54
63
  const { flat, groups } = this.model.layout(c, taken);
55
64
  const row = {};
56
65
  for (const [name, key] of flat)
@@ -83,12 +92,15 @@ export class Renderer {
83
92
  Object.assign(row, this.#values(id, layout, ROW_KEYS));
84
93
  for (const branch of pattern.branches)
85
94
  row[branch.key] = this.#relation(item, branch);
86
- if ('edge' in pattern && pattern.edge && item.edge !== undefined)
87
- row.$edge = this.#edgeValues(item.edge);
95
+ const edge = pattern.edge;
96
+ if (edge !== undefined && item.edge !== undefined)
97
+ row.$edge = this.#edgeValues(item.edge, edge);
88
98
  return freeze(row);
89
99
  }
90
- /** What `.withEdge()` adds: the Edge's Class and values, or the names it withholds. */
91
- #edgeValues(edge) {
100
+ /** What `.withEdge()` adds: the Edge's Class, and its values or the names it withholds. */
101
+ #edgeValues(edge, select) {
102
+ if (select === 'reference')
103
+ return freeze({ $class: edge.class });
92
104
  const c = this.#known(edge.class);
93
105
  if (this.#withheld(edge))
94
106
  return freeze({ $class: edge.class, $withheld: this.#names(c) });
@@ -116,14 +128,13 @@ export class Renderer {
116
128
  const row = { ...identity };
117
129
  if (plan.select === 'value')
118
130
  Object.assign(row, this.#values(key, c, roles));
119
- const end = (rendered, classes) => {
131
+ const end = (rendered, { classes, select }) => {
120
132
  if (rendered === undefined)
121
133
  return undefined;
122
- const pattern = { classes, select: plan.select, withheld: true, branches: [] };
123
- return this.node(rendered, pattern);
134
+ return this.node(rendered, { classes, select, withheld: true, branches: [] });
124
135
  };
125
- row[plan.source.role] = end(item.source, plan.source.classes);
126
- row[plan.target.role] = end(item.target, plan.target.classes);
136
+ row[plan.source.role] = end(item.source, plan.source);
137
+ row[plan.target.role] = end(item.target, plan.target);
127
138
  return freeze(row);
128
139
  }
129
140
  /** A relation's answer for one parent, by its verb. */
@@ -154,7 +165,7 @@ export class Renderer {
154
165
  return items[0] === undefined ? undefined : this.node(items[0], plan);
155
166
  case 'all':
156
167
  if (items.length > plan.bound)
157
- throw new QueryError('TOO_MANY', this.recipe, { limit: 'max', bound: plan.bound });
168
+ throw new QueryError('TOO_MANY', this.query, { limit: 'max', bound: plan.bound });
158
169
  return this.#rows(items, plan);
159
170
  default:
160
171
  return this.#rows(items, plan);
@@ -176,12 +187,12 @@ export class Renderer {
176
187
  }
177
188
  }
178
189
  #integrity(at, found) {
179
- return new QueryError('INTEGRITY', this.recipe, { ...at, found });
190
+ return new QueryError('INTEGRITY', this.query, { ...at, found });
180
191
  }
181
192
  /** The one row `get` or `find` gives: one the reader may not read is an error, since it exists. */
182
193
  #one(item, pattern) {
183
194
  if (this.#withheld(item.fact))
184
- throw new QueryError('NOT_READABLE', this.recipe, { class: this.#class(item), id: item.fact });
195
+ throw new QueryError('NOT_READABLE', this.query, { class: this.#class(item), id: item.fact });
185
196
  return this.node(item, pattern);
186
197
  }
187
198
  #rows(items, pattern) {
@@ -195,7 +206,7 @@ export class Renderer {
195
206
  rootOne(item, root) {
196
207
  if (this.#withheld(item.fact)) {
197
208
  const fact = item.fact;
198
- throw new QueryError('NOT_READABLE', this.recipe, {
209
+ throw new QueryError('NOT_READABLE', this.query, {
199
210
  class: typeof fact === 'string' ? this.#class(item) : fact.class,
200
211
  id: fact,
201
212
  });
@@ -208,10 +219,10 @@ function classLabel(plan) {
208
219
  return plan.root?.classes.join(' | ') ?? '';
209
220
  }
210
221
  /** The answer of a root read with nothing to read: its verb's empty answer. */
211
- export function emptyResult(recipe, plan) {
222
+ export function emptyResult(query, plan) {
212
223
  switch (plan.verb) {
213
224
  case 'get':
214
- throw new QueryError('NOT_FOUND', recipe, {
225
+ throw new QueryError('NOT_FOUND', query, {
215
226
  class: classLabel(plan),
216
227
  ...(plan.target === undefined ? {} : { target: plan.target }),
217
228
  });
@@ -231,24 +242,24 @@ export function emptyResult(recipe, plan) {
231
242
  }
232
243
  }
233
244
  /** A root read's result from its answer, by its verb. */
234
- export function rootResult(recipe, model, plan, answer) {
245
+ export function rootResult(query, model, plan, answer) {
235
246
  const json = answer;
236
247
  if ('exists' in json)
237
248
  return json.exists === true;
238
249
  if ('count' in json)
239
250
  return freeze({ count: json.count, more: json.more === true });
240
251
  const listAnswer = answer;
241
- const renderer = new Renderer(recipe, model, listAnswer);
252
+ const renderer = new Renderer(query, model, listAnswer);
242
253
  const { items, complete, cursor } = listAnswer.list;
243
254
  const root = plan.root ?? documentRoot(plan);
244
255
  switch (plan.verb) {
245
256
  case 'get':
246
257
  case 'find': {
247
258
  if (items.length > 1)
248
- throw new QueryError('INTEGRITY', recipe, { relation: classLabel(plan), found: 2 });
259
+ throw new QueryError('INTEGRITY', query, { relation: classLabel(plan), found: 2 });
249
260
  const [item] = items;
250
261
  if (item === undefined)
251
- return emptyResult(recipe, plan);
262
+ return emptyResult(query, plan);
252
263
  return renderer.rootOne(item, root);
253
264
  }
254
265
  case 'first':
@@ -264,7 +275,7 @@ export function rootResult(recipe, model, plan, answer) {
264
275
  });
265
276
  case 'all':
266
277
  if (items.length > plan.bound)
267
- throw new QueryError('TOO_MANY', recipe, { limit: 'max', bound: plan.bound });
278
+ throw new QueryError('TOO_MANY', query, { limit: 'max', bound: plan.bound });
268
279
  return renderer.rootRows(items, root);
269
280
  default:
270
281
  return renderer.rootRows(items, root);
@@ -282,7 +293,7 @@ function documentRoot(plan) {
282
293
  relation: b.name,
283
294
  verb: b.mode === 'count' ? 'count' : 'firstN',
284
295
  empty: false,
285
- edge: b.selectEdge !== undefined,
296
+ edge: b.selectEdge,
286
297
  classes: [],
287
298
  select: b.select === 'value' ? 'value' : 'reference',
288
299
  withheld: false,
@@ -297,12 +308,13 @@ function documentRoot(plan) {
297
308
  branches: branches(list),
298
309
  };
299
310
  const select = (list.select ?? {});
311
+ const rendering = (side) => (select[side] === 'value' ? 'value' : 'reference');
300
312
  return {
301
313
  kind: 'edges',
302
314
  classes: [],
303
- select: select.edge === 'value' ? 'value' : 'reference',
315
+ select: rendering('edge'),
304
316
  withheld: false,
305
- source: { role: 'source', classes: [] },
306
- target: { role: 'target', classes: [] },
317
+ source: { role: 'source', classes: [], select: rendering('source') },
318
+ target: { role: 'target', classes: [], select: rendering('target') },
307
319
  };
308
320
  }
@@ -1,49 +1,39 @@
1
1
  /**
2
- * Run a v7 recipe (WP-S3): its steps in order, each one's reads together, at most 8 in flight,
2
+ * Run a Query v7 (WP-S3): its steps in order, each one's reads together, at most 8 in flight,
3
3
  * each read at its own snapshot (the stand-in); then `output`. A step's bounds (S11) are checked
4
4
  * before it sends anything, and every Kernel refusal of a read is a `QueryError`.
5
5
  */
6
6
  import type { Domain } from '../../../platform/schema/index.js';
7
+ import type { Errors, InputOf, OutputContext, OutputOf, DefinedQuery } from '../defined/query.js';
7
8
  import type { ReadPlan } from '../handles/plan.js';
8
- import type { AnyQuery } from '../language/query.js';
9
- import type { Errors, InputOf, OutputContext, OutputOf, Recipe } from '../recipe/recipe.js';
9
+ import type { QueryClient } from './client.js';
10
10
  import { type Model } from '../handles/model.js';
11
11
  import { type DeclaredClass } from './crossings.js';
12
- /** What a recipe sends a read with: the cursor a page continues. */
13
- export interface RecipeQueryOptions {
14
- readonly paging?: {
15
- readonly after: string;
16
- };
17
- }
18
- /** The client a recipe reads through: one v7 document per request. */
19
- export interface RecipeClient {
20
- query(document: AnyQuery, options: RecipeQueryOptions): Promise<unknown>;
21
- }
22
- /** A page recipe's call: its size, from 1 to its `max` (by default), and the cursor it continues. */
23
- export interface RecipeCallOptions {
12
+ /** A page Query's call: its size, from 1 to its `max` (by default), and the cursor it continues. */
13
+ export interface QueryPageOptions {
24
14
  readonly size?: number;
25
15
  readonly after?: string;
26
16
  }
27
- /** A recipe call's options: the authority it reads under, and a page recipe's size and cursor. */
28
- export type RecipeOptions<R, As extends string> = {
17
+ /** A Query call's options: the authority it reads under, and a page Query's size and cursor. */
18
+ export type QueryOptions<R, As extends string> = {
29
19
  readonly as: As;
30
- } & ('~page' extends keyof R ? RecipeCallOptions : {});
20
+ } & ('~page' extends keyof R ? QueryPageOptions : {});
31
21
  type Step = (...parameters: readonly unknown[]) => unknown;
32
- /** One recipe call: its reads so far, against S11, and the Classes they cross, against S12. */
22
+ /** One Query call: its reads so far, against S11, and the Classes they cross, against S12. */
33
23
  export declare class Run {
34
24
  #private;
35
- readonly client: RecipeClient;
25
+ readonly client: QueryClient;
36
26
  readonly domain: Domain;
37
- readonly recipe: string;
27
+ readonly query: string;
38
28
  readonly model: Model;
39
- constructor(client: RecipeClient, domain: Domain, recipe: string, declared?: readonly DeclaredClass[]);
29
+ constructor(client: QueryClient, domain: Domain, query: string, declared?: readonly DeclaredClass[]);
40
30
  /**
41
31
  * S11, before a step sends: its reads, its verbs' bounds, its rows and each property `in` its
42
32
  * documents write. Then S12: each foreign Class its relations cross.
43
33
  */
44
34
  admit(plans: readonly ReadPlan[]): void;
45
35
  /** One read's result: its verb's empty answer, or its documents' answers merged and rendered. */
46
- read(plan: ReadPlan, paging?: RecipeCallOptions): Promise<unknown>;
36
+ read(plan: ReadPlan, paging?: QueryPageOptions): Promise<unknown>;
47
37
  }
48
38
  /** What `steps(…)` returned: its first record of facets, and the functions of the next ones. */
49
39
  interface StepsOf {
@@ -51,11 +41,11 @@ interface StepsOf {
51
41
  readonly next: readonly Step[];
52
42
  }
53
43
  /**
54
- * The `steps` a recipe's `build` receives: two to eight steps, the first a record of facets and
44
+ * The `steps` a Query's `build` receives: two to eight steps, the first a record of facets and
55
45
  * each next one a function of the merged results before it. It reads nothing: what it returns is
56
46
  * read when `build` returns it.
57
47
  */
58
- export declare function stepsFor(recipe: string): (...steps: readonly unknown[]) => object;
48
+ export declare function stepsFor(query: string): (...steps: readonly unknown[]) => object;
59
49
  /** The steps `build` returned, or `undefined` for one read or one record of facets. */
60
50
  export declare function stepsOf(built: unknown): StepsOf | undefined;
61
51
  /** Where each facet's results go: one read's index, a list of them, or nothing. */
@@ -70,16 +60,16 @@ export declare function planStep(run: Run, built: unknown, single: boolean, earl
70
60
  readonly shape?: Shape;
71
61
  };
72
62
  /**
73
- * Run a recipe for `input` against `domain`, the resolved Domain it was written for: what `build`
74
- * returns, each of its steps in order, then its `output`. A page recipe takes its call's `size`
75
- * and `after`. A read the Kernel refuses raises a `QueryError`; `output` raises the recipe's
63
+ * Run a Query for `input` against `domain`, the resolved Domain it was written for: what `build`
64
+ * returns, each of its steps in order, then its `output`. A page Query takes its call's `size`
65
+ * and `after`. A read the Kernel refuses raises a `QueryError`; `output` raises the Query's
76
66
  * declared errors. With the Class requirements
77
67
  * the Domain's build `declared`, each step's relations are checked against them first (S12).
78
68
  */
79
- export declare function runQueryRecipe<R extends Recipe>(client: RecipeClient, domain: Domain, recipe: R, input: InputOf<R>, call?: RecipeCallOptions, declared?: readonly DeclaredClass[]): Promise<OutputOf<R>>;
69
+ export declare function runQuery<R extends DefinedQuery>(client: QueryClient, domain: Domain, query: R, input: InputOf<R>, call?: QueryPageOptions, declared?: readonly DeclaredClass[]): Promise<OutputOf<R>>;
80
70
  /**
81
- * The context a recipe's `output`, or a Mutation recipe's `reject`, receives: its declared errors,
71
+ * The context a Query's `output`, or a defined Mutation's `reject`, receives: its declared errors,
82
72
  * and `is`, which narrows a row by Class.
83
73
  */
84
- export declare function outputContext(recipe: object, model: Model): OutputContext<unknown, Errors>;
74
+ export declare function outputContext(query: object, model: Model): OutputContext<unknown, Errors>;
85
75
  export {};
@@ -2,11 +2,11 @@ import * as errors from '@astrale-os/kernel-protocol/errors';
2
2
  import { isSnapshotStale, ResponseError, retryAfterOf } from '../../../platform/client/index.js';
3
3
  import { declaredErrorFactory } from '../../error/error.js';
4
4
  import { requireAnswerConformance } from '../answer/conformance.js';
5
+ import { querySpecOf } from '../defined/define.js';
5
6
  import { handlesOf } from '../handles/build.js';
6
7
  import { modelOf } from '../handles/model.js';
7
8
  import { isRead, planOf } from '../handles/plan.js';
8
9
  import { describe } from '../handles/terms.js';
9
- import { recipeSpecOf } from '../recipe/define.js';
10
10
  import { MAXIMUM_BOUND, MAXIMUM_READS, MAXIMUM_ROWS, MAXIMUM_STEPS, pastPage, rowsOf, wideIn, } from './bounds.js';
11
11
  import { Crossings } from './crossings.js';
12
12
  import { QueryError } from './error.js';
@@ -19,19 +19,19 @@ const IN_FLIGHT = 8;
19
19
  const STALE_RESENDS = 3;
20
20
  /** A refusal's protocol code by name, for one that gives no reason. */
21
21
  const CODE_NAMES = new Map(Object.entries(errors.CODES).map(([name, code]) => [code, name]));
22
- /** One recipe call: its reads so far, against S11, and the Classes they cross, against S12. */
22
+ /** One Query call: its reads so far, against S11, and the Classes they cross, against S12. */
23
23
  export class Run {
24
24
  client;
25
25
  domain;
26
- recipe;
26
+ query;
27
27
  #reads = 0;
28
28
  #rows = 0;
29
29
  model;
30
30
  #crossings;
31
- constructor(client, domain, recipe, declared) {
31
+ constructor(client, domain, query, declared) {
32
32
  this.client = client;
33
33
  this.domain = domain;
34
- this.recipe = recipe;
34
+ this.query = query;
35
35
  this.model = modelOf(domain);
36
36
  this.#crossings =
37
37
  declared === undefined ? undefined : new Crossings(this.model, domain, declared);
@@ -43,28 +43,28 @@ export class Run {
43
43
  admit(plans) {
44
44
  this.#reads += plans.length;
45
45
  if (this.#reads > MAXIMUM_READS)
46
- throw new QueryError('TOO_MANY', this.recipe, { limit: 'reads', bound: MAXIMUM_READS });
46
+ throw new QueryError('TOO_MANY', this.query, { limit: 'reads', bound: MAXIMUM_READS });
47
47
  if (plans.some(pastPage))
48
- throw new QueryError('TOO_MANY', this.recipe, { limit: 'rows', bound: MAXIMUM_BOUND });
48
+ throw new QueryError('TOO_MANY', this.query, { limit: 'rows', bound: MAXIMUM_BOUND });
49
49
  this.#rows += plans.reduce((total, plan) => total + rowsOf(plan), 0);
50
50
  if (this.#rows > MAXIMUM_ROWS)
51
- throw new QueryError('TOO_MANY', this.recipe, { limit: 'rows', bound: MAXIMUM_ROWS });
51
+ throw new QueryError('TOO_MANY', this.query, { limit: 'rows', bound: MAXIMUM_ROWS });
52
52
  for (const plan of plans) {
53
53
  const path = plan.document === undefined ? undefined : wideIn(plan.document);
54
54
  // Into the request `{ document, paging }`, as the Kernel's refusals point.
55
55
  if (path !== undefined)
56
- throw new QueryError('INPUT_INVALID', this.recipe, {
56
+ throw new QueryError('INPUT_INVALID', this.query, {
57
57
  limit: 'in',
58
58
  path: `/document${path}`,
59
59
  });
60
60
  }
61
61
  for (const plan of plans)
62
- this.#crossings?.check(this.recipe, plan);
62
+ this.#crossings?.check(this.query, plan);
63
63
  }
64
64
  /** One read's result: its verb's empty answer, or its documents' answers merged and rendered. */
65
65
  async read(plan, paging) {
66
66
  if (plan.empty)
67
- return emptyResult(this.recipe, plan);
67
+ return emptyResult(this.query, plan);
68
68
  let document = plan.document;
69
69
  if (plan.verb === 'page' && paging?.size !== undefined)
70
70
  document = withLimit(document, paging.size);
@@ -78,7 +78,7 @@ export class Run {
78
78
  requireAnswerConformance(this.domain, answer);
79
79
  answers.push(answer);
80
80
  }
81
- return rootResult(this.recipe, this.model, plan, merge(document, answers));
81
+ return rootResult(this.query, this.model, plan, merge(document, answers));
82
82
  }
83
83
  async #send(document, after) {
84
84
  const paging = after === undefined ? {} : { paging: { after } };
@@ -105,25 +105,25 @@ export class Run {
105
105
  const details = (error.reason?.details ?? {});
106
106
  const cause = { cause: error };
107
107
  if (isSnapshotStale(error))
108
- return new QueryError('REFUSED', this.recipe, { kernelCode, retryable: true }, cause);
108
+ return new QueryError('REFUSED', this.query, { kernelCode, retryable: true }, cause);
109
109
  const refusals = refusalsOf(error);
110
110
  if (refusals !== undefined) {
111
111
  const path = refusals[0]?.path ?? (typeof details.path === 'string' ? details.path : undefined);
112
- return new QueryError('INPUT_INVALID', this.recipe, {
112
+ return new QueryError('INPUT_INVALID', this.query, {
113
113
  kernelCode,
114
114
  ...(typeof details.phase === 'string' ? { phase: details.phase } : {}),
115
115
  ...(path === undefined ? {} : { path }),
116
116
  }, { ...cause, ...(refusals.length === 0 ? {} : { reason: describeRefusals(refusals) }) });
117
117
  }
118
118
  if (kernelCode === 'QUERY_EXPECTATION_MISMATCH')
119
- return new QueryError('OUTDATED', this.recipe, { kernelCode: 'QUERY_EXPECTATION_MISMATCH' }, cause);
119
+ return new QueryError('OUTDATED', this.query, { kernelCode: 'QUERY_EXPECTATION_MISMATCH' }, cause);
120
120
  // A Kernel that predates v7 refuses its request at the Function's input.
121
121
  if (isUnserved(error))
122
- return new QueryError('UNSUPPORTED', this.recipe, { feature: 'v7' }, cause);
122
+ return new QueryError('UNSUPPORTED', this.query, { feature: 'v7' }, cause);
123
123
  // Only a trusted backend answers v7 (a maintenance session's refusal says when to retry).
124
124
  if (kernelCode === 'QUERY_BACKEND_CONTRACT')
125
- return new QueryError('UNSUPPORTED', this.recipe, { feature: 'v7' }, cause);
126
- return new QueryError('REFUSED', this.recipe, { kernelCode, retryable: false }, cause);
125
+ return new QueryError('UNSUPPORTED', this.query, { feature: 'v7' }, cause);
126
+ return new QueryError('REFUSED', this.query, { kernelCode, retryable: false }, cause);
127
127
  }
128
128
  }
129
129
  /** A page document of `size` rows. */
@@ -153,22 +153,22 @@ async function pooled(tasks, limit) {
153
153
  }
154
154
  const stepped = new WeakMap();
155
155
  /**
156
- * The `steps` a recipe's `build` receives: two to eight steps, the first a record of facets and
156
+ * The `steps` a Query's `build` receives: two to eight steps, the first a record of facets and
157
157
  * each next one a function of the merged results before it. It reads nothing: what it returns is
158
158
  * read when `build` returns it.
159
159
  */
160
- export function stepsFor(recipe) {
160
+ export function stepsFor(query) {
161
161
  return (...steps) => {
162
162
  if (steps.length < 2 || steps.length > MAXIMUM_STEPS)
163
- throw new TypeError(`Recipe ${recipe}: steps takes 2 to ${MAXIMUM_STEPS} steps; got ${steps.length}. ${steps.length < 2
163
+ throw new TypeError(`Query ${query}: steps takes 2 to ${MAXIMUM_STEPS} steps; got ${steps.length}. ${steps.length < 2
164
164
  ? 'Reads that wait for none are what build returns: one read, or a record of facets.'
165
165
  : 'Read the facets that wait for the same results in one step.'}`);
166
166
  const [first, ...next] = steps;
167
167
  if (!isFacets(first))
168
- throw new TypeError(`Recipe ${recipe}: the first step is a record of facets; got ${described(first)}.`);
168
+ throw new TypeError(`Query ${query}: the first step is a record of facets; got ${described(first)}.`);
169
169
  const position = next.findIndex((step) => typeof step !== 'function');
170
170
  if (position !== -1)
171
- throw new TypeError(`Recipe ${recipe}: each step after the first is a function of the results before it; step ${position + 2} is ${describe(next[position])}.`);
171
+ throw new TypeError(`Query ${query}: each step after the first is a function of the results before it; step ${position + 2} is ${describe(next[position])}.`);
172
172
  const value = Object.freeze({});
173
173
  stepped.set(value, Object.freeze({ first, next: Object.freeze(next) }));
174
174
  return value;
@@ -197,19 +197,19 @@ export function stepsOf(built) {
197
197
  * The facets a step returned, each a read, a list of reads (`items.map`) or nothing. One read is
198
198
  * `build`'s alone, without steps: each step returns a record of facets.
199
199
  */
200
- function facetsOf(recipe, built, single) {
200
+ function facetsOf(query, built, single) {
201
201
  if (!isFacets(built)) {
202
202
  const expected = single
203
203
  ? 'build returns one read, a record of facets, or steps(…)'
204
204
  : 'each step returns a record of facets';
205
- throw new TypeError(`Recipe ${recipe}: ${expected}; got ${described(built)}.`);
205
+ throw new TypeError(`Query ${query}: ${expected}; got ${described(built)}.`);
206
206
  }
207
207
  return Object.entries(built);
208
208
  }
209
- /** A page is a recipe's single read: one step that returns it. */
210
- function refusePage(recipe, plan) {
209
+ /** A page is a Query's single read: one step that returns it. */
210
+ function refusePage(query, plan) {
211
211
  if (plan.verb === 'page')
212
- throw new TypeError(`Recipe ${recipe}: a page is a recipe's single read (D22); read the others with first or all.`);
212
+ throw new TypeError(`Query ${query}: a page is a Query's single read (D22); read the others with first or all.`);
213
213
  }
214
214
  /**
215
215
  * The reads of one step, admitted (S11, S12): a single read, or each facet's in order and the
@@ -222,10 +222,10 @@ export function planStep(run, built, single, earlier = {}) {
222
222
  run.admit(plans);
223
223
  return { plans };
224
224
  }
225
- const facets = facetsOf(run.recipe, built, single);
225
+ const facets = facetsOf(run.query, built, single);
226
226
  for (const [name] of facets)
227
227
  if (Object.hasOwn(earlier, name))
228
- throw new TypeError(`Recipe ${run.recipe}: an earlier step read the facet ${name}; name this step's facet otherwise.`);
228
+ throw new TypeError(`Query ${run.query}: an earlier step read the facet ${name}; name this step's facet otherwise.`);
229
229
  const plans = [];
230
230
  const shape = [];
231
231
  for (const [name, facet] of facets) {
@@ -236,7 +236,7 @@ export function planStep(run, built, single, earlier = {}) {
236
236
  else
237
237
  shape.push([name, plans.push(planOf(facet)) - 1]);
238
238
  }
239
- plans.forEach((plan) => refusePage(run.recipe, plan));
239
+ plans.forEach((plan) => refusePage(run.query, plan));
240
240
  run.admit(plans);
241
241
  return { plans, shape };
242
242
  }
@@ -259,41 +259,41 @@ async function runStep(run, built, single, call, earlier) {
259
259
  return Object.freeze(record);
260
260
  }
261
261
  /** The size and cursor of a page call, checked against the page's `max`. */
262
- function pageCall(recipe, call, max) {
262
+ function pageCall(query, call, max) {
263
263
  const { size, after } = call;
264
264
  if (max === undefined) {
265
265
  if (size !== undefined || after !== undefined)
266
- throw new TypeError(`Recipe ${recipe} is not a page: size and after continue a page recipe.`);
266
+ throw new TypeError(`Query ${query} is not a page: size and after continue a page Query.`);
267
267
  return {};
268
268
  }
269
269
  if (size !== undefined && (!Number.isInteger(size) || size < 1 || size > max))
270
- throw new TypeError(`Recipe ${recipe}: size is a whole number from 1 to the page's max, ${max}; got ${describe(size)}.`);
270
+ throw new TypeError(`Query ${query}: size is a whole number from 1 to the page's max, ${max}; got ${describe(size)}.`);
271
271
  if (after !== undefined && (typeof after !== 'string' || after.length === 0))
272
- throw new TypeError(`Recipe ${recipe}: after is the cursor a page returned as next.`);
272
+ throw new TypeError(`Query ${query}: after is the cursor a page returned as next.`);
273
273
  return { size: size ?? max, ...(after === undefined ? {} : { after }) };
274
274
  }
275
275
  /**
276
- * Run a recipe for `input` against `domain`, the resolved Domain it was written for: what `build`
277
- * returns, each of its steps in order, then its `output`. A page recipe takes its call's `size`
278
- * and `after`. A read the Kernel refuses raises a `QueryError`; `output` raises the recipe's
276
+ * Run a Query for `input` against `domain`, the resolved Domain it was written for: what `build`
277
+ * returns, each of its steps in order, then its `output`. A page Query takes its call's `size`
278
+ * and `after`. A read the Kernel refuses raises a `QueryError`; `output` raises the Query's
279
279
  * declared errors. With the Class requirements
280
280
  * the Domain's build `declared`, each step's relations are checked against them first (S12).
281
281
  */
282
- export async function runQueryRecipe(client, domain, recipe, input, call = {}, declared) {
283
- const spec = recipeSpecOf(recipe);
282
+ export async function runQuery(client, domain, query, input, call = {}, declared) {
283
+ const spec = querySpecOf(query);
284
284
  if (spec === undefined)
285
- throw new TypeError(`Not a v7 recipe: define one with defineQuery<Schema>()(id, { build }); got ${describe(recipe)}.`);
286
- const run = new Run(client, domain, recipe.id, declared);
287
- const built = spec.build(input, handlesOf(domain), stepsFor(recipe.id));
285
+ throw new TypeError(`Not a Query v7: define one with defineQuery<Schema>()(id, { build }); got ${describe(query)}.`);
286
+ const run = new Run(client, domain, query.id, declared);
287
+ const built = spec.build(input, handlesOf(domain), stepsFor(query.id));
288
288
  const steps = stepsOf(built);
289
289
  let answered;
290
290
  if (steps === undefined) {
291
291
  const page = isRead(built) && planOf(built).verb === 'page';
292
- const options = pageCall(recipe.id, call, page ? planOf(built).bound : undefined);
292
+ const options = pageCall(query.id, call, page ? planOf(built).bound : undefined);
293
293
  answered = await runStep(run, built, true, options, {});
294
294
  }
295
295
  else {
296
- pageCall(recipe.id, call, undefined);
296
+ pageCall(query.id, call, undefined);
297
297
  let merged = {};
298
298
  for (const step of [() => steps.first, ...steps.next]) {
299
299
  const results = await runStep(run, step(Object.freeze({ ...merged })), false, {}, merged);
@@ -303,15 +303,15 @@ export async function runQueryRecipe(client, domain, recipe, input, call = {}, d
303
303
  }
304
304
  if (spec.output === undefined)
305
305
  return answered;
306
- return spec.output(answered, input, outputContext(recipe, run.model));
306
+ return spec.output(answered, input, outputContext(query, run.model));
307
307
  }
308
308
  /**
309
- * The context a recipe's `output`, or a Mutation recipe's `reject`, receives: its declared errors,
309
+ * The context a Query's `output`, or a defined Mutation's `reject`, receives: its declared errors,
310
310
  * and `is`, which narrows a row by Class.
311
311
  */
312
- export function outputContext(recipe, model) {
312
+ export function outputContext(query, model) {
313
313
  return {
314
- error: declaredErrorFactory(recipe),
314
+ error: declaredErrorFactory(query),
315
315
  is: (row, cls) => {
316
316
  try {
317
317
  return model.satisfies(model.classOf(row.$class)).has(cls);
@@ -1,5 +1,5 @@
1
1
  import type { QueryCountAnswer, QueryExistsAnswer, QueryListAnswer } from '../answer/answer.js';
2
- import type { AnyQuery } from '../language/query.js';
2
+ import type { QueryDocument } from '../language/query.js';
3
3
  /** The Kernel's set members per v7 document: ids and value-set points, guards included. */
4
4
  export declare const MAXIMUM_MEMBERS = 32768;
5
5
  type ListAnswer = QueryListAnswer<unknown, unknown>;
@@ -15,10 +15,10 @@ export declare function membersOf(value: unknown): number;
15
15
  * an ordered read can merge. A document whose members lie elsewhere is sent whole, and the Kernel
16
16
  * names the limit.
17
17
  */
18
- export declare function split(document: AnyQuery, values: boolean): readonly AnyQuery[];
18
+ export declare function split(document: QueryDocument, values: boolean): readonly QueryDocument[];
19
19
  /**
20
20
  * The answers of a split read as one: a list merged in the document's order and cut to its limit,
21
21
  * `exists` if any slice has one, a count summed up to its bound.
22
22
  */
23
- export declare function merge(document: AnyQuery, answers: readonly Answer[]): Answer;
23
+ export declare function merge(document: QueryDocument, answers: readonly Answer[]): Answer;
24
24
  export {};
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Long id lists (spec § Recipes: the contract, § Semantics): a document past the Kernel's set
2
+ * Long id lists (spec § Queries: the contract, § Semantics): a document past the Kernel's set
3
3
  * members is sent as several, its root id list split, and their answers merged back in the
4
4
  * Kernel's order and cut to the read's limit. Only a Node root's id list splits, and only for a
5
5
  * read that is not a page: a page's cursor continues one document.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * The handles a recipe's `build` receives (S1), built once per resolved Domain: its own Classes,
2
+ * The handles a Query's `build` receives (S1), built once per resolved Domain: its own Classes,
3
3
  * each dependency under its alias, the Kernel's Classes under `kernel`, its Core nodes under
4
4
  * `core`, `origin`, and `document`, the escape hatch (D23). The fixed names win over a Class or
5
5
  * an alias of the same name.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * The Domain a recipe reads, indexed once per Schema (spike designs D1, D3 and D10).
2
+ * The Domain a Query reads, indexed once per Schema (spike designs D1, D3 and D10).
3
3
  *
4
4
  * Every generic is keyed by the Schema type `S` and Class keys, never by resolved Classes:
5
5
  * instantiations are cached per (Schema, key), and messages print the key. The Domain is indexed