@north-light/api 0.3.524 → 0.3.526
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.
|
@@ -3,6 +3,8 @@ export interface DeclaredOutputShape {
|
|
|
3
3
|
constraint: string;
|
|
4
4
|
/** Named members of an `object` value. Declaring them closes the object. */
|
|
5
5
|
children?: DeclaredOutputField[];
|
|
6
|
+
/** Shape of every member `children` does not declare. Absent means the object is closed. */
|
|
7
|
+
extra?: DeclaredOutputShape;
|
|
6
8
|
/** Shape of each contained value of an `array` or `map` value. */
|
|
7
9
|
items?: DeclaredOutputShape;
|
|
8
10
|
/** Alternatives of a `union` value. */
|
|
@@ -31,7 +33,7 @@ export declare function validateDeclaredResult(output: DeclaredOutputField[], re
|
|
|
31
33
|
} | null;
|
|
32
34
|
/** Checks one JSON value against one shape, recursively: object members, array
|
|
33
35
|
* items, map values, union variants, enum values and nullability. An object that
|
|
34
|
-
* declares `children` is closed
|
|
35
|
-
* depth
|
|
36
|
+
* declares `children` is closed unless it carries `extra`: an undeclared
|
|
37
|
+
* member is a mismatch at any depth, or must fit `extra`. Returns null, or the first mismatch with its full path from `path`.
|
|
36
38
|
* A declared type that is not recognized is accepted. */
|
|
37
39
|
export declare function validateValue(shape: DeclaredOutputShape, value: unknown, path: string): ValueMismatch | null;
|
|
@@ -19,8 +19,8 @@ export function validateDeclaredResult(output, result) {
|
|
|
19
19
|
}
|
|
20
20
|
/** Checks one JSON value against one shape, recursively: object members, array
|
|
21
21
|
* items, map values, union variants, enum values and nullability. An object that
|
|
22
|
-
* declares `children` is closed
|
|
23
|
-
* depth
|
|
22
|
+
* declares `children` is closed unless it carries `extra`: an undeclared
|
|
23
|
+
* member is a mismatch at any depth, or must fit `extra`. Returns null, or the first mismatch with its full path from `path`.
|
|
24
24
|
* A declared type that is not recognized is accepted. */
|
|
25
25
|
export function validateValue(shape, value, path) {
|
|
26
26
|
const atoms = shape.type.split('|').map((part) => part.trim()).filter(Boolean);
|
|
@@ -135,8 +135,15 @@ function atomMismatch(type, shape, v, path) {
|
|
|
135
135
|
}
|
|
136
136
|
const declared = new Set(shape.children.map((member) => member.name));
|
|
137
137
|
for (const key of Object.keys(v)) {
|
|
138
|
-
if (
|
|
138
|
+
if (declared.has(key))
|
|
139
|
+
continue;
|
|
140
|
+
if (shape.extra === undefined)
|
|
139
141
|
return mismatch(`${path}.${key}`, 'no such member (the object is closed)', v[key]);
|
|
142
|
+
if (v[key] === undefined)
|
|
143
|
+
continue;
|
|
144
|
+
const inner = validateValue(shape.extra, v[key], `${path}.${key}`);
|
|
145
|
+
if (inner !== null)
|
|
146
|
+
return inner;
|
|
140
147
|
}
|
|
141
148
|
return null;
|
|
142
149
|
}
|
|
@@ -44,9 +44,9 @@ const PARAM_KEYS_BY_KIND = {
|
|
|
44
44
|
stdin: new Set(['kind', 'name', 'required', 'constraint']),
|
|
45
45
|
'context-file': new Set(['kind', 'name', 'required', 'constraint', 'shape']),
|
|
46
46
|
};
|
|
47
|
-
const OUTPUT_KEYS = new Set(['name', 'type', 'required', 'constraint', 'children', 'items', 'variants', 'values']);
|
|
47
|
+
const OUTPUT_KEYS = new Set(['name', 'type', 'required', 'constraint', 'children', 'extra', 'items', 'variants', 'values']);
|
|
48
48
|
/** An `items` element shape: an output field without a name or required. */
|
|
49
|
-
const OUTPUT_SHAPE_KEYS = new Set(['type', 'constraint', 'children', 'items', 'variants', 'values']);
|
|
49
|
+
const OUTPUT_SHAPE_KEYS = new Set(['type', 'constraint', 'children', 'extra', 'items', 'variants', 'values']);
|
|
50
50
|
/** UPPER_SNAKE_CASE environment variable name — the only form an ambient default
|
|
51
51
|
* may name, so a typo'd lowercase key fails at authoring time rather than
|
|
52
52
|
* silently never resolving. */
|
|
@@ -378,6 +378,25 @@ function validateOutputShape(f, fp, topLevel, issue) {
|
|
|
378
378
|
return null;
|
|
379
379
|
children = c;
|
|
380
380
|
}
|
|
381
|
+
let extra;
|
|
382
|
+
if (f['extra'] !== undefined) {
|
|
383
|
+
const raw = f['extra'];
|
|
384
|
+
const ep = `${fp}.extra`;
|
|
385
|
+
if (base !== 'object' || f['children'] === undefined) {
|
|
386
|
+
issue('command_node_invalid', `extra is only valid on an object field that declares children`, `type ${type}${f['children'] === undefined ? ' without children' : ''}`, 'type "object" with children (optionally "| null")', 'Remove extra, or declare children; an object without children already accepts any members, and a map takes items.', ep);
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
if (!isRecord(raw)) {
|
|
390
|
+
issue('command_node_invalid', `output extra must be an object`, typeName(raw), 'a { type, constraint, ... } shape', 'Fix the extra shape.', ep);
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
393
|
+
if (!checkKeys(raw, OUTPUT_SHAPE_KEYS, ep, issue))
|
|
394
|
+
return null;
|
|
395
|
+
const shape = validateOutputShape(raw, ep, false, issue);
|
|
396
|
+
if (shape === null)
|
|
397
|
+
return null;
|
|
398
|
+
extra = shape;
|
|
399
|
+
}
|
|
381
400
|
let items;
|
|
382
401
|
if (f['items'] !== undefined) {
|
|
383
402
|
const raw = f['items'];
|
|
@@ -433,6 +452,7 @@ function validateOutputShape(f, fp, topLevel, issue) {
|
|
|
433
452
|
type,
|
|
434
453
|
constraint: f['constraint'],
|
|
435
454
|
...(children !== undefined ? { children } : {}),
|
|
455
|
+
...(extra !== undefined ? { extra } : {}),
|
|
436
456
|
...(items !== undefined ? { items } : {}),
|
|
437
457
|
...(variants !== undefined ? { variants } : {}),
|
|
438
458
|
...(values !== undefined ? { values } : {}),
|
|
@@ -16,6 +16,9 @@ export interface ManifestFieldShape {
|
|
|
16
16
|
constraint: string;
|
|
17
17
|
/** Named members; only when the base type is `object`. */
|
|
18
18
|
children?: ManifestField[];
|
|
19
|
+
/** The shape every member not named in `children` must have; only when the base type is `object` and `children` is present.
|
|
20
|
+
* An object with `children` and no `extra` is closed. */
|
|
21
|
+
extra?: ManifestFieldShape;
|
|
19
22
|
/** Value shape; only when the base type is `array` or `map`. */
|
|
20
23
|
items?: ManifestFieldShape;
|
|
21
24
|
/** Alternatives; required when the base type is `union`. */
|
package/dist/core/help.js
CHANGED
|
@@ -327,11 +327,20 @@ function renderOutputFields(fields, indent, lines) {
|
|
|
327
327
|
renderOutputShapeBody(f, `${indent} `, lines);
|
|
328
328
|
}
|
|
329
329
|
}
|
|
330
|
+
/** An open object's last row: the shape of the members it does not declare, then that shape's own body one indent deeper. */
|
|
331
|
+
function renderExtraRow(shape, indent, lines) {
|
|
332
|
+
const extra = shape.extra;
|
|
333
|
+
if (extra === undefined)
|
|
334
|
+
return;
|
|
335
|
+
lines.push(`${indent}other members: ${outputTypePhrase(extra)}.${outputValuesNote(extra)}${extra.constraint.length > 0 ? ` ${extra.constraint}` : ''}`);
|
|
336
|
+
renderOutputShapeBody(extra, `${indent} `, lines);
|
|
337
|
+
}
|
|
330
338
|
/** The nested part of a shape: object children, or an array element's shape. */
|
|
331
339
|
function renderOutputShapeBody(shape, indent, lines) {
|
|
332
340
|
if (shape.children !== undefined && shape.children.length > 0) {
|
|
333
341
|
renderOutputFields(shape.children, indent, lines);
|
|
334
342
|
}
|
|
343
|
+
renderExtraRow(shape, indent, lines);
|
|
335
344
|
for (const variant of shape.variants ?? []) {
|
|
336
345
|
const note = outputValuesNote(variant);
|
|
337
346
|
lines.push(`${indent}variant: ${outputTypePhrase(variant)}.${note}${variant.constraint.length > 0 ? ` ${variant.constraint}` : ''}`);
|
|
@@ -350,6 +359,11 @@ function renderOutputShapeBody(shape, indent, lines) {
|
|
|
350
359
|
}
|
|
351
360
|
if (item.children !== undefined && item.children.length > 0) {
|
|
352
361
|
renderOutputFields(item.children, indent, lines);
|
|
362
|
+
renderExtraRow(item, indent, lines);
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
if (item.extra !== undefined) {
|
|
366
|
+
renderExtraRow(item, indent, lines);
|
|
353
367
|
return;
|
|
354
368
|
}
|
|
355
369
|
eachWord = outputBaseType(item.type) === 'map' ? 'each value' : 'each element';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@north-light/api",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.526",
|
|
4
4
|
"description": "Typed crtrd /v1 API contract — DTOs, route builders, the error contract, the CrtrClient, and the command-plugin manifest format. Zero runtime dependencies.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/api/index.js",
|