@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.
Files changed (233) hide show
  1. package/README.md +2 -0
  2. package/dist/attr-schema-validate.d.ts.map +1 -1
  3. package/dist/attr-schema-validate.js +9 -12
  4. package/dist/attr-schema-validate.js.map +1 -1
  5. package/dist/constants.d.ts +1 -0
  6. package/dist/constants.d.ts.map +1 -1
  7. package/dist/constants.js +1 -0
  8. package/dist/constants.js.map +1 -1
  9. package/dist/core/attr/meta-attr-filter.d.ts.map +1 -1
  10. package/dist/core/attr/meta-attr-filter.js +8 -2
  11. package/dist/core/attr/meta-attr-filter.js.map +1 -1
  12. package/dist/core/attr/meta-attr.d.ts +14 -0
  13. package/dist/core/attr/meta-attr.d.ts.map +1 -1
  14. package/dist/core/attr/meta-attr.js +23 -1
  15. package/dist/core/attr/meta-attr.js.map +1 -1
  16. package/dist/core/field/shared-enum.d.ts +21 -0
  17. package/dist/core/field/shared-enum.d.ts.map +1 -0
  18. package/dist/core/field/shared-enum.js +49 -0
  19. package/dist/core/field/shared-enum.js.map +1 -0
  20. package/dist/core/identity/meta-identity.d.ts +1 -1
  21. package/dist/core/identity/meta-identity.d.ts.map +1 -1
  22. package/dist/core/identity/meta-identity.js +8 -13
  23. package/dist/core/identity/meta-identity.js.map +1 -1
  24. package/dist/core/index.d.ts +2 -0
  25. package/dist/core/index.d.ts.map +1 -1
  26. package/dist/core/index.js +3 -0
  27. package/dist/core/index.js.map +1 -1
  28. package/dist/core/object/object-constants.d.ts +14 -1
  29. package/dist/core/object/object-constants.d.ts.map +1 -1
  30. package/dist/core/object/object-constants.js +18 -0
  31. package/dist/core/object/object-constants.js.map +1 -1
  32. package/dist/core/object/object-definition.embedded.d.ts.map +1 -1
  33. package/dist/core/object/object-definition.embedded.js +80 -0
  34. package/dist/core/object/object-definition.embedded.js.map +1 -1
  35. package/dist/core/relationship/derive-m2m-fields.d.ts +1 -1
  36. package/dist/core/relationship/derive-m2m-fields.d.ts.map +1 -1
  37. package/dist/core/relationship/derive-m2m-fields.js +15 -44
  38. package/dist/core/relationship/derive-m2m-fields.js.map +1 -1
  39. package/dist/core/relationship/find-reference.d.ts.map +1 -1
  40. package/dist/core/relationship/find-reference.js +4 -2
  41. package/dist/core/relationship/find-reference.js.map +1 -1
  42. package/dist/core/relationship/resolve-relationship-reference.d.ts +6 -5
  43. package/dist/core/relationship/resolve-relationship-reference.d.ts.map +1 -1
  44. package/dist/core/relationship/resolve-relationship-reference.js +9 -7
  45. package/dist/core/relationship/resolve-relationship-reference.js.map +1 -1
  46. package/dist/core/reporting/meta-dimension.d.ts +13 -0
  47. package/dist/core/reporting/meta-dimension.d.ts.map +1 -0
  48. package/dist/core/reporting/meta-dimension.js +30 -0
  49. package/dist/core/reporting/meta-dimension.js.map +1 -0
  50. package/dist/core/reporting/meta-measure.d.ts +24 -0
  51. package/dist/core/reporting/meta-measure.d.ts.map +1 -0
  52. package/dist/core/reporting/meta-measure.js +54 -0
  53. package/dist/core/reporting/meta-measure.js.map +1 -0
  54. package/dist/core/reporting/meta-segment.d.ts +6 -0
  55. package/dist/core/reporting/meta-segment.d.ts.map +1 -0
  56. package/dist/core/reporting/meta-segment.js +13 -0
  57. package/dist/core/reporting/meta-segment.js.map +1 -0
  58. package/dist/core/reporting/report-accessors.d.ts +27 -0
  59. package/dist/core/reporting/report-accessors.d.ts.map +1 -0
  60. package/dist/core/reporting/report-accessors.js +56 -0
  61. package/dist/core/reporting/report-accessors.js.map +1 -0
  62. package/dist/core/reporting/report-describe.d.ts +59 -0
  63. package/dist/core/reporting/report-describe.d.ts.map +1 -0
  64. package/dist/core/reporting/report-describe.js +182 -0
  65. package/dist/core/reporting/report-describe.js.map +1 -0
  66. package/dist/core/reporting/report-read-model.d.ts +34 -0
  67. package/dist/core/reporting/report-read-model.d.ts.map +1 -0
  68. package/dist/core/reporting/report-read-model.js +165 -0
  69. package/dist/core/reporting/report-read-model.js.map +1 -0
  70. package/dist/core/reporting/report-shape.d.ts +103 -0
  71. package/dist/core/reporting/report-shape.d.ts.map +1 -0
  72. package/dist/core/reporting/report-shape.js +398 -0
  73. package/dist/core/reporting/report-shape.js.map +1 -0
  74. package/dist/core/reporting/reporting-constants.d.ts +65 -0
  75. package/dist/core/reporting/reporting-constants.d.ts.map +1 -0
  76. package/dist/core/reporting/reporting-constants.js +84 -0
  77. package/dist/core/reporting/reporting-constants.js.map +1 -0
  78. package/dist/core/reporting/reporting-definition.embedded.d.ts +3 -0
  79. package/dist/core/reporting/reporting-definition.embedded.d.ts.map +1 -0
  80. package/dist/core/reporting/reporting-definition.embedded.js +185 -0
  81. package/dist/core/reporting/reporting-definition.embedded.js.map +1 -0
  82. package/dist/core/requirement/resolve-claim.d.ts +8 -0
  83. package/dist/core/requirement/resolve-claim.d.ts.map +1 -1
  84. package/dist/core/requirement/resolve-claim.js +21 -0
  85. package/dist/core/requirement/resolve-claim.js.map +1 -1
  86. package/dist/core/yaml-desugar.d.ts.map +1 -1
  87. package/dist/core/yaml-desugar.js +12 -82
  88. package/dist/core/yaml-desugar.js.map +1 -1
  89. package/dist/core-types.d.ts.map +1 -1
  90. package/dist/core-types.js +28 -1
  91. package/dist/core-types.js.map +1 -1
  92. package/dist/docs-ref.d.ts +19 -0
  93. package/dist/docs-ref.d.ts.map +1 -0
  94. package/dist/docs-ref.js +37 -0
  95. package/dist/docs-ref.js.map +1 -0
  96. package/dist/errors.d.ts +1 -1
  97. package/dist/errors.d.ts.map +1 -1
  98. package/dist/errors.js +17 -6
  99. package/dist/errors.js.map +1 -1
  100. package/dist/fmt.d.ts.map +1 -1
  101. package/dist/fmt.js +3 -1
  102. package/dist/fmt.js.map +1 -1
  103. package/dist/index.d.ts +15 -4
  104. package/dist/index.d.ts.map +1 -1
  105. package/dist/index.js +18 -4
  106. package/dist/index.js.map +1 -1
  107. package/dist/loader/declared-duplicate-fields.d.ts +19 -0
  108. package/dist/loader/declared-duplicate-fields.d.ts.map +1 -0
  109. package/dist/loader/declared-duplicate-fields.js +97 -0
  110. package/dist/loader/declared-duplicate-fields.js.map +1 -0
  111. package/dist/loader/meta-data-loader.d.ts.map +1 -1
  112. package/dist/loader/meta-data-loader.js +18 -1
  113. package/dist/loader/meta-data-loader.js.map +1 -1
  114. package/dist/loader/reporting-validation.d.ts +11 -0
  115. package/dist/loader/reporting-validation.d.ts.map +1 -0
  116. package/dist/loader/reporting-validation.js +883 -0
  117. package/dist/loader/reporting-validation.js.map +1 -0
  118. package/dist/loader/resolve-references.d.ts +8 -0
  119. package/dist/loader/resolve-references.d.ts.map +1 -0
  120. package/dist/loader/resolve-references.js +132 -0
  121. package/dist/loader/resolve-references.js.map +1 -0
  122. package/dist/loader/validation-passes.d.ts.map +1 -1
  123. package/dist/loader/validation-passes.js +14 -9
  124. package/dist/loader/validation-passes.js.map +1 -1
  125. package/dist/loader/validation-registry.d.ts.map +1 -1
  126. package/dist/loader/validation-registry.js +33 -10
  127. package/dist/loader/validation-registry.js.map +1 -1
  128. package/dist/metamodel-docs/index.js +3 -3
  129. package/dist/metamodel-docs/index.js.map +1 -1
  130. package/dist/metamodel-docs/render.js +4 -4
  131. package/dist/metamodel-docs/render.js.map +1 -1
  132. package/dist/naming-refs.d.ts +102 -50
  133. package/dist/naming-refs.d.ts.map +1 -1
  134. package/dist/naming-refs.js +223 -97
  135. package/dist/naming-refs.js.map +1 -1
  136. package/dist/naming.d.ts +42 -7
  137. package/dist/naming.d.ts.map +1 -1
  138. package/dist/naming.js +43 -5
  139. package/dist/naming.js.map +1 -1
  140. package/dist/object-serializer.d.ts.map +1 -1
  141. package/dist/object-serializer.js +6 -9
  142. package/dist/object-serializer.js.map +1 -1
  143. package/dist/parser-core.d.ts.map +1 -1
  144. package/dist/parser-core.js +18 -48
  145. package/dist/parser-core.js.map +1 -1
  146. package/dist/persistence/db/db-definition.embedded.js +2 -2
  147. package/dist/persistence/db/db-definition.embedded.js.map +1 -1
  148. package/dist/persistence/source/meta-source.d.ts +5 -3
  149. package/dist/persistence/source/meta-source.d.ts.map +1 -1
  150. package/dist/persistence/source/meta-source.js +8 -6
  151. package/dist/persistence/source/meta-source.js.map +1 -1
  152. package/dist/registry-manifest-exclusions.js +2 -2
  153. package/dist/registry-manifest-exclusions.js.map +1 -1
  154. package/dist/registry-manifest.d.ts +1 -1
  155. package/dist/registry-manifest.js +1 -1
  156. package/dist/release-docs.d.ts +11 -0
  157. package/dist/release-docs.d.ts.map +1 -0
  158. package/dist/release-docs.js +48 -0
  159. package/dist/release-docs.js.map +1 -0
  160. package/dist/serializer-json.d.ts +15 -1
  161. package/dist/serializer-json.d.ts.map +1 -1
  162. package/dist/serializer-json.js +55 -17
  163. package/dist/serializer-json.js.map +1 -1
  164. package/dist/shared/base-types.d.ts +7 -1
  165. package/dist/shared/base-types.d.ts.map +1 -1
  166. package/dist/shared/base-types.js +9 -0
  167. package/dist/shared/base-types.js.map +1 -1
  168. package/dist/shared/meta-data.d.ts +10 -0
  169. package/dist/shared/meta-data.d.ts.map +1 -1
  170. package/dist/shared/meta-data.js +22 -1
  171. package/dist/shared/meta-data.js.map +1 -1
  172. package/dist/shared/meta-root.d.ts +13 -1
  173. package/dist/shared/meta-root.d.ts.map +1 -1
  174. package/dist/shared/meta-root.js +19 -1
  175. package/dist/shared/meta-root.js.map +1 -1
  176. package/dist/super-resolve.d.ts +4 -1
  177. package/dist/super-resolve.d.ts.map +1 -1
  178. package/dist/super-resolve.js +44 -65
  179. package/dist/super-resolve.js.map +1 -1
  180. package/dist/vocabulary-catalog.js +2 -2
  181. package/dist/vocabulary-catalog.js.map +1 -1
  182. package/package.json +6 -1
  183. package/src/attr-schema-validate.ts +9 -13
  184. package/src/constants.ts +1 -0
  185. package/src/core/attr/meta-attr-filter.ts +8 -1
  186. package/src/core/attr/meta-attr.ts +24 -1
  187. package/src/core/field/shared-enum.ts +53 -0
  188. package/src/core/identity/meta-identity.ts +9 -12
  189. package/src/core/index.ts +5 -0
  190. package/src/core/object/object-constants.ts +19 -0
  191. package/src/core/object/object-definition.embedded.ts +80 -0
  192. package/src/core/relationship/derive-m2m-fields.ts +15 -48
  193. package/src/core/relationship/find-reference.ts +4 -2
  194. package/src/core/relationship/resolve-relationship-reference.ts +11 -9
  195. package/src/core/reporting/meta-dimension.ts +41 -0
  196. package/src/core/reporting/meta-measure.ts +75 -0
  197. package/src/core/reporting/meta-segment.ts +14 -0
  198. package/src/core/reporting/report-accessors.ts +73 -0
  199. package/src/core/reporting/report-describe.ts +206 -0
  200. package/src/core/reporting/report-read-model.ts +177 -0
  201. package/src/core/reporting/report-shape.ts +493 -0
  202. package/src/core/reporting/reporting-constants.ts +104 -0
  203. package/src/core/reporting/reporting-definition.embedded.ts +192 -0
  204. package/src/core/requirement/resolve-claim.ts +20 -0
  205. package/src/core/yaml-desugar.ts +9 -89
  206. package/src/core-types.ts +41 -0
  207. package/src/docs-ref.ts +41 -0
  208. package/src/errors.ts +17 -6
  209. package/src/fmt.ts +3 -1
  210. package/src/index.ts +72 -4
  211. package/src/loader/declared-duplicate-fields.ts +114 -0
  212. package/src/loader/meta-data-loader.ts +25 -1
  213. package/src/loader/reporting-validation.ts +1137 -0
  214. package/src/loader/resolve-references.ts +144 -0
  215. package/src/loader/validation-passes.ts +13 -9
  216. package/src/loader/validation-registry.ts +37 -9
  217. package/src/metamodel-docs/index.ts +3 -3
  218. package/src/metamodel-docs/render.ts +4 -4
  219. package/src/naming-refs.ts +237 -94
  220. package/src/naming.ts +75 -11
  221. package/src/object-serializer.ts +7 -9
  222. package/src/parser-core.ts +19 -56
  223. package/src/persistence/db/db-definition.embedded.ts +2 -2
  224. package/src/persistence/source/meta-source.ts +8 -6
  225. package/src/registry-manifest-exclusions.ts +2 -2
  226. package/src/registry-manifest.ts +1 -1
  227. package/src/release-docs.ts +48 -0
  228. package/src/serializer-json.ts +79 -12
  229. package/src/shared/base-types.ts +9 -0
  230. package/src/shared/meta-data.ts +22 -1
  231. package/src/shared/meta-root.ts +20 -1
  232. package/src/super-resolve.ts +49 -63
  233. package/src/vocabulary-catalog.ts +2 -2
@@ -16,6 +16,9 @@ import {
16
16
  IDENTITY_REFERENCE_ATTR_ON_UPDATE,
17
17
  } from "./identity-constants.js";
18
18
  import type { MetaRoot } from "../../shared/meta-root.js";
19
+ import type { MetaObject } from "../object/meta-object.js";
20
+ import { TYPE_OBJECT } from "../../shared/base-types.js";
21
+ import { refTarget } from "../../naming-refs.js";
19
22
 
20
23
  /** Strongly-typed identity generation strategies. */
21
24
  export type IdentityGeneration = "increment" | "uuid" | "assigned";
@@ -144,7 +147,7 @@ export class MetaReferenceIdentity extends MetaIdentity {
144
147
  * points at. Priority: explicit dotted-form `@references: "Entity.field"`
145
148
  * override → target entity's primary identity field → "id" fallback.
146
149
  *
147
- * Returns undefined only if the target entity cannot be found in `root`.
150
+ * Returns undefined only if the target entity does not resolve.
148
151
  * Centralizes the FK-target resolution rule used by projection view DDL,
149
152
  * Drizzle schema emit, and migration schema comparison.
150
153
  */
@@ -152,17 +155,11 @@ export class MetaReferenceIdentity extends MetaIdentity {
152
155
  const explicit = this.targetFields[0];
153
156
  if (explicit !== undefined) return explicit;
154
157
 
155
- const targetName = this.targetEntity;
156
- if (targetName === undefined) return undefined;
157
- // targetEntity may be package-qualified (FQN); findObject is keyed by bare
158
- // name, so fall back to the bare suffix after the last "::". Mirrors the
159
- // resolveTargetTable fix; without it a cross-package reference resolves no
160
- // target and the PK column wrongly defaults to "id".
161
- const targetObj = root.findObject(targetName)
162
- ?? (targetName.includes("::")
163
- ? root.findObject(targetName.slice(targetName.lastIndexOf("::") + 2))
164
- : undefined);
165
- if (!targetObj) return undefined;
158
+ // ADR-0059 R7: the target the loader bound for @references, else (a tree built
159
+ // without the loader) the value resolved from this reference's package.
160
+ const bound = refTarget(this, IDENTITY_REFERENCE_ATTR_REFERENCES);
161
+ if (bound === undefined || bound.type !== TYPE_OBJECT) return undefined;
162
+ const targetObj = bound as MetaObject;
166
163
 
167
164
  const primary = targetObj.primaryIdentity();
168
165
  // ADR-0039: resolving — the target's @fields may be inherited via extends.
package/src/core/index.ts CHANGED
@@ -16,3 +16,8 @@ export { UriSource } from "../loader/sources/uri-source.js";
16
16
  export { parseYaml } from "./parser-yaml.js";
17
17
  export { loadAndExportJson } from "./export-json.js";
18
18
  export type { ExportResult } from "./export-json.js";
19
+
20
+ // The `meta verify` field lint's structural pre-parse walk. Lives here, not on the
21
+ // root entry, because it parses YAML.
22
+ export { declaredDuplicateFields } from "../loader/declared-duplicate-fields.js";
23
+ export type { DeclaredDuplicateField } from "../loader/declared-duplicate-fields.js";
@@ -14,12 +14,15 @@ import { SUBTYPE_BASE } from "../../shared/base-types.js";
14
14
  export const OBJECT_SUBTYPE_ENTITY = "entity";
15
15
  export const OBJECT_SUBTYPE_VALUE = "value";
16
16
  export const OBJECT_SUBTYPE_PROJECTION = "projection";
17
+ // - report : declared dimensions x measures of one entity, compiled to a read-only view (FR-044)
18
+ export const OBJECT_SUBTYPE_REPORT = "report";
17
19
 
18
20
  export const OBJECT_SUBTYPES = [
19
21
  SUBTYPE_BASE,
20
22
  OBJECT_SUBTYPE_ENTITY,
21
23
  OBJECT_SUBTYPE_VALUE,
22
24
  OBJECT_SUBTYPE_PROJECTION,
25
+ OBJECT_SUBTYPE_REPORT,
23
26
  ] as const;
24
27
  export type ObjectSubType = (typeof OBJECT_SUBTYPES)[number];
25
28
 
@@ -49,3 +52,19 @@ export const OBJECT_ATTR_DISCRIMINATOR_VALUE = "discriminatorValue";
49
52
  * but resolves against the projection's OWN declared fields rather than one
50
53
  * aggregated entity. An aggregate-derived field is not addressable (fail-closed). */
51
54
  export const OBJECT_PROJECTION_ATTR_FILTER = "filter";
55
+
56
+ // ---------------------------------------------------------------------------
57
+ // FR-044 — object.report attrs
58
+ // ---------------------------------------------------------------------------
59
+ /** The object.entity whose rows the report aggregates. */
60
+ export const OBJECT_REPORT_ATTR_FROM = "from";
61
+ /** Dimension names of @from; a time dimension is written `name:grain`. */
62
+ export const OBJECT_REPORT_ATTR_DIMENSIONS = "dimensions";
63
+ /** Measure names of @from. */
64
+ export const OBJECT_REPORT_ATTR_MEASURES = "measures";
65
+ /** Optional segment of @from scoping the rows. */
66
+ export const OBJECT_REPORT_ATTR_SEGMENT = "segment";
67
+ /** Optional row scope over @from's fields (an attr.filter). */
68
+ export const OBJECT_REPORT_ATTR_FILTER = "filter";
69
+ /** Optional to-one path from @from to the entity whose rows supply the report's rows (R8). */
70
+ export const OBJECT_REPORT_ATTR_SPINE = "spine";
@@ -80,6 +80,27 @@ export const OBJECT_DEFINITION: ProviderDefinition = {
80
80
  "min": 0,
81
81
  "max": null
82
82
  },
83
+ {
84
+ "type": "dimension",
85
+ "subType": "*",
86
+ "name": "*",
87
+ "min": 0,
88
+ "max": null
89
+ },
90
+ {
91
+ "type": "measure",
92
+ "subType": "*",
93
+ "name": "*",
94
+ "min": 0,
95
+ "max": null
96
+ },
97
+ {
98
+ "type": "segment",
99
+ "subType": "*",
100
+ "name": "*",
101
+ "min": 0,
102
+ "max": null
103
+ },
83
104
  {
84
105
  "type": "attr",
85
106
  "subType": "string",
@@ -132,6 +153,65 @@ export const OBJECT_DEFINITION: ProviderDefinition = {
132
153
  "description": "Optional row-scope predicate (a portable attr.filter object: eq/ne/gt/gte/lt/lte/like/in/isNull with and/or, desugared to { field: { op: value } } at parse time) selecting which rows the view returns — lowered to an outer SQL WHERE. Resolves against the projection's own declared fields; an aggregate-derived field is not addressable (fail-closed)."
133
154
  }
134
155
  ]
156
+ },
157
+ {
158
+ "type": "object",
159
+ "subType": "report",
160
+ "extendsBase": true,
161
+ "description": "A declared report (FR-044): a fixed combination of dimensions and measures of ONE entity (@from), compiled to a read-only view. One row per distinct dimension tuple; no dimensions means exactly one row. Fields are DERIVED, not declared: one per dimension (a time dimension's field is <dimension><Grain>, e.g. purchasedAtDay) and one per measure. Every measure must belong to @from (two fact tables are two reports). @filter and @segment scope rows before grouping and combine by AND. Read-only: no writes, no get-by-id.",
162
+ "whenToUse": "Dashboard totals, per-day series and per-group summaries that would otherwise be hand-written GROUP BY queries.",
163
+ "children": [
164
+ {
165
+ "type": "attr",
166
+ "subType": "string",
167
+ "name": "from",
168
+ "min": 1,
169
+ "max": 1,
170
+ "description": "The object.entity whose rows the report aggregates."
171
+ },
172
+ {
173
+ "type": "attr",
174
+ "subType": "string",
175
+ "name": "dimensions",
176
+ "isArray": true,
177
+ "min": 0,
178
+ "max": 1,
179
+ "description": "Dimension names of @from; a time dimension is written 'name:grain'. Absent means one global row."
180
+ },
181
+ {
182
+ "type": "attr",
183
+ "subType": "string",
184
+ "name": "measures",
185
+ "isArray": true,
186
+ "min": 1,
187
+ "max": 1,
188
+ "description": "Measure names of @from."
189
+ },
190
+ {
191
+ "type": "attr",
192
+ "subType": "string",
193
+ "name": "segment",
194
+ "min": 0,
195
+ "max": 1,
196
+ "description": "Optional segment of @from scoping the rows."
197
+ },
198
+ {
199
+ "type": "attr",
200
+ "subType": "filter",
201
+ "name": "filter",
202
+ "min": 0,
203
+ "max": 1,
204
+ "description": "Optional row scope over @from's fields; may use relative-date values."
205
+ },
206
+ {
207
+ "type": "attr",
208
+ "subType": "string",
209
+ "name": "spine",
210
+ "min": 0,
211
+ "max": 1,
212
+ "description": "Optional to-one path from @from to the entity whose rows supply the report's rows (e.g. 'Purchase.program'), written like a dimension's @via. With it the report has one row per distinct dimension tuple among THAT entity's rows, including the ones no row of @from refers to: a count there is 0 and any other measure is null unless it declares @default. Every listed dimension must be reached through this path. @segment and @filter still scope the rows of @from and never remove a row. A row of @from whose reference is null or matches nothing is in no row of the report."
213
+ }
214
+ ]
135
215
  }
136
216
  ]
137
217
  };
@@ -44,7 +44,9 @@ import type { MetaRoot } from "../../shared/meta-root.js";
44
44
  import type { MetaRelationship } from "./meta-relationship.js";
45
45
  import type { MetaReferenceIdentity } from "../identity/meta-identity.js";
46
46
  import { stripPackage } from "../../naming.js";
47
- import { resolveObjectRef } from "../../naming-refs.js";
47
+ import { refTarget } from "../../naming-refs.js";
48
+ import { IDENTITY_REFERENCE_ATTR_REFERENCES } from "../identity/identity-constants.js";
49
+ import { RELATIONSHIP_ATTR_OBJECT_REF, RELATIONSHIP_ATTR_THROUGH } from "./relationship-constants.js";
48
50
  import { TYPE_OBJECT } from "../../shared/base-types.js";
49
51
  import { PACKAGE_SEPARATOR } from "../../shared/structural.js";
50
52
 
@@ -69,37 +71,6 @@ function refFkField(ref: MetaReferenceIdentity): string | undefined {
69
71
  return ref.fields[0];
70
72
  }
71
73
 
72
- /**
73
- * The root entity a reference name denotes, or undefined — resolved by the ONE
74
- * matcher the loader uses (`resolveObjectRef`, ADR-0041/0042): a fully-qualified
75
- * name binds exactly on its resolution key, a bare name binds in the REFERRER's
76
- * package.
77
- *
78
- * This exists so the SUBJECT comparison below can be made on object IDENTITY.
79
- * A bare-name compare cannot tell `a::NodeBase` from `b::NodeBase`, which made a
80
- * genuine cross-package hetero M:N read as a self-join the moment the subject
81
- * set held two names — and the first-match-wins bare arm this used to carry
82
- * (Java's deferred #174) bound a bare name to whichever same-short-named entity
83
- * happened to load first, which is load-ORDER dependence, not a naming rule.
84
- */
85
- function findEntity(
86
- root: MetaRoot,
87
- name: string | undefined,
88
- referrerPkg: string,
89
- ): MetaObject | undefined {
90
- if (name === undefined || name === "") return undefined;
91
- // ADR-0041/0042 through the single resolveObjectRef matcher — an FQN resolves
92
- // exactly on its resolution key, a BARE name resolves in the REFERRER's
93
- // package. The bare arm used to be `objects().find(o => o.name === bare)`,
94
- // which took whichever same-short-named entity loaded first: a bare
95
- // `@objectRef: "Tag"` on an abstract base in package `base` bound to
96
- // `acme::Tag` merely because the acme source was read first. Callers pass the
97
- // referrer that owns the name they are resolving — the DECLARING entity for
98
- // the relationship's own @objectRef, the JUNCTION for its references' —
99
- // because those are different packages whenever a base is inherited across one.
100
- return resolveObjectRef(root, name, referrerPkg).node as MetaObject | undefined;
101
- }
102
-
103
74
  /**
104
75
  * Derive the source/target junction FK fields for a M:N relationship.
105
76
  *
@@ -108,7 +79,7 @@ function findEntity(
108
79
  * @param source the entity the caller is navigating from. Accepted alongside
109
80
  * `rel.parent` as a name for the relationship's subject, and used
110
81
  * as the declaring entity when `rel` has no entity parent.
111
- * @param root the loaded model root (to find the junction entity)
82
+ * @param root the loaded model root
112
83
  * @throws M2MDerivationError when the junction is missing/malformed or the
113
84
  * self-join is ambiguous.
114
85
  */
@@ -134,9 +105,10 @@ export function deriveM2MFields(
134
105
  `relationship "${declaringEntity.name}.${rel.name}" is missing @through (required for M:N derivation)`,
135
106
  );
136
107
  }
137
- // ADR-0041/0042 — resolve @through through the SAME matcher the loader uses
138
- // (resolveObjectRef, via validation-passes' _findObject): an FQN resolves
139
- // exactly on its resolution key, a bare name resolves package-locally.
108
+ // ADR-0059 R7 — @through, @objectRef and the junction's @references are read
109
+ // as bound targets (refTarget), each resolved in the package of the node that
110
+ // declares it, so an inherited reference keeps the meaning it had where it was
111
+ // written.
140
112
  //
141
113
  // This used to be `root.findObject(fqn)` with a fall back to the bare suffix
142
114
  // after the last "::". findObject is keyed by BARE name, so the FQN lookup
@@ -147,12 +119,7 @@ export function deriveM2MFields(
147
119
  // nowhere near the navigating entity. The pairing then failed, so a valid
148
120
  // cross-package M:N emitted no traversal route in TS and C# and failed the
149
121
  // build in Java, Kotlin and Python.
150
- //
151
- // Note findEntity() below was ALREADY FQN-exact for the same reason (see its
152
- // doc comment). The two halves of one function disagreed about how a
153
- // package-qualified name resolves; only the junction half was wrong.
154
- const referrerPkg = declaringEntity.package ?? declaringEntity.fileDefaultPackage ?? "";
155
- const junction = resolveObjectRef(root, throughName, referrerPkg).node as MetaObject | undefined;
122
+ const junction = refTarget(rel, RELATIONSHIP_ATTR_THROUGH) as MetaObject | undefined;
156
123
  if (junction === undefined) {
157
124
  throw new M2MDerivationError(
158
125
  `relationship "${declaringEntity.name}.${rel.name}" @through "${throughName}" does not resolve to an entity`,
@@ -202,16 +169,16 @@ export function deriveM2MFields(
202
169
  name !== undefined && subjectNames.includes(stripPackage(name));
203
170
 
204
171
  // The junction owns its references' @references names, so those resolve in the
205
- // JUNCTION's package; the relationship's own @objectRef resolves in the
206
- // DECLARING entity's. Under `extends` across packages these differ, and using
207
- // one for both is what bound a bare @objectRef to the wrong same-named entity.
208
- const junctionPkg = junction.package ?? junction.fileDefaultPackage ?? "";
172
+ // package of the node that declares them; the relationship's own @objectRef in
173
+ // the package of the node that declares it. Under `extends` across packages these
174
+ // differ, and using one for both is what bound a bare @objectRef to the wrong
175
+ // same-named entity.
209
176
  const refEntity = (r: MetaReferenceIdentity): MetaObject | undefined =>
210
- findEntity(root, r.targetEntity, junctionPkg);
177
+ refTarget(r, IDENTITY_REFERENCE_ATTR_REFERENCES) as MetaObject | undefined;
211
178
 
212
179
  // Defensive bare fallback when @objectRef does not resolve — loader validation
213
180
  // normally guarantees it does. Same carve-out the Java reference makes.
214
- const targetEntityNode = findEntity(root, targetName, referrerPkg);
181
+ const targetEntityNode = refTarget(rel, RELATIONSHIP_ATTR_OBJECT_REF) as MetaObject | undefined;
215
182
  const isSelfJoin = targetEntityNode !== undefined
216
183
  ? isSubject(targetEntityNode)
217
184
  : isSubjectName(targetName);
@@ -8,7 +8,8 @@
8
8
 
9
9
  import type { MetaObject } from "../object/meta-object.js";
10
10
  import type { MetaReferenceIdentity } from "../identity/meta-identity.js";
11
- import { stripPackage } from "../../naming.js";
11
+ import { refTarget } from "../../naming-refs.js";
12
+ import { IDENTITY_REFERENCE_ATTR_REFERENCES } from "../identity/identity-constants.js";
12
13
 
13
14
  export interface ReferenceLookup {
14
15
  /** The entity whose identity.reference points at `other`. */
@@ -39,7 +40,8 @@ export function findReferencesBetween(
39
40
  const found: ReferenceLookup[] = [];
40
41
  for (const [holder, other] of [[a, b], [b, a]] as const) {
41
42
  for (const ref of holder.referenceIdentities()) {
42
- if (stripPackage(ref.targetEntity) === other.name) {
43
+ // ADR-0059 R7: compare the bound target, never a short name.
44
+ if (refTarget(ref, IDENTITY_REFERENCE_ATTR_REFERENCES) === other) {
43
45
  found.push({ holder, other, referenceIdentity: ref });
44
46
  }
45
47
  }
@@ -10,7 +10,9 @@
10
10
 
11
11
  import type { MetaObject } from "../object/meta-object.js";
12
12
  import type { MetaReferenceIdentity } from "../identity/meta-identity.js";
13
- import { stripPackage } from "../../naming.js";
13
+ import type { MetaData } from "../../shared/meta-data.js";
14
+ import { refTarget } from "../../naming-refs.js";
15
+ import { IDENTITY_REFERENCE_ATTR_REFERENCES } from "../identity/identity-constants.js";
14
16
 
15
17
  /**
16
18
  * Trailing suffixes stripped from a CANDIDATE's name/FK field when building its
@@ -51,20 +53,20 @@ export function referencePairingKeys(ref: MetaReferenceIdentity): Set<string> {
51
53
  }
52
54
 
53
55
  /**
54
- * Every identity.reference on `holder` whose @references targets `targetEntity`.
55
- * Package-insensitive on both sides: @references and @objectRef may each be bare
56
- * or fully qualified.
56
+ * Every identity.reference on `holder` whose @references targets `targetEntity`: a
57
+ * reference matches when its @references is bound to that node (ADR-0059 R7: identity,
58
+ * never a short name). An unresolved target (undefined) matches nothing.
57
59
  */
58
60
  export function referenceCandidatesFor(
59
61
  holder: MetaObject,
60
- targetEntity: string,
62
+ targetEntity: MetaData | undefined,
61
63
  ): MetaReferenceIdentity[] {
62
- const target = stripPackage(targetEntity);
64
+ if (targetEntity === undefined) return [];
63
65
  // ADR-0039: resolving — referenceIdentities() honors references inherited via extends.
64
66
  return holder
65
67
  .referenceIdentities()
66
- .filter((ref) => stripPackage(ref.targetEntity ?? "") === target)
67
- .filter((ref) => refFkField(ref) !== undefined);
68
+ .filter((ref) => refFkField(ref) !== undefined)
69
+ .filter((ref) => refTarget(ref, IDENTITY_REFERENCE_ATTR_REFERENCES) === targetEntity);
68
70
  }
69
71
 
70
72
  /**
@@ -82,7 +84,7 @@ export function referenceCandidatesFor(
82
84
  export function resolveRelationshipReference(
83
85
  holder: MetaObject,
84
86
  relationshipName: string,
85
- targetEntity: string,
87
+ targetEntity: MetaData | undefined,
86
88
  sourceRefField?: string,
87
89
  ): MetaReferenceIdentity | undefined {
88
90
  const candidates = referenceCandidatesFor(holder, targetEntity);
@@ -0,0 +1,41 @@
1
+ // MetaDimension — concrete node class for type=dimension nodes (FR-044).
2
+ //
3
+ // Extends MetaData directly. Accessors are RESOLVING (ADR-0039): a dimension
4
+ // declared on an abstract base entity is read through the same accessors as one
5
+ // declared on the concrete entity.
6
+
7
+ import { MetaData } from "../../shared/meta-data.js";
8
+ import {
9
+ DIMENSION_SUBTYPE_TIME,
10
+ REPORTING_ATTR_GRAINS,
11
+ REPORTING_ATTR_OF,
12
+ REPORTING_ATTR_VIA,
13
+ TIME_GRAINS,
14
+ type TimeGrain,
15
+ } from "./reporting-constants.js";
16
+
17
+ export class MetaDimension extends MetaData {
18
+ /** True for `dimension.time` (grain truncation); false for `dimension.attribute`. */
19
+ isTime(): boolean {
20
+ return this.subType === DIMENSION_SUBTYPE_TIME;
21
+ }
22
+
23
+ /** Dotted `Entity.field` reference naming the grouped column. */
24
+ of(): string | undefined {
25
+ const v = this.attr(REPORTING_ATTR_OF);
26
+ return typeof v === "string" ? v : undefined;
27
+ }
28
+
29
+ /** Optional dotted to-one relationship path from the owning entity to the `@of` entity. */
30
+ via(): string | undefined {
31
+ const v = this.attr(REPORTING_ATTR_VIA);
32
+ return typeof v === "string" ? v : undefined;
33
+ }
34
+
35
+ /** The grains a `dimension.time` supports (empty for `dimension.attribute`). */
36
+ grains(): TimeGrain[] {
37
+ const v = this.attr(REPORTING_ATTR_GRAINS);
38
+ const items = Array.isArray(v) ? v : typeof v === "string" ? [v] : [];
39
+ return items.filter((g): g is TimeGrain => (TIME_GRAINS as readonly unknown[]).includes(g));
40
+ }
41
+ }
@@ -0,0 +1,75 @@
1
+ // MetaMeasure — concrete node class for type=measure nodes (FR-044).
2
+ //
3
+ // Extends MetaData directly. Accessors are RESOLVING (ADR-0039).
4
+
5
+ import { MetaData } from "../../shared/meta-data.js";
6
+ import {
7
+ MEASURE_AGGS,
8
+ MEASURE_SUBTYPE_RATIO,
9
+ REPORTING_ATTR_AGG,
10
+ REPORTING_ATTR_DEFAULT,
11
+ REPORTING_ATTR_DENOMINATOR,
12
+ REPORTING_ATTR_DISTINCT,
13
+ REPORTING_ATTR_FILTER,
14
+ REPORTING_ATTR_NUMERATOR,
15
+ REPORTING_ATTR_OF,
16
+ REPORTING_ATTR_SEGMENT,
17
+ type MeasureAgg,
18
+ } from "./reporting-constants.js";
19
+
20
+ function optionalString(v: unknown): string | undefined {
21
+ return typeof v === "string" ? v : undefined;
22
+ }
23
+
24
+ export class MetaMeasure extends MetaData {
25
+ /** True for `measure.ratio`; false for `measure.aggregate`. */
26
+ isRatio(): boolean {
27
+ return this.subType === MEASURE_SUBTYPE_RATIO;
28
+ }
29
+
30
+ /** The aggregate function (`measure.aggregate` only). */
31
+ agg(): MeasureAgg | undefined {
32
+ const v = this.attr(REPORTING_ATTR_AGG);
33
+ return (MEASURE_AGGS as readonly unknown[]).includes(v) ? (v as MeasureAgg) : undefined;
34
+ }
35
+
36
+ /** True when `@distinct` is set (legal only with `@agg: count`). */
37
+ distinct(): boolean {
38
+ return this.attr(REPORTING_ATTR_DISTINCT) === true;
39
+ }
40
+
41
+ /** The `Entity.field` references in `@of`: a bare string is one column, a list is the tuple form. */
42
+ ofColumns(): string[] {
43
+ const v = this.attr(REPORTING_ATTR_OF);
44
+ if (Array.isArray(v)) return v.filter((x): x is string => typeof x === "string");
45
+ return typeof v === "string" ? [v] : [];
46
+ }
47
+
48
+ /** Name of a segment declared on the same entity; combines with `@filter` by AND. */
49
+ segmentName(): string | undefined {
50
+ return optionalString(this.attr(REPORTING_ATTR_SEGMENT));
51
+ }
52
+
53
+ /** The canonical row-scope filter, when one is declared. */
54
+ filter(): Record<string, unknown> | undefined {
55
+ const v = this.attr(REPORTING_ATTR_FILTER);
56
+ return typeof v === "object" && v !== null && !Array.isArray(v) ? (v as Record<string, unknown>) : undefined;
57
+ }
58
+
59
+ /** `@default`: the integer this measure reads when it would otherwise be null (rule M7/M8
60
+ * decide where it is legal). ADR-0039: resolving, so an inherited measure keeps it. */
61
+ defaultValue(): number | undefined {
62
+ const v = this.attr(REPORTING_ATTR_DEFAULT);
63
+ return typeof v === "number" && Number.isInteger(v) ? v : undefined;
64
+ }
65
+
66
+ /** Name of the `measure.aggregate` sibling used as the numerator (`measure.ratio` only). */
67
+ numerator(): string | undefined {
68
+ return optionalString(this.attr(REPORTING_ATTR_NUMERATOR));
69
+ }
70
+
71
+ /** Name of the `measure.aggregate` sibling used as the denominator (`measure.ratio` only). */
72
+ denominator(): string | undefined {
73
+ return optionalString(this.attr(REPORTING_ATTR_DENOMINATOR));
74
+ }
75
+ }
@@ -0,0 +1,14 @@
1
+ // MetaSegment — concrete node class for type=segment nodes (FR-044).
2
+ //
3
+ // Extends MetaData directly. Accessors are RESOLVING (ADR-0039).
4
+
5
+ import { MetaData } from "../../shared/meta-data.js";
6
+ import { REPORTING_ATTR_FILTER } from "./reporting-constants.js";
7
+
8
+ export class MetaSegment extends MetaData {
9
+ /** The named row scope: a canonical attr.filter over the declaring entity's fields. */
10
+ filter(): Record<string, unknown> | undefined {
11
+ const v = this.attr(REPORTING_ATTR_FILTER);
12
+ return typeof v === "object" && v !== null && !Array.isArray(v) ? (v as Record<string, unknown>) : undefined;
13
+ }
14
+ }
@@ -0,0 +1,73 @@
1
+ // Free accessors over an `object.report` node (FR-044). Used by the Plan 2
2
+ // lowering and by the loader's report validation, so the `name:grain` parse and
3
+ // the derived-field-name rule have exactly one definition.
4
+
5
+ import type { MetaData } from "../../shared/meta-data.js";
6
+ import {
7
+ OBJECT_REPORT_ATTR_DIMENSIONS,
8
+ OBJECT_REPORT_ATTR_FROM,
9
+ OBJECT_REPORT_ATTR_MEASURES,
10
+ OBJECT_REPORT_ATTR_SPINE,
11
+ } from "../object/object-constants.js";
12
+ import { CHILD_REF_SEPARATOR } from "../../shared/structural.js";
13
+ import { REPORT_DIMENSION_GRAIN_SEPARATOR } from "./reporting-constants.js";
14
+
15
+ export interface ReportDimensionItem {
16
+ readonly name: string;
17
+ readonly grain?: string;
18
+ }
19
+
20
+ function stringList(v: unknown): string[] {
21
+ if (Array.isArray(v)) return v.filter((x): x is string => typeof x === "string");
22
+ return typeof v === "string" ? [v] : [];
23
+ }
24
+
25
+ /** The `@from` entity name of a report. */
26
+ export function reportFrom(obj: MetaData): string | undefined {
27
+ const v = obj.attr(OBJECT_REPORT_ATTR_FROM);
28
+ return typeof v === "string" ? v : undefined;
29
+ }
30
+
31
+ /** `@spine`: the to-one path to the entity whose rows supply the report's rows. */
32
+ export function reportSpine(obj: MetaData): string | undefined {
33
+ const v = obj.attr(OBJECT_REPORT_ATTR_SPINE);
34
+ return typeof v === "string" && v !== "" ? v : undefined;
35
+ }
36
+
37
+ /** The `@dimensions` items, each `name` or `name:grain`. */
38
+ export function reportDimensionItems(obj: MetaData): ReportDimensionItem[] {
39
+ return stringList(obj.attr(OBJECT_REPORT_ATTR_DIMENSIONS)).map((raw) => {
40
+ const i = raw.indexOf(REPORT_DIMENSION_GRAIN_SEPARATOR);
41
+ return i === -1 ? { name: raw } : { name: raw.slice(0, i), grain: raw.slice(i + 1) };
42
+ });
43
+ }
44
+
45
+ /** The `@measures` items AS WRITTEN: each a bare measure `name`, or a dotted
46
+ * `Entity.name` (loader rule R3). Use {@link reportMeasureItemName} for the measure name. */
47
+ export function reportMeasureNames(obj: MetaData): string[] {
48
+ return stringList(obj.attr(OBJECT_REPORT_ATTR_MEASURES));
49
+ }
50
+
51
+ /**
52
+ * The measure a `@measures` item names: the segment after its LAST `.`
53
+ * (`total`, `Sale.total` and `acme::shop::Sale.total` all name `total`). It is also
54
+ * the derived report field's name. The part before that `.`, when present, is an
55
+ * entity qualifier, which {@link reportMeasureItemOwner} returns.
56
+ */
57
+ export function reportMeasureItemName(item: string): string {
58
+ const dot = item.lastIndexOf(CHILD_REF_SEPARATOR);
59
+ return dot === -1 ? item : item.slice(dot + CHILD_REF_SEPARATOR.length);
60
+ }
61
+
62
+ /** The entity qualifier of a dotted `@measures` item (`Sale` in `Sale.total`), or
63
+ * undefined for a bare item. Loader rule R3: it names `@from` or an entity `@from` extends. */
64
+ export function reportMeasureItemOwner(item: string): string | undefined {
65
+ const dot = item.lastIndexOf(CHILD_REF_SEPARATOR);
66
+ return dot === -1 ? undefined : item.slice(0, dot);
67
+ }
68
+
69
+ /** The derived report field for a dimension item: `name` (attribute) or `name` + Capitalized(grain) (time). */
70
+ export function reportDerivedFieldName(item: ReportDimensionItem): string {
71
+ if (item.grain === undefined || item.grain === "") return item.name;
72
+ return item.name + item.grain.charAt(0).toUpperCase() + item.grain.slice(1);
73
+ }