@warlock.js/cascade 5.19.1 → 5.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. package/CHANGELOG.md +36 -1
  2. package/cjs/index.cjs +2356 -1039
  3. package/cjs/index.cjs.map +1 -1
  4. package/esm/context/database-transaction-context.d.mts +10 -0
  5. package/esm/context/database-transaction-context.d.mts.map +1 -1
  6. package/esm/context/database-transaction-context.mjs +17 -0
  7. package/esm/context/database-transaction-context.mjs.map +1 -1
  8. package/esm/contracts/query-builder.contract.d.mts +30 -3
  9. package/esm/contracts/query-builder.contract.d.mts.map +1 -1
  10. package/esm/data-source/data-source-registry.d.mts +33 -1
  11. package/esm/data-source/data-source-registry.d.mts.map +1 -1
  12. package/esm/data-source/data-source-registry.mjs +75 -5
  13. package/esm/data-source/data-source-registry.mjs.map +1 -1
  14. package/esm/database-dirty-tracker.d.mts +5 -0
  15. package/esm/database-dirty-tracker.d.mts.map +1 -1
  16. package/esm/database-dirty-tracker.mjs +16 -0
  17. package/esm/database-dirty-tracker.mjs.map +1 -1
  18. package/esm/drivers/mongodb/mongodb-driver.d.mts +12 -0
  19. package/esm/drivers/mongodb/mongodb-driver.d.mts.map +1 -1
  20. package/esm/drivers/mongodb/mongodb-driver.mjs +58 -19
  21. package/esm/drivers/mongodb/mongodb-driver.mjs.map +1 -1
  22. package/esm/drivers/mongodb/mongodb-migration-driver.d.mts +1 -1
  23. package/esm/drivers/mongodb/mongodb-migration-driver.d.mts.map +1 -1
  24. package/esm/drivers/mongodb/mongodb-migration-driver.mjs +15 -18
  25. package/esm/drivers/mongodb/mongodb-migration-driver.mjs.map +1 -1
  26. package/esm/drivers/mongodb/mongodb-query-builder.d.mts +13 -3
  27. package/esm/drivers/mongodb/mongodb-query-builder.d.mts.map +1 -1
  28. package/esm/drivers/mongodb/mongodb-query-builder.mjs +72 -19
  29. package/esm/drivers/mongodb/mongodb-query-builder.mjs.map +1 -1
  30. package/esm/drivers/mongodb/mongodb-query-parser.d.mts +6 -0
  31. package/esm/drivers/mongodb/mongodb-query-parser.d.mts.map +1 -1
  32. package/esm/drivers/mongodb/mongodb-query-parser.mjs +128 -30
  33. package/esm/drivers/mongodb/mongodb-query-parser.mjs.map +1 -1
  34. package/esm/drivers/postgres/index.d.mts +2 -3
  35. package/esm/drivers/postgres/index.mjs +1 -2
  36. package/esm/drivers/postgres/postgres-blueprint.mjs +4 -4
  37. package/esm/drivers/postgres/postgres-blueprint.mjs.map +1 -1
  38. package/esm/drivers/postgres/postgres-dialect.d.mts +5 -1
  39. package/esm/drivers/postgres/postgres-dialect.d.mts.map +1 -1
  40. package/esm/drivers/postgres/postgres-dialect.mjs +38 -8
  41. package/esm/drivers/postgres/postgres-dialect.mjs.map +1 -1
  42. package/esm/drivers/postgres/postgres-driver.d.mts +52 -29
  43. package/esm/drivers/postgres/postgres-driver.d.mts.map +1 -1
  44. package/esm/drivers/postgres/postgres-driver.mjs +196 -58
  45. package/esm/drivers/postgres/postgres-driver.mjs.map +1 -1
  46. package/esm/drivers/postgres/postgres-migration-driver.d.mts +2 -1
  47. package/esm/drivers/postgres/postgres-migration-driver.d.mts.map +1 -1
  48. package/esm/drivers/postgres/postgres-migration-driver.mjs +8 -6
  49. package/esm/drivers/postgres/postgres-migration-driver.mjs.map +1 -1
  50. package/esm/drivers/postgres/postgres-query-builder.d.mts +18 -7
  51. package/esm/drivers/postgres/postgres-query-builder.d.mts.map +1 -1
  52. package/esm/drivers/postgres/postgres-query-builder.mjs +146 -31
  53. package/esm/drivers/postgres/postgres-query-builder.mjs.map +1 -1
  54. package/esm/drivers/postgres/postgres-query-parser.d.mts +23 -1
  55. package/esm/drivers/postgres/postgres-query-parser.d.mts.map +1 -1
  56. package/esm/drivers/postgres/postgres-query-parser.mjs +160 -43
  57. package/esm/drivers/postgres/postgres-query-parser.mjs.map +1 -1
  58. package/esm/drivers/postgres/postgres-sql-serializer.mjs +3 -2
  59. package/esm/drivers/postgres/postgres-sql-serializer.mjs.map +1 -1
  60. package/esm/drivers/postgres/postgres-type-converters.mjs +57 -0
  61. package/esm/drivers/postgres/postgres-type-converters.mjs.map +1 -0
  62. package/esm/events/model-events.d.mts +1 -1
  63. package/esm/events/model-events.d.mts.map +1 -1
  64. package/esm/events/model-events.mjs.map +1 -1
  65. package/esm/index.d.mts +6 -6
  66. package/esm/index.mjs +5 -5
  67. package/esm/migration/column-builder.d.mts.map +1 -1
  68. package/esm/migration/column-builder.mjs +2 -2
  69. package/esm/migration/column-builder.mjs.map +1 -1
  70. package/esm/migration/index.d.mts +2 -2
  71. package/esm/migration/index.mjs +1 -1
  72. package/esm/migration/migration-order.mjs +5 -2
  73. package/esm/migration/migration-order.mjs.map +1 -1
  74. package/esm/migration/migration-runner.d.mts +50 -5
  75. package/esm/migration/migration-runner.d.mts.map +1 -1
  76. package/esm/migration/migration-runner.mjs +370 -147
  77. package/esm/migration/migration-runner.mjs.map +1 -1
  78. package/esm/migration/migration.d.mts +36 -3
  79. package/esm/migration/migration.d.mts.map +1 -1
  80. package/esm/migration/migration.mjs +62 -5
  81. package/esm/migration/migration.mjs.map +1 -1
  82. package/esm/model/methods/accessor-methods.mjs +1 -3
  83. package/esm/model/methods/accessor-methods.mjs.map +1 -1
  84. package/esm/model/methods/delete-methods.mjs +6 -2
  85. package/esm/model/methods/delete-methods.mjs.map +1 -1
  86. package/esm/model/methods/hydration-methods.mjs +16 -2
  87. package/esm/model/methods/hydration-methods.mjs.map +1 -1
  88. package/esm/model/methods/instance-event-methods.mjs +2 -1
  89. package/esm/model/methods/instance-event-methods.mjs.map +1 -1
  90. package/esm/model/methods/meta-methods.mjs +53 -16
  91. package/esm/model/methods/meta-methods.mjs.map +1 -1
  92. package/esm/model/methods/query-methods.mjs +86 -15
  93. package/esm/model/methods/query-methods.mjs.map +1 -1
  94. package/esm/model/methods/static-event-methods.mjs +16 -1
  95. package/esm/model/methods/static-event-methods.mjs.map +1 -1
  96. package/esm/model/methods/write-methods.d.mts.map +1 -1
  97. package/esm/model/methods/write-methods.mjs +50 -50
  98. package/esm/model/methods/write-methods.mjs.map +1 -1
  99. package/esm/model/model.d.mts +16 -1
  100. package/esm/model/model.d.mts.map +1 -1
  101. package/esm/model/model.mjs +39 -5
  102. package/esm/model/model.mjs.map +1 -1
  103. package/esm/model/register-model.d.mts +6 -1
  104. package/esm/model/register-model.d.mts.map +1 -1
  105. package/esm/model/register-model.mjs +16 -1
  106. package/esm/model/register-model.mjs.map +1 -1
  107. package/esm/query-builder/query-builder.d.mts +21 -2
  108. package/esm/query-builder/query-builder.d.mts.map +1 -1
  109. package/esm/query-builder/query-builder.mjs +97 -20
  110. package/esm/query-builder/query-builder.mjs.map +1 -1
  111. package/esm/relations/key-conventions.mjs +13 -3
  112. package/esm/relations/key-conventions.mjs.map +1 -1
  113. package/esm/relations/pivot-operations.mjs +1 -1
  114. package/esm/relations/relation-loader.d.mts +15 -5
  115. package/esm/relations/relation-loader.d.mts.map +1 -1
  116. package/esm/relations/relation-loader.mjs +51 -16
  117. package/esm/relations/relation-loader.mjs.map +1 -1
  118. package/esm/remover/database-remover.d.mts.map +1 -1
  119. package/esm/remover/database-remover.mjs +21 -7
  120. package/esm/remover/database-remover.mjs.map +1 -1
  121. package/esm/restorer/database-restorer.d.mts +12 -2
  122. package/esm/restorer/database-restorer.d.mts.map +1 -1
  123. package/esm/restorer/database-restorer.mjs +43 -13
  124. package/esm/restorer/database-restorer.mjs.map +1 -1
  125. package/esm/sync/model-events.mjs +63 -3
  126. package/esm/sync/model-events.mjs.map +1 -1
  127. package/esm/sync/model-sync-operation.d.mts.map +1 -1
  128. package/esm/sync/model-sync-operation.mjs +3 -2
  129. package/esm/sync/model-sync-operation.mjs.map +1 -1
  130. package/esm/sync/sync-manager.d.mts +3 -42
  131. package/esm/sync/sync-manager.d.mts.map +1 -1
  132. package/esm/sync/sync-manager.mjs +37 -126
  133. package/esm/sync/sync-manager.mjs.map +1 -1
  134. package/esm/transactions/after-commit.d.mts +24 -0
  135. package/esm/transactions/after-commit.d.mts.map +1 -0
  136. package/esm/transactions/after-commit.mjs +47 -0
  137. package/esm/transactions/after-commit.mjs.map +1 -0
  138. package/esm/utils/connect-to-database.d.mts.map +1 -1
  139. package/esm/utils/connect-to-database.mjs +4 -4
  140. package/esm/utils/connect-to-database.mjs.map +1 -1
  141. package/esm/utils/database-writer.utils.d.mts +1 -1
  142. package/esm/utils/database-writer.utils.d.mts.map +1 -1
  143. package/esm/utils/database-writer.utils.mjs +1 -0
  144. package/esm/utils/database-writer.utils.mjs.map +1 -1
  145. package/esm/utils/define-model.d.mts.map +1 -1
  146. package/esm/utils/define-model.mjs +2 -10
  147. package/esm/utils/define-model.mjs.map +1 -1
  148. package/esm/utils/is-valid-date-value.mjs +3 -2
  149. package/esm/utils/is-valid-date-value.mjs.map +1 -1
  150. package/esm/utils/once-connected.d.mts.map +1 -1
  151. package/esm/utils/once-connected.mjs +12 -2
  152. package/esm/utils/once-connected.mjs.map +1 -1
  153. package/esm/validation/mutators/embed-mutator.mjs +19 -11
  154. package/esm/validation/mutators/embed-mutator.mjs.map +1 -1
  155. package/esm/validation/plugins/embed-validator-plugin.d.mts +2 -2
  156. package/esm/validation/plugins/embed-validator-plugin.d.mts.map +1 -1
  157. package/esm/validation/plugins/embed-validator-plugin.mjs +8 -2
  158. package/esm/validation/plugins/embed-validator-plugin.mjs.map +1 -1
  159. package/esm/validation/rules/database-model-rule.mjs +6 -5
  160. package/esm/validation/rules/database-model-rule.mjs.map +1 -1
  161. package/esm/validation/rules/exists-rule.mjs +6 -4
  162. package/esm/validation/rules/exists-rule.mjs.map +1 -1
  163. package/esm/validation/rules/unique-rule.mjs +5 -2
  164. package/esm/validation/rules/unique-rule.mjs.map +1 -1
  165. package/esm/validation/validators/embed-validator.d.mts.map +1 -1
  166. package/esm/validation/validators/embed-validator.mjs +1 -1
  167. package/esm/validation/validators/embed-validator.mjs.map +1 -1
  168. package/esm/writer/database-writer.d.mts +27 -0
  169. package/esm/writer/database-writer.d.mts.map +1 -1
  170. package/esm/writer/database-writer.mjs +84 -17
  171. package/esm/writer/database-writer.mjs.map +1 -1
  172. package/llms-full.txt +2783 -2766
  173. package/llms.txt +15 -15
  174. package/package.json +6 -6
  175. package/skills/aggregate-data/SKILL.md +165 -165
  176. package/skills/alter-migration/SKILL.md +531 -531
  177. package/skills/configure-delete-strategy/SKILL.md +130 -130
  178. package/skills/define-model/SKILL.md +205 -205
  179. package/skills/define-relations/SKILL.md +171 -171
  180. package/skills/index.md +23 -0
  181. package/skills/manage-data-sources/SKILL.md +140 -140
  182. package/skills/manage-transactions/SKILL.md +208 -191
  183. package/skills/paginate-results/SKILL.md +122 -122
  184. package/skills/perform-atomic-ops/SKILL.md +156 -156
  185. package/skills/query-data/SKILL.md +213 -213
  186. package/skills/run-cascade-cli/SKILL.md +125 -125
  187. package/skills/search-by-vector/SKILL.md +127 -127
  188. package/skills/subscribe-to-model-events/SKILL.md +148 -148
  189. package/skills/track-changes/SKILL.md +127 -127
  190. package/skills/write-migration/SKILL.md +206 -206
  191. package/esm/drivers/postgres/postgres-sync-adapter.d.mts +0 -79
  192. package/esm/drivers/postgres/postgres-sync-adapter.d.mts.map +0 -1
  193. package/esm/drivers/postgres/postgres-sync-adapter.mjs +0 -162
  194. package/esm/drivers/postgres/postgres-sync-adapter.mjs.map +0 -1
@@ -1,130 +1,130 @@
1
- ---
2
- name: configure-delete-strategy
3
- description: "Use delete strategies in @warlock.js/cascade; use when you need to configure delete strategy."
4
- ---
5
-
6
- # Use delete strategies
7
-
8
- `destroy()` does more than `DELETE FROM table`. Cascade supports three strategies that change what happens to the record. Your data source's `defaultDeleteStrategy` applies unless you override per-model or per-call; with nothing configured the fallback is `permanent`.
9
-
10
- ## The three strategies
11
-
12
- `DeleteStrategy` is `"permanent" | "soft" | "trash"`.
13
-
14
- | Strategy | Behavior | Reversible? |
15
- | --- | --- | --- |
16
- | `permanent` | DELETE the row / document (the default fallback) | No |
17
- | `soft` | Set the `deletedAt` column; the row stays in the table | Yes — via `Model.restore(id)` |
18
- | `trash` | Move the record to a separate table / collection, then delete the original | Yes — via `Model.restore(id)` |
19
-
20
- ## Set the default per-model
21
-
22
- ```ts
23
- import { Model, RegisterModel } from "@warlock.js/cascade";
24
-
25
- @RegisterModel()
26
- export class User extends Model<UserSchema> {
27
- public static table = "users";
28
- public static schema = userSchema;
29
- public static deleteStrategy = "soft" as const; // every destroy() is soft unless overridden
30
- public static deletedAtColumn = "deletedAt"; // default; set false to disable the column
31
- }
32
- ```
33
-
34
- When the strategy resolves to `"soft"`, `Migration.create(Model, { … })` adds the `deletedAt` column for you (using `deletedAtColumn`), so the schema matches what `destroy()` writes — no need to declare it in the migration. Opt out per table with `{ softDeletes: false }`. See [`@warlock.js/cascade/write-migration/SKILL.md`](@warlock.js/cascade/write-migration/SKILL.md).
35
-
36
- ## Override per-call
37
-
38
- ```ts
39
- await user.destroy(); // uses the model's strategy
40
- await user.destroy({ strategy: "permanent" }); // GDPR-compliant hard delete
41
- await user.destroy({ strategy: "trash" }); // move to the trash table
42
- ```
43
-
44
- Resolution order: `destroy({ strategy })` → `static deleteStrategy` → data source `defaultDeleteStrategy` → `"permanent"`.
45
-
46
- After a `"soft"` destroy the in-memory instance reflects the change — `model.get(deletedAtColumn)` returns the persisted timestamp (the row stays, so the instance stays usable).
47
-
48
- ## Restoring — static, by id
49
-
50
- Restore is a **static** operation keyed by the record's id. It auto-detects whether the record was soft-deleted or trashed:
51
-
52
- ```ts
53
- const user = await User.restore(123); // clears deletedAt or pulls from trash; fires "restored"
54
- const user2 = await User.restore(123, { onIdConflict: "fail" });
55
-
56
- await User.restoreAll(); // restore every deleted record for this model
57
- ```
58
-
59
- There is no instance `user.restore()` — call `User.restore(id)`. `restoreAll(options?)` restores all soft-deleted (or all trashed) records for the model's table.
60
-
61
- ## Trash strategy — a separate store
62
-
63
- ```ts
64
- await user.destroy({ strategy: "trash" }); // moved to the model's trash table, original deleted
65
-
66
- await User.restore(user.id); // pull back out of the trash table
67
- ```
68
-
69
- The trash table name resolves as: `static trashTable` on the model → data source `defaultTrashTable` → the `{table}Trash` pattern (e.g. `usersTrash`). Useful when soft-delete clutter would bloat the main table.
70
-
71
- ## Querying soft-deleted records — you filter, Cascade doesn't
72
-
73
- **Important:** Cascade does **not** auto-hide soft-deleted rows. A plain `User.all()` returns deleted rows too, because the query builder has no built-in `deletedAt` filter. If you want the common "active records only" default, register a global scope yourself:
74
-
75
- ```ts
76
- @RegisterModel()
77
- export class User extends Model<UserSchema> {
78
- public static table = "users";
79
- public static schema = userSchema;
80
- public static deleteStrategy = "soft" as const;
81
-
82
- static {
83
- // hide soft-deleted rows from every query by default
84
- this.addGlobalScope("notDeleted", (query) => {
85
- query.whereNull("deletedAt");
86
- });
87
- }
88
- }
89
- ```
90
-
91
- With that scope in place:
92
-
93
- ```ts
94
- await User.all(); // active only (scope applied)
95
- await User.query().withoutGlobalScope("notDeleted").get(); // active + soft-deleted
96
- await User.query().withoutGlobalScope("notDeleted").whereNotNull("deletedAt").get(); // only deleted
97
- ```
98
-
99
- `withoutGlobalScope("notDeleted")` bypasses the filter for one query; `withoutGlobalScopes()` drops all of them. See [`@warlock.js/cascade/query-data/SKILL.md`](@warlock.js/cascade/query-data/SKILL.md) for scopes.
100
-
101
- ## Lifecycle hooks fire on all strategies
102
-
103
- The delete events are `deleting` / `deleted` (not `destroying` / `destroyed`):
104
-
105
- ```ts
106
- User.on("deleting", async (user) => {
107
- /* about to delete, any strategy */
108
- });
109
- User.on("deleted", async (user) => {
110
- /* delete completed; context carries the strategy + trashRecord for trash */
111
- });
112
- User.on("restored", async (user) => {
113
- /* soft-delete or trash restoration completed */
114
- });
115
- ```
116
-
117
- See [`@warlock.js/cascade/subscribe-to-model-events/SKILL.md`](@warlock.js/cascade/subscribe-to-model-events/SKILL.md).
118
-
119
- ## Things NOT to do
120
-
121
- - Don't switch a model to `soft` without a migration adding the `deletedAt` column. The strategy writes to it.
122
- - Don't assume soft-deleted rows are hidden automatically — they're not. Add a `notDeleted` global scope (above) or filter `whereNull("deletedAt")` explicitly.
123
- - Don't call `user.restore()` — restore is static: `User.restore(id)` / `User.restoreAll()`.
124
- - Don't expect `restore` to undo a `permanent` delete. Hard deletes are gone.
125
- - Don't use soft delete for GDPR right-to-be-forgotten data. The record stays in the DB; you need `permanent` (plus backup cleanup).
126
-
127
- ## See also
128
-
129
- - [`@warlock.js/cascade/subscribe-to-model-events/SKILL.md`](@warlock.js/cascade/subscribe-to-model-events/SKILL.md) — `deleting` / `deleted` / `restored` events
130
- - [`@warlock.js/cascade/query-data/SKILL.md`](@warlock.js/cascade/query-data/SKILL.md) — global scopes and `withoutGlobalScope` for surfacing deleted rows
1
+ ---
2
+ name: configure-delete-strategy
3
+ description: 'Pick the delete behavior — `permanent` (hard delete), `soft` (set `deletedAt`, keep the row), `trash` (move to a separate table). Configure via `static deleteStrategy` or `.destroy({ strategy })`; restore via static `Model.restore(id)` / `Model.restoreAll()`. Triggers: `static deleteStrategy`, `.destroy`, `Model.restore`, `Model.restoreAll`, `deletedAtColumn`, `trashTable`; "soft delete users", "restore a deleted record", "GDPR hard delete"; typical import `import { Model } from "@warlock.js/cascade"`. Skip: lifecycle events — `@warlock.js/cascade/subscribe-to-model-events/SKILL.md`; competing libs `mongoose-delete`, `typeorm softRemove`, `sequelize` paranoid.'
4
+ ---
5
+
6
+ # Use delete strategies
7
+
8
+ `destroy()` does more than `DELETE FROM table`. Cascade supports three strategies that change what happens to the record. Your data source's `defaultDeleteStrategy` applies unless you override per-model or per-call; with nothing configured the fallback is `permanent`.
9
+
10
+ ## The three strategies
11
+
12
+ `DeleteStrategy` is `"permanent" | "soft" | "trash"`.
13
+
14
+ | Strategy | Behavior | Reversible? |
15
+ | --- | --- | --- |
16
+ | `permanent` | DELETE the row / document (the default fallback) | No |
17
+ | `soft` | Set the `deletedAt` column; the row stays in the table | Yes — via `Model.restore(id)` |
18
+ | `trash` | Move the record to a separate table / collection, then delete the original | Yes — via `Model.restore(id)` |
19
+
20
+ ## Set the default per-model
21
+
22
+ ```ts
23
+ import { Model, RegisterModel } from "@warlock.js/cascade";
24
+
25
+ @RegisterModel()
26
+ export class User extends Model<UserSchema> {
27
+ public static table = "users";
28
+ public static schema = userSchema;
29
+ public static deleteStrategy = "soft" as const; // every destroy() is soft unless overridden
30
+ public static deletedAtColumn = "deletedAt"; // default; set false to disable the column
31
+ }
32
+ ```
33
+
34
+ When the strategy resolves to `"soft"`, `Migration.create(Model, { … })` adds the `deletedAt` column for you (using `deletedAtColumn`), so the schema matches what `destroy()` writes — no need to declare it in the migration. Opt out per table with `{ softDeletes: false }`. See [`@warlock.js/cascade/write-migration/SKILL.md`](@warlock.js/cascade/write-migration/SKILL.md).
35
+
36
+ ## Override per-call
37
+
38
+ ```ts
39
+ await user.destroy(); // uses the model's strategy
40
+ await user.destroy({ strategy: "permanent" }); // GDPR-compliant hard delete
41
+ await user.destroy({ strategy: "trash" }); // move to the trash table
42
+ ```
43
+
44
+ Resolution order: `destroy({ strategy })` → `static deleteStrategy` → data source `defaultDeleteStrategy` → `"permanent"`.
45
+
46
+ After a `"soft"` destroy the in-memory instance reflects the change — `model.get(deletedAtColumn)` returns the persisted timestamp (the row stays, so the instance stays usable).
47
+
48
+ ## Restoring — static, by id
49
+
50
+ Restore is a **static** operation keyed by the record's id. It auto-detects whether the record was soft-deleted or trashed:
51
+
52
+ ```ts
53
+ const user = await User.restore(123); // clears deletedAt or pulls from trash; fires "restored"
54
+ const user2 = await User.restore(123, { onIdConflict: "fail" });
55
+
56
+ await User.restoreAll(); // restore every deleted record for this model
57
+ ```
58
+
59
+ There is no instance `user.restore()` — call `User.restore(id)`. `restoreAll(options?)` restores all soft-deleted (or all trashed) records for the model's table.
60
+
61
+ ## Trash strategy — a separate store
62
+
63
+ ```ts
64
+ await user.destroy({ strategy: "trash" }); // moved to the model's trash table, original deleted
65
+
66
+ await User.restore(user.id); // pull back out of the trash table
67
+ ```
68
+
69
+ The trash table name resolves as: `static trashTable` on the model → data source `defaultTrashTable` → the `{table}Trash` pattern (e.g. `usersTrash`). Useful when soft-delete clutter would bloat the main table.
70
+
71
+ ## Querying soft-deleted records — you filter, Cascade doesn't
72
+
73
+ **Important:** Cascade does **not** auto-hide soft-deleted rows. A plain `User.all()` returns deleted rows too, because the query builder has no built-in `deletedAt` filter. If you want the common "active records only" default, register a global scope yourself:
74
+
75
+ ```ts
76
+ @RegisterModel()
77
+ export class User extends Model<UserSchema> {
78
+ public static table = "users";
79
+ public static schema = userSchema;
80
+ public static deleteStrategy = "soft" as const;
81
+
82
+ static {
83
+ // hide soft-deleted rows from every query by default
84
+ this.addGlobalScope("notDeleted", (query) => {
85
+ query.whereNull("deletedAt");
86
+ });
87
+ }
88
+ }
89
+ ```
90
+
91
+ With that scope in place:
92
+
93
+ ```ts
94
+ await User.all(); // active only (scope applied)
95
+ await User.query().withoutGlobalScope("notDeleted").get(); // active + soft-deleted
96
+ await User.query().withoutGlobalScope("notDeleted").whereNotNull("deletedAt").get(); // only deleted
97
+ ```
98
+
99
+ `withoutGlobalScope("notDeleted")` bypasses the filter for one query; `withoutGlobalScopes()` drops all of them. See [`@warlock.js/cascade/query-data/SKILL.md`](@warlock.js/cascade/query-data/SKILL.md) for scopes.
100
+
101
+ ## Lifecycle hooks fire on all strategies
102
+
103
+ The delete events are `deleting` / `deleted` (not `destroying` / `destroyed`):
104
+
105
+ ```ts
106
+ User.on("deleting", async (user) => {
107
+ /* about to delete, any strategy */
108
+ });
109
+ User.on("deleted", async (user) => {
110
+ /* delete completed; context carries the strategy + trashRecord for trash */
111
+ });
112
+ User.on("restored", async (user) => {
113
+ /* soft-delete or trash restoration completed */
114
+ });
115
+ ```
116
+
117
+ See [`@warlock.js/cascade/subscribe-to-model-events/SKILL.md`](@warlock.js/cascade/subscribe-to-model-events/SKILL.md).
118
+
119
+ ## Things NOT to do
120
+
121
+ - Don't switch a model to `soft` without a migration adding the `deletedAt` column. The strategy writes to it.
122
+ - Don't assume soft-deleted rows are hidden automatically — they're not. Add a `notDeleted` global scope (above) or filter `whereNull("deletedAt")` explicitly.
123
+ - Don't call `user.restore()` — restore is static: `User.restore(id)` / `User.restoreAll()`.
124
+ - Don't expect `restore` to undo a `permanent` delete. Hard deletes are gone.
125
+ - Don't use soft delete for GDPR right-to-be-forgotten data. The record stays in the DB; you need `permanent` (plus backup cleanup).
126
+
127
+ ## See also
128
+
129
+ - [`@warlock.js/cascade/subscribe-to-model-events/SKILL.md`](@warlock.js/cascade/subscribe-to-model-events/SKILL.md) — `deleting` / `deleted` / `restored` events
130
+ - [`@warlock.js/cascade/query-data/SKILL.md`](@warlock.js/cascade/query-data/SKILL.md) — global scopes and `withoutGlobalScope` for surfacing deleted rows