@loomcli/core 0.2.0 → 0.4.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 +642 -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 +80 -20
  12. package/dist/extension.js +115 -30
  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 +15 -6
  19. package/dist/index.js +5 -2
  20. package/dist/inspect.d.ts +38 -4
  21. package/dist/inspect.js +69 -6
  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 +174 -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/chain.js CHANGED
@@ -1,8 +1,69 @@
1
1
  import { prepareDispatch, routeInvocation } from './command.js';
2
- import { InternalError, reasonOf, toFailure } from './errors.js';
2
+ import { InternalError, reasonOf, routedSubject, toFailure } from './errors.js';
3
3
  import { inspectGraph } from './inspect.js';
4
4
  import { booleanValue } from './options.js';
5
5
  import { pluginSentence } from './plugin.js';
6
+ /**
7
+ * The view one run selects, which is one value whichever middleware wrote it. The assignment is
8
+ * kept as it arrived, because a JavaScript caller reaches the setter with any value and the check
9
+ * belongs at the dispatch boundary, where the fault it raises ranks behind a held fault.
10
+ */
11
+ class ViewSelection {
12
+ #assigned = undefined;
13
+ #reached = false;
14
+ #result;
15
+ constructor(result) {
16
+ this.#result = result;
17
+ }
18
+ /**
19
+ * What `view` reads: the assigned name, or the declaration's default until one is assigned, and
20
+ * `null` on a Command that declares no result, whatever was assigned there. An assignment that is
21
+ * not a name reads as the default, because the getter answers a view name and the assignment is
22
+ * the boundary's fault. The assignment itself is kept either way, so the boundary still raises it.
23
+ */
24
+ read() {
25
+ if (!this.#result) {
26
+ return null;
27
+ }
28
+ const assigned = this.#assigned;
29
+ if (assigned !== undefined && typeof assigned.name === 'string') {
30
+ return assigned.name;
31
+ }
32
+ return this.#result.default;
33
+ }
34
+ /** The last assignment before the boundary wins; one made after it changes nothing. */
35
+ assign(identity, name) {
36
+ if (!this.#reached) {
37
+ this.#assigned = { identity, name };
38
+ }
39
+ }
40
+ /** The chain reached the dispatch boundary, so this run's view is fixed whatever follows. */
41
+ reach() {
42
+ this.#reached = true;
43
+ }
44
+ /**
45
+ * The name the boundary dispatches through: `null` when no middleware assigned one and the
46
+ * declaration's default stands. A plugin that selected a view has the name checked here.
47
+ */
48
+ resolve(path) {
49
+ const assigned = this.#assigned;
50
+ if (assigned === undefined) {
51
+ return null;
52
+ }
53
+ const plugin = pluginSentence(assigned.identity);
54
+ const { name } = assigned;
55
+ if (!this.#result) {
56
+ throw new InternalError(`${plugin} selected view "${String(name)}" on ${routedSubject(path)}, which declares no result.`, undefined);
57
+ }
58
+ if (typeof name !== 'string') {
59
+ throw new InternalError(`${plugin} selected a view that is not a string on ${routedSubject(path)}.`, undefined);
60
+ }
61
+ if (!this.#result.views.has(name)) {
62
+ throw new InternalError(`${plugin} selected view "${name}", which ${routedSubject(path)} does not name.`, undefined);
63
+ }
64
+ return name;
65
+ }
66
+ }
6
67
  /**
7
68
  * The default export a loader must resolve to. A loaded module is data core never declared, so the
8
69
  * check is the one runtime fact that decides it: the export is callable. Core calls it with the
@@ -133,8 +194,9 @@ function nextOf(turn) {
133
194
  throw error;
134
195
  });
135
196
  state.downstream = pending;
136
- // Core awaits the downstream promise itself, so a middleware that never awaits `next()` still
137
- // Holds the chain open and never ends the run with an unobserved rejection.
197
+ // Core awaits the downstream promise itself.
198
+ // A middleware that never awaits `next()` still holds the chain open.
199
+ // The run therefore never ends with an unobserved rejection.
138
200
  void pending.catch(() => undefined);
139
201
  return pending;
140
202
  };
@@ -174,8 +236,8 @@ async function settle(turn, thrown) {
174
236
  return reported(chain, 'taken-over');
175
237
  }
176
238
  await quiet(state.downstream);
177
- // A middleware that caught the rejection reports what the chain reached; the recorded failure
178
- // Still decides the exit code.
239
+ // A middleware that caught the rejection reports what the chain reached.
240
+ // The recorded failure still decides the exit code.
179
241
  return reported(chain, state.outcome ?? (chain.invoked() ? 'dispatched' : 'taken-over'));
180
242
  }
181
243
  /** The module one loader answers with, whether it throws where it is called or rejects later. */
@@ -215,12 +277,23 @@ async function runEntry(entry, index, chain) {
215
277
  return settle(turn, thrown);
216
278
  }
217
279
  /** The whole chain, answering with the failure it raised when a middleware caught that failure. */
218
- async function runChain(invocation, routed, entries) {
280
+ async function runChain(invocation, routed, prepared) {
281
+ const entries = activatedEntries(invocation.plugins, routed.scan);
219
282
  const run = { invoked: false, raised: undefined };
283
+ const selection = new ViewSelection(prepared.result);
284
+ /**
285
+ * The dispatch boundary: the point the chain reaches when its last middleware continues. Core
286
+ * raises the held fault here, so it ranks ahead of a bad view assignment, or else reads the
287
+ * selected view and dispatches the action.
288
+ */
220
289
  const terminal = async () => {
221
- const dispatch = await prepareDispatch(invocation.graph, routed, invocation);
290
+ selection.reach();
291
+ if (prepared.kind === 'held') {
292
+ throw prepared.fault;
293
+ }
294
+ const view = selection.resolve(routed.path);
222
295
  run.invoked = true;
223
- await dispatch();
296
+ await prepared.dispatch(view);
224
297
  return 'dispatched';
225
298
  };
226
299
  const cancelled = () => invocation.signal.aborted;
@@ -245,7 +318,14 @@ async function runChain(invocation, routed, entries) {
245
318
  next,
246
319
  options: entry.options,
247
320
  out: invocation.out,
321
+ request: prepared.request,
248
322
  signal: invocation.signal,
323
+ get view() {
324
+ return selection.read();
325
+ },
326
+ set view(name) {
327
+ selection.assign(entry.identity, name);
328
+ },
249
329
  }),
250
330
  invoked: () => run.invoked,
251
331
  record: (error) => {
@@ -271,17 +351,21 @@ async function runChain(invocation, routed, entries) {
271
351
  return run.raised;
272
352
  }
273
353
  /**
274
- * Runs one invocation: the global pre-scan, routing, the middleware chain, and the phases the chain
275
- * terminates in. A middleware that returns without calling `next()` has taken over, so the
276
- * remaining tokens are never parsed and nothing later in the chain runs.
354
+ * Runs one invocation: the global pre-scan, routing, the dispatch this invocation prepares, and the
355
+ * middleware chain it then runs. Local parsing and validation run ahead of the chain so that a
356
+ * middleware reads the request, and the fault they find is held until the dispatch boundary. A
357
+ * middleware that returns without calling `next()` has taken over, so the held fault is never
358
+ * raised and nothing later in the chain runs.
277
359
  */
278
360
  async function runInvocation(invocation) {
279
361
  const routed = routeInvocation(invocation.graph, invocation.host.argv);
280
- const entries = activatedEntries(invocation.plugins, routed.scan);
281
- const raised = await runChain(invocation, routed, entries);
362
+ invocation.route(routed.path);
363
+ const prepared = await prepareDispatch(invocation.graph, routed, invocation);
364
+ const raised = await runChain(invocation, routed, prepared);
282
365
  if (raised) {
283
- // The chain resolved because a middleware caught the rejection. The failure it caught still
284
- // Decides the exit code, the rule an action's caught output rejection already follows.
366
+ // The chain resolved because a middleware caught the rejection.
367
+ // The failure it caught still decides the exit code.
368
+ // That is the rule an action's caught output rejection already follows.
285
369
  throw raised;
286
370
  }
287
371
  }
package/dist/command.d.ts CHANGED
@@ -1,9 +1,11 @@
1
+ import type { RegisteredGlobals } from './environment.js';
1
2
  import type { DescriptorRegistry, ExtensionRecords, ExtensionValue } from './extension.js';
2
- import type { BuiltGlobals, GlobalOptions } from './globals.js';
3
+ import type { BuiltGlobals, GlobalsState } from './globals.js';
3
4
  import { compileOptions } from './options.js';
4
5
  import type { OptionValues } from './options.js';
5
6
  import type { BuiltPlugin, PluginBuild } from './plugin.js';
6
- import type { Action, ArgumentConfig, ArgumentValue, declaredTypes, DeclaredTypes, DefaultConstraint, GlobalNameConstraint, Host, MultipleConstraint, NameConstraint, OptionConfig, OptionValue, Out, ValidateOmittedConstraint } from './types.js';
7
+ import type { ContextualStyle } from './style.js';
8
+ import type { Action, ActionChannel, ArgumentConfig, ArgumentValue, declaredTypes, DeclaredResult, DeclaredTypes, DefaultConstraint, GlobalNameConstraint, Host, MultipleConstraint, NameConstraint, OpenResult, OptionConfig, OptionValue, Out, Request, ResultBinding, ResultViews, ResultViewsOf, RowViews, ValidateOmittedConstraint } from './types.js';
7
9
  import type { ArgumentInput, DefaultValues, InputDeclaration, OptionInput, ValidatedInputs } from './validation.js';
8
10
  /** One positional slot: the declaration it fills and whether it takes the remaining tokens. */
9
11
  export interface ArgumentSlot {
@@ -12,8 +14,10 @@ export interface ArgumentSlot {
12
14
  variadic: boolean;
13
15
  }
14
16
  export interface DispatchInput {
17
+ style: ContextualStyle;
15
18
  host: Host;
16
- out: Out;
19
+ /** The action's channel, whose `results` accepts whatever the routed declaration named. */
20
+ out: Out<OpenResult>;
17
21
  passthrough: string[];
18
22
  signal: AbortSignal;
19
23
  values: ValidatedInputs;
@@ -44,29 +48,39 @@ export interface BuiltCommand {
44
48
  inputs: readonly InputDeclaration[];
45
49
  name: string | null;
46
50
  options: ReturnType<typeof compileOptions>;
51
+ /** The result the Command declares, or nothing where it declares none. */
52
+ /** The built declaration is the shape the write site reads, so the channel carries it. */
53
+ result: DeclaredResult | undefined;
47
54
  routes: ReadonlyMap<string, RoutedChild>;
48
55
  }
49
56
  /** Phantom key. It marks a Command value, so only a Command can be attached as a child. */
50
57
  export declare const commandValue: unique symbol;
51
58
  /**
52
- * Every authoring call a Command can publish. A Command's type state is a subset of these names,
59
+ * The input, alias, child, and action calls a Command can publish. Its type state is a subset,
53
60
  * and each call removes the names it invalidates. A Command attaches children at any depth, so
54
61
  * `command()` belongs to every Command and to the unnamed root alike.
55
62
  */
56
- export type CommandMethod = 'action' | 'alias' | 'argument' | 'command' | 'option';
63
+ export type CommandMethod = 'action' | 'alias' | 'argument' | 'command' | 'option' | 'result' | 'rows';
57
64
  /** One Command declares arguments or attaches children, so the first call removes the other. */
58
65
  export type AfterArgument<State> = Exclude<State, 'command'>;
59
66
  /** The same rule read from the other side. */
60
67
  export type AfterCommand<State> = Exclude<State, 'argument'>;
61
- /** The action is the last declaration call, so no declaration call survives it. */
68
+ /** One Command declares one result, so either call removes both. */
69
+ export type AfterResult<State> = Exclude<State, 'result' | 'rows'>;
70
+ /** Registering the action closes input, alias, child, and further action declarations. */
62
71
  export type AfterAction = never;
63
- /** A declaration made after the action, kept in authoring order so build reports the first. */
72
+ /** A declaration made after its authoring phase closed; build reports the first in call order. */
64
73
  type LateDeclaration = {
65
74
  alias: string;
66
75
  kind: 'alias';
76
+ } | {
77
+ name: string;
78
+ kind: 'global';
67
79
  } | {
68
80
  child: object;
69
81
  kind: 'child';
82
+ } | {
83
+ kind: 'result';
70
84
  } | {
71
85
  input: InputDeclaration;
72
86
  kind: 'input';
@@ -76,8 +90,8 @@ type LateDeclaration = {
76
90
  * which the types reject and a JavaScript author can still write, reports as the call it is.
77
91
  */
78
92
  type AliasDeclaration = readonly string[];
79
- /** The attachable shape of a Command, without its inferred declaration types. */
80
- interface AttachedCommand {
93
+ /** The private handle one graph node is built through, without its inferred declaration types. */
94
+ interface CommandNodeHandle {
81
95
  readonly name: string | null;
82
96
  build(context: BuildContext): BuiltCommand;
83
97
  }
@@ -92,15 +106,36 @@ interface BuildContext {
92
106
  descriptors: DescriptorRegistry;
93
107
  extensions: ExtensionRecords;
94
108
  globals: BuiltGlobals;
95
- owners: Map<AttachedCommand, string | null>;
109
+ owners: Map<CommandNodeHandle, string | null>;
110
+ /** The route from the root to the Command being built, which a lifecycle hook reads. */
111
+ path: readonly string[];
112
+ /** The installed plugins in installation order, whose hooks run over every Command. */
113
+ plugins: readonly BuiltPlugin[];
96
114
  }
97
115
  /**
98
116
  * Everything one Command declaration holds. The transitions below copy it with fields replaced, and
99
117
  * the Command and Application builders share them, so one declaration call has one implementation.
100
- * Absent globals stay `undefined`, so every declaration without globals agrees on identity.
101
118
  */
119
+ /**
120
+ * One call of the results lane, in the order it was made. A `result()` or `rows()` call declares
121
+ * the unit, and a `views()` call reshapes the views of whichever declaration it follows.
122
+ * Each record arrives unexamined, because build owns every rule the lane carries.
123
+ */
124
+ export type ResultCall = {
125
+ kind: 'value' | 'rows';
126
+ views: unknown;
127
+ } | {
128
+ default: unknown;
129
+ kind: 'views';
130
+ views: unknown;
131
+ };
102
132
  export interface CommandState<Args, Options, Globals> {
103
- actions: readonly Action<Args, Globals & Options>[];
133
+ /**
134
+ * The registered actions, with the declared result erased. An action is stored under the widest
135
+ * result, so a handler typed from its own declaration stores here and the channel that carries
136
+ * the result is built for it at dispatch.
137
+ */
138
+ actions: readonly Action<Args, Globals & Options, OpenResult>[];
104
139
  aliases: readonly AliasDeclaration[];
105
140
  bind: (values: ValidatedInputs) => {
106
141
  args: Args;
@@ -109,13 +144,13 @@ export interface CommandState<Args, Options, Globals> {
109
144
  children: readonly object[];
110
145
  deprecated: unknown;
111
146
  description: unknown;
112
- extensions: unknown;
147
+ extensions: readonly unknown[];
113
148
  hidden: unknown;
114
- globals: GlobalOptions<Globals> | undefined;
115
149
  inputs: readonly InputDeclaration[];
116
150
  late: readonly LateDeclaration[];
117
151
  name: string | null;
118
152
  options: unknown;
153
+ results: readonly ResultCall[];
119
154
  }
120
155
  /**
121
156
  * The state every declaration starts from. The unnamed root and each named Command share it.
@@ -126,7 +161,6 @@ export declare function freshState<Globals>(declaration: {
126
161
  deprecated: unknown;
127
162
  description: unknown;
128
163
  extensions: unknown;
129
- globals: GlobalOptions<Globals> | undefined;
130
164
  hidden: unknown;
131
165
  name: string | null;
132
166
  options: unknown;
@@ -135,9 +169,21 @@ export declare function freshState<Globals>(declaration: {
135
169
  export declare function declareArgument<Args, Options, Globals, Name extends string, Config extends ArgumentConfig>(state: CommandState<Args, Options, Globals>, input: ArgumentInput<Name, Config>): CommandState<Args & Record<Name, ArgumentValue<Config>>, Options, Globals>;
136
170
  /** The declared value joins `options` under its literal name, typed by its own config. */
137
171
  export declare function declareOption<Args, Options, Globals, Name extends string, Config extends OptionConfig>(state: CommandState<Args, Options, Globals>, input: OptionInput<Name, Config>): CommandState<Args, Options & Record<Name, OptionValue<Config>>, Globals>;
172
+ /** Globals close when composition starts; retain late calls for the shared build-order check. */
173
+ export declare function recordGlobalOption<Args, Options, Globals>(state: CommandState<Args, Options, Globals>, name: string): CommandState<Args, Options, Globals>;
138
174
  /** One call's names stay one group, so the empty call the types reject still reports as one. */
139
175
  export declare function declareAlias<Args, Options, Globals>(state: CommandState<Args, Options, Globals>, names: AliasDeclaration): CommandState<Args, Options, Globals>;
140
- export declare function declareAction<Args, Options, Globals>(state: CommandState<Args, Options, Globals>, handler: Action<Args, Globals & Options>): CommandState<Args, Options, Globals>;
176
+ /** Extension layers remain open after inputs and the action have been fixed. */
177
+ export declare function declareExtensions<Args, Options, Globals>(state: CommandState<Args, Options, Globals>, values: readonly ExtensionValue<'command'>[]): CommandState<Args, Options, Globals>;
178
+ /**
179
+ * The handler is typed against the result its own declaration carries, and the state holds one
180
+ * list for every declaration, so the context each handler receives is read back at the call.
181
+ */
182
+ export declare function declareAction<Args, Options, Globals, Result>(state: CommandState<Args, Options, Globals>, handler: Action<Args, Globals & Options, Result>): CommandState<Args, Options, Globals>;
183
+ /** The result declaration, which closes both result calls and reports lateness like the rest. */
184
+ export declare function declareResult<Args, Options, Globals>(state: CommandState<Args, Options, Globals>, kind: 'value' | 'rows', declaration: unknown): CommandState<Args, Options, Globals>;
185
+ /** A `views()` call reshapes views and closes nothing, so it is never a late declaration. */
186
+ export declare function declareResultViews<Args, Options, Globals>(state: CommandState<Args, Options, Globals>, replacements: unknown, options: unknown): CommandState<Args, Options, Globals>;
141
187
  /** Attaching is a declaration call too, so the receiver keeps the children it already had. */
142
188
  export declare function attachChild<Args, Options, Globals>(state: CommandState<Args, Options, Globals>, child: object): CommandState<Args, Options, Globals>;
143
189
  /** Validates one declaration against the shared globals table and compiles it for dispatch. */
@@ -149,27 +195,57 @@ export interface BuiltGraph {
149
195
  root: BuiltCommand;
150
196
  }
151
197
  /** The globals table and the owners record each compile once per invocation and the whole graph shares them. */
152
- export declare function buildGraph<Args, Options, Globals>(root: CommandState<Args, Options, Globals>, install: PluginBuild & {
198
+ export declare function buildGraph<Args, Options, Globals>(root: CommandState<Args, Options, Globals>, globals: GlobalsState<Globals>, install: PluginBuild & {
153
199
  plugins: readonly BuiltPlugin[];
154
200
  }): BuiltGraph;
155
- export declare class CommandBuilder<Args, Options, Globals, State extends CommandMethod = CommandMethod> {
201
+ export declare class CommandBuilder<Args, Options, Globals, State extends CommandMethod = CommandMethod, Result = unknown> {
156
202
  #private;
157
203
  readonly [commandValue]: true;
158
- readonly [declaredTypes]: DeclaredTypes<Args, Options, Globals>;
204
+ readonly [declaredTypes]: DeclaredTypes<Args, Options, Globals, Result>;
159
205
  constructor(state: CommandState<Args, Options, Globals>);
160
206
  get name(): string | null;
161
- argument<const Name extends string, const Config extends ArgumentConfig>(name: Name, config: Config & NameConstraint<Name> & NoInfer<DefaultConstraint<Config>> & NoInfer<ValidateOmittedConstraint<Config>>): Command<Args & Record<Name, ArgumentValue<Config>>, Options, Globals, AfterArgument<State>>;
162
- option<const Name extends string, const Config extends OptionConfig>(name: Name, config: Config & NameConstraint<Name> & GlobalNameConstraint<Name, Globals> & NoInfer<DefaultConstraint<Config>> & NoInfer<MultipleConstraint<Config>> & NoInfer<ValidateOmittedConstraint<Config>>): Command<Args, Options & Record<Name, OptionValue<Config>>, Globals, State>;
207
+ argument<const Name extends string, const Config extends ArgumentConfig>(name: Name, config: Config & NameConstraint<Name> & NoInfer<DefaultConstraint<Config>> & NoInfer<ValidateOmittedConstraint<Config>>): Command<Args & Record<Name, ArgumentValue<Config>>, Options, Globals, AfterArgument<State>, Result>;
208
+ option<const Name extends string, const Config extends OptionConfig>(name: Name, config: Config & NameConstraint<Name> & GlobalNameConstraint<Name, Globals> & NoInfer<DefaultConstraint<Config>> & NoInfer<MultipleConstraint<Config>> & NoInfer<ValidateOmittedConstraint<Config>>): Command<Args, Options & Record<Name, OptionValue<Config>>, Globals, State, Result>;
163
209
  /**
164
210
  * Aliases are other bare tokens that route to this Command. They invalidate no call, and the
165
211
  * tuple rest parameter rejects a call that names none.
166
212
  */
167
- alias(...names: [string, ...string[]]): Command<Args, Options, Globals, State>;
213
+ alias(...names: [string, ...string[]]): Command<Args, Options, Globals, State, Result>;
168
214
  /** A child arrives in any type state, because its own action is the call that finished it. */
169
- command(child: Command<unknown, unknown, Globals>): Command<Args, Options, Globals, AfterCommand<State>>;
170
- /** The action is the last declaration call, so the value it returns publishes `AfterAction`. */
171
- action(handler: Action<Args, Globals & Options>): Command<Args, Options, Globals>;
215
+ command<const Child extends Command<unknown, unknown, Globals>>(child: Child & NoInfer<AttachmentConstraint<Globals, Child>>): Command<Args, Options, Globals, AfterCommand<State>, Result>;
216
+ /**
217
+ * The value this Command produces for its consumer. The type argument is stated by the author,
218
+ * so the views record states no type of its own and an omitted argument names none either.
219
+ */
220
+ result<Value>(declaration: {
221
+ views: ResultViews<NoInfer<Value>>;
222
+ }): Command<Args, Options, Globals, AfterResult<State>, {
223
+ kind: 'value';
224
+ value: Value;
225
+ }>;
226
+ /** The same declaration over a sequence, whose type argument is one row. */
227
+ rows<Row>(declaration: {
228
+ views: RowViews<NoInfer<Row>>;
229
+ }): Command<Args, Options, Globals, AfterResult<State>, {
230
+ kind: 'rows';
231
+ row: Row;
232
+ }>;
233
+ /**
234
+ * Views after the fact. It merges by key, so an existing name is replaced in place and a new one
235
+ * is appended, and `default` names the key core renders when nothing selects another.
236
+ */
237
+ views(replacements: ResultViewsOf<Result>, options?: {
238
+ default?: string;
239
+ }): Command<Args, Options, Globals, State, Result>;
240
+ /** The action closes input authoring; `extend()` remains outside this state transition. */
241
+ action(handler: Action<Args, Globals & Options, Result>): Command<Args, Options, Globals, AfterAction, Result>;
242
+ extend(...values: readonly ExtensionValue<'command'>[]): Command<Args, Options, Globals, State, Result>;
172
243
  build(context: BuildContext): BuiltCommand;
244
+ /**
245
+ * The same runtime value in the state the calling method's return type names. Each call states
246
+ * its own transition, and the declared result travels with it unless the call replaces it.
247
+ */
248
+ private derive;
173
249
  }
174
250
  /**
175
251
  * The authoring surface of a Command in one type state. Every call returns a new declaration value,
@@ -178,22 +254,30 @@ export declare class CommandBuilder<Args, Options, Globals, State extends Comman
178
254
  * calls a value still offers. It defaults to the state after `action()`, which publishes the fewest
179
255
  * calls, so `Command<A, O, G>` accepts a Command in any state, a finished one included.
180
256
  */
181
- export type Command<Args = {}, Options = {}, Globals = {}, State extends CommandMethod = AfterAction> = Pick<CommandBuilder<Args, Options, Globals, State>, typeof commandValue | typeof declaredTypes | State>;
257
+ export type Command<Args = {}, Options = {}, Globals = {}, State extends CommandMethod = AfterAction, Result = unknown> = Pick<CommandBuilder<Args, Options, Globals, State, Result>, typeof commandValue | typeof declaredTypes | 'extend' | State | ResultMethod<Result>>;
182
258
  /**
183
- * Everything a Command configures beside its declarations: the options value every Command in one
184
- * application shares, and the core facts the declaration carries.
259
+ * `views()` is published in every state on a declaration that carries a result, and on none that
260
+ * carries none. It is a key of the picked surface rather than a member of the state union, because
261
+ * the state union answers the calls a declaration closes and this one closes nothing.
185
262
  */
186
- export interface CommandOptions<Globals = {}> {
187
- globals?: GlobalOptions<Globals>;
263
+ export type ResultMethod<Result> = unknown extends Result ? never : 'views';
264
+ /**
265
+ * The core facts and initial extension values a named Command carries.
266
+ */
267
+ export interface CommandOptions {
188
268
  description?: string;
189
269
  hidden?: boolean;
190
270
  deprecated?: string;
191
271
  extensions?: readonly ExtensionValue<'command'>[];
192
272
  }
193
- interface CommandConstructor {
194
- new (name: string): Command<{}, {}, {}, CommandMethod>;
195
- new <Globals = {}>(name: string, options: CommandOptions<Globals>): Command<{}, {}, Globals, CommandMethod>;
196
- }
273
+ /** Collect every union member's known local keys before testing for a global collision. */
274
+ type LocalKeys<Child> = Child extends {
275
+ readonly [declaredTypes]: {
276
+ options: infer Options;
277
+ };
278
+ } ? keyof Options : never;
279
+ export type AttachmentConstraint<Globals, Child> = Extract<keyof Globals, LocalKeys<Child>> extends never ? unknown : never;
280
+ type CommandConstructor = new (name: string, options?: CommandOptions) => Command<{}, {}, RegisteredGlobals, CommandMethod>;
197
281
  /** The public constructor takes a name and one options object, as the Application does. */
198
282
  export declare const Command: CommandConstructor;
199
283
  /** Every declaration in the graph, so defaults are validated before any token is read. */
@@ -213,15 +297,35 @@ export interface RoutedInvocation {
213
297
  }
214
298
  /** Consumes the globals table, then routes the remaining bare tokens to a Command. */
215
299
  export declare function routeInvocation(graph: BuiltGraph, argv: readonly string[]): RoutedInvocation;
216
- /**
217
- * The phases the middleware chain terminates in: the callable check, local parsing, validation, and
218
- * the action. It answers with the call that dispatches, so the caller records that the action was
219
- * invoked at the moment it invokes it and no earlier failure reads as a dispatch.
220
- */
221
- export declare function prepareDispatch(graph: BuiltGraph, routed: RoutedInvocation, invocation: {
300
+ /** What one invocation reaches the middleware chain with. */
301
+ export interface DispatchInvocation {
302
+ /** The channel the action receives, which the results lane builds from the routed node. */
303
+ channel: (binding: ResultBinding) => ActionChannel;
222
304
  defaults: DefaultValues;
223
305
  host: Host;
224
- out: Out;
225
306
  signal: AbortSignal;
226
- }): Promise<() => unknown>;
307
+ style: ContextualStyle;
308
+ }
309
+ /**
310
+ * One invocation prepared ahead of the middleware chain. `'ready'` carries the request a middleware
311
+ * reads and the call that dispatches; `'held'` carries the fault this phase found, which core
312
+ * raises at the dispatch boundary and never before, so a takeover swallows it. `result` is what the
313
+ * routed Command declared, whose views a middleware selects among, on either shape.
314
+ */
315
+ export type Prepared = {
316
+ result: DeclaredResult | undefined;
317
+ } & ({
318
+ dispatch: (view: string | null) => Promise<void>;
319
+ kind: 'ready';
320
+ request: Request;
321
+ } | {
322
+ fault: unknown;
323
+ kind: 'held';
324
+ request: null;
325
+ });
326
+ /**
327
+ * Prepares one dispatch ahead of the middleware chain and holds whatever fault it found, so a
328
+ * middleware reads the request before the action runs and a takeover never observes the fault.
329
+ */
330
+ export declare function prepareDispatch(graph: BuiltGraph, routed: RoutedInvocation, invocation: DispatchInvocation): Promise<Prepared>;
227
331
  export {};