@metaobjectsdev/metadata 0.6.0 → 0.7.0-rc.10
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 +54 -3
- package/dist/attr-schema-validate.js +7 -7
- package/dist/attr-schema-validate.js.map +1 -1
- package/dist/core/export-json.d.ts +6 -7
- package/dist/core/export-json.d.ts.map +1 -1
- package/dist/core/export-json.js +15 -17
- package/dist/core/export-json.js.map +1 -1
- package/dist/core/index.d.ts +4 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +6 -2
- package/dist/core/index.js.map +1 -1
- package/dist/core/parser-yaml.d.ts.map +1 -1
- package/dist/core/parser-yaml.js +36 -11
- package/dist/core/parser-yaml.js.map +1 -1
- package/dist/core/yaml-desugar.d.ts.map +1 -1
- package/dist/core/yaml-desugar.js +54 -5
- package/dist/core/yaml-desugar.js.map +1 -1
- package/dist/core/yaml-positions-walker.d.ts +21 -0
- package/dist/core/yaml-positions-walker.d.ts.map +1 -0
- package/dist/core/yaml-positions-walker.js +75 -0
- package/dist/core/yaml-positions-walker.js.map +1 -0
- package/dist/core/yaml-positions.d.ts +19 -0
- package/dist/core/yaml-positions.d.ts.map +1 -0
- package/dist/core/yaml-positions.js +60 -0
- package/dist/core/yaml-positions.js.map +1 -0
- package/dist/core-types.d.ts.map +1 -1
- package/dist/core-types.js +7 -4
- package/dist/core-types.js.map +1 -1
- package/dist/errors.d.ts +32 -9
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +44 -5
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -2
- package/dist/index.js.map +1 -1
- package/dist/json-path.d.ts +8 -0
- package/dist/json-path.d.ts.map +1 -0
- package/dist/json-path.js +39 -0
- package/dist/json-path.js.map +1 -0
- package/dist/loader/meta-data-loader.d.ts +47 -6
- package/dist/loader/meta-data-loader.d.ts.map +1 -1
- package/dist/loader/meta-data-loader.js +126 -8
- package/dist/loader/meta-data-loader.js.map +1 -1
- package/dist/loader/meta-data-source.d.ts +6 -2
- package/dist/loader/meta-data-source.d.ts.map +1 -1
- package/dist/loader/meta-data-source.js +10 -6
- package/dist/loader/meta-data-source.js.map +1 -1
- package/dist/loader/shortcuts.d.ts +9 -0
- package/dist/loader/shortcuts.d.ts.map +1 -0
- package/dist/loader/shortcuts.js +19 -0
- package/dist/loader/shortcuts.js.map +1 -0
- package/dist/loader/sources/directory-source.d.ts +15 -0
- package/dist/loader/sources/directory-source.d.ts.map +1 -0
- package/dist/loader/sources/directory-source.js +80 -0
- package/dist/loader/sources/directory-source.js.map +1 -0
- package/dist/loader/sources/file-source.d.ts +12 -0
- package/dist/loader/sources/file-source.d.ts.map +1 -0
- package/dist/loader/sources/file-source.js +46 -0
- package/dist/loader/sources/file-source.js.map +1 -0
- package/dist/loader/sources/index.d.ts +5 -0
- package/dist/loader/sources/index.d.ts.map +1 -0
- package/dist/loader/sources/index.js +5 -0
- package/dist/loader/sources/index.js.map +1 -0
- package/dist/loader/sources/uri-source.d.ts +9 -0
- package/dist/loader/sources/uri-source.d.ts.map +1 -0
- package/dist/loader/sources/uri-source.js +42 -0
- package/dist/loader/sources/uri-source.js.map +1 -0
- package/dist/loader/validation-passes.d.ts.map +1 -1
- package/dist/loader/validation-passes.js +92 -28
- package/dist/loader/validation-passes.js.map +1 -1
- package/dist/naming.d.ts +15 -2
- package/dist/naming.d.ts.map +1 -1
- package/dist/naming.js +20 -6
- package/dist/naming.js.map +1 -1
- package/dist/parser-core.d.ts +17 -4
- package/dist/parser-core.d.ts.map +1 -1
- package/dist/parser-core.js +371 -44
- package/dist/parser-core.js.map +1 -1
- package/dist/parser-json.d.ts.map +1 -1
- package/dist/parser-json.js +10 -2
- package/dist/parser-json.js.map +1 -1
- package/dist/persistence/source/validate-source-roles.js +2 -2
- package/dist/persistence/source/validate-source-roles.js.map +1 -1
- package/dist/semantic-diff.d.ts +5 -0
- package/dist/semantic-diff.d.ts.map +1 -0
- package/dist/semantic-diff.js +49 -0
- package/dist/semantic-diff.js.map +1 -0
- package/dist/shared/meta-data.d.ts +10 -0
- package/dist/shared/meta-data.d.ts.map +1 -1
- package/dist/shared/meta-data.js +23 -0
- package/dist/shared/meta-data.js.map +1 -1
- package/dist/source.d.ts +96 -0
- package/dist/source.d.ts.map +1 -0
- package/dist/source.js +38 -0
- package/dist/source.js.map +1 -0
- package/dist/subtype-rules.js +1 -1
- package/dist/subtype-rules.js.map +1 -1
- package/dist/super-resolve.d.ts +2 -0
- package/dist/super-resolve.d.ts.map +1 -1
- package/dist/super-resolve.js +1 -1
- package/dist/super-resolve.js.map +1 -1
- package/dist/template/template-constants.d.ts +3 -1
- package/dist/template/template-constants.d.ts.map +1 -1
- package/dist/template/template-constants.js +22 -6
- package/dist/template/template-constants.js.map +1 -1
- package/dist/template/template-schema.d.ts.map +1 -1
- package/dist/template/template-schema.js +41 -1
- package/dist/template/template-schema.js.map +1 -1
- package/package.json +1 -1
- package/src/attr-schema-validate.ts +7 -7
- package/src/core/export-json.ts +15 -18
- package/src/core/index.ts +8 -2
- package/src/core/parser-yaml.ts +38 -11
- package/src/core/yaml-desugar.ts +58 -4
- package/src/core/yaml-positions-walker.ts +101 -0
- package/src/core/yaml-positions.ts +80 -0
- package/src/core-types.ts +7 -4
- package/src/errors.ts +57 -8
- package/src/index.ts +28 -3
- package/src/json-path.ts +46 -0
- package/src/loader/meta-data-loader.ts +168 -10
- package/src/loader/meta-data-source.ts +10 -6
- package/src/loader/shortcuts.ts +31 -0
- package/src/loader/sources/directory-source.ts +90 -0
- package/src/{core → loader/sources}/file-source.ts +3 -3
- package/src/loader/sources/index.ts +6 -0
- package/src/loader/sources/uri-source.ts +44 -0
- package/src/loader/validation-passes.ts +96 -29
- package/src/naming.ts +39 -7
- package/src/parser-core.ts +412 -46
- package/src/parser-json.ts +11 -2
- package/src/persistence/source/validate-source-roles.ts +2 -2
- package/src/semantic-diff.ts +48 -0
- package/src/shared/meta-data.ts +28 -0
- package/src/source.ts +99 -0
- package/src/subtype-rules.ts +1 -1
- package/src/super-resolve.ts +3 -1
- package/src/template/template-constants.ts +23 -6
- package/src/template/template-schema.ts +43 -0
- package/src/core/file-meta-data-loader.ts +0 -89
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"template-constants.d.ts","sourceRoot":"","sources":["../../src/template/template-constants.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"template-constants.d.ts","sourceRoot":"","sources":["../../src/template/template-constants.ts"],"names":[],"mappings":"AAkBA,eAAO,MAAM,uBAAuB,WAAW,CAAC;AAChD,eAAO,MAAM,uBAAuB,WAAW,CAAC;AAChD,eAAO,MAAM,yBAAyB,aAAa,CAAC;AAEpD,eAAO,MAAM,iBAAiB,mDAKpB,CAAC;AACX,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAIjE,eAAO,MAAM,yBAAyB,eAAe,CAAC;AACtD,eAAO,MAAM,sBAAsB,YAAY,CAAC;AAChD,eAAO,MAAM,oBAAoB,WAAW,CAAC;AAC7C,eAAO,MAAM,uBAAuB,aAAa,CAAC;AAClD,eAAO,MAAM,mBAAmB,UAAU,CAAC;AAC3C,eAAO,MAAM,mBAAmB,UAAU,CAAC;AAI3C,eAAO,MAAM,2BAA2B,iBAAiB,CAAC;AAG1D,eAAO,MAAM,wBAAwB,cAAc,CAAC;AACpD,eAAO,MAAM,4BAA4B,kBAAkB,CAAC;AAC5D,eAAO,MAAM,mBAAmB,UAAU,CAAC;AAU3C,eAAO,MAAM,uBAAuB,aAAa,CAAC;AAIlD,eAAO,MAAM,gBAAgB,4EAQnB,CAAC;AACX,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC"}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
// template.* subtype vocabulary + reserved attribute names (FR-004, R1).
|
|
1
|
+
// template.* subtype vocabulary + reserved attribute names (FR-004, R1; ADR-0011).
|
|
2
2
|
//
|
|
3
|
-
// `template` is the fourth-pillar base type
|
|
4
|
-
// a
|
|
5
|
-
//
|
|
6
|
-
//
|
|
3
|
+
// `template` is the fourth-pillar base type — a typed payload bound to either
|
|
4
|
+
// a rendered text artifact (prompt/output) or to a tool-call envelope.
|
|
5
|
+
// Three subtypes:
|
|
6
|
+
// - prompt: LLM-targeted renderable text. Carries the prompt-overlay attrs.
|
|
7
|
+
// Renderable body required via @textRef.
|
|
7
8
|
// - output: every other rendered artifact (email, export, docs, config).
|
|
9
|
+
// Renderable body required via @textRef.
|
|
10
|
+
// - toolcall: LLM tool-call envelope (no renderable body — the body IS the
|
|
11
|
+
// structured output schema resolved via @payloadRef). Per ADR-0011.
|
|
8
12
|
//
|
|
9
13
|
// Format is the @format ATTRIBUTE (closed set below), never a subtype — the
|
|
10
14
|
// render engine keys its escaper off @format, so a new format costs one escaper
|
|
@@ -12,12 +16,15 @@
|
|
|
12
16
|
import { SUBTYPE_BASE } from "../shared/base-types.js";
|
|
13
17
|
export const TEMPLATE_SUBTYPE_PROMPT = "prompt";
|
|
14
18
|
export const TEMPLATE_SUBTYPE_OUTPUT = "output";
|
|
19
|
+
export const TEMPLATE_SUBTYPE_TOOLCALL = "toolcall";
|
|
15
20
|
export const TEMPLATE_SUBTYPES = [
|
|
16
21
|
SUBTYPE_BASE,
|
|
17
22
|
TEMPLATE_SUBTYPE_PROMPT,
|
|
18
23
|
TEMPLATE_SUBTYPE_OUTPUT,
|
|
24
|
+
TEMPLATE_SUBTYPE_TOOLCALL,
|
|
19
25
|
];
|
|
20
|
-
// Generic reserved attrs (
|
|
26
|
+
// Generic reserved attrs (prompt + output). The "@" is applied at wire time.
|
|
27
|
+
// NOT inherited by toolcall — toolcall has no renderable body.
|
|
21
28
|
export const TEMPLATE_ATTR_PAYLOAD_REF = "payloadRef";
|
|
22
29
|
export const TEMPLATE_ATTR_TEXT_REF = "textRef";
|
|
23
30
|
export const TEMPLATE_ATTR_FORMAT = "format";
|
|
@@ -32,6 +39,15 @@ export const TEMPLATE_ATTR_REQUIRED_TAGS = "requiredTags";
|
|
|
32
39
|
export const TEMPLATE_ATTR_MAX_TOKENS = "maxTokens";
|
|
33
40
|
export const TEMPLATE_ATTR_REQUIRED_SLOTS = "requiredSlots";
|
|
34
41
|
export const TEMPLATE_ATTR_MODEL = "model";
|
|
42
|
+
// Toolcall-specific attrs (template.toolcall only). Vendor-agnostic; vendor
|
|
43
|
+
// wire details (retry semantics, fallback shapes, etc.) are added by consumer
|
|
44
|
+
// providers via registry.extend per ADR-0011.
|
|
45
|
+
//
|
|
46
|
+
// @description is intentionally NOT a toolcall-specific constant — every type
|
|
47
|
+
// gets @description via the documentation common-attrs provider. Tool
|
|
48
|
+
// descriptions surfaced to the LLM use the same @description common attr
|
|
49
|
+
// doc-gen uses.
|
|
50
|
+
export const TEMPLATE_ATTR_TOOL_NAME = "toolName";
|
|
35
51
|
// Closed format set — escaping/whitespace behavior is keyed off this in the
|
|
36
52
|
// render engine's escaper registry (FR-004 R7).
|
|
37
53
|
export const TEMPLATE_FORMATS = [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"template-constants.js","sourceRoot":"","sources":["../../src/template/template-constants.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"template-constants.js","sourceRoot":"","sources":["../../src/template/template-constants.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,EAAE;AACF,8EAA8E;AAC9E,uEAAuE;AACvE,kBAAkB;AAClB,8EAA8E;AAC9E,qDAAqD;AACrD,2EAA2E;AAC3E,qDAAqD;AACrD,6EAA6E;AAC7E,kFAAkF;AAClF,EAAE;AACF,4EAA4E;AAC5E,gFAAgF;AAChF,6DAA6D;AAE7D,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAEvD,MAAM,CAAC,MAAM,uBAAuB,GAAG,QAAQ,CAAC;AAChD,MAAM,CAAC,MAAM,uBAAuB,GAAG,QAAQ,CAAC;AAChD,MAAM,CAAC,MAAM,yBAAyB,GAAG,UAAU,CAAC;AAEpD,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,YAAY;IACZ,uBAAuB;IACvB,uBAAuB;IACvB,yBAAyB;CACjB,CAAC;AAGX,6EAA6E;AAC7E,+DAA+D;AAC/D,MAAM,CAAC,MAAM,yBAAyB,GAAG,YAAY,CAAC;AACtD,MAAM,CAAC,MAAM,sBAAsB,GAAG,SAAS,CAAC;AAChD,MAAM,CAAC,MAAM,oBAAoB,GAAG,QAAQ,CAAC;AAC7C,MAAM,CAAC,MAAM,uBAAuB,GAAG,UAAU,CAAC;AAClD,MAAM,CAAC,MAAM,mBAAmB,GAAG,OAAO,CAAC;AAC3C,MAAM,CAAC,MAAM,mBAAmB,GAAG,OAAO,CAAC;AAC3C,mFAAmF;AACnF,+EAA+E;AAC/E,uDAAuD;AACvD,MAAM,CAAC,MAAM,2BAA2B,GAAG,cAAc,CAAC;AAE1D,+CAA+C;AAC/C,MAAM,CAAC,MAAM,wBAAwB,GAAG,WAAW,CAAC;AACpD,MAAM,CAAC,MAAM,4BAA4B,GAAG,eAAe,CAAC;AAC5D,MAAM,CAAC,MAAM,mBAAmB,GAAG,OAAO,CAAC;AAE3C,4EAA4E;AAC5E,8EAA8E;AAC9E,8CAA8C;AAC9C,EAAE;AACF,8EAA8E;AAC9E,sEAAsE;AACtE,yEAAyE;AACzE,gBAAgB;AAChB,MAAM,CAAC,MAAM,uBAAuB,GAAG,UAAU,CAAC;AAElD,4EAA4E;AAC5E,gDAAgD;AAChD,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,MAAM;IACN,MAAM;IACN,KAAK;IACL,KAAK;IACL,MAAM;IACN,UAAU;IACV,aAAa;CACL,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"template-schema.d.ts","sourceRoot":"","sources":["../../src/template/template-schema.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"template-schema.d.ts","sourceRoot":"","sources":["../../src/template/template-schema.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAuIjD,eAAO,MAAM,kBAAkB,2BAK7B,CAAC"}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// `verify` scope (Plan #3), not load-time validation.
|
|
8
8
|
import { ATTR_SUBTYPE_STRING, ATTR_SUBTYPE_INT, ATTR_SUBTYPE_STRINGARRAY, } from "../core/attr/attr-constants.js";
|
|
9
9
|
import { SUBTYPE_BASE } from "../shared/base-types.js";
|
|
10
|
-
import { TEMPLATE_SUBTYPE_PROMPT, TEMPLATE_SUBTYPE_OUTPUT, TEMPLATE_ATTR_PAYLOAD_REF, TEMPLATE_ATTR_TEXT_REF, TEMPLATE_ATTR_FORMAT, TEMPLATE_ATTR_MAX_CHARS, TEMPLATE_ATTR_OWNER, TEMPLATE_ATTR_SINCE, TEMPLATE_ATTR_REQUIRED_TAGS, TEMPLATE_ATTR_MAX_TOKENS, TEMPLATE_ATTR_REQUIRED_SLOTS, TEMPLATE_ATTR_MODEL, TEMPLATE_FORMATS, } from "./template-constants.js";
|
|
10
|
+
import { TEMPLATE_SUBTYPE_PROMPT, TEMPLATE_SUBTYPE_OUTPUT, TEMPLATE_SUBTYPE_TOOLCALL, TEMPLATE_ATTR_PAYLOAD_REF, TEMPLATE_ATTR_TEXT_REF, TEMPLATE_ATTR_FORMAT, TEMPLATE_ATTR_MAX_CHARS, TEMPLATE_ATTR_OWNER, TEMPLATE_ATTR_SINCE, TEMPLATE_ATTR_REQUIRED_TAGS, TEMPLATE_ATTR_MAX_TOKENS, TEMPLATE_ATTR_REQUIRED_SLOTS, TEMPLATE_ATTR_MODEL, TEMPLATE_ATTR_TOOL_NAME, TEMPLATE_FORMATS, } from "./template-constants.js";
|
|
11
11
|
// Generic attrs shared by template.prompt and template.output.
|
|
12
12
|
const genericAttrs = [
|
|
13
13
|
{
|
|
@@ -76,9 +76,49 @@ const promptOverlayAttrs = [
|
|
|
76
76
|
description: "Target model id (LLM-specific).",
|
|
77
77
|
},
|
|
78
78
|
];
|
|
79
|
+
// Toolcall attrs (template.toolcall only — does NOT inherit genericAttrs).
|
|
80
|
+
// Per ADR-0011: vendor-agnostic in core; vendor wire details (retry semantics,
|
|
81
|
+
// fallback shapes, parallel invocation, cache hints) added by consumer
|
|
82
|
+
// providers via registry.extend(TYPE_TEMPLATE, "toolcall", { attributes: [...] }).
|
|
83
|
+
//
|
|
84
|
+
// Critical: @textRef is intentionally NOT required here. A tool-call has no
|
|
85
|
+
// renderable text body — the body IS the structured output schema resolved
|
|
86
|
+
// via @payloadRef. This is the design rationale for toolcall being its own
|
|
87
|
+
// subtype rather than template.output + @toolName.
|
|
88
|
+
//
|
|
89
|
+
// @description is intentionally NOT declared here — it's already a documentation
|
|
90
|
+
// common attr added to every type by docProvider. Tool descriptions surfaced to
|
|
91
|
+
// the LLM read the same @description common attr that doc-gen uses.
|
|
92
|
+
const toolcallAttrs = [
|
|
93
|
+
{
|
|
94
|
+
name: TEMPLATE_ATTR_TOOL_NAME,
|
|
95
|
+
valueType: ATTR_SUBTYPE_STRING,
|
|
96
|
+
required: true,
|
|
97
|
+
description: "Wire tool name surfaced to the LLM (vendor-specific format).",
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
name: TEMPLATE_ATTR_PAYLOAD_REF,
|
|
101
|
+
valueType: ATTR_SUBTYPE_STRING,
|
|
102
|
+
required: true,
|
|
103
|
+
description: "Output value-object the tool produces (resolved against the metamodel).",
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
name: TEMPLATE_ATTR_OWNER,
|
|
107
|
+
valueType: ATTR_SUBTYPE_STRING,
|
|
108
|
+
required: false,
|
|
109
|
+
description: "Governance: the owner of this toolcall.",
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: TEMPLATE_ATTR_SINCE,
|
|
113
|
+
valueType: ATTR_SUBTYPE_STRING,
|
|
114
|
+
required: false,
|
|
115
|
+
description: "Governance: the version this toolcall was introduced in.",
|
|
116
|
+
},
|
|
117
|
+
];
|
|
79
118
|
export const TEMPLATE_ATTRS_MAP = new Map([
|
|
80
119
|
[SUBTYPE_BASE, []],
|
|
81
120
|
[TEMPLATE_SUBTYPE_PROMPT, [...genericAttrs, ...promptOverlayAttrs]],
|
|
82
121
|
[TEMPLATE_SUBTYPE_OUTPUT, [...genericAttrs]],
|
|
122
|
+
[TEMPLATE_SUBTYPE_TOOLCALL, [...toolcallAttrs]],
|
|
83
123
|
]);
|
|
84
124
|
//# sourceMappingURL=template-schema.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"template-schema.js","sourceRoot":"","sources":["../../src/template/template-schema.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,EAAE;AACF,kFAAkF;AAClF,4EAA4E;AAC5E,8EAA8E;AAC9E,gFAAgF;AAChF,sDAAsD;AAGtD,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,yBAAyB,EACzB,sBAAsB,EACtB,oBAAoB,EACpB,uBAAuB,EACvB,mBAAmB,EACnB,mBAAmB,EACnB,2BAA2B,EAC3B,wBAAwB,EACxB,4BAA4B,EAC5B,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,yBAAyB,CAAC;AAEjC,+DAA+D;AAC/D,MAAM,YAAY,GAAiB;IACjC;QACE,IAAI,EAAE,yBAAyB;QAC/B,SAAS,EAAE,mBAAmB;QAC9B,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,sFAAsF;KACpG;IACD;QACE,IAAI,EAAE,sBAAsB;QAC5B,SAAS,EAAE,mBAAmB;QAC9B,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,mGAAmG;KACjH;IACD;QACE,IAAI,EAAE,oBAAoB;QAC1B,SAAS,EAAE,mBAAmB;QAC9B,QAAQ,EAAE,KAAK;QACf,OAAO,EAAE,MAAM;QACf,aAAa,EAAE,CAAC,GAAG,gBAAgB,CAAC;QACpC,WAAW,EAAE,yEAAyE;KACvF;IACD;QACE,IAAI,EAAE,uBAAuB;QAC7B,SAAS,EAAE,gBAAgB;QAC3B,QAAQ,EAAE,KAAK;QACf,WAAW,EAAE,qDAAqD;KACnE;IACD;QACE,IAAI,EAAE,mBAAmB;QACzB,SAAS,EAAE,mBAAmB;QAC9B,QAAQ,EAAE,KAAK;QACf,WAAW,EAAE,yCAAyC;KACvD;IACD;QACE,IAAI,EAAE,mBAAmB;QACzB,SAAS,EAAE,mBAAmB;QAC9B,QAAQ,EAAE,KAAK;QACf,WAAW,EAAE,0DAA0D;KACxE;IACD;QACE,IAAI,EAAE,2BAA2B;QACjC,SAAS,EAAE,wBAAwB;QACnC,QAAQ,EAAE,KAAK;QACf,WAAW,EAAE,kFAAkF;KAChG;CACF,CAAC;AAEF,4CAA4C;AAC5C,MAAM,kBAAkB,GAAiB;IACvC;QACE,IAAI,EAAE,wBAAwB;QAC9B,SAAS,EAAE,gBAAgB;QAC3B,QAAQ,EAAE,KAAK;QACf,WAAW,EAAE,sDAAsD;KACpE;IACD;QACE,IAAI,EAAE,4BAA4B;QAClC,SAAS,EAAE,wBAAwB;QACnC,QAAQ,EAAE,KAAK;QACf,WAAW,EAAE,mEAAmE;KACjF;IACD;QACE,IAAI,EAAE,mBAAmB;QACzB,SAAS,EAAE,mBAAmB;QAC9B,QAAQ,EAAE,KAAK;QACf,WAAW,EAAE,iCAAiC;KAC/C;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAuB;IAC9D,CAAC,YAAY,EAAE,EAAE,CAAC;IAClB,CAAC,uBAAuB,EAAE,CAAC,GAAG,YAAY,EAAE,GAAG,kBAAkB,CAAC,CAAC;IACnE,CAAC,uBAAuB,EAAE,CAAC,GAAG,YAAY,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"template-schema.js","sourceRoot":"","sources":["../../src/template/template-schema.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,EAAE;AACF,kFAAkF;AAClF,4EAA4E;AAC5E,8EAA8E;AAC9E,gFAAgF;AAChF,sDAAsD;AAGtD,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,yBAAyB,EACzB,yBAAyB,EACzB,sBAAsB,EACtB,oBAAoB,EACpB,uBAAuB,EACvB,mBAAmB,EACnB,mBAAmB,EACnB,2BAA2B,EAC3B,wBAAwB,EACxB,4BAA4B,EAC5B,mBAAmB,EACnB,uBAAuB,EACvB,gBAAgB,GACjB,MAAM,yBAAyB,CAAC;AAEjC,+DAA+D;AAC/D,MAAM,YAAY,GAAiB;IACjC;QACE,IAAI,EAAE,yBAAyB;QAC/B,SAAS,EAAE,mBAAmB;QAC9B,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,sFAAsF;KACpG;IACD;QACE,IAAI,EAAE,sBAAsB;QAC5B,SAAS,EAAE,mBAAmB;QAC9B,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,mGAAmG;KACjH;IACD;QACE,IAAI,EAAE,oBAAoB;QAC1B,SAAS,EAAE,mBAAmB;QAC9B,QAAQ,EAAE,KAAK;QACf,OAAO,EAAE,MAAM;QACf,aAAa,EAAE,CAAC,GAAG,gBAAgB,CAAC;QACpC,WAAW,EAAE,yEAAyE;KACvF;IACD;QACE,IAAI,EAAE,uBAAuB;QAC7B,SAAS,EAAE,gBAAgB;QAC3B,QAAQ,EAAE,KAAK;QACf,WAAW,EAAE,qDAAqD;KACnE;IACD;QACE,IAAI,EAAE,mBAAmB;QACzB,SAAS,EAAE,mBAAmB;QAC9B,QAAQ,EAAE,KAAK;QACf,WAAW,EAAE,yCAAyC;KACvD;IACD;QACE,IAAI,EAAE,mBAAmB;QACzB,SAAS,EAAE,mBAAmB;QAC9B,QAAQ,EAAE,KAAK;QACf,WAAW,EAAE,0DAA0D;KACxE;IACD;QACE,IAAI,EAAE,2BAA2B;QACjC,SAAS,EAAE,wBAAwB;QACnC,QAAQ,EAAE,KAAK;QACf,WAAW,EAAE,kFAAkF;KAChG;CACF,CAAC;AAEF,4CAA4C;AAC5C,MAAM,kBAAkB,GAAiB;IACvC;QACE,IAAI,EAAE,wBAAwB;QAC9B,SAAS,EAAE,gBAAgB;QAC3B,QAAQ,EAAE,KAAK;QACf,WAAW,EAAE,sDAAsD;KACpE;IACD;QACE,IAAI,EAAE,4BAA4B;QAClC,SAAS,EAAE,wBAAwB;QACnC,QAAQ,EAAE,KAAK;QACf,WAAW,EAAE,mEAAmE;KACjF;IACD;QACE,IAAI,EAAE,mBAAmB;QACzB,SAAS,EAAE,mBAAmB;QAC9B,QAAQ,EAAE,KAAK;QACf,WAAW,EAAE,iCAAiC;KAC/C;CACF,CAAC;AAEF,2EAA2E;AAC3E,+EAA+E;AAC/E,uEAAuE;AACvE,mFAAmF;AACnF,EAAE;AACF,4EAA4E;AAC5E,2EAA2E;AAC3E,2EAA2E;AAC3E,mDAAmD;AACnD,EAAE;AACF,iFAAiF;AACjF,gFAAgF;AAChF,oEAAoE;AACpE,MAAM,aAAa,GAAiB;IAClC;QACE,IAAI,EAAE,uBAAuB;QAC7B,SAAS,EAAE,mBAAmB;QAC9B,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,8DAA8D;KAC5E;IACD;QACE,IAAI,EAAE,yBAAyB;QAC/B,SAAS,EAAE,mBAAmB;QAC9B,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,yEAAyE;KACvF;IACD;QACE,IAAI,EAAE,mBAAmB;QACzB,SAAS,EAAE,mBAAmB;QAC9B,QAAQ,EAAE,KAAK;QACf,WAAW,EAAE,yCAAyC;KACvD;IACD;QACE,IAAI,EAAE,mBAAmB;QACzB,SAAS,EAAE,mBAAmB;QAC9B,QAAQ,EAAE,KAAK;QACf,WAAW,EAAE,0DAA0D;KACxE;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAuB;IAC9D,CAAC,YAAY,EAAE,EAAE,CAAC;IAClB,CAAC,uBAAuB,EAAE,CAAC,GAAG,YAAY,EAAE,GAAG,kBAAkB,CAAC,CAAC;IACnE,CAAC,uBAAuB,EAAE,CAAC,GAAG,YAAY,CAAC,CAAC;IAC5C,CAAC,yBAAyB,EAAE,CAAC,GAAG,aAAa,CAAC,CAAC;CAChD,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -100,7 +100,7 @@ function validateNode(
|
|
|
100
100
|
errors.push(
|
|
101
101
|
new ParseError(
|
|
102
102
|
`Common attr '${ca.name}' conflicts with per-type attr on ${typeKey}`,
|
|
103
|
-
{ code: "ERR_PROVIDER_ATTR_CONFLICT" },
|
|
103
|
+
{ code: "ERR_PROVIDER_ATTR_CONFLICT", source: node.source },
|
|
104
104
|
),
|
|
105
105
|
);
|
|
106
106
|
reportedConflicts.add(typeKey);
|
|
@@ -126,7 +126,7 @@ function validateNode(
|
|
|
126
126
|
errors.push(
|
|
127
127
|
new ParseError(
|
|
128
128
|
`${nodeLabel(node)} is missing required attribute '@${spec.name}'`,
|
|
129
|
-
{ code: "ERR_MISSING_REQUIRED_ATTR" },
|
|
129
|
+
{ code: "ERR_MISSING_REQUIRED_ATTR", source: node.source },
|
|
130
130
|
),
|
|
131
131
|
);
|
|
132
132
|
}
|
|
@@ -145,7 +145,7 @@ function validateNode(
|
|
|
145
145
|
const valueErrors = inst.validateValue(value);
|
|
146
146
|
if (valueErrors.length > 0) {
|
|
147
147
|
for (const ve of valueErrors) {
|
|
148
|
-
errors.push(new ParseError(`${nodeLabel(node)} ${ve.message}`, { code: "ERR_BAD_ATTR_VALUE" }));
|
|
148
|
+
errors.push(new ParseError(`${nodeLabel(node)} ${ve.message}`, { code: "ERR_BAD_ATTR_VALUE", source: node.source }));
|
|
149
149
|
}
|
|
150
150
|
continue; // type wrong → skip allowedValues
|
|
151
151
|
}
|
|
@@ -159,7 +159,7 @@ function validateNode(
|
|
|
159
159
|
`${nodeLabel(node)} attribute '@${inst.name}' has value ` +
|
|
160
160
|
`'${String(value)}' which is not one of the allowed values: ` +
|
|
161
161
|
`${spec.allowedValues.map((v) => String(v)).join(", ")}`,
|
|
162
|
-
{ code: "ERR_BAD_ATTR_VALUE" },
|
|
162
|
+
{ code: "ERR_BAD_ATTR_VALUE", source: node.source },
|
|
163
163
|
),
|
|
164
164
|
);
|
|
165
165
|
}
|
|
@@ -180,7 +180,7 @@ function validateNode(
|
|
|
180
180
|
errors.push(
|
|
181
181
|
new ParseError(
|
|
182
182
|
`${nodeLabel(node)} must declare at least one value in '@${FIELD_ATTR_VALUES}'.`,
|
|
183
|
-
{ code: "ERR_BAD_ATTR_VALUE" },
|
|
183
|
+
{ code: "ERR_BAD_ATTR_VALUE", source: node.source },
|
|
184
184
|
),
|
|
185
185
|
);
|
|
186
186
|
} else {
|
|
@@ -194,14 +194,14 @@ function validateNode(
|
|
|
194
194
|
`${nodeLabel(node)} attribute '@${FIELD_ATTR_VALUES}' member '${member}' ` +
|
|
195
195
|
`is not a valid identifier (must match ${ENUM_MEMBER_PATTERN.source}). ` +
|
|
196
196
|
`Non-identifier-safe member strings require a symbol↔value mapping (deferred).`,
|
|
197
|
-
{ code: "ERR_BAD_ATTR_VALUE" },
|
|
197
|
+
{ code: "ERR_BAD_ATTR_VALUE", source: node.source },
|
|
198
198
|
),
|
|
199
199
|
);
|
|
200
200
|
} else if (seen.has(member)) {
|
|
201
201
|
errors.push(
|
|
202
202
|
new ParseError(
|
|
203
203
|
`${nodeLabel(node)} attribute '@${FIELD_ATTR_VALUES}' has duplicate member '${member}'.`,
|
|
204
|
-
{ code: "ERR_BAD_ATTR_VALUE" },
|
|
204
|
+
{ code: "ERR_BAD_ATTR_VALUE", source: node.source },
|
|
205
205
|
),
|
|
206
206
|
);
|
|
207
207
|
} else {
|
package/src/core/export-json.ts
CHANGED
|
@@ -10,13 +10,12 @@
|
|
|
10
10
|
// - Content errors (parse/validation failures) are collected in errors[] and
|
|
11
11
|
// returned in the result — they do NOT throw. `json` is still produced from
|
|
12
12
|
// whatever tree the Loader returned (Loader always returns a valid MetaData).
|
|
13
|
-
// - I/O failures (missing/unreadable directory)
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
13
|
+
// - I/O failures (missing/unreadable directory) surface as collected errors
|
|
14
|
+
// via MetaDataLoader.fromDirectory; `loadAndExportJson` surfaces them
|
|
15
|
+
// unchanged in `ExportResult.errors`. It does not throw for directory or
|
|
16
|
+
// metadata problems.
|
|
17
17
|
|
|
18
|
-
import {
|
|
19
|
-
import type { LoadOptions } from "../loader/meta-data-loader.js";
|
|
18
|
+
import { MetaDataLoader, type LoadOptions } from "../loader/meta-data-loader.js";
|
|
20
19
|
import { canonicalSerialize } from "../serializer-json.js";
|
|
21
20
|
|
|
22
21
|
// ---------------------------------------------------------------------------
|
|
@@ -39,28 +38,26 @@ export interface ExportResult {
|
|
|
39
38
|
* Load all metadata under `dir` and export the entire model as one flattened
|
|
40
39
|
* canonical-JSON document.
|
|
41
40
|
*
|
|
42
|
-
*
|
|
43
|
-
* composed via `composeRegistry(coreProviders)`),
|
|
44
|
-
* resulting tree with `canonicalSerialize`.
|
|
41
|
+
* Routes through `MetaDataLoader.fromDirectory` (using the default registry
|
|
42
|
+
* composed via `composeRegistry(coreProviders)` when none supplied), then
|
|
43
|
+
* serializes the resulting tree with `canonicalSerialize`.
|
|
45
44
|
*
|
|
46
45
|
* @param dir Absolute or relative path to the directory containing `meta.*.json` files.
|
|
47
|
-
* @param opts Optional loader options
|
|
48
|
-
*
|
|
49
|
-
* supplied as `opts.exclude` — see `FileMetaDataLoader.loadDirectory`.
|
|
46
|
+
* @param opts Optional loader options (registry, freeze, strict). The
|
|
47
|
+
* `exclude` glob list can be supplied as `opts.exclude`.
|
|
50
48
|
*/
|
|
51
49
|
export async function loadAndExportJson(
|
|
52
50
|
dir: string,
|
|
53
51
|
opts?: LoadOptions & { exclude?: string[] },
|
|
54
52
|
): Promise<ExportResult> {
|
|
55
|
-
const
|
|
56
|
-
const loader = new FileMetaDataLoader(loaderOpts);
|
|
57
|
-
// Only pass the exclude option when defined, to satisfy exactOptionalPropertyTypes.
|
|
58
|
-
const dirOpts = exclude !== undefined ? { exclude } : undefined;
|
|
59
|
-
const result = await loader.loadDirectory(dir, dirOpts);
|
|
53
|
+
const result = await MetaDataLoader.fromDirectory(dir, opts);
|
|
60
54
|
const json = canonicalSerialize(result.root);
|
|
61
55
|
return {
|
|
62
56
|
json,
|
|
63
57
|
errors: result.errors,
|
|
64
|
-
|
|
58
|
+
// FR5a: LoadResult.warnings is now LoaderWarning[]; ExportResult preserves
|
|
59
|
+
// its public string[] shape (callers print warnings as text). Extract the
|
|
60
|
+
// message for back-compat.
|
|
61
|
+
warnings: result.warnings.map((w) => w.message),
|
|
65
62
|
};
|
|
66
63
|
}
|
package/src/core/index.ts
CHANGED
|
@@ -5,8 +5,14 @@
|
|
|
5
5
|
// load-and-export convenience. The root `@metaobjectsdev/metadata` entry is
|
|
6
6
|
// browser-safe and imports none of this. See the package README.
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
// Source impls — the node:fs-backed MetaDataSource implementations. Live
|
|
9
|
+
// under `loader/sources/`; re-exported here so server-side consumers can pull
|
|
10
|
+
// them from the same `/core` entry that already houses the YAML parser.
|
|
11
|
+
export { FileSource } from "../loader/sources/file-source.js";
|
|
12
|
+
export { DirectorySource } from "../loader/sources/directory-source.js";
|
|
13
|
+
export type { DirectoryOptions } from "../loader/sources/directory-source.js";
|
|
14
|
+
export { UriSource } from "../loader/sources/uri-source.js";
|
|
15
|
+
|
|
10
16
|
export { parseYaml } from "./parser-yaml.js";
|
|
11
17
|
export { loadAndExportJson } from "./export-json.js";
|
|
12
18
|
export type { ExportResult } from "./export-json.js";
|
package/src/core/parser-yaml.ts
CHANGED
|
@@ -1,15 +1,38 @@
|
|
|
1
1
|
// Authoring YAML parser.
|
|
2
2
|
//
|
|
3
|
-
// parseYaml is a front-end:
|
|
4
|
-
// (parser-core.ts). The desugar applies the four authoring-sugar
|
|
5
|
-
// resulting typed tree is identical to the one the equivalent
|
|
6
|
-
// produces.
|
|
3
|
+
// parseYaml is a front-end: parseYamlWithPositions → desugar → the shared
|
|
4
|
+
// buildTree (parser-core.ts). The desugar applies the four authoring-sugar
|
|
5
|
+
// rules so the resulting typed tree is identical to the one the equivalent
|
|
6
|
+
// canonical JSON produces.
|
|
7
|
+
//
|
|
8
|
+
// FR5b: the parse phase now preserves YAML source positions through the
|
|
9
|
+
// pipeline. parseYamlWithPositions attaches a Symbol-keyed position-by-key
|
|
10
|
+
// map onto every mapping object; desugar shallow-copies that property; and
|
|
11
|
+
// buildTree's `format: "yaml"` mode reads the position when stamping
|
|
12
|
+
// `node.source.yamlPosition` (see parser-core.ts).
|
|
7
13
|
|
|
8
|
-
import { parse as parseYamlText } from "yaml";
|
|
9
14
|
import { ParseError } from "../errors.js";
|
|
10
|
-
import { buildTree
|
|
15
|
+
import { buildTree } from "../parser-core.js";
|
|
11
16
|
import type { ParseOptions, ParseResult } from "../parser-core.js";
|
|
17
|
+
import type { ErrorSource } from "../source.js";
|
|
12
18
|
import { desugar } from "./yaml-desugar.js";
|
|
19
|
+
import { parseYamlWithPositions } from "./yaml-positions-walker.js";
|
|
20
|
+
|
|
21
|
+
/** FR5a / ADR-0009 — build a YAML-source envelope rooted at "$".
|
|
22
|
+
* yamlPosition on the envelope is left undefined here; envelopes for
|
|
23
|
+
* THROWN parser errors carry positions only when the parse phase pushed
|
|
24
|
+
* the path into the live parser-core builder (see populateNodeSource in
|
|
25
|
+
* parser-core.ts). Errors raised before buildTree (e.g. yaml syntax
|
|
26
|
+
* failures, an empty desugar result) lack a node — `yamlPosition` is
|
|
27
|
+
* intentionally omitted, per the spec's "skip on desugar-synthesized
|
|
28
|
+
* nodes" decision. */
|
|
29
|
+
function yamlSource(sourceName: string | undefined): ErrorSource {
|
|
30
|
+
return {
|
|
31
|
+
format: "yaml",
|
|
32
|
+
files: [sourceName ?? "<unknown>"],
|
|
33
|
+
jsonPath: "$",
|
|
34
|
+
};
|
|
35
|
+
}
|
|
13
36
|
|
|
14
37
|
export function parseYaml(content: string, opts: ParseOptions): ParseResult {
|
|
15
38
|
// Strip UTF-8 BOM if present (consistent with parseJson).
|
|
@@ -20,11 +43,11 @@ export function parseYaml(content: string, opts: ParseOptions): ParseResult {
|
|
|
20
43
|
// The loader's per-source try/catch collects the throw into LoadResult.errors.
|
|
21
44
|
let parsed: unknown;
|
|
22
45
|
try {
|
|
23
|
-
parsed =
|
|
46
|
+
parsed = parseYamlWithPositions(normalizedContent).value;
|
|
24
47
|
} catch (err) {
|
|
25
48
|
throw new ParseError(
|
|
26
49
|
`Invalid YAML: ${(err as Error).message}`,
|
|
27
|
-
{
|
|
50
|
+
{ code: "ERR_MALFORMED_YAML", source: yamlSource(opts.sourceName) },
|
|
28
51
|
);
|
|
29
52
|
}
|
|
30
53
|
|
|
@@ -37,11 +60,14 @@ export function parseYaml(content: string, opts: ParseOptions): ParseResult {
|
|
|
37
60
|
const first = desugarErrors[0]!;
|
|
38
61
|
throw new ParseError(
|
|
39
62
|
first.message,
|
|
40
|
-
{
|
|
63
|
+
{ code: first.code ?? "ERR_MALFORMED_YAML", source: yamlSource(opts.sourceName) },
|
|
41
64
|
);
|
|
42
65
|
}
|
|
43
66
|
|
|
44
|
-
|
|
67
|
+
// FR5b — buildTree needs to know the source-format discriminant so
|
|
68
|
+
// populateNodeSource emits `format: "yaml"` envelopes (with the
|
|
69
|
+
// optional yamlPosition) instead of the default `format: "json"`.
|
|
70
|
+
const result = buildTree(canonical, { ...opts, sourceFormat: "yaml" });
|
|
45
71
|
|
|
46
72
|
// Merge collected desugar errors ahead of buildTree's own collected errors.
|
|
47
73
|
// Each CollectedError carries its own stable code when set (e.g.
|
|
@@ -50,13 +76,14 @@ export function parseYaml(content: string, opts: ParseOptions): ParseResult {
|
|
|
50
76
|
const desugarParseErrors = desugarErrors.map(
|
|
51
77
|
(e) =>
|
|
52
78
|
new ParseError(e.message, {
|
|
53
|
-
...errOpts(opts.sourceName),
|
|
54
79
|
code: e.code ?? "ERR_MALFORMED_YAML",
|
|
80
|
+
source: yamlSource(opts.sourceName),
|
|
55
81
|
}),
|
|
56
82
|
);
|
|
57
83
|
return {
|
|
58
84
|
root: result.root,
|
|
59
85
|
warnings: result.warnings,
|
|
60
86
|
errors: [...desugarParseErrors, ...result.errors],
|
|
87
|
+
envelopeWarnings: result.envelopeWarnings,
|
|
61
88
|
};
|
|
62
89
|
}
|
package/src/core/yaml-desugar.ts
CHANGED
|
@@ -38,6 +38,11 @@ import {
|
|
|
38
38
|
RESERVED_KEY_IS_ARRAY,
|
|
39
39
|
TYPE_SUBTYPE_SEPARATOR,
|
|
40
40
|
} from "../shared/structural.js";
|
|
41
|
+
import {
|
|
42
|
+
getPositionMap,
|
|
43
|
+
setPositionMap,
|
|
44
|
+
type PositionMap,
|
|
45
|
+
} from "./yaml-positions.js";
|
|
41
46
|
import {
|
|
42
47
|
ATTR_SUBTYPE_STRING,
|
|
43
48
|
ATTR_SUBTYPE_CLASS,
|
|
@@ -99,6 +104,11 @@ function desugarNode(
|
|
|
99
104
|
const rawKey = entries[0]!;
|
|
100
105
|
const rawBody = (input as Record<string, unknown>)[rawKey];
|
|
101
106
|
|
|
107
|
+
// FR5b — capture the wrapper-level position-by-key map BEFORE re-keying.
|
|
108
|
+
// The author's raw key (with `[]` suffix and possibly omitted subType) is
|
|
109
|
+
// the lookup key; the desugar's canonical key is what we emit.
|
|
110
|
+
const wrapperPositions = getPositionMap(input);
|
|
111
|
+
|
|
102
112
|
// Rule 4: a trailing "[]" on the key → isArray.
|
|
103
113
|
let key = rawKey;
|
|
104
114
|
let isArray = false;
|
|
@@ -111,7 +121,10 @@ function desugarNode(
|
|
|
111
121
|
const canonicalKey = resolveKey(key, registry, errors, path);
|
|
112
122
|
|
|
113
123
|
// Rule 2: a scalar body → { name: <scalar> }.
|
|
114
|
-
|
|
124
|
+
// FR5b — propagate the wrapper-key's position into the synthesized body
|
|
125
|
+
// when the input body was a scalar (no body-side positions to inherit).
|
|
126
|
+
const wrapperKeyPos = wrapperPositions?.[rawKey];
|
|
127
|
+
const body = desugarBody(rawBody, registry, canonicalKey, errors, path, wrapperKeyPos);
|
|
115
128
|
|
|
116
129
|
// Rule 4 (cont.): stamp isArray onto the canonical body.
|
|
117
130
|
if (isArray) body[RESERVED_KEY_IS_ARRAY] = true;
|
|
@@ -131,7 +144,16 @@ function desugarNode(
|
|
|
131
144
|
}
|
|
132
145
|
// A non-array `children` value is left untouched — buildTree reports it.
|
|
133
146
|
|
|
134
|
-
|
|
147
|
+
// FR5b — emit a wrapper-level position-by-key map for the canonical wrapper
|
|
148
|
+
// so buildTree's per-child iteration can read the position via the same
|
|
149
|
+
// lookup it uses for JSON input. The single key transformation is
|
|
150
|
+
// rawKey → canonicalKey (Rule 1 fuses the subType, Rule 4 strips `[]`).
|
|
151
|
+
const outWrapper: Record<string, unknown> = { [canonicalKey]: body };
|
|
152
|
+
if (wrapperKeyPos !== undefined) {
|
|
153
|
+
setPositionMap(outWrapper, { [canonicalKey]: wrapperKeyPos });
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return outWrapper;
|
|
135
157
|
}
|
|
136
158
|
|
|
137
159
|
// Rule 1 — resolve a possibly-bare key to a fused `type.subType` token.
|
|
@@ -169,16 +191,30 @@ function desugarBody(
|
|
|
169
191
|
canonicalKey: string,
|
|
170
192
|
errors: CollectedError[],
|
|
171
193
|
path: string,
|
|
194
|
+
/** FR5b — position of the WRAPPER key (the `field.string:` line). Used to
|
|
195
|
+
* back-fill `yamlPosition` on synthesized bodies (Rule 2's scalar lift) +
|
|
196
|
+
* empty bodies; for mapping bodies we use the body's own position-by-key
|
|
197
|
+
* map. */
|
|
198
|
+
wrapperKeyPos: { line: number; col: number } | undefined,
|
|
172
199
|
): Record<string, unknown> {
|
|
173
200
|
if (
|
|
174
201
|
typeof rawBody === "string" ||
|
|
175
202
|
typeof rawBody === "number" ||
|
|
176
203
|
typeof rawBody === "boolean"
|
|
177
204
|
) {
|
|
178
|
-
|
|
205
|
+
// FR5b — the synthesized `{ name: rawBody }` has no YAML-side
|
|
206
|
+
// counterpart; we attribute the `name` slot to the wrapper-key's
|
|
207
|
+
// position (the only YAML position that meaningfully belongs to this
|
|
208
|
+
// synthesis).
|
|
209
|
+
const out: Record<string, unknown> = { [RESERVED_KEY_NAME]: rawBody };
|
|
210
|
+
if (wrapperKeyPos !== undefined) {
|
|
211
|
+
setPositionMap(out, { [RESERVED_KEY_NAME]: wrapperKeyPos });
|
|
212
|
+
}
|
|
213
|
+
return out;
|
|
179
214
|
}
|
|
180
215
|
if (rawBody === null || rawBody === undefined) {
|
|
181
216
|
// An empty body (`field.string:` with nothing after) → an empty node.
|
|
217
|
+
// No body keys to position; the wrapper carries the node's position.
|
|
182
218
|
return {};
|
|
183
219
|
}
|
|
184
220
|
if (Array.isArray(rawBody)) {
|
|
@@ -192,20 +228,38 @@ function desugarBody(
|
|
|
192
228
|
// Rule D2 (type-coercion guard).
|
|
193
229
|
const src = rawBody as Record<string, unknown>;
|
|
194
230
|
const out: Record<string, unknown> = {};
|
|
231
|
+
// FR5b — translate the body's position-by-key map across the sigil-free
|
|
232
|
+
// rewrite. A bare `filterable` key in the source maps to `@filterable` in
|
|
233
|
+
// the canonical body; the YAML position belongs to BOTH names (the YAML
|
|
234
|
+
// author only wrote one). We re-key the position map to match the canonical
|
|
235
|
+
// body's keys so buildTree's per-attr inspection (FR5b follow-ups, e.g.
|
|
236
|
+
// ERR_BAD_ATTR_VALUE) can find the position via the canonical key.
|
|
237
|
+
const srcPositions = getPositionMap(src);
|
|
238
|
+
const outPositions: PositionMap = {};
|
|
239
|
+
let hasOutPositions = false;
|
|
195
240
|
const schemaIndex = attrSchemaIndex(registry, canonicalKey);
|
|
196
241
|
for (const key of Object.keys(src)) {
|
|
242
|
+
let outKey: string;
|
|
197
243
|
if (RESERVED_KEYS.has(key) || key.startsWith(ATTR_PREFIX)) {
|
|
198
244
|
out[key] = src[key];
|
|
245
|
+
outKey = key;
|
|
199
246
|
// D2 also applies to author-written @-keys (the awkward form).
|
|
200
247
|
const attrName = key.startsWith(ATTR_PREFIX) ? key.slice(ATTR_PREFIX.length) : "";
|
|
201
248
|
if (attrName !== "" && !RESERVED_KEYS.has(attrName)) {
|
|
202
249
|
checkCoercion(attrName, src[key], schemaIndex, errors, path);
|
|
203
250
|
}
|
|
204
251
|
} else {
|
|
205
|
-
|
|
252
|
+
outKey = `${ATTR_PREFIX}${key}`;
|
|
253
|
+
out[outKey] = src[key];
|
|
206
254
|
checkCoercion(key, src[key], schemaIndex, errors, path);
|
|
207
255
|
}
|
|
256
|
+
const pos = srcPositions?.[key];
|
|
257
|
+
if (pos !== undefined) {
|
|
258
|
+
outPositions[outKey] = pos;
|
|
259
|
+
hasOutPositions = true;
|
|
260
|
+
}
|
|
208
261
|
}
|
|
262
|
+
if (hasOutPositions) setPositionMap(out, outPositions);
|
|
209
263
|
return out;
|
|
210
264
|
}
|
|
211
265
|
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// FR5b — YAML AST → JS walker that preserves source positions.
|
|
2
|
+
//
|
|
3
|
+
// This module is the only place inside @metaobjectsdev/metadata that
|
|
4
|
+
// imports the `yaml` package. It lives in core/ alongside parser-yaml.ts,
|
|
5
|
+
// and is reached only via that parser — never via src/index.ts. The
|
|
6
|
+
// browser-safety test guards this invariant.
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
parseDocument,
|
|
10
|
+
isAlias,
|
|
11
|
+
isMap,
|
|
12
|
+
isScalar,
|
|
13
|
+
isSeq,
|
|
14
|
+
LineCounter,
|
|
15
|
+
type Document,
|
|
16
|
+
} from "yaml";
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
setPositionMap,
|
|
20
|
+
type PositionMap,
|
|
21
|
+
} from "./yaml-positions.js";
|
|
22
|
+
|
|
23
|
+
/** Result of parsing YAML text with positions retained. */
|
|
24
|
+
export interface YamlParseResult {
|
|
25
|
+
/** The JS object (same shape as `yaml.parse(text)` returns), with
|
|
26
|
+
* position-by-key maps attached to every mapping. */
|
|
27
|
+
value: unknown;
|
|
28
|
+
/** The yaml library's LineCounter — exposed for callers that need to map
|
|
29
|
+
* additional ranges (e.g. surfacing errors raised by the YAML library
|
|
30
|
+
* itself). */
|
|
31
|
+
lineCounter: LineCounter;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Parse YAML text and return a JS object with positions attached.
|
|
35
|
+
*
|
|
36
|
+
* Mirrors the contract of `yaml.parse(text)` for the shapes the metaobjects
|
|
37
|
+
* authoring grammar uses (mappings, sequences, scalars). Aliases and tags
|
|
38
|
+
* are deferred via the underlying parseDocument call — i.e. they resolve as
|
|
39
|
+
* the library normally would.
|
|
40
|
+
*
|
|
41
|
+
* Throws on YAML syntax errors (same behavior as `yaml.parse`). */
|
|
42
|
+
export function parseYamlWithPositions(text: string): YamlParseResult {
|
|
43
|
+
const lineCounter = new LineCounter();
|
|
44
|
+
const doc = parseDocument(text, { lineCounter });
|
|
45
|
+
// Surface YAML syntax errors as a throw, matching `yaml.parse` behavior.
|
|
46
|
+
// (parseDocument collects them rather than throwing.)
|
|
47
|
+
if (doc.errors.length > 0) {
|
|
48
|
+
throw doc.errors[0]!;
|
|
49
|
+
}
|
|
50
|
+
const value = yamlNodeToJs(doc.contents, lineCounter, doc);
|
|
51
|
+
return { value, lineCounter };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Walk a yaml AST node into a JS structure. For each YAMLMap, attach a
|
|
55
|
+
// position-by-key map onto the resulting JS object — the position of each
|
|
56
|
+
// key is the (line, col) of the KEY token in the YAML source.
|
|
57
|
+
function yamlNodeToJs(
|
|
58
|
+
node: unknown,
|
|
59
|
+
lineCounter: LineCounter,
|
|
60
|
+
doc: Document,
|
|
61
|
+
): unknown {
|
|
62
|
+
if (node === null || node === undefined) return null;
|
|
63
|
+
if (isScalar(node)) {
|
|
64
|
+
// Honour the library's default scalar typing (numbers / booleans /
|
|
65
|
+
// strings / null all come through Scalar.value).
|
|
66
|
+
return node.value;
|
|
67
|
+
}
|
|
68
|
+
if (isAlias(node)) {
|
|
69
|
+
// Resolve an anchor alias (e.g. `*col` after `&col sku_code`) to its
|
|
70
|
+
// target value — same behaviour as the library's toJS().
|
|
71
|
+
const target = node.resolve(doc);
|
|
72
|
+
return yamlNodeToJs(target, lineCounter, doc);
|
|
73
|
+
}
|
|
74
|
+
if (isMap(node)) {
|
|
75
|
+
const out: Record<string, unknown> = {};
|
|
76
|
+
const positions: PositionMap = {};
|
|
77
|
+
let hasAnyPosition = false;
|
|
78
|
+
for (const pair of node.items) {
|
|
79
|
+
// Only string-keyed entries are valid in metaobjects authoring; ignore
|
|
80
|
+
// exotic keys (numeric / complex) — they'd already break the desugar.
|
|
81
|
+
if (!isScalar(pair.key)) continue;
|
|
82
|
+
const keyText = String(pair.key.value);
|
|
83
|
+
const valueJs = yamlNodeToJs(pair.value, lineCounter, doc);
|
|
84
|
+
out[keyText] = valueJs;
|
|
85
|
+
const keyRange = pair.key.range;
|
|
86
|
+
if (keyRange !== null && keyRange !== undefined) {
|
|
87
|
+
const pos = lineCounter.linePos(keyRange[0]);
|
|
88
|
+
positions[keyText] = { line: pos.line, col: pos.col };
|
|
89
|
+
hasAnyPosition = true;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (hasAnyPosition) setPositionMap(out, positions);
|
|
93
|
+
return out;
|
|
94
|
+
}
|
|
95
|
+
if (isSeq(node)) {
|
|
96
|
+
return node.items.map((item) => yamlNodeToJs(item, lineCounter, doc));
|
|
97
|
+
}
|
|
98
|
+
// Tags / unsupported — fall back to null. The metaobjects authoring
|
|
99
|
+
// grammar does not use them.
|
|
100
|
+
return null;
|
|
101
|
+
}
|