@metaobjectsdev/metadata 0.23.2 → 0.24.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.
Files changed (131) hide show
  1. package/dist/attr-schema-validate.d.ts.map +1 -1
  2. package/dist/attr-schema-validate.js +17 -2
  3. package/dist/attr-schema-validate.js.map +1 -1
  4. package/dist/core/field/field-constants.d.ts +18 -5
  5. package/dist/core/field/field-constants.d.ts.map +1 -1
  6. package/dist/core/field/field-constants.js +21 -5
  7. package/dist/core/field/field-constants.js.map +1 -1
  8. package/dist/core/field/field-definition.embedded.d.ts.map +1 -1
  9. package/dist/core/field/field-definition.embedded.js +8 -3
  10. package/dist/core/field/field-definition.embedded.js.map +1 -1
  11. package/dist/core/field/validate-field-mutability.d.ts +22 -0
  12. package/dist/core/field/validate-field-mutability.d.ts.map +1 -0
  13. package/dist/core/field/validate-field-mutability.js +208 -0
  14. package/dist/core/field/validate-field-mutability.js.map +1 -0
  15. package/dist/core/identity/identity-constants.d.ts +12 -0
  16. package/dist/core/identity/identity-constants.d.ts.map +1 -1
  17. package/dist/core/identity/identity-constants.js +15 -0
  18. package/dist/core/identity/identity-constants.js.map +1 -1
  19. package/dist/core/object/object-definition.embedded.js +2 -2
  20. package/dist/core/object/object-definition.embedded.js.map +1 -1
  21. package/dist/core/requirement/meta-requirement.d.ts +1 -2
  22. package/dist/core/requirement/meta-requirement.d.ts.map +1 -1
  23. package/dist/core/requirement/meta-requirement.js +2 -6
  24. package/dist/core/requirement/meta-requirement.js.map +1 -1
  25. package/dist/core/requirement/requirement-constants.d.ts +3 -8
  26. package/dist/core/requirement/requirement-constants.d.ts.map +1 -1
  27. package/dist/core/requirement/requirement-constants.js +2 -9
  28. package/dist/core/requirement/requirement-constants.js.map +1 -1
  29. package/dist/core/requirement/requirement-definition.embedded.d.ts.map +1 -1
  30. package/dist/core/requirement/requirement-definition.embedded.js +8 -46
  31. package/dist/core/requirement/requirement-definition.embedded.js.map +1 -1
  32. package/dist/core/requirement/resolve-claim.d.ts +37 -0
  33. package/dist/core/requirement/resolve-claim.d.ts.map +1 -0
  34. package/dist/core/requirement/resolve-claim.js +83 -0
  35. package/dist/core/requirement/resolve-claim.js.map +1 -0
  36. package/dist/core-types.d.ts.map +1 -1
  37. package/dist/core-types.js +10 -10
  38. package/dist/core-types.js.map +1 -1
  39. package/dist/errors.d.ts +2 -2
  40. package/dist/errors.d.ts.map +1 -1
  41. package/dist/errors.js +16 -2
  42. package/dist/errors.js.map +1 -1
  43. package/dist/index.d.ts +7 -1
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +13 -1
  46. package/dist/index.js.map +1 -1
  47. package/dist/library/library-sources.d.ts +11 -0
  48. package/dist/library/library-sources.d.ts.map +1 -1
  49. package/dist/library/library-sources.js +13 -0
  50. package/dist/library/library-sources.js.map +1 -1
  51. package/dist/loader/meta-data-loader.d.ts.map +1 -1
  52. package/dist/loader/meta-data-loader.js +10 -9
  53. package/dist/loader/meta-data-loader.js.map +1 -1
  54. package/dist/loader/validation-passes.js +2 -2
  55. package/dist/loader/validation-passes.js.map +1 -1
  56. package/dist/naming-refs.d.ts.map +1 -1
  57. package/dist/naming-refs.js +2 -3
  58. package/dist/naming-refs.js.map +1 -1
  59. package/dist/parser-core.d.ts.map +1 -1
  60. package/dist/parser-core.js +22 -5
  61. package/dist/parser-core.js.map +1 -1
  62. package/dist/persistence/origin/meta-origin.d.ts +0 -10
  63. package/dist/persistence/origin/meta-origin.d.ts.map +1 -1
  64. package/dist/persistence/origin/meta-origin.js +1 -15
  65. package/dist/persistence/origin/meta-origin.js.map +1 -1
  66. package/dist/persistence/origin/origin-constants.d.ts +2 -4
  67. package/dist/persistence/origin/origin-constants.d.ts.map +1 -1
  68. package/dist/persistence/origin/origin-constants.js +9 -7
  69. package/dist/persistence/origin/origin-constants.js.map +1 -1
  70. package/dist/persistence/origin/origin-definition.embedded.d.ts.map +1 -1
  71. package/dist/persistence/origin/origin-definition.embedded.js +0 -16
  72. package/dist/persistence/origin/origin-definition.embedded.js.map +1 -1
  73. package/dist/registry-manifest.d.ts +9 -4
  74. package/dist/registry-manifest.d.ts.map +1 -1
  75. package/dist/registry-manifest.js +9 -4
  76. package/dist/registry-manifest.js.map +1 -1
  77. package/dist/retired-vocabulary.d.ts +92 -0
  78. package/dist/retired-vocabulary.d.ts.map +1 -0
  79. package/dist/retired-vocabulary.js +204 -0
  80. package/dist/retired-vocabulary.js.map +1 -0
  81. package/dist/super-resolve.d.ts +33 -0
  82. package/dist/super-resolve.d.ts.map +1 -1
  83. package/dist/super-resolve.js +56 -1
  84. package/dist/super-resolve.js.map +1 -1
  85. package/dist/template/prompt-definition.embedded.js +1 -1
  86. package/dist/template/prompt-definition.embedded.js.map +1 -1
  87. package/dist/template/template-constants.d.ts +6 -0
  88. package/dist/template/template-constants.d.ts.map +1 -1
  89. package/dist/template/template-constants.js +24 -1
  90. package/dist/template/template-constants.js.map +1 -1
  91. package/dist/template/template-definition.embedded.d.ts.map +1 -1
  92. package/dist/template/template-definition.embedded.js +34 -21
  93. package/dist/template/template-definition.embedded.js.map +1 -1
  94. package/dist/vocabulary-rewrite.d.ts +33 -0
  95. package/dist/vocabulary-rewrite.d.ts.map +1 -0
  96. package/dist/vocabulary-rewrite.js +310 -0
  97. package/dist/vocabulary-rewrite.js.map +1 -0
  98. package/package.json +6 -1
  99. package/src/attr-schema-validate.ts +17 -2
  100. package/src/core/field/field-constants.ts +24 -5
  101. package/src/core/field/field-definition.embedded.ts +8 -3
  102. package/src/core/field/validate-field-mutability.ts +259 -0
  103. package/src/core/identity/identity-constants.ts +16 -0
  104. package/src/core/object/object-definition.embedded.ts +2 -2
  105. package/src/core/requirement/meta-requirement.ts +1 -7
  106. package/src/core/requirement/requirement-constants.ts +2 -9
  107. package/src/core/requirement/requirement-definition.embedded.ts +8 -46
  108. package/src/core/requirement/resolve-claim.ts +98 -0
  109. package/src/core-types.ts +9 -10
  110. package/src/errors.ts +16 -2
  111. package/src/index.ts +29 -1
  112. package/src/library/library-sources.ts +14 -0
  113. package/src/loader/meta-data-loader.ts +10 -9
  114. package/src/loader/validation-passes.ts +3 -3
  115. package/src/naming-refs.ts +1 -3
  116. package/src/parser-core.ts +27 -5
  117. package/src/persistence/origin/meta-origin.ts +0 -16
  118. package/src/persistence/origin/origin-constants.ts +9 -8
  119. package/src/persistence/origin/origin-definition.embedded.ts +0 -16
  120. package/src/registry-manifest.ts +9 -4
  121. package/src/retired-vocabulary.ts +280 -0
  122. package/src/super-resolve.ts +59 -1
  123. package/src/template/prompt-definition.embedded.ts +1 -1
  124. package/src/template/template-constants.ts +27 -1
  125. package/src/template/template-definition.embedded.ts +34 -21
  126. package/src/vocabulary-rewrite.ts +346 -0
  127. package/dist/core/field/validate-field-readonly.d.ts +0 -9
  128. package/dist/core/field/validate-field-readonly.d.ts.map +0 -1
  129. package/dist/core/field/validate-field-readonly.js +0 -128
  130. package/dist/core/field/validate-field-readonly.js.map +0 -1
  131. package/src/core/field/validate-field-readonly.ts +0 -154
package/src/errors.ts CHANGED
@@ -138,8 +138,9 @@ export const ERROR_CODES = [
138
138
  "ERR_PHYSICAL_NAME_KIND_MISMATCH",
139
139
  "ERR_PHYSICAL_NAME_MULTIPLE",
140
140
  // FR-013 — field-level @readOnly cross-attribute validation.
141
+ "ERR_MUTABILITY_AUTOSET_CONFLICT",
142
+ "ERR_MUTABILITY_DOWNGRADE",
141
143
  "ERR_READONLY_ASSIGNED_PRIMARY",
142
- "ERR_READONLY_DOWNGRADE",
143
144
  // FR-015 — source.rdb @parameterRef typed-input validation.
144
145
  "ERR_PARAMETER_REF_UNRESOLVED",
145
146
  "ERR_PARAMETER_REF_NOT_VALUE_OBJECT",
@@ -208,6 +209,18 @@ export const ERROR_CODES = [
208
209
  // an integer array. An array-of-enum stays string-backed: drop @intValueMap,
209
210
  // or make the field scalar.
210
211
  "ERR_ENUM_INT_VALUE_MAP_ARRAY",
212
+ // Phase-1 metadata-source-resolution — a path source declared in
213
+ // .metaobjects/config.json does not exist on disk.
214
+ "ERR_SOURCE_UNRESOLVED",
215
+ // Phase-1 metadata-source-resolution — a declared source kind (resource or
216
+ // package) is not supported by this toolchain.
217
+ "ERR_SOURCE_KIND_UNSUPPORTED",
218
+ // Phase-1 metadata-source-resolution — a scope include/exclude package
219
+ // pattern is malformed (empty pattern or empty :: segment).
220
+ "ERR_SCOPE_PATTERN_INVALID",
221
+ // Phase-1 metadata-source-resolution — no metadata collection was discovered:
222
+ // no config declaring sources, and no default metaobjects/ directory.
223
+ "ERR_COLLECTION_NOT_FOUND",
211
224
  "ERR_UNKNOWN",
212
225
  ] as const;
213
226
 
@@ -226,7 +239,8 @@ export const WARNING_CODES = [
226
239
  // FR-013 — @readOnly on a field child of object.value. The persistence
227
240
  // implication does not apply to value-objects; the attr is retained for
228
241
  // language-specific record/struct treatment (e.g. Kotlin `val` vs `var`).
229
- "WARN_READONLY_VALUE_OBJECT",
242
+ "WARN_MUTABILITY_VALUE_OBJECT",
243
+ "WARN_MUTABILITY_READONLY_HOST",
230
244
  // #208 — a host object whose source carries @unmanaged also declares an
231
245
  // origin.*-bearing (derived) field. Deliberate asymmetry with
232
246
  // ERR_ORIGIN_UNDER_SQL_BODY: @unmanaged acts on nothing (the tool never
package/src/index.ts CHANGED
@@ -27,6 +27,14 @@ export * from "./shared/base-types.js";
27
27
  export * from "./shared/structural.js";
28
28
  export * from "./core/object/object-constants.js";
29
29
  export * from "./core/field/field-constants.js";
30
+ // FR-037 R1 — THE mutability accessors. Every consumer deciding "may this be
31
+ // written, and when?" must go through these rather than reading the attr, so the
32
+ // absent-means-readWrite default lives in exactly one place per port.
33
+ export {
34
+ fieldMutability,
35
+ isReadOnlyMutability,
36
+ isWriteOnceMutability,
37
+ } from "./core/field/validate-field-mutability.js";
30
38
  export * from "./core/attr/attr-constants.js";
31
39
  export * from "./core/documentation/doc-constants.js";
32
40
  export * from "./core/validator/validator-constants.js";
@@ -34,6 +42,13 @@ export * from "./core/identity/identity-constants.js";
34
42
  export * from "./core/index/index-constants.js";
35
43
  export * from "./core/requirement/requirement-constants.js";
36
44
  export { MetaRequirement } from "./core/requirement/meta-requirement.js";
45
+ // Shared `@implementedBy` resolution — one resolver for the CLI's requirement
46
+ // checks and codegen's requirement-test fan-out (FR-038).
47
+ export {
48
+ resolveClaim,
49
+ resolveClaimTarget,
50
+ resolveMember,
51
+ } from "./core/requirement/resolve-claim.js";
37
52
  export * from "./core/relationship/relationship-constants.js";
38
53
  export * from "./core/query/query-constants.js";
39
54
  export * from "./persistence/source/source-constants.js";
@@ -121,7 +136,6 @@ export {
121
136
  MetaOrigin,
122
137
  MetaPassthroughOrigin,
123
138
  MetaAggregateOrigin,
124
- MetaCollectionOrigin,
125
139
  MetaComputedOrigin,
126
140
  MetaFirstOrigin,
127
141
  } from "./persistence/origin/meta-origin.js";
@@ -273,3 +287,17 @@ export {
273
287
  stripPackage,
274
288
  } from "./naming.js";
275
289
  export type { EntityNameMap, ColumnNamingStrategy } from "./naming.js";
290
+
291
+ // Retired vocabulary: the map the loader reads to explain a retirement, and the raw-document
292
+ // rewriter `meta upgrade` drives from the SAME entries — so the error message and the fix
293
+ // cannot drift apart.
294
+ export {
295
+ RETIRED_VOCABULARY,
296
+ retiredAttr,
297
+ retiredAttrValue,
298
+ retiredSubType,
299
+ retirementHint,
300
+ } from "./retired-vocabulary.js";
301
+ export type { RetirementNote, RetiredEntry, VocabularyRewrite } from "./retired-vocabulary.js";
302
+ export { rewriteDocument } from "./vocabulary-rewrite.js";
303
+ export type { RewriteResult, RewriteChange, RewriteRefusal, RewriteOpts } from "./vocabulary-rewrite.js";
@@ -51,6 +51,20 @@ function getLibraryDir(): string | undefined {
51
51
  return (_cache ??= { dir: libraryDirOnDisk() }).dir;
52
52
  }
53
53
 
54
+ /**
55
+ * The library package names this build ships, sorted.
56
+ *
57
+ * `librarySources` skips an unrecognised package silently — the right behaviour for a
58
+ * programmatic caller asking for something a given version may not ship. A name a human
59
+ * typed into a config file is a different case: skipping it silently resurfaces later as
60
+ * `ERR_UNRESOLVED_SUPER` pointing at the adopter's own metadata, which is the wrong place
61
+ * to go looking. Config readers use this to refuse an unknown name and say what IS
62
+ * available (Python's `project_config` draws the same line, in the same place).
63
+ */
64
+ export function knownLibraryPackages(): string[] {
65
+ return Object.keys(REFS_BY_PACKAGE).sort();
66
+ }
67
+
54
68
  /**
55
69
  * Returns a list of `MetaDataSource` instances for the requested library packages.
56
70
  *
@@ -24,10 +24,10 @@ import { validateSourceRoles } from "../persistence/source/validate-source-roles
24
24
  import { validateSourceEscapes } from "../persistence/source/validate-source-escapes.js";
25
25
  import { validateSourcePhysicalNames } from "../persistence/source/validate-source-physical-names.js";
26
26
  import { validateSourceParameterRef } from "../persistence/source/validate-source-parameter-ref.js";
27
- import { validateFieldReadOnly } from "../core/field/validate-field-readonly.js";
27
+ import { validateFieldMutability } from "../core/field/validate-field-mutability.js";
28
28
  import { validateEnumNormalizeAmbiguity } from "../core/field/validate-enum-normalize-ambiguity.js";
29
29
  import { validateDiscriminator } from "../core/object/validate-discriminator.js";
30
- import { resolveDeferredSupers } from "../super-resolve.js";
30
+ import { resolveDeferredSupers, EXTENDS_TARGET_MISMATCH_RULE } from "../super-resolve.js";
31
31
  import { validateSubtypeRules } from "../subtype-rules.js";
32
32
  import { validateMaxOccurs } from "../validate-max-occurs.js";
33
33
  import { validateIdentityPassthrough } from "../core/identity/validate-identity-passthrough.js";
@@ -534,7 +534,7 @@ export class MetaDataLoader {
534
534
  const t = failure.target;
535
535
  errors.push(
536
536
  new ParseError(
537
- `the extends target '${failure.ref}' is ${t?.type}.${t?.subType} but the extending node '${failure.nodeFqn}' is ${r?.type}.${r?.subType} — a dotted extends must target a node of the same type and subtype`,
537
+ `the extends target '${failure.ref}' is ${t?.type}.${t?.subType} but the extending node '${failure.nodeFqn}' is ${r?.type}.${r?.subType} — ${EXTENDS_TARGET_MISMATCH_RULE}`,
538
538
  {
539
539
  code: "ERR_EXTENDS_TARGET_MISMATCH",
540
540
  source: resolvedSource(failure.source, failure.nodeFqn, failure.ref),
@@ -650,12 +650,13 @@ export class MetaDataLoader {
650
650
  errors.push(...physicalNameResult.errors);
651
651
  envelopeWarnings.push(...physicalNameResult.warnings);
652
652
 
653
- // FR-013 — field-level @readOnly cross-attribute rules
654
- // (ERR_READONLY_DOWNGRADE / ERR_READONLY_ASSIGNED_PRIMARY /
655
- // WARN_READONLY_VALUE_OBJECT).
656
- const readOnlyResult = validateFieldReadOnly(root);
657
- errors.push(...readOnlyResult.errors);
658
- envelopeWarnings.push(...readOnlyResult.warnings);
653
+ // FR-037 R1 — field-level @mutability cross-attribute rules
654
+ // (ERR_MUTABILITY_AUTOSET_CONFLICT / ERR_MUTABILITY_DOWNGRADE /
655
+ // ERR_READONLY_ASSIGNED_PRIMARY / WARN_MUTABILITY_VALUE_OBJECT /
656
+ // WARN_MUTABILITY_READONLY_HOST).
657
+ const mutabilityResult = validateFieldMutability(root);
658
+ errors.push(...mutabilityResult.errors);
659
+ envelopeWarnings.push(...mutabilityResult.warnings);
659
660
 
660
661
  // Authoring guard — a field.enum vocabulary that is ambiguous under the
661
662
  // default `@normalize: strip` (a member equal to the concatenation of
@@ -1109,8 +1109,8 @@ export function validateOriginPaths(root: MetaData): ParseError[] {
1109
1109
  for (const field of obj.ownChildren().filter((c) => c.type === TYPE_FIELD)) {
1110
1110
  for (const origin of field.ownChildren().filter((c) => c.type === TYPE_ORIGIN)) {
1111
1111
  // #210 — assembly origins live on projections. A value-hosted field may
1112
- // not carry origin.aggregate / origin.computed / origin.collection /
1113
- // origin.first: a value is constructed — by a caller or by embedding —
1112
+ // not carry origin.aggregate / origin.computed / origin.first:
1113
+ // a value is constructed — by a caller or by embedding —
1114
1114
  // never assembled from a backing store. origin.passthrough STAYS legal
1115
1115
  // on a value (FR-015 parameter lineage; the B5 exemption below).
1116
1116
  if (
@@ -1997,7 +1997,7 @@ function checkFilterClauses(
1997
1997
  // - a plain (extends-bound / no-origin) or origin.passthrough or origin.computed
1998
1998
  // field → addressable (a base/joined column, or an inlined row-level expression).
1999
1999
  // - an aggregate-derived field (origin.aggregate — count/sum/…/any/all/collect —
2000
- // or origin.first / origin.collection) → NOT addressable: a WHERE runs before
2000
+ // or origin.first) → NOT addressable: a WHERE runs before
2001
2001
  // aggregation, so it cannot see an aggregate (post-aggregate filtering is HAVING,
2002
2002
  // a separate later extension). Fail-closed → ERR_BAD_ATTR_FILTER.
2003
2003
  // - a ref naming no declared field → dangling → ERR_BAD_ATTR_FILTER.
@@ -37,7 +37,6 @@ import {
37
37
  ORIGIN_PASSTHROUGH_ATTR_VIA,
38
38
  ORIGIN_AGGREGATE_ATTR_OF,
39
39
  ORIGIN_AGGREGATE_ATTR_VIA,
40
- ORIGIN_COLLECTION_ATTR_VIA,
41
40
  } from "./persistence/origin/origin-constants.js";
42
41
  import { SOURCE_ATTR_PARAMETER_REF } from "./persistence/source/source-constants.js";
43
42
  import { TEMPLATE_ATTR_PAYLOAD_REF, TEMPLATE_ATTR_RESPONSE_REF } from "./template/template-constants.js";
@@ -63,10 +62,9 @@ export const REF_BEARING_ATTR_NAMES: ReadonlySet<string> = new Set<string>([
63
62
  RELATIONSHIP_ATTR_THROUGH, // ADR-0042: the M:N junction ref joins the desugar+resolution set.
64
63
  IDENTITY_REFERENCE_ATTR_REFERENCES,
65
64
  ORIGIN_PASSTHROUGH_ATTR_FROM,
66
- ORIGIN_PASSTHROUGH_ATTR_VIA, // = ORIGIN_AGGREGATE_ATTR_VIA = ORIGIN_COLLECTION_ATTR_VIA ("via")
65
+ ORIGIN_PASSTHROUGH_ATTR_VIA, // = ORIGIN_AGGREGATE_ATTR_VIA ("via")
67
66
  ORIGIN_AGGREGATE_ATTR_OF,
68
67
  ORIGIN_AGGREGATE_ATTR_VIA,
69
- ORIGIN_COLLECTION_ATTR_VIA,
70
68
  SOURCE_ATTR_PARAMETER_REF,
71
69
  TEMPLATE_ATTR_PAYLOAD_REF,
72
70
  TEMPLATE_ATTR_RESPONSE_REF,
@@ -29,9 +29,16 @@ import { MetaRoot } from "./shared/meta-root.js";
29
29
  import { MetaAttr } from "./core/attr/meta-attr.js";
30
30
  import { canonicalSerialize, inferAttrSubType } from "./serializer-json.js";
31
31
  import { ParseError, type ErrorCode } from "./errors.js";
32
+ // #337 — see retired-vocabulary.ts. Diagnostic only; no load outcome changes.
33
+ import { retiredSubType, retirementHint } from "./retired-vocabulary.js";
32
34
  import { resolvedSource, type ErrorSource, type LoaderWarning, type Contributor } from "./source.js";
33
35
  import { semanticDiff } from "./semantic-diff.js";
34
- import { resolveSuperRef, isChildTargetingRef } from "./super-resolve.js";
36
+ import {
37
+ resolveSuperRef,
38
+ isChildTargetingRef,
39
+ extendsTargetCompatible,
40
+ EXTENDS_TARGET_MISMATCH_RULE,
41
+ } from "./super-resolve.js";
35
42
  import { JsonPathBuilder } from "./json-path.js";
36
43
  import { getYamlPosition, type YamlPosition } from "./core/yaml-positions.js";
37
44
  import {
@@ -422,8 +429,13 @@ export function buildTree(parsed: unknown, opts: ParseOptions): ParseResult {
422
429
  _currentPath!.pushKey(rootKey);
423
430
  const src = errSource();
424
431
  _currentPath!.pop();
432
+ // Same hint at the ROOT door. No retired subtype is root-legal today, so this arm
433
+ // is currently unreachable — wired anyway, because "fixed one door, left the
434
+ // other" is how a rule ends up half-true and passing every probe.
435
+ const retiredRoot = retiredSubType(rootType, rootSubType);
425
436
  throw new ParseError(
426
- `Unknown root type "${rootType}.${rootSubType}" — not registered`,
437
+ `Unknown root type "${rootType}.${rootSubType}" — ` +
438
+ (retiredRoot !== undefined ? retirementHint(retiredRoot) : "not registered"),
427
439
  { code: rootTypeCode, source: src },
428
440
  );
429
441
  }
@@ -607,12 +619,18 @@ function parseNodeFresh(
607
619
  // FR-024 — a dotted child-targeting ref must resolve to a node of the
608
620
  // SAME type and subtype as the extending node. Dotted-only check; the
609
621
  // shipped top-level extends behavior is unchanged.
622
+ //
623
+ // The predicate is IMPORTED, not restated: this eager path and
624
+ // super-resolve's deferred path are two doors onto one rule, and they
625
+ // previously held independent copies of the boolean — so #310's relaxation
626
+ // applied to one would have left the other refusing, on whichever path a
627
+ // given loader configuration happens to take.
610
628
  if (
611
629
  isChildTargetingRef(model.superRef) &&
612
- (superModel.type !== model.type || superModel.subType !== model.subType)
630
+ !extendsTargetCompatible(model, superModel)
613
631
  ) {
614
632
  throw new ParseError(
615
- `the extends target '${model.superRef}' is ${superModel.type}.${superModel.subType} but the extending node '${model.fqn()}' is ${model.type}.${model.subType} — a dotted extends must target a node of the same type and subtype`,
633
+ `the extends target '${model.superRef}' is ${superModel.type}.${superModel.subType} but the extending node '${model.fqn()}' is ${model.type}.${model.subType} — ${EXTENDS_TARGET_MISMATCH_RULE}`,
616
634
  {
617
635
  code: "ERR_EXTENDS_TARGET_MISMATCH",
618
636
  source: resolvedSource(errSource(), model.fqn(), model.superRef),
@@ -1253,9 +1271,13 @@ function processChildren(
1253
1271
  const childTypeCode = explicit && registry.allSubTypesOf(childType).length > 0
1254
1272
  ? "ERR_UNKNOWN_SUBTYPE" as const
1255
1273
  : "ERR_UNKNOWN_TYPE" as const;
1274
+ // #337: a subtype we RETIRED (origin.collection) gets its retirement and its
1275
+ // migration, not a bare "not registered" that reads like a broken install.
1276
+ const retiredChild = retiredSubType(childType, childSubType);
1256
1277
  errors.push(
1257
1278
  new ParseError(
1258
- `Unknown type "${childType}.${childSubType}" — not registered`,
1279
+ `Unknown type "${childType}.${childSubType}" — ` +
1280
+ (retiredChild !== undefined ? retirementHint(retiredChild) : "not registered"),
1259
1281
  { code: childTypeCode, source: errSource() },
1260
1282
  ),
1261
1283
  );
@@ -14,7 +14,6 @@ import {
14
14
  ORIGIN_AGGREGATE_ATTR_AGG,
15
15
  ORIGIN_AGGREGATE_ATTR_OF,
16
16
  ORIGIN_AGGREGATE_ATTR_VIA,
17
- ORIGIN_COLLECTION_ATTR_VIA,
18
17
  ORIGIN_COMPUTED_ATTR_EXPR,
19
18
  ORIGIN_FIRST_ATTR_OF,
20
19
  ORIGIN_FIRST_ATTR_VIA,
@@ -77,21 +76,6 @@ export class MetaAggregateOrigin extends MetaOrigin {
77
76
  }
78
77
  }
79
78
 
80
- /**
81
- * Collection origin — the (array) field's value is a relationship-derived
82
- * array of nested view-objects (FR-004 R4). Carries `@via` (required): the
83
- * dotted relationship path the collection walks (e.g. "Author.posts"), or a
84
- * wildcard-prefixed selector for a package-spanning collection (e.g. "*.User").
85
- */
86
- export class MetaCollectionOrigin extends MetaOrigin {
87
- /** The dotted relationship path (or wildcard selector) this collection is sourced from. */
88
- get via(): string | undefined {
89
- // ADR-0039: own — origin.* never inherits (ADR-0029), so own is correct.
90
- const v = this.ownAttr(ORIGIN_COLLECTION_ATTR_VIA);
91
- return typeof v === "string" ? v : undefined;
92
- }
93
- }
94
-
95
79
  /**
96
80
  * Computed origin (#195) — a row-level value computed from the base entity's own
97
81
  * fields via a structured expression tree (`@expr`). No related rows, no `@via`.
@@ -13,7 +13,15 @@ import { SUBTYPE_BASE } from "../../shared/base-types.js";
13
13
 
14
14
  export const ORIGIN_SUBTYPE_PASSTHROUGH = "passthrough";
15
15
  export const ORIGIN_SUBTYPE_AGGREGATE = "aggregate";
16
- export const ORIGIN_SUBTYPE_COLLECTION = "collection";
16
+ // FR-037 R2 — `collection` is RESERVED, NOT REGISTERED. It duplicated
17
+ // `origin.aggregate @agg: collect` on a strictly smaller attr set (@via only —
18
+ // no @filter, no @orderBy, no @distinct), and nothing dispatched on it: its last
19
+ // real consumer, the payload-VO typing edge, was deleted in 0.20.16 (#270) for
20
+ // being actively wrong. Re-entry bar (ADR-0007 Amendment 2): a member enters the
21
+ // registry only when a shipping consumer dispatches on it. The designated
22
+ // re-entry shape is `@agg: collect` with `@of` made OPTIONAL (absent = whole-
23
+ // object rollup, typed by the field's declared @objectRef + isArray), NOT a
24
+ // restored subtype.
17
25
  // #195 — computed: a row-level value from the base entity's own fields via a
18
26
  // structured @expr tree (no related rows). first: the single related row picked
19
27
  // by @orderBy along @via, projecting @of (argmax-then-project).
@@ -24,7 +32,6 @@ export const ORIGIN_SUBTYPES = [
24
32
  SUBTYPE_BASE,
25
33
  ORIGIN_SUBTYPE_PASSTHROUGH,
26
34
  ORIGIN_SUBTYPE_AGGREGATE,
27
- ORIGIN_SUBTYPE_COLLECTION,
28
35
  ORIGIN_SUBTYPE_COMPUTED,
29
36
  ORIGIN_SUBTYPE_FIRST,
30
37
  ] as const;
@@ -38,7 +45,6 @@ export type OriginSubType = (typeof ORIGIN_SUBTYPES)[number];
38
45
  export const ASSEMBLY_ORIGIN_SUBTYPES = [
39
46
  ORIGIN_SUBTYPE_AGGREGATE,
40
47
  ORIGIN_SUBTYPE_COMPUTED,
41
- ORIGIN_SUBTYPE_COLLECTION,
42
48
  ORIGIN_SUBTYPE_FIRST,
43
49
  ] as const;
44
50
 
@@ -50,11 +56,6 @@ export const ORIGIN_PASSTHROUGH_ATTR_VIA = "via";
50
56
  // does NOT generate a cast; real type-converting projections are origin.computed (#195/#159).
51
57
  export const ORIGIN_PASSTHROUGH_ATTR_CONVERT = "convert";
52
58
 
53
- // collection attrs — a relationship-derived array of nested view-objects
54
- // (FR-004 R4). @via is the dotted relationship path (optionally wildcard-
55
- // prefixed, e.g. "*.User", for a package-spanning collection).
56
- export const ORIGIN_COLLECTION_ATTR_VIA = "via";
57
-
58
59
  // aggregate attrs
59
60
  export const ORIGIN_AGGREGATE_ATTR_AGG = "agg";
60
61
  export const ORIGIN_AGGREGATE_ATTR_OF = "of";
@@ -114,22 +114,6 @@ export const ORIGIN_DEFINITION: ProviderDefinition = {
114
114
  }
115
115
  ]
116
116
  },
117
- {
118
- "type": "origin",
119
- "subType": "collection",
120
- "description": "A relationship-derived array of nested view-objects: walks @via to produce the collection (e.g. 'Author.posts'), or a wildcard selector for a package-spanning collection.",
121
- "whenToUse": "A projection needs an array of nested child view-objects (a parent with its children inline). Declare it instead of hand-assembling the nested query + mapping.",
122
- "children": [
123
- {
124
- "type": "attr",
125
- "subType": "string",
126
- "name": "via",
127
- "min": 1,
128
- "max": 1,
129
- "description": "Dotted relationship path the collection walks to produce an array of nested view-objects (e.g. 'Author.posts'), or a wildcard selector for a package-spanning collection (e.g. '*.User')."
130
- }
131
- ]
132
- },
133
117
  {
134
118
  "type": "origin",
135
119
  "subType": "computed",
@@ -103,11 +103,16 @@ interface ManifestType {
103
103
  * manifest (the byte-exact bill of materials), NOT a per-provider or per-file marker:
104
104
  * every port emits the same string, asserted by registry-conformance. Pre-1.0 the
105
105
  * vocabulary is still `0.x` (semver = unstable, may change) but largely settled going
106
- * into the quiet period, so this is `"0.9"` (near-final); the 1.0 cut (readiness G1)
107
- * freezes it to `"1.0"`. Decoupled from every package line — it is the spec version,
108
- * not a package version.
106
+ * into the quiet period; the 1.0 cut (readiness G1) freezes it to `"1.0"`. Decoupled
107
+ * from every package line — it is the spec version, not a package version.
108
+ *
109
+ * The current value is the declaration below and nothing else. Do not restate it in
110
+ * prose: `scripts/check-metamodel-version.mjs --set` rewrites the declaration, so any
111
+ * copy in a comment goes stale on the very next bump (it said `"0.9"` here while the
112
+ * constant read `"0.10"`). Bump with that script — never by hand — so the manifest and
113
+ * all four port constants move together.
109
114
  */
110
- export const METAMODEL_VERSION = "0.9";
115
+ export const METAMODEL_VERSION = "0.10";
111
116
 
112
117
  /** The full canonical manifest. All collections are sorted for byte-stability. */
113
118
  interface RegistryManifest {