@metaobjectsdev/metadata 0.8.1-rc.1 → 0.9.0-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 (220) hide show
  1. package/dist/attr-schema-validate.d.ts.map +1 -1
  2. package/dist/attr-schema-validate.js +49 -1
  3. package/dist/attr-schema-validate.js.map +1 -1
  4. package/dist/core/field/field-constants.d.ts +27 -2
  5. package/dist/core/field/field-constants.d.ts.map +1 -1
  6. package/dist/core/field/field-constants.js +30 -2
  7. package/dist/core/field/field-constants.js.map +1 -1
  8. package/dist/core/field/field-schema.d.ts +9 -1
  9. package/dist/core/field/field-schema.d.ts.map +1 -1
  10. package/dist/core/field/field-schema.js +34 -3
  11. package/dist/core/field/field-schema.js.map +1 -1
  12. package/dist/core/field/meta-field.d.ts +10 -0
  13. package/dist/core/field/meta-field.d.ts.map +1 -1
  14. package/dist/core/field/meta-field.js +36 -1
  15. package/dist/core/field/meta-field.js.map +1 -1
  16. package/dist/core/field/validate-field-readonly.d.ts +9 -0
  17. package/dist/core/field/validate-field-readonly.d.ts.map +1 -0
  18. package/dist/core/field/validate-field-readonly.js +116 -0
  19. package/dist/core/field/validate-field-readonly.js.map +1 -0
  20. package/dist/core/object/meta-object-aware.d.ts +11 -0
  21. package/dist/core/object/meta-object-aware.d.ts.map +1 -0
  22. package/dist/core/object/meta-object-aware.js +15 -0
  23. package/dist/core/object/meta-object-aware.js.map +1 -0
  24. package/dist/core/object/meta-object.d.ts +21 -0
  25. package/dist/core/object/meta-object.d.ts.map +1 -1
  26. package/dist/core/object/meta-object.js +43 -2
  27. package/dist/core/object/meta-object.js.map +1 -1
  28. package/dist/core/object/object-class-registry.d.ts +22 -0
  29. package/dist/core/object/object-class-registry.d.ts.map +1 -0
  30. package/dist/core/object/object-class-registry.js +38 -0
  31. package/dist/core/object/object-class-registry.js.map +1 -0
  32. package/dist/core/object/object-constants.d.ts +7 -0
  33. package/dist/core/object/object-constants.d.ts.map +1 -1
  34. package/dist/core/object/object-constants.js +14 -0
  35. package/dist/core/object/object-constants.js.map +1 -1
  36. package/dist/core/object/object-schema.d.ts.map +1 -1
  37. package/dist/core/object/object-schema.js +24 -1
  38. package/dist/core/object/object-schema.js.map +1 -1
  39. package/dist/core/object/validate-discriminator.d.ts +4 -0
  40. package/dist/core/object/validate-discriminator.d.ts.map +1 -0
  41. package/dist/core/object/validate-discriminator.js +145 -0
  42. package/dist/core/object/validate-discriminator.js.map +1 -0
  43. package/dist/core/object/value-object.d.ts +23 -0
  44. package/dist/core/object/value-object.d.ts.map +1 -0
  45. package/dist/core/object/value-object.js +51 -0
  46. package/dist/core/object/value-object.js.map +1 -0
  47. package/dist/core/query/query-constants.d.ts.map +1 -1
  48. package/dist/core/query/query-constants.js +4 -0
  49. package/dist/core/query/query-constants.js.map +1 -1
  50. package/dist/core-types.d.ts.map +1 -1
  51. package/dist/core-types.js +16 -4
  52. package/dist/core-types.js.map +1 -1
  53. package/dist/errors.d.ts +2 -2
  54. package/dist/errors.d.ts.map +1 -1
  55. package/dist/errors.js +23 -0
  56. package/dist/errors.js.map +1 -1
  57. package/dist/index.d.ts +5 -0
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/index.js +4 -0
  60. package/dist/index.js.map +1 -1
  61. package/dist/loader/meta-data-loader.d.ts.map +1 -1
  62. package/dist/loader/meta-data-loader.js +26 -1
  63. package/dist/loader/meta-data-loader.js.map +1 -1
  64. package/dist/loader/validation-passes.d.ts +1 -0
  65. package/dist/loader/validation-passes.d.ts.map +1 -1
  66. package/dist/loader/validation-passes.js +122 -2
  67. package/dist/loader/validation-passes.js.map +1 -1
  68. package/dist/naming.d.ts.map +1 -1
  69. package/dist/naming.js +7 -6
  70. package/dist/naming.js.map +1 -1
  71. package/dist/persistence/db/db-constants.d.ts +23 -0
  72. package/dist/persistence/db/db-constants.d.ts.map +1 -1
  73. package/dist/persistence/db/db-constants.js +31 -0
  74. package/dist/persistence/db/db-constants.js.map +1 -1
  75. package/dist/persistence/db/db-provider.js +3 -3
  76. package/dist/persistence/db/db-provider.js.map +1 -1
  77. package/dist/persistence/db/db-schema.d.ts +8 -0
  78. package/dist/persistence/db/db-schema.d.ts.map +1 -1
  79. package/dist/persistence/db/db-schema.js +17 -1
  80. package/dist/persistence/db/db-schema.js.map +1 -1
  81. package/dist/persistence/source/meta-source.d.ts +15 -1
  82. package/dist/persistence/source/meta-source.d.ts.map +1 -1
  83. package/dist/persistence/source/meta-source.js +55 -3
  84. package/dist/persistence/source/meta-source.js.map +1 -1
  85. package/dist/persistence/source/source-constants.d.ts +20 -1
  86. package/dist/persistence/source/source-constants.d.ts.map +1 -1
  87. package/dist/persistence/source/source-constants.js +36 -1
  88. package/dist/persistence/source/source-constants.js.map +1 -1
  89. package/dist/persistence/source/source-schema.d.ts.map +1 -1
  90. package/dist/persistence/source/source-schema.js +65 -4
  91. package/dist/persistence/source/source-schema.js.map +1 -1
  92. package/dist/persistence/source/validate-source-parameter-ref.d.ts +4 -0
  93. package/dist/persistence/source/validate-source-parameter-ref.d.ts.map +1 -0
  94. package/dist/persistence/source/validate-source-parameter-ref.js +96 -0
  95. package/dist/persistence/source/validate-source-parameter-ref.js.map +1 -0
  96. package/dist/persistence/source/validate-source-physical-names.d.ts +9 -0
  97. package/dist/persistence/source/validate-source-physical-names.d.ts.map +1 -0
  98. package/dist/persistence/source/validate-source-physical-names.js +79 -0
  99. package/dist/persistence/source/validate-source-physical-names.js.map +1 -0
  100. package/dist/serializer-json.d.ts.map +1 -1
  101. package/dist/serializer-json.js +43 -0
  102. package/dist/serializer-json.js.map +1 -1
  103. package/dist/template/template-constants.d.ts +9 -0
  104. package/dist/template/template-constants.d.ts.map +1 -1
  105. package/dist/template/template-constants.js +20 -0
  106. package/dist/template/template-constants.js.map +1 -1
  107. package/dist/template/template-schema.d.ts.map +1 -1
  108. package/dist/template/template-schema.js +42 -3
  109. package/dist/template/template-schema.js.map +1 -1
  110. package/package.json +1 -1
  111. package/src/attr-schema-validate.ts +70 -0
  112. package/src/core/field/field-constants.ts +37 -2
  113. package/src/core/field/field-schema.ts +43 -2
  114. package/src/core/field/meta-field.ts +39 -0
  115. package/src/core/field/validate-field-readonly.ts +142 -0
  116. package/src/core/object/meta-object-aware.ts +27 -0
  117. package/src/core/object/meta-object.ts +47 -2
  118. package/src/core/object/object-class-registry.ts +48 -0
  119. package/src/core/object/object-constants.ts +17 -0
  120. package/src/core/object/object-schema.ts +29 -1
  121. package/src/core/object/validate-discriminator.ts +195 -0
  122. package/src/core/object/value-object.ts +65 -0
  123. package/src/core/query/query-constants.ts +5 -0
  124. package/src/core-types.ts +17 -4
  125. package/src/errors.ts +23 -0
  126. package/src/index.ts +10 -0
  127. package/src/loader/meta-data-loader.ts +31 -1
  128. package/src/loader/validation-passes.ts +161 -0
  129. package/src/naming.ts +6 -5
  130. package/src/persistence/db/db-constants.ts +40 -0
  131. package/src/persistence/db/db-provider.ts +3 -3
  132. package/src/persistence/db/db-schema.ts +20 -0
  133. package/src/persistence/source/meta-source.ts +64 -2
  134. package/src/persistence/source/source-constants.ts +40 -1
  135. package/src/persistence/source/source-schema.ts +78 -3
  136. package/src/persistence/source/validate-source-parameter-ref.ts +143 -0
  137. package/src/persistence/source/validate-source-physical-names.ts +123 -0
  138. package/src/serializer-json.ts +50 -0
  139. package/src/template/template-constants.ts +23 -0
  140. package/src/template/template-schema.ts +49 -2
  141. package/dist/constants.d.ts +0 -208
  142. package/dist/constants.d.ts.map +0 -1
  143. package/dist/constants.js +0 -419
  144. package/dist/constants.js.map +0 -1
  145. package/dist/core/file-meta-data-loader.d.ts +0 -18
  146. package/dist/core/file-meta-data-loader.d.ts.map +0 -1
  147. package/dist/core/file-meta-data-loader.js +0 -81
  148. package/dist/core/file-meta-data-loader.js.map +0 -1
  149. package/dist/core/file-source.d.ts +0 -12
  150. package/dist/core/file-source.d.ts.map +0 -1
  151. package/dist/core/file-source.js +0 -46
  152. package/dist/core/file-source.js.map +0 -1
  153. package/dist/core-attr-schemas.d.ts +0 -22
  154. package/dist/core-attr-schemas.d.ts.map +0 -1
  155. package/dist/core-attr-schemas.js +0 -324
  156. package/dist/core-attr-schemas.js.map +0 -1
  157. package/dist/db/db-attr-schemas.d.ts +0 -8
  158. package/dist/db/db-attr-schemas.d.ts.map +0 -1
  159. package/dist/db/db-attr-schemas.js +0 -26
  160. package/dist/db/db-attr-schemas.js.map +0 -1
  161. package/dist/db/db-provider.d.ts +0 -3
  162. package/dist/db/db-provider.d.ts.map +0 -1
  163. package/dist/db/db-provider.js +0 -28
  164. package/dist/db/db-provider.js.map +0 -1
  165. package/dist/meta/find-reference.d.ts +0 -22
  166. package/dist/meta/find-reference.d.ts.map +0 -1
  167. package/dist/meta/find-reference.js +0 -29
  168. package/dist/meta/find-reference.js.map +0 -1
  169. package/dist/meta/meta-attr.d.ts +0 -8
  170. package/dist/meta/meta-attr.d.ts.map +0 -1
  171. package/dist/meta/meta-attr.js +0 -17
  172. package/dist/meta/meta-attr.js.map +0 -1
  173. package/dist/meta/meta-data.d.ts +0 -107
  174. package/dist/meta/meta-data.d.ts.map +0 -1
  175. package/dist/meta/meta-data.js +0 -302
  176. package/dist/meta/meta-data.js.map +0 -1
  177. package/dist/meta/meta-field.d.ts +0 -48
  178. package/dist/meta/meta-field.d.ts.map +0 -1
  179. package/dist/meta/meta-field.js +0 -94
  180. package/dist/meta/meta-field.js.map +0 -1
  181. package/dist/meta/meta-identity.d.ts +0 -71
  182. package/dist/meta/meta-identity.d.ts.map +0 -1
  183. package/dist/meta/meta-identity.js +0 -129
  184. package/dist/meta/meta-identity.js.map +0 -1
  185. package/dist/meta/meta-layout.d.ts +0 -23
  186. package/dist/meta/meta-layout.d.ts.map +0 -1
  187. package/dist/meta/meta-layout.js +0 -45
  188. package/dist/meta/meta-layout.js.map +0 -1
  189. package/dist/meta/meta-object.d.ts +0 -40
  190. package/dist/meta/meta-object.d.ts.map +0 -1
  191. package/dist/meta/meta-object.js +0 -81
  192. package/dist/meta/meta-object.js.map +0 -1
  193. package/dist/meta/meta-origin.d.ts +0 -32
  194. package/dist/meta/meta-origin.d.ts.map +0 -1
  195. package/dist/meta/meta-origin.js +0 -55
  196. package/dist/meta/meta-origin.js.map +0 -1
  197. package/dist/meta/meta-relationship.d.ts +0 -11
  198. package/dist/meta/meta-relationship.d.ts.map +0 -1
  199. package/dist/meta/meta-relationship.js +0 -27
  200. package/dist/meta/meta-relationship.js.map +0 -1
  201. package/dist/meta/meta-root.d.ts +0 -12
  202. package/dist/meta/meta-root.d.ts.map +0 -1
  203. package/dist/meta/meta-root.js +0 -24
  204. package/dist/meta/meta-root.js.map +0 -1
  205. package/dist/meta/meta-source.d.ts +0 -18
  206. package/dist/meta/meta-source.d.ts.map +0 -1
  207. package/dist/meta/meta-source.js +0 -31
  208. package/dist/meta/meta-source.js.map +0 -1
  209. package/dist/meta/meta-validator.d.ts +0 -29
  210. package/dist/meta/meta-validator.d.ts.map +0 -1
  211. package/dist/meta/meta-validator.js +0 -49
  212. package/dist/meta/meta-validator.js.map +0 -1
  213. package/dist/meta/meta-view.d.ts +0 -4
  214. package/dist/meta/meta-view.d.ts.map +0 -1
  215. package/dist/meta/meta-view.js +0 -8
  216. package/dist/meta/meta-view.js.map +0 -1
  217. package/dist/persistence/db/db-attr-schemas.d.ts +0 -8
  218. package/dist/persistence/db/db-attr-schemas.d.ts.map +0 -1
  219. package/dist/persistence/db/db-attr-schemas.js +0 -28
  220. package/dist/persistence/db/db-attr-schemas.js.map +0 -1
@@ -1,6 +1,46 @@
1
1
  // DB concern constants — physical DB column attr keys.
2
2
 
3
+ import {
4
+ FIELD_SUBTYPE_STRING,
5
+ FIELD_SUBTYPE_TIMESTAMP,
6
+ } from "../../core/field/field-constants.js";
7
+
3
8
  /** Column name override on a field (maps to @column in metadata). */
4
9
  export const FIELD_ATTR_COLUMN = "column";
5
10
  /** When true, suppress the @filterable-without-index Loader warning (Project D drift check). */
6
11
  export const FIELD_ATTR_DB_INDEXED = "db.indexed";
12
+
13
+ /**
14
+ * R6 Plan 2b: `@dbColumnType` — physical DB column-type override on a field.
15
+ * Selects the DB column type WITHOUT changing the logical field type or its
16
+ * native binding (ADR-0013 — the canonical physical escape hatch). Registered
17
+ * by the dbProvider, validated as a (logical subtype × value) pairing.
18
+ */
19
+ export const FIELD_ATTR_DB_COLUMN_TYPE = "dbColumnType";
20
+
21
+ /** `@dbColumnType: uuid` — native Postgres `uuid` column (legal on field.string). */
22
+ export const DB_COLUMN_TYPE_UUID = "uuid";
23
+ /** `@dbColumnType: jsonb` — genuinely-open `jsonb` column (legal on field.string). */
24
+ export const DB_COLUMN_TYPE_JSONB = "jsonb";
25
+ /** `@dbColumnType: timestamp_with_tz` — `timestamp with time zone` (legal on field.timestamp). */
26
+ export const DB_COLUMN_TYPE_TIMESTAMP_WITH_TZ = "timestamp_with_tz";
27
+
28
+ /** The closed set of legal `@dbColumnType` values (raw-dialect passthrough deferred). */
29
+ export const DB_COLUMN_TYPE_VALUES = [
30
+ DB_COLUMN_TYPE_UUID,
31
+ DB_COLUMN_TYPE_JSONB,
32
+ DB_COLUMN_TYPE_TIMESTAMP_WITH_TZ,
33
+ ] as const;
34
+ export type DbColumnTypeValue = (typeof DB_COLUMN_TYPE_VALUES)[number];
35
+
36
+ /**
37
+ * Legal `@dbColumnType` value → the field subtypes it may be applied to (ADR-0013,
38
+ * R6 Plan 2b). Any other (subtype × value) pairing — or an unrecognized value — is
39
+ * an ERR_BAD_ATTR_VALUE. Keyed by value; the value-set is enforced by membership in
40
+ * this map. Subtype names are the bare FIELD_SUBTYPE_* constants (string / timestamp).
41
+ */
42
+ export const DB_COLUMN_TYPE_LEGAL_SUBTYPES: Readonly<Record<DbColumnTypeValue, readonly string[]>> = {
43
+ [DB_COLUMN_TYPE_UUID]: [FIELD_SUBTYPE_STRING],
44
+ [DB_COLUMN_TYPE_JSONB]: [FIELD_SUBTYPE_STRING],
45
+ [DB_COLUMN_TYPE_TIMESTAMP_WITH_TZ]: [FIELD_SUBTYPE_TIMESTAMP],
46
+ } as const;
@@ -8,18 +8,18 @@ import type { TypeRegistry } from "../../registry.js";
8
8
  import { TYPE_FIELD, TYPE_SOURCE } from "../../shared/base-types.js";
9
9
  import { FIELD_SUBTYPES } from "../../core/field/field-constants.js";
10
10
  import { SOURCE_SUBTYPE_RDB } from "../source/source-constants.js";
11
- import { columnSchema, dbIndexedSchema } from "./db-schema.js";
11
+ import { columnSchema, dbIndexedSchema, dbColumnTypeSchema } from "./db-schema.js";
12
12
  import { sourceRdbAttrs } from "../source/source-schema.js";
13
13
 
14
14
  export const dbProvider: MetaDataTypeProvider = {
15
15
  id: "metaobjects-db",
16
16
  dependencies: ["metaobjects-core-types"],
17
17
  description:
18
- "DB-domain attributes — @column / @db.indexed on fields, @table/@kind/@role/@schema on source.rdb.",
18
+ "DB-domain attributes — @column / @db.indexed / @dbColumnType on fields, @table/@kind/@role/@schema on source.rdb.",
19
19
  registerTypes(registry: TypeRegistry): void {
20
20
  for (const subType of FIELD_SUBTYPES) {
21
21
  registry.extend(TYPE_FIELD, subType, {
22
- attributes: [columnSchema, dbIndexedSchema],
22
+ attributes: [columnSchema, dbIndexedSchema, dbColumnTypeSchema],
23
23
  });
24
24
  }
25
25
  // source.rdb — @table/@kind/@role/@schema attrs.
@@ -9,6 +9,8 @@ import {
9
9
  import {
10
10
  FIELD_ATTR_COLUMN,
11
11
  FIELD_ATTR_DB_INDEXED,
12
+ FIELD_ATTR_DB_COLUMN_TYPE,
13
+ DB_COLUMN_TYPE_VALUES,
12
14
  } from "./db-constants.js";
13
15
 
14
16
  /** `@column` — column-name override on every field subtype (source.rdb). */
@@ -28,3 +30,21 @@ export const dbIndexedSchema: AttrSchema = {
28
30
  description:
29
31
  "When true, suppress the @filterable-without-index Loader warning (the field is indexed by other means).",
30
32
  };
33
+
34
+ /**
35
+ * R6 Plan 2b: `@dbColumnType` — physical DB column-type override on every field
36
+ * subtype. The legal value depends on the field's logical subtype, so the
37
+ * subtype × value pairing is validated own-only by the loader
38
+ * (validateAttrSchema → ERR_BAD_ATTR_VALUE), not by a flat `allowedValues` set.
39
+ * The string valueType keeps the schema's type check intact.
40
+ */
41
+ export const dbColumnTypeSchema: AttrSchema = {
42
+ name: FIELD_ATTR_DB_COLUMN_TYPE,
43
+ valueType: ATTR_SUBTYPE_STRING,
44
+ required: false,
45
+ description:
46
+ "Physical DB column-type override (ADR-0013 escape hatch). Legal values are " +
47
+ `${DB_COLUMN_TYPE_VALUES.join(" | ")}, each legal only on a specific logical ` +
48
+ "field subtype (uuid/jsonb on field.string, timestamp_with_tz on field.timestamp). " +
49
+ "The logical field type and its native binding are unchanged.",
50
+ };
@@ -1,21 +1,40 @@
1
1
  // MetaSource — concrete node class for type=source nodes.
2
2
  // Declares where an object's data lives (Project E).
3
- // source.rdb uses @table/@kind/@role/@schema; read-only is derived from @kind.
3
+ // source.rdb uses kind-aware physical-name aliases + @kind/@role/@schema;
4
+ // read-only-ness is derived from @kind.
4
5
  //
5
6
  // Extends MetaData directly: no model wrapper, no metaOf() indirection.
6
7
 
7
8
  import { MetaData } from "../../shared/meta-data.js";
9
+ import { pluralize, toSnakeCase } from "../../naming.js";
8
10
  import {
9
11
  SOURCE_ATTR_TABLE,
12
+ SOURCE_ATTR_VIEW,
13
+ SOURCE_ATTR_MATERIALIZED_VIEW,
14
+ SOURCE_ATTR_PROC,
15
+ SOURCE_ATTR_FUNCTION,
10
16
  SOURCE_ATTR_KIND,
11
17
  SOURCE_ATTR_ROLE,
12
18
  SOURCE_READ_ONLY_KINDS,
13
19
  DEFAULT_SOURCE_KIND,
14
20
  DEFAULT_SOURCE_ROLE,
21
+ PHYSICAL_NAME_ATTR_BY_KIND,
15
22
  } from "./source-constants.js";
16
23
 
24
+ /** Every kind-aware physical-name alias key, ordered for deterministic
25
+ * iteration (matches the SOURCE_RDB_KINDS order). */
26
+ const ALL_PHYSICAL_NAME_ALIASES = [
27
+ SOURCE_ATTR_TABLE,
28
+ SOURCE_ATTR_VIEW,
29
+ SOURCE_ATTR_MATERIALIZED_VIEW,
30
+ SOURCE_ATTR_PROC,
31
+ SOURCE_ATTR_FUNCTION,
32
+ ] as const;
33
+
17
34
  export class MetaSource extends MetaData {
18
- /** Physical SQL table/view name from @table (source.rdb). */
35
+ /** Physical SQL table/view name from the legacy @table attr. Kept as a
36
+ * back-compat accessor that reads ONLY the @table slot — callers should
37
+ * use physicalName for the FR-016 four-step rule. */
19
38
  get tableName(): string | undefined {
20
39
  const v = this.ownAttr(SOURCE_ATTR_TABLE);
21
40
  return typeof v === "string" && v !== "" ? v : undefined;
@@ -48,4 +67,47 @@ export class MetaSource extends MetaData {
48
67
  isWritable(): boolean {
49
68
  return !this.isReadOnly();
50
69
  }
70
+
71
+ /**
72
+ * Resolved physical SQL name for this source, following the FR-016 / ADR-0018
73
+ * four-step rule:
74
+ * 1. Kind-matching alias (e.g. @proc when @kind: "storedProc").
75
+ * 2. Legacy @table for non-table kind (pre-1.0 fallback).
76
+ * 3. Source's bare structural `name` via snake_case.
77
+ * 4. Owning entity's name via pluralize(snake_case).
78
+ *
79
+ * Callers needing the legacy raw @table slot only should use `tableName`;
80
+ * codegen / migrate / runtime should use `physicalName`.
81
+ */
82
+ get physicalName(): string {
83
+ const kind = this.effectiveKind;
84
+
85
+ // Step 1: kind-matching alias.
86
+ const canonicalAttr = PHYSICAL_NAME_ATTR_BY_KIND.get(kind);
87
+ if (canonicalAttr !== undefined) {
88
+ const v = this.ownAttr(canonicalAttr);
89
+ if (typeof v === "string" && v !== "") return v;
90
+ }
91
+
92
+ // Step 2: legacy @table for non-table kind.
93
+ if (canonicalAttr !== SOURCE_ATTR_TABLE) {
94
+ const legacy = this.ownAttr(SOURCE_ATTR_TABLE);
95
+ if (typeof legacy === "string" && legacy !== "") return legacy;
96
+ }
97
+
98
+ // Step 3: source's structural `name` via snake_case (no pluralization —
99
+ // the source's name IS the logical name).
100
+ if (this.name !== "") {
101
+ return toSnakeCase(this.name);
102
+ }
103
+
104
+ // Step 4: owning entity's name via pluralize(snake_case). The MetaData
105
+ // parent of a source is always the entity that declared it.
106
+ const owner = this.parent;
107
+ if (owner !== undefined && owner.name !== "") {
108
+ return pluralize(toSnakeCase(owner.name));
109
+ }
110
+
111
+ return "";
112
+ }
51
113
  }
@@ -30,13 +30,40 @@ export const DEFAULT_DB_SCHEMA_POSTGRES = "public";
30
30
  // Source v2 (ADR-0007) — rdb paradigm: kind, role, physical table name.
31
31
  // ---------------------------------------------------------------------------
32
32
 
33
- /** Physical table/view name on source.rdb. */
33
+ // FR-016 / ADR-0007 point 2: the source's "logical name" is the structural
34
+ // `name` field every MetaData node already carries — accessed via `source.name`,
35
+ // NOT an @-attr. (An @-attr would conflict with the ERR_RESERVED_ATTR rule for
36
+ // the reserved structural key `name`.) See the four-step physical-name
37
+ // resolution rule on MetaSource.physicalName.
38
+
39
+ // --- Per-kind physical-name aliases (FR-016 / ADR-0018) -----------------------
40
+ // One canonical attr key per rdb @kind. The single internal physical-name
41
+ // "slot" on the source is filled by whichever alias matches @kind; the
42
+ // canonical serializer emits the kind-matching alias regardless of which
43
+ // spelling was on input.
44
+ /** Physical SQL table name. Canonical attr for @kind: "table" (default). */
34
45
  export const SOURCE_ATTR_TABLE = "table";
46
+ /** Physical SQL view name. Canonical attr for @kind: "view". */
47
+ export const SOURCE_ATTR_VIEW = "view";
48
+ /** Physical SQL materialized-view name. Canonical attr for @kind: "materializedView". */
49
+ export const SOURCE_ATTR_MATERIALIZED_VIEW = "materializedView";
50
+ /** Physical SQL stored-procedure name. Canonical attr for @kind: "storedProc". */
51
+ export const SOURCE_ATTR_PROC = "proc";
52
+ /** Physical SQL table-function name. Canonical attr for @kind: "tableFunction". */
53
+ export const SOURCE_ATTR_FUNCTION = "function";
54
+
35
55
  /** Object kind within the rdb paradigm; read-only-ness is derived from it. */
36
56
  export const SOURCE_ATTR_KIND = "kind";
37
57
  /** Multi-source role; exactly one primary per object. */
38
58
  export const SOURCE_ATTR_ROLE = "role";
39
59
 
60
+ /** FR-015: reference to an object.value describing the input shape of a callable
61
+ * source. Required for @kind: "storedProc" | "tableFunction" that take args;
62
+ * ignored for non-callable kinds. Wire-format symmetric with template.@payloadRef
63
+ * (FR-004) — the typed-input pattern reuses object.value rather than minting a
64
+ * new parameter.* node type. */
65
+ export const SOURCE_ATTR_PARAMETER_REF = "parameterRef";
66
+
40
67
  export const SOURCE_KIND_TABLE = "table";
41
68
  export const SOURCE_KIND_VIEW = "view";
42
69
  export const SOURCE_KIND_MATERIALIZED_VIEW = "materializedView";
@@ -52,6 +79,18 @@ export const SOURCE_RDB_KINDS = [
52
79
  ] as const;
53
80
  export type SourceRdbKind = (typeof SOURCE_RDB_KINDS)[number];
54
81
 
82
+ /** Map @kind → canonical kind-aware physical-name attr key (FR-016 / ADR-0018).
83
+ * Drives the four-step physical-name resolution rule and the canonical-serializer
84
+ * per-kind rewrite. The single internal physical-name "slot" on a source is the
85
+ * value of whichever alias matches the source's @kind. */
86
+ export const PHYSICAL_NAME_ATTR_BY_KIND: ReadonlyMap<string, string> = new Map([
87
+ [SOURCE_KIND_TABLE, SOURCE_ATTR_TABLE],
88
+ [SOURCE_KIND_VIEW, SOURCE_ATTR_VIEW],
89
+ [SOURCE_KIND_MATERIALIZED_VIEW, SOURCE_ATTR_MATERIALIZED_VIEW],
90
+ [SOURCE_KIND_STORED_PROC, SOURCE_ATTR_PROC],
91
+ [SOURCE_KIND_TABLE_FUNCTION, SOURCE_ATTR_FUNCTION],
92
+ ]);
93
+
55
94
  /** rdb @kind default when omitted (writable table). */
56
95
  export const DEFAULT_SOURCE_KIND = SOURCE_KIND_TABLE;
57
96
 
@@ -5,20 +5,67 @@ import type { AttrSchema } from "../../registry.js";
5
5
  import { ATTR_SUBTYPE_STRING } from "../../core/attr/attr-constants.js";
6
6
  import {
7
7
  SOURCE_ATTR_TABLE,
8
+ SOURCE_ATTR_VIEW,
9
+ SOURCE_ATTR_MATERIALIZED_VIEW,
10
+ SOURCE_ATTR_PROC,
11
+ SOURCE_ATTR_FUNCTION,
8
12
  SOURCE_ATTR_KIND,
9
13
  SOURCE_ATTR_ROLE,
10
14
  SOURCE_ATTR_SCHEMA,
15
+ SOURCE_ATTR_PARAMETER_REF,
11
16
  SOURCE_RDB_KINDS,
12
17
  SOURCE_ROLES,
13
18
  } from "./source-constants.js";
14
19
 
15
- /** `@table` — physical SQL table or view name for source.rdb. */
20
+ /** `@table` — physical SQL table name for @kind: "table" (default). FR-016: one
21
+ * of five kind-aware physical-name aliases; all write to the same internal slot.
22
+ * Pre-1.0 legacy: also accepted with non-table @kind (canonical-serializer
23
+ * rewrites; loader emits WARN_LEGACY_PHYSICAL_NAME_ALIAS). */
16
24
  const tableSchema: AttrSchema = {
17
25
  name: SOURCE_ATTR_TABLE,
18
26
  valueType: ATTR_SUBTYPE_STRING,
19
27
  required: false,
20
28
  description:
21
- "Physical SQL table or view name for this rdb source. Defaults to the object name run through the project's columnNamingStrategy when omitted.",
29
+ "Physical SQL table name for source.rdb @kind: \"table\" (default). FR-016: " +
30
+ "Defaults from the source's bare structural `name` via the project's columnNamingStrategy " +
31
+ "when omitted, then from the owning entity's name. Pre-1.0 legacy spelling for " +
32
+ "view/materializedView/storedProc/tableFunction kinds during the transition; " +
33
+ "canonical-serializer rewrites to the kind-matching alias.",
34
+ };
35
+
36
+ /** `@view` — physical SQL view name for @kind: "view" (FR-016 / ADR-0018). */
37
+ const viewSchema: AttrSchema = {
38
+ name: SOURCE_ATTR_VIEW,
39
+ valueType: ATTR_SUBTYPE_STRING,
40
+ required: false,
41
+ description: "Physical SQL view name for source.rdb @kind: \"view\". Same internal slot as @table.",
42
+ };
43
+
44
+ /** `@materializedView` — physical SQL materialized-view name for @kind: "materializedView". */
45
+ const materializedViewSchema: AttrSchema = {
46
+ name: SOURCE_ATTR_MATERIALIZED_VIEW,
47
+ valueType: ATTR_SUBTYPE_STRING,
48
+ required: false,
49
+ description:
50
+ "Physical SQL materialized-view name for source.rdb @kind: \"materializedView\". Same internal slot as @table.",
51
+ };
52
+
53
+ /** `@proc` — physical SQL stored-procedure name for @kind: "storedProc". */
54
+ const procSchema: AttrSchema = {
55
+ name: SOURCE_ATTR_PROC,
56
+ valueType: ATTR_SUBTYPE_STRING,
57
+ required: false,
58
+ description:
59
+ "Physical SQL stored-procedure name for source.rdb @kind: \"storedProc\". Same internal slot as @table.",
60
+ };
61
+
62
+ /** `@function` — physical SQL table-function name for @kind: "tableFunction". */
63
+ const functionSchema: AttrSchema = {
64
+ name: SOURCE_ATTR_FUNCTION,
65
+ valueType: ATTR_SUBTYPE_STRING,
66
+ required: false,
67
+ description:
68
+ "Physical SQL table-function name for source.rdb @kind: \"tableFunction\". Same internal slot as @table.",
22
69
  };
23
70
 
24
71
  /** `@kind` — object kind within the rdb paradigm; drives read-only derivation. */
@@ -50,5 +97,33 @@ const schemaSchema: AttrSchema = {
50
97
  "Optional database schema name (e.g. 'catalog', 'public'). Postgres defaults to 'public'; SQLite rejects any non-default value.",
51
98
  };
52
99
 
100
+ /** `@parameterRef` — name or FQN of an object.value describing the input shape
101
+ * of a callable source (FR-015). Required when @kind is "storedProc" or
102
+ * "tableFunction" and the proc takes args; ignored for non-callable kinds.
103
+ * Mirrors template.@payloadRef from FR-004. */
104
+ const parameterRefSchema: AttrSchema = {
105
+ name: SOURCE_ATTR_PARAMETER_REF,
106
+ valueType: ATTR_SUBTYPE_STRING,
107
+ required: false,
108
+ description:
109
+ "FR-015: name or FQN of an object.value describing the input shape of " +
110
+ "this source's callable interface. Permitted on @kind: \"storedProc\" / " +
111
+ "\"tableFunction\"; rejected on non-callable kinds (table / view / " +
112
+ "materializedView). Field children of the referenced object.value become " +
113
+ "the call-site parameter list in declaration order. Symmetric with " +
114
+ "template.@payloadRef in FR-004 — the typed-input pattern reuses " +
115
+ "object.value rather than minting a new parameter.* node type.",
116
+ };
117
+
53
118
  /** All attr schemas for source.rdb, to be registered via registry.extend. */
54
- export const sourceRdbAttrs: AttrSchema[] = [tableSchema, kindSchema, roleSchema, schemaSchema];
119
+ export const sourceRdbAttrs: AttrSchema[] = [
120
+ tableSchema,
121
+ viewSchema,
122
+ materializedViewSchema,
123
+ procSchema,
124
+ functionSchema,
125
+ kindSchema,
126
+ roleSchema,
127
+ schemaSchema,
128
+ parameterRefSchema,
129
+ ];
@@ -0,0 +1,143 @@
1
+ // Validation pass: source.rdb @parameterRef typed-input rules (FR-015).
2
+ //
3
+ // Codes:
4
+ // ERR_PARAMETER_REF_UNRESOLVED — @parameterRef names a non-existent object.
5
+ // ERR_PARAMETER_REF_NOT_VALUE_OBJECT — @parameterRef points at an object.entity
6
+ // instead of object.value.
7
+ // ERR_PARAMETER_REF_ON_NON_CALLABLE_KIND
8
+ // — @parameterRef set with @kind: "table" /
9
+ // "view" / "materializedView". Only the
10
+ // callable kinds (storedProc, tableFunction)
11
+ // accept parameters.
12
+ // ERR_PARAMETER_REF_PASSTHROUGH_TYPE_MISMATCH
13
+ // — a parameter field uses origin.passthrough
14
+ // @from: "Entity.field" but the parameter's
15
+ // subtype does not match the referenced
16
+ // field's subtype.
17
+
18
+ import type { MetaData } from "../../shared/meta-data.js";
19
+ import { ParseError } from "../../errors.js";
20
+ import {
21
+ TYPE_OBJECT,
22
+ TYPE_SOURCE,
23
+ TYPE_FIELD,
24
+ TYPE_ORIGIN,
25
+ } from "../../shared/base-types.js";
26
+ import {
27
+ OBJECT_SUBTYPE_VALUE,
28
+ OBJECT_SUBTYPE_ENTITY,
29
+ } from "../../core/object/object-constants.js";
30
+ import { PACKAGE_SEPARATOR } from "../../shared/structural.js";
31
+ import { MetaSource } from "./meta-source.js";
32
+ import {
33
+ SOURCE_ATTR_PARAMETER_REF,
34
+ SOURCE_SUBTYPE_RDB,
35
+ SOURCE_KIND_STORED_PROC,
36
+ SOURCE_KIND_TABLE_FUNCTION,
37
+ } from "./source-constants.js";
38
+ import {
39
+ ORIGIN_SUBTYPE_PASSTHROUGH,
40
+ ORIGIN_PASSTHROUGH_ATTR_FROM,
41
+ } from "../../persistence/origin/origin-constants.js";
42
+
43
+ const CALLABLE_KINDS = new Set<string>([
44
+ SOURCE_KIND_STORED_PROC,
45
+ SOURCE_KIND_TABLE_FUNCTION,
46
+ ]);
47
+
48
+ export function validateSourceParameterRef(root: MetaData): ParseError[] {
49
+ const errors: ParseError[] = [];
50
+
51
+ // Pre-index every object by name AND fqn so resolution costs O(1) per source.
52
+ const objectIndex = new Map<string, MetaData>();
53
+ for (const obj of root.ownChildren().filter((c) => c.type === TYPE_OBJECT)) {
54
+ objectIndex.set(obj.name, obj);
55
+ const fqn = obj.package !== undefined && obj.package !== ""
56
+ ? `${obj.package}${PACKAGE_SEPARATOR}${obj.name}`
57
+ : obj.name;
58
+ objectIndex.set(fqn, obj);
59
+ }
60
+
61
+ for (const obj of root.ownChildren().filter((c) => c.type === TYPE_OBJECT)) {
62
+ for (const source of obj.ownChildren().filter(
63
+ (c): c is MetaSource =>
64
+ c.type === TYPE_SOURCE && c.subType === SOURCE_SUBTYPE_RDB && c instanceof MetaSource,
65
+ )) {
66
+ const ref = source.ownAttr(SOURCE_ATTR_PARAMETER_REF);
67
+ if (typeof ref !== "string" || ref === "") continue;
68
+
69
+ // ERR_PARAMETER_REF_ON_NON_CALLABLE_KIND — checked even before resolution
70
+ // so authoring mistakes on the wrong kind surface immediately.
71
+ if (!CALLABLE_KINDS.has(source.effectiveKind)) {
72
+ errors.push(
73
+ new ParseError(
74
+ `source.rdb on object "${obj.name}" has @parameterRef but @kind is "${source.effectiveKind}"; ` +
75
+ `only "storedProc" or "tableFunction" accept parameters`,
76
+ { code: "ERR_PARAMETER_REF_ON_NON_CALLABLE_KIND", source: source.source },
77
+ ),
78
+ );
79
+ continue;
80
+ }
81
+
82
+ const target = objectIndex.get(ref);
83
+ if (target === undefined) {
84
+ errors.push(
85
+ new ParseError(
86
+ `source.rdb on object "${obj.name}" @parameterRef = "${ref}" does not resolve to any known object`,
87
+ { code: "ERR_PARAMETER_REF_UNRESOLVED", source: source.source },
88
+ ),
89
+ );
90
+ continue;
91
+ }
92
+
93
+ if (target.subType !== OBJECT_SUBTYPE_VALUE) {
94
+ const reason = target.subType === OBJECT_SUBTYPE_ENTITY
95
+ ? "an object.entity (entities have identity; parameter shapes are value-objects)"
96
+ : `an object.${target.subType}`;
97
+ errors.push(
98
+ new ParseError(
99
+ `source.rdb on object "${obj.name}" @parameterRef = "${ref}" resolves to ${reason}; ` +
100
+ `use an object.value`,
101
+ { code: "ERR_PARAMETER_REF_NOT_VALUE_OBJECT", source: source.source },
102
+ ),
103
+ );
104
+ continue;
105
+ }
106
+
107
+ // ERR_PARAMETER_REF_PASSTHROUGH_TYPE_MISMATCH — every parameter field
108
+ // with origin.passthrough must have a subtype matching the referenced
109
+ // field. The origin path validation pass checks the from-path resolves;
110
+ // here we just check the subtype alignment.
111
+ for (const paramField of target.ownChildren().filter((c) => c.type === TYPE_FIELD)) {
112
+ const passthrough = paramField.ownChildren().find(
113
+ (c) => c.type === TYPE_ORIGIN && c.subType === ORIGIN_SUBTYPE_PASSTHROUGH,
114
+ );
115
+ if (passthrough === undefined) continue;
116
+ const from = passthrough.ownAttr(ORIGIN_PASSTHROUGH_ATTR_FROM);
117
+ if (typeof from !== "string" || from === "") continue;
118
+ const dot = from.indexOf(".");
119
+ if (dot < 0) continue;
120
+ const targetEntityName = from.slice(0, dot);
121
+ const targetFieldName = from.slice(dot + 1);
122
+ const targetEntity = objectIndex.get(targetEntityName);
123
+ if (targetEntity === undefined) continue; // origin-paths pass surfaces this
124
+ const targetField = targetEntity.ownChildren().find(
125
+ (c) => c.type === TYPE_FIELD && c.name === targetFieldName,
126
+ );
127
+ if (targetField === undefined) continue;
128
+ if (paramField.subType !== targetField.subType) {
129
+ errors.push(
130
+ new ParseError(
131
+ `parameter field "${paramField.name}" (field.${paramField.subType}) on @parameterRef ` +
132
+ `"${ref}" uses origin.passthrough @from: "${from}", but ` +
133
+ `${targetEntity.name}.${targetFieldName} is field.${targetField.subType}; types must match`,
134
+ { code: "ERR_PARAMETER_REF_PASSTHROUGH_TYPE_MISMATCH", source: paramField.source },
135
+ ),
136
+ );
137
+ }
138
+ }
139
+ }
140
+ }
141
+
142
+ return errors;
143
+ }
@@ -0,0 +1,123 @@
1
+ // Validation pass: per-kind physical-name aliases on source.rdb (FR-016 / ADR-0018).
2
+ //
3
+ // Each source.rdb may declare at most one of @table / @view / @materializedView /
4
+ // @proc / @function. The chosen alias must match the source's @kind, with one
5
+ // pre-1.0 legacy exception: @table is also accepted for non-table kinds (e.g.
6
+ // @kind: "storedProc" + @table: "fn_x"), which emits a WARN_LEGACY_PHYSICAL_NAME_ALIAS.
7
+ //
8
+ // Codes:
9
+ // ERR_PHYSICAL_NAME_MULTIPLE — two or more kind-aware aliases on one source.
10
+ // ERR_PHYSICAL_NAME_KIND_MISMATCH — alias other than @table set with a non-matching @kind.
11
+ // WARN_LEGACY_PHYSICAL_NAME_ALIAS — @table set with a non-table @kind (legacy spelling).
12
+
13
+ import type { MetaData } from "../../shared/meta-data.js";
14
+ import { ParseError } from "../../errors.js";
15
+ import type { LoaderWarning } from "../../source.js";
16
+ import { TYPE_OBJECT, TYPE_SOURCE } from "../../shared/base-types.js";
17
+ import { MetaSource } from "./meta-source.js";
18
+ import {
19
+ SOURCE_ATTR_TABLE,
20
+ SOURCE_ATTR_VIEW,
21
+ SOURCE_ATTR_MATERIALIZED_VIEW,
22
+ SOURCE_ATTR_PROC,
23
+ SOURCE_ATTR_FUNCTION,
24
+ SOURCE_SUBTYPE_RDB,
25
+ PHYSICAL_NAME_ATTR_BY_KIND,
26
+ } from "./source-constants.js";
27
+
28
+ const ALL_PHYSICAL_NAME_ALIASES = [
29
+ SOURCE_ATTR_TABLE,
30
+ SOURCE_ATTR_VIEW,
31
+ SOURCE_ATTR_MATERIALIZED_VIEW,
32
+ SOURCE_ATTR_PROC,
33
+ SOURCE_ATTR_FUNCTION,
34
+ ] as const;
35
+
36
+ export interface PhysicalNameValidationResult {
37
+ errors: ParseError[];
38
+ warnings: LoaderWarning[];
39
+ }
40
+
41
+ export function validateSourcePhysicalNames(root: MetaData): PhysicalNameValidationResult {
42
+ const errors: ParseError[] = [];
43
+ const warnings: LoaderWarning[] = [];
44
+
45
+ for (const obj of root.ownChildren().filter((c) => c.type === TYPE_OBJECT)) {
46
+ const sources = obj
47
+ .ownChildren()
48
+ .filter(
49
+ (c): c is MetaSource =>
50
+ c.type === TYPE_SOURCE && c.subType === SOURCE_SUBTYPE_RDB && c instanceof MetaSource,
51
+ );
52
+
53
+ for (const source of sources) {
54
+ // Empty-string check first — explicit "" is meaningless and an
55
+ // authoring error regardless of which alias was used.
56
+ for (const attr of ALL_PHYSICAL_NAME_ALIASES) {
57
+ const v = source.ownAttr(attr);
58
+ if (typeof v === "string" && v === "") {
59
+ errors.push(
60
+ new ParseError(
61
+ `source.rdb on object "${obj.name}" sets @${attr} to an empty string; physical name attrs require a non-empty value`,
62
+ { code: "ERR_BAD_ATTR_VALUE", source: source.source },
63
+ ),
64
+ );
65
+ }
66
+ }
67
+
68
+ const setAliases = ALL_PHYSICAL_NAME_ALIASES.filter((attr) => {
69
+ const v = source.ownAttr(attr);
70
+ return typeof v === "string" && v !== "";
71
+ });
72
+
73
+ if (setAliases.length > 1) {
74
+ errors.push(
75
+ new ParseError(
76
+ `source.rdb on object "${obj.name}" declares multiple physical-name aliases (${setAliases
77
+ .map((a) => `@${a}`)
78
+ .join(", ")}); set exactly one`,
79
+ { code: "ERR_PHYSICAL_NAME_MULTIPLE", source: source.source },
80
+ ),
81
+ );
82
+ continue;
83
+ }
84
+
85
+ if (setAliases.length === 0) continue;
86
+
87
+ const chosenAlias = setAliases[0]!;
88
+ const expectedAlias = PHYSICAL_NAME_ATTR_BY_KIND.get(source.effectiveKind);
89
+
90
+ if (chosenAlias === expectedAlias) continue;
91
+
92
+ // Legacy: @table is permitted for non-table kinds with a warning.
93
+ if (chosenAlias === SOURCE_ATTR_TABLE) {
94
+ warnings.push({
95
+ code: "WARN_LEGACY_PHYSICAL_NAME_ALIAS",
96
+ message:
97
+ `source.rdb on object "${obj.name}" uses @table with @kind: "${source.effectiveKind}"; ` +
98
+ `prefer the kind-matching alias @${expectedAlias} (ADR-0018)`,
99
+ source: source.source,
100
+ });
101
+ continue;
102
+ }
103
+
104
+ // Any other mismatch is a hard error.
105
+ errors.push(
106
+ new ParseError(
107
+ `source.rdb on object "${obj.name}" uses @${chosenAlias} with @kind: "${source.effectiveKind}"; ` +
108
+ `@${chosenAlias} is only valid for @kind: "${kindForAlias(chosenAlias)}"`,
109
+ { code: "ERR_PHYSICAL_NAME_KIND_MISMATCH", source: source.source },
110
+ ),
111
+ );
112
+ }
113
+ }
114
+
115
+ return { errors, warnings };
116
+ }
117
+
118
+ function kindForAlias(alias: string): string {
119
+ for (const [kind, attr] of PHYSICAL_NAME_ATTR_BY_KIND.entries()) {
120
+ if (attr === alias) return kind;
121
+ }
122
+ return "(unknown)";
123
+ }