@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.
- package/dist/application.d.ts +59 -34
- package/dist/application.js +162 -59
- package/dist/chain.d.ts +25 -6
- package/dist/chain.js +99 -15
- package/dist/command.d.ts +146 -42
- package/dist/command.js +620 -78
- package/dist/environment.d.ts +22 -0
- package/dist/environment.js +1 -0
- package/dist/errors.d.ts +31 -59
- package/dist/errors.js +49 -106
- package/dist/extension.d.ts +5 -1
- package/dist/extension.js +18 -1
- package/dist/globals.d.ts +14 -25
- package/dist/globals.js +10 -61
- package/dist/glyphs.generated.d.ts +464 -0
- package/dist/glyphs.generated.js +491 -0
- package/dist/host.js +2 -1
- package/dist/index.d.ts +14 -5
- package/dist/index.js +5 -2
- package/dist/inspect.d.ts +26 -3
- package/dist/inspect.js +19 -5
- package/dist/lanes.d.ts +26 -0
- package/dist/lanes.js +45 -0
- package/dist/output.d.ts +93 -15
- package/dist/output.js +307 -34
- package/dist/plugin.d.ts +32 -16
- package/dist/plugin.js +46 -18
- package/dist/rendering.d.ts +21 -0
- package/dist/rendering.js +72 -0
- package/dist/sequence.d.ts +41 -0
- package/dist/sequence.js +225 -0
- package/dist/style-ansi.d.ts +13 -0
- package/dist/style-ansi.js +306 -0
- package/dist/style-layout.d.ts +29 -0
- package/dist/style-layout.js +228 -0
- package/dist/style-resolve.d.ts +6 -0
- package/dist/style-resolve.js +26 -0
- package/dist/style-state.d.ts +14 -0
- package/dist/style-state.js +179 -0
- package/dist/style-wire.d.ts +31 -0
- package/dist/style-wire.js +201 -0
- package/dist/style.d.ts +86 -0
- package/dist/style.js +201 -0
- package/dist/theme.d.ts +3 -0
- package/dist/theme.js +22 -0
- package/dist/types.d.ts +169 -21
- package/dist/validation.d.ts +8 -1
- package/dist/validation.js +17 -2
- package/dist/view.d.ts +180 -0
- package/dist/view.js +307 -0
- package/package.json +2 -1
package/dist/application.d.ts
CHANGED
|
@@ -1,25 +1,23 @@
|
|
|
1
|
-
import type { AfterAction, AfterArgument, AfterCommand, Command, CommandMethod, CommandState } from './command.js';
|
|
2
|
-
import type {
|
|
1
|
+
import type { AfterAction, AttachmentConstraint, AfterArgument, AfterCommand, AfterResult, Command, CommandMethod, CommandState, ResultMethod } from './command.js';
|
|
2
|
+
import type { ApplicationEnvironment, applicationEnvironment } from './environment.js';
|
|
3
3
|
import type { ExtensionValue } from './extension.js';
|
|
4
|
-
import type {
|
|
4
|
+
import type { GlobalsState } from './globals.js';
|
|
5
5
|
import type { CommandGraph } from './inspect.js';
|
|
6
6
|
import type { Plugin } from './plugin.js';
|
|
7
|
-
import type {
|
|
7
|
+
import type { RenderingPolicy } from './rendering.js';
|
|
8
|
+
import type { Action, ArgumentConfig, ArgumentValue, declaredTypes, DeclaredTypes, DefaultConstraint, GlobalNameConstraint, MultipleConstraint, NameConstraint, ExitCode, OptionConfig, OptionValue, ResultViews, ResultViewsOf, RowViews, RunOptions, ValidateOmittedConstraint } from './types.js';
|
|
9
|
+
import type { ViewOverride } from './view.js';
|
|
8
10
|
/**
|
|
9
11
|
* Every authoring call an Application can publish, beside `run()` and `name`, which always remain.
|
|
10
12
|
* An Application's type state is a subset of these, and each call removes the names it invalidates.
|
|
11
13
|
* The unnamed root declares what a named Command declares, except for `alias()`: the root answers
|
|
12
14
|
* to no bare token, so it has no name to alias.
|
|
13
15
|
*/
|
|
14
|
-
export type ApplicationMethod = Exclude<CommandMethod, 'alias'
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
export interface ApplicationOptions<Globals = {}> {
|
|
20
|
-
globals?: GlobalOptions<Globals>;
|
|
21
|
-
failures?: readonly FailureRenderer[];
|
|
22
|
-
plugins?: readonly Plugin[];
|
|
16
|
+
export type ApplicationMethod = Exclude<CommandMethod, 'alias'> | 'globalOption';
|
|
17
|
+
export interface ApplicationOptions<Plugins extends readonly Plugin[] = readonly Plugin[]> {
|
|
18
|
+
rendering?: RenderingPolicy;
|
|
19
|
+
views?: readonly ViewOverride[];
|
|
20
|
+
plugins?: Plugins;
|
|
23
21
|
extensions?: readonly ExtensionValue<'command'>[];
|
|
24
22
|
description?: string;
|
|
25
23
|
version?: string;
|
|
@@ -28,33 +26,63 @@ export interface ApplicationOptions<Globals = {}> {
|
|
|
28
26
|
* The Application holds the unnamed root's declaration state and applies the same transitions a
|
|
29
27
|
* Command does, so each declaration call has one typed implementation and no builder to recover.
|
|
30
28
|
*/
|
|
31
|
-
declare class ApplicationBuilder<Args, Options, Globals, State extends ApplicationMethod = ApplicationMethod> {
|
|
29
|
+
declare class ApplicationBuilder<Args, Options, Globals, State extends ApplicationMethod = ApplicationMethod, Plugins extends readonly Plugin[] = readonly [], Result = unknown> {
|
|
32
30
|
#private;
|
|
33
|
-
readonly [
|
|
31
|
+
readonly [applicationEnvironment]: ApplicationEnvironment<Globals, Plugins>;
|
|
32
|
+
readonly [declaredTypes]: DeclaredTypes<Args, Options, Globals, Result>;
|
|
34
33
|
constructor(name: string, root: CommandState<Args, Options, Globals>, config: {
|
|
35
34
|
declared: DeclaredFacts;
|
|
36
|
-
|
|
35
|
+
views: unknown;
|
|
37
36
|
options?: unknown;
|
|
38
|
-
plugins:
|
|
37
|
+
plugins: Plugins | undefined;
|
|
38
|
+
globals: GlobalsState<Globals>;
|
|
39
39
|
});
|
|
40
40
|
get name(): string;
|
|
41
|
-
argument<const Name extends string, const Config extends ArgumentConfig>(name: Name, config: Config & NameConstraint<Name> & NoInfer<DefaultConstraint<Config>> & NoInfer<ValidateOmittedConstraint<Config>>): Application<Args & Record<Name, ArgumentValue<Config>>, Options, Globals, AfterArgument<State
|
|
42
|
-
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>>): Application<Args, Options & Record<Name, OptionValue<Config>>, Globals, State>;
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
argument<const Name extends string, const Config extends ArgumentConfig>(name: Name, config: Config & NameConstraint<Name> & NoInfer<DefaultConstraint<Config>> & NoInfer<ValidateOmittedConstraint<Config>>): Application<Args & Record<Name, ArgumentValue<Config>>, Options, Globals, AfterArgument<State>, Plugins, Result>;
|
|
42
|
+
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>>): Application<Args, Options & Record<Name, OptionValue<Config>>, Globals, State, Plugins, Result>;
|
|
43
|
+
globalOption<const Name extends string, const Config extends OptionConfig>(name: Name, config: Config & NameConstraint<Name> & (Name extends keyof Options ? {
|
|
44
|
+
'This option name is already declared as a local option': Name;
|
|
45
|
+
} : unknown) & NoInfer<DefaultConstraint<Config>> & NoInfer<MultipleConstraint<Config>> & NoInfer<ValidateOmittedConstraint<Config>>): Application<Args, Options, Globals & Record<Name, OptionValue<Config>>, State, Plugins, Result>;
|
|
46
|
+
/** Registering the action closes input authoring; extension configuration remains available. */
|
|
47
|
+
action(handler: Action<Args, Globals & Options, Result>): Application<Args, Options, Globals, AfterAction, Plugins, Result>;
|
|
45
48
|
/** A child arrives in any type state, because its own action is the call that finished it. */
|
|
46
|
-
command
|
|
49
|
+
command<const Child extends Command<unknown, unknown, Globals>>(child: Child & NoInfer<AttachmentConstraint<Globals, Child>>): Application<Args, Options, Globals, AfterCommand<Exclude<State, 'globalOption'>>, Plugins, Result>;
|
|
50
|
+
/**
|
|
51
|
+
* The value the root action produces for its consumer. The type argument is stated by the
|
|
52
|
+
* author, as it is on a Command.
|
|
53
|
+
*/
|
|
54
|
+
result<Value>(declaration: {
|
|
55
|
+
views: ResultViews<NoInfer<Value>>;
|
|
56
|
+
}): Application<Args, Options, Globals, AfterResult<State>, Plugins, {
|
|
57
|
+
kind: 'value';
|
|
58
|
+
value: Value;
|
|
59
|
+
}>;
|
|
60
|
+
/** The same declaration over a sequence, whose type argument is one row. */
|
|
61
|
+
rows<Row>(declaration: {
|
|
62
|
+
views: RowViews<NoInfer<Row>>;
|
|
63
|
+
}): Application<Args, Options, Globals, AfterResult<State>, Plugins, {
|
|
64
|
+
kind: 'rows';
|
|
65
|
+
row: Row;
|
|
66
|
+
}>;
|
|
67
|
+
/** Views after the fact, merged by key, as it is on a Command. */
|
|
68
|
+
views(replacements: ResultViewsOf<Result>, options?: {
|
|
69
|
+
default?: string;
|
|
70
|
+
}): Application<Args, Options, Globals, State, Plugins, Result>;
|
|
71
|
+
extend(...values: readonly ExtensionValue<'command'>[]): Application<Args, Options, Globals, State, Plugins, Result>;
|
|
47
72
|
/**
|
|
48
|
-
*
|
|
73
|
+
* Root declaration calls preserve the Application configuration. The next state
|
|
49
74
|
* travels through this call: each method names its transition in its return type, and the
|
|
50
75
|
* wrapper publishes the same runtime value in exactly that state.
|
|
51
76
|
*/
|
|
52
77
|
private derive;
|
|
53
78
|
/**
|
|
54
|
-
* Every rule that reads the declarations alone, in the order `run()` reads them: the
|
|
55
|
-
*
|
|
56
|
-
* then the whole Command graph. The
|
|
57
|
-
*
|
|
79
|
+
* Every rule that reads the declarations alone, in the order `run()` reads them: the
|
|
80
|
+
* application's own view overrides, the options slot, the installed list, each plugin's
|
|
81
|
+
* declarations, then the whole Command graph. The application's overrides are read and published
|
|
82
|
+
* first, because they need core's identities and nothing else, so every later declaration error
|
|
83
|
+
* reaches them, while a fault in that list itself reports through core's own text. The merged
|
|
84
|
+
* registry is published once the whole build has succeeded, so a build-time fault never resolves
|
|
85
|
+
* through a plugin's overrides, which build has not yet validated.
|
|
58
86
|
*/
|
|
59
87
|
private prepare;
|
|
60
88
|
/**
|
|
@@ -74,19 +102,16 @@ declare class ApplicationBuilder<Args, Options, Globals, State extends Applicati
|
|
|
74
102
|
* It defaults to the state after `action()`, which publishes the fewest calls, so
|
|
75
103
|
* `Application<A, O, G>` accepts an application in any state, a finished one included.
|
|
76
104
|
*/
|
|
77
|
-
export type Application<Args = {}, Options = {}, Globals = {}, State extends ApplicationMethod = AfterAction> = Pick<ApplicationBuilder<Args, Options, Globals, State>, typeof declaredTypes | 'inspect' | 'name' | 'run' | State
|
|
105
|
+
export type Application<Args = {}, Options = {}, Globals = {}, State extends ApplicationMethod = AfterAction, Plugins extends readonly Plugin[] = readonly Plugin[], Result = unknown> = Pick<ApplicationBuilder<Args, Options, Globals, State, Plugins, Result>, typeof applicationEnvironment | typeof declaredTypes | 'extend' | 'inspect' | 'name' | 'run' | State | ResultMethod<Result>>;
|
|
78
106
|
interface ApplicationConstructor {
|
|
79
|
-
new (name: string): Application<{}, {}, {}, ApplicationMethod>;
|
|
80
|
-
new <
|
|
107
|
+
new (name: string): Application<{}, {}, {}, ApplicationMethod, readonly []>;
|
|
108
|
+
new <const Plugins extends readonly Plugin[] = readonly []>(name: string, options: ApplicationOptions<Plugins>): Application<{}, {}, {}, ApplicationMethod, Plugins>;
|
|
81
109
|
}
|
|
82
110
|
/** The same facts as the constructor captured them, before any rule has read them. */
|
|
83
111
|
interface DeclaredFacts {
|
|
112
|
+
rendering: unknown;
|
|
84
113
|
description: unknown;
|
|
85
114
|
version: unknown;
|
|
86
115
|
}
|
|
87
|
-
/**
|
|
88
|
-
* The public constructor takes a name and one options object. The globals type narrows to the
|
|
89
|
-
* supplied value, and the failure renderers configure the application the way its commands do.
|
|
90
|
-
*/
|
|
91
116
|
export declare const Application: ApplicationConstructor;
|
|
92
117
|
export {};
|
package/dist/application.js
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import { runInvocation } from './chain.js';
|
|
2
|
-
import { attachChild, buildGraph, collectInputs, declareAction, declareArgument, declareOption, freshState, } from './command.js';
|
|
3
|
-
import {
|
|
2
|
+
import { attachChild, buildGraph, collectInputs, declareAction, declareExtensions, declareArgument, declareOption, declareResult, declareResultViews, freshState, recordGlobalOption, } from './command.js';
|
|
3
|
+
import { DeclarationError, InternalError, reasonOf, toFailure } from './errors.js';
|
|
4
4
|
import { checkDescription, checkNoListingFacts, checkVersion, isPlainObject } from './facts.js';
|
|
5
|
-
import {
|
|
5
|
+
import { declareGlobalOption, emptyGlobals } from './globals.js';
|
|
6
6
|
import { captureHost } from './host.js';
|
|
7
7
|
import { inspectGraph } from './inspect.js';
|
|
8
|
+
import { coreViews } from './lanes.js';
|
|
8
9
|
import { Output, reportPlainly } from './output.js';
|
|
9
10
|
import { buildPlugins, installPlugins, ownedSignals, pluginSentence } from './plugin.js';
|
|
11
|
+
import { renderingPolicy } from './rendering.js';
|
|
10
12
|
import { bracketRun, cancellationCode, isCancellationEcho } from './signals.js';
|
|
11
13
|
import { captureConfig, checkDeclarations, prepareInputs } from './validation.js';
|
|
14
|
+
import { buildViews, describeFailure, viewIdentities } from './view.js';
|
|
12
15
|
/** The registry a failure is reported through when the application's own could not be built. */
|
|
13
|
-
const
|
|
16
|
+
const noViews = [];
|
|
14
17
|
/**
|
|
15
18
|
* Whether one failure is the cancellation the run already reports, which core does not report a
|
|
16
19
|
* second time. Any other failure after cancellation is rendered as usual.
|
|
@@ -18,6 +21,29 @@ const noRegistrations = new Map();
|
|
|
18
21
|
function silenced(thrown, signal, cancelled) {
|
|
19
22
|
return cancelled !== undefined && isCancellationEcho(thrown, signal.reason);
|
|
20
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* The stand-in for "this run has no primary failure", which is a value no thrown value can be.
|
|
26
|
+
* `undefined` is itself throwable, so the absence is spelled here rather than borrowed from it.
|
|
27
|
+
*/
|
|
28
|
+
const noPrimary = Symbol('no primary');
|
|
29
|
+
/**
|
|
30
|
+
* Whether the primary outcome carries one recorded cause already: the value itself, or a failure
|
|
31
|
+
* that wraps it at any depth, which an action that caught a source failure and rethrew its own
|
|
32
|
+
* produces. Such a cause is reported once, through the primary outcome that carries it.
|
|
33
|
+
*/
|
|
34
|
+
function carried(primary, cause) {
|
|
35
|
+
const seen = new Set();
|
|
36
|
+
let value = primary;
|
|
37
|
+
while (value !== noPrimary && !seen.has(value)) {
|
|
38
|
+
if (value === cause) {
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
seen.add(value);
|
|
42
|
+
// A failure wraps its own cause under `cause`, and one that declares none ends the walk.
|
|
43
|
+
value = value instanceof Error && 'cause' in value ? value.cause : noPrimary;
|
|
44
|
+
}
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
21
47
|
/**
|
|
22
48
|
* The caller's own signal, read where it enters. A JavaScript caller reaches the slot with any
|
|
23
49
|
* value, and a value that is not an `AbortSignal` would otherwise escape as a raw TypeError.
|
|
@@ -38,8 +64,10 @@ function checkSignal(signal) {
|
|
|
38
64
|
class ApplicationBuilder {
|
|
39
65
|
#name;
|
|
40
66
|
#root;
|
|
41
|
-
|
|
67
|
+
// The override list the constructor read out of the options slot, unexamined until build.
|
|
68
|
+
#views;
|
|
42
69
|
#plugins;
|
|
70
|
+
#globals;
|
|
43
71
|
// The constructor's raw options argument, kept for the slot's own shape rules.
|
|
44
72
|
// The options-slot rules answer at the same point every other authoring fault does:
|
|
45
73
|
// `inspect()` and `run()`.
|
|
@@ -48,10 +76,11 @@ class ApplicationBuilder {
|
|
|
48
76
|
#declared;
|
|
49
77
|
constructor(name, root, config) {
|
|
50
78
|
this.#declared = config.declared;
|
|
51
|
-
this.#
|
|
79
|
+
this.#views = config.views;
|
|
52
80
|
this.#name = name;
|
|
53
81
|
this.#options = config.options;
|
|
54
82
|
this.#plugins = config.plugins;
|
|
83
|
+
this.#globals = config.globals;
|
|
55
84
|
this.#root = root;
|
|
56
85
|
}
|
|
57
86
|
get name() {
|
|
@@ -73,7 +102,21 @@ class ApplicationBuilder {
|
|
|
73
102
|
};
|
|
74
103
|
return this.derive(declareOption(this.#root, input));
|
|
75
104
|
}
|
|
76
|
-
|
|
105
|
+
globalOption(name, config) {
|
|
106
|
+
const input = {
|
|
107
|
+
config: captureConfig(config),
|
|
108
|
+
kind: 'option',
|
|
109
|
+
name,
|
|
110
|
+
};
|
|
111
|
+
return new ApplicationBuilder(this.#name, recordGlobalOption(this.#root, name), {
|
|
112
|
+
declared: this.#declared,
|
|
113
|
+
globals: declareGlobalOption(this.#globals, input),
|
|
114
|
+
options: this.#options,
|
|
115
|
+
plugins: this.#plugins,
|
|
116
|
+
views: this.#views,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
/** Registering the action closes input authoring; extension configuration remains available. */
|
|
77
120
|
action(handler) {
|
|
78
121
|
return this.derive(declareAction(this.#root, handler));
|
|
79
122
|
}
|
|
@@ -82,37 +125,60 @@ class ApplicationBuilder {
|
|
|
82
125
|
return this.derive(attachChild(this.#root, child));
|
|
83
126
|
}
|
|
84
127
|
/**
|
|
85
|
-
*
|
|
128
|
+
* The value the root action produces for its consumer. The type argument is stated by the
|
|
129
|
+
* author, as it is on a Command.
|
|
130
|
+
*/
|
|
131
|
+
result(declaration) {
|
|
132
|
+
return this.derive(declareResult(this.#root, 'value', declaration));
|
|
133
|
+
}
|
|
134
|
+
/** The same declaration over a sequence, whose type argument is one row. */
|
|
135
|
+
rows(declaration) {
|
|
136
|
+
return this.derive(declareResult(this.#root, 'rows', declaration));
|
|
137
|
+
}
|
|
138
|
+
/** Views after the fact, merged by key, as it is on a Command. */
|
|
139
|
+
views(replacements, options) {
|
|
140
|
+
return this.derive(declareResultViews(this.#root, replacements, options));
|
|
141
|
+
}
|
|
142
|
+
extend(...values) {
|
|
143
|
+
return this.derive(declareExtensions(this.#root, values));
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Root declaration calls preserve the Application configuration. The next state
|
|
86
147
|
* travels through this call: each method names its transition in its return type, and the
|
|
87
148
|
* wrapper publishes the same runtime value in exactly that state.
|
|
88
149
|
*/
|
|
89
150
|
derive(root) {
|
|
90
151
|
return new ApplicationBuilder(this.#name, root, {
|
|
91
152
|
declared: this.#declared,
|
|
92
|
-
|
|
153
|
+
globals: this.#globals,
|
|
93
154
|
options: this.#options,
|
|
94
155
|
plugins: this.#plugins,
|
|
156
|
+
views: this.#views,
|
|
95
157
|
});
|
|
96
158
|
}
|
|
97
159
|
/**
|
|
98
|
-
* Every rule that reads the declarations alone, in the order `run()` reads them: the
|
|
99
|
-
*
|
|
100
|
-
* then the whole Command graph. The
|
|
101
|
-
*
|
|
160
|
+
* Every rule that reads the declarations alone, in the order `run()` reads them: the
|
|
161
|
+
* application's own view overrides, the options slot, the installed list, each plugin's
|
|
162
|
+
* declarations, then the whole Command graph. The application's overrides are read and published
|
|
163
|
+
* first, because they need core's identities and nothing else, so every later declaration error
|
|
164
|
+
* reaches them, while a fault in that list itself reports through core's own text. The merged
|
|
165
|
+
* registry is published once the whole build has succeeded, so a build-time fault never resolves
|
|
166
|
+
* through a plugin's overrides, which build has not yet validated.
|
|
102
167
|
*/
|
|
103
|
-
prepare(
|
|
168
|
+
prepare(stage) {
|
|
169
|
+
const identities = viewIdentities(coreViews);
|
|
170
|
+
const application = buildViews({ declares: false, sentence: 'The Application' }, this.#views, identities);
|
|
171
|
+
stage.views([application]);
|
|
172
|
+
stage.rendering(renderingPolicy(this.#declared.rendering));
|
|
104
173
|
const facts = checkOptions(this.#options, this.#declared);
|
|
105
|
-
const installed = installPlugins(this.#plugins);
|
|
106
|
-
const application = buildFailures(this.#failures);
|
|
107
|
-
register(application);
|
|
174
|
+
const installed = installPlugins(this.#plugins ?? []);
|
|
108
175
|
const install = { descriptors: new Map(), extensions: new Map() };
|
|
109
176
|
const plugins = buildPlugins(installed, install);
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
]));
|
|
114
|
-
const graph = buildGraph(this.#root, { ...install, plugins });
|
|
177
|
+
stage.plugins(plugins);
|
|
178
|
+
const contributors = plugins.map((entry) => buildViews({ declares: true, sentence: pluginSentence(entry.identity) }, entry.views, identities));
|
|
179
|
+
const graph = buildGraph(this.#root, this.#globals, { ...install, plugins });
|
|
115
180
|
checkDeclarations([...graph.globals.inputs, ...collectInputs(graph.root)]);
|
|
181
|
+
stage.views([application, ...contributors]);
|
|
116
182
|
return { facts, graph, plugins };
|
|
117
183
|
}
|
|
118
184
|
/**
|
|
@@ -122,7 +188,11 @@ class ApplicationBuilder {
|
|
|
122
188
|
* Nothing is cached: each call builds the graph anew.
|
|
123
189
|
*/
|
|
124
190
|
inspect() {
|
|
125
|
-
const built = this.prepare(
|
|
191
|
+
const built = this.prepare({
|
|
192
|
+
plugins: () => undefined,
|
|
193
|
+
rendering: () => undefined,
|
|
194
|
+
views: () => undefined,
|
|
195
|
+
});
|
|
126
196
|
return inspectGraph(this.#name, built.graph, built.facts);
|
|
127
197
|
}
|
|
128
198
|
async run(options) {
|
|
@@ -134,6 +204,8 @@ class ApplicationBuilder {
|
|
|
134
204
|
let registry = undefined;
|
|
135
205
|
// Faults a plugin raised beside the primary outcome, reported after it and never before it.
|
|
136
206
|
const faults = [];
|
|
207
|
+
// The failure this run reports as its primary outcome, so nothing reports it a second time.
|
|
208
|
+
let primary = noPrimary;
|
|
137
209
|
// One private controller per run, subscribed to the caller's signal at run entry.
|
|
138
210
|
const controller = new AbortController();
|
|
139
211
|
/**
|
|
@@ -157,10 +229,24 @@ class ApplicationBuilder {
|
|
|
157
229
|
const overrides = options?.host;
|
|
158
230
|
stderr = overrides?.stderr ?? stderr;
|
|
159
231
|
const host = captureHost(overrides, stderr);
|
|
160
|
-
|
|
232
|
+
const invocationOutput = new Output(host, controller.signal);
|
|
233
|
+
output = invocationOutput;
|
|
234
|
+
// The policy is read inside the build, after the application's own overrides are published.
|
|
235
|
+
// A faulty rendering declaration then reports through the view the application listed.
|
|
236
|
+
let policy = {};
|
|
161
237
|
signals = bracketRun(controller, checkSignal(options?.signal));
|
|
162
|
-
const built = this.prepare(
|
|
163
|
-
|
|
238
|
+
const built = this.prepare({
|
|
239
|
+
plugins: (plugins) => {
|
|
240
|
+
invocationOutput.configure(policy, plugins.find((entry) => entry.theme !== undefined)?.theme ?? new Map());
|
|
241
|
+
},
|
|
242
|
+
rendering: (declared) => {
|
|
243
|
+
policy = { ...declared, ...renderingPolicy(options?.rendering) };
|
|
244
|
+
invocationOutput.configure(policy, new Map());
|
|
245
|
+
},
|
|
246
|
+
views: (value) => {
|
|
247
|
+
registry = value;
|
|
248
|
+
invocationOutput.useViews(value);
|
|
249
|
+
},
|
|
164
250
|
});
|
|
165
251
|
const { graph } = built;
|
|
166
252
|
const inputs = { globals: graph.globals.inputs, locals: collectInputs(graph.root) };
|
|
@@ -173,6 +259,7 @@ class ApplicationBuilder {
|
|
|
173
259
|
*/
|
|
174
260
|
signals.install(ownedSignals(built.plugins));
|
|
175
261
|
await runInvocation({
|
|
262
|
+
channel: (binding) => invocationOutput.channel(binding),
|
|
176
263
|
defaults,
|
|
177
264
|
facts: built.facts,
|
|
178
265
|
graph,
|
|
@@ -181,7 +268,11 @@ class ApplicationBuilder {
|
|
|
181
268
|
out: output.out,
|
|
182
269
|
plugins: built.plugins,
|
|
183
270
|
report: (fault) => faults.push(fault),
|
|
271
|
+
route: (path) => {
|
|
272
|
+
invocationOutput.useRoute(path);
|
|
273
|
+
},
|
|
184
274
|
signal: controller.signal,
|
|
275
|
+
style: output.style,
|
|
185
276
|
});
|
|
186
277
|
}
|
|
187
278
|
// The fault check covers the same window the write accounting covers.
|
|
@@ -189,20 +280,21 @@ class ApplicationBuilder {
|
|
|
189
280
|
await output.settle();
|
|
190
281
|
const fault = output.fault;
|
|
191
282
|
if (fault) {
|
|
192
|
-
// The action returned, so the
|
|
283
|
+
// The action returned, so the view failure is this invocation's own failure.
|
|
193
284
|
throw new InternalError(`Rendering output failed: ${reasonOf(fault.cause)}`, fault.cause);
|
|
194
285
|
}
|
|
195
286
|
}
|
|
196
287
|
catch (error) {
|
|
288
|
+
primary = error;
|
|
197
289
|
try {
|
|
198
290
|
const failure = toFailure(error);
|
|
199
291
|
code = failure.exitCode;
|
|
200
|
-
output ??= new Output(
|
|
292
|
+
output ??= new Output(captureHost(undefined, stderr), controller.signal);
|
|
201
293
|
const writes = await output.settle();
|
|
202
294
|
if (writes.kind === 'ok' && !silenced(error, controller.signal, cancellation())) {
|
|
203
|
-
const report = describeFailure(registry ??
|
|
295
|
+
const report = describeFailure(registry ?? noViews, failure, output.context('stderr'));
|
|
204
296
|
if (report.kind === 'rendered') {
|
|
205
|
-
// The
|
|
297
|
+
// The view owns the trailing newline; output resolves its marked text.
|
|
206
298
|
await output.report(report.text);
|
|
207
299
|
}
|
|
208
300
|
else {
|
|
@@ -217,14 +309,24 @@ class ApplicationBuilder {
|
|
|
217
309
|
reportingFailed = true;
|
|
218
310
|
}
|
|
219
311
|
}
|
|
312
|
+
/**
|
|
313
|
+
* A sequence that stopped on its own source reports the same way: the call the action never
|
|
314
|
+
* awaited observed nothing, and a failure the action let propagate is the primary outcome
|
|
315
|
+
* already, so the one it raised is not reported twice.
|
|
316
|
+
*/
|
|
317
|
+
for (const cause of output?.stopped ?? []) {
|
|
318
|
+
if (!carried(primary, cause)) {
|
|
319
|
+
faults.push(toFailure(cause));
|
|
320
|
+
}
|
|
321
|
+
}
|
|
220
322
|
// A plugin's own fault is reported after the primary outcome and turns a would-be 0 into 1.
|
|
221
|
-
// The primary outcome keeps its code, the way a
|
|
222
|
-
// It is reported the way the primary failure is, so
|
|
323
|
+
// The primary outcome keeps its code, the way a view failure leaves it alone.
|
|
324
|
+
// It is reported the way the primary failure is, so an override answers its class.
|
|
223
325
|
for (const fault of faults) {
|
|
224
326
|
if (!silenced(fault, controller.signal, cancellation())) {
|
|
225
327
|
code = code === 0 ? 1 : code;
|
|
226
328
|
try {
|
|
227
|
-
const report = describeFailure(registry ??
|
|
329
|
+
const report = describeFailure(registry ?? noViews, fault, output?.context('stderr'));
|
|
228
330
|
if (report.kind === 'rendered') {
|
|
229
331
|
await output?.report(report.text);
|
|
230
332
|
}
|
|
@@ -251,7 +353,7 @@ class ApplicationBuilder {
|
|
|
251
353
|
}
|
|
252
354
|
/**
|
|
253
355
|
* One rule orders every code: a cancelled run resolves its signal's code, and a broken
|
|
254
|
-
* failure
|
|
356
|
+
* failure view or destination in that run is reported as text without changing it. The
|
|
255
357
|
* signal decides the code whatever the action did afterward, so this reading comes last.
|
|
256
358
|
*/
|
|
257
359
|
code = cancellation() ?? code;
|
|
@@ -263,19 +365,17 @@ class ApplicationBuilder {
|
|
|
263
365
|
}
|
|
264
366
|
}
|
|
265
367
|
}
|
|
266
|
-
/**
|
|
267
|
-
* The second argument, read where it is supplied. The retired positional form declares its globals
|
|
268
|
-
* on a value that holds no `globals` key, so without this rule the globals vanish silently and the
|
|
269
|
-
* operator, not the author, meets the consequence as an unknown-option error. The slot's own shape
|
|
270
|
-
* settles first, because a slot that is not an options object carries no facts to report.
|
|
271
|
-
*/
|
|
368
|
+
/** Reject obsolete wiring before silently losing options that invocations depend on. */
|
|
272
369
|
function checkOptions(options, declared) {
|
|
273
|
-
if (isGlobalOptions(options)) {
|
|
274
|
-
throw new DeclarationError('The Application takes an options object. Supply { globals } instead of a positional GlobalOptions value.');
|
|
275
|
-
}
|
|
276
370
|
if (options !== undefined) {
|
|
277
371
|
if (!isPlainObject(options)) {
|
|
278
|
-
throw new DeclarationError('The Application options must be an object. Supply
|
|
372
|
+
throw new DeclarationError('The Application options must be an object. Supply an Application options object.');
|
|
373
|
+
}
|
|
374
|
+
if ('globals' in options) {
|
|
375
|
+
throw new DeclarationError('The Application options contain globals. Declare them with globalOption(name, config).');
|
|
376
|
+
}
|
|
377
|
+
if ('failures' in options) {
|
|
378
|
+
throw new DeclarationError('The Application options contain failures. Declare view overrides under views with override(key, view).');
|
|
279
379
|
}
|
|
280
380
|
// The root is every page's entry point, so it carries neither listing fact.
|
|
281
381
|
// A key that may not be there is a fault of the slot, so it answers with the slot's shape.
|
|
@@ -286,35 +386,38 @@ function checkOptions(options, declared) {
|
|
|
286
386
|
version: checkVersion(declared.version),
|
|
287
387
|
};
|
|
288
388
|
}
|
|
289
|
-
/**
|
|
290
|
-
* The runtime class behind the public constructor. It is generic so that an instance's `Globals`
|
|
291
|
-
* is the type of the value it holds, with `{}` standing in when there is none, which is what each
|
|
292
|
-
* signature of the constructor interface publishes.
|
|
293
|
-
*/
|
|
389
|
+
/** Constructor inference preserves the installed plugin tuple; globals start empty. */
|
|
294
390
|
class ApplicationDeclaration extends ApplicationBuilder {
|
|
295
391
|
constructor(name, options) {
|
|
296
|
-
// The options slot is read defensively, never inspected
|
|
297
|
-
//
|
|
392
|
+
// The options slot is read defensively, never inspected.
|
|
393
|
+
// An invalid value still yields `views` and the facts of some kind.
|
|
394
|
+
// `checkOptions` reports such a value at build.
|
|
298
395
|
// The root's own slot and core facts stay empty, because the Application checks its own slot.
|
|
299
396
|
// Its diagnostics name the Application rather than the root Command.
|
|
300
397
|
super(name, freshState({
|
|
301
398
|
deprecated: undefined,
|
|
302
399
|
description: undefined,
|
|
303
400
|
extensions: options?.extensions,
|
|
304
|
-
globals: options?.globals,
|
|
305
401
|
hidden: undefined,
|
|
306
402
|
name: null,
|
|
307
403
|
options: undefined,
|
|
308
404
|
}), {
|
|
309
|
-
declared: {
|
|
310
|
-
|
|
405
|
+
declared: {
|
|
406
|
+
description: options?.description,
|
|
407
|
+
rendering: isPlainObject(options?.rendering)
|
|
408
|
+
? { ...options.rendering }
|
|
409
|
+
: options?.rendering,
|
|
410
|
+
version: options?.version,
|
|
411
|
+
},
|
|
412
|
+
globals: emptyGlobals(),
|
|
311
413
|
options,
|
|
312
|
-
plugins: options?.plugins
|
|
414
|
+
plugins: options?.plugins,
|
|
415
|
+
// The read is loose because the slot is reachable from JavaScript with any value at all.
|
|
416
|
+
// An Application value passed here answers `views` with its own authoring method.
|
|
417
|
+
// The public `ApplicationOptions.views` stays exactly `readonly ViewOverride[]`.
|
|
418
|
+
// An options slot that is no plain object carries no override list, and its own rule reports it.
|
|
419
|
+
views: isPlainObject(options) ? options.views : undefined,
|
|
313
420
|
});
|
|
314
421
|
}
|
|
315
422
|
}
|
|
316
|
-
/**
|
|
317
|
-
* The public constructor takes a name and one options object. The globals type narrows to the
|
|
318
|
-
* supplied value, and the failure renderers configure the application the way its commands do.
|
|
319
|
-
*/
|
|
320
423
|
export const Application = ApplicationDeclaration;
|
package/dist/chain.d.ts
CHANGED
|
@@ -2,7 +2,8 @@ import type { BuiltGraph } from './command.js';
|
|
|
2
2
|
import type { LoomError } from './errors.js';
|
|
3
3
|
import type { CommandGraph, CommandNode } from './inspect.js';
|
|
4
4
|
import type { BuiltPlugin, PluginOptions, PluginOptionValues } from './plugin.js';
|
|
5
|
-
import type {
|
|
5
|
+
import type { ContextualStyle } from './style.js';
|
|
6
|
+
import type { ActionChannel, Host, OpenResult, Out, Request, ResultBinding } from './types.js';
|
|
6
7
|
import type { DefaultValues } from './validation.js';
|
|
7
8
|
/**
|
|
8
9
|
* What the rest of one chain did: the action ran, a later middleware took over by returning without
|
|
@@ -12,12 +13,19 @@ type ChainOutcome = 'cancelled' | 'dispatched' | 'taken-over';
|
|
|
12
13
|
/**
|
|
13
14
|
* What one middleware receives. `graph` is the frozen graph `inspect()` returns, built once for the
|
|
14
15
|
* run, and `command` is the routed node inside it. `options` holds this plugin's own option values
|
|
15
|
-
* and never another plugin's or the application's globals.
|
|
16
|
+
* and never another plugin's or the application's globals. `request` is the routed Command's
|
|
17
|
+
* invocation, parsed and validated ahead of the chain, and `null` while core holds a fault and on a
|
|
18
|
+
* group. `view` names the view the result renders through: it reads as the declaration's default
|
|
19
|
+
* until a middleware assigns one, and as `null` on a Command that declares none. The last
|
|
20
|
+
* assignment before the dispatch boundary wins, and one made after it changes nothing.
|
|
16
21
|
*/
|
|
17
22
|
interface MiddlewareContext<Options extends PluginOptions = PluginOptions> {
|
|
18
23
|
readonly options: PluginOptionValues<Options>;
|
|
19
24
|
readonly graph: CommandGraph;
|
|
20
25
|
readonly command: CommandNode;
|
|
26
|
+
readonly request: Request | null;
|
|
27
|
+
get view(): string | null;
|
|
28
|
+
set view(name: string);
|
|
21
29
|
readonly host: Host;
|
|
22
30
|
readonly out: Out;
|
|
23
31
|
readonly signal: AbortSignal;
|
|
@@ -25,6 +33,9 @@ interface MiddlewareContext<Options extends PluginOptions = PluginOptions> {
|
|
|
25
33
|
}
|
|
26
34
|
/** Everything one invocation needs after its graph is built and its defaults are validated. */
|
|
27
35
|
interface Invocation {
|
|
36
|
+
style: ContextualStyle;
|
|
37
|
+
/** The action's own channel, built from the routed Command's declaration when it dispatches. */
|
|
38
|
+
channel: (binding: ResultBinding) => ActionChannel;
|
|
28
39
|
defaults: DefaultValues;
|
|
29
40
|
facts: {
|
|
30
41
|
description: string | undefined;
|
|
@@ -33,16 +44,24 @@ interface Invocation {
|
|
|
33
44
|
graph: BuiltGraph;
|
|
34
45
|
host: Host;
|
|
35
46
|
name: string;
|
|
36
|
-
|
|
47
|
+
/**
|
|
48
|
+
* The invocation's own channel. A middleware reads it as the neutral `Out`, and the action
|
|
49
|
+
* receives the channel the results lane builds for the Command that was routed.
|
|
50
|
+
*/
|
|
51
|
+
out: Out<OpenResult>;
|
|
37
52
|
plugins: readonly BuiltPlugin[];
|
|
38
53
|
/** A fault reported after the primary outcome, which turns a would-be 0 into 1. */
|
|
39
54
|
report: (fault: LoomError) => void;
|
|
55
|
+
/** The routed path, published where routing resolved it, which output names in its own line. */
|
|
56
|
+
route: (path: readonly string[]) => void;
|
|
40
57
|
signal: AbortSignal;
|
|
41
58
|
}
|
|
42
59
|
/**
|
|
43
|
-
* Runs one invocation: the global pre-scan, routing, the
|
|
44
|
-
*
|
|
45
|
-
*
|
|
60
|
+
* Runs one invocation: the global pre-scan, routing, the dispatch this invocation prepares, and the
|
|
61
|
+
* middleware chain it then runs. Local parsing and validation run ahead of the chain so that a
|
|
62
|
+
* middleware reads the request, and the fault they find is held until the dispatch boundary. A
|
|
63
|
+
* middleware that returns without calling `next()` has taken over, so the held fault is never
|
|
64
|
+
* raised and nothing later in the chain runs.
|
|
46
65
|
*/
|
|
47
66
|
declare function runInvocation(invocation: Invocation): Promise<void>;
|
|
48
67
|
export type { ChainOutcome, Invocation, MiddlewareContext };
|