@metaobjectsdev/migrate-ts 0.11.0 → 0.11.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.
@@ -5,13 +5,18 @@ export declare function findField(entity: MetaObject, name: string): MetaData |
5
5
  export declare function isRequired(field: MetaData): boolean;
6
6
  /**
7
7
  * Resolve the referential actions for a foreign key inferred from an
8
- * identity.reference, by correlating it with a sibling relationship on the
9
- * same entity (matched on target-entity name).
8
+ * identity.reference.
10
9
  *
11
- * - No correlated relationship → both undefined (no ON DELETE / ON UPDATE clause).
12
- * - With a relationship: onDelete defaults from the relationship subtype
13
- * (composition→cascade, aggregation→set-null, association→restrict);
14
- * onUpdate defaults to "cascade". Explicit @onDelete / @onUpdate override.
10
+ * Precedence (highest first):
11
+ * 1. @onDelete / @onUpdate declared DIRECTLY on the identity.reference — the
12
+ * reference IS the FK, so the action may be declared right where the FK is.
13
+ * 2. A correlated sibling relationship on the same entity (matched on
14
+ * target-entity name): its explicit @onDelete, else its subtype default
15
+ * (composition→cascade, aggregation→set-null, association→restrict);
16
+ * onUpdate defaults to "cascade".
17
+ * 3. Neither → undefined (no ON DELETE / ON UPDATE clause).
18
+ *
19
+ * - "setnull" is accepted as an alias for the canonical "set-null".
15
20
  * - Resolved "no-action" → undefined: introspection in introspect/{postgres,sqlite}.ts
16
21
  * omits actions when the DB value is "no-action", so the expected side does the same
17
22
  * to keep round-trip diffs clean.
@@ -1 +1 @@
1
- {"version":3,"file":"referential-actions.d.ts","sourceRoot":"","sources":["../src/referential-actions.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,UAAU,EACf,KAAK,qBAAqB,EAC1B,KAAK,QAAQ,EACd,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAO3C,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,EAAE,CAM/D;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAKhF;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAMnD;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,qBAAqB,GACzB;IAAE,QAAQ,EAAE,QAAQ,GAAG,SAAS,CAAC;IAAC,QAAQ,EAAE,QAAQ,GAAG,SAAS,CAAA;CAAE,CAoBpE;AAWD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,qBAAqB,EAC1B,QAAQ,EAAE,QAAQ,GAAG,SAAS,EAC9B,cAAc,EAAE,MAAM,GACrB,IAAI,CAeN"}
1
+ {"version":3,"file":"referential-actions.d.ts","sourceRoot":"","sources":["../src/referential-actions.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,UAAU,EACf,KAAK,qBAAqB,EAC1B,KAAK,QAAQ,EACd,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAO3C,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,EAAE,CAM/D;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAKhF;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAMnD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,qBAAqB,GACzB;IAAE,QAAQ,EAAE,QAAQ,GAAG,SAAS,CAAC;IAAC,QAAQ,EAAE,QAAQ,GAAG,SAAS,CAAA;CAAE,CAgCpE;AAcD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,qBAAqB,EAC1B,QAAQ,EAAE,QAAQ,GAAG,SAAS,EAC9B,cAAc,EAAE,MAAM,GACrB,IAAI,CAeN"}
@@ -27,13 +27,18 @@ export function isRequired(field) {
27
27
  }
28
28
  /**
29
29
  * Resolve the referential actions for a foreign key inferred from an
30
- * identity.reference, by correlating it with a sibling relationship on the
31
- * same entity (matched on target-entity name).
30
+ * identity.reference.
32
31
  *
33
- * - No correlated relationship → both undefined (no ON DELETE / ON UPDATE clause).
34
- * - With a relationship: onDelete defaults from the relationship subtype
35
- * (composition→cascade, aggregation→set-null, association→restrict);
36
- * onUpdate defaults to "cascade". Explicit @onDelete / @onUpdate override.
32
+ * Precedence (highest first):
33
+ * 1. @onDelete / @onUpdate declared DIRECTLY on the identity.reference — the
34
+ * reference IS the FK, so the action may be declared right where the FK is.
35
+ * 2. A correlated sibling relationship on the same entity (matched on
36
+ * target-entity name): its explicit @onDelete, else its subtype default
37
+ * (composition→cascade, aggregation→set-null, association→restrict);
38
+ * onUpdate defaults to "cascade".
39
+ * 3. Neither → undefined (no ON DELETE / ON UPDATE clause).
40
+ *
41
+ * - "setnull" is accepted as an alias for the canonical "set-null".
37
42
  * - Resolved "no-action" → undefined: introspection in introspect/{postgres,sqlite}.ts
38
43
  * omits actions when the DB value is "no-action", so the expected side does the same
39
44
  * to keep round-trip diffs clean.
@@ -58,20 +63,31 @@ export function resolveReferentialActions(entity, ref) {
58
63
  // resolve to undefined (no clause emitted) — surfacing the mismatch as a
59
64
  // silent loss of intent rather than a wrong action. Cross-language ports
60
65
  // should match the same correlation rule.
66
+ // (1) Actions declared directly on the FK-defining reference win.
67
+ const refOnDelete = ref.onDelete;
68
+ const refOnUpdate = ref.onUpdate;
69
+ // (2) Otherwise correlate with a sibling relationship and use its action /
70
+ // subtype default. onUpdate's "cascade" default only applies when a
71
+ // relationship is present, so a reference-only FK with no explicit
72
+ // @onUpdate emits no ON UPDATE clause.
61
73
  const rel = entity.relationships().find((r) => r.objectRef === target);
62
- if (rel === undefined)
63
- return { onDelete: undefined, onUpdate: undefined };
64
- const onDeleteRaw = rel.onDelete ?? ON_DELETE_DEFAULT_BY_SUBTYPE[rel.subType];
65
- const onUpdateRaw = rel.onUpdate ?? ON_UPDATE_DEFAULT;
74
+ const onDeleteRaw = refOnDelete ??
75
+ (rel ? (rel.onDelete ?? ON_DELETE_DEFAULT_BY_SUBTYPE[rel.subType]) : undefined);
76
+ const onUpdateRaw = refOnUpdate ??
77
+ (rel ? (rel.onUpdate ?? ON_UPDATE_DEFAULT) : undefined);
66
78
  return {
67
79
  onDelete: normalize(onDeleteRaw),
68
80
  onUpdate: normalize(onUpdateRaw),
69
81
  };
70
82
  }
71
83
  function normalize(a) {
72
- if (a === undefined || a === "no-action")
84
+ if (a === undefined)
85
+ return undefined;
86
+ // Accept the hyphen-less spelling as an alias for the canonical kebab-case form.
87
+ const canonical = a === "setnull" ? "set-null" : a;
88
+ if (canonical === "no-action")
73
89
  return undefined;
74
- return a;
90
+ return canonical;
75
91
  }
76
92
  // ---------------------------------------------------------------------------
77
93
  // Set-null / NOT NULL guard
@@ -1 +1 @@
1
- {"version":3,"file":"referential-actions.js","sourceRoot":"","sources":["../src/referential-actions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"referential-actions.js","sourceRoot":"","sources":["../src/referential-actions.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metaobjectsdev/migrate-ts",
3
- "version": "0.11.0",
3
+ "version": "0.11.1-rc.1",
4
4
  "description": "Schema migration tooling for MetaObjects: diff metadata vs DB and emit SQL for Postgres and SQLite.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -45,7 +45,7 @@
45
45
  },
46
46
  "dependencies": {
47
47
  "@iarna/toml": "^2.2.5",
48
- "@metaobjectsdev/metadata": "0.11.0"
48
+ "@metaobjectsdev/metadata": "0.11.1-rc.1"
49
49
  },
50
50
  "peerDependencies": {
51
51
  "kysely": ">=0.27.0"
@@ -41,13 +41,18 @@ export function isRequired(field: MetaData): boolean {
41
41
 
42
42
  /**
43
43
  * Resolve the referential actions for a foreign key inferred from an
44
- * identity.reference, by correlating it with a sibling relationship on the
45
- * same entity (matched on target-entity name).
44
+ * identity.reference.
46
45
  *
47
- * - No correlated relationship → both undefined (no ON DELETE / ON UPDATE clause).
48
- * - With a relationship: onDelete defaults from the relationship subtype
49
- * (composition→cascade, aggregation→set-null, association→restrict);
50
- * onUpdate defaults to "cascade". Explicit @onDelete / @onUpdate override.
46
+ * Precedence (highest first):
47
+ * 1. @onDelete / @onUpdate declared DIRECTLY on the identity.reference — the
48
+ * reference IS the FK, so the action may be declared right where the FK is.
49
+ * 2. A correlated sibling relationship on the same entity (matched on
50
+ * target-entity name): its explicit @onDelete, else its subtype default
51
+ * (composition→cascade, aggregation→set-null, association→restrict);
52
+ * onUpdate defaults to "cascade".
53
+ * 3. Neither → undefined (no ON DELETE / ON UPDATE clause).
54
+ *
55
+ * - "setnull" is accepted as an alias for the canonical "set-null".
51
56
  * - Resolved "no-action" → undefined: introspection in introspect/{postgres,sqlite}.ts
52
57
  * omits actions when the DB value is "no-action", so the expected side does the same
53
58
  * to keep round-trip diffs clean.
@@ -75,11 +80,23 @@ export function resolveReferentialActions(
75
80
  // resolve to undefined (no clause emitted) — surfacing the mismatch as a
76
81
  // silent loss of intent rather than a wrong action. Cross-language ports
77
82
  // should match the same correlation rule.
83
+ // (1) Actions declared directly on the FK-defining reference win.
84
+ const refOnDelete = ref.onDelete;
85
+ const refOnUpdate = ref.onUpdate;
86
+
87
+ // (2) Otherwise correlate with a sibling relationship and use its action /
88
+ // subtype default. onUpdate's "cascade" default only applies when a
89
+ // relationship is present, so a reference-only FK with no explicit
90
+ // @onUpdate emits no ON UPDATE clause.
78
91
  const rel = entity.relationships().find((r) => r.objectRef === target);
79
- if (rel === undefined) return { onDelete: undefined, onUpdate: undefined };
80
92
 
81
- const onDeleteRaw = rel.onDelete ?? ON_DELETE_DEFAULT_BY_SUBTYPE[rel.subType];
82
- const onUpdateRaw = rel.onUpdate ?? ON_UPDATE_DEFAULT;
93
+ const onDeleteRaw =
94
+ refOnDelete ??
95
+ (rel ? (rel.onDelete ?? ON_DELETE_DEFAULT_BY_SUBTYPE[rel.subType]) : undefined);
96
+ const onUpdateRaw =
97
+ refOnUpdate ??
98
+ (rel ? (rel.onUpdate ?? ON_UPDATE_DEFAULT) : undefined);
99
+
83
100
  return {
84
101
  onDelete: normalize(onDeleteRaw),
85
102
  onUpdate: normalize(onUpdateRaw),
@@ -87,8 +104,11 @@ export function resolveReferentialActions(
87
104
  }
88
105
 
89
106
  function normalize(a: string | undefined): FkAction | undefined {
90
- if (a === undefined || a === "no-action") return undefined;
91
- return a as FkAction;
107
+ if (a === undefined) return undefined;
108
+ // Accept the hyphen-less spelling as an alias for the canonical kebab-case form.
109
+ const canonical = a === "setnull" ? "set-null" : a;
110
+ if (canonical === "no-action") return undefined;
111
+ return canonical as FkAction;
92
112
  }
93
113
 
94
114
  // ---------------------------------------------------------------------------