@metaobjectsdev/metadata 1.0.13 → 1.1.1-rc.1
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 +2 -0
- package/dist/attr-schema-validate.d.ts.map +1 -1
- package/dist/attr-schema-validate.js +9 -12
- package/dist/attr-schema-validate.js.map +1 -1
- package/dist/constants.d.ts +1 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +1 -0
- package/dist/constants.js.map +1 -1
- package/dist/core/attr/meta-attr-filter.d.ts.map +1 -1
- package/dist/core/attr/meta-attr-filter.js +8 -2
- package/dist/core/attr/meta-attr-filter.js.map +1 -1
- package/dist/core/attr/meta-attr.d.ts +14 -0
- package/dist/core/attr/meta-attr.d.ts.map +1 -1
- package/dist/core/attr/meta-attr.js +23 -1
- package/dist/core/attr/meta-attr.js.map +1 -1
- package/dist/core/field/shared-enum.d.ts +21 -0
- package/dist/core/field/shared-enum.d.ts.map +1 -0
- package/dist/core/field/shared-enum.js +49 -0
- package/dist/core/field/shared-enum.js.map +1 -0
- package/dist/core/identity/meta-identity.d.ts +1 -1
- package/dist/core/identity/meta-identity.d.ts.map +1 -1
- package/dist/core/identity/meta-identity.js +8 -13
- package/dist/core/identity/meta-identity.js.map +1 -1
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +3 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/object/object-constants.d.ts +14 -1
- package/dist/core/object/object-constants.d.ts.map +1 -1
- package/dist/core/object/object-constants.js +18 -0
- package/dist/core/object/object-constants.js.map +1 -1
- package/dist/core/object/object-definition.embedded.d.ts.map +1 -1
- package/dist/core/object/object-definition.embedded.js +80 -0
- package/dist/core/object/object-definition.embedded.js.map +1 -1
- package/dist/core/relationship/derive-m2m-fields.d.ts +1 -1
- package/dist/core/relationship/derive-m2m-fields.d.ts.map +1 -1
- package/dist/core/relationship/derive-m2m-fields.js +15 -44
- package/dist/core/relationship/derive-m2m-fields.js.map +1 -1
- package/dist/core/relationship/find-reference.d.ts.map +1 -1
- package/dist/core/relationship/find-reference.js +4 -2
- package/dist/core/relationship/find-reference.js.map +1 -1
- package/dist/core/relationship/resolve-relationship-reference.d.ts +6 -5
- package/dist/core/relationship/resolve-relationship-reference.d.ts.map +1 -1
- package/dist/core/relationship/resolve-relationship-reference.js +9 -7
- package/dist/core/relationship/resolve-relationship-reference.js.map +1 -1
- package/dist/core/reporting/meta-dimension.d.ts +13 -0
- package/dist/core/reporting/meta-dimension.d.ts.map +1 -0
- package/dist/core/reporting/meta-dimension.js +30 -0
- package/dist/core/reporting/meta-dimension.js.map +1 -0
- package/dist/core/reporting/meta-measure.d.ts +24 -0
- package/dist/core/reporting/meta-measure.d.ts.map +1 -0
- package/dist/core/reporting/meta-measure.js +54 -0
- package/dist/core/reporting/meta-measure.js.map +1 -0
- package/dist/core/reporting/meta-segment.d.ts +6 -0
- package/dist/core/reporting/meta-segment.d.ts.map +1 -0
- package/dist/core/reporting/meta-segment.js +13 -0
- package/dist/core/reporting/meta-segment.js.map +1 -0
- package/dist/core/reporting/report-accessors.d.ts +27 -0
- package/dist/core/reporting/report-accessors.d.ts.map +1 -0
- package/dist/core/reporting/report-accessors.js +56 -0
- package/dist/core/reporting/report-accessors.js.map +1 -0
- package/dist/core/reporting/report-describe.d.ts +59 -0
- package/dist/core/reporting/report-describe.d.ts.map +1 -0
- package/dist/core/reporting/report-describe.js +182 -0
- package/dist/core/reporting/report-describe.js.map +1 -0
- package/dist/core/reporting/report-read-model.d.ts +34 -0
- package/dist/core/reporting/report-read-model.d.ts.map +1 -0
- package/dist/core/reporting/report-read-model.js +165 -0
- package/dist/core/reporting/report-read-model.js.map +1 -0
- package/dist/core/reporting/report-shape.d.ts +103 -0
- package/dist/core/reporting/report-shape.d.ts.map +1 -0
- package/dist/core/reporting/report-shape.js +398 -0
- package/dist/core/reporting/report-shape.js.map +1 -0
- package/dist/core/reporting/reporting-constants.d.ts +65 -0
- package/dist/core/reporting/reporting-constants.d.ts.map +1 -0
- package/dist/core/reporting/reporting-constants.js +84 -0
- package/dist/core/reporting/reporting-constants.js.map +1 -0
- package/dist/core/reporting/reporting-definition.embedded.d.ts +3 -0
- package/dist/core/reporting/reporting-definition.embedded.d.ts.map +1 -0
- package/dist/core/reporting/reporting-definition.embedded.js +185 -0
- package/dist/core/reporting/reporting-definition.embedded.js.map +1 -0
- package/dist/core/requirement/resolve-claim.d.ts +8 -0
- package/dist/core/requirement/resolve-claim.d.ts.map +1 -1
- package/dist/core/requirement/resolve-claim.js +21 -0
- package/dist/core/requirement/resolve-claim.js.map +1 -1
- package/dist/core/yaml-desugar.d.ts.map +1 -1
- package/dist/core/yaml-desugar.js +12 -82
- package/dist/core/yaml-desugar.js.map +1 -1
- package/dist/core-types.d.ts.map +1 -1
- package/dist/core-types.js +28 -1
- package/dist/core-types.js.map +1 -1
- package/dist/docs-ref.d.ts +19 -0
- package/dist/docs-ref.d.ts.map +1 -0
- package/dist/docs-ref.js +37 -0
- package/dist/docs-ref.js.map +1 -0
- package/dist/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +17 -6
- package/dist/errors.js.map +1 -1
- package/dist/fmt.d.ts.map +1 -1
- package/dist/fmt.js +3 -1
- package/dist/fmt.js.map +1 -1
- package/dist/index.d.ts +15 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -4
- package/dist/index.js.map +1 -1
- package/dist/loader/declared-duplicate-fields.d.ts +19 -0
- package/dist/loader/declared-duplicate-fields.d.ts.map +1 -0
- package/dist/loader/declared-duplicate-fields.js +97 -0
- package/dist/loader/declared-duplicate-fields.js.map +1 -0
- package/dist/loader/meta-data-loader.d.ts.map +1 -1
- package/dist/loader/meta-data-loader.js +18 -1
- package/dist/loader/meta-data-loader.js.map +1 -1
- package/dist/loader/reporting-validation.d.ts +11 -0
- package/dist/loader/reporting-validation.d.ts.map +1 -0
- package/dist/loader/reporting-validation.js +883 -0
- package/dist/loader/reporting-validation.js.map +1 -0
- package/dist/loader/resolve-references.d.ts +8 -0
- package/dist/loader/resolve-references.d.ts.map +1 -0
- package/dist/loader/resolve-references.js +132 -0
- package/dist/loader/resolve-references.js.map +1 -0
- package/dist/loader/validation-passes.d.ts.map +1 -1
- package/dist/loader/validation-passes.js +14 -9
- package/dist/loader/validation-passes.js.map +1 -1
- package/dist/loader/validation-registry.d.ts.map +1 -1
- package/dist/loader/validation-registry.js +33 -10
- package/dist/loader/validation-registry.js.map +1 -1
- package/dist/metamodel-docs/index.js +3 -3
- package/dist/metamodel-docs/index.js.map +1 -1
- package/dist/metamodel-docs/render.js +4 -4
- package/dist/metamodel-docs/render.js.map +1 -1
- package/dist/naming-refs.d.ts +102 -50
- package/dist/naming-refs.d.ts.map +1 -1
- package/dist/naming-refs.js +223 -97
- package/dist/naming-refs.js.map +1 -1
- package/dist/naming.d.ts +42 -7
- package/dist/naming.d.ts.map +1 -1
- package/dist/naming.js +43 -5
- package/dist/naming.js.map +1 -1
- package/dist/object-serializer.d.ts.map +1 -1
- package/dist/object-serializer.js +6 -9
- package/dist/object-serializer.js.map +1 -1
- package/dist/parser-core.d.ts.map +1 -1
- package/dist/parser-core.js +18 -48
- package/dist/parser-core.js.map +1 -1
- package/dist/persistence/db/db-definition.embedded.js +2 -2
- package/dist/persistence/db/db-definition.embedded.js.map +1 -1
- package/dist/persistence/source/meta-source.d.ts +5 -3
- package/dist/persistence/source/meta-source.d.ts.map +1 -1
- package/dist/persistence/source/meta-source.js +8 -6
- package/dist/persistence/source/meta-source.js.map +1 -1
- package/dist/registry-manifest-exclusions.js +2 -2
- package/dist/registry-manifest-exclusions.js.map +1 -1
- package/dist/registry-manifest.d.ts +1 -1
- package/dist/registry-manifest.js +1 -1
- package/dist/release-docs.d.ts +11 -0
- package/dist/release-docs.d.ts.map +1 -0
- package/dist/release-docs.js +48 -0
- package/dist/release-docs.js.map +1 -0
- package/dist/serializer-json.d.ts +15 -1
- package/dist/serializer-json.d.ts.map +1 -1
- package/dist/serializer-json.js +55 -17
- package/dist/serializer-json.js.map +1 -1
- package/dist/shared/base-types.d.ts +7 -1
- package/dist/shared/base-types.d.ts.map +1 -1
- package/dist/shared/base-types.js +9 -0
- package/dist/shared/base-types.js.map +1 -1
- 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 +22 -1
- package/dist/shared/meta-data.js.map +1 -1
- package/dist/shared/meta-root.d.ts +13 -1
- package/dist/shared/meta-root.d.ts.map +1 -1
- package/dist/shared/meta-root.js +19 -1
- package/dist/shared/meta-root.js.map +1 -1
- package/dist/super-resolve.d.ts +4 -1
- package/dist/super-resolve.d.ts.map +1 -1
- package/dist/super-resolve.js +44 -65
- package/dist/super-resolve.js.map +1 -1
- package/dist/vocabulary-catalog.js +2 -2
- package/dist/vocabulary-catalog.js.map +1 -1
- package/package.json +6 -1
- package/src/attr-schema-validate.ts +9 -13
- package/src/constants.ts +1 -0
- package/src/core/attr/meta-attr-filter.ts +8 -1
- package/src/core/attr/meta-attr.ts +24 -1
- package/src/core/field/shared-enum.ts +53 -0
- package/src/core/identity/meta-identity.ts +9 -12
- package/src/core/index.ts +5 -0
- package/src/core/object/object-constants.ts +19 -0
- package/src/core/object/object-definition.embedded.ts +80 -0
- package/src/core/relationship/derive-m2m-fields.ts +15 -48
- package/src/core/relationship/find-reference.ts +4 -2
- package/src/core/relationship/resolve-relationship-reference.ts +11 -9
- package/src/core/reporting/meta-dimension.ts +41 -0
- package/src/core/reporting/meta-measure.ts +75 -0
- package/src/core/reporting/meta-segment.ts +14 -0
- package/src/core/reporting/report-accessors.ts +73 -0
- package/src/core/reporting/report-describe.ts +206 -0
- package/src/core/reporting/report-read-model.ts +177 -0
- package/src/core/reporting/report-shape.ts +493 -0
- package/src/core/reporting/reporting-constants.ts +104 -0
- package/src/core/reporting/reporting-definition.embedded.ts +192 -0
- package/src/core/requirement/resolve-claim.ts +20 -0
- package/src/core/yaml-desugar.ts +9 -89
- package/src/core-types.ts +41 -0
- package/src/docs-ref.ts +41 -0
- package/src/errors.ts +17 -6
- package/src/fmt.ts +3 -1
- package/src/index.ts +72 -4
- package/src/loader/declared-duplicate-fields.ts +114 -0
- package/src/loader/meta-data-loader.ts +25 -1
- package/src/loader/reporting-validation.ts +1137 -0
- package/src/loader/resolve-references.ts +144 -0
- package/src/loader/validation-passes.ts +13 -9
- package/src/loader/validation-registry.ts +37 -9
- package/src/metamodel-docs/index.ts +3 -3
- package/src/metamodel-docs/render.ts +4 -4
- package/src/naming-refs.ts +237 -94
- package/src/naming.ts +75 -11
- package/src/object-serializer.ts +7 -9
- package/src/parser-core.ts +19 -56
- package/src/persistence/db/db-definition.embedded.ts +2 -2
- package/src/persistence/source/meta-source.ts +8 -6
- package/src/registry-manifest-exclusions.ts +2 -2
- package/src/registry-manifest.ts +1 -1
- package/src/release-docs.ts +48 -0
- package/src/serializer-json.ts +79 -12
- package/src/shared/base-types.ts +9 -0
- package/src/shared/meta-data.ts +22 -1
- package/src/shared/meta-root.ts +20 -1
- package/src/super-resolve.ts +49 -63
- package/src/vocabulary-catalog.ts +2 -2
package/src/naming-refs.ts
CHANGED
|
@@ -1,30 +1,25 @@
|
|
|
1
|
-
//
|
|
1
|
+
// References: the one resolver every reference form goes through (ADR-0059).
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
3
|
+
// Sources do not interpret references: YAML, canonical JSON and any future source
|
|
4
|
+
// build the tree with every reference string exactly as authored. This module
|
|
5
|
+
// gives each form its meaning, once, against the built tree:
|
|
6
6
|
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
// `..::Rest` (one or more leading `..::`) → drop one package segment from P
|
|
16
|
-
// per `..::`, then resolve Rest
|
|
17
|
-
// (itself bare/qualified) against the
|
|
18
|
-
// reduced package. Over-drop (more
|
|
19
|
-
// `..::` than P has segments) throws.
|
|
7
|
+
// `::Rest` root-absolute — the node whose resolution key is exactly `Rest`
|
|
8
|
+
// (`::Apple` is the root-level Apple; `::a::b::C` is `a::b::C`).
|
|
9
|
+
// `..::Rest` parent-relative — drop one segment of the referrer's package per
|
|
10
|
+
// `..::`, then the exact key `<remaining>::Rest`. More `..::` than
|
|
11
|
+
// the package has segments is ERR_BAD_ATTR_VALUE.
|
|
12
|
+
// `pkg::Name` exact.
|
|
13
|
+
// `Name` the referrer's own package (`<referrerPkg>::Name`), else the
|
|
14
|
+
// root-level `Name` (ADR-0042 §2). Never another package.
|
|
20
15
|
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
// the
|
|
16
|
+
// No form falls back to another: `::Apple` never binds a local `acme::Apple`.
|
|
17
|
+
// A FR-024 dotted child tail (`Owner.child.grand`) starts at the first `.` after the
|
|
18
|
+
// last `::`; only the owner part is resolved by form, and the tail is kept as written.
|
|
24
19
|
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
//
|
|
20
|
+
// `spellRef` is the inverse: the one canonical spelling of a resolved target that
|
|
21
|
+
// resolves to the same node with or without a referrer. The `resolveReferences`
|
|
22
|
+
// loader pass rewrites every reference to it, so consumers read canonical values.
|
|
28
23
|
|
|
29
24
|
import type { MetaData } from "./shared/meta-data.js";
|
|
30
25
|
import { PACKAGE_SEPARATOR, PACKAGE_PARENT, CHILD_REF_SEPARATOR } from "./shared/structural.js";
|
|
@@ -39,22 +34,20 @@ import {
|
|
|
39
34
|
ORIGIN_AGGREGATE_ATTR_VIA,
|
|
40
35
|
} from "./persistence/origin/origin-constants.js";
|
|
41
36
|
import { SOURCE_ATTR_PARAMETER_REF } from "./persistence/source/source-constants.js";
|
|
37
|
+
import { OBJECT_REPORT_ATTR_FROM, OBJECT_REPORT_ATTR_SPINE } from "./core/object/object-constants.js";
|
|
42
38
|
import { TEMPLATE_ATTR_PAYLOAD_REF, TEMPLATE_ATTR_RESPONSE_REF } from "./template/template-constants.js";
|
|
43
39
|
|
|
44
40
|
const PARENT_PREFIX = PACKAGE_PARENT + PACKAGE_SEPARATOR; // "..::"
|
|
45
41
|
|
|
46
42
|
/**
|
|
47
|
-
* The inline (`@`-prefixed) attribute names whose VALUE is a metadata
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* ADR-0042 §4 — it desugars to FQN and resolves package-local like every other
|
|
56
|
-
* object ref. `@sourceRefField` (a FK FIELD name, not an object ref) is NOT in
|
|
57
|
-
* the set.
|
|
43
|
+
* The inline (`@`-prefixed) attribute names whose VALUE is a metadata object
|
|
44
|
+
* reference (ADR-0059 R4). The structural `extends` key is handled separately (it is
|
|
45
|
+
* not `@`-prefixed). `@objectRef`/`@references`/`@through` are pure object refs;
|
|
46
|
+
* `@from`/`@of`/`@via`/`@spine` carry an entity HEAD with a dotted
|
|
47
|
+
* relationship/field tail; `@parameterRef`/`@payloadRef`/`@responseRef` name
|
|
48
|
+
* value-objects. The same set is used in every port. `@sourceRefField` (a FK FIELD
|
|
49
|
+
* name) and `@implementedBy` (which may name a template as well as an object) are
|
|
50
|
+
* NOT in the set.
|
|
58
51
|
*/
|
|
59
52
|
export const REF_BEARING_ATTR_NAMES: ReadonlySet<string> = new Set<string>([
|
|
60
53
|
RELATIONSHIP_ATTR_OBJECT_REF, // = FIELD_ATTR_OBJECT_REF (same spelling "objectRef")
|
|
@@ -68,13 +61,11 @@ export const REF_BEARING_ATTR_NAMES: ReadonlySet<string> = new Set<string>([
|
|
|
68
61
|
SOURCE_ATTR_PARAMETER_REF,
|
|
69
62
|
TEMPLATE_ATTR_PAYLOAD_REF,
|
|
70
63
|
TEMPLATE_ATTR_RESPONSE_REF,
|
|
64
|
+
OBJECT_REPORT_ATTR_FROM, // = ORIGIN_PASSTHROUGH_ATTR_FROM ("from")
|
|
65
|
+
OBJECT_REPORT_ATTR_SPINE,
|
|
71
66
|
]);
|
|
72
67
|
|
|
73
|
-
/**
|
|
74
|
-
* True when `raw` is a relative reference form (`::Rest` or `..::Rest`) that the
|
|
75
|
-
* YAML desugar must expand before canonical JSON. Canonical JSON must be FQN; a
|
|
76
|
-
* relative ref surviving into it is `ERR_RELATIVE_REF_IN_CANONICAL`.
|
|
77
|
-
*/
|
|
68
|
+
/** True when `raw` is a relative reference form (`::Rest` or `..::Rest`). */
|
|
78
69
|
export function isRelativeRef(raw: string): boolean {
|
|
79
70
|
return raw.startsWith(PACKAGE_SEPARATOR) || raw.startsWith(PARENT_PREFIX);
|
|
80
71
|
}
|
|
@@ -86,7 +77,7 @@ export function isRelativeRef(raw: string): boolean {
|
|
|
86
77
|
* at the first `.` AFTER the last `::`. Returns `{ owner, tail }` where `tail`
|
|
87
78
|
* includes the leading `.` (or is "" when there is no child suffix).
|
|
88
79
|
*/
|
|
89
|
-
function splitChildTail(raw: string): { owner: string; tail: string } {
|
|
80
|
+
export function splitChildTail(raw: string): { owner: string; tail: string } {
|
|
90
81
|
const lastSep = raw.lastIndexOf(PACKAGE_SEPARATOR);
|
|
91
82
|
const segStart = lastSep === -1 ? 0 : lastSep + PACKAGE_SEPARATOR.length;
|
|
92
83
|
const dotInSeg = raw.indexOf(CHILD_REF_SEPARATOR, segStart);
|
|
@@ -95,8 +86,11 @@ function splitChildTail(raw: string): { owner: string; tail: string } {
|
|
|
95
86
|
}
|
|
96
87
|
|
|
97
88
|
/**
|
|
98
|
-
* Expand an authored reference's OWNER part (no child tail)
|
|
99
|
-
*
|
|
89
|
+
* Expand an authored reference's OWNER part (no child tail) against a package
|
|
90
|
+
* context: `::Rest` → `Rest`, `..::Rest` → the reduced package `::Rest`,
|
|
91
|
+
* `pkg::Name` unchanged, bare `Name` → `<P>::Name`. Throws on parent-relative
|
|
92
|
+
* over-drop. The resolver uses it for the two relative forms only; a bare name is
|
|
93
|
+
* resolved own-package-then-root (see `ownerCandidateKeys`).
|
|
100
94
|
*/
|
|
101
95
|
function expandOwner(owner: string, packageContext: string): string {
|
|
102
96
|
// root-absolute: leading "::" → strip; the remainder is absolute from root.
|
|
@@ -136,73 +130,176 @@ function expandOwner(owner: string, packageContext: string): string {
|
|
|
136
130
|
}
|
|
137
131
|
|
|
138
132
|
/**
|
|
139
|
-
* Expand an authored reference
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
* file-default). Throws on parent-relative over-drop — the caller emits an error.
|
|
133
|
+
* Expand an authored reference against a package context, preserving any FR-024
|
|
134
|
+
* dotted child suffix. Throws on parent-relative over-drop — the caller emits an
|
|
135
|
+
* error.
|
|
143
136
|
*/
|
|
144
137
|
export function expandRef(raw: string, packageContext: string): string {
|
|
145
138
|
const { owner, tail } = splitChildTail(raw);
|
|
146
139
|
return expandOwner(owner, packageContext) + tail;
|
|
147
140
|
}
|
|
148
141
|
|
|
142
|
+
/** A parent-relative reference with more `..::` than its referrer's package has segments. */
|
|
143
|
+
export class RefOverDropError extends Error {
|
|
144
|
+
readonly code = "ERR_BAD_ATTR_VALUE" as const;
|
|
145
|
+
}
|
|
146
|
+
|
|
149
147
|
/**
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
* package-
|
|
153
|
-
* - **FQN** `ref` (contains `::`) → EXACT match on `resolutionKey()`. No
|
|
154
|
-
* bare-tail fallback, so an FQN pointing at one package never binds a
|
|
155
|
-
* same-named object in another.
|
|
156
|
-
* - **bare** `ref` (no `::`) → the referrer's OWN package
|
|
157
|
-
* (`<referrerPkg>::<ref>`), else a **root-level** (empty-package) object
|
|
158
|
-
* whose resolution key IS `ref`. No cross-package bare resolution, no
|
|
159
|
-
* globally-unique scan.
|
|
160
|
-
*
|
|
161
|
-
* Objects keep a BARE `fqn()` per the FR5d cross-port contract, so the canonical
|
|
162
|
-
* FQN accessor is `resolutionKey()` (`<package | fileDefaultPackage>::<name>`) —
|
|
163
|
-
* this mirrors `super-resolve`'s `findInTree`. This is the single matcher the
|
|
164
|
-
* non-super resolvers (origin `@from`/`@of`/`@via` heads, template
|
|
165
|
-
* `@payloadRef`/`@responseRef`, source `@parameterRef`, relationship `@through`)
|
|
166
|
-
* share, so resolution behaves identically everywhere a ref resolves.
|
|
167
|
-
*
|
|
168
|
-
* `referrerPkg` defaults to `""` — the fail-closed FQN-exact-plus-root-level
|
|
169
|
-
* behavior. The LOADER always passes the real referrer package (bare refs there
|
|
170
|
-
* must resolve package-locally); downstream CODEGEN resolvers, which run on
|
|
171
|
-
* already-validated metadata whose refs are FQN, may omit it. An empty
|
|
172
|
-
* `referrerPkg` never binds a bare ref across a package boundary (fail-closed),
|
|
173
|
-
* so omission can only fail to resolve — never mis-resolve.
|
|
148
|
+
* The resolution keys an OWNER reference (no child tail) may bind, in the order to
|
|
149
|
+
* try them (ADR-0059 R2). Every form but bare is one exact key; bare is the
|
|
150
|
+
* referrer's own package, then root-level. Throws RefOverDropError on over-drop.
|
|
174
151
|
*/
|
|
175
|
-
export function
|
|
176
|
-
|
|
177
|
-
if (
|
|
178
|
-
|
|
179
|
-
|
|
152
|
+
export function ownerCandidateKeys(owner: string, referrerPkg: string): readonly string[] {
|
|
153
|
+
if (owner.startsWith(PACKAGE_SEPARATOR)) return [owner.slice(PACKAGE_SEPARATOR.length)];
|
|
154
|
+
if (owner.startsWith(PARENT_PREFIX)) {
|
|
155
|
+
try {
|
|
156
|
+
return [expandOwner(owner, referrerPkg)];
|
|
157
|
+
} catch (err) {
|
|
158
|
+
throw new RefOverDropError((err as Error).message);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (owner.includes(PACKAGE_SEPARATOR)) return [owner];
|
|
162
|
+
return referrerPkg !== "" ? [`${referrerPkg}${PACKAGE_SEPARATOR}${owner}`, owner] : [owner];
|
|
180
163
|
}
|
|
181
164
|
|
|
182
165
|
/**
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
|
|
188
|
-
|
|
166
|
+
* Does root-level object `node` satisfy object reference `ref` (no child tail),
|
|
167
|
+
* declared by a node whose effective package is `referrerPkg`? True when the node's
|
|
168
|
+
* resolution key is the FIRST candidate key that any object could bind — so it
|
|
169
|
+
* agrees with `resolveObjectRef`. Never matches on a short name.
|
|
170
|
+
*/
|
|
171
|
+
export function refMatchesObject(node: MetaData, ref: string, referrerPkg: string): boolean {
|
|
172
|
+
const resolved = resolveObjectRef(node.parent ?? node, ref, referrerPkg).node;
|
|
173
|
+
if (resolved !== undefined) return resolved === node;
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** The root's direct children of `type` (ADR-0039: root has no super). */
|
|
178
|
+
function topLevelOfType(root: MetaData, type: string | undefined): readonly MetaData[] {
|
|
179
|
+
const all = root.children();
|
|
180
|
+
return type === undefined ? all : all.filter((c) => c.type === type);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** Bind an owner reference among `candidates` by exact resolution key (R2). */
|
|
184
|
+
function bindOwner(
|
|
185
|
+
candidates: readonly MetaData[],
|
|
186
|
+
owner: string,
|
|
187
|
+
referrerPkg: string,
|
|
188
|
+
): { node?: MetaData | undefined; error?: string | undefined } {
|
|
189
|
+
let keys: readonly string[];
|
|
190
|
+
try {
|
|
191
|
+
keys = ownerCandidateKeys(owner, referrerPkg);
|
|
192
|
+
} catch (err) {
|
|
193
|
+
return { error: (err as Error).message };
|
|
194
|
+
}
|
|
195
|
+
for (const key of keys) {
|
|
196
|
+
const node = candidates.find((c) => c.resolutionKey() === key);
|
|
197
|
+
if (node !== undefined) return { node };
|
|
198
|
+
}
|
|
199
|
+
return {};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Resolve a metadata OBJECT reference (no child tail) under ADR-0059 R2.
|
|
204
|
+
* `referrerPkg` is the referrer's package (R3). Returns `{ node }` on resolution,
|
|
205
|
+
* `{ error }` for a parent-relative over-drop, or `{}` when nothing matches. The
|
|
206
|
+
* single object resolver every object-ref site shares.
|
|
189
207
|
*/
|
|
190
208
|
export function resolveObjectRef(
|
|
191
209
|
root: MetaData,
|
|
192
210
|
ref: string,
|
|
193
211
|
referrerPkg: string,
|
|
194
|
-
): { node?: MetaData | undefined } {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
212
|
+
): { node?: MetaData | undefined; error?: string | undefined } {
|
|
213
|
+
return bindOwner(topLevelOfType(root, TYPE_OBJECT), ref, referrerPkg);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Resolve a reference that may carry a dotted child tail (ADR-0059 R2): bind the
|
|
218
|
+
* owner among the root's top-level nodes (of `ownerType` when given), then walk the
|
|
219
|
+
* tail by child name. Intermediate segments must be unique among the current node's
|
|
220
|
+
* effective children; the last segment is scoped to `lastType` when given. Returns
|
|
221
|
+
* the owner as well as the target, so a caller can spell the owner and keep the tail.
|
|
222
|
+
*/
|
|
223
|
+
export function resolveRef(
|
|
224
|
+
root: MetaData,
|
|
225
|
+
ref: string,
|
|
226
|
+
referrerPkg: string,
|
|
227
|
+
opts?: { ownerType?: string; lastType?: string },
|
|
228
|
+
): { node?: MetaData | undefined; owner?: MetaData | undefined; error?: string | undefined } {
|
|
229
|
+
const { owner, tail } = splitChildTail(ref);
|
|
230
|
+
const bound = bindOwner(topLevelOfType(root, opts?.ownerType), owner, referrerPkg);
|
|
231
|
+
if (bound.node === undefined) return bound;
|
|
232
|
+
if (tail === "") return { node: bound.node, owner: bound.node };
|
|
233
|
+
const path = tail.slice(CHILD_REF_SEPARATOR.length).split(CHILD_REF_SEPARATOR);
|
|
234
|
+
if (path.some((p) => p === "")) return {};
|
|
235
|
+
let current: MetaData = bound.node;
|
|
236
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
237
|
+
const matches = current.children().filter((c) => c.name === path[i]);
|
|
238
|
+
if (matches.length !== 1) return { owner: bound.node };
|
|
239
|
+
current = matches[0]!;
|
|
198
240
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
241
|
+
const last = path[path.length - 1]!;
|
|
242
|
+
const target = current
|
|
243
|
+
.children()
|
|
244
|
+
.find((c) => c.name === last && (opts?.lastType === undefined || c.type === opts.lastType));
|
|
245
|
+
return { node: target, owner: bound.node };
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* ADR-0059 R7 — resolve a CANONICAL reference with no referrer: strip one leading
|
|
250
|
+
* `::`, match a top-level node's resolution key exactly, then walk any dotted tail.
|
|
251
|
+
* For consumers that hold a resolved model and no referrer context. Never matches
|
|
252
|
+
* on a short name.
|
|
253
|
+
*/
|
|
254
|
+
export function lookupCanonical(
|
|
255
|
+
root: MetaData,
|
|
256
|
+
value: string,
|
|
257
|
+
opts?: { ownerType?: string; lastType?: string },
|
|
258
|
+
): MetaData | undefined {
|
|
259
|
+
const { owner, tail } = splitChildTail(value);
|
|
260
|
+
const exact = owner.startsWith(PACKAGE_SEPARATOR) ? owner : `${PACKAGE_SEPARATOR}${owner}`;
|
|
261
|
+
return resolveRef(root, exact + tail, "", opts).node;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* ADR-0059 R6 — the canonical spelling of a resolved owner, as written from a
|
|
266
|
+
* referrer in package `referrerPkg`: `pkg::Name` for a packaged target, `::Name` for
|
|
267
|
+
* a root-level target from a packaged referrer, `Name` from a root-level referrer.
|
|
268
|
+
* Every spelling resolves to the same node with or without a referrer.
|
|
269
|
+
*/
|
|
270
|
+
export function spellRef(target: MetaData, referrerPkg: string, tail = ""): string {
|
|
271
|
+
const key = target.resolutionKey();
|
|
272
|
+
const rootLevel = !key.includes(PACKAGE_SEPARATOR);
|
|
273
|
+
return (rootLevel && referrerPkg !== "" ? `${PACKAGE_SEPARATOR}${key}` : key) + tail;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/** Effective package of a node: its own `package`, else its file's (ADR-0059 R10). */
|
|
277
|
+
export function effectivePackageOf(node: MetaData): string {
|
|
278
|
+
return node.package ?? node.fileDefaultPackage ?? "";
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/** The top-level node (a direct child of the root) that physically contains `node`. */
|
|
282
|
+
export function topLevelNodeOf(node: MetaData): MetaData {
|
|
283
|
+
let current = node;
|
|
284
|
+
while (current.parent !== undefined && current.parent.parent !== undefined) current = current.parent;
|
|
285
|
+
return current;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* ADR-0059 R3 default — the package a reference attribute resolves in: the effective
|
|
290
|
+
* package of the top-level node that PHYSICALLY declares the attribute. For an
|
|
291
|
+
* attribute `node` inherits through `extends`, that is the ancestor that wrote it,
|
|
292
|
+
* so an inherited reference means what it meant where it was written.
|
|
293
|
+
*/
|
|
294
|
+
export function referrerPackageFor(node: MetaData, attrName: string): string {
|
|
295
|
+
let declarer: MetaData | undefined = node;
|
|
296
|
+
const seen = new Set<MetaData>();
|
|
297
|
+
// ADR-0039: own — locating the layer that physically declares the attr.
|
|
298
|
+
while (declarer !== undefined && !declarer.ownHasAttr(attrName) && !seen.has(declarer)) {
|
|
299
|
+
seen.add(declarer);
|
|
300
|
+
declarer = declarer.superResolved;
|
|
301
|
+
}
|
|
302
|
+
return effectivePackageOf(topLevelNodeOf(declarer ?? node));
|
|
206
303
|
}
|
|
207
304
|
|
|
208
305
|
/**
|
|
@@ -213,6 +310,27 @@ export function resolveObjectRef(
|
|
|
213
310
|
* suggest). Appended to the per-attr unresolved-ref error message.
|
|
214
311
|
*/
|
|
215
312
|
export function didYouMeanHint(root: MetaData, ref: string): string {
|
|
313
|
+
const found = sameNamedObjects(root, ref);
|
|
314
|
+
if (found === undefined) return "";
|
|
315
|
+
return ` An object named "${found.shortName}" exists in: ${found.candidates.join(", ")}. Qualify it with its package (FQN).`;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* The did-you-mean suffix for an UNRESOLVED `extends` ref: the same candidate list
|
|
320
|
+
* as {@link didYouMeanHint}, plus the exact spelling to write. A bare `extends`
|
|
321
|
+
* resolves in the referrer's own package (ADR-0042/0059), so a base declared in
|
|
322
|
+
* another package must be named by its FQN. Returns "" when nothing is named alike.
|
|
323
|
+
*/
|
|
324
|
+
export function didYouMeanSuperHint(root: MetaData, ref: string): string {
|
|
325
|
+
const found = sameNamedObjects(root, ref);
|
|
326
|
+
if (found === undefined) return "";
|
|
327
|
+
return ` An object named "${found.shortName}" exists in: ${found.candidates.join(", ")}. Qualify it with its package (FQN): write extends: "${found.candidates[0]}".`;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function sameNamedObjects(
|
|
331
|
+
root: MetaData,
|
|
332
|
+
ref: string,
|
|
333
|
+
): { shortName: string; candidates: string[] } | undefined {
|
|
216
334
|
const { owner } = splitChildTail(ref);
|
|
217
335
|
const sep = owner.lastIndexOf(PACKAGE_SEPARATOR);
|
|
218
336
|
const shortName = sep === -1 ? owner : owner.slice(sep + PACKAGE_SEPARATOR.length);
|
|
@@ -220,6 +338,31 @@ export function didYouMeanHint(root: MetaData, ref: string): string {
|
|
|
220
338
|
.children()
|
|
221
339
|
.filter((c) => c.type === TYPE_OBJECT && c.name === shortName)
|
|
222
340
|
.map((c) => c.resolutionKey());
|
|
223
|
-
|
|
224
|
-
|
|
341
|
+
return candidates.length === 0 ? undefined : { shortName, candidates };
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* ADR-0059 R7 — the object a reference attribute on `node` names: the owner the loader
|
|
346
|
+
* bound once (`MetaAttr.resolvedTarget`), read RESOLVING so an inherited attribute
|
|
347
|
+
* works (ADR-0039). For a dotted ref (`Week.id`, `Program.weeks`) it is the owner
|
|
348
|
+
* object, not the tail. A tree built without the loader has no recorded target, so the
|
|
349
|
+
* value is resolved here from the package of the node that declared it. Undefined when
|
|
350
|
+
* the attribute is absent, is a list, or names nothing. Consumers use this instead of
|
|
351
|
+
* comparing a reference value to a short name.
|
|
352
|
+
*/
|
|
353
|
+
export function refTarget(node: MetaData, attrName: string): MetaData | undefined {
|
|
354
|
+
const seen = new Set<MetaData>();
|
|
355
|
+
for (let n: MetaData | undefined = node; n !== undefined && !seen.has(n); n = n.superResolved) {
|
|
356
|
+
seen.add(n);
|
|
357
|
+
// ADR-0039: own — locating the layer that declares the attr, walking the super chain.
|
|
358
|
+
const attr = n.ownMetaAttr(attrName);
|
|
359
|
+
if (attr === undefined) continue;
|
|
360
|
+
const recorded = attr.resolvedTarget;
|
|
361
|
+
if (recorded !== undefined) return Array.isArray(recorded) ? undefined : (recorded as MetaData);
|
|
362
|
+
const value = attr.value;
|
|
363
|
+
if (typeof value !== "string" || value === "") return undefined;
|
|
364
|
+
const { owner } = splitChildTail(value);
|
|
365
|
+
return resolveObjectRef(node.root(), owner, effectivePackageOf(topLevelNodeOf(n))).node;
|
|
366
|
+
}
|
|
367
|
+
return undefined;
|
|
225
368
|
}
|
package/src/naming.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { docsBlobUrl } from "./docs-ref.js";
|
|
1
2
|
import type { MetaData } from "./shared/meta-data.js";
|
|
2
3
|
import { TYPE_FIELD } from "./shared/base-types.js";
|
|
3
4
|
import { PACKAGE_SEPARATOR } from "./shared/structural.js";
|
|
@@ -53,18 +54,81 @@ export function toKebabCase(s: string): string {
|
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
/**
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
* the same
|
|
60
|
-
*
|
|
61
|
-
* literal.
|
|
57
|
+
* How a field with no `@column` becomes a column name. One project setting,
|
|
58
|
+
* `naming.columns` in `.metaobjects/config.json`, read by every port (ADR-0058): the
|
|
59
|
+
* loaded root carries it ({@link physicalNamingOf}), so codegen, migrate, the runtime
|
|
60
|
+
* and verify resolve the same name. Not a metadata attr: `@column` is the per-field
|
|
61
|
+
* override, this is the rule for the rest.
|
|
62
62
|
*/
|
|
63
63
|
export type ColumnNamingStrategy = "snake_case" | "literal" | "kebab-case";
|
|
64
64
|
|
|
65
|
-
/**
|
|
65
|
+
/** Every column-naming rule, in the order the conformance corpus lists them. */
|
|
66
|
+
export const COLUMN_NAMING_STRATEGIES: readonly ColumnNamingStrategy[] = ["snake_case", "literal", "kebab-case"];
|
|
67
|
+
|
|
68
|
+
/** The default in EVERY port: what `meta migrate` has always created. */
|
|
66
69
|
export const DEFAULT_COLUMN_NAMING_STRATEGY: ColumnNamingStrategy = "snake_case";
|
|
67
70
|
|
|
71
|
+
/**
|
|
72
|
+
* How an object with no physical-name attribute (`@table`, `@view`, ...) and no source name
|
|
73
|
+
* gets its table name: the entity (or source) name cased by this rule. The entity-name
|
|
74
|
+
* fallback keeps the frozen legacy pluralizer under every rule ({@link pluralizeTableNameLegacy}).
|
|
75
|
+
*/
|
|
76
|
+
export type TableNamingRule = "snake_case" | "literal";
|
|
77
|
+
|
|
78
|
+
/** Every table-naming rule. */
|
|
79
|
+
export const TABLE_NAMING_RULES: readonly TableNamingRule[] = ["snake_case", "literal"];
|
|
80
|
+
|
|
81
|
+
/** The project's physical-naming rules: `naming` in `.metaobjects/config.json` (ADR-0058). */
|
|
82
|
+
export interface PhysicalNaming {
|
|
83
|
+
readonly columns: ColumnNamingStrategy;
|
|
84
|
+
readonly tables: TableNamingRule;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** The default in every port, equal to what `meta migrate` has always created. */
|
|
88
|
+
export const DEFAULT_PHYSICAL_NAMING: PhysicalNaming = Object.freeze({
|
|
89
|
+
columns: DEFAULT_COLUMN_NAMING_STRATEGY,
|
|
90
|
+
tables: "snake_case",
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The physical-naming rules for the tree `node` belongs to: what its root carries, set by the
|
|
95
|
+
* project-aware load from `.metaobjects/config.json`, else {@link DEFAULT_PHYSICAL_NAMING}.
|
|
96
|
+
* Read through the root, not passed around, so every consumer of one loaded model resolves
|
|
97
|
+
* the same physical names.
|
|
98
|
+
*/
|
|
99
|
+
export function physicalNamingOf(node: MetaData): PhysicalNaming {
|
|
100
|
+
const naming = (node.root() as { physicalNaming?: PhysicalNaming }).physicalNaming;
|
|
101
|
+
return naming ?? DEFAULT_PHYSICAL_NAMING;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** The migration guide every refusal of a former naming source names (ADR-0058). */
|
|
105
|
+
export const PHYSICAL_NAMING_MIGRATION_GUIDE_PATH = "docs/features/migrations/physical-naming.md";
|
|
106
|
+
|
|
107
|
+
/** The guide on `main`: the default when a caller has no release to pin to. */
|
|
108
|
+
export const PHYSICAL_NAMING_MIGRATION_GUIDE = docsBlobUrl(PHYSICAL_NAMING_MIGRATION_GUIDE_PATH);
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* What a former column-naming source prints when it is still used (ADR-0058): the one
|
|
112
|
+
* setting, where it goes, and the guide. Same words in every port. `what` names the source
|
|
113
|
+
* exactly; `value` is the rule it gave, carried into the example so the move is mechanical.
|
|
114
|
+
*/
|
|
115
|
+
export function physicalNamingRemovedMessage(
|
|
116
|
+
what: string,
|
|
117
|
+
value: string = "literal",
|
|
118
|
+
guide: string = PHYSICAL_NAMING_MIGRATION_GUIDE,
|
|
119
|
+
): string {
|
|
120
|
+
return (
|
|
121
|
+
`${what} was removed in 1.1.0. Column and table naming is one project setting now, read by ` +
|
|
122
|
+
`every port: "naming" in .metaobjects/config.json, for example "naming": { "columns": "${value}" }. ` +
|
|
123
|
+
`Move it there and delete ${what}. See ${guide}`
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** `name` cased by the table rule. */
|
|
128
|
+
export function applyTableNamingRule(name: string, rule: TableNamingRule): string {
|
|
129
|
+
return rule === "literal" ? name : toSnakeCase(name);
|
|
130
|
+
}
|
|
131
|
+
|
|
68
132
|
export function applyColumnNamingStrategy(name: string, strategy: ColumnNamingStrategy): string {
|
|
69
133
|
switch (strategy) {
|
|
70
134
|
case "literal": return name;
|
|
@@ -238,13 +302,13 @@ export function resolveTableName(entity: MetaData): string {
|
|
|
238
302
|
const source = primaryRdbSource(entity);
|
|
239
303
|
if (source !== undefined) return source.physicalName;
|
|
240
304
|
// Frozen: this is a DEFAULT PHYSICAL name, not an API-surface name. See
|
|
241
|
-
// pluralizeTableNameLegacy's doc.
|
|
242
|
-
return pluralizeTableNameLegacy(
|
|
305
|
+
// pluralizeTableNameLegacy's doc. Cased by the project's table rule (ADR-0058).
|
|
306
|
+
return pluralizeTableNameLegacy(applyTableNamingRule(entity.name, physicalNamingOf(entity).tables));
|
|
243
307
|
}
|
|
244
308
|
|
|
245
309
|
export function resolveColumnName(
|
|
246
310
|
field: MetaData,
|
|
247
|
-
strategy: ColumnNamingStrategy =
|
|
311
|
+
strategy: ColumnNamingStrategy = physicalNamingOf(field).columns,
|
|
248
312
|
): string {
|
|
249
313
|
// ADR-0039: resolving — a concrete field may inherit @column via extends.
|
|
250
314
|
const col = field.attr(FIELD_ATTR_COLUMN);
|
|
@@ -335,7 +399,7 @@ export interface EntityNameMap {
|
|
|
335
399
|
|
|
336
400
|
export function buildNameMap(
|
|
337
401
|
entity: MetaData,
|
|
338
|
-
strategy: ColumnNamingStrategy =
|
|
402
|
+
strategy: ColumnNamingStrategy = physicalNamingOf(entity).columns,
|
|
339
403
|
): EntityNameMap {
|
|
340
404
|
const jsToDb = new Map<string, string>();
|
|
341
405
|
const dbToJs = new Map<string, string>();
|
package/src/object-serializer.ts
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
DATA_TYPE_OBJECT,
|
|
21
21
|
} from "./data-type.js";
|
|
22
22
|
import { TYPE_OBJECT } from "./shared/base-types.js";
|
|
23
|
+
import { lookupCanonical } from "./naming-refs.js";
|
|
23
24
|
|
|
24
25
|
const TYPE_DISCRIMINATOR = "@type";
|
|
25
26
|
|
|
@@ -142,16 +143,13 @@ function coerceNumeric(raw: unknown): unknown {
|
|
|
142
143
|
return raw;
|
|
143
144
|
}
|
|
144
145
|
|
|
145
|
-
/** Resolve an object-typed field's `@objectRef` to its target MetaObject
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
* raw value through — best-effort). */
|
|
146
|
+
/** Resolve an object-typed field's `@objectRef` to its target MetaObject. The loader
|
|
147
|
+
* has already rewritten the ref to its canonical spelling (ADR-0059 R5), so it is
|
|
148
|
+
* looked up exactly, with no referrer (R7). Returns undefined when there is no ref
|
|
149
|
+
* or it does not resolve (caller passes the raw value through — best-effort). */
|
|
149
150
|
function resolveObjectRef(field: MetaField, mo: MetaObject): MetaObject | undefined {
|
|
150
151
|
const ref = field.objectRef;
|
|
151
152
|
if (ref === undefined) return undefined;
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const found: MetaData | undefined = mo.root().ownChildByTypeAndName(TYPE_OBJECT, ref);
|
|
155
|
-
if (found === undefined) return undefined;
|
|
156
|
-
return found as MetaObject;
|
|
153
|
+
const found: MetaData | undefined = lookupCanonical(mo.root(), ref, { ownerType: TYPE_OBJECT });
|
|
154
|
+
return found as MetaObject | undefined;
|
|
157
155
|
}
|