@warlock.js/cascade 5.27.0 → 5.29.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.
- package/CHANGELOG.md +13 -0
- package/cjs/index.cjs +134 -4
- package/cjs/index.cjs.map +1 -1
- package/esm/database-dirty-tracker.d.mts +38 -0
- package/esm/database-dirty-tracker.d.mts.map +1 -1
- package/esm/database-dirty-tracker.mjs +77 -2
- package/esm/database-dirty-tracker.mjs.map +1 -1
- package/esm/model/methods/accessor-methods.mjs +11 -0
- package/esm/model/methods/accessor-methods.mjs.map +1 -1
- package/esm/utils/prune-empty-parents.mjs +38 -0
- package/esm/utils/prune-empty-parents.mjs.map +1 -0
- package/esm/writer/database-writer.d.mts.map +1 -1
- package/esm/writer/database-writer.mjs +14 -2
- package/esm/writer/database-writer.mjs.map +1 -1
- package/llms-full.txt +93 -93
- package/llms.txt +15 -15
- package/package.json +5 -5
- package/skills/README.md +17 -17
- package/skills/aggregate-data/SKILL.md +3 -3
- package/skills/alter-migration/SKILL.md +4 -4
- package/skills/cascade-basics/SKILL.md +21 -21
- package/skills/configure-delete-strategy/SKILL.md +6 -6
- package/skills/define-model/SKILL.md +8 -8
- package/skills/define-relations/SKILL.md +3 -3
- package/skills/manage-data-sources/SKILL.md +4 -4
- package/skills/manage-transactions/SKILL.md +4 -4
- package/skills/paginate-results/SKILL.md +4 -4
- package/skills/perform-atomic-ops/SKILL.md +5 -5
- package/skills/query-data/SKILL.md +10 -10
- package/skills/run-cascade-cli/SKILL.md +3 -3
- package/skills/search-by-vector/SKILL.md +3 -3
- package/skills/subscribe-to-model-events/SKILL.md +5 -5
- package/skills/track-changes/SKILL.md +3 -3
- package/skills/write-migration/SKILL.md +7 -7
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: query-data
|
|
3
|
-
description: 'Query records via the model — `.where(field, value)` / `.where(field, op, value)`, `.find(id)` / `.first` / `.all`, `.orderBy`, `.count` / `.exists`, `.lean()` plain-object reads, plus `.whereIn` / `.whereBetween` / `.whereLike` / `.pluck` / `.firstOrFail` / scopes via `addScope`. Covers filter safety: `where()` rejects `$`-prefixed keys (`UnsafeFilterError`), `whereRaw()` string form rejects `$where`/`$function`/`$accumulator` (`UnsafeRawExpressionError`), and `whereLike`/`whereStartsWith`/`whereEndsWith`/`whereSearch` match string arguments literally (pass a `RegExp` for pattern semantics). Triggers: `.where`, `.find`, `.first`, `.firstOrFail`, `.all`, `.get`, `.orderBy`, `.exists`, `.whereIn`, `.whereBetween`, `.whereLike`, `.whereRaw`, `.lean`, `UnsupportedLeanOperationError`, `addScope`, `escapeRegex`, `likePatternToRegexSource`, `UnsafeFilterError`, `UnsafeRawExpressionError`; "filter by status", "find by id", "fetch active users", "check existence", "search box query", "is where() safe from injection"; typical import `import { Model } from "@warlock.js/cascade"`. Skip: pagination —
|
|
3
|
+
description: 'Query records via the model — `.where(field, value)` / `.where(field, op, value)`, `.find(id)` / `.first` / `.all`, `.orderBy`, `.count` / `.exists`, `.lean()` plain-object reads, plus `.whereIn` / `.whereBetween` / `.whereLike` / `.pluck` / `.firstOrFail` / scopes via `addScope`. Covers filter safety: `where()` rejects `$`-prefixed keys (`UnsafeFilterError`), `whereRaw()` string form rejects `$where`/`$function`/`$accumulator` (`UnsafeRawExpressionError`), and `whereLike`/`whereStartsWith`/`whereEndsWith`/`whereSearch` match string arguments literally (pass a `RegExp` for pattern semantics). Triggers: `.where`, `.find`, `.first`, `.firstOrFail`, `.all`, `.get`, `.orderBy`, `.exists`, `.whereIn`, `.whereBetween`, `.whereLike`, `.whereRaw`, `.lean`, `UnsupportedLeanOperationError`, `addScope`, `escapeRegex`, `likePatternToRegexSource`, `UnsafeFilterError`, `UnsafeRawExpressionError`; "filter by status", "find by id", "fetch active users", "check existence", "search box query", "is where() safe from injection"; typical import `import { Model } from "@warlock.js/cascade"`. Skip: pagination — the `paginate-results` topic; aggregates — the `aggregate-data` topic.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Query data
|
|
@@ -48,7 +48,7 @@ Equivalent to chained equalities. Useful when the filter comes from a dynamic so
|
|
|
48
48
|
|
|
49
49
|
### Filters are safe to build from request data — but only through this API
|
|
50
50
|
|
|
51
|
-
`where(field, value)`, `where(field, "=", value)` and the object form all run the value through a `$`-prefixed-key check before it reaches the driver, so `User.first({ email: req.body.email, password: req.body.password })` throws `UnsafeFilterError` instead of compiling `{ password: { $ne: null } }` into a live MongoDB operator query (the classic NoSQL auth-bypass). This applies to every filter-accepting model static too — `first`, `findFirst`, `findAll`, `count`, `paginate`, `all`, `deleteMany`/`delete`, `deleteOne`, and the atomic/find-and-modify statics in
|
|
51
|
+
`where(field, value)`, `where(field, "=", value)` and the object form all run the value through a `$`-prefixed-key check before it reaches the driver, so `User.first({ email: req.body.email, password: req.body.password })` throws `UnsafeFilterError` instead of compiling `{ password: { $ne: null } }` into a live MongoDB operator query (the classic NoSQL auth-bypass). This applies to every filter-accepting model static too — `first`, `findFirst`, `findAll`, `count`, `paginate`, `all`, `deleteMany`/`delete`, `deleteOne`, and the atomic/find-and-modify statics in the `perform-atomic-ops` topic. Explicit operator APIs (`where(field, operator, value)` for operators other than `=`, `whereIn`/`whereNull`/`whereBetween`/…, object-form `whereRaw({ ... })`) are intentionally not routed through this check — they already express an operator on purpose. Dotted paths (`"profile.name"`) and plain nested-object equality values remain valid; only literal `$`-prefixed keys are rejected. `sanitizeFilter` / `sanitizeFilterValue` are exported if you need the same check on a filter you forward to a driver-level API directly.
|
|
52
52
|
|
|
53
53
|
```ts
|
|
54
54
|
await User.first({ email, password }); // throws UnsafeFilterError if password is `{ $ne: null }`
|
|
@@ -102,7 +102,7 @@ const newest = await User
|
|
|
102
102
|
|
|
103
103
|
`.orderBy(field, "asc" | "desc")` sorts. Default direction is `"asc"`. Chain multiple `.orderBy()` for tiebreakers.
|
|
104
104
|
|
|
105
|
-
For pagination see
|
|
105
|
+
For pagination see the `paginate-results` topic.
|
|
106
106
|
|
|
107
107
|
## Plain objects — `.lean()`
|
|
108
108
|
|
|
@@ -135,7 +135,7 @@ const activeUsers = await User.all({ status: "active" });
|
|
|
135
135
|
|
|
136
136
|
`Model.all(filter?)` is the shortcut for "fetch all records matching a simple equality filter, or every record if no filter."
|
|
137
137
|
|
|
138
|
-
**Caution.** `.all()` with no filter loads the entire table. Use
|
|
138
|
+
**Caution.** `.all()` with no filter loads the entire table. Use pagination (see the `paginate-results` topic) for tables larger than a few hundred rows.
|
|
139
139
|
|
|
140
140
|
## The wider query vocabulary
|
|
141
141
|
|
|
@@ -149,11 +149,11 @@ Cascade's query builder has around 60 methods. Reach for these as the need arise
|
|
|
149
149
|
| `.whereDate(field, value)`, `.whereDateBetween`, `.whereDateBefore`, `.whereDateAfter` | Date helpers |
|
|
150
150
|
| `.whereLike(field, pattern)` / `.whereStartsWith` / `.whereEndsWith` / `.whereSearch` | Pattern matching — see below for literal-vs-regex semantics |
|
|
151
151
|
| `.whereHas(relation, callback)` | Filter by conditions on a related model |
|
|
152
|
-
| `.sum(field)` / `.avg(field)` / `.min(field)` / `.max(field)` | Aggregates —
|
|
152
|
+
| `.sum(field)` / `.avg(field)` / `.min(field)` / `.max(field)` | Aggregates — the `aggregate-data` topic |
|
|
153
153
|
| `.distinct(field)` / `.pluck(field)` | Single-field reads (distinct values, flat list) |
|
|
154
154
|
| `.chunk(size, callback)` | Stream a large table in batches |
|
|
155
|
-
| `.cursorPaginate({ limit, cursor })` | Cursor pagination —
|
|
156
|
-
| `.similarTo(column, embedding)` | Vector similarity —
|
|
155
|
+
| `.cursorPaginate({ limit, cursor })` | Cursor pagination — the `paginate-results` topic |
|
|
156
|
+
| `.similarTo(column, embedding)` | Vector similarity — the `search-by-vector` topic |
|
|
157
157
|
|
|
158
158
|
Each chains off `User.where(...)` or `User.query()` and ends with the appropriate terminator. (`where`, `with`, `joinWith`, `first`, `count`, `find`, `all`, `paginate` are static shortcuts on the model; the rest live on the query builder, so reach them via `User.query()` or by chaining off a static `where`.)
|
|
159
159
|
|
|
@@ -208,6 +208,6 @@ const activeUsers = await User.query().scope("active").get();
|
|
|
208
208
|
|
|
209
209
|
## See also
|
|
210
210
|
|
|
211
|
-
-
|
|
212
|
-
-
|
|
213
|
-
-
|
|
211
|
+
- The `define-relations` topic — `.with(...)`, `.whereHas(...)`, eager loading
|
|
212
|
+
- The `paginate-results` topic — pagination + cursor + chunk
|
|
213
|
+
- The `aggregate-data` topic — `.sum`, `.avg`, `.groupBy`, `.having`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: run-cascade-cli
|
|
3
|
-
description: 'Cascade''s standalone `cascade` binary + the Operations API it wraps — `cascade migrate` / `migrate:list` / `migrate:rollback` / `migrate:export-sql`, and `runMigrations` / `rollbackMigrations` / `freshMigrate` / `exportMigrationsSQL` / `listExecutedMigrations` / `createDatabase` / `dropAllTables` / `migrationRunner`. Triggers: `cascade migrate`, `migrate:list`, `migrate:rollback`, `migrate:export-sql`, `runMigrations`, `rollbackMigrations`, `freshMigrate`, `exportMigrationsSQL`, `listExecutedMigrations`, `migrationRunner`; "run migrations in deploy/CI", "reset DB for tests", "programmatic migration", "foreign key constraint cannot be implemented", `CASCADE_PRIMARY_KEY`; typical import `import { runMigrations, migrationRunner } from "@warlock.js/cascade"`. Skip: writing migration files —
|
|
3
|
+
description: 'Cascade''s standalone `cascade` binary + the Operations API it wraps — `cascade migrate` / `migrate:list` / `migrate:rollback` / `migrate:export-sql`, and `runMigrations` / `rollbackMigrations` / `freshMigrate` / `exportMigrationsSQL` / `listExecutedMigrations` / `createDatabase` / `dropAllTables` / `migrationRunner`. Triggers: `cascade migrate`, `migrate:list`, `migrate:rollback`, `migrate:export-sql`, `runMigrations`, `rollbackMigrations`, `freshMigrate`, `exportMigrationsSQL`, `listExecutedMigrations`, `migrationRunner`; "run migrations in deploy/CI", "reset DB for tests", "programmatic migration", "foreign key constraint cannot be implemented", `CASCADE_PRIMARY_KEY`; typical import `import { runMigrations, migrationRunner } from "@warlock.js/cascade"`. Skip: writing migration files — the `write-migration` topic; competing tools `knex migrate:latest`, `prisma migrate deploy`, `typeorm migration:run`.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Run the cascade CLI / Operations API
|
|
@@ -121,5 +121,5 @@ Reach for these when you need migrations inside test setup (`beforeAll(async ()
|
|
|
121
121
|
|
|
122
122
|
## See also
|
|
123
123
|
|
|
124
|
-
-
|
|
125
|
-
-
|
|
124
|
+
- The `write-migration` topic — writing migration files
|
|
125
|
+
- The `manage-data-sources` topic — multi-database routing
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: search-by-vector
|
|
3
|
-
description: 'Vector similarity search via `.similarTo(column, embedding, alias?)` — adds a similarity `score` column and orders by vector distance so the index is used; cap results with `.limit()`. Postgres uses pgvector (IVFFlat index via `this.vectorIndex`); MongoDB needs Atlas. Schema: `this.vector(column, dimensions)` + `this.vectorIndex(column, { dimensions, similarity })`. Triggers: `.similarTo`, `this.vector`, `this.vectorIndex`, `.whereFullText`, pgvector; "semantic search", "RAG retrieval", "find similar articles", "hybrid vector + full-text"; typical import `import { Model } from "@warlock.js/cascade"`. Skip: query basics —
|
|
3
|
+
description: 'Vector similarity search via `.similarTo(column, embedding, alias?)` — adds a similarity `score` column and orders by vector distance so the index is used; cap results with `.limit()`. Postgres uses pgvector (IVFFlat index via `this.vectorIndex`); MongoDB needs Atlas. Schema: `this.vector(column, dimensions)` + `this.vectorIndex(column, { dimensions, similarity })`. Triggers: `.similarTo`, `this.vector`, `this.vectorIndex`, `.whereFullText`, pgvector; "semantic search", "RAG retrieval", "find similar articles", "hybrid vector + full-text"; typical import `import { Model } from "@warlock.js/cascade"`. Skip: query basics — the `query-data` topic; semantic cache — the `use-cache-similarity` topic of the `warlock-js-cache` skill; competing libs `pgvector` directly, `chromadb`, `pinecone`, `weaviate`, `qdrant`.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Use vector search
|
|
@@ -123,5 +123,5 @@ For local dev with MongoDB, develop the vector path against Postgres + pgvector.
|
|
|
123
123
|
|
|
124
124
|
## See also
|
|
125
125
|
|
|
126
|
-
-
|
|
127
|
-
-
|
|
126
|
+
- The `query-data` topic — `.where`, `.whereFullText`, the broader query vocabulary
|
|
127
|
+
- The `use-cache-similarity` topic of the `warlock-js-cache` skill — semantic cache of LLM output
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: subscribe-to-model-events
|
|
3
|
-
description: 'Hook into model lifecycle events — `saving` / `saved`, `creating` / `created`, `updating` / `updated`, `validating` / `validated`, `deleting` / `deleted`, `restoring` / `restored`, `fetching` / `fetched`. Per-model `Model.on(event, fn)` or global via `Model.globalEvents()`. Triggers: `Model.on`, `Model.off`, `saving`, `saved`, `created`, `updated`, `deleting`, `deleted`, `restored`; "audit log on save", "notify on change", "denormalize into search index"; typical import `import { Model } from "@warlock.js/cascade"`. Skip: dirty tracking —
|
|
3
|
+
description: 'Hook into model lifecycle events — `saving` / `saved`, `creating` / `created`, `updating` / `updated`, `validating` / `validated`, `deleting` / `deleted`, `restoring` / `restored`, `fetching` / `fetched`. Per-model `Model.on(event, fn)` or global via `Model.globalEvents()`. Triggers: `Model.on`, `Model.off`, `saving`, `saved`, `created`, `updated`, `deleting`, `deleted`, `restored`; "audit log on save", "notify on change", "denormalize into search index"; typical import `import { Model } from "@warlock.js/cascade"`. Skip: dirty tracking — the `track-changes` topic; competing libs `mongoose` middleware, `typeorm` subscribers, `prisma` extensions.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Use model events
|
|
@@ -63,7 +63,7 @@ User.on("saving", async (user) => {
|
|
|
63
63
|
});
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
Pair with `isDirty()` from
|
|
66
|
+
Pair with `isDirty()` from the `track-changes` topic so the listener only runs its expensive check when the relevant field changed.
|
|
67
67
|
|
|
68
68
|
## Global listeners — across all models
|
|
69
69
|
|
|
@@ -129,7 +129,7 @@ User.on("deleted", async (user) => {
|
|
|
129
129
|
});
|
|
130
130
|
```
|
|
131
131
|
|
|
132
|
-
See
|
|
132
|
+
See the `use-cache-tags` topic of the `warlock-js-cache` skill.
|
|
133
133
|
|
|
134
134
|
### Side effects that must only fire after commit
|
|
135
135
|
|
|
@@ -144,5 +144,5 @@ For external side effects (queues, emails) that must only fire if a surrounding
|
|
|
144
144
|
|
|
145
145
|
## See also
|
|
146
146
|
|
|
147
|
-
-
|
|
148
|
-
-
|
|
147
|
+
- The `track-changes` topic — `isDirty` / `getDirtyColumnsWithValues` for "only run if this field changed"
|
|
148
|
+
- The `configure-delete-strategy` topic — `deleting` / `deleted` / `restored` around delete strategies
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: track-changes
|
|
3
|
-
description: 'Inspect a model''s pending changes — `hasChanges()` (any field dirty?), `isDirty(column)` (one column), `getDirtyColumns()` (changed field names), `getDirtyColumnsWithValues()` (old + new per field), `getRemovedColumns()` (unset fields). Triggers: `hasChanges`, `isDirty`, `getDirtyColumns`, `getDirtyColumnsWithValues`, `getRemovedColumns`; "only run if email changed", "diff for an audit log", "what fields are dirty", "compare old vs new value"; typical import `import { Model } from "@warlock.js/cascade"`. Skip: hooking into save —
|
|
3
|
+
description: 'Inspect a model''s pending changes — `hasChanges()` (any field dirty?), `isDirty(column)` (one column), `getDirtyColumns()` (changed field names), `getDirtyColumnsWithValues()` (old + new per field), `getRemovedColumns()` (unset fields). Triggers: `hasChanges`, `isDirty`, `getDirtyColumns`, `getDirtyColumnsWithValues`, `getRemovedColumns`; "only run if email changed", "diff for an audit log", "what fields are dirty", "compare old vs new value"; typical import `import { Model } from "@warlock.js/cascade"`. Skip: hooking into save — the `subscribe-to-model-events` topic; update idioms — the `define-model` topic; competing libs `mongoose` `isModified` / `modifiedPaths`, `typeorm` change detection.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Track changes
|
|
@@ -123,5 +123,5 @@ Before 4.9.1 a `Date` merged over a column that **already held a `Date`** was si
|
|
|
123
123
|
|
|
124
124
|
## See also
|
|
125
125
|
|
|
126
|
-
-
|
|
127
|
-
-
|
|
126
|
+
- The `subscribe-to-model-events` topic — combining with `saved` / `saving` for cross-cutting behavior
|
|
127
|
+
- The `define-model` topic — the `.set` / `.merge` / `.save` idioms that stage changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: write-migration
|
|
3
|
-
description: 'Write a Cascade migration — the declarative `Migration.create(Model, { columns })` / `Migration.alter(Model, { ... })` factory is the primary form (column helpers `string` / `text` / `uuid` / `integer` imported from cascade, chained with `.notNullable()` / `.unique()` / `.references()`); the `extends Migration` class form is the imperative escape hatch. Run with the `cascade migrate` CLI; pin a source via `public dataSource`. Triggers: `Migration.create`, `Migration.alter`, `string()`, `text()`, `uuid()`, `.references`, `extends Migration`, `cascade migrate`; "write a migration", "create the users table", "add a column", "rollback the last batch"; typical import `import { Migration, text, uuid } from "@warlock.js/cascade"`. Skip: running migrations programmatically —
|
|
3
|
+
description: 'Write a Cascade migration — the declarative `Migration.create(Model, { columns })` / `Migration.alter(Model, { ... })` factory is the primary form (column helpers `string` / `text` / `uuid` / `integer` imported from cascade, chained with `.notNullable()` / `.unique()` / `.references()`); the `extends Migration` class form is the imperative escape hatch. Run with the `cascade migrate` CLI; pin a source via `public dataSource`. Triggers: `Migration.create`, `Migration.alter`, `string()`, `text()`, `uuid()`, `.references`, `extends Migration`, `cascade migrate`; "write a migration", "create the users table", "add a column", "rollback the last batch"; typical import `import { Migration, text, uuid } from "@warlock.js/cascade"`. Skip: running migrations programmatically — the `run-cascade-cli` topic; per-source migrations — the `manage-data-sources` topic; competing tools `knex migrate`, `prisma migrate`, `typeorm migration`.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Write a migration
|
|
@@ -47,7 +47,7 @@ export default Migration.create(User, {
|
|
|
47
47
|
- It only fires for the `"soft"` strategy — `"permanent"` and `"trash"` add nothing. No driver defaults to `"soft"`, so this never fires unless soft delete is opted into.
|
|
48
48
|
- Opt out for one table with `{ softDeletes: false }`; force it on with `{ softDeletes: true }`. A model with `deletedAtColumn = false` is never wired. An already-declared `deletedAt` in the map is not duplicated.
|
|
49
49
|
|
|
50
|
-
See
|
|
50
|
+
See the `configure-delete-strategy` topic for the strategies themselves.
|
|
51
51
|
|
|
52
52
|
## Running migrations
|
|
53
53
|
|
|
@@ -58,7 +58,7 @@ npx cascade migrate:list # which migrations have been executed
|
|
|
58
58
|
npx cascade migrate:export-sql # write .up.sql / .down.sql instead of executing
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
`cascade migrate` discovers migration files via the `-p`/`--path` glob (default `./migrations/**`), runs them in order, and records each in the `_migrations` table / collection. See
|
|
61
|
+
`cascade migrate` discovers migration files via the `-p`/`--path` glob (default `./migrations/**`), runs them in order, and records each in the `_migrations` table / collection. See the `run-cascade-cli` topic for every flag and the programmatic Operations API.
|
|
62
62
|
|
|
63
63
|
## File naming convention
|
|
64
64
|
|
|
@@ -189,7 +189,7 @@ export default Migration.create(AnalyticsEvent, {
|
|
|
189
189
|
});
|
|
190
190
|
```
|
|
191
191
|
|
|
192
|
-
For a class-form migration not bound to a model, set `public readonly dataSource = "analytics"` directly. See
|
|
192
|
+
For a class-form migration not bound to a model, set `public readonly dataSource = "analytics"` directly. See the `manage-data-sources` topic for the registry.
|
|
193
193
|
|
|
194
194
|
## Things NOT to do
|
|
195
195
|
|
|
@@ -201,6 +201,6 @@ For a class-form migration not bound to a model, set `public readonly dataSource
|
|
|
201
201
|
|
|
202
202
|
## See also
|
|
203
203
|
|
|
204
|
-
-
|
|
205
|
-
-
|
|
206
|
-
-
|
|
204
|
+
- The `run-cascade-cli` topic — CLI flags + Operations API for programmatic runs
|
|
205
|
+
- The `manage-data-sources` topic — multi-DB migrations
|
|
206
|
+
- The `configure-delete-strategy` topic — soft / trash / permanent deletes and the `deletedAt` column
|