@loomcli/core 0.5.0 → 0.6.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 +18 -2
- package/dist/application.js +266 -71
- package/dist/bindings.d.ts +15 -10
- package/dist/bindings.js +34 -15
- package/dist/chain.d.ts +15 -6
- package/dist/chain.js +40 -20
- package/dist/command-rules.d.ts +55 -0
- package/dist/command-rules.js +142 -0
- package/dist/command.d.ts +65 -23
- package/dist/command.js +695 -226
- package/dist/controls.d.ts +8 -0
- package/dist/controls.js +23 -0
- package/dist/defect.d.ts +18 -0
- package/dist/defect.js +272 -0
- package/dist/developer.d.ts +24 -0
- package/dist/developer.js +52 -0
- package/dist/diagnostic-text.d.ts +81 -0
- package/dist/diagnostic-text.js +283 -0
- package/dist/diagnostic.d.ts +11 -0
- package/dist/diagnostic.js +70 -0
- package/dist/errors.d.ts +108 -24
- package/dist/errors.js +324 -53
- package/dist/exit-codes.d.ts +45 -0
- package/dist/exit-codes.js +46 -0
- package/dist/extension.d.ts +41 -8
- package/dist/extension.js +142 -57
- package/dist/facts.d.ts +66 -11
- package/dist/facts.js +98 -22
- package/dist/globals.d.ts +29 -21
- package/dist/globals.js +115 -46
- package/dist/hints.d.ts +79 -0
- package/dist/hints.js +247 -0
- package/dist/host.d.ts +13 -0
- package/dist/host.js +43 -1
- package/dist/identity.d.ts +19 -0
- package/dist/identity.js +72 -0
- package/dist/index.d.ts +11 -2
- package/dist/index.js +5 -0
- package/dist/input-rules.d.ts +64 -0
- package/dist/input-rules.js +145 -0
- package/dist/inspect.d.ts +12 -4
- package/dist/inspect.js +88 -28
- package/dist/lanes.js +1 -1
- package/dist/locate.js +4 -4
- package/dist/options.d.ts +23 -2
- package/dist/options.js +135 -44
- package/dist/output.d.ts +9 -2
- package/dist/output.js +18 -2
- package/dist/plain.d.ts +6 -0
- package/dist/plain.js +12 -0
- package/dist/plugin-rules.d.ts +62 -0
- package/dist/plugin-rules.js +155 -0
- package/dist/plugin.d.ts +22 -10
- package/dist/plugin.js +348 -116
- package/dist/prototypes.d.ts +7 -0
- package/dist/prototypes.js +29 -0
- package/dist/rendering.d.ts +6 -1
- package/dist/rendering.js +23 -5
- package/dist/rules.d.ts +51 -0
- package/dist/rules.js +115 -0
- package/dist/sequence.js +6 -1
- package/dist/sources.d.ts +6 -4
- package/dist/sources.js +22 -13
- package/dist/style-wire.js +1 -1
- package/dist/style.js +1 -1
- package/dist/theme.d.ts +4 -0
- package/dist/theme.js +25 -5
- package/dist/thenable.d.ts +15 -0
- package/dist/thenable.js +29 -0
- package/dist/translators.d.ts +69 -0
- package/dist/translators.js +253 -0
- package/dist/types.d.ts +11 -1
- package/dist/validation.d.ts +44 -3
- package/dist/validation.js +156 -57
- package/dist/view.d.ts +48 -14
- package/dist/view.js +155 -77
- package/package.json +1 -1
package/dist/options.js
CHANGED
|
@@ -1,52 +1,123 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { declaredName } from './command-rules.js';
|
|
2
|
+
import { valueCode } from './diagnostic-text.js';
|
|
3
|
+
import { DeclarationError, MissingValueError, quoted, RepeatedOptionError, ShortGroupError, UnexpectedValueError, UnknownOptionError, } from './errors.js';
|
|
4
|
+
import { factFault, flagFault, siteFinding } from './facts.js';
|
|
5
|
+
import { booleanOptionMultiple, optionDeclaredTwice, optionPolarity, optionType, polarityOnString, shortAlias, shortOnlyBothPolarities, shortOnlyWithoutShort, spellingTaken, } from './input-rules.js';
|
|
2
6
|
/** Every parsed value lands in one of these maps; `lists` holds the repeated string options. */
|
|
3
7
|
export function emptyValues() {
|
|
4
8
|
return { booleans: new Map(), lists: new Map(), spellings: new Map(), strings: new Map() };
|
|
5
9
|
}
|
|
6
|
-
|
|
10
|
+
/**
|
|
11
|
+
* The part of one declaration that yields a spelling of the given role, which a spelling fault
|
|
12
|
+
* marks: the declared name for the long form, `short` for the short alias, and the `polarity` that
|
|
13
|
+
* generates a negative form.
|
|
14
|
+
*/
|
|
15
|
+
export function spellingMark(site, role) {
|
|
16
|
+
if (role === 'long') {
|
|
17
|
+
return site.named;
|
|
18
|
+
}
|
|
19
|
+
return `${site.at}.${role === 'short' ? 'short' : 'polarity'}`;
|
|
20
|
+
}
|
|
21
|
+
/** The declared name answers the declared-name rule an argument's name answers. */
|
|
22
|
+
export function checkOptionName(name, site) {
|
|
23
|
+
const findings = [siteFinding(site, site.named)];
|
|
7
24
|
if (typeof name !== 'string') {
|
|
8
|
-
throw new DeclarationError(
|
|
25
|
+
throw new DeclarationError(declaredName, {
|
|
26
|
+
correction: 'Supply a string name.',
|
|
27
|
+
findings,
|
|
28
|
+
sentence: `Option name ${valueCode(name)} is not a string.`,
|
|
29
|
+
});
|
|
9
30
|
}
|
|
10
31
|
if (!name || name.startsWith('-') || /[\s=]/u.test(name)) {
|
|
11
|
-
throw new DeclarationError(
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
32
|
+
throw new DeclarationError(declaredName, {
|
|
33
|
+
correction: 'Use a nonempty name without a leading hyphen, whitespace, or "=".',
|
|
34
|
+
findings,
|
|
35
|
+
sentence: `Option name ${quoted(name)} is invalid.`,
|
|
36
|
+
});
|
|
15
37
|
}
|
|
38
|
+
}
|
|
39
|
+
/** The short alias, and `shortOnly`, which leaves the option that alias alone. */
|
|
40
|
+
function checkShortForms(config, site, subject) {
|
|
16
41
|
if (config.short !== undefined &&
|
|
17
42
|
(typeof config.short !== 'string' || !/^[A-Za-z]$/u.test(config.short))) {
|
|
18
|
-
throw
|
|
43
|
+
throw factFault(shortAlias, site, {
|
|
44
|
+
correction: 'Supply one ASCII letter.',
|
|
45
|
+
fact: 'short',
|
|
46
|
+
sentence: `${subject} declares a short alias that is not one ASCII letter.`,
|
|
47
|
+
});
|
|
19
48
|
}
|
|
20
49
|
if (config.shortOnly !== undefined && typeof config.shortOnly !== 'boolean') {
|
|
21
|
-
throw
|
|
50
|
+
throw flagFault(site, 'shortOnly');
|
|
22
51
|
}
|
|
23
52
|
if (config.shortOnly && config.short === undefined) {
|
|
24
|
-
throw
|
|
53
|
+
throw factFault(shortOnlyWithoutShort, site, {
|
|
54
|
+
correction: 'Add short or remove shortOnly.',
|
|
55
|
+
fact: 'shortOnly',
|
|
56
|
+
sentence: `${subject} declares shortOnly and no short alias.`,
|
|
57
|
+
});
|
|
25
58
|
}
|
|
59
|
+
}
|
|
60
|
+
/** A Boolean option's polarity, which a string option does not declare. */
|
|
61
|
+
function checkPolarity(config, site, subject) {
|
|
62
|
+
if (config.polarity === undefined) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (config.type !== 'boolean') {
|
|
66
|
+
throw factFault(polarityOnString, site, {
|
|
67
|
+
correction: 'Remove polarity or use type "boolean".',
|
|
68
|
+
fact: 'polarity',
|
|
69
|
+
sentence: `${subject} declares polarity but is not Boolean.`,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
if (!['positive', 'both', 'negative'].includes(config.polarity)) {
|
|
73
|
+
throw factFault(optionPolarity, site, {
|
|
74
|
+
correction: 'Use "positive", "both", or "negative".',
|
|
75
|
+
fact: 'polarity',
|
|
76
|
+
sentence: `${subject} has an invalid polarity.`,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
if (config.polarity === 'both' && config.shortOnly) {
|
|
80
|
+
throw factFault(shortOnlyBothPolarities, site, {
|
|
81
|
+
correction: 'Enable long forms or select one polarity.',
|
|
82
|
+
fact: 'shortOnly',
|
|
83
|
+
sentence: `${subject} cannot express both polarities with shortOnly.`,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/** Every rule one option declaration answers alone, before the table meets it. */
|
|
88
|
+
function validateDeclaration({ name, config }, site) {
|
|
89
|
+
checkOptionName(name, site);
|
|
90
|
+
const subject = `Option ${quoted(name)}`;
|
|
91
|
+
if (!['string', 'boolean'].includes(config.type)) {
|
|
92
|
+
throw factFault(optionType, site, {
|
|
93
|
+
correction: 'Use "string" or "boolean".',
|
|
94
|
+
fact: 'type',
|
|
95
|
+
sentence: `${subject} has an invalid type.`,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
checkShortForms(config, site, subject);
|
|
26
99
|
if (config.type === 'boolean' && config.multiple !== undefined) {
|
|
27
|
-
throw
|
|
100
|
+
throw factFault(booleanOptionMultiple, site, {
|
|
101
|
+
correction: 'Remove multiple or declare a string option.',
|
|
102
|
+
fact: 'multiple',
|
|
103
|
+
sentence: `${subject} is a boolean option and declares multiple.`,
|
|
104
|
+
});
|
|
28
105
|
}
|
|
29
106
|
if (config.multiple !== undefined && typeof config.multiple !== 'boolean') {
|
|
30
|
-
throw
|
|
31
|
-
}
|
|
32
|
-
if (config.polarity !== undefined) {
|
|
33
|
-
if (config.type !== 'boolean') {
|
|
34
|
-
throw new DeclarationError(`Option "${name}" declares polarity but is not Boolean. Remove polarity or use type "boolean".`);
|
|
35
|
-
}
|
|
36
|
-
if (!['positive', 'both', 'negative'].includes(config.polarity)) {
|
|
37
|
-
throw new DeclarationError(`Option "${name}" has an invalid polarity. Use "positive", "both", or "negative".`);
|
|
38
|
-
}
|
|
39
|
-
if (config.polarity === 'both' && config.shortOnly) {
|
|
40
|
-
throw new DeclarationError(`Option "${name}" cannot express both polarities with shortOnly. Enable long forms or select one polarity.`);
|
|
41
|
-
}
|
|
107
|
+
throw flagFault(site, 'multiple');
|
|
42
108
|
}
|
|
109
|
+
checkPolarity(config, site, subject);
|
|
43
110
|
}
|
|
44
|
-
function addSpelling(
|
|
45
|
-
const existing =
|
|
111
|
+
function addSpelling(claims, spelling, claim) {
|
|
112
|
+
const existing = claims.get(spelling);
|
|
46
113
|
if (existing) {
|
|
47
|
-
throw new DeclarationError(
|
|
114
|
+
throw new DeclarationError(spellingTaken, {
|
|
115
|
+
correction: 'Change one declaration.',
|
|
116
|
+
findings: [existing, claim].map(({ option, site }) => siteFinding(site, spellingMark(site, option.role))),
|
|
117
|
+
sentence: `Option spelling ${quoted(spelling)} is used by both ${quoted(existing.option.name)} and ${quoted(claim.option.name)}.`,
|
|
118
|
+
});
|
|
48
119
|
}
|
|
49
|
-
|
|
120
|
+
claims.set(spelling, claim);
|
|
50
121
|
}
|
|
51
122
|
/**
|
|
52
123
|
* One Boolean option's value for one invocation: the value the parser consumed, or the value its
|
|
@@ -57,37 +128,50 @@ function addSpelling(spellings, spelling, option) {
|
|
|
57
128
|
export function booleanValue(values, name, config) {
|
|
58
129
|
return values.booleans.get(name) ?? config.polarity === 'negative';
|
|
59
130
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
131
|
+
/**
|
|
132
|
+
* One scope's options compiled into the spelling table the parser reads, with every rule one
|
|
133
|
+
* declaration answers alone and every rule two of them answer together.
|
|
134
|
+
*/
|
|
135
|
+
export function compileOptions(declarations, scope) {
|
|
136
|
+
const claims = new Map();
|
|
137
|
+
const names = new Map();
|
|
138
|
+
for (const declaration of declarations) {
|
|
139
|
+
const { name, config } = declaration;
|
|
140
|
+
const site = scope.siteOf(declaration);
|
|
141
|
+
validateDeclaration({ config, name }, site);
|
|
142
|
+
const first = names.get(name);
|
|
143
|
+
if (first) {
|
|
144
|
+
const earlier = scope.siteOf(first);
|
|
145
|
+
throw new DeclarationError(optionDeclaredTwice, {
|
|
146
|
+
correction: 'Remove or rename the duplicate.',
|
|
147
|
+
findings: [
|
|
148
|
+
siteFinding(earlier, earlier.named, 'the first declaration'),
|
|
149
|
+
siteFinding(site, site.named, 'the second declaration'),
|
|
150
|
+
],
|
|
151
|
+
sentence: `Option ${quoted(name)} is declared more than once on ${scope.subject}.`,
|
|
152
|
+
});
|
|
67
153
|
}
|
|
68
|
-
names.
|
|
154
|
+
names.set(name, declaration);
|
|
69
155
|
const positive = config.type === 'string'
|
|
70
156
|
? { multiple: config.multiple === true, name, type: 'string' }
|
|
71
157
|
: { name, type: 'boolean', value: config.polarity !== 'negative' };
|
|
72
158
|
if (!config.shortOnly) {
|
|
73
159
|
if (config.type === 'string' || config.polarity !== 'negative') {
|
|
74
|
-
addSpelling(
|
|
160
|
+
addSpelling(claims, `--${name}`, { option: { ...positive, role: 'long' }, site });
|
|
75
161
|
}
|
|
76
162
|
if (config.type === 'boolean' &&
|
|
77
163
|
(config.polarity === 'both' || config.polarity === 'negative')) {
|
|
78
|
-
addSpelling(
|
|
79
|
-
name,
|
|
80
|
-
|
|
81
|
-
type: 'boolean',
|
|
82
|
-
value: false,
|
|
164
|
+
addSpelling(claims, `--no-${name}`, {
|
|
165
|
+
option: { name, role: 'negative', type: 'boolean', value: false },
|
|
166
|
+
site,
|
|
83
167
|
});
|
|
84
168
|
}
|
|
85
169
|
}
|
|
86
170
|
if (config.short !== undefined) {
|
|
87
|
-
addSpelling(
|
|
171
|
+
addSpelling(claims, `-${config.short}`, { option: { ...positive, role: 'short' }, site });
|
|
88
172
|
}
|
|
89
173
|
}
|
|
90
|
-
return
|
|
174
|
+
return new Map([...claims].map(([spelling, { option }]) => [spelling, option]));
|
|
91
175
|
}
|
|
92
176
|
/**
|
|
93
177
|
* Whether a token reads as an option: it starts with a hyphen. Routing stops at one, and a
|
|
@@ -199,6 +283,9 @@ function readOption(spellings, input, state) {
|
|
|
199
283
|
/**
|
|
200
284
|
* A hyphen token belongs to the globals when its long spelling or every short letter does. The
|
|
201
285
|
* pre-scan reads the globals alone, so a letter it does not own is only "not a global option".
|
|
286
|
+
* The scan stops at a global value option, because the letters after it may be the value the
|
|
287
|
+
* operator meant to pass: the token then belongs to the globals, and parsing reports the
|
|
288
|
+
* value-position fault that names that option alone.
|
|
202
289
|
*/
|
|
203
290
|
function isGlobalToken(spellings, token) {
|
|
204
291
|
const long = longToken(token);
|
|
@@ -210,8 +297,12 @@ function isGlobalToken(spellings, token) {
|
|
|
210
297
|
let other = '';
|
|
211
298
|
for (let index = 0; index < group.length; index += 1) {
|
|
212
299
|
const letter = group.charAt(index);
|
|
213
|
-
|
|
300
|
+
const option = spellings.get(`-${letter}`);
|
|
301
|
+
if (option) {
|
|
214
302
|
global = global === '' ? letter : global;
|
|
303
|
+
if (option.type === 'string') {
|
|
304
|
+
break;
|
|
305
|
+
}
|
|
215
306
|
}
|
|
216
307
|
else {
|
|
217
308
|
other = other === '' ? letter : other;
|
package/dist/output.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type { RenderingPolicy } from './rendering.js';
|
|
|
4
4
|
import type { Palette } from './style-state.js';
|
|
5
5
|
import type { ActionChannel, Host, OpenResult, Out, ResultBinding, ViewContext } from './types.js';
|
|
6
6
|
import type { ViewRegistry } from './view.js';
|
|
7
|
-
type WriteState = {
|
|
7
|
+
export type WriteState = {
|
|
8
8
|
kind: 'ok';
|
|
9
9
|
} | {
|
|
10
10
|
kind: 'failed';
|
|
@@ -19,6 +19,7 @@ export declare class Output {
|
|
|
19
19
|
private readonly signal;
|
|
20
20
|
private readonly destinations;
|
|
21
21
|
private renderFault;
|
|
22
|
+
private readonly viewFaults;
|
|
22
23
|
private readonly stops;
|
|
23
24
|
private route;
|
|
24
25
|
/**
|
|
@@ -56,7 +57,7 @@ export declare class Output {
|
|
|
56
57
|
private resultFault;
|
|
57
58
|
/** The registry one invocation resolves through, republished as each contributor is read. */
|
|
58
59
|
useViews(registry: ViewRegistry): void;
|
|
59
|
-
/** The
|
|
60
|
+
/** The path routing walked, updated as each name routes, which an incomplete sequence names. */
|
|
60
61
|
useRoute(path: readonly string[]): void;
|
|
61
62
|
/** What this invocation's output raised beside its calls, in the order it was raised. */
|
|
62
63
|
get stopped(): readonly unknown[];
|
|
@@ -112,6 +113,12 @@ export declare class Output {
|
|
|
112
113
|
* awaits the call must not end the process with an unhandled rejection.
|
|
113
114
|
*/
|
|
114
115
|
private renderFailed;
|
|
116
|
+
/**
|
|
117
|
+
* Whether one value is what a view or a message check failed with during this invocation. A
|
|
118
|
+
* broken view is a defect in the code that broke the view contract, so an action or a source that
|
|
119
|
+
* lets its rejection propagate reports it as that defect.
|
|
120
|
+
*/
|
|
121
|
+
raisedByView(value: unknown): boolean;
|
|
115
122
|
/** What a view failed with during this invocation, if one did. */
|
|
116
123
|
get fault(): {
|
|
117
124
|
cause: unknown;
|
package/dist/output.js
CHANGED
|
@@ -145,10 +145,13 @@ export class Output {
|
|
|
145
145
|
signal;
|
|
146
146
|
destinations = new Map();
|
|
147
147
|
renderFault = undefined;
|
|
148
|
+
// Every value a view failed with.
|
|
149
|
+
// A caller that lets one propagate never offers it to a translator.
|
|
150
|
+
viewFaults = new WeakSet();
|
|
148
151
|
// Every fault the output path raised beside its calls, reported after the primary outcome.
|
|
149
152
|
// A source a sequence stopped on and a results-lane fault are both such faults.
|
|
150
153
|
stops = [];
|
|
151
|
-
// The
|
|
154
|
+
// The path routing walked, which an incomplete sequence names, updated as each name routes.
|
|
152
155
|
route = [];
|
|
153
156
|
/**
|
|
154
157
|
* The channel every caller writes through. It is typed with the result left open, because the
|
|
@@ -263,7 +266,7 @@ export class Output {
|
|
|
263
266
|
useViews(registry) {
|
|
264
267
|
this.registry = registry;
|
|
265
268
|
}
|
|
266
|
-
/** The
|
|
269
|
+
/** The path routing walked, updated as each name routes, which an incomplete sequence names. */
|
|
267
270
|
useRoute(path) {
|
|
268
271
|
this.route = path;
|
|
269
272
|
}
|
|
@@ -395,10 +398,23 @@ export class Output {
|
|
|
395
398
|
*/
|
|
396
399
|
renderFailed(cause) {
|
|
397
400
|
this.renderFault ??= { cause };
|
|
401
|
+
if ((typeof cause === 'object' || typeof cause === 'function') && cause !== null) {
|
|
402
|
+
this.viewFaults.add(cause);
|
|
403
|
+
}
|
|
398
404
|
const rejection = Promise.reject(cause);
|
|
399
405
|
void rejection.catch(() => undefined);
|
|
400
406
|
return rejection;
|
|
401
407
|
}
|
|
408
|
+
/**
|
|
409
|
+
* Whether one value is what a view or a message check failed with during this invocation. A
|
|
410
|
+
* broken view is a defect in the code that broke the view contract, so an action or a source that
|
|
411
|
+
* lets its rejection propagate reports it as that defect.
|
|
412
|
+
*/
|
|
413
|
+
raisedByView(value) {
|
|
414
|
+
return ((typeof value === 'object' || typeof value === 'function') &&
|
|
415
|
+
value !== null &&
|
|
416
|
+
this.viewFaults.has(value));
|
|
417
|
+
}
|
|
402
418
|
/** What a view failed with during this invocation, if one did. */
|
|
403
419
|
get fault() {
|
|
404
420
|
return this.renderFault;
|
package/dist/plain.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A structural value core reads as plain data: an object literal, and never a declaration that
|
|
3
|
+
* carries state of its own. The options slots read it to reject a value that is not an options
|
|
4
|
+
* object, and inspection reads it to copy a declared value faithfully.
|
|
5
|
+
*/
|
|
6
|
+
export declare function isPlainObject(value: unknown): value is Record<string, unknown>;
|
package/dist/plain.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A structural value core reads as plain data: an object literal, and never a declaration that
|
|
3
|
+
* carries state of its own. The options slots read it to reject a value that is not an options
|
|
4
|
+
* object, and inspection reads it to copy a declared value faithfully.
|
|
5
|
+
*/
|
|
6
|
+
export function isPlainObject(value) {
|
|
7
|
+
if (value === null || typeof value !== 'object') {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
const prototype = Object.getPrototypeOf(value);
|
|
11
|
+
return prototype === Object.prototype || prototype === null;
|
|
12
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/** A slot that holds a list, such as `plugins` or `commands`, holding a value of another kind. */
|
|
2
|
+
declare const notAList: import("./diagnostic-text.js").DiagnosticRule;
|
|
3
|
+
/**
|
|
4
|
+
* A declaration core reads by its keys, such as a plugin's middleware or a constructor's options,
|
|
5
|
+
* that is not an object.
|
|
6
|
+
*/
|
|
7
|
+
declare const notAnObject: import("./diagnostic-text.js").DiagnosticRule;
|
|
8
|
+
/** A list entry that its factory did not build, such as a hand-made plugin or translation. */
|
|
9
|
+
declare const foreignValue: import("./diagnostic-text.js").DiagnosticRule;
|
|
10
|
+
/** One plugin identity installed twice. */
|
|
11
|
+
declare const pluginInstalledTwice: import("./diagnostic-text.js").DiagnosticRule;
|
|
12
|
+
/** A second plugin claiming the theme, the signals, or the configuration source slot. */
|
|
13
|
+
declare const slotTaken: import("./diagnostic-text.js").DiagnosticRule;
|
|
14
|
+
/** A plugin, extension, or view identity outside the identity grammar. */
|
|
15
|
+
declare const invalidIdentity: import("./diagnostic-text.js").DiagnosticRule;
|
|
16
|
+
/** A validator or a presence rule on a plugin option. */
|
|
17
|
+
declare const pluginOptionRule: import("./diagnostic-text.js").DiagnosticRule;
|
|
18
|
+
/** A middleware activation that is missing, empty, or names an option the plugin lacks. */
|
|
19
|
+
declare const middlewareActivation: import("./diagnostic-text.js").DiagnosticRule;
|
|
20
|
+
/** A loader, a hook, or a translator that core cannot call. */
|
|
21
|
+
declare const notAFunction: import("./diagnostic-text.js").DiagnosticRule;
|
|
22
|
+
/** A claimed signal outside SIGINT and SIGTERM. */
|
|
23
|
+
declare const unknownSignal: import("./diagnostic-text.js").DiagnosticRule;
|
|
24
|
+
/** One signal claimed twice by one plugin. */
|
|
25
|
+
declare const signalClaimedTwice: import("./diagnostic-text.js").DiagnosticRule;
|
|
26
|
+
/** A configuration source binding that is not one of the plugin's option extensions. */
|
|
27
|
+
declare const sourceBinding: import("./diagnostic-text.js").DiagnosticRule;
|
|
28
|
+
/** A plugin option that carries its own plugin's source binding. */
|
|
29
|
+
declare const sourceBoundOwnOption: import("./diagnostic-text.js").DiagnosticRule;
|
|
30
|
+
/** Two distinct objects under one extension or declared-view identity. */
|
|
31
|
+
declare const twoPackageCopies: import("./diagnostic-text.js").DiagnosticRule;
|
|
32
|
+
/** A descriptor with no Standard Schema. */
|
|
33
|
+
declare const extensionWithoutSchema: import("./diagnostic-text.js").DiagnosticRule;
|
|
34
|
+
/** An extension value on a declaration its descriptor does not target. */
|
|
35
|
+
declare const extensionTarget: import("./diagnostic-text.js").DiagnosticRule;
|
|
36
|
+
/** Two values of one extension in one layer. */
|
|
37
|
+
declare const extensionValueTwice: import("./diagnostic-text.js").DiagnosticRule;
|
|
38
|
+
/** An extension value its schema rejects, by an issue or by a throw. */
|
|
39
|
+
declare const invalidExtensionValue: import("./diagnostic-text.js").DiagnosticRule;
|
|
40
|
+
/** An extension schema that answers with a promise. */
|
|
41
|
+
declare const asyncExtensionSchema: import("./diagnostic-text.js").DiagnosticRule;
|
|
42
|
+
/** An extension output that is not plain data. */
|
|
43
|
+
declare const extensionOutput: import("./diagnostic-text.js").DiagnosticRule;
|
|
44
|
+
/** An override whose key is neither a declared view nor a failure class. */
|
|
45
|
+
declare const overrideKey: import("./diagnostic-text.js").DiagnosticRule;
|
|
46
|
+
/** One key overridden twice inside one contributor. */
|
|
47
|
+
declare const overrideTwice: import("./diagnostic-text.js").DiagnosticRule;
|
|
48
|
+
/** A `translate()` key that is not a class, or that is a failure class. */
|
|
49
|
+
declare const translationKey: import("./diagnostic-text.js").DiagnosticRule;
|
|
50
|
+
/** An `onCommandAttach` hook that threw or returned a value that is not the attached Command. */
|
|
51
|
+
declare const brokenAttachHook: import("./diagnostic-text.js").DiagnosticRule;
|
|
52
|
+
/** The retired `globals` or `failures` Application option. */
|
|
53
|
+
declare const retiredApplicationOption: import("./diagnostic-text.js").DiagnosticRule;
|
|
54
|
+
/** A packet that is not an object, or whose build is neither value. */
|
|
55
|
+
declare const invalidPacket: import("./diagnostic-text.js").DiagnosticRule;
|
|
56
|
+
/** A rendering policy that is not an object, or holds a setting outside its closed set. */
|
|
57
|
+
declare const renderingPolicyRule: import("./diagnostic-text.js").DiagnosticRule;
|
|
58
|
+
/** A plugin theme that is not a mapping of names to unapplied concrete style chains. */
|
|
59
|
+
declare const themeMapping: import("./diagnostic-text.js").DiagnosticRule;
|
|
60
|
+
/** A theme name that a built-in style member already holds. */
|
|
61
|
+
declare const themeNameTaken: import("./diagnostic-text.js").DiagnosticRule;
|
|
62
|
+
export { asyncExtensionSchema, brokenAttachHook, extensionOutput, extensionTarget, extensionValueTwice, extensionWithoutSchema, foreignValue, invalidExtensionValue, invalidIdentity, invalidPacket, middlewareActivation, notAFunction, notAList, notAnObject, overrideKey, overrideTwice, pluginInstalledTwice, pluginOptionRule, renderingPolicyRule, retiredApplicationOption, signalClaimedTwice, slotTaken, sourceBinding, sourceBoundOwnOption, themeMapping, themeNameTaken, translationKey, twoPackageCopies, unknownSignal, };
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { registerRule } from './diagnostic-text.js';
|
|
2
|
+
/*
|
|
3
|
+
* Core's rules for the declaration faults of plugins, extensions, views, translators, lifecycle
|
|
4
|
+
* hooks, and the Application options that install them. Each is declared once here and shared by
|
|
5
|
+
* every site that raises it, as a plugin's rules are.
|
|
6
|
+
*/
|
|
7
|
+
/** A slot that holds a list, such as `plugins` or `commands`, holding a value of another kind. */
|
|
8
|
+
const notAList = registerRule('@loomcli/core/not-a-list', {
|
|
9
|
+
explanation: 'Core reads plugins, commands, extensions, views, translators, and signals each as a list, in order. A value of any other kind has no entries to read.',
|
|
10
|
+
headline: 'Not a list',
|
|
11
|
+
});
|
|
12
|
+
/**
|
|
13
|
+
* A declaration core reads by its keys, such as a plugin's middleware or a constructor's options,
|
|
14
|
+
* that is not an object.
|
|
15
|
+
*/
|
|
16
|
+
const notAnObject = registerRule('@loomcli/core/not-an-object', {
|
|
17
|
+
explanation: "Core reads the options of a Command and of the Application, a plugin's definition, its options record, each of its option declarations, its middleware, its source, and the config of an argument or option by their keys. A value of any other kind has no keys to read.",
|
|
18
|
+
headline: 'Not an object',
|
|
19
|
+
});
|
|
20
|
+
/** A list entry that its factory did not build, such as a hand-made plugin or translation. */
|
|
21
|
+
const foreignValue = registerRule('@loomcli/core/foreign-value', {
|
|
22
|
+
explanation: 'Core reads a plugin, an extension, an extension value, a declared view, a view override, and a translation through facts its factory recorded when it built the value. Any other value carries none, even one of the same shape.',
|
|
23
|
+
headline: 'Value not from its factory',
|
|
24
|
+
});
|
|
25
|
+
/** One plugin identity installed twice. */
|
|
26
|
+
const pluginInstalledTwice = registerRule('@loomcli/core/plugin-installed-twice', {
|
|
27
|
+
explanation: 'Core keys each plugin by its identity, and a plugin contributes its options, middleware, hooks, and views once, so a second installation would contribute each of them again.',
|
|
28
|
+
headline: 'Plugin installed twice',
|
|
29
|
+
});
|
|
30
|
+
/** A second plugin claiming the theme, the signals, or the configuration source slot. */
|
|
31
|
+
const slotTaken = registerRule('@loomcli/core/slot-taken', {
|
|
32
|
+
explanation: 'A slot is a position exactly one plugin claims: the theme, the process signals, and the configuration source. A second claim would leave two plugins answering where core asks one.',
|
|
33
|
+
headline: 'Slot already claimed',
|
|
34
|
+
});
|
|
35
|
+
/** A plugin, extension, or view identity outside the identity grammar. */
|
|
36
|
+
const invalidIdentity = registerRule('@loomcli/core/invalid-identity', {
|
|
37
|
+
explanation: 'An identity keys what a plugin, an extension, or a view contributes, names it in every diagnostic, and prefixes the identities of the rules its package declares, so it is a package name as npm spells one, scoped or not, then any subpath segments, each after a / and each of lowercase letters and digits in words joined by single hyphens.',
|
|
38
|
+
headline: 'Invalid identity',
|
|
39
|
+
});
|
|
40
|
+
/** A validator or a presence rule on a plugin option. */
|
|
41
|
+
const pluginOptionRule = registerRule('@loomcli/core/plugin-option-rule', {
|
|
42
|
+
explanation: "A plugin's middleware interprets its own options' values, so a plugin option declares how it parses and nothing more: no validator and no presence rule.",
|
|
43
|
+
headline: 'Rule on a plugin option',
|
|
44
|
+
});
|
|
45
|
+
/** A middleware activation that is missing, empty, or names an option the plugin lacks. */
|
|
46
|
+
const middlewareActivation = registerRule('@loomcli/core/middleware-activation', {
|
|
47
|
+
explanation: "Activation decides when core loads a plugin's middleware: on every run with 'always', or only when an invocation supplies one of the plugin's own options the list names, so a middleware no invocation needs costs it nothing.",
|
|
48
|
+
headline: 'Invalid middleware activation',
|
|
49
|
+
});
|
|
50
|
+
/** A loader, a hook, or a translator that core cannot call. */
|
|
51
|
+
const notAFunction = registerRule('@loomcli/core/not-a-function', {
|
|
52
|
+
explanation: 'Core calls each of these values at a point of its own: load when a run first reaches a middleware or a source, onCommandAttach at graph build, onFailure when a failure renders, and a translator when a foreign throw reaches it. A value core cannot call leaves it nothing to run.',
|
|
53
|
+
headline: 'Not a function',
|
|
54
|
+
});
|
|
55
|
+
/** A claimed signal outside SIGINT and SIGTERM. */
|
|
56
|
+
const unknownSignal = registerRule('@loomcli/core/unknown-signal', {
|
|
57
|
+
explanation: 'Core installs listeners for SIGINT and SIGTERM alone, the two signals that ask a command-line program to stop, so a plugin claims one of those.',
|
|
58
|
+
headline: 'Unknown signal',
|
|
59
|
+
});
|
|
60
|
+
/** One signal claimed twice by one plugin. */
|
|
61
|
+
const signalClaimedTwice = registerRule('@loomcli/core/signal-claimed-twice', {
|
|
62
|
+
explanation: 'Core installs one listener for each signal a plugin claims, and a second listener on one signal would take the forced path on the first signal the run receives.',
|
|
63
|
+
headline: 'Signal claimed twice',
|
|
64
|
+
});
|
|
65
|
+
/** A configuration source binding that is not one of the plugin's option extensions. */
|
|
66
|
+
const sourceBinding = registerRule('@loomcli/core/source-binding', {
|
|
67
|
+
explanation: 'A configuration source answers the options that carry its binding, an extension the plugin lists under extensions that applies to options. Core asks the source about those options without knowing what the binding means.',
|
|
68
|
+
headline: 'Invalid source binding',
|
|
69
|
+
});
|
|
70
|
+
/** A plugin option that carries its own plugin's source binding. */
|
|
71
|
+
const sourceBoundOwnOption = registerRule('@loomcli/core/source-bound-own-option', {
|
|
72
|
+
explanation: "A plugin's own options resolve before its configuration source loads, because the source reads them, so none of them can take a value from that source.",
|
|
73
|
+
headline: 'Source bound to its own option',
|
|
74
|
+
});
|
|
75
|
+
/** Two distinct objects under one extension or declared-view identity. */
|
|
76
|
+
const twoPackageCopies = registerRule('@loomcli/core/two-package-copies', {
|
|
77
|
+
explanation: 'Core keys each extension and each declared view by its identity and compares it by reference. Two distinct objects under one identity mean two copies of the package that defines it are installed, and a value one copy made cannot be read through the other.',
|
|
78
|
+
headline: 'Two copies of one package',
|
|
79
|
+
});
|
|
80
|
+
/** A descriptor with no Standard Schema. */
|
|
81
|
+
const extensionWithoutSchema = registerRule('@loomcli/core/extension-without-schema', {
|
|
82
|
+
explanation: "Core validates each extension value against its descriptor's Standard Schema at the call that carries it, so a descriptor with no schema leaves the value unchecked.",
|
|
83
|
+
headline: 'Extension without a schema',
|
|
84
|
+
});
|
|
85
|
+
/** An extension value on a declaration its descriptor does not target. */
|
|
86
|
+
const extensionTarget = registerRule('@loomcli/core/extension-target', {
|
|
87
|
+
explanation: 'An extension is defined for one target, Commands, options, or arguments, and a typed read takes that kind of node alone, so a value on another kind would never be read.',
|
|
88
|
+
headline: 'Extension on the wrong target',
|
|
89
|
+
});
|
|
90
|
+
/** Two values of one extension in one layer. */
|
|
91
|
+
const extensionValueTwice = registerRule('@loomcli/core/extension-value-twice', {
|
|
92
|
+
explanation: 'One extensions list or one extend() call sets each extension once, collecting or not, so two values of one extension leave it unclear which the author meant.',
|
|
93
|
+
headline: 'Extension value twice',
|
|
94
|
+
});
|
|
95
|
+
/** An extension value its schema rejects, by an issue or by a throw. */
|
|
96
|
+
const invalidExtensionValue = registerRule('@loomcli/core/invalid-extension-value', {
|
|
97
|
+
explanation: "An extension value passes its descriptor's schema at the call that carries it, so every plugin that reads it reads a value the schema accepted.",
|
|
98
|
+
headline: 'Invalid extension value',
|
|
99
|
+
});
|
|
100
|
+
/** An extension schema that answers with a promise. */
|
|
101
|
+
const asyncExtensionSchema = registerRule('@loomcli/core/async-extension-schema', {
|
|
102
|
+
explanation: 'Core validates extension values synchronously while it builds the declaration, so a schema that answers with a promise leaves it no verdict to read.',
|
|
103
|
+
headline: 'Asynchronous extension schema',
|
|
104
|
+
});
|
|
105
|
+
/** An extension output that is not plain data. */
|
|
106
|
+
const extensionOutput = registerRule('@loomcli/core/extension-output', {
|
|
107
|
+
explanation: "Every projection, the manifest included, reads an extension's output as frozen plain data: strings, finite numbers, Booleans, null, arrays, and plain objects.",
|
|
108
|
+
headline: 'Extension output not plain data',
|
|
109
|
+
});
|
|
110
|
+
/** An override whose key is neither a declared view nor a failure class. */
|
|
111
|
+
const overrideKey = registerRule('@loomcli/core/override-key', {
|
|
112
|
+
explanation: 'An override replaces the view of a declared view or of a failure class, so its key is one of them.',
|
|
113
|
+
headline: 'Invalid override key',
|
|
114
|
+
});
|
|
115
|
+
/** One key overridden twice inside one contributor. */
|
|
116
|
+
const overrideTwice = registerRule('@loomcli/core/override-twice', {
|
|
117
|
+
explanation: 'Within one contributor, one key answers to one override, so two leave it unclear which the author meant. The same key overridden by two contributors resolves to the first installed.',
|
|
118
|
+
headline: 'Key overridden twice',
|
|
119
|
+
});
|
|
120
|
+
/** A `translate()` key that is not a class, or that is a failure class. */
|
|
121
|
+
const translationKey = registerRule('@loomcli/core/translation-key', {
|
|
122
|
+
explanation: 'A translation keys on the foreign error class it replaces. Core offers a translator only a thrown value no failure class made, so the key is a class and never a failure class.',
|
|
123
|
+
headline: 'Invalid translation key',
|
|
124
|
+
});
|
|
125
|
+
/** An `onCommandAttach` hook that threw or returned a value that is not the attached Command. */
|
|
126
|
+
const brokenAttachHook = registerRule('@loomcli/core/broken-attach-hook', {
|
|
127
|
+
explanation: "onCommandAttach receives each Command's declaration at graph build and returns it, or a value derived from it, synchronously and without throwing. Core builds the Command the hook returns, so a throw or any other value leaves it nothing to build.",
|
|
128
|
+
headline: 'Broken attach hook',
|
|
129
|
+
});
|
|
130
|
+
/** The retired `globals` or `failures` Application option. */
|
|
131
|
+
const retiredApplicationOption = registerRule('@loomcli/core/retired-application-option', {
|
|
132
|
+
explanation: 'The Application no longer reads globals or failures. A global option is declared with globalOption(), so its type reaches every action, and a failure view is an override under views.',
|
|
133
|
+
headline: 'Retired Application option',
|
|
134
|
+
});
|
|
135
|
+
/** A packet that is not an object, or whose build is neither value. */
|
|
136
|
+
const invalidPacket = registerRule('@loomcli/core/invalid-packet', {
|
|
137
|
+
explanation: 'The packet says whether the application was built for development, which decides whether a defect shows the author its Developer Diagnostic or the operator one generic message. Its build reads development or distributed.',
|
|
138
|
+
headline: 'Invalid packet',
|
|
139
|
+
});
|
|
140
|
+
/** A rendering policy that is not an object, or holds a setting outside its closed set. */
|
|
141
|
+
const renderingPolicyRule = registerRule('@loomcli/core/rendering-policy', {
|
|
142
|
+
explanation: 'The rendering policy decides whether output carries color, modifiers, hyperlinks, and terminal controls. color, modifiers, and hyperlinks each read auto, always, or never, and terminalControls reads strip or preserve.',
|
|
143
|
+
headline: 'Invalid rendering policy',
|
|
144
|
+
});
|
|
145
|
+
/** A plugin theme that is not a mapping of names to unapplied concrete style chains. */
|
|
146
|
+
const themeMapping = registerRule('@loomcli/core/theme-mapping', {
|
|
147
|
+
explanation: "A plugin's theme maps each name to an unapplied chain of concrete styles. A semantic token reads the theme itself, so a chain that holds one has no concrete style to resolve to.",
|
|
148
|
+
headline: 'Invalid theme mapping',
|
|
149
|
+
});
|
|
150
|
+
/** A theme name that a built-in style member already holds. */
|
|
151
|
+
const themeNameTaken = registerRule('@loomcli/core/theme-name-taken', {
|
|
152
|
+
explanation: 'Each theme name becomes a member of the style object beside the built-in members, so a name a built-in already holds would hide it.',
|
|
153
|
+
headline: 'Theme name taken',
|
|
154
|
+
});
|
|
155
|
+
export { asyncExtensionSchema, brokenAttachHook, extensionOutput, extensionTarget, extensionValueTwice, extensionWithoutSchema, foreignValue, invalidExtensionValue, invalidIdentity, invalidPacket, middlewareActivation, notAFunction, notAList, notAnObject, overrideKey, overrideTwice, pluginInstalledTwice, pluginOptionRule, renderingPolicyRule, retiredApplicationOption, signalClaimedTwice, slotTaken, sourceBinding, sourceBoundOwnOption, themeMapping, themeNameTaken, translationKey, twoPackageCopies, unknownSignal, };
|