@metaobjectsdev/migrate-ts 0.21.5 → 0.21.6

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.
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAc,MAAM,YAAY,CAAC;AAMrD;;;;;;;;;GASG;AACH,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,SAAkB,IAAI,6BAA6B;IACnD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,eAAe,EAAE,MAAM,EAAE,CAAC;gBAEvB,UAAU,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE;CAWlF;AA8CD,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,SAAkB,IAAI,yBAAyB;IAC/C,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,CAAC;gBAEnB,OAAO,EAAE,MAAM,EAAE;CAU9B;AAED;;;;;;;;;GASG;AACH,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,SAAkB,IAAI,2BAA2B;IACjD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,cAAc,EAAE,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;gBAEb,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,EAAE,kBAAkB,EAAE,MAAM,EAAE,EAAE,MAAM,CAAC,EAAE,MAAM;CAenG"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAc,MAAM,YAAY,CAAC;AAMrD;;;;;;;;;GASG;AACH,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,SAAkB,IAAI,6BAA6B;IACnD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,eAAe,EAAE,MAAM,EAAE,CAAC;gBAEvB,UAAU,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE;CAWlF;AAkDD,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,SAAkB,IAAI,yBAAyB;IAC/C,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,CAAC;gBAEnB,OAAO,EAAE,MAAM,EAAE;CAU9B;AAED;;;;;;;;;GASG;AACH,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,SAAkB,IAAI,2BAA2B;IACjD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,cAAc,EAAE,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;gBAEb,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,EAAE,kBAAkB,EAAE,MAAM,EAAE,EAAE,MAAM,CAAC,EAAE,MAAM;CAenG"}
package/dist/errors.js CHANGED
@@ -33,6 +33,10 @@ const ENABLE_FLAG_BY_KIND = {
33
33
  "change-column-nullable": "allow.nullableToNotNull",
34
34
  "drop-index": "allow.dropIndex",
35
35
  "drop-fk": "allow.dropFk",
36
+ // change-column-default is blocked in exactly one shape — dropping a live
37
+ // Postgres auto-sequence default with no declared @generation (see
38
+ // diff/status.ts) — and allow.dropIdentityDefault is the flag that enables it.
39
+ "change-column-default": "allow.dropIdentityDefault",
36
40
  };
37
41
  function changeLocator(c) {
38
42
  switch (c.kind) {
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAEA,8EAA8E;AAC9E,4DAA4D;AAC5D,8EAA8E;AAE9E;;;;;;;;;GASG;AACH,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IAC9B,IAAI,GAAG,yBAAyB,CAAC;IAC1C,UAAU,CAAS;IACnB,cAAc,CAAS;IACvB,eAAe,CAAW;IAEnC,YAAY,UAAkB,EAAE,cAAsB,EAAE,eAAyB;QAC/E,MAAM,SAAS,GAAG,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7C,KAAK,CACH,WAAW,UAAU,qBAAqB,cAAc,4BAA4B;YACpF,iBAAiB,SAAS,oCAAoC;YAC9D,uHAAuH,CACxH,CAAC;QACF,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;QACrC,IAAI,CAAC,eAAe,GAAG,eAAe,CAAC;IACzC,CAAC;CACF;AAED,MAAM,mBAAmB,GAAwC;IAC/D,aAAa,EAAE,kBAAkB;IACjC,YAAY,EAAE,iBAAiB;IAC/B,oBAAoB,EAAE,kBAAkB;IACxC,wBAAwB,EAAE,yBAAyB;IACnD,YAAY,EAAE,iBAAiB;IAC/B,SAAS,EAAE,cAAc;CAC1B,CAAC;AAEF,SAAS,aAAa,CAAC,CAAS;IAC9B,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;QACf,KAAK,aAAa,CAAC;QACnB,KAAK,eAAe,CAAC;QACrB,KAAK,oBAAoB,CAAC;QAC1B,KAAK,wBAAwB,CAAC;QAC9B,KAAK,uBAAuB;YAC1B,OAAO,GAAG,CAAC,CAAC,KAAK,IAAI,QAAQ,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC3D,KAAK,YAAY;YACf,OAAO,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QACvC,KAAK,YAAY,CAAC;QAClB,KAAK,cAAc;YACjB,OAAO,CAAC,CAAC,IAAI,KAAK,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC;QACjE,KAAK,cAAc;YACjB,OAAO,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC;QACtB,KAAK,WAAW;YACd,OAAO,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACtC,KAAK,YAAY;YACf,OAAO,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;QACjC,KAAK,QAAQ;YACX,OAAO,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;QACnC,KAAK,SAAS;YACZ,OAAO,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC;QAC9B,KAAK,WAAW;YACd,OAAO,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACtC,KAAK,YAAY;YACf,OAAO,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;QACjC,KAAK,aAAa,CAAC;QACnB,KAAK,cAAc;YACjB,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QACrB,KAAK,WAAW;YACd,OAAO,CAAC,CAAC,IAAI,CAAC;IAClB,CAAC;AACH,CAAC;AAED,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAC1B,IAAI,GAAG,qBAAqB,CAAC;IACtC,OAAO,CAAW;IAClB,WAAW,CAAW;IAE/B,YAAY,OAAiB;QAC3B,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YAC9B,MAAM,IAAI,GAAG,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,wBAAwB,CAAC;YACrE,OAAO,GAAG,CAAC,CAAC,IAAI,OAAO,aAAa,CAAC,CAAC,CAAC,UAAU,IAAI,EAAE,CAAC;QAC1D,CAAC,CAAC,CAAC;QACH,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,MAAM,4BAA4B,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QAChF,KAAK,CAAC,GAAG,CAAC,CAAC;QACX,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;IAC3B,CAAC;CACF;AAED;;;;;;;;;GASG;AACH,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IAC5B,IAAI,GAAG,uBAAuB,CAAC;IACxC,KAAK,CAAS;IACd,cAAc,CAAW;IACzB,kBAAkB,CAAW;IAC7B,MAAM,CAAU;IAEzB,YAAY,KAAa,EAAE,cAAwB,EAAE,kBAA4B,EAAE,MAAe;QAChG,MAAM,SAAS,GAAG,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;QACtE,MAAM,GAAG,GAAG,CAAC,IAAc,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,gBAAgB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC;QAC1G,KAAK,CACH,mBAAmB,SAAS,0CAA0C,GAAG,CAAC,cAAc,CAAC,MAAM;YAC7F,YAAY,GAAG,CAAC,kBAAkB,CAAC,6DAA6D;YAChG,gGAAgG;YAChG,qGAAqG;YACrG,sDAAsD,CACzD,CAAC;QACF,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;QACrC,IAAI,CAAC,kBAAkB,GAAG,kBAAkB,CAAC;QAC7C,IAAI,MAAM,KAAK,SAAS;YAAE,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACjD,CAAC;CACF"}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
@@ -10,13 +10,27 @@ export declare function isRequired(field: MetaData): boolean;
10
10
  * Precedence (highest first):
11
11
  * 1. @onDelete / @onUpdate declared DIRECTLY on the identity.reference — the
12
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).
13
+ * 2. A correlated sibling relationship on the same entity matched
14
+ * package-aware against the resolved @references target (refMatchesObject
15
+ * / ADR-0042, so bare and FQN forms pair correctly); an M:N relationship
16
+ * (@through) never correlates with a direct FK. Its explicit @onDelete,
17
+ * else its subtype default (composition→cascade, aggregation→set-null,
18
+ * association→restrict); onUpdate defaults to "cascade".
19
+ * 3. A correlated REVERSE relationship on the TARGET entity — the documented
20
+ * parent-side authoring shape ("Program owns weeks": composition declared
21
+ * on the parent with @objectRef back at this FK-owning entity). Same
22
+ * explicit-action-else-subtype-default resolution as tier 2. Guards:
23
+ * an M:N relationship (@through) never correlates (it describes the
24
+ * junction path, not this direct FK); when the FK-owning entity holds
25
+ * MORE THAN ONE enforced reference to the same target the reverse
26
+ * relationship contributes nothing (it cannot say which FK carries the
27
+ * ownership edge — arming all of them could cascade through an edge the
28
+ * author never designated; fail closed); and an INFERRED set-null default
29
+ * (parent-side aggregation, no explicit @onDelete) on a NOT NULL FK drops
30
+ * the INFERRED contributions only — an authored @onUpdate on that same
31
+ * relationship still applies (see the in-body guard comment).
32
+ * 4. None → undefined (no ON DELETE / ON UPDATE clause).
18
33
  *
19
- * - "setnull" is accepted as an alias for the canonical "set-null".
20
34
  * - Resolved "no-action" → undefined: introspection in introspect/{postgres,sqlite}.ts
21
35
  * omits actions when the DB value is "no-action", so the expected side does the same
22
36
  * 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,CAO/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,CAOnD;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"}
1
+ {"version":3,"file":"referential-actions.d.ts","sourceRoot":"","sources":["../src/referential-actions.ts"],"names":[],"mappings":"AAAA,OAAO,EASL,KAAK,UAAU,EAEf,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,CAO/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,CAOnD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;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,CAiFpE;AAkED;;;;;;;;;;;;;;;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,4 +1,4 @@
1
- import { ON_DELETE_DEFAULT_BY_SUBTYPE, ON_UPDATE_DEFAULT, FIELD_ATTR_REQUIRED, IDENTITY_ATTR_FIELDS, TYPE_VALIDATOR, VALIDATOR_SUBTYPE_REQUIRED, } from "@metaobjectsdev/metadata";
1
+ import { ON_DELETE_DEFAULT_BY_SUBTYPE, ON_UPDATE_DEFAULT, FIELD_ATTR_REQUIRED, IDENTITY_ATTR_FIELDS, TYPE_VALIDATOR, VALIDATOR_SUBTYPE_REQUIRED, refMatchesObject, resolveObjectRef, } from "@metaobjectsdev/metadata";
2
2
  import { SetNullNotNullableError } from "./errors.js";
3
3
  // ---------------------------------------------------------------------------
4
4
  // Shared field helpers — exported for use by expected-schema.ts
@@ -34,13 +34,27 @@ export function isRequired(field) {
34
34
  * Precedence (highest first):
35
35
  * 1. @onDelete / @onUpdate declared DIRECTLY on the identity.reference — the
36
36
  * reference IS the FK, so the action may be declared right where the FK is.
37
- * 2. A correlated sibling relationship on the same entity (matched on
38
- * target-entity name): its explicit @onDelete, else its subtype default
39
- * (composition→cascade, aggregation→set-null, association→restrict);
40
- * onUpdate defaults to "cascade".
41
- * 3. Neither undefined (no ON DELETE / ON UPDATE clause).
37
+ * 2. A correlated sibling relationship on the same entity matched
38
+ * package-aware against the resolved @references target (refMatchesObject
39
+ * / ADR-0042, so bare and FQN forms pair correctly); an M:N relationship
40
+ * (@through) never correlates with a direct FK. Its explicit @onDelete,
41
+ * else its subtype default (composition→cascade, aggregation→set-null,
42
+ * association→restrict); onUpdate defaults to "cascade".
43
+ * 3. A correlated REVERSE relationship on the TARGET entity — the documented
44
+ * parent-side authoring shape ("Program owns weeks": composition declared
45
+ * on the parent with @objectRef back at this FK-owning entity). Same
46
+ * explicit-action-else-subtype-default resolution as tier 2. Guards:
47
+ * an M:N relationship (@through) never correlates (it describes the
48
+ * junction path, not this direct FK); when the FK-owning entity holds
49
+ * MORE THAN ONE enforced reference to the same target the reverse
50
+ * relationship contributes nothing (it cannot say which FK carries the
51
+ * ownership edge — arming all of them could cascade through an edge the
52
+ * author never designated; fail closed); and an INFERRED set-null default
53
+ * (parent-side aggregation, no explicit @onDelete) on a NOT NULL FK drops
54
+ * the INFERRED contributions only — an authored @onUpdate on that same
55
+ * relationship still applies (see the in-body guard comment).
56
+ * 4. None → undefined (no ON DELETE / ON UPDATE clause).
42
57
  *
43
- * - "setnull" is accepted as an alias for the canonical "set-null".
44
58
  * - Resolved "no-action" → undefined: introspection in introspect/{postgres,sqlite}.ts
45
59
  * omits actions when the DB value is "no-action", so the expected side does the same
46
60
  * to keep round-trip diffs clean.
@@ -58,38 +72,139 @@ export function resolveReferentialActions(entity, ref) {
58
72
  const target = ref.targetEntity;
59
73
  if (target === undefined)
60
74
  return { onDelete: undefined, onUpdate: undefined };
61
- // Correlation is by exact-string match. Every fixture in the corpus uses
62
- // bare entity names for @objectRef and @references (no `::`-FQN form), so
63
- // bare-vs-bare matching is sufficient today. If a future author writes an
64
- // FQN value on either side, this find returns undefined and both actions
65
- // resolve to undefined (no clause emitted) — surfacing the mismatch as a
66
- // silent loss of intent rather than a wrong action. Cross-language ports
67
- // should match the same correlation rule.
68
75
  // (1) Actions declared directly on the FK-defining reference win.
69
76
  const refOnDelete = ref.onDelete;
70
77
  const refOnUpdate = ref.onUpdate;
78
+ // Resolve the reference's target ONCE, package-aware (ADR-0042: a bare
79
+ // @references resolves in the DECLARING owner's package). Both relationship
80
+ // tiers then correlate against the resolved node with refMatchesObject, so a
81
+ // bare @references pairs correctly with an FQN @objectRef (and vice versa) —
82
+ // an exact-string tier 2 used to miss the FQN form, letting the parent-side
83
+ // tier override a child-side declaration.
84
+ const root = entity.parent;
85
+ const refOwner = ref.parent ?? entity;
86
+ const refOwnerPkg = refOwner.package ?? refOwner.fileDefaultPackage ?? "";
87
+ const targetObj = root !== undefined ? resolveObjectRef(root, target, refOwnerPkg).node : undefined;
71
88
  // (2) Otherwise correlate with a sibling relationship and use its action /
72
89
  // subtype default. onUpdate's "cascade" default only applies when a
73
90
  // relationship is present, so a reference-only FK with no explicit
74
- // @onUpdate emits no ON UPDATE clause.
75
- const rel = entity.relationships().find((r) => r.objectRef === target);
91
+ // @onUpdate emits no ON UPDATE clause. An M:N relationship (@through)
92
+ // never correlates it describes the junction path, not this direct FK.
93
+ // When the target does not resolve (dangling @references — normally a
94
+ // load error), fall back to the legacy exact-string match so behavior on
95
+ // partially-valid trees is unchanged.
96
+ // (3) Failing that, correlate the REVERSE relationship declared on the
97
+ // TARGET entity (the documented parent-side authoring shape).
98
+ let rel = entity.relationships().find((r) => {
99
+ if (r.through !== undefined)
100
+ return false;
101
+ const objectRef = r.objectRef;
102
+ if (objectRef === undefined)
103
+ return false;
104
+ if (targetObj === undefined)
105
+ return objectRef === target;
106
+ const relOwner = r.parent ?? entity;
107
+ const relOwnerPkg = relOwner.package ?? relOwner.fileDefaultPackage ?? "";
108
+ return refMatchesObject(targetObj, objectRef, relOwnerPkg);
109
+ });
110
+ // When the tier-3 satisfiability guard fires, the reverse relationship's
111
+ // AUTHORED @onUpdate still applies (only the inferred contributions drop).
112
+ let suppressedReverseOnUpdate;
113
+ if (rel === undefined && targetObj !== undefined) {
114
+ const reverse = findReverseRelationship(entity, ref, targetObj);
115
+ // Tier-3 satisfiability guard: an INFERRED set-null default (a parent-side
116
+ // aggregation with no explicit @onDelete) is unsatisfiable when any FK
117
+ // column is NOT NULL — SET NULL cannot fire there, and letting it through
118
+ // would turn a previously-valid model into a hard SetNullNotNullableError
119
+ // purely because the correlation got smarter. An inferred default never
120
+ // breaks a model: the INFERRED contributions drop (today's bare FK), while
121
+ // anything the author explicitly wrote survives — an EXPLICIT @onDelete:
122
+ // "set-null" flows through and hits the loud validateSetNullNullability
123
+ // error (the author asked for it), and an EXPLICIT @onUpdate is honored
124
+ // (silently dropping it would be the original bug again).
125
+ if (reverse !== undefined) {
126
+ const unsatisfiableInferredSetNull = reverse.onDelete === undefined &&
127
+ ON_DELETE_DEFAULT_BY_SUBTYPE[reverse.subType] === "set-null" &&
128
+ readIdentityFields(ref).some((jsName) => {
129
+ const field = findField(entity, jsName);
130
+ return field !== undefined && isRequired(field);
131
+ });
132
+ if (unsatisfiableInferredSetNull) {
133
+ suppressedReverseOnUpdate = reverse.onUpdate;
134
+ }
135
+ else {
136
+ rel = reverse;
137
+ }
138
+ }
139
+ }
76
140
  const onDeleteRaw = refOnDelete ??
77
141
  (rel ? (rel.onDelete ?? ON_DELETE_DEFAULT_BY_SUBTYPE[rel.subType]) : undefined);
78
142
  const onUpdateRaw = refOnUpdate ??
79
- (rel ? (rel.onUpdate ?? ON_UPDATE_DEFAULT) : undefined);
143
+ (rel ? (rel.onUpdate ?? ON_UPDATE_DEFAULT) : suppressedReverseOnUpdate);
80
144
  return {
81
145
  onDelete: normalize(onDeleteRaw),
82
146
  onUpdate: normalize(onUpdateRaw),
83
147
  };
84
148
  }
149
+ /**
150
+ * Tier-3 correlation: the relationship declared on the TARGET (parent) entity
151
+ * pointing back at the FK-owning entity — the shape the docs and the authoring
152
+ * skill teach ("Author owns posts": `relationship.composition { @objectRef:
153
+ * "Post", @cardinality: "many" }` on Author, while Post owns the FK).
154
+ *
155
+ * Guards (each fails closed to "no contribution"):
156
+ * - An M:N relationship (`@through`) never correlates — it describes the
157
+ * junction path, not this direct FK (the junction's own FKs correlate via
158
+ * its own identity.reference children). The same guard applies at tier 2.
159
+ * - When the FK-owning entity holds more than one enforced reference to the
160
+ * same target, the reverse relationship cannot say WHICH FK carries the
161
+ * ownership edge, so it contributes to none of them (arming every FK could
162
+ * cascade through an edge the author never designated).
163
+ *
164
+ * If multiple reverse relationships point back at the entity (rare), the first
165
+ * one is used — mirroring the tier-2 sibling-relationship rule.
166
+ */
167
+ function findReverseRelationship(entity, ref, targetObj) {
168
+ const root = entity.parent;
169
+ if (root === undefined)
170
+ return undefined;
171
+ // Ambiguity guard: exactly one enforced reference from `entity` to this
172
+ // target, and it must be `ref` itself.
173
+ const refsToTarget = entity.referenceIdentities().filter((r) => {
174
+ if (!r.enforce)
175
+ return false;
176
+ const t = r.targetEntity;
177
+ if (t === undefined)
178
+ return false;
179
+ const owner = r.parent ?? entity;
180
+ const ownerPkg = owner.package ?? owner.fileDefaultPackage ?? "";
181
+ return resolveObjectRef(root, t, ownerPkg).node === targetObj;
182
+ });
183
+ if (refsToTarget.length !== 1 || refsToTarget[0] !== ref)
184
+ return undefined;
185
+ // The reverse relationship's bare @objectRef resolves in ITS declaring
186
+ // owner's package (normally the target entity's own package).
187
+ return targetObj.relationships().find((r) => {
188
+ if (r.through !== undefined)
189
+ return false; // M:N — junction path, not this FK
190
+ const objectRef = r.objectRef;
191
+ if (objectRef === undefined)
192
+ return false;
193
+ const relOwner = r.parent ?? targetObj;
194
+ const relOwnerPkg = relOwner.package ?? relOwner.fileDefaultPackage ?? "";
195
+ return refMatchesObject(entity, objectRef, relOwnerPkg);
196
+ });
197
+ }
85
198
  function normalize(a) {
86
199
  if (a === undefined)
87
200
  return undefined;
88
- // Accept the hyphen-less spelling as an alias for the canonical kebab-case form.
89
- const canonical = a === "setnull" ? "set-null" : a;
90
- if (canonical === "no-action")
201
+ // Values are load-validated against REFERENTIAL_ACTIONS (allowedValues on both
202
+ // relationship.* and since ADR-0047 identity.reference), so only canonical
203
+ // kebab-case spellings reach this point. The legacy "setnull" alias was retired
204
+ // with the ADR-0047 registration: it now fails load with ERR_BAD_ATTR_VALUE.
205
+ if (a === "no-action")
91
206
  return undefined;
92
- return canonical;
207
+ return a;
93
208
  }
94
209
  // ---------------------------------------------------------------------------
95
210
  // 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":"AAAA,OAAO,EACL,4BAA4B,EAC5B,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,cAAc,EACd,0BAA0B,EAC1B,gBAAgB,EAChB,gBAAgB,GAKjB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAEtD,8EAA8E;AAC9E,gEAAgE;AAChE,8EAA8E;AAE9E,MAAM,UAAU,kBAAkB,CAAC,QAAkB;IACnD,kFAAkF;IAClF,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;IAChD,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC3E,6EAA6E;IAC7E,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACpG,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,MAAkB,EAAE,IAAY;IACxD,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC;QACpC,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI;YAAE,OAAO,KAAK,CAAC;IACxC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAe;IACxC,uFAAuF;IACvF,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IAC7C,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC;IAClD,OAAO,KAAK,CAAC,QAAQ,EAAE,CAAC,IAAI,CAC1B,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,cAAc,IAAI,CAAC,CAAC,OAAO,KAAK,0BAA0B,CAC7E,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,MAAM,UAAU,yBAAyB,CACvC,MAAkB,EAClB,GAA0B;IAE1B,MAAM,MAAM,GAAG,GAAG,CAAC,YAAY,CAAC;IAChC,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;IAE9E,kEAAkE;IAClE,MAAM,WAAW,GAAG,GAAG,CAAC,QAAQ,CAAC;IACjC,MAAM,WAAW,GAAG,GAAG,CAAC,QAAQ,CAAC;IAEjC,uEAAuE;IACvE,4EAA4E;IAC5E,6EAA6E;IAC7E,6EAA6E;IAC7E,4EAA4E;IAC5E,0CAA0C;IAC1C,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC;IAC3B,MAAM,QAAQ,GAAG,GAAG,CAAC,MAAM,IAAI,MAAM,CAAC;IACtC,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,IAAI,QAAQ,CAAC,kBAAkB,IAAI,EAAE,CAAC;IAC1E,MAAM,SAAS,GACb,IAAI,KAAK,SAAS,CAAC,CAAC,CAAE,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,IAA+B,CAAC,CAAC,CAAC,SAAS,CAAC;IAEhH,2EAA2E;IAC3E,wEAAwE;IACxE,uEAAuE;IACvE,0EAA0E;IAC1E,6EAA6E;IAC7E,0EAA0E;IAC1E,6EAA6E;IAC7E,0CAA0C;IAC1C,uEAAuE;IACvE,kEAAkE;IAClE,IAAI,GAAG,GAAG,MAAM,CAAC,aAAa,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;QAC1C,IAAI,CAAC,CAAC,OAAO,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QAC1C,MAAM,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC;QAC9B,IAAI,SAAS,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QAC1C,IAAI,SAAS,KAAK,SAAS;YAAE,OAAO,SAAS,KAAK,MAAM,CAAC;QACzD,MAAM,QAAQ,GAAG,CAAC,CAAC,MAAM,IAAI,MAAM,CAAC;QACpC,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,IAAI,QAAQ,CAAC,kBAAkB,IAAI,EAAE,CAAC;QAC1E,OAAO,gBAAgB,CAAC,SAAS,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;IACH,yEAAyE;IACzE,2EAA2E;IAC3E,IAAI,yBAA6C,CAAC;IAClD,IAAI,GAAG,KAAK,SAAS,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QACjD,MAAM,OAAO,GAAG,uBAAuB,CAAC,MAAM,EAAE,GAAG,EAAE,SAAS,CAAC,CAAC;QAChE,2EAA2E;QAC3E,uEAAuE;QACvE,0EAA0E;QAC1E,0EAA0E;QAC1E,wEAAwE;QACxE,2EAA2E;QAC3E,yEAAyE;QACzE,wEAAwE;QACxE,wEAAwE;QACxE,0DAA0D;QAC1D,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,MAAM,4BAA4B,GAChC,OAAO,CAAC,QAAQ,KAAK,SAAS;gBAC9B,4BAA4B,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,UAAU;gBAC5D,kBAAkB,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE;oBACtC,MAAM,KAAK,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;oBACxC,OAAO,KAAK,KAAK,SAAS,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC;gBAClD,CAAC,CAAC,CAAC;YACL,IAAI,4BAA4B,EAAE,CAAC;gBACjC,yBAAyB,GAAG,OAAO,CAAC,QAAQ,CAAC;YAC/C,CAAC;iBAAM,CAAC;gBACN,GAAG,GAAG,OAAO,CAAC;YAChB,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,WAAW,GACf,WAAW;QACX,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,IAAI,4BAA4B,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAClF,MAAM,WAAW,GACf,WAAW;QACX,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,IAAI,iBAAiB,CAAC,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC;IAE1E,OAAO;QACL,QAAQ,EAAE,SAAS,CAAC,WAAW,CAAC;QAChC,QAAQ,EAAE,SAAS,CAAC,WAAW,CAAC;KACjC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,SAAS,uBAAuB,CAC9B,MAAkB,EAClB,GAA0B,EAC1B,SAAqB;IAErB,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC;IAC3B,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAEzC,wEAAwE;IACxE,uCAAuC;IACvC,MAAM,YAAY,GAAG,MAAM,CAAC,mBAAmB,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;QAC7D,IAAI,CAAC,CAAC,CAAC,OAAO;YAAE,OAAO,KAAK,CAAC;QAC7B,MAAM,CAAC,GAAG,CAAC,CAAC,YAAY,CAAC;QACzB,IAAI,CAAC,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QAClC,MAAM,KAAK,GAAG,CAAC,CAAC,MAAM,IAAI,MAAM,CAAC;QACjC,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,kBAAkB,IAAI,EAAE,CAAC;QACjE,OAAO,gBAAgB,CAAC,IAAI,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC;IAChE,CAAC,CAAC,CAAC;IACH,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,IAAI,YAAY,CAAC,CAAC,CAAC,KAAK,GAAG;QAAE,OAAO,SAAS,CAAC;IAE3E,uEAAuE;IACvE,8DAA8D;IAC9D,OAAO,SAAS,CAAC,aAAa,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;QAC1C,IAAI,CAAC,CAAC,OAAO,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC,CAAC,mCAAmC;QAC9E,MAAM,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC;QAC9B,IAAI,SAAS,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QAC1C,MAAM,QAAQ,GAAG,CAAC,CAAC,MAAM,IAAI,SAAS,CAAC;QACvC,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,IAAI,QAAQ,CAAC,kBAAkB,IAAI,EAAE,CAAC;QAC1E,OAAO,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,SAAS,CAAC,CAAqB;IACtC,IAAI,CAAC,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACtC,+EAA+E;IAC/E,+EAA+E;IAC/E,gFAAgF;IAChF,6EAA6E;IAC7E,IAAI,CAAC,KAAK,WAAW;QAAE,OAAO,SAAS,CAAC;IACxC,OAAO,CAAa,CAAC;AACvB,CAAC;AAED,8EAA8E;AAC9E,4BAA4B;AAC5B,8EAA8E;AAE9E;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,0BAA0B,CACxC,MAAkB,EAClB,GAA0B,EAC1B,QAA8B,EAC9B,cAAsB;IAEtB,IAAI,QAAQ,KAAK,UAAU;QAAE,OAAO;IAEpC,MAAM,cAAc,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;IAC/C,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,KAAK,MAAM,MAAM,IAAI,cAAc,EAAE,CAAC;QACpC,MAAM,KAAK,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACxC,IAAI,KAAK,KAAK,SAAS,IAAI,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YAC7C,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAED,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,uBAAuB,CAAC,MAAM,CAAC,IAAI,EAAE,cAAc,EAAE,SAAS,CAAC,CAAC;IAC5E,CAAC;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metaobjectsdev/migrate-ts",
3
- "version": "0.21.5",
3
+ "version": "0.21.6",
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.21.5"
48
+ "@metaobjectsdev/metadata": "0.21.6"
49
49
  },
50
50
  "peerDependencies": {
51
51
  "kysely": ">=0.27.0 <0.30.0"
package/src/errors.ts CHANGED
@@ -40,6 +40,10 @@ const ENABLE_FLAG_BY_KIND: Partial<Record<ChangeKind, string>> = {
40
40
  "change-column-nullable": "allow.nullableToNotNull",
41
41
  "drop-index": "allow.dropIndex",
42
42
  "drop-fk": "allow.dropFk",
43
+ // change-column-default is blocked in exactly one shape — dropping a live
44
+ // Postgres auto-sequence default with no declared @generation (see
45
+ // diff/status.ts) — and allow.dropIdentityDefault is the flag that enables it.
46
+ "change-column-default": "allow.dropIdentityDefault",
43
47
  };
44
48
 
45
49
  function changeLocator(c: Change): string {
@@ -5,7 +5,10 @@ import {
5
5
  IDENTITY_ATTR_FIELDS,
6
6
  TYPE_VALIDATOR,
7
7
  VALIDATOR_SUBTYPE_REQUIRED,
8
+ refMatchesObject,
9
+ resolveObjectRef,
8
10
  type MetaObject,
11
+ type MetaRelationship,
9
12
  type MetaReferenceIdentity,
10
13
  type MetaData,
11
14
  } from "@metaobjectsdev/metadata";
@@ -48,13 +51,27 @@ export function isRequired(field: MetaData): boolean {
48
51
  * Precedence (highest first):
49
52
  * 1. @onDelete / @onUpdate declared DIRECTLY on the identity.reference — the
50
53
  * reference IS the FK, so the action may be declared right where the FK is.
51
- * 2. A correlated sibling relationship on the same entity (matched on
52
- * target-entity name): its explicit @onDelete, else its subtype default
53
- * (composition→cascade, aggregation→set-null, association→restrict);
54
- * onUpdate defaults to "cascade".
55
- * 3. Neither undefined (no ON DELETE / ON UPDATE clause).
54
+ * 2. A correlated sibling relationship on the same entity matched
55
+ * package-aware against the resolved @references target (refMatchesObject
56
+ * / ADR-0042, so bare and FQN forms pair correctly); an M:N relationship
57
+ * (@through) never correlates with a direct FK. Its explicit @onDelete,
58
+ * else its subtype default (composition→cascade, aggregation→set-null,
59
+ * association→restrict); onUpdate defaults to "cascade".
60
+ * 3. A correlated REVERSE relationship on the TARGET entity — the documented
61
+ * parent-side authoring shape ("Program owns weeks": composition declared
62
+ * on the parent with @objectRef back at this FK-owning entity). Same
63
+ * explicit-action-else-subtype-default resolution as tier 2. Guards:
64
+ * an M:N relationship (@through) never correlates (it describes the
65
+ * junction path, not this direct FK); when the FK-owning entity holds
66
+ * MORE THAN ONE enforced reference to the same target the reverse
67
+ * relationship contributes nothing (it cannot say which FK carries the
68
+ * ownership edge — arming all of them could cascade through an edge the
69
+ * author never designated; fail closed); and an INFERRED set-null default
70
+ * (parent-side aggregation, no explicit @onDelete) on a NOT NULL FK drops
71
+ * the INFERRED contributions only — an authored @onUpdate on that same
72
+ * relationship still applies (see the in-body guard comment).
73
+ * 4. None → undefined (no ON DELETE / ON UPDATE clause).
56
74
  *
57
- * - "setnull" is accepted as an alias for the canonical "set-null".
58
75
  * - Resolved "no-action" → undefined: introspection in introspect/{postgres,sqlite}.ts
59
76
  * omits actions when the DB value is "no-action", so the expected side does the same
60
77
  * to keep round-trip diffs clean.
@@ -75,29 +92,78 @@ export function resolveReferentialActions(
75
92
  const target = ref.targetEntity;
76
93
  if (target === undefined) return { onDelete: undefined, onUpdate: undefined };
77
94
 
78
- // Correlation is by exact-string match. Every fixture in the corpus uses
79
- // bare entity names for @objectRef and @references (no `::`-FQN form), so
80
- // bare-vs-bare matching is sufficient today. If a future author writes an
81
- // FQN value on either side, this find returns undefined and both actions
82
- // resolve to undefined (no clause emitted) — surfacing the mismatch as a
83
- // silent loss of intent rather than a wrong action. Cross-language ports
84
- // should match the same correlation rule.
85
95
  // (1) Actions declared directly on the FK-defining reference win.
86
96
  const refOnDelete = ref.onDelete;
87
97
  const refOnUpdate = ref.onUpdate;
88
98
 
99
+ // Resolve the reference's target ONCE, package-aware (ADR-0042: a bare
100
+ // @references resolves in the DECLARING owner's package). Both relationship
101
+ // tiers then correlate against the resolved node with refMatchesObject, so a
102
+ // bare @references pairs correctly with an FQN @objectRef (and vice versa) —
103
+ // an exact-string tier 2 used to miss the FQN form, letting the parent-side
104
+ // tier override a child-side declaration.
105
+ const root = entity.parent;
106
+ const refOwner = ref.parent ?? entity;
107
+ const refOwnerPkg = refOwner.package ?? refOwner.fileDefaultPackage ?? "";
108
+ const targetObj =
109
+ root !== undefined ? (resolveObjectRef(root, target, refOwnerPkg).node as MetaObject | undefined) : undefined;
110
+
89
111
  // (2) Otherwise correlate with a sibling relationship and use its action /
90
112
  // subtype default. onUpdate's "cascade" default only applies when a
91
113
  // relationship is present, so a reference-only FK with no explicit
92
- // @onUpdate emits no ON UPDATE clause.
93
- const rel = entity.relationships().find((r) => r.objectRef === target);
114
+ // @onUpdate emits no ON UPDATE clause. An M:N relationship (@through)
115
+ // never correlates it describes the junction path, not this direct FK.
116
+ // When the target does not resolve (dangling @references — normally a
117
+ // load error), fall back to the legacy exact-string match so behavior on
118
+ // partially-valid trees is unchanged.
119
+ // (3) Failing that, correlate the REVERSE relationship declared on the
120
+ // TARGET entity (the documented parent-side authoring shape).
121
+ let rel = entity.relationships().find((r) => {
122
+ if (r.through !== undefined) return false;
123
+ const objectRef = r.objectRef;
124
+ if (objectRef === undefined) return false;
125
+ if (targetObj === undefined) return objectRef === target;
126
+ const relOwner = r.parent ?? entity;
127
+ const relOwnerPkg = relOwner.package ?? relOwner.fileDefaultPackage ?? "";
128
+ return refMatchesObject(targetObj, objectRef, relOwnerPkg);
129
+ });
130
+ // When the tier-3 satisfiability guard fires, the reverse relationship's
131
+ // AUTHORED @onUpdate still applies (only the inferred contributions drop).
132
+ let suppressedReverseOnUpdate: string | undefined;
133
+ if (rel === undefined && targetObj !== undefined) {
134
+ const reverse = findReverseRelationship(entity, ref, targetObj);
135
+ // Tier-3 satisfiability guard: an INFERRED set-null default (a parent-side
136
+ // aggregation with no explicit @onDelete) is unsatisfiable when any FK
137
+ // column is NOT NULL — SET NULL cannot fire there, and letting it through
138
+ // would turn a previously-valid model into a hard SetNullNotNullableError
139
+ // purely because the correlation got smarter. An inferred default never
140
+ // breaks a model: the INFERRED contributions drop (today's bare FK), while
141
+ // anything the author explicitly wrote survives — an EXPLICIT @onDelete:
142
+ // "set-null" flows through and hits the loud validateSetNullNullability
143
+ // error (the author asked for it), and an EXPLICIT @onUpdate is honored
144
+ // (silently dropping it would be the original bug again).
145
+ if (reverse !== undefined) {
146
+ const unsatisfiableInferredSetNull =
147
+ reverse.onDelete === undefined &&
148
+ ON_DELETE_DEFAULT_BY_SUBTYPE[reverse.subType] === "set-null" &&
149
+ readIdentityFields(ref).some((jsName) => {
150
+ const field = findField(entity, jsName);
151
+ return field !== undefined && isRequired(field);
152
+ });
153
+ if (unsatisfiableInferredSetNull) {
154
+ suppressedReverseOnUpdate = reverse.onUpdate;
155
+ } else {
156
+ rel = reverse;
157
+ }
158
+ }
159
+ }
94
160
 
95
161
  const onDeleteRaw =
96
162
  refOnDelete ??
97
163
  (rel ? (rel.onDelete ?? ON_DELETE_DEFAULT_BY_SUBTYPE[rel.subType]) : undefined);
98
164
  const onUpdateRaw =
99
165
  refOnUpdate ??
100
- (rel ? (rel.onUpdate ?? ON_UPDATE_DEFAULT) : undefined);
166
+ (rel ? (rel.onUpdate ?? ON_UPDATE_DEFAULT) : suppressedReverseOnUpdate);
101
167
 
102
168
  return {
103
169
  onDelete: normalize(onDeleteRaw),
@@ -105,12 +171,64 @@ export function resolveReferentialActions(
105
171
  };
106
172
  }
107
173
 
174
+ /**
175
+ * Tier-3 correlation: the relationship declared on the TARGET (parent) entity
176
+ * pointing back at the FK-owning entity — the shape the docs and the authoring
177
+ * skill teach ("Author owns posts": `relationship.composition { @objectRef:
178
+ * "Post", @cardinality: "many" }` on Author, while Post owns the FK).
179
+ *
180
+ * Guards (each fails closed to "no contribution"):
181
+ * - An M:N relationship (`@through`) never correlates — it describes the
182
+ * junction path, not this direct FK (the junction's own FKs correlate via
183
+ * its own identity.reference children). The same guard applies at tier 2.
184
+ * - When the FK-owning entity holds more than one enforced reference to the
185
+ * same target, the reverse relationship cannot say WHICH FK carries the
186
+ * ownership edge, so it contributes to none of them (arming every FK could
187
+ * cascade through an edge the author never designated).
188
+ *
189
+ * If multiple reverse relationships point back at the entity (rare), the first
190
+ * one is used — mirroring the tier-2 sibling-relationship rule.
191
+ */
192
+ function findReverseRelationship(
193
+ entity: MetaObject,
194
+ ref: MetaReferenceIdentity,
195
+ targetObj: MetaObject,
196
+ ): MetaRelationship | undefined {
197
+ const root = entity.parent;
198
+ if (root === undefined) return undefined;
199
+
200
+ // Ambiguity guard: exactly one enforced reference from `entity` to this
201
+ // target, and it must be `ref` itself.
202
+ const refsToTarget = entity.referenceIdentities().filter((r) => {
203
+ if (!r.enforce) return false;
204
+ const t = r.targetEntity;
205
+ if (t === undefined) return false;
206
+ const owner = r.parent ?? entity;
207
+ const ownerPkg = owner.package ?? owner.fileDefaultPackage ?? "";
208
+ return resolveObjectRef(root, t, ownerPkg).node === targetObj;
209
+ });
210
+ if (refsToTarget.length !== 1 || refsToTarget[0] !== ref) return undefined;
211
+
212
+ // The reverse relationship's bare @objectRef resolves in ITS declaring
213
+ // owner's package (normally the target entity's own package).
214
+ return targetObj.relationships().find((r) => {
215
+ if (r.through !== undefined) return false; // M:N — junction path, not this FK
216
+ const objectRef = r.objectRef;
217
+ if (objectRef === undefined) return false;
218
+ const relOwner = r.parent ?? targetObj;
219
+ const relOwnerPkg = relOwner.package ?? relOwner.fileDefaultPackage ?? "";
220
+ return refMatchesObject(entity, objectRef, relOwnerPkg);
221
+ });
222
+ }
223
+
108
224
  function normalize(a: string | undefined): FkAction | undefined {
109
225
  if (a === undefined) return undefined;
110
- // Accept the hyphen-less spelling as an alias for the canonical kebab-case form.
111
- const canonical = a === "setnull" ? "set-null" : a;
112
- if (canonical === "no-action") return undefined;
113
- return canonical as FkAction;
226
+ // Values are load-validated against REFERENTIAL_ACTIONS (allowedValues on both
227
+ // relationship.* and since ADR-0047 identity.reference), so only canonical
228
+ // kebab-case spellings reach this point. The legacy "setnull" alias was retired
229
+ // with the ADR-0047 registration: it now fails load with ERR_BAD_ATTR_VALUE.
230
+ if (a === "no-action") return undefined;
231
+ return a as FkAction;
114
232
  }
115
233
 
116
234
  // ---------------------------------------------------------------------------