@loomcli/core 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/application.d.ts +59 -34
  2. package/dist/application.js +162 -59
  3. package/dist/chain.d.ts +25 -6
  4. package/dist/chain.js +99 -15
  5. package/dist/command.d.ts +146 -42
  6. package/dist/command.js +620 -78
  7. package/dist/environment.d.ts +22 -0
  8. package/dist/environment.js +1 -0
  9. package/dist/errors.d.ts +31 -59
  10. package/dist/errors.js +49 -106
  11. package/dist/extension.d.ts +5 -1
  12. package/dist/extension.js +18 -1
  13. package/dist/globals.d.ts +14 -25
  14. package/dist/globals.js +10 -61
  15. package/dist/glyphs.generated.d.ts +464 -0
  16. package/dist/glyphs.generated.js +491 -0
  17. package/dist/host.js +2 -1
  18. package/dist/index.d.ts +14 -5
  19. package/dist/index.js +5 -2
  20. package/dist/inspect.d.ts +26 -3
  21. package/dist/inspect.js +19 -5
  22. package/dist/lanes.d.ts +26 -0
  23. package/dist/lanes.js +45 -0
  24. package/dist/output.d.ts +93 -15
  25. package/dist/output.js +307 -34
  26. package/dist/plugin.d.ts +32 -16
  27. package/dist/plugin.js +46 -18
  28. package/dist/rendering.d.ts +21 -0
  29. package/dist/rendering.js +72 -0
  30. package/dist/sequence.d.ts +41 -0
  31. package/dist/sequence.js +225 -0
  32. package/dist/style-ansi.d.ts +13 -0
  33. package/dist/style-ansi.js +306 -0
  34. package/dist/style-layout.d.ts +29 -0
  35. package/dist/style-layout.js +228 -0
  36. package/dist/style-resolve.d.ts +6 -0
  37. package/dist/style-resolve.js +26 -0
  38. package/dist/style-state.d.ts +14 -0
  39. package/dist/style-state.js +179 -0
  40. package/dist/style-wire.d.ts +31 -0
  41. package/dist/style-wire.js +201 -0
  42. package/dist/style.d.ts +86 -0
  43. package/dist/style.js +201 -0
  44. package/dist/theme.d.ts +3 -0
  45. package/dist/theme.js +22 -0
  46. package/dist/types.d.ts +169 -21
  47. package/dist/validation.d.ts +8 -1
  48. package/dist/validation.js +17 -2
  49. package/dist/view.d.ts +180 -0
  50. package/dist/view.js +307 -0
  51. package/package.json +2 -1
package/dist/types.d.ts CHANGED
@@ -2,6 +2,9 @@
2
2
  import type { Readable, Writable } from 'node:stream';
3
3
  import type { StandardSchemaV1 } from '@standard-schema/spec';
4
4
  import type { ExtensionValue } from './extension.js';
5
+ import type { ResultNode } from './inspect.js';
6
+ import type { RenderingPolicy } from './rendering.js';
7
+ import type { ContextualStyle } from './style.js';
5
8
  type LowercaseLetter = 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z';
6
9
  type ShortAlias = LowercaseLetter | Uppercase<LowercaseLetter>;
7
10
  type OptionSpelling = {
@@ -89,6 +92,7 @@ export interface OutputTerminal extends InputTerminal {
89
92
  rows: number | undefined;
90
93
  }
91
94
  export interface Host {
95
+ platform: string;
92
96
  argv: string[];
93
97
  cwd: string;
94
98
  env: Record<string, string | undefined>;
@@ -102,6 +106,7 @@ export interface Host {
102
106
  stderr: Writable;
103
107
  }
104
108
  export interface RunOptions {
109
+ rendering?: RenderingPolicy;
105
110
  host?: Partial<Host>;
106
111
  /**
107
112
  * A caller-owned signal that cancels the run. Core subscribes to it at run entry and honors an
@@ -143,24 +148,162 @@ export type ValidationContext = {
143
148
  passthrough: readonly string[];
144
149
  supplied: SuppliedInputs;
145
150
  };
151
+ /** Immutable authoring and measurement context for one view destination. */
152
+ export interface ViewContext {
153
+ readonly style: ContextualStyle;
154
+ readonly width: (text: string) => number;
155
+ }
156
+ /** A pure synchronous view turns one typed value into the marked text core resolves. */
157
+ export interface View<Data> {
158
+ render: (data: Readonly<Data>, context: ViewContext) => string;
159
+ /** A view has one shape; the row view of Results is the other. */
160
+ row?: never;
161
+ }
162
+ /**
163
+ * A row view renders a sequence one row at a time.
164
+ * `head` and `tail` open and close the sequence, and each defaults to the empty string.
165
+ * Every function is pure and synchronous and owns the newlines in the text it returns.
166
+ */
167
+ export interface RowView<Row> {
168
+ row: (row: Readonly<Row>, index: number, context: ViewContext) => string;
169
+ head?: (context: ViewContext) => string;
170
+ tail?: (count: number, context: ViewContext) => string;
171
+ /** A row view has one shape; the whole view of Rendered output is the other. */
172
+ render?: never;
173
+ }
174
+ /** The views record of a value result: every entry renders the whole value. */
175
+ export type ResultViews<Value> = Readonly<Record<string, View<Value>>>;
176
+ /**
177
+ * The views record of a rows result: a whole view over the collected rows, which core buffers the
178
+ * sequence for, or a row view, which core feeds as the rows arrive.
179
+ */
180
+ export type RowViews<Row> = Readonly<Record<string, View<readonly Row[]> | RowView<Row>>>;
181
+ /**
182
+ * One view a result names, with its data type erased, as the view registry erases a declared
183
+ * view's. The write site reads each function back through the key that resolved it.
184
+ */
185
+ export type ResultView = View<never> | RowView<never>;
186
+ /**
187
+ * One declared result as the write site reads it: the unit the action emits, the view names it
188
+ * declares in record order, and the key core renders when nothing selects another.
189
+ */
190
+ export interface DeclaredResult {
191
+ default: string;
192
+ kind: 'value' | 'rows';
193
+ views: ReadonlyMap<string, ResultView>;
194
+ }
195
+ /** Phantom key. It brands the surface a lifecycle hook receives, so a forged value is not one. */
196
+ export declare const attachedCommand: unique symbol;
197
+ /**
198
+ * One Command as `onCommandAttach` receives it: the facts `inspect()` publishes, with their types
199
+ * erased, and the authoring calls a hook may make. Each call returns a new value whose facts hold
200
+ * what the call added, so a hook reads its own earlier calls back. The calls a hook cannot make are
201
+ * absent, because each of them changes what the action was compiled against or the graph's shape.
202
+ */
203
+ export interface AttachedCommand {
204
+ readonly [attachedCommand]: true;
205
+ /** The Command's own name, and `null` for the root. */
206
+ readonly name: string | null;
207
+ readonly path: readonly string[];
208
+ readonly hasAction: boolean;
209
+ /** The declared argument names, in declaration order. */
210
+ readonly arguments: readonly string[];
211
+ /** The declared local option names, in declaration order. */
212
+ readonly options: readonly string[];
213
+ readonly result: ResultNode | null;
214
+ argument(name: string, config: ArgumentConfig): AttachedCommand;
215
+ option(name: string, config: OptionConfig): AttachedCommand;
216
+ views(replacements: Readonly<Record<string, ResultView>>, options?: {
217
+ default?: string;
218
+ }): AttachedCommand;
219
+ extend(...values: readonly ExtensionValue<'command'>[]): AttachedCommand;
220
+ }
221
+ /**
222
+ * The lifecycle hook core calls once per Command at graph build, in installation order, each
223
+ * receiving what the previous plugin's hook returned.
224
+ */
225
+ export type CommandAttachHook = (command: AttachedCommand) => AttachedCommand;
226
+ /**
227
+ * What `out.results` accepts for one declared result. The declaration rides in the declared types
228
+ * as a closed discriminant, so a Command that declares none carries the neutral `unknown` and its
229
+ * `out.results` takes `never`. The parameter is never a union, so distribution reaches one member.
230
+ */
231
+ export type ResultInput<Result> = Result extends {
232
+ kind: 'value';
233
+ value: infer Value;
234
+ } ? Value : Result extends {
235
+ kind: 'rows';
236
+ row: infer Row;
237
+ } ? Iterable<Row> | AsyncIterable<Row> : never;
146
238
  /**
147
- * One value turned into the exact text core writes. A renderer owns every byte, the trailing
148
- * newline included. It is synchronous and pure: it receives the value alone, returns a string, and
149
- * holds no output handle. A throw or a non-string return is a renderer failure.
239
+ * The result an `out` carries where the declaration is not in hand. Its `results` accepts any
240
+ * value, because the authoring call already checked what the Command declares, and the channel
241
+ * core builds for an action carries that declaration at run time.
150
242
  */
151
- export interface Renderer<Data> {
152
- render: (data: Readonly<Data>) => string;
243
+ export interface OpenResult {
244
+ kind: 'value';
245
+ value: unknown;
153
246
  }
154
- export interface Out {
247
+ /** The record a `views()` call takes, which is the shape the carried result names. */
248
+ export type ResultViewsOf<Result> = Result extends {
249
+ kind: 'value';
250
+ value: infer Value;
251
+ } ? ResultViews<Value> : Result extends {
252
+ kind: 'rows';
253
+ row: infer Row;
254
+ } ? RowViews<Row> : never;
255
+ export interface Out<Result = unknown> {
155
256
  print(message: string): Promise<void>;
156
257
  info(message: string): Promise<void>;
157
258
  success(message: string): Promise<void>;
158
259
  warn(message: string): Promise<void>;
159
260
  error(message: string): Promise<void>;
160
- /** The neutral presentation call: a rendered value has no purpose and no destination. */
161
- render<Data>(data: Data, renderer: Renderer<Data>): Promise<void>;
261
+ /** The neutral view call: a rendered value has no purpose and no destination. */
262
+ render<Data>(data: Data, view: View<Data>): Promise<void>;
263
+ /** The same call over a sequence: core writes each row's text as the source yields it. */
264
+ render<Row>(rows: Iterable<Row> | AsyncIterable<Row>, view: RowView<Row>): Promise<void>;
265
+ /**
266
+ * The Command's own result, emitted once. Property syntax keeps the parameter contravariant, so
267
+ * a neutral `Out` never stands in for one that carries a declaration.
268
+ */
269
+ results: (value: ResultInput<Result>) => Promise<void>;
162
270
  fatal(message: string): never;
163
271
  }
272
+ /**
273
+ * What the action's channel answers to: the routed path its diagnostics name, the result the
274
+ * routed Command declared, and the view this run selected. The declaration decides the
275
+ * destinations the channel carries, so the redirect is read from the graph once and never from the
276
+ * view a run selected; `view` decides the rendering alone, and is `null` when nothing selected one
277
+ * and the declaration's default stands.
278
+ */
279
+ export interface ResultBinding {
280
+ path: readonly string[];
281
+ result: DeclaredResult | undefined;
282
+ view: string | null;
283
+ }
284
+ /**
285
+ * The routed Command's invocation after parsing and validation, which every middleware reads. The
286
+ * values are what the action receives, the output of each declaration's schema, for that Command's
287
+ * own arguments and local options; global and plugin option values are not here. The records are
288
+ * untyped and frozen, because a middleware runs ahead of every action and the graph carries no
289
+ * type for a value.
290
+ */
291
+ export interface Request {
292
+ readonly args: Readonly<Record<string, unknown>>;
293
+ readonly options: Readonly<Record<string, unknown>>;
294
+ readonly passthrough: readonly string[];
295
+ }
296
+ /** The channel one action receives, with the emission the results lane holds it to. */
297
+ export interface ActionChannel {
298
+ out: Out<OpenResult>;
299
+ /** Whether the action emitted its result, which the missing rule reads after it returned. */
300
+ emitted: () => boolean;
301
+ /**
302
+ * Stops every sequence this channel still has pending. The action's own failure stays primary,
303
+ * so a sequence it never awaited is stopped rather than drained.
304
+ */
305
+ stop: () => void;
306
+ }
164
307
  export type StringOption = OptionSpelling & Presence & Multiplicity & Omission & Described & Listed & OptionExtensions & {
165
308
  type: 'string';
166
309
  polarity?: never;
@@ -251,8 +394,9 @@ export type BooleanOption = (OptionSpelling & Described & Listed & OptionExtensi
251
394
  export type OptionConfig = StringOption | BooleanOption;
252
395
  /**
253
396
  * The parsing part of a string option config, which is all a plugin option declares. A plugin
254
- * option carries no schema and no presence rule, because it is read before local parsing, where the
255
- * validation context every schema is promised cannot exist. Its middleware interprets the value.
397
+ * option carries no schema and no presence rule, because the pre-scan consumes it ahead of routing,
398
+ * where the validation context every schema is promised cannot exist. Its middleware interprets
399
+ * the value.
256
400
  * A Boolean plugin option is an ordinary `BooleanOption`, which already declares none of them.
257
401
  */
258
402
  export type PluginStringOption = OptionSpelling & Multiplicity & Described & Listed & OptionExtensions & {
@@ -273,36 +417,40 @@ export type OptionValue<Config extends OptionConfig> = Config extends StringOpti
273
417
  } | {
274
418
  validateOmitted: true;
275
419
  } ? never : undefined) : boolean;
276
- export interface ActionContext<Args, Options = {}> {
420
+ export interface ActionContext<Args, Options = {}, Result = unknown> {
421
+ readonly style: ContextualStyle;
277
422
  args: Args;
278
423
  options: Options;
279
424
  passthrough: string[];
280
- out: Out;
425
+ out: Out<Result>;
281
426
  host: Host;
282
427
  /** The run's cancellation signal, which a caller or an installed signals owner aborts. */
283
428
  signal: AbortSignal;
284
429
  }
285
- export type Action<Args, Options = {}> = (context: ActionContext<Args, Options>) => unknown;
430
+ export type Action<Args, Options = {}, Result = unknown> = (context: ActionContext<Args, Options, Result>) => unknown;
286
431
  /** Phantom key. It keeps the inferred declaration types exact and holds no runtime value. */
287
432
  export declare const declaredTypes: unique symbol;
288
433
  /**
289
- * The three inferred types one declaration carries. The phantom member keeps them exact. Args and
290
- * options widen, so a child can satisfy a looser reader. The globals appear in both a parameter and
291
- * a return position, which makes them invariant: a child's globals must be the parent's own type,
292
- * not a subset and not a superset, because one table serves every Command in the graph.
434
+ * Arguments and local options describe the action's own inputs. Globals are a requirement on the
435
+ * Receiving Application: a library that needs none can attach wherever its local keys are disjoint.
293
436
  */
294
- export interface DeclaredTypes<Args, Options, Globals> {
437
+ export interface DeclaredTypes<Args, Options, Globals, Result = unknown> {
295
438
  args: Args;
296
- globals: (value: Globals) => Globals;
439
+ globals: (value: Globals) => void;
297
440
  options: Options;
441
+ /**
442
+ * The result the Command declares, or `unknown` where it declares none. A plain field keeps it
443
+ * covariant, so a child that carries one still satisfies a neutral `Command` annotation.
444
+ */
445
+ result: Result;
298
446
  }
299
447
  /**
300
448
  * The handler one declaration accepts. It reads the phantom types, not the `action()` call, so it
301
449
  * holds on a fresh declaration, on a partly declared one, and on one that registered its action.
302
450
  */
303
451
  export type ActionHandler<Declaration> = Declaration extends {
304
- [declaredTypes]: DeclaredTypes<infer Args, infer Options, infer Globals>;
305
- } ? Action<Args, Globals & Options> : never;
452
+ [declaredTypes]: DeclaredTypes<infer Args, infer Options, infer Globals, infer Result>;
453
+ } ? Action<Args, Globals & Options, Result> : never;
306
454
  /** The args object an extracted handler receives for this declaration. */
307
455
  export type ActionArgs<Declaration> = ActionArgument<Declaration> extends {
308
456
  args: infer Args;
@@ -36,6 +36,8 @@ export interface Invocation {
36
36
  host: Host;
37
37
  inputs: ScopedInputs;
38
38
  passthrough: readonly string[];
39
+ /** The run's cancellation signal, which stops this phase between two schema calls. */
40
+ signal: AbortSignal;
39
41
  supplied: SuppliedValues;
40
42
  }
41
43
  /**
@@ -50,6 +52,11 @@ declare class ValidatedInputs {
50
52
  argument<Name extends string, Config extends ArgumentConfig>(input: ArgumentInput<Name, Config>): Record<Name, ArgumentValue<Config>>;
51
53
  /** The one-key record this option contributes to `options`, typed by its own config. */
52
54
  option<Name extends string, Config extends OptionConfig>(input: OptionInput<Name, Config>): Record<Name, OptionValue<Config>>;
55
+ /**
56
+ * One validated value read without its declared type, which is what an untyped reader such as a
57
+ * middleware's request receives. The declared types stay with the two readers above.
58
+ */
59
+ read(input: InputDeclaration): unknown;
53
60
  }
54
61
  export type { ValidatedInputs };
55
62
  /**
@@ -65,7 +72,7 @@ export declare function captureConfig<Config extends ArgumentConfig | OptionConf
65
72
  export declare function validatesOmission(input: InputDeclaration): boolean;
66
73
  /**
67
74
  * The dotted path an issue names inside a value, or `undefined` when the issue names the value
68
- * itself. Core's default text and an application's own renderer read a position through this one
75
+ * itself. Core's default text and an application's own view read a position through this one
69
76
  * helper, so a rejected item reads alike wherever its diagnostic is written.
70
77
  */
71
78
  export declare function issuePath(issue: StandardSchemaV1.Issue): string | undefined;
@@ -29,6 +29,13 @@ class ValidatedInputs {
29
29
  option(input) {
30
30
  return this.#field(input);
31
31
  }
32
+ /**
33
+ * One validated value read without its declared type, which is what an untyped reader such as a
34
+ * middleware's request receives. The declared types stay with the two readers above.
35
+ */
36
+ read(input) {
37
+ return this.#values.get(input);
38
+ }
32
39
  #field(input) {
33
40
  // Last resort: no typed path exists. The map stores every validated value as `unknown`.
34
41
  // An object literal with a generic computed key does not type as `Record<Name, _>` either.
@@ -227,7 +234,7 @@ async function validate(input, raw, context) {
227
234
  }
228
235
  /**
229
236
  * The dotted path an issue names inside a value, or `undefined` when the issue names the value
230
- * itself. Core's default text and an application's own renderer read a position through this one
237
+ * itself. Core's default text and an application's own view read a position through this one
231
238
  * helper, so a rejected item reads alike wherever its diagnostic is written.
232
239
  */
233
240
  export function issuePath(issue) {
@@ -239,7 +246,7 @@ export function issuePath(issue) {
239
246
  /**
240
247
  * The issues one rejection reports. A schema that returned none still rejected the value, so the
241
248
  * placeholder stands in for its silence. Reporting takes this list once: the reported problem
242
- * carries it and the default text is derived from it, so a renderer and core read the same issues.
249
+ * carries it and the default text is derived from it, so a view and core read the same issues.
243
250
  */
244
251
  function reported(issues) {
245
252
  return issues.length === 0
@@ -366,6 +373,14 @@ export async function validateValues(invocation) {
366
373
  lines.push(...messages(suppliedName(entry.input, spelling), issues));
367
374
  };
368
375
  for (const entry of declarations) {
376
+ if (invocation.signal.aborted) {
377
+ /**
378
+ * A cancelled run starts no further schema call. The one already in flight was awaited
379
+ * above, and whatever this phase collected is never raised, because the run resolves its
380
+ * cancellation code instead.
381
+ */
382
+ break;
383
+ }
369
384
  const { input } = entry;
370
385
  if (input.kind === 'option' && input.config.type === 'boolean') {
371
386
  values.set(input, booleanValue(supplied.options, input.name, input.config));
package/dist/view.d.ts ADDED
@@ -0,0 +1,180 @@
1
+ import type { LoomError } from './errors.js';
2
+ import type { RowView, View, ViewContext } from './types.js';
3
+ /** Phantom key. It brands a declared view and holds no runtime value. */
4
+ declare const declaredView: unique symbol;
5
+ /** Phantom key. It makes a declared view invariant in its data type and holds no runtime value. */
6
+ declare const invariant: unique symbol;
7
+ /** Phantom key. It brands a view override and holds no runtime value. */
8
+ declare const viewOverride: unique symbol;
9
+ /** The brand one declared view carries, as an extension value carries its own. */
10
+ interface DeclaredViewBrand {
11
+ readonly [declaredView]: true;
12
+ }
13
+ /**
14
+ * One declared view: the identity a diagnostic names it by, and the default view function an
15
+ * override replaces. The witness makes `Data` invariant, so a declared view is never reassigned as
16
+ * a declared view of another data type and a replacement that requires data the key does not carry
17
+ * is a compile error.
18
+ */
19
+ interface DeclaredView<Data> extends View<Data>, DeclaredViewBrand {
20
+ readonly identity: string;
21
+ readonly [invariant]: (data: Data) => Data;
22
+ }
23
+ /**
24
+ * One declared row view: the same brand and witness a declared view carries, over the row type.
25
+ * `override` keyed by it takes a row view, under the rules a declared view follows unchanged.
26
+ */
27
+ interface DeclaredRowView<Row> extends RowView<Row>, DeclaredViewBrand {
28
+ readonly identity: string;
29
+ readonly [invariant]: (row: Row) => Row;
30
+ }
31
+ /**
32
+ * The supertype a contribution list uses. It keeps the identity, the brand, and a view function of
33
+ * either shape over `never`, and drops the invariance witness, because a list cannot carry one type
34
+ * parameter per element and an invariant type has no common supertype across data types.
35
+ */
36
+ type AnyDeclaredView = (View<never> | RowView<never>) & DeclaredViewBrand & {
37
+ readonly identity: string;
38
+ };
39
+ /** A failure class as an override key, so `UsageError` and an application's subclass both fit. */
40
+ type FailureClass<Failure extends LoomError> = abstract new (...args: never[]) => Failure;
41
+ /**
42
+ * One stored view function, with the data type erased. A registry holds one entry per key and
43
+ * cannot carry a type parameter per entry, so `override(key, replacement)` is where the
44
+ * replacement is typed against the key it answers.
45
+ */
46
+ type ViewFunction = (data: never, context: ViewContext) => unknown;
47
+ /** One stored row function, with the row type erased for the same reason. */
48
+ type RowFunction = (row: never, index: number, context: ViewContext) => unknown;
49
+ /** One stored function that opens a sequence and reads the context alone. */
50
+ type HeadFunction = (context: ViewContext) => unknown;
51
+ /** One stored function that closes a sequence and reads its completed row count. */
52
+ type TailFunction = (count: number, context: ViewContext) => unknown;
53
+ /** One stored view value: the functions of whichever shape its author supplied. */
54
+ interface StoredView {
55
+ render?: ViewFunction | undefined;
56
+ row?: RowFunction | undefined;
57
+ head?: HeadFunction | undefined;
58
+ tail?: TailFunction | undefined;
59
+ }
60
+ /** Which of the two exclusive view functions one value carries, or that it carries the wrong set. */
61
+ type ViewShape = 'both' | 'neither' | 'render' | 'row';
62
+ /**
63
+ * The shape one value names. The argument is read defensively, because every call that takes a view
64
+ * is reachable from JavaScript and `null` is one such value.
65
+ */
66
+ declare function shapeOf(value: {
67
+ render?: unknown;
68
+ row?: unknown;
69
+ } | null | undefined): ViewShape;
70
+ /**
71
+ * One declared view of either shape: an identity and its default functions. The data type is
72
+ * inferred from the function's first parameter when that parameter is an object type or a
73
+ * `readonly` array, and is stated for a primitive or a union, because inference runs through
74
+ * `Readonly<Data>`. The two shapes are told apart by the function present, and a definition that
75
+ * carries both or neither is rejected here rather than guessed at.
76
+ */
77
+ declare function view<Data>(identity: string, definition: View<Data>): DeclaredView<Data>;
78
+ declare function view<Row>(identity: string, definition: RowView<Row>): DeclaredRowView<Row>;
79
+ /**
80
+ * What one override replaces: a declared view, a failure class read as its prototype, or a value
81
+ * that is neither, which build reports as the entry fault of the list that holds it.
82
+ */
83
+ type OverrideKey = {
84
+ kind: 'view';
85
+ view: AnyDeclaredView;
86
+ } | {
87
+ kind: 'failure';
88
+ name: string;
89
+ prototype: object;
90
+ } | {
91
+ kind: 'invalid';
92
+ };
93
+ /** One override: the key it answers and the view value that supersedes the default. */
94
+ interface OverrideRecord {
95
+ key: OverrideKey;
96
+ replacement: StoredView;
97
+ }
98
+ /** The runtime value `override()` returns. Its pair lives in the registry above. */
99
+ declare class OverrideDeclaration {
100
+ readonly [viewOverride]: true;
101
+ constructor(record: OverrideRecord);
102
+ }
103
+ /** An opaque override pairing one key with the view function that replaces its default. */
104
+ type ViewOverride = Pick<OverrideDeclaration, typeof viewOverride>;
105
+ /** What a plugin's own list holds: the views it declares and the overrides it makes. */
106
+ type ViewContribution = AnyDeclaredView | ViewOverride;
107
+ /**
108
+ * One override pairing a key with a replacement view. Under a declared view the replacement is
109
+ * typed from the view's data; under a failure class it is typed from the class's instances, which
110
+ * is the typed path for a class-keyed list, because an array literal cannot carry a different type
111
+ * parameter per element.
112
+ */
113
+ declare function override<Data>(key: DeclaredView<Data>, replacement: NoInfer<View<Data>>): ViewOverride;
114
+ declare function override<Row>(key: DeclaredRowView<Row>, replacement: NoInfer<RowView<Row>>): ViewOverride;
115
+ declare function override<Failure extends LoomError>(key: FailureClass<Failure> & {
116
+ readonly [declaredView]?: never;
117
+ }, replacement: NoInfer<View<Failure>>): ViewOverride;
118
+ /** One contributor's overrides, read once per build and consulted in contributor order. */
119
+ interface ViewContributions {
120
+ failures: Map<unknown, StoredView>;
121
+ views: Map<AnyDeclaredView, StoredView>;
122
+ }
123
+ /**
124
+ * Every contributor in resolution order: the application's overrides, then each installed plugin's
125
+ * in installation order. The declaring view's own default answers when no contributor does.
126
+ */
127
+ type ViewRegistry = readonly ViewContributions[];
128
+ /** The identities one build has met, so a second object under one identity is visible. */
129
+ type ViewIdentities = Map<string, AnyDeclaredView>;
130
+ /** How one contributor's diagnostics name it, and whether its list may declare a view. */
131
+ interface ViewSubject {
132
+ /** A plugin declares views beside its overrides; an application overrides alone. */
133
+ declares: boolean;
134
+ sentence: string;
135
+ }
136
+ /** The identity register one build starts from, holding the views core itself declares. */
137
+ declare function viewIdentities(declared: readonly AnyDeclaredView[]): ViewIdentities;
138
+ /**
139
+ * One contributor's own overrides, with the identity of every declared view it lists or names as a
140
+ * key registered. Listing a declared view is what puts its identity on the graph; an application
141
+ * lists overrides alone, so a declaration in its list is the same fault as any other value.
142
+ */
143
+ declare function buildViews(subject: ViewSubject, declared: unknown, identities: ViewIdentities): ViewContributions;
144
+ /**
145
+ * The view function one declared view resolves to: the first contributor that overrides it, then
146
+ * its own default. A bare view is never overridden, so it resolves to its own function.
147
+ */
148
+ declare function resolveView<Data>(registry: ViewRegistry, value: View<Data>): (data: Data, context: ViewContext) => unknown;
149
+ /** The three functions one sequence writes through: `head`, `row` per item, and `tail`. */
150
+ interface ResolvedRowView<Row> {
151
+ row: (row: Row, index: number, context: ViewContext) => unknown;
152
+ head: HeadFunction | undefined;
153
+ tail: TailFunction | undefined;
154
+ }
155
+ /**
156
+ * The row functions one row view resolves to, by the walk `resolveView` defines. A replacement
157
+ * supplies the whole shape, so an override that omits `head` drops the default's `head` with it.
158
+ */
159
+ declare function resolveRowView<Row>(registry: ViewRegistry, value: RowView<Row>): ResolvedRowView<Row>;
160
+ /**
161
+ * The report of one failure: the text core writes, and whether a view produced it. An unrendered
162
+ * report carries core's own text, which the plain fallback path writes beside the diagnostic
163
+ * naming the view that could not answer.
164
+ */
165
+ type FailureReport = {
166
+ kind: 'rendered';
167
+ text: string;
168
+ } | {
169
+ kind: 'unrendered';
170
+ text: string;
171
+ reason: string;
172
+ };
173
+ /**
174
+ * The text core writes for one failure. Resolution walks the registry as `resolveFailure` defines
175
+ * it and falls to core's own default text, which escapes the raw facts it interpolates. A
176
+ * `FatalError` keeps the authored marked message it was given.
177
+ */
178
+ declare function describeFailure(registry: ViewRegistry, failure: LoomError, context?: ViewContext): FailureReport;
179
+ export type { AnyDeclaredView, DeclaredRowView, DeclaredView, DeclaredViewBrand, FailureClass, FailureReport, ResolvedRowView, ViewContribution, ViewContributions, ViewIdentities, ViewOverride, ViewRegistry, ViewShape, };
180
+ export { buildViews, describeFailure, override, resolveRowView, resolveView, shapeOf, view, viewIdentities, };