@texuli/spec 0.7.0 → 0.8.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/README.md +64 -10
- package/dist/build.d.ts +3 -2
- package/dist/build.js +7 -3
- package/dist/catalog.d.ts +3 -2
- package/dist/catalog.js +3 -2
- package/dist/codegen/kotlin.js +3 -0
- package/dist/conformance.js +19 -1
- package/dist/contract.d.ts +4 -1
- package/dist/contract.js +9 -0
- package/dist/generated/typescript/blueprint.d.ts +284 -2
- package/dist/generated/typescript/blueprint.js +1 -1
- package/dist/generated/typescript/catalog-schema.d.ts +5 -0
- package/dist/generated/typescript/catalog-schema.js +1 -1
- package/dist/generated/typescript/diagnostics.d.ts +1 -1
- package/dist/generated/typescript/diagnostics.js +1 -1
- package/dist/generated/typescript/manifest.js +1 -1
- package/dist/generated/typescript/vocabulary.d.ts +29 -2
- package/dist/generated/typescript/vocabulary.js +7 -2
- package/dist/generated/typescript/workflow.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/migrations/0.7.0-0.8.0.d.ts +8 -0
- package/dist/migrations/0.7.0-0.8.0.js +11 -0
- package/dist/migrations/index.js +2 -0
- package/dist/source.d.ts +90 -0
- package/dist/source.js +366 -0
- package/dist/validate.d.ts +9 -0
- package/dist/validate.js +47 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -1
package/dist/validate.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { SpecDiagnostic, SpecDiagnosticCode, SpecValidation, SpecWarning }
|
|
|
2
2
|
import type { SpecCatalog } from './catalog.js';
|
|
3
3
|
import { type SpecStateType } from './contract.js';
|
|
4
4
|
import { type Schema } from './dialect.js';
|
|
5
|
+
import { type SpecSourceDescriptor } from './source.js';
|
|
5
6
|
export type { SpecDiagnostic, SpecDiagnosticCode, SpecValidation, SpecWarning };
|
|
6
7
|
/**
|
|
7
8
|
* Checks one workflow graph against the workflow schema:
|
|
@@ -20,6 +21,12 @@ export interface SpecDocuments {
|
|
|
20
21
|
workflowSchema?: Schema;
|
|
21
22
|
/** `validateWorkflowGraph` from `@texuli/spec/workflow`, for hosts that render workflows. */
|
|
22
23
|
validateWorkflowGraph?: WorkflowGraphValidator;
|
|
24
|
+
/**
|
|
25
|
+
* The host's data sources (spec 0.8.0), as `registry.describe()` from
|
|
26
|
+
* `@texuli/core/data-source` returns them. A `$source` binding is checked
|
|
27
|
+
* against them; without them every binding is refused (`source`).
|
|
28
|
+
*/
|
|
29
|
+
sources?: readonly SpecSourceDescriptor[];
|
|
23
30
|
}
|
|
24
31
|
/** The workflow schema and its validator, when the host supplied both. */
|
|
25
32
|
export interface WorkflowSupport {
|
|
@@ -58,6 +65,8 @@ export interface TreeOptions {
|
|
|
58
65
|
warnings?: SpecWarning[];
|
|
59
66
|
/** Checks `workflowGraph` positions; without it they fail closed. */
|
|
60
67
|
workflow?: WorkflowSupport;
|
|
68
|
+
/** The host's data sources; without them `$source` bindings fail closed. */
|
|
69
|
+
sources?: readonly SpecSourceDescriptor[];
|
|
61
70
|
}
|
|
62
71
|
/**
|
|
63
72
|
* Validate a blueprint envelope against the spec alone: the envelope schema,
|
package/dist/validate.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ACTION_FIELDS, contractRoot, DATA_BINDING_KEY, SPEC_ACTION_OPS, SPEC_LIMITS, SPEC_REGEX, SPEC_STATE_TYPES, STATE_BINDING_KEY, STATE_FIELDS, toDialectSchema, } from "./contract.js";
|
|
2
2
|
import { isRecord, validateInstance } from "./dialect.js";
|
|
3
|
+
import { checkSourceBinding, isSourceBinding, SOURCE_BINDING_KEY, } from "./source.js";
|
|
3
4
|
import { checkStateSurface, surfaceInputs } from "./surface.js";
|
|
4
5
|
const MAX_TREE_DEPTH = 64;
|
|
5
6
|
const ACTION_KEYS = new Set(Object.values(ACTION_FIELDS).flatMap((f) => f.allowed));
|
|
@@ -53,6 +54,7 @@ export function validateBlueprint(input, documents, options = {}) {
|
|
|
53
54
|
root: contractRoot(documents.blueprintSchema, workflow?.schema),
|
|
54
55
|
workflow,
|
|
55
56
|
warnings,
|
|
57
|
+
...(documents.sources ? { sources: documents.sources } : {}),
|
|
56
58
|
}));
|
|
57
59
|
}
|
|
58
60
|
return {
|
|
@@ -301,6 +303,7 @@ export function validateTree(tree, catalog, options = {}) {
|
|
|
301
303
|
insideForm,
|
|
302
304
|
out,
|
|
303
305
|
workflow: options.workflow,
|
|
306
|
+
sources: options.sources,
|
|
304
307
|
});
|
|
305
308
|
if (!component) {
|
|
306
309
|
context.fail('unknown-id', `${path}.id`, `"${node.id}" is not in the catalog.`);
|
|
@@ -418,6 +421,10 @@ function checkProps(given, bind, component, path, context) {
|
|
|
418
421
|
fail('extension-prop', here, `"${name}" is a ${prop.extension} extension (${prop.kind}) and cannot come from data.`);
|
|
419
422
|
continue;
|
|
420
423
|
}
|
|
424
|
+
if (isSourceBinding(value)) {
|
|
425
|
+
checkPropSource(value, prop, component, here, context);
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
421
428
|
if (isBindingShaped(value)) {
|
|
422
429
|
checkPropBinding(value, prop, here, context);
|
|
423
430
|
continue;
|
|
@@ -443,6 +450,38 @@ function checkProps(given, bind, component, path, context) {
|
|
|
443
450
|
}
|
|
444
451
|
}
|
|
445
452
|
}
|
|
453
|
+
/**
|
|
454
|
+
* A whole prop read from a host data source (spec 0.8.0): a sourceable prop,
|
|
455
|
+
* the closed `$defs.sourceBinding` shape, then the binding against the
|
|
456
|
+
* host's sources (fail closed without them).
|
|
457
|
+
*/
|
|
458
|
+
function checkPropSource(value, prop, component, path, context) {
|
|
459
|
+
const fail = (code, at, message) => context.fail(code, `${path}${at}`, message);
|
|
460
|
+
if (!(component.sourceable ?? []).includes(prop.name)) {
|
|
461
|
+
fail('source', '', `"${prop.name}" of "${component.id}" cannot read a data source.`);
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
const shape = context.root.$defs?.sourceBinding;
|
|
465
|
+
if (!shape) {
|
|
466
|
+
fail('source', '', 'The blueprint schema has no data-source binding.');
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
const issues = validateInstance(shape, value, context.root);
|
|
470
|
+
const first = issues[0];
|
|
471
|
+
if (first) {
|
|
472
|
+
fail('source', first.path.slice(1), `A data-source binding is ${SHAPE_HINT}: ${first.message}`);
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
const itemSchema = prop.schema?.items;
|
|
476
|
+
const itemFields = itemSchema?.properties ? Object.keys(itemSchema.properties) : undefined;
|
|
477
|
+
for (const issue of checkSourceBinding(value, {
|
|
478
|
+
...(context.sources ? { sources: context.sources } : {}),
|
|
479
|
+
...(itemFields ? { itemFields } : {}),
|
|
480
|
+
})) {
|
|
481
|
+
fail(issue.code, issue.at, issue.message);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
const SHAPE_HINT = `{"${SOURCE_BINDING_KEY}": name, "query": {...}, "map": {...}}`;
|
|
446
485
|
/** Whether `value` is a legal value of `prop` (kind, enum and property schema). */
|
|
447
486
|
function fits(prop, value, root) {
|
|
448
487
|
if (kindProblem(prop, value) !== null)
|
|
@@ -494,6 +533,10 @@ function walkOpaque(value, path, depth, context) {
|
|
|
494
533
|
}
|
|
495
534
|
if (!isRecord(value))
|
|
496
535
|
return;
|
|
536
|
+
if (isSourceBinding(value)) {
|
|
537
|
+
context.fail('source', path, 'A data-source binding reads only a whole top-level prop.');
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
497
540
|
if (isBindingShaped(value)) {
|
|
498
541
|
nestedBinding(value, path, context, undefined);
|
|
499
542
|
return;
|
|
@@ -577,6 +620,10 @@ function checkPropSchemaValue(name, propSchema, value, path, context) {
|
|
|
577
620
|
}
|
|
578
621
|
return schemaIssues;
|
|
579
622
|
}
|
|
623
|
+
if (isSourceBinding(current)) {
|
|
624
|
+
sub.fail('source', at(relative), 'A data-source binding reads only a whole top-level prop.');
|
|
625
|
+
return [];
|
|
626
|
+
}
|
|
580
627
|
if (isBindingShaped(current)) {
|
|
581
628
|
nestedBinding(current, at(relative), sub, node);
|
|
582
629
|
return [];
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** The semantic version of the spec documents in this package. */
|
|
2
|
-
export declare const SPEC_VERSION = "0.
|
|
2
|
+
export declare const SPEC_VERSION = "0.8.0";
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** The semantic version of the spec documents in this package. */
|
|
2
|
-
export const SPEC_VERSION = '0.
|
|
2
|
+
export const SPEC_VERSION = '0.8.0';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@texuli/spec",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "The schema-first blueprint contract: JSON schemas, the catalog vocabulary, deterministic TypeScript and Kotlin projections, and the conformance harness.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"svelte",
|
|
@@ -50,6 +50,10 @@
|
|
|
50
50
|
"./stream": {
|
|
51
51
|
"types": "./dist/stream/index.d.ts",
|
|
52
52
|
"default": "./dist/stream/index.js"
|
|
53
|
+
},
|
|
54
|
+
"./source": {
|
|
55
|
+
"types": "./dist/source.d.ts",
|
|
56
|
+
"default": "./dist/source.js"
|
|
53
57
|
}
|
|
54
58
|
},
|
|
55
59
|
"files": [
|