@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.
- package/CHANGELOG.md +36 -1
- package/cjs/index.cjs +2356 -1039
- package/cjs/index.cjs.map +1 -1
- package/esm/context/database-transaction-context.d.mts +10 -0
- package/esm/context/database-transaction-context.d.mts.map +1 -1
- package/esm/context/database-transaction-context.mjs +17 -0
- package/esm/context/database-transaction-context.mjs.map +1 -1
- package/esm/contracts/query-builder.contract.d.mts +30 -3
- package/esm/contracts/query-builder.contract.d.mts.map +1 -1
- package/esm/data-source/data-source-registry.d.mts +33 -1
- package/esm/data-source/data-source-registry.d.mts.map +1 -1
- package/esm/data-source/data-source-registry.mjs +75 -5
- package/esm/data-source/data-source-registry.mjs.map +1 -1
- package/esm/database-dirty-tracker.d.mts +5 -0
- package/esm/database-dirty-tracker.d.mts.map +1 -1
- package/esm/database-dirty-tracker.mjs +16 -0
- package/esm/database-dirty-tracker.mjs.map +1 -1
- package/esm/drivers/mongodb/mongodb-driver.d.mts +12 -0
- package/esm/drivers/mongodb/mongodb-driver.d.mts.map +1 -1
- package/esm/drivers/mongodb/mongodb-driver.mjs +58 -19
- package/esm/drivers/mongodb/mongodb-driver.mjs.map +1 -1
- package/esm/drivers/mongodb/mongodb-migration-driver.d.mts +1 -1
- package/esm/drivers/mongodb/mongodb-migration-driver.d.mts.map +1 -1
- package/esm/drivers/mongodb/mongodb-migration-driver.mjs +15 -18
- package/esm/drivers/mongodb/mongodb-migration-driver.mjs.map +1 -1
- package/esm/drivers/mongodb/mongodb-query-builder.d.mts +13 -3
- package/esm/drivers/mongodb/mongodb-query-builder.d.mts.map +1 -1
- package/esm/drivers/mongodb/mongodb-query-builder.mjs +72 -19
- package/esm/drivers/mongodb/mongodb-query-builder.mjs.map +1 -1
- package/esm/drivers/mongodb/mongodb-query-parser.d.mts +6 -0
- package/esm/drivers/mongodb/mongodb-query-parser.d.mts.map +1 -1
- package/esm/drivers/mongodb/mongodb-query-parser.mjs +128 -30
- package/esm/drivers/mongodb/mongodb-query-parser.mjs.map +1 -1
- package/esm/drivers/postgres/index.d.mts +2 -3
- package/esm/drivers/postgres/index.mjs +1 -2
- package/esm/drivers/postgres/postgres-blueprint.mjs +4 -4
- package/esm/drivers/postgres/postgres-blueprint.mjs.map +1 -1
- package/esm/drivers/postgres/postgres-dialect.d.mts +5 -1
- package/esm/drivers/postgres/postgres-dialect.d.mts.map +1 -1
- package/esm/drivers/postgres/postgres-dialect.mjs +38 -8
- package/esm/drivers/postgres/postgres-dialect.mjs.map +1 -1
- package/esm/drivers/postgres/postgres-driver.d.mts +52 -29
- package/esm/drivers/postgres/postgres-driver.d.mts.map +1 -1
- package/esm/drivers/postgres/postgres-driver.mjs +196 -58
- package/esm/drivers/postgres/postgres-driver.mjs.map +1 -1
- package/esm/drivers/postgres/postgres-migration-driver.d.mts +2 -1
- package/esm/drivers/postgres/postgres-migration-driver.d.mts.map +1 -1
- package/esm/drivers/postgres/postgres-migration-driver.mjs +8 -6
- package/esm/drivers/postgres/postgres-migration-driver.mjs.map +1 -1
- package/esm/drivers/postgres/postgres-query-builder.d.mts +18 -7
- package/esm/drivers/postgres/postgres-query-builder.d.mts.map +1 -1
- package/esm/drivers/postgres/postgres-query-builder.mjs +146 -31
- package/esm/drivers/postgres/postgres-query-builder.mjs.map +1 -1
- package/esm/drivers/postgres/postgres-query-parser.d.mts +23 -1
- package/esm/drivers/postgres/postgres-query-parser.d.mts.map +1 -1
- package/esm/drivers/postgres/postgres-query-parser.mjs +160 -43
- package/esm/drivers/postgres/postgres-query-parser.mjs.map +1 -1
- package/esm/drivers/postgres/postgres-sql-serializer.mjs +3 -2
- package/esm/drivers/postgres/postgres-sql-serializer.mjs.map +1 -1
- package/esm/drivers/postgres/postgres-type-converters.mjs +57 -0
- package/esm/drivers/postgres/postgres-type-converters.mjs.map +1 -0
- package/esm/events/model-events.d.mts +1 -1
- package/esm/events/model-events.d.mts.map +1 -1
- package/esm/events/model-events.mjs.map +1 -1
- package/esm/index.d.mts +6 -6
- package/esm/index.mjs +5 -5
- package/esm/migration/column-builder.d.mts.map +1 -1
- package/esm/migration/column-builder.mjs +2 -2
- package/esm/migration/column-builder.mjs.map +1 -1
- package/esm/migration/index.d.mts +2 -2
- package/esm/migration/index.mjs +1 -1
- package/esm/migration/migration-order.mjs +5 -2
- package/esm/migration/migration-order.mjs.map +1 -1
- package/esm/migration/migration-runner.d.mts +50 -5
- package/esm/migration/migration-runner.d.mts.map +1 -1
- package/esm/migration/migration-runner.mjs +370 -147
- package/esm/migration/migration-runner.mjs.map +1 -1
- package/esm/migration/migration.d.mts +36 -3
- package/esm/migration/migration.d.mts.map +1 -1
- package/esm/migration/migration.mjs +62 -5
- package/esm/migration/migration.mjs.map +1 -1
- package/esm/model/methods/accessor-methods.mjs +1 -3
- package/esm/model/methods/accessor-methods.mjs.map +1 -1
- package/esm/model/methods/delete-methods.mjs +6 -2
- package/esm/model/methods/delete-methods.mjs.map +1 -1
- package/esm/model/methods/hydration-methods.mjs +16 -2
- package/esm/model/methods/hydration-methods.mjs.map +1 -1
- package/esm/model/methods/instance-event-methods.mjs +2 -1
- package/esm/model/methods/instance-event-methods.mjs.map +1 -1
- package/esm/model/methods/meta-methods.mjs +53 -16
- package/esm/model/methods/meta-methods.mjs.map +1 -1
- package/esm/model/methods/query-methods.mjs +86 -15
- package/esm/model/methods/query-methods.mjs.map +1 -1
- package/esm/model/methods/static-event-methods.mjs +16 -1
- package/esm/model/methods/static-event-methods.mjs.map +1 -1
- package/esm/model/methods/write-methods.d.mts.map +1 -1
- package/esm/model/methods/write-methods.mjs +50 -50
- package/esm/model/methods/write-methods.mjs.map +1 -1
- package/esm/model/model.d.mts +16 -1
- package/esm/model/model.d.mts.map +1 -1
- package/esm/model/model.mjs +39 -5
- package/esm/model/model.mjs.map +1 -1
- package/esm/model/register-model.d.mts +6 -1
- package/esm/model/register-model.d.mts.map +1 -1
- package/esm/model/register-model.mjs +16 -1
- package/esm/model/register-model.mjs.map +1 -1
- package/esm/query-builder/query-builder.d.mts +21 -2
- package/esm/query-builder/query-builder.d.mts.map +1 -1
- package/esm/query-builder/query-builder.mjs +97 -20
- package/esm/query-builder/query-builder.mjs.map +1 -1
- package/esm/relations/key-conventions.mjs +13 -3
- package/esm/relations/key-conventions.mjs.map +1 -1
- package/esm/relations/pivot-operations.mjs +1 -1
- package/esm/relations/relation-loader.d.mts +15 -5
- package/esm/relations/relation-loader.d.mts.map +1 -1
- package/esm/relations/relation-loader.mjs +51 -16
- package/esm/relations/relation-loader.mjs.map +1 -1
- package/esm/remover/database-remover.d.mts.map +1 -1
- package/esm/remover/database-remover.mjs +21 -7
- package/esm/remover/database-remover.mjs.map +1 -1
- package/esm/restorer/database-restorer.d.mts +12 -2
- package/esm/restorer/database-restorer.d.mts.map +1 -1
- package/esm/restorer/database-restorer.mjs +43 -13
- package/esm/restorer/database-restorer.mjs.map +1 -1
- package/esm/sync/model-events.mjs +63 -3
- package/esm/sync/model-events.mjs.map +1 -1
- package/esm/sync/model-sync-operation.d.mts.map +1 -1
- package/esm/sync/model-sync-operation.mjs +3 -2
- package/esm/sync/model-sync-operation.mjs.map +1 -1
- package/esm/sync/sync-manager.d.mts +3 -42
- package/esm/sync/sync-manager.d.mts.map +1 -1
- package/esm/sync/sync-manager.mjs +37 -126
- package/esm/sync/sync-manager.mjs.map +1 -1
- package/esm/transactions/after-commit.d.mts +24 -0
- package/esm/transactions/after-commit.d.mts.map +1 -0
- package/esm/transactions/after-commit.mjs +47 -0
- package/esm/transactions/after-commit.mjs.map +1 -0
- package/esm/utils/connect-to-database.d.mts.map +1 -1
- package/esm/utils/connect-to-database.mjs +4 -4
- package/esm/utils/connect-to-database.mjs.map +1 -1
- package/esm/utils/database-writer.utils.d.mts +1 -1
- package/esm/utils/database-writer.utils.d.mts.map +1 -1
- package/esm/utils/database-writer.utils.mjs +1 -0
- package/esm/utils/database-writer.utils.mjs.map +1 -1
- package/esm/utils/define-model.d.mts.map +1 -1
- package/esm/utils/define-model.mjs +2 -10
- package/esm/utils/define-model.mjs.map +1 -1
- package/esm/utils/is-valid-date-value.mjs +3 -2
- package/esm/utils/is-valid-date-value.mjs.map +1 -1
- package/esm/utils/once-connected.d.mts.map +1 -1
- package/esm/utils/once-connected.mjs +12 -2
- package/esm/utils/once-connected.mjs.map +1 -1
- package/esm/validation/mutators/embed-mutator.mjs +19 -11
- package/esm/validation/mutators/embed-mutator.mjs.map +1 -1
- package/esm/validation/plugins/embed-validator-plugin.d.mts +2 -2
- package/esm/validation/plugins/embed-validator-plugin.d.mts.map +1 -1
- package/esm/validation/plugins/embed-validator-plugin.mjs +8 -2
- package/esm/validation/plugins/embed-validator-plugin.mjs.map +1 -1
- package/esm/validation/rules/database-model-rule.mjs +6 -5
- package/esm/validation/rules/database-model-rule.mjs.map +1 -1
- package/esm/validation/rules/exists-rule.mjs +6 -4
- package/esm/validation/rules/exists-rule.mjs.map +1 -1
- package/esm/validation/rules/unique-rule.mjs +5 -2
- package/esm/validation/rules/unique-rule.mjs.map +1 -1
- package/esm/validation/validators/embed-validator.d.mts.map +1 -1
- package/esm/validation/validators/embed-validator.mjs +1 -1
- package/esm/validation/validators/embed-validator.mjs.map +1 -1
- package/esm/writer/database-writer.d.mts +27 -0
- package/esm/writer/database-writer.d.mts.map +1 -1
- package/esm/writer/database-writer.mjs +84 -17
- package/esm/writer/database-writer.mjs.map +1 -1
- package/llms-full.txt +2783 -2766
- package/llms.txt +15 -15
- package/package.json +6 -6
- package/skills/aggregate-data/SKILL.md +165 -165
- package/skills/alter-migration/SKILL.md +531 -531
- package/skills/configure-delete-strategy/SKILL.md +130 -130
- package/skills/define-model/SKILL.md +205 -205
- package/skills/define-relations/SKILL.md +171 -171
- package/skills/index.md +23 -0
- package/skills/manage-data-sources/SKILL.md +140 -140
- package/skills/manage-transactions/SKILL.md +208 -191
- package/skills/paginate-results/SKILL.md +122 -122
- package/skills/perform-atomic-ops/SKILL.md +156 -156
- package/skills/query-data/SKILL.md +213 -213
- package/skills/run-cascade-cli/SKILL.md +125 -125
- package/skills/search-by-vector/SKILL.md +127 -127
- package/skills/subscribe-to-model-events/SKILL.md +148 -148
- package/skills/track-changes/SKILL.md +127 -127
- package/skills/write-migration/SKILL.md +206 -206
- package/esm/drivers/postgres/postgres-sync-adapter.d.mts +0 -79
- package/esm/drivers/postgres/postgres-sync-adapter.d.mts.map +0 -1
- package/esm/drivers/postgres/postgres-sync-adapter.mjs +0 -162
- package/esm/drivers/postgres/postgres-sync-adapter.mjs.map +0 -1
|
@@ -12,9 +12,7 @@ function getFieldValue(model, field, defaultValue) {
|
|
|
12
12
|
function setFieldValue(model, field, value) {
|
|
13
13
|
const path = String(field);
|
|
14
14
|
set(model.data, path, value);
|
|
15
|
-
|
|
16
|
-
set(partial, path, value);
|
|
17
|
-
model.dirtyTracker.mergeChanges(partial);
|
|
15
|
+
model.dirtyTracker.setAtPath(path, value);
|
|
18
16
|
return model;
|
|
19
17
|
}
|
|
20
18
|
function hasField(model, field) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"accessor-methods.mjs","names":[],"sources":["../../../../../../../../cascade/src/model/methods/accessor-methods.ts"],"sourcesContent":["import { get, merge, only, set, unset } from \"@mongez/reinforcements\";\nimport type { Model } from \"../model\";\n\n/**\n * Sentinel symbol to distinguish a genuinely missing field from a field\n * whose value is `undefined`. Encapsulated here — callers use `hasField()`.\n */\nconst MISSING_VALUE = Symbol(\"missing\");\n\nexport function getFieldValue(model: Model, field: string, defaultValue?: unknown): any {\n return get(model.data, field, defaultValue);\n}\n\nexport function setFieldValue(model: Model, field: string, value: unknown): Model {\n const path = String(field);\n set(model.data, path, value);\n\n
|
|
1
|
+
{"version":3,"file":"accessor-methods.mjs","names":[],"sources":["../../../../../../../../cascade/src/model/methods/accessor-methods.ts"],"sourcesContent":["import { get, merge, only, set, unset } from \"@mongez/reinforcements\";\nimport type { Model } from \"../model\";\n\n/**\n * Sentinel symbol to distinguish a genuinely missing field from a field\n * whose value is `undefined`. Encapsulated here — callers use `hasField()`.\n */\nconst MISSING_VALUE = Symbol(\"missing\");\n\nexport function getFieldValue(model: Model, field: string, defaultValue?: unknown): any {\n return get(model.data, field, defaultValue);\n}\n\nexport function setFieldValue(model: Model, field: string, value: unknown): Model {\n const path = String(field);\n set(model.data, path, value);\n\n model.dirtyTracker.setAtPath(path, value);\n\n return model;\n}\n\nexport function hasField(model: Model, field: string): boolean {\n return get(model.data, field, MISSING_VALUE as any) !== MISSING_VALUE;\n}\n\nexport function incrementField(model: Model, field: string, amount?: number): Model {\n const value = getFieldValue(model, field, 0) as number;\n const incrementedValue = value + (amount ?? 1);\n return setFieldValue(model, field, incrementedValue);\n}\n\nexport function decrementField(model: Model, field: string, amount?: number): Model {\n const value = getFieldValue(model, field, 0) as number;\n const decrementedValue = value - (amount ?? 1);\n return setFieldValue(model, field, decrementedValue);\n}\n\nexport function unsetFields(model: Model, ...fields: string[]): Model {\n model.data = unset(model.data, fields);\n model.dirtyTracker.unset(fields);\n\n return model;\n}\n\n/**\n * Identity columns that a mass-assignment payload may never carry into an\n * already-persisted model. `merge()` is the method request bodies reach\n * (`model.merge(req.body)` / `save({ merge })`), and the primary key is what\n * the UPDATE filter is built from — letting it through means a payload of\n * `{ id: \"<victim-id>\" }` rewrites which document the save targets. Both the\n * configured primary key and the two framework identity columns are dropped,\n * because `id` and `_id` are managed by the writer/driver either way.\n *\n * A NEW model is untouched: creating with an explicit id is a legitimate flow,\n * and the writer itself merges the driver-returned document (`_id`, defaults)\n * back onto the instance before it is marked persisted.\n */\nfunction stripIdentityColumns(\n model: Model,\n values: Record<string, unknown>,\n): Record<string, unknown> {\n if (model.isNew) return values;\n\n const identityColumns = new Set([model.getPrimaryKey(), \"id\", \"_id\"]);\n const blockedKeys = Object.keys(values).filter(key => identityColumns.has(key));\n\n if (blockedKeys.length === 0) return values;\n\n // Copy — the caller's object (often the request body itself) is not ours to mutate.\n const safeValues = { ...values };\n for (const key of blockedKeys) {\n delete safeValues[key];\n }\n\n return safeValues;\n}\n\nexport function mergeFields(model: Model, values: Record<string, unknown>): Model {\n return mergeDriverFields(model, stripIdentityColumns(model, values));\n}\n\n/**\n * Merge a document the DRIVER produced (generated `_id`, `RETURNING *`, DB\n * defaults) back onto the model, identity columns included.\n *\n * Framework-internal counterpart of {@link mergeFields}: the write pipeline is\n * the one caller allowed to set identity columns on an already-persisted\n * instance, because the values come from the database rather than from a\n * request. Never route caller-supplied data through this.\n */\nexport function mergeDriverFields(model: Model, values: Record<string, unknown>): Model {\n model.data = merge(model.data, values) as any;\n model.dirtyTracker.mergeChanges(values);\n return model;\n}\n\nexport function getOnlyFields(model: Model, fields: string[]): Record<string, unknown> {\n return only(model.data, fields);\n}\n\nexport function getStringField(model: Model, key: string, defaultValue?: string): string | undefined {\n return getFieldValue(model, key, defaultValue) as string | undefined;\n}\n\nexport function getNumberField(model: Model, key: string, defaultValue?: number): number | undefined {\n return getFieldValue(model, key, defaultValue) as number | undefined;\n}\n\nexport function getBooleanField(model: Model, key: string, defaultValue?: boolean): boolean | undefined {\n return getFieldValue(model, key, defaultValue) as boolean | undefined;\n}\n"],"mappings":";;;;;;;AAOA,MAAM,gBAAgB,OAAO,SAAS;AAEtC,SAAgB,cAAc,OAAc,OAAe,cAA6B;CACtF,OAAO,IAAI,MAAM,MAAM,OAAO,YAAY;AAC5C;AAEA,SAAgB,cAAc,OAAc,OAAe,OAAuB;CAChF,MAAM,OAAO,OAAO,KAAK;CACzB,IAAI,MAAM,MAAM,MAAM,KAAK;CAE3B,MAAM,aAAa,UAAU,MAAM,KAAK;CAExC,OAAO;AACT;AAEA,SAAgB,SAAS,OAAc,OAAwB;CAC7D,OAAO,IAAI,MAAM,MAAM,OAAO,aAAoB,MAAM;AAC1D;AAEA,SAAgB,eAAe,OAAc,OAAe,QAAwB;CAGlF,OAAO,cAAc,OAAO,OAFd,cAAc,OAAO,OAAO,CACb,KAAK,UAAU,EACO;AACrD;AAEA,SAAgB,eAAe,OAAc,OAAe,QAAwB;CAGlF,OAAO,cAAc,OAAO,OAFd,cAAc,OAAO,OAAO,CACb,KAAK,UAAU,EACO;AACrD;AAEA,SAAgB,YAAY,OAAc,GAAG,QAAyB;CACpE,MAAM,OAAO,MAAM,MAAM,MAAM,MAAM;CACrC,MAAM,aAAa,MAAM,MAAM;CAE/B,OAAO;AACT;;;;;;;;;;;;;;AAeA,SAAS,qBACP,OACA,QACyB;CACzB,IAAI,MAAM,OAAO,OAAO;CAExB,MAAM,kBAAkB,IAAI,IAAI;EAAC,MAAM,cAAc;EAAG;EAAM;CAAK,CAAC;CACpE,MAAM,cAAc,OAAO,KAAK,MAAM,CAAC,CAAC,QAAO,QAAO,gBAAgB,IAAI,GAAG,CAAC;CAE9E,IAAI,YAAY,WAAW,GAAG,OAAO;CAGrC,MAAM,aAAa,EAAE,GAAG,OAAO;CAC/B,KAAK,MAAM,OAAO,aAChB,OAAO,WAAW;CAGpB,OAAO;AACT;AAEA,SAAgB,YAAY,OAAc,QAAwC;CAChF,OAAO,kBAAkB,OAAO,qBAAqB,OAAO,MAAM,CAAC;AACrE;;;;;;;;;;AAWA,SAAgB,kBAAkB,OAAc,QAAwC;CACtF,MAAM,OAAO,MAAM,MAAM,MAAM,MAAM;CACrC,MAAM,aAAa,aAAa,MAAM;CACtC,OAAO;AACT;AAEA,SAAgB,cAAc,OAAc,QAA2C;CACrF,OAAO,KAAK,MAAM,MAAM,MAAM;AAChC;AAEA,SAAgB,eAAe,OAAc,KAAa,cAA2C;CACnG,OAAO,cAAc,OAAO,KAAK,YAAY;AAC/C;AAEA,SAAgB,eAAe,OAAc,KAAa,cAA2C;CACnG,OAAO,cAAc,OAAO,KAAK,YAAY;AAC/C;AAEA,SAAgB,gBAAgB,OAAc,KAAa,cAA6C;CACtG,OAAO,cAAc,OAAO,KAAK,YAAY;AAC/C"}
|
|
@@ -6,10 +6,14 @@ async function destroyModel(model, options) {
|
|
|
6
6
|
return new DatabaseRemover(model).destroy(options);
|
|
7
7
|
}
|
|
8
8
|
async function deleteRecords(ModelClass, filter) {
|
|
9
|
-
|
|
9
|
+
const sanitized = filter ? sanitizeFilter(filter) : void 0;
|
|
10
|
+
if (!sanitized || Object.keys(sanitized).length === 0) throw new Error(`${ModelClass.name}.delete() requires a filter: pass an explicit filter, or use ${ModelClass.name}.deleteAll() / ${ModelClass.name}.query().delete() to delete every row.`);
|
|
11
|
+
return ModelClass.query().where(sanitized).delete();
|
|
10
12
|
}
|
|
11
13
|
async function deleteOneRecord(ModelClass, filter) {
|
|
12
|
-
|
|
14
|
+
const query = ModelClass.query();
|
|
15
|
+
if (filter && Object.keys(filter).length > 0) query.where(sanitizeFilter(filter));
|
|
16
|
+
return query.deleteOne();
|
|
13
17
|
}
|
|
14
18
|
|
|
15
19
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delete-methods.mjs","names":[],"sources":["../../../../../../../../cascade/src/model/methods/delete-methods.ts"],"sourcesContent":["import { DatabaseRemover } from \"../../remover/database-remover\";\r\nimport type { DeleteStrategy } from \"../../types\";\r\nimport type { RemoverResult } from \"../../contracts\";\r\nimport { sanitizeFilter } from \"../../utils/sanitize-filter\";\r\nimport type { ChildModel, Model } from \"../model\";\r\n\r\nexport async function destroyModel(\r\n model: Model,\r\n options?: { strategy?: DeleteStrategy; skipEvents?: boolean },\r\n): Promise<RemoverResult> {\r\n const remover = new DatabaseRemover(model);\r\n return remover.destroy(options);\r\n}\r\n\r\nexport async function deleteRecords(\r\n ModelClass: ChildModel<any>,\r\n filter?: Record<string, unknown>,\r\n): Promise<number> {\r\n
|
|
1
|
+
{"version":3,"file":"delete-methods.mjs","names":[],"sources":["../../../../../../../../cascade/src/model/methods/delete-methods.ts"],"sourcesContent":["import { DatabaseRemover } from \"../../remover/database-remover\";\r\nimport type { DeleteStrategy } from \"../../types\";\r\nimport type { RemoverResult } from \"../../contracts\";\r\nimport { sanitizeFilter } from \"../../utils/sanitize-filter\";\r\nimport type { ChildModel, Model } from \"../model\";\r\n\r\nexport async function destroyModel(\r\n model: Model,\r\n options?: { strategy?: DeleteStrategy; skipEvents?: boolean },\r\n): Promise<RemoverResult> {\r\n const remover = new DatabaseRemover(model);\r\n return remover.destroy(options);\r\n}\r\n\r\nexport async function deleteRecords(\r\n ModelClass: ChildModel<any>,\r\n filter?: Record<string, unknown>,\r\n): Promise<number> {\r\n const sanitized = filter ? sanitizeFilter(filter) : undefined;\r\n\r\n if (!sanitized || Object.keys(sanitized).length === 0) {\r\n throw new Error(\r\n `${ModelClass.name}.delete() requires a filter: pass an explicit filter, or use ${ModelClass.name}.deleteAll() / ${ModelClass.name}.query().delete() to delete every row.`,\r\n );\r\n }\r\n\r\n // Through the query builder so global scopes (tenant, soft delete) apply.\r\n return ModelClass.query().where(sanitized).delete();\r\n}\r\n\r\n/** Delete every row visible to the model's global scopes. Explicit on purpose. */\r\nexport async function deleteAllRecords(ModelClass: ChildModel<any>): Promise<number> {\r\n return ModelClass.query().delete();\r\n}\r\n\r\nexport async function deleteOneRecord(\r\n ModelClass: ChildModel<any>,\r\n filter?: Record<string, unknown>,\r\n): Promise<number> {\r\n const query = ModelClass.query();\r\n\r\n if (filter && Object.keys(filter).length > 0) {\r\n query.where(sanitizeFilter(filter));\r\n }\r\n\r\n return query.deleteOne();\r\n}\r\n"],"mappings":";;;;AAMA,eAAsB,aACpB,OACA,SACwB;CAExB,OAAO,IADa,gBAAgB,KACvB,CAAC,CAAC,QAAQ,OAAO;AAChC;AAEA,eAAsB,cACpB,YACA,QACiB;CACjB,MAAM,YAAY,SAAS,eAAe,MAAM,IAAI;CAEpD,IAAI,CAAC,aAAa,OAAO,KAAK,SAAS,CAAC,CAAC,WAAW,GAClD,MAAM,IAAI,MACR,GAAG,WAAW,KAAK,+DAA+D,WAAW,KAAK,iBAAiB,WAAW,KAAK,uCACrI;CAIF,OAAO,WAAW,MAAM,CAAC,CAAC,MAAM,SAAS,CAAC,CAAC,OAAO;AACpD;AAOA,eAAsB,gBACpB,YACA,QACiB;CACjB,MAAM,QAAQ,WAAW,MAAM;CAE/B,IAAI,UAAU,OAAO,KAAK,MAAM,CAAC,CAAC,SAAS,GACzC,MAAM,MAAM,eAAe,MAAM,CAAC;CAGpC,OAAO,MAAM,UAAU;AACzB"}
|
|
@@ -1,8 +1,22 @@
|
|
|
1
1
|
import { RelationHydrator } from "../../relations/relation-hydrator.mjs";
|
|
2
|
+
import { clone } from "@mongez/reinforcements";
|
|
3
|
+
import { DateValidator } from "@warlock.js/seal";
|
|
2
4
|
|
|
3
5
|
//#region ../cascade/src/model/methods/hydration-methods.ts
|
|
6
|
+
/**
|
|
7
|
+
* Drivers re-inflate any ISO-looking string into a Date. Only fields the model
|
|
8
|
+
* schema declares as dates may be converted, so put every other string back.
|
|
9
|
+
*/
|
|
10
|
+
function restoreUndeclaredDateStrings(ModelClass, original, data) {
|
|
11
|
+
const shape = ModelClass.schema?.schema ?? {};
|
|
12
|
+
for (const [key, value] of Object.entries(original)) if (data[key] instanceof Date && !(shape[key] instanceof DateValidator)) data[key] = value;
|
|
13
|
+
}
|
|
4
14
|
function hydrateModel(ModelClass, data) {
|
|
5
|
-
const
|
|
15
|
+
const strings = {};
|
|
16
|
+
for (const [key, value] of Object.entries(data)) if (typeof value === "string") strings[key] = value;
|
|
17
|
+
const deserialized = ModelClass.getDriver().deserialize(data);
|
|
18
|
+
restoreUndeclaredDateStrings(ModelClass, strings, deserialized);
|
|
19
|
+
const model = new ModelClass(deserialized);
|
|
6
20
|
model.isNew = false;
|
|
7
21
|
return model;
|
|
8
22
|
}
|
|
@@ -26,7 +40,7 @@ function serializeModel(model) {
|
|
|
26
40
|
return model.constructor.getDataSource().driver.serialize(model.data);
|
|
27
41
|
}
|
|
28
42
|
function cloneModel(model) {
|
|
29
|
-
const clonedData =
|
|
43
|
+
const clonedData = clone(model.data);
|
|
30
44
|
const clonedModel = new (model.self())(clonedData);
|
|
31
45
|
clonedModel.isNew = model.isNew;
|
|
32
46
|
deepFreezeObject(clonedModel.data);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hydration-methods.mjs","names":[],"sources":["../../../../../../../../cascade/src/model/methods/hydration-methods.ts"],"sourcesContent":["import { RelationHydrator, type ModelSnapshot, type SerializedRelation } from \"../../relations/relation-hydrator\";\nimport type { ChildModel, Model } from \"../model\";\n\nexport function hydrateModel<TModel extends Model = Model>(\n ModelClass: ChildModel<TModel>,\n data: Record<string, unknown>,\n): TModel {\n const
|
|
1
|
+
{"version":3,"file":"hydration-methods.mjs","names":[],"sources":["../../../../../../../../cascade/src/model/methods/hydration-methods.ts"],"sourcesContent":["import { clone } from \"@mongez/reinforcements\";\nimport { DateValidator } from \"@warlock.js/seal\";\nimport { RelationHydrator, type ModelSnapshot, type SerializedRelation } from \"../../relations/relation-hydrator\";\nimport type { ChildModel, Model } from \"../model\";\n\n/**\n * Drivers re-inflate any ISO-looking string into a Date. Only fields the model\n * schema declares as dates may be converted, so put every other string back.\n */\nfunction restoreUndeclaredDateStrings<TModel extends Model>(\n ModelClass: ChildModel<TModel>,\n original: Record<string, string>,\n data: Record<string, unknown>,\n): void {\n const shape = ModelClass.schema?.schema ?? {};\n\n for (const [key, value] of Object.entries(original)) {\n if (data[key] instanceof Date && !(shape[key] instanceof DateValidator)) {\n data[key] = value;\n }\n }\n}\n\nexport function hydrateModel<TModel extends Model = Model>(\n ModelClass: ChildModel<TModel>,\n data: Record<string, unknown>,\n): TModel {\n const strings: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(data)) {\n if (typeof value === \"string\") strings[key] = value;\n }\n\n const deserialized = ModelClass.getDriver().deserialize(data);\n restoreUndeclaredDateStrings(ModelClass, strings, deserialized);\n\n const model = new ModelClass(deserialized);\n model.isNew = false;\n return model;\n}\n\nexport function modelFromSnapshot<TModel extends Model>(\n ModelClass: ChildModel<TModel>,\n snapshot: ModelSnapshot,\n): TModel {\n const model = ModelClass.hydrate(snapshot.data) as TModel;\n RelationHydrator.hydrate(model, ModelClass.relations, snapshot.relations);\n return model;\n}\n\nexport function modelToSnapshot(model: Model): ModelSnapshot {\n const driver = (model.constructor as typeof Model).getDataSource().driver;\n const relations: Record<string, SerializedRelation> = {};\n\n for (const [name, related] of model.loadedRelations) {\n if (related === null) {\n relations[name] = null;\n } else if (Array.isArray(related)) {\n relations[name] = related.map((m) => (m instanceof Object && typeof m.toSnapshot === \"function\" ? m.toSnapshot() : m));\n } else if (related instanceof Object && typeof related.toSnapshot === \"function\") {\n relations[name] = related.toSnapshot();\n }\n }\n\n return {\n data: driver.serialize({ ...model.data }) as Record<string, unknown>,\n relations,\n };\n}\n\nexport function serializeModel(model: Model) {\n return (model.constructor as typeof Model).getDataSource().driver.serialize(model.data);\n}\n\nexport function cloneModel<TModel extends Model>(model: TModel): TModel {\n const clonedData = clone(model.data);\n const ModelClass = model.self();\n const clonedModel = new ModelClass(clonedData) as TModel;\n\n clonedModel.isNew = model.isNew;\n deepFreezeObject(clonedModel.data);\n clonedModel.dirtyTracker.reset();\n\n return clonedModel;\n}\n\nexport function deepFreezeObject<T>(obj: T): T {\n Object.freeze(obj);\n\n Object.getOwnPropertyNames(obj).forEach((prop) => {\n const value = (obj as any)[prop];\n if (\n value !== null &&\n (typeof value === \"object\" || typeof value === \"function\") &&\n !Object.isFrozen(value)\n ) {\n deepFreezeObject(value);\n }\n });\n\n return obj;\n}\n\nexport function replaceModelData<TModel extends Model>(\n model: TModel,\n data: Record<string, unknown>,\n): void {\n model.data = data as any;\n model.dirtyTracker.replaceCurrentData(data);\n}\n"],"mappings":";;;;;;;;;AASA,SAAS,6BACP,YACA,UACA,MACM;CACN,MAAM,QAAQ,WAAW,QAAQ,UAAU,CAAC;CAE5C,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,GAChD,IAAI,KAAK,gBAAgB,QAAQ,EAAE,MAAM,gBAAgB,gBACvD,KAAK,OAAO;AAGlB;AAEA,SAAgB,aACd,YACA,MACQ;CACR,MAAM,UAAkC,CAAC;CAEzC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,IAAI,GAC5C,IAAI,OAAO,UAAU,UAAU,QAAQ,OAAO;CAGhD,MAAM,eAAe,WAAW,UAAU,CAAC,CAAC,YAAY,IAAI;CAC5D,6BAA6B,YAAY,SAAS,YAAY;CAE9D,MAAM,QAAQ,IAAI,WAAW,YAAY;CACzC,MAAM,QAAQ;CACd,OAAO;AACT;AAEA,SAAgB,kBACd,YACA,UACQ;CACR,MAAM,QAAQ,WAAW,QAAQ,SAAS,IAAI;CAC9C,iBAAiB,QAAQ,OAAO,WAAW,WAAW,SAAS,SAAS;CACxE,OAAO;AACT;AAEA,SAAgB,gBAAgB,OAA6B;CAC3D,MAAM,SAAU,MAAM,YAA6B,cAAc,CAAC,CAAC;CACnE,MAAM,YAAgD,CAAC;CAEvD,KAAK,MAAM,CAAC,MAAM,YAAY,MAAM,iBAClC,IAAI,YAAY,MACd,UAAU,QAAQ;MACb,IAAI,MAAM,QAAQ,OAAO,GAC9B,UAAU,QAAQ,QAAQ,KAAK,MAAO,aAAa,UAAU,OAAO,EAAE,eAAe,aAAa,EAAE,WAAW,IAAI,CAAE;MAChH,IAAI,mBAAmB,UAAU,OAAO,QAAQ,eAAe,YACpE,UAAU,QAAQ,QAAQ,WAAW;CAIzC,OAAO;EACL,MAAM,OAAO,UAAU,EAAE,GAAG,MAAM,KAAK,CAAC;EACxC;CACF;AACF;AAEA,SAAgB,eAAe,OAAc;CAC3C,OAAQ,MAAM,YAA6B,cAAc,CAAC,CAAC,OAAO,UAAU,MAAM,IAAI;AACxF;AAEA,SAAgB,WAAiC,OAAuB;CACtE,MAAM,aAAa,MAAM,MAAM,IAAI;CAEnC,MAAM,cAAc,KADD,MAAM,KACQ,GAAE,UAAU;CAE7C,YAAY,QAAQ,MAAM;CAC1B,iBAAiB,YAAY,IAAI;CACjC,YAAY,aAAa,MAAM;CAE/B,OAAO;AACT;AAEA,SAAgB,iBAAoB,KAAW;CAC7C,OAAO,OAAO,GAAG;CAEjB,OAAO,oBAAoB,GAAG,CAAC,CAAC,SAAS,SAAS;EAChD,MAAM,QAAS,IAAY;EAC3B,IACE,UAAU,SACT,OAAO,UAAU,YAAY,OAAO,UAAU,eAC/C,CAAC,OAAO,SAAS,KAAK,GAEtB,iBAAiB,KAAK;CAE1B,CAAC;CAED,OAAO;AACT;AAEA,SAAgB,iBACd,OACA,MACM;CACN,MAAM,OAAO;CACb,MAAM,aAAa,mBAAmB,IAAI;AAC5C"}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { globalModelEvents } from "../../events/model-events.mjs";
|
|
2
|
+
import { emitStaticEvent } from "./static-event-methods.mjs";
|
|
2
3
|
|
|
3
4
|
//#region ../cascade/src/model/methods/instance-event-methods.ts
|
|
4
5
|
async function emitModelEvent(model, event, context) {
|
|
5
6
|
const ctor = model.constructor;
|
|
6
7
|
await model.events.emit(event, model, context);
|
|
7
|
-
await ctor
|
|
8
|
+
await emitStaticEvent(ctor, event, model, context);
|
|
8
9
|
await globalModelEvents.emit(event, model, context);
|
|
9
10
|
}
|
|
10
11
|
function onModelEvent(model, event, listener) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instance-event-methods.mjs","names":[],"sources":["../../../../../../../../cascade/src/model/methods/instance-event-methods.ts"],"sourcesContent":["import {\n globalModelEvents,\n type ModelEventListener,\n type ModelEventName,\n} from \"../../events/model-events\";\nimport type { Model } from \"../model\";\n\nexport async function emitModelEvent<TContext = unknown>(\n model: Model,\n event: ModelEventName,\n context?: TContext,\n): Promise<void> {\n const ctor = model.constructor as any;\n await model.events.emit(event, model, context as TContext);\n await ctor
|
|
1
|
+
{"version":3,"file":"instance-event-methods.mjs","names":[],"sources":["../../../../../../../../cascade/src/model/methods/instance-event-methods.ts"],"sourcesContent":["import {\n globalModelEvents,\n type ModelEventListener,\n type ModelEventName,\n} from \"../../events/model-events\";\nimport type { Model } from \"../model\";\nimport { emitStaticEvent } from \"./static-event-methods\";\n\nexport async function emitModelEvent<TContext = unknown>(\n model: Model,\n event: ModelEventName,\n context?: TContext,\n): Promise<void> {\n const ctor = model.constructor as any;\n await model.events.emit(event, model, context as TContext);\n await emitStaticEvent(ctor, event, model, context as TContext);\n await globalModelEvents.emit(event, model, context as TContext);\n}\n\nexport function onModelEvent<TContext = unknown>(\n model: Model,\n event: ModelEventName,\n listener: ModelEventListener<any, TContext>,\n): () => void {\n return model.events.on(event, listener);\n}\n\nexport function onceModelEvent<TContext = unknown>(\n model: Model,\n event: ModelEventName,\n listener: ModelEventListener<any, TContext>,\n): () => void {\n return model.events.once(event, listener);\n}\n\nexport function offModelEvent<TContext = unknown>(\n model: Model,\n event: ModelEventName,\n listener: ModelEventListener<any, TContext>,\n): void {\n model.events.off(event, listener);\n}\n"],"mappings":";;;;AAQA,eAAsB,eACpB,OACA,OACA,SACe;CACf,MAAM,OAAO,MAAM;CACnB,MAAM,MAAM,OAAO,KAAK,OAAO,OAAO,OAAmB;CACzD,MAAM,gBAAgB,MAAM,OAAO,OAAO,OAAmB;CAC7D,MAAM,kBAAkB,KAAK,OAAO,OAAO,OAAmB;AAChE;AAEA,SAAgB,aACd,OACA,OACA,UACY;CACZ,OAAO,MAAM,OAAO,GAAG,OAAO,QAAQ;AACxC;AAEA,SAAgB,eACd,OACA,OACA,UACY;CACZ,OAAO,MAAM,OAAO,KAAK,OAAO,QAAQ;AAC1C;AAEA,SAAgB,cACd,OACA,OACA,UACM;CACN,MAAM,OAAO,IAAI,OAAO,QAAQ;AAClC"}
|
|
@@ -1,34 +1,71 @@
|
|
|
1
1
|
import { DatabaseWriter } from "../../writer/database-writer.mjs";
|
|
2
|
+
import { get, set } from "@mongez/reinforcements";
|
|
2
3
|
|
|
3
4
|
//#region ../cascade/src/model/methods/meta-methods.ts
|
|
5
|
+
/**
|
|
6
|
+
* Whether a subclass (or an intermediate base below `Model`) set the static
|
|
7
|
+
* itself. The base `Model` initialises several statics (strictMode,
|
|
8
|
+
* autoGenerateId, incrementIdBy, deletedAtColumn), so comparing to
|
|
9
|
+
* `undefined` would never let data-source defaults apply.
|
|
10
|
+
*/
|
|
11
|
+
function isExplicitStatic(ModelClass, key) {
|
|
12
|
+
let current = ModelClass;
|
|
13
|
+
while (current && Object.getPrototypeOf(current) !== Function.prototype) {
|
|
14
|
+
if (Object.prototype.hasOwnProperty.call(current, key) && current[key] !== void 0) return true;
|
|
15
|
+
current = Object.getPrototypeOf(current);
|
|
16
|
+
}
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
4
19
|
function applyDefaultsToModel(ModelClass, defaults) {
|
|
5
|
-
if (defaults.autoGenerateId !== void 0 && ModelClass
|
|
6
|
-
if (defaults.initialId !== void 0 && ModelClass
|
|
7
|
-
if (defaults.randomInitialId !== void 0 && ModelClass
|
|
8
|
-
if (defaults.incrementIdBy !== void 0 && ModelClass
|
|
9
|
-
if (defaults.randomIncrement !== void 0 && ModelClass
|
|
10
|
-
if (defaults.createdAtColumn !== void 0 && ModelClass
|
|
11
|
-
if (defaults.updatedAtColumn !== void 0 && ModelClass
|
|
12
|
-
if (defaults.deleteStrategy !== void 0 && ModelClass
|
|
13
|
-
if (defaults.deletedAtColumn !== void 0 && ModelClass
|
|
14
|
-
if (defaults.trashTable !== void 0 && ModelClass
|
|
20
|
+
if (defaults.autoGenerateId !== void 0 && !isExplicitStatic(ModelClass, "autoGenerateId")) ModelClass.autoGenerateId = defaults.autoGenerateId;
|
|
21
|
+
if (defaults.initialId !== void 0 && !isExplicitStatic(ModelClass, "initialId")) ModelClass.initialId = defaults.initialId;
|
|
22
|
+
if (defaults.randomInitialId !== void 0 && !isExplicitStatic(ModelClass, "randomInitialId")) ModelClass.randomInitialId = defaults.randomInitialId;
|
|
23
|
+
if (defaults.incrementIdBy !== void 0 && !isExplicitStatic(ModelClass, "incrementIdBy")) ModelClass.incrementIdBy = defaults.incrementIdBy;
|
|
24
|
+
if (defaults.randomIncrement !== void 0 && !isExplicitStatic(ModelClass, "randomIncrement")) ModelClass.randomIncrement = defaults.randomIncrement;
|
|
25
|
+
if (defaults.createdAtColumn !== void 0 && !isExplicitStatic(ModelClass, "createdAtColumn")) ModelClass.createdAtColumn = defaults.createdAtColumn;
|
|
26
|
+
if (defaults.updatedAtColumn !== void 0 && !isExplicitStatic(ModelClass, "updatedAtColumn")) ModelClass.updatedAtColumn = defaults.updatedAtColumn;
|
|
27
|
+
if (defaults.deleteStrategy !== void 0 && !isExplicitStatic(ModelClass, "deleteStrategy")) ModelClass.deleteStrategy = defaults.deleteStrategy;
|
|
28
|
+
if (defaults.deletedAtColumn !== void 0 && !isExplicitStatic(ModelClass, "deletedAtColumn")) ModelClass.deletedAtColumn = defaults.deletedAtColumn;
|
|
29
|
+
if (defaults.trashTable !== void 0 && !isExplicitStatic(ModelClass, "trashTable")) if (typeof defaults.trashTable === "function") ModelClass.trashTable = defaults.trashTable(ModelClass.table);
|
|
15
30
|
else ModelClass.trashTable = defaults.trashTable;
|
|
16
|
-
if (defaults.strictMode !== void 0 && ModelClass
|
|
31
|
+
if (defaults.strictMode !== void 0 && !isExplicitStatic(ModelClass, "strictMode")) ModelClass.strictMode = defaults.strictMode;
|
|
17
32
|
}
|
|
18
33
|
async function generateModelNextId(model) {
|
|
19
34
|
await new DatabaseWriter(model).generateNextId();
|
|
20
35
|
return model.id;
|
|
21
36
|
}
|
|
22
37
|
async function performAtomicUpdate(model, operations) {
|
|
23
|
-
return model.self().atomic({
|
|
38
|
+
return model.self().atomic({ [model.self().primaryKey]: model.trustedPrimaryKey }, operations);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Apply an atomic delta to the local value without marking the field dirty.
|
|
42
|
+
* The database already holds the change, so a later `save()` must not send a
|
|
43
|
+
* stale `$set` for it. Other pending changes stay dirty.
|
|
44
|
+
*/
|
|
45
|
+
function applyLocalDelta(model, field, delta) {
|
|
46
|
+
const tracker = model.dirtyTracker;
|
|
47
|
+
const fieldWasDirty = tracker.isDirty(field);
|
|
48
|
+
const value = get(model.data, field, 0) + delta;
|
|
49
|
+
set(model.data, field, value);
|
|
50
|
+
if (fieldWasDirty || tracker.getRemovedColumns().length > 0) {
|
|
51
|
+
tracker.setAtPath(field, value);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const pending = tracker.getDirtyColumnsWithValues();
|
|
55
|
+
const baseline = structuredClone(model.data);
|
|
56
|
+
for (const [column, values] of Object.entries(pending)) set(baseline, column, values.oldValue);
|
|
57
|
+
tracker.reset(baseline);
|
|
58
|
+
for (const [column, values] of Object.entries(pending)) tracker.setAtPath(column, values.newValue);
|
|
24
59
|
}
|
|
25
60
|
async function performAtomicIncrement(model, field, amount = 1) {
|
|
26
|
-
model
|
|
27
|
-
|
|
61
|
+
const result = await performAtomicUpdate(model, { $inc: { [field]: amount } });
|
|
62
|
+
applyLocalDelta(model, field, amount);
|
|
63
|
+
return result;
|
|
28
64
|
}
|
|
29
65
|
async function performAtomicDecrement(model, field, amount = 1) {
|
|
30
|
-
model
|
|
31
|
-
|
|
66
|
+
const result = await performAtomicUpdate(model, { $inc: { [field]: -amount } });
|
|
67
|
+
applyLocalDelta(model, field, -amount);
|
|
68
|
+
return result;
|
|
32
69
|
}
|
|
33
70
|
|
|
34
71
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"meta-methods.mjs","names":[],"sources":["../../../../../../../../cascade/src/model/methods/meta-methods.ts"],"sourcesContent":["import { DatabaseWriter } from \"../../writer/database-writer\";\nimport type { Model } from \"../model\";\n\nexport function applyDefaultsToModel(ModelClass: any, defaults: any): void {\n // Only apply defaults if model doesn't have its own value\n\n // ============================================================================\n // ID Generation\n // ============================================================================\n if (defaults.autoGenerateId !== undefined && ModelClass
|
|
1
|
+
{"version":3,"file":"meta-methods.mjs","names":[],"sources":["../../../../../../../../cascade/src/model/methods/meta-methods.ts"],"sourcesContent":["import { get, set } from \"@mongez/reinforcements\";\r\nimport { DatabaseWriter } from \"../../writer/database-writer\";\r\nimport type { Model } from \"../model\";\r\n\r\n/**\r\n * Whether a subclass (or an intermediate base below `Model`) set the static\r\n * itself. The base `Model` initialises several statics (strictMode,\r\n * autoGenerateId, incrementIdBy, deletedAtColumn), so comparing to\r\n * `undefined` would never let data-source defaults apply.\r\n */\r\nfunction isExplicitStatic(ModelClass: any, key: string): boolean {\r\n let current = ModelClass;\r\n\r\n // The base Model is the class whose parent is `Function.prototype`; its own\r\n // initialisers are framework fallbacks, not explicit choices.\r\n while (current && Object.getPrototypeOf(current) !== Function.prototype) {\r\n if (Object.prototype.hasOwnProperty.call(current, key) && current[key] !== undefined) {\r\n return true;\r\n }\r\n\r\n current = Object.getPrototypeOf(current);\r\n }\r\n\r\n return false;\r\n}\r\n\r\nexport function applyDefaultsToModel(ModelClass: any, defaults: any): void {\r\n // Only apply defaults if model doesn't have its own value\r\n\r\n // ============================================================================\r\n // ID Generation\r\n // ============================================================================\r\n if (defaults.autoGenerateId !== undefined && !isExplicitStatic(ModelClass, \"autoGenerateId\")) {\r\n ModelClass.autoGenerateId = defaults.autoGenerateId;\r\n }\r\n if (defaults.initialId !== undefined && !isExplicitStatic(ModelClass, \"initialId\")) {\r\n ModelClass.initialId = defaults.initialId;\r\n }\r\n if (defaults.randomInitialId !== undefined && !isExplicitStatic(ModelClass, \"randomInitialId\")) {\r\n ModelClass.randomInitialId = defaults.randomInitialId;\r\n }\r\n if (defaults.incrementIdBy !== undefined && !isExplicitStatic(ModelClass, \"incrementIdBy\")) {\r\n ModelClass.incrementIdBy = defaults.incrementIdBy;\r\n }\r\n if (defaults.randomIncrement !== undefined && !isExplicitStatic(ModelClass, \"randomIncrement\")) {\r\n ModelClass.randomIncrement = defaults.randomIncrement;\r\n }\r\n\r\n // ============================================================================\r\n // Timestamps\r\n // ============================================================================\r\n if (defaults.createdAtColumn !== undefined && !isExplicitStatic(ModelClass, \"createdAtColumn\")) {\r\n ModelClass.createdAtColumn = defaults.createdAtColumn;\r\n }\r\n\r\n if (defaults.updatedAtColumn !== undefined && !isExplicitStatic(ModelClass, \"updatedAtColumn\")) {\r\n ModelClass.updatedAtColumn = defaults.updatedAtColumn;\r\n }\r\n\r\n // ============================================================================\r\n // Deletion\r\n // ============================================================================\r\n if (defaults.deleteStrategy !== undefined && !isExplicitStatic(ModelClass, \"deleteStrategy\")) {\r\n ModelClass.deleteStrategy = defaults.deleteStrategy;\r\n }\r\n if (defaults.deletedAtColumn !== undefined && !isExplicitStatic(ModelClass, \"deletedAtColumn\")) {\r\n ModelClass.deletedAtColumn = defaults.deletedAtColumn;\r\n }\r\n if (defaults.trashTable !== undefined && !isExplicitStatic(ModelClass, \"trashTable\")) {\r\n // Handle function-based trash table\r\n if (typeof defaults.trashTable === \"function\") {\r\n ModelClass.trashTable = defaults.trashTable(ModelClass.table);\r\n } else {\r\n ModelClass.trashTable = defaults.trashTable;\r\n }\r\n }\r\n\r\n // ============================================================================\r\n // Validation\r\n // ============================================================================\r\n if (defaults.strictMode !== undefined && !isExplicitStatic(ModelClass, \"strictMode\")) {\r\n ModelClass.strictMode = defaults.strictMode;\r\n }\r\n}\r\n\r\nexport async function generateModelNextId(model: Model): Promise<number | string> {\r\n const writer = new DatabaseWriter(model);\r\n await writer.generateNextId();\r\n return model.id!;\r\n}\r\n\r\nexport async function performAtomicUpdate(\r\n model: Model,\r\n operations: Record<string, unknown>,\r\n): Promise<number> {\r\n return model.self().atomic({ [model.self().primaryKey]: model.trustedPrimaryKey }, operations);\r\n}\r\n\r\n/**\r\n * Apply an atomic delta to the local value without marking the field dirty.\r\n * The database already holds the change, so a later `save()` must not send a\r\n * stale `$set` for it. Other pending changes stay dirty.\r\n */\r\nfunction applyLocalDelta(model: Model, field: string, delta: number): void {\r\n const tracker = model.dirtyTracker;\r\n const fieldWasDirty = tracker.isDirty(field);\r\n const value = (get(model.data, field, 0) as number) + delta;\r\n\r\n set(model.data, field, value);\r\n\r\n // A removed column can't be replayed after a baseline reset, so fall back to\r\n // plain dirty tracking for that rare case.\r\n if (fieldWasDirty || tracker.getRemovedColumns().length > 0) {\r\n tracker.setAtPath(field, value);\r\n return;\r\n }\r\n\r\n const pending = tracker.getDirtyColumnsWithValues();\r\n const baseline = structuredClone(model.data) as Record<string, unknown>;\r\n\r\n for (const [column, values] of Object.entries(pending)) {\r\n set(baseline, column, values.oldValue);\r\n }\r\n\r\n tracker.reset(baseline);\r\n\r\n for (const [column, values] of Object.entries(pending)) {\r\n tracker.setAtPath(column, values.newValue);\r\n }\r\n}\r\n\r\nexport async function performAtomicIncrement<T extends string>(\r\n model: Model,\r\n field: T,\r\n amount = 1,\r\n): Promise<number> {\r\n const result = await performAtomicUpdate(model, { $inc: { [field]: amount } });\r\n applyLocalDelta(model, field, amount);\r\n return result;\r\n}\r\n\r\nexport async function performAtomicDecrement<T extends string>(\r\n model: Model,\r\n field: T,\r\n amount = 1,\r\n): Promise<number> {\r\n const result = await performAtomicUpdate(model, { $inc: { [field]: -amount } });\r\n applyLocalDelta(model, field, -amount);\r\n return result;\r\n}\r\n"],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { dataSourceRegistry } from "../../data-source/data-source-registry.mjs";
|
|
2
2
|
import { sanitizeFilter } from "../../utils/sanitize-filter.mjs";
|
|
3
|
+
import { emitStaticEvent } from "./static-event-methods.mjs";
|
|
3
4
|
import { warnUndeclaredSensitiveFields } from "./serialization-methods.mjs";
|
|
4
5
|
|
|
5
6
|
//#region ../cascade/src/model/methods/query-methods.ts
|
|
@@ -10,15 +11,29 @@ function buildQuery(ModelClass, BaseModel) {
|
|
|
10
11
|
queryBuilder.disabledGlobalScopes = /* @__PURE__ */ new Set();
|
|
11
12
|
queryBuilder.relationDefinitions = ModelClass.relations;
|
|
12
13
|
queryBuilder.modelClass = ModelClass;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
let fetchStartedAt = Date.now();
|
|
15
|
+
queryBuilder.onFetching(async () => {
|
|
16
|
+
fetchStartedAt = Date.now();
|
|
17
|
+
await emitStaticEvent(ModelClass, "fetching", queryBuilder, {
|
|
18
|
+
table: ModelClass.table,
|
|
19
|
+
modelClass: ModelClass
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
queryBuilder.onHydrating(async (records, context) => {
|
|
23
|
+
await emitStaticEvent(ModelClass, "hydrating", records, {
|
|
24
|
+
query: queryBuilder,
|
|
25
|
+
hydrateCallback: context?.hydrateCallback
|
|
26
|
+
});
|
|
16
27
|
});
|
|
17
28
|
queryBuilder.hydrate((data) => {
|
|
18
29
|
return ModelClass.hydrate(data);
|
|
19
30
|
});
|
|
20
|
-
queryBuilder.onFetched(async (models) => {
|
|
21
|
-
await ModelClass
|
|
31
|
+
queryBuilder.onFetched(async (models, context) => {
|
|
32
|
+
await emitStaticEvent(ModelClass, "fetched", models, {
|
|
33
|
+
query: queryBuilder,
|
|
34
|
+
rawRecords: context?.rawRecords ?? [],
|
|
35
|
+
duration: context?.duration ?? Date.now() - fetchStartedAt
|
|
36
|
+
});
|
|
22
37
|
});
|
|
23
38
|
return queryBuilder;
|
|
24
39
|
}
|
|
@@ -81,35 +96,91 @@ function toDriverOptions(options) {
|
|
|
81
96
|
return rest;
|
|
82
97
|
}
|
|
83
98
|
/**
|
|
99
|
+
* Pin a driver-level write to the rows the model's global scopes (tenant, soft
|
|
100
|
+
* delete) allow. The driver write APIs take a raw filter and never see scopes,
|
|
101
|
+
* so the target ids are resolved through the scoped query builder and added to
|
|
102
|
+
* the filter. `null` means no visible row matches: the write must be a no-op.
|
|
103
|
+
*/
|
|
104
|
+
async function scopeWriteFilter(ModelClass, filter, single) {
|
|
105
|
+
const primaryKey = ModelClass.primaryKey;
|
|
106
|
+
const ids = await ModelClass.query().where(filter).pluck(primaryKey);
|
|
107
|
+
if (ids.length === 0) return null;
|
|
108
|
+
return {
|
|
109
|
+
...filter,
|
|
110
|
+
[primaryKey]: single ? ids[0] : { $in: ids }
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Scoped filter for a write that may insert (`upsert`). With no visible match
|
|
115
|
+
* the driver would insert or, worse, update a row a scope hides (another
|
|
116
|
+
* tenant's, a soft-deleted one), so a hidden match is refused.
|
|
117
|
+
*/
|
|
118
|
+
async function scopeUpsertFilter(ModelClass, filter) {
|
|
119
|
+
const scoped = await scopeWriteFilter(ModelClass, filter, true);
|
|
120
|
+
if (scoped) return scoped;
|
|
121
|
+
if (await ModelClass.query().withoutGlobalScopes().where(filter).exists()) throw new Error(`${ModelClass.name}: upsert matches a row hidden by a global scope (tenant or soft delete); refusing to touch it.`);
|
|
122
|
+
return filter;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
84
125
|
* Run an atomic update and return the number of documents modified plus the
|
|
85
126
|
* number inserted by an `upsert`.
|
|
86
127
|
*/
|
|
87
128
|
async function performAtomic(ModelClass, filter, operations, options) {
|
|
88
|
-
const
|
|
129
|
+
const resolved = resolveFilter(filter, options);
|
|
130
|
+
const scoped = options?.upsert ? await scopeUpsertFilter(ModelClass, resolved) : await scopeWriteFilter(ModelClass, resolved, false);
|
|
131
|
+
if (!scoped) return 0;
|
|
132
|
+
const result = await ModelClass.getDriver().atomic(ModelClass.table, scoped, operations, toDriverOptions(options));
|
|
89
133
|
return result.modifiedCount + (result.upsertedCount ?? 0);
|
|
90
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* Update a row by id through the model writer: the row is loaded (scopes
|
|
137
|
+
* apply), merged, and saved, so the schema, strictMode, casting, `updatedAt`
|
|
138
|
+
* and events all apply. Returns 1 when a row was saved, 0 when none is visible.
|
|
139
|
+
*/
|
|
91
140
|
async function updateById(ModelClass, id, data) {
|
|
92
|
-
|
|
141
|
+
const model = await ModelClass.query().where(ModelClass.primaryKey, id).first();
|
|
142
|
+
if (!model) return 0;
|
|
143
|
+
model.merge(data);
|
|
144
|
+
await model.save();
|
|
145
|
+
return 1;
|
|
93
146
|
}
|
|
94
147
|
async function findAndUpdateRecords(ModelClass, filter, update, options) {
|
|
95
|
-
|
|
148
|
+
const primaryKey = ModelClass.primaryKey;
|
|
149
|
+
const safeFilter = sanitizeFilter(filter);
|
|
150
|
+
const ids = await ModelClass.query().where(safeFilter).pluck(primaryKey);
|
|
151
|
+
if (ids.length === 0) {
|
|
152
|
+
if (!options?.upsert) return [];
|
|
153
|
+
await performAtomic(ModelClass, safeFilter, update, {
|
|
154
|
+
...options,
|
|
155
|
+
trustedFilter: true
|
|
156
|
+
});
|
|
157
|
+
return await ModelClass.query().where(safeFilter).get();
|
|
158
|
+
}
|
|
159
|
+
await performAtomic(ModelClass, { [primaryKey]: { $in: ids } }, update, {
|
|
96
160
|
...options,
|
|
97
|
-
trustedFilter:
|
|
161
|
+
trustedFilter: true
|
|
98
162
|
});
|
|
99
|
-
return await ModelClass.query().
|
|
163
|
+
return await ModelClass.query().whereIn(primaryKey, ids).get();
|
|
100
164
|
}
|
|
101
165
|
async function findOneAndUpdateRecord(ModelClass, filter, update, options) {
|
|
102
|
-
const
|
|
166
|
+
const resolved = resolveFilter(filter, options);
|
|
167
|
+
const scoped = options?.upsert ? await scopeUpsertFilter(ModelClass, resolved) : await scopeWriteFilter(ModelClass, resolved, true);
|
|
168
|
+
if (!scoped) return null;
|
|
169
|
+
const result = await ModelClass.getDriver().findOneAndUpdate(ModelClass.table, scoped, update, toDriverOptions(options));
|
|
103
170
|
if (!result) return null;
|
|
104
|
-
return
|
|
171
|
+
return ModelClass.hydrate(result);
|
|
105
172
|
}
|
|
106
173
|
async function findAndReplaceRecord(ModelClass, filter, document) {
|
|
107
|
-
const
|
|
174
|
+
const scoped = await scopeWriteFilter(ModelClass, sanitizeFilter(filter), true);
|
|
175
|
+
if (!scoped) return null;
|
|
176
|
+
const result = await ModelClass.getDriver().replace(ModelClass.table, scoped, document);
|
|
108
177
|
if (!result) return null;
|
|
109
|
-
return
|
|
178
|
+
return ModelClass.hydrate(result);
|
|
110
179
|
}
|
|
111
180
|
async function findOneAndDeleteRecord(ModelClass, filter, options) {
|
|
112
|
-
const
|
|
181
|
+
const scoped = await scopeWriteFilter(ModelClass, sanitizeFilter(filter), true);
|
|
182
|
+
if (!scoped) return null;
|
|
183
|
+
const result = await ModelClass.getDriver().findOneAndDelete(ModelClass.table, scoped, options);
|
|
113
184
|
if (!result) return null;
|
|
114
185
|
const model = ModelClass.hydrate(result);
|
|
115
186
|
model.dirtyTracker.reset();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query-methods.mjs","names":["ctor"],"sources":["../../../../../../../../cascade/src/model/methods/query-methods.ts"],"sourcesContent":["import type {\r\n AtomicUpdate,\r\n AtomicUpdateOptions,\r\n FindOneAndUpdateOptions,\r\n PaginationOptions,\r\n PaginationResult,\r\n QueryBuilderContract,\r\n} from \"../../contracts\";\r\nimport type { DataSource } from \"../../data-source/data-source\";\r\nimport { dataSourceRegistry } from \"../../data-source/data-source-registry\";\r\nimport { sanitizeFilter } from \"../../utils/sanitize-filter\";\r\nimport type { ChildModel, GlobalScopeDefinition, Model } from \"../model\";\r\nimport { warnUndeclaredSensitiveFields } from \"./serialization-methods\";\r\n\r\nexport function buildQuery<TModel extends Model>(\r\n ModelClass: ChildModel<TModel>,\r\n BaseModel: typeof Model,\r\n): QueryBuilderContract<TModel> {\r\n const queryBuilder = ModelClass.newQueryBuilder<TModel>();\r\n const qb = queryBuilder;\r\n\r\n // Collect global scopes from base Model and child model\r\n const allGlobalScopes = new Map<string, GlobalScopeDefinition>([\r\n ...BaseModel.globalScopes,\r\n ...ModelClass.globalScopes,\r\n ]);\r\n\r\n queryBuilder.pendingGlobalScopes = allGlobalScopes;\r\n queryBuilder.availableLocalScopes = ModelClass.localScopes;\r\n queryBuilder.disabledGlobalScopes = new Set();\r\n queryBuilder.relationDefinitions = ModelClass.relations;\r\n queryBuilder.modelClass = ModelClass;\r\n\r\n ModelClass.events().emitFetching(queryBuilder, {\r\n table: ModelClass.table,\r\n modelClass: ModelClass,\r\n });\r\n\r\n queryBuilder.hydrate((data: any) => {\r\n return ModelClass.hydrate(data);\r\n });\r\n\r\n // Eager-loading runs inside the driver's `get()` so it works for any\r\n // builder construction path (Model.query, Model.newQueryBuilder, custom\r\n // subclassed builders). This hook only handles the model-level `fetched`\r\n // event emission.\r\n queryBuilder.onFetched(async (models: any[]) => {\r\n await ModelClass.events().emit(\"fetched\", models as any, {});\r\n });\r\n\r\n return queryBuilder;\r\n}\r\n\r\nexport function buildNewQueryBuilder<TModel extends Model>(\r\n ModelClass: ChildModel<TModel>,\r\n): QueryBuilderContract<TModel> {\r\n const dataSource = ModelClass.getDataSource();\r\n\r\n if (ModelClass.builder) {\r\n const BuilderClass = ModelClass.builder;\r\n return new BuilderClass(ModelClass.table, dataSource) as QueryBuilderContract<TModel>;\r\n }\r\n\r\n const queryBuilder = dataSource.driver.queryBuilder<TModel>(ModelClass.table);\r\n return queryBuilder;\r\n}\r\n\r\nexport async function findFirst<TModel extends Model>(\r\n ModelClass: ChildModel<TModel>,\r\n filter?: Record<string, unknown>,\r\n): Promise<TModel | null> {\r\n const query = ModelClass.query();\r\n if (filter) {\r\n query.where(filter);\r\n }\r\n return query.first();\r\n}\r\n\r\nexport async function findLast<TModel extends Model>(\r\n ModelClass: ChildModel<TModel>,\r\n filter?: Record<string, unknown>,\r\n): Promise<TModel | null> {\r\n const query = ModelClass.query();\r\n if (filter) {\r\n query.where(filter);\r\n }\r\n\r\n return query.last();\r\n}\r\n\r\nexport async function findAll<TModel extends Model>(\r\n ModelClass: ChildModel<TModel>,\r\n filter?: Record<string, unknown>,\r\n): Promise<TModel[]> {\r\n const query = ModelClass.query();\r\n if (filter) {\r\n query.where(filter);\r\n }\r\n return query.get();\r\n}\r\n\r\nexport function countRecords<TModel extends Model>(\r\n ModelClass: ChildModel<TModel>,\r\n filter?: Record<string, unknown>,\r\n): Promise<number> {\r\n const query = ModelClass.query();\r\n if (filter) {\r\n query.where(filter);\r\n }\r\n return query.count();\r\n}\r\n\r\nexport async function findById<TModel extends Model>(\r\n ModelClass: ChildModel<TModel>,\r\n id: string | number,\r\n): Promise<TModel | null> {\r\n const query = ModelClass.query();\r\n return query.where(ModelClass.primaryKey, id).first();\r\n}\r\n\r\nexport async function paginateRecords<TModel extends Model>(\r\n ModelClass: ChildModel<TModel>,\r\n options: PaginationOptions & { filter?: Record<string, unknown> } = {},\r\n): Promise<PaginationResult<TModel>> {\r\n const query = ModelClass.query();\r\n if (options.filter) {\r\n query.where(options.filter);\r\n }\r\n\r\n return query.paginate({\r\n limit: options.limit,\r\n page: options.page,\r\n });\r\n}\r\n\r\nexport async function findLatest<TModel extends Model>(\r\n ModelClass: ChildModel<TModel>,\r\n filter?: Record<string, unknown>,\r\n): Promise<TModel[]> {\r\n const query = ModelClass.query();\r\n if (filter) {\r\n query.where(filter);\r\n }\r\n return (await query.latest()) as unknown as TModel[];\r\n}\r\n\r\nexport function increaseField<TModel extends Model>(\r\n ModelClass: ChildModel<TModel>,\r\n filter: Record<string, unknown>,\r\n field: string,\r\n amount: number,\r\n): Promise<number> {\r\n const query = ModelClass.query().where(filter);\r\n return query.increment(field, amount);\r\n}\r\n\r\nexport function decreaseField<TModel extends Model>(\r\n ModelClass: ChildModel<TModel>,\r\n filter: Record<string, unknown>,\r\n field: string,\r\n amount: number,\r\n): Promise<number> {\r\n const query = ModelClass.query().where(filter);\r\n return query.decrement(field, amount);\r\n}\r\n\r\n// The atomic/find-and-modify statics below forward their filter straight to\r\n// the driver — they never pass through where(), so they must run the same\r\n// operator-injection check themselves. Only the FILTER is sanitized; update\r\n// operators ($set/$inc/…) are the point of these APIs and stay untouched.\r\n//\r\n// `trustedFilter` is the explicit, code-authored opt-out: it lets a conditional\r\n// filter such as `{ used: { $lt: 10 } }` through. It is stripped before the\r\n// options reach the driver.\r\nfunction resolveFilter(\r\n filter: Record<string, unknown>,\r\n options: AtomicUpdateOptions | undefined,\r\n): Record<string, unknown> {\r\n return options?.trustedFilter ? filter : sanitizeFilter(filter);\r\n}\r\n\r\nfunction toDriverOptions<TOptions extends AtomicUpdateOptions>(\r\n options: TOptions | undefined,\r\n): Omit<TOptions, \"trustedFilter\"> | undefined {\r\n if (!options) {\r\n return undefined;\r\n }\r\n\r\n const { trustedFilter: _trustedFilter, ...rest } = options;\r\n\r\n return rest;\r\n}\r\n\r\n/**\r\n * Run an atomic update and return the number of documents modified plus the\r\n * number inserted by an `upsert`.\r\n */\r\nexport async function performAtomic<TModel extends Model>(\r\n ModelClass: ChildModel<TModel>,\r\n filter: Record<string, unknown>,\r\n operations: AtomicUpdate,\r\n options?: AtomicUpdateOptions,\r\n): Promise<number> {\r\n const result = await ModelClass.getDriver().atomic(\r\n ModelClass.table,\r\n resolveFilter(filter, options),\r\n operations,\r\n toDriverOptions(options),\r\n );\r\n\r\n return result.modifiedCount + (result.upsertedCount ?? 0);\r\n}\r\n\r\nexport async function updateById<TModel extends Model>(\r\n ModelClass: ChildModel<TModel>,\r\n id: string | number,\r\n data: Record<string, unknown>,\r\n): Promise<number> {\r\n const result = await ModelClass.getDriver().update(ModelClass.table, { [ModelClass.primaryKey]: id }, { $set: data });\r\n return result.modifiedCount;\r\n}\r\n\r\nexport async function findAndUpdateRecords<TModel extends Model>(\r\n ModelClass: ChildModel<TModel>,\r\n filter: Record<string, unknown>,\r\n update: AtomicUpdate,\r\n options?: Omit<AtomicUpdateOptions, \"trustedFilter\">,\r\n): Promise<TModel[]> {\r\n await performAtomic(ModelClass, filter, update, { ...options, trustedFilter: false });\r\n return await ModelClass.query().where(filter).get();\r\n}\r\n\r\nexport async function findOneAndUpdateRecord<TModel extends Model>(\r\n ModelClass: ChildModel<TModel>,\r\n filter: Record<string, unknown>,\r\n update: AtomicUpdate,\r\n options?: FindOneAndUpdateOptions,\r\n): Promise<TModel | null> {\r\n const result = await ModelClass.getDriver().findOneAndUpdate(\r\n ModelClass.table,\r\n resolveFilter(filter, options),\r\n update,\r\n toDriverOptions(options),\r\n );\r\n if (!result) return null;\r\n const ctor = ModelClass as any;\r\n return new ctor(result);\r\n}\r\n\r\nexport async function findAndReplaceRecord<TModel extends Model>(\r\n ModelClass: ChildModel<TModel>,\r\n filter: Record<string, unknown>,\r\n document: Record<string, unknown>,\r\n): Promise<TModel | null> {\r\n const result = await ModelClass.getDriver().replace(\r\n ModelClass.table,\r\n sanitizeFilter(filter),\r\n document,\r\n );\r\n if (!result) return null;\r\n const ctor = ModelClass as any;\r\n return new ctor(result);\r\n}\r\n\r\nexport async function findOneAndDeleteRecord<TModel extends Model>(\r\n ModelClass: ChildModel<TModel>,\r\n filter: Record<string, unknown>,\r\n options?: Record<string, unknown>,\r\n): Promise<TModel | null> {\r\n const driver = ModelClass.getDriver();\r\n const result = await driver.findOneAndDelete(ModelClass.table, sanitizeFilter(filter), options);\r\n\r\n if (!result) {\r\n return null;\r\n }\r\n\r\n const model = ModelClass.hydrate(result as Record<string, unknown>);\r\n model.dirtyTracker.reset();\r\n return model;\r\n}\r\n\r\nexport function resolveDataSource<TModel extends Model>(\r\n ModelClass: ChildModel<TModel>,\r\n): DataSource {\r\n const ref = ModelClass.dataSource;\r\n let dataSource: DataSource;\r\n\r\n if (typeof ref === \"string\") {\r\n dataSource = dataSourceRegistry.get(ref);\r\n } else if (ref) {\r\n dataSource = ref;\r\n } else {\r\n dataSource = dataSourceRegistry.get();\r\n }\r\n\r\n if (!ModelClass.hasOwnProperty(\"_defaultsApplied\")) {\r\n const driverDefaults = dataSource.driver.modelDefaults || {};\r\n const dataSourceDefaults = dataSource.modelDefaults || {};\r\n\r\n const mergedDefaults = {\r\n ...driverDefaults,\r\n ...dataSourceDefaults,\r\n };\r\n\r\n if (Object.keys(mergedDefaults).length > 0) {\r\n (ModelClass as any).applyModelDefaults(mergedDefaults);\r\n }\r\n\r\n (ModelClass as any)._defaultsApplied = true;\r\n }\r\n\r\n // First data-source resolution is the earliest per-class hook every model\r\n // passes through — warn here (once) if the schema carries credential-shaped\r\n // fields that toJSON() would expose.\r\n warnUndeclaredSensitiveFields(ModelClass);\r\n\r\n return dataSource;\r\n}\r\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"query-methods.mjs","names":[],"sources":["../../../../../../../../cascade/src/model/methods/query-methods.ts"],"sourcesContent":["import type {\n AtomicUpdate,\n AtomicUpdateOptions,\n FindOneAndUpdateOptions,\n PaginationOptions,\n PaginationResult,\n QueryBuilderContract,\n} from \"../../contracts\";\nimport type { DataSource } from \"../../data-source/data-source\";\nimport { dataSourceRegistry } from \"../../data-source/data-source-registry\";\nimport { sanitizeFilter } from \"../../utils/sanitize-filter\";\nimport type { ChildModel, GlobalScopeDefinition, Model } from \"../model\";\nimport { emitStaticEvent } from \"./static-event-methods\";\nimport { warnUndeclaredSensitiveFields } from \"./serialization-methods\";\n\nexport function buildQuery<TModel extends Model>(\n ModelClass: ChildModel<TModel>,\n BaseModel: typeof Model,\n): QueryBuilderContract<TModel> {\n const queryBuilder = ModelClass.newQueryBuilder<TModel>();\n const qb = queryBuilder;\n\n // Collect global scopes from base Model and child model\n const allGlobalScopes = new Map<string, GlobalScopeDefinition>([\n ...BaseModel.globalScopes,\n ...ModelClass.globalScopes,\n ]);\n\n queryBuilder.pendingGlobalScopes = allGlobalScopes;\n queryBuilder.availableLocalScopes = ModelClass.localScopes;\n queryBuilder.disabledGlobalScopes = new Set();\n queryBuilder.relationDefinitions = ModelClass.relations;\n queryBuilder.modelClass = ModelClass;\n\n let fetchStartedAt = Date.now();\n\n // Emitted from the builder's execute path (awaited), not at construction, so\n // listener changes apply before the query runs and a throwing listener fails\n // the query instead of becoming an unhandled rejection.\n queryBuilder.onFetching(async () => {\n fetchStartedAt = Date.now();\n\n await emitStaticEvent(ModelClass, \"fetching\", queryBuilder, {\n table: ModelClass.table,\n modelClass: ModelClass,\n });\n });\n\n queryBuilder.onHydrating(async (records: any[], context: any) => {\n await emitStaticEvent(ModelClass, \"hydrating\", records as any, {\n query: queryBuilder,\n hydrateCallback: context?.hydrateCallback,\n });\n });\n\n queryBuilder.hydrate((data: any) => {\n return ModelClass.hydrate(data);\n });\n\n // Eager-loading runs inside the driver's `get()` so it works for any\n // builder construction path (Model.query, Model.newQueryBuilder, custom\n // subclassed builders). This hook only handles the model-level `fetched`\n // event emission.\n queryBuilder.onFetched(async (models: any[], context: any) => {\n await emitStaticEvent(ModelClass, \"fetched\", models as any, {\n query: queryBuilder,\n rawRecords: context?.rawRecords ?? [],\n duration: context?.duration ?? Date.now() - fetchStartedAt,\n });\n });\n\n return queryBuilder;\n}\n\nexport function buildNewQueryBuilder<TModel extends Model>(\n ModelClass: ChildModel<TModel>,\n): QueryBuilderContract<TModel> {\n const dataSource = ModelClass.getDataSource();\n\n if (ModelClass.builder) {\n const BuilderClass = ModelClass.builder;\n return new BuilderClass(ModelClass.table, dataSource) as QueryBuilderContract<TModel>;\n }\n\n const queryBuilder = dataSource.driver.queryBuilder<TModel>(ModelClass.table);\n return queryBuilder;\n}\n\nexport async function findFirst<TModel extends Model>(\n ModelClass: ChildModel<TModel>,\n filter?: Record<string, unknown>,\n): Promise<TModel | null> {\n const query = ModelClass.query();\n if (filter) {\n query.where(filter);\n }\n return query.first();\n}\n\nexport async function findLast<TModel extends Model>(\n ModelClass: ChildModel<TModel>,\n filter?: Record<string, unknown>,\n): Promise<TModel | null> {\n const query = ModelClass.query();\n if (filter) {\n query.where(filter);\n }\n\n return query.last();\n}\n\nexport async function findAll<TModel extends Model>(\n ModelClass: ChildModel<TModel>,\n filter?: Record<string, unknown>,\n): Promise<TModel[]> {\n const query = ModelClass.query();\n if (filter) {\n query.where(filter);\n }\n return query.get();\n}\n\nexport function countRecords<TModel extends Model>(\n ModelClass: ChildModel<TModel>,\n filter?: Record<string, unknown>,\n): Promise<number> {\n const query = ModelClass.query();\n if (filter) {\n query.where(filter);\n }\n return query.count();\n}\n\nexport async function findById<TModel extends Model>(\n ModelClass: ChildModel<TModel>,\n id: string | number,\n): Promise<TModel | null> {\n const query = ModelClass.query();\n return query.where(ModelClass.primaryKey, id).first();\n}\n\nexport async function paginateRecords<TModel extends Model>(\n ModelClass: ChildModel<TModel>,\n options: PaginationOptions & { filter?: Record<string, unknown> } = {},\n): Promise<PaginationResult<TModel>> {\n const query = ModelClass.query();\n if (options.filter) {\n query.where(options.filter);\n }\n\n return query.paginate({\n limit: options.limit,\n page: options.page,\n });\n}\n\nexport async function findLatest<TModel extends Model>(\n ModelClass: ChildModel<TModel>,\n filter?: Record<string, unknown>,\n): Promise<TModel[]> {\n const query = ModelClass.query();\n if (filter) {\n query.where(filter);\n }\n return (await query.latest()) as unknown as TModel[];\n}\n\nexport function increaseField<TModel extends Model>(\n ModelClass: ChildModel<TModel>,\n filter: Record<string, unknown>,\n field: string,\n amount: number,\n): Promise<number> {\n const query = ModelClass.query().where(filter);\n return query.increment(field, amount);\n}\n\nexport function decreaseField<TModel extends Model>(\n ModelClass: ChildModel<TModel>,\n filter: Record<string, unknown>,\n field: string,\n amount: number,\n): Promise<number> {\n const query = ModelClass.query().where(filter);\n return query.decrement(field, amount);\n}\n\n// The atomic/find-and-modify statics below forward their filter straight to\n// the driver — they never pass through where(), so they must run the same\n// operator-injection check themselves. Only the FILTER is sanitized; update\n// operators ($set/$inc/…) are the point of these APIs and stay untouched.\n//\n// `trustedFilter` is the explicit, code-authored opt-out: it lets a conditional\n// filter such as `{ used: { $lt: 10 } }` through. It is stripped before the\n// options reach the driver.\nfunction resolveFilter(\n filter: Record<string, unknown>,\n options: AtomicUpdateOptions | undefined,\n): Record<string, unknown> {\n return options?.trustedFilter ? filter : sanitizeFilter(filter);\n}\n\nfunction toDriverOptions<TOptions extends AtomicUpdateOptions>(\n options: TOptions | undefined,\n): Omit<TOptions, \"trustedFilter\"> | undefined {\n if (!options) {\n return undefined;\n }\n\n const { trustedFilter: _trustedFilter, ...rest } = options;\n\n return rest;\n}\n\n/**\n * Pin a driver-level write to the rows the model's global scopes (tenant, soft\n * delete) allow. The driver write APIs take a raw filter and never see scopes,\n * so the target ids are resolved through the scoped query builder and added to\n * the filter. `null` means no visible row matches: the write must be a no-op.\n */\nexport async function scopeWriteFilter(\n ModelClass: ChildModel<any>,\n filter: Record<string, unknown>,\n single: boolean,\n): Promise<Record<string, unknown> | null> {\n const primaryKey = ModelClass.primaryKey;\n const ids = await ModelClass.query().where(filter).pluck(primaryKey);\n\n if (ids.length === 0) {\n return null;\n }\n\n return { ...filter, [primaryKey]: single ? ids[0] : { $in: ids } };\n}\n\n/**\n * Scoped filter for a write that may insert (`upsert`). With no visible match\n * the driver would insert or, worse, update a row a scope hides (another\n * tenant's, a soft-deleted one), so a hidden match is refused.\n */\nexport async function scopeUpsertFilter(\n ModelClass: ChildModel<any>,\n filter: Record<string, unknown>,\n): Promise<Record<string, unknown>> {\n const scoped = await scopeWriteFilter(ModelClass, filter, true);\n\n if (scoped) {\n return scoped;\n }\n\n const hidden = await ModelClass.query().withoutGlobalScopes().where(filter).exists();\n\n if (hidden) {\n throw new Error(\n `${ModelClass.name}: upsert matches a row hidden by a global scope (tenant or soft delete); refusing to touch it.`,\n );\n }\n\n return filter;\n}\n\n/**\n * Run an atomic update and return the number of documents modified plus the\n * number inserted by an `upsert`.\n */\nexport async function performAtomic<TModel extends Model>(\n ModelClass: ChildModel<TModel>,\n filter: Record<string, unknown>,\n operations: AtomicUpdate,\n options?: AtomicUpdateOptions,\n): Promise<number> {\n const resolved = resolveFilter(filter, options);\n const scoped = options?.upsert\n ? await scopeUpsertFilter(ModelClass, resolved)\n : await scopeWriteFilter(ModelClass, resolved, false);\n\n if (!scoped) {\n return 0;\n }\n\n const result = await ModelClass.getDriver().atomic(\n ModelClass.table,\n scoped,\n operations,\n toDriverOptions(options),\n );\n\n return result.modifiedCount + (result.upsertedCount ?? 0);\n}\n\n/**\n * Update a row by id through the model writer: the row is loaded (scopes\n * apply), merged, and saved, so the schema, strictMode, casting, `updatedAt`\n * and events all apply. Returns 1 when a row was saved, 0 when none is visible.\n */\nexport async function updateById<TModel extends Model>(\n ModelClass: ChildModel<TModel>,\n id: string | number,\n data: Record<string, unknown>,\n): Promise<number> {\n const model = await ModelClass.query().where(ModelClass.primaryKey, id).first();\n\n if (!model) {\n return 0;\n }\n\n model.merge(data);\n await model.save();\n\n return 1;\n}\n\nexport async function findAndUpdateRecords<TModel extends Model>(\n ModelClass: ChildModel<TModel>,\n filter: Record<string, unknown>,\n update: AtomicUpdate,\n options?: Omit<AtomicUpdateOptions, \"trustedFilter\">,\n): Promise<TModel[]> {\n const primaryKey = ModelClass.primaryKey;\n const safeFilter = sanitizeFilter(filter);\n\n // Capture the matching ids first: the update usually changes the very field\n // the filter matches on, so re-querying with the filter would find nothing.\n const ids = await ModelClass.query().where(safeFilter).pluck(primaryKey);\n\n if (ids.length === 0) {\n if (!options?.upsert) {\n return [];\n }\n\n await performAtomic(ModelClass, safeFilter, update, { ...options, trustedFilter: true });\n return await ModelClass.query().where(safeFilter).get();\n }\n\n await performAtomic(\n ModelClass,\n { [primaryKey]: { $in: ids } },\n update,\n { ...options, trustedFilter: true },\n );\n\n return await ModelClass.query().whereIn(primaryKey, ids).get();\n}\n\nexport async function findOneAndUpdateRecord<TModel extends Model>(\n ModelClass: ChildModel<TModel>,\n filter: Record<string, unknown>,\n update: AtomicUpdate,\n options?: FindOneAndUpdateOptions,\n): Promise<TModel | null> {\n const resolved = resolveFilter(filter, options);\n const scoped = options?.upsert\n ? await scopeUpsertFilter(ModelClass, resolved)\n : await scopeWriteFilter(ModelClass, resolved, true);\n\n if (!scoped) return null;\n\n const result = await ModelClass.getDriver().findOneAndUpdate(\n ModelClass.table,\n scoped,\n update,\n toDriverOptions(options),\n );\n if (!result) return null;\n return ModelClass.hydrate(result as Record<string, unknown>) as TModel;\n}\n\nexport async function findAndReplaceRecord<TModel extends Model>(\n ModelClass: ChildModel<TModel>,\n filter: Record<string, unknown>,\n document: Record<string, unknown>,\n): Promise<TModel | null> {\n const scoped = await scopeWriteFilter(ModelClass, sanitizeFilter(filter), true);\n\n if (!scoped) return null;\n\n const result = await ModelClass.getDriver().replace(ModelClass.table, scoped, document);\n if (!result) return null;\n return ModelClass.hydrate(result as Record<string, unknown>) as TModel;\n}\n\nexport async function findOneAndDeleteRecord<TModel extends Model>(\n ModelClass: ChildModel<TModel>,\n filter: Record<string, unknown>,\n options?: Record<string, unknown>,\n): Promise<TModel | null> {\n const scoped = await scopeWriteFilter(ModelClass, sanitizeFilter(filter), true);\n\n if (!scoped) {\n return null;\n }\n\n const result = await ModelClass.getDriver().findOneAndDelete(ModelClass.table, scoped, options);\n\n if (!result) {\n return null;\n }\n\n const model = ModelClass.hydrate(result as Record<string, unknown>);\n model.dirtyTracker.reset();\n return model;\n}\n\nexport function resolveDataSource<TModel extends Model>(\n ModelClass: ChildModel<TModel>,\n): DataSource {\n const ref = ModelClass.dataSource;\n let dataSource: DataSource;\n\n if (typeof ref === \"string\") {\n dataSource = dataSourceRegistry.get(ref);\n } else if (ref) {\n dataSource = ref;\n } else {\n dataSource = dataSourceRegistry.get();\n }\n\n if (!ModelClass.hasOwnProperty(\"_defaultsApplied\")) {\n const driverDefaults = dataSource.driver.modelDefaults || {};\n const dataSourceDefaults = dataSource.modelDefaults || {};\n\n const mergedDefaults = {\n ...driverDefaults,\n ...dataSourceDefaults,\n };\n\n if (Object.keys(mergedDefaults).length > 0) {\n (ModelClass as any).applyModelDefaults(mergedDefaults);\n }\n\n (ModelClass as any)._defaultsApplied = true;\n }\n\n // First data-source resolution is the earliest per-class hook every model\n // passes through — warn here (once) if the schema carries credential-shaped\n // fields that toJSON() would expose.\n warnUndeclaredSensitiveFields(ModelClass);\n\n return dataSource;\n}\n"],"mappings":";;;;;;AAeA,SAAgB,WACd,YACA,WAC8B;CAC9B,MAAM,eAAe,WAAW,gBAAwB;CASxD,aAAa,sBAAsB,IALP,IAAmC,CAC7D,GAAG,UAAU,cACb,GAAG,WAAW,YAChB,CAEiD;CACjD,aAAa,uBAAuB,WAAW;CAC/C,aAAa,uCAAuB,IAAI,IAAI;CAC5C,aAAa,sBAAsB,WAAW;CAC9C,aAAa,aAAa;CAE1B,IAAI,iBAAiB,KAAK,IAAI;CAK9B,aAAa,WAAW,YAAY;EAClC,iBAAiB,KAAK,IAAI;EAE1B,MAAM,gBAAgB,YAAY,YAAY,cAAc;GAC1D,OAAO,WAAW;GAClB,YAAY;EACd,CAAC;CACH,CAAC;CAED,aAAa,YAAY,OAAO,SAAgB,YAAiB;EAC/D,MAAM,gBAAgB,YAAY,aAAa,SAAgB;GAC7D,OAAO;GACP,iBAAiB,SAAS;EAC5B,CAAC;CACH,CAAC;CAED,aAAa,SAAS,SAAc;EAClC,OAAO,WAAW,QAAQ,IAAI;CAChC,CAAC;CAMD,aAAa,UAAU,OAAO,QAAe,YAAiB;EAC5D,MAAM,gBAAgB,YAAY,WAAW,QAAe;GAC1D,OAAO;GACP,YAAY,SAAS,cAAc,CAAC;GACpC,UAAU,SAAS,YAAY,KAAK,IAAI,IAAI;EAC9C,CAAC;CACH,CAAC;CAED,OAAO;AACT;AAEA,SAAgB,qBACd,YAC8B;CAC9B,MAAM,aAAa,WAAW,cAAc;CAE5C,IAAI,WAAW,SAAS;EACtB,MAAM,eAAe,WAAW;EAChC,OAAO,IAAI,aAAa,WAAW,OAAO,UAAU;CACtD;CAGA,OADqB,WAAW,OAAO,aAAqB,WAAW,KACrD;AACpB;AAEA,eAAsB,UACpB,YACA,QACwB;CACxB,MAAM,QAAQ,WAAW,MAAM;CAC/B,IAAI,QACF,MAAM,MAAM,MAAM;CAEpB,OAAO,MAAM,MAAM;AACrB;AAEA,eAAsB,SACpB,YACA,QACwB;CACxB,MAAM,QAAQ,WAAW,MAAM;CAC/B,IAAI,QACF,MAAM,MAAM,MAAM;CAGpB,OAAO,MAAM,KAAK;AACpB;AAEA,eAAsB,QACpB,YACA,QACmB;CACnB,MAAM,QAAQ,WAAW,MAAM;CAC/B,IAAI,QACF,MAAM,MAAM,MAAM;CAEpB,OAAO,MAAM,IAAI;AACnB;AAEA,SAAgB,aACd,YACA,QACiB;CACjB,MAAM,QAAQ,WAAW,MAAM;CAC/B,IAAI,QACF,MAAM,MAAM,MAAM;CAEpB,OAAO,MAAM,MAAM;AACrB;AAEA,eAAsB,SACpB,YACA,IACwB;CAExB,OADc,WAAW,MACd,CAAC,CAAC,MAAM,WAAW,YAAY,EAAE,CAAC,CAAC,MAAM;AACtD;AAEA,eAAsB,gBACpB,YACA,UAAoE,CAAC,GAClC;CACnC,MAAM,QAAQ,WAAW,MAAM;CAC/B,IAAI,QAAQ,QACV,MAAM,MAAM,QAAQ,MAAM;CAG5B,OAAO,MAAM,SAAS;EACpB,OAAO,QAAQ;EACf,MAAM,QAAQ;CAChB,CAAC;AACH;AAEA,eAAsB,WACpB,YACA,QACmB;CACnB,MAAM,QAAQ,WAAW,MAAM;CAC/B,IAAI,QACF,MAAM,MAAM,MAAM;CAEpB,OAAQ,MAAM,MAAM,OAAO;AAC7B;AAEA,SAAgB,cACd,YACA,QACA,OACA,QACiB;CAEjB,OADc,WAAW,MAAM,CAAC,CAAC,MAAM,MAC5B,CAAC,CAAC,UAAU,OAAO,MAAM;AACtC;AAEA,SAAgB,cACd,YACA,QACA,OACA,QACiB;CAEjB,OADc,WAAW,MAAM,CAAC,CAAC,MAAM,MAC5B,CAAC,CAAC,UAAU,OAAO,MAAM;AACtC;AAUA,SAAS,cACP,QACA,SACyB;CACzB,OAAO,SAAS,gBAAgB,SAAS,eAAe,MAAM;AAChE;AAEA,SAAS,gBACP,SAC6C;CAC7C,IAAI,CAAC,SACH;CAGF,MAAM,EAAE,eAAe,gBAAgB,GAAG,SAAS;CAEnD,OAAO;AACT;;;;;;;AAQA,eAAsB,iBACpB,YACA,QACA,QACyC;CACzC,MAAM,aAAa,WAAW;CAC9B,MAAM,MAAM,MAAM,WAAW,MAAM,CAAC,CAAC,MAAM,MAAM,CAAC,CAAC,MAAM,UAAU;CAEnE,IAAI,IAAI,WAAW,GACjB,OAAO;CAGT,OAAO;EAAE,GAAG;GAAS,aAAa,SAAS,IAAI,KAAK,EAAE,KAAK,IAAI;CAAE;AACnE;;;;;;AAOA,eAAsB,kBACpB,YACA,QACkC;CAClC,MAAM,SAAS,MAAM,iBAAiB,YAAY,QAAQ,IAAI;CAE9D,IAAI,QACF,OAAO;CAKT,IAAI,MAFiB,WAAW,MAAM,CAAC,CAAC,oBAAoB,CAAC,CAAC,MAAM,MAAM,CAAC,CAAC,OAAO,GAGjF,MAAM,IAAI,MACR,GAAG,WAAW,KAAK,+FACrB;CAGF,OAAO;AACT;;;;;AAMA,eAAsB,cACpB,YACA,QACA,YACA,SACiB;CACjB,MAAM,WAAW,cAAc,QAAQ,OAAO;CAC9C,MAAM,SAAS,SAAS,SACpB,MAAM,kBAAkB,YAAY,QAAQ,IAC5C,MAAM,iBAAiB,YAAY,UAAU,KAAK;CAEtD,IAAI,CAAC,QACH,OAAO;CAGT,MAAM,SAAS,MAAM,WAAW,UAAU,CAAC,CAAC,OAC1C,WAAW,OACX,QACA,YACA,gBAAgB,OAAO,CACzB;CAEA,OAAO,OAAO,iBAAiB,OAAO,iBAAiB;AACzD;;;;;;AAOA,eAAsB,WACpB,YACA,IACA,MACiB;CACjB,MAAM,QAAQ,MAAM,WAAW,MAAM,CAAC,CAAC,MAAM,WAAW,YAAY,EAAE,CAAC,CAAC,MAAM;CAE9E,IAAI,CAAC,OACH,OAAO;CAGT,MAAM,MAAM,IAAI;CAChB,MAAM,MAAM,KAAK;CAEjB,OAAO;AACT;AAEA,eAAsB,qBACpB,YACA,QACA,QACA,SACmB;CACnB,MAAM,aAAa,WAAW;CAC9B,MAAM,aAAa,eAAe,MAAM;CAIxC,MAAM,MAAM,MAAM,WAAW,MAAM,CAAC,CAAC,MAAM,UAAU,CAAC,CAAC,MAAM,UAAU;CAEvE,IAAI,IAAI,WAAW,GAAG;EACpB,IAAI,CAAC,SAAS,QACZ,OAAO,CAAC;EAGV,MAAM,cAAc,YAAY,YAAY,QAAQ;GAAE,GAAG;GAAS,eAAe;EAAK,CAAC;EACvF,OAAO,MAAM,WAAW,MAAM,CAAC,CAAC,MAAM,UAAU,CAAC,CAAC,IAAI;CACxD;CAEA,MAAM,cACJ,YACA,GAAG,aAAa,EAAE,KAAK,IAAI,EAAE,GAC7B,QACA;EAAE,GAAG;EAAS,eAAe;CAAK,CACpC;CAEA,OAAO,MAAM,WAAW,MAAM,CAAC,CAAC,QAAQ,YAAY,GAAG,CAAC,CAAC,IAAI;AAC/D;AAEA,eAAsB,uBACpB,YACA,QACA,QACA,SACwB;CACxB,MAAM,WAAW,cAAc,QAAQ,OAAO;CAC9C,MAAM,SAAS,SAAS,SACpB,MAAM,kBAAkB,YAAY,QAAQ,IAC5C,MAAM,iBAAiB,YAAY,UAAU,IAAI;CAErD,IAAI,CAAC,QAAQ,OAAO;CAEpB,MAAM,SAAS,MAAM,WAAW,UAAU,CAAC,CAAC,iBAC1C,WAAW,OACX,QACA,QACA,gBAAgB,OAAO,CACzB;CACA,IAAI,CAAC,QAAQ,OAAO;CACpB,OAAO,WAAW,QAAQ,MAAiC;AAC7D;AAEA,eAAsB,qBACpB,YACA,QACA,UACwB;CACxB,MAAM,SAAS,MAAM,iBAAiB,YAAY,eAAe,MAAM,GAAG,IAAI;CAE9E,IAAI,CAAC,QAAQ,OAAO;CAEpB,MAAM,SAAS,MAAM,WAAW,UAAU,CAAC,CAAC,QAAQ,WAAW,OAAO,QAAQ,QAAQ;CACtF,IAAI,CAAC,QAAQ,OAAO;CACpB,OAAO,WAAW,QAAQ,MAAiC;AAC7D;AAEA,eAAsB,uBACpB,YACA,QACA,SACwB;CACxB,MAAM,SAAS,MAAM,iBAAiB,YAAY,eAAe,MAAM,GAAG,IAAI;CAE9E,IAAI,CAAC,QACH,OAAO;CAGT,MAAM,SAAS,MAAM,WAAW,UAAU,CAAC,CAAC,iBAAiB,WAAW,OAAO,QAAQ,OAAO;CAE9F,IAAI,CAAC,QACH,OAAO;CAGT,MAAM,QAAQ,WAAW,QAAQ,MAAiC;CAClE,MAAM,aAAa,MAAM;CACzB,OAAO;AACT;AAEA,SAAgB,kBACd,YACY;CACZ,MAAM,MAAM,WAAW;CACvB,IAAI;CAEJ,IAAI,OAAO,QAAQ,UACjB,aAAa,mBAAmB,IAAI,GAAG;MAClC,IAAI,KACT,aAAa;MAEb,aAAa,mBAAmB,IAAI;CAGtC,IAAI,CAAC,WAAW,eAAe,kBAAkB,GAAG;EAClD,MAAM,iBAAiB,WAAW,OAAO,iBAAiB,CAAC;EAC3D,MAAM,qBAAqB,WAAW,iBAAiB,CAAC;EAExD,MAAM,iBAAiB;GACrB,GAAG;GACH,GAAG;EACL;EAEA,IAAI,OAAO,KAAK,cAAc,CAAC,CAAC,SAAS,GACvC,AAAC,WAAmB,mBAAmB,cAAc;EAGvD,AAAC,WAAmB,mBAAmB;CACzC;CAKA,8BAA8B,UAAU;CAExC,OAAO;AACT"}
|
|
@@ -15,6 +15,21 @@ function getModelEvents(ModelClass) {
|
|
|
15
15
|
}
|
|
16
16
|
return events;
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Emit a static event to the class's listeners and then to every ancestor
|
|
20
|
+
* class's listeners up to `Model`, so a listener registered on a shared base
|
|
21
|
+
* (`class AppModel extends Model`) fires for its subclasses too. Ancestors are
|
|
22
|
+
* only read, never given an emitter they don't already have.
|
|
23
|
+
*/
|
|
24
|
+
async function emitStaticEvent(ModelClass, event, model, context) {
|
|
25
|
+
await ModelClass.events().emit(event, model, context);
|
|
26
|
+
let ancestor = Object.getPrototypeOf(ModelClass);
|
|
27
|
+
while (ancestor && ancestor !== Function.prototype) {
|
|
28
|
+
const events = modelEventsRegistry.get(ancestor);
|
|
29
|
+
if (events) await events.emit(event, model, context);
|
|
30
|
+
ancestor = Object.getPrototypeOf(ancestor);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
18
33
|
function cleanupModelEvents(ModelClass) {
|
|
19
34
|
modelEventsRegistry.delete(ModelClass);
|
|
20
35
|
removeModelFromRegistery(ModelClass.name);
|
|
@@ -33,5 +48,5 @@ function getGlobalEvents() {
|
|
|
33
48
|
}
|
|
34
49
|
|
|
35
50
|
//#endregion
|
|
36
|
-
export { cleanupModelEvents, getGlobalEvents, getModelEvents, offStaticEvent, onStaticEvent, onceStaticEvent };
|
|
51
|
+
export { cleanupModelEvents, emitStaticEvent, getGlobalEvents, getModelEvents, offStaticEvent, onStaticEvent, onceStaticEvent };
|
|
37
52
|
//# sourceMappingURL=static-event-methods.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"static-event-methods.mjs","names":[],"sources":["../../../../../../../../cascade/src/model/methods/static-event-methods.ts"],"sourcesContent":["import {\n ModelEvents,\n globalModelEvents,\n type ModelEventListener,\n type ModelEventName,\n} from \"../../events/model-events\";\nimport { removeModelFromRegistery } from \"../register-model\";\nimport type { ChildModel, Model } from \"../model\";\n\n/**\n * Isolated event emitter registry — one ModelEvents instance per model constructor.\n * Encapsulated here so no other module needs to know it exists.\n */\nconst modelEventsRegistry = new WeakMap<any, any>();\n\nexport function getModelEvents<TModel extends Model>(\n ModelClass: any,\n): ModelEvents<TModel> {\n let events = modelEventsRegistry.get(ModelClass);\n if (!events) {\n events = new ModelEvents<TModel>();\n modelEventsRegistry.set(ModelClass, events);\n }\n return events as ModelEvents<TModel>;\n}\n\nexport function cleanupModelEvents(ModelClass: any): void {\n modelEventsRegistry.delete(ModelClass);\n removeModelFromRegistery(ModelClass.name);\n}\n\nexport function onStaticEvent<TModel extends Model = Model, TContext = unknown>(\n ModelClass: ChildModel<TModel>,\n event: ModelEventName,\n listener: ModelEventListener<TModel, TContext>,\n): () => void {\n return ModelClass.events<TModel>().on(event, listener);\n}\n\nexport function onceStaticEvent<TModel extends Model = Model, TContext = unknown>(\n ModelClass: ChildModel<TModel>,\n event: ModelEventName,\n listener: ModelEventListener<TModel, TContext>,\n): () => void {\n return ModelClass.events<TModel>().once(event, listener);\n}\n\nexport function offStaticEvent<TModel extends Model = Model, TContext = unknown>(\n ModelClass: ChildModel<TModel>,\n event: ModelEventName,\n listener: ModelEventListener<TModel, TContext>,\n): void {\n ModelClass.events<TModel>().off(event, listener);\n}\n\nexport function getGlobalEvents(): ModelEvents<Model> {\n return globalModelEvents;\n}\n"],"mappings":";;;;;;;;AAaA,MAAM,sCAAsB,IAAI,QAAkB;AAElD,SAAgB,eACd,YACqB;CACrB,IAAI,SAAS,oBAAoB,IAAI,UAAU;CAC/C,IAAI,CAAC,QAAQ;EACX,SAAS,IAAI,YAAoB;EACjC,oBAAoB,IAAI,YAAY,MAAM;CAC5C;CACA,OAAO;AACT;AAEA,SAAgB,mBAAmB,YAAuB;CACxD,oBAAoB,OAAO,UAAU;CACrC,yBAAyB,WAAW,IAAI;AAC1C;AAEA,SAAgB,cACd,YACA,OACA,UACY;CACZ,OAAO,WAAW,OAAe,CAAC,CAAC,GAAG,OAAO,QAAQ;AACvD;AAEA,SAAgB,gBACd,YACA,OACA,UACY;CACZ,OAAO,WAAW,OAAe,CAAC,CAAC,KAAK,OAAO,QAAQ;AACzD;AAEA,SAAgB,eACd,YACA,OACA,UACM;CACN,WAAW,OAAe,CAAC,CAAC,IAAI,OAAO,QAAQ;AACjD;AAEA,SAAgB,kBAAsC;CACpD,OAAO;AACT"}
|
|
1
|
+
{"version":3,"file":"static-event-methods.mjs","names":[],"sources":["../../../../../../../../cascade/src/model/methods/static-event-methods.ts"],"sourcesContent":["import {\n ModelEvents,\n globalModelEvents,\n type ModelEventListener,\n type ModelEventName,\n} from \"../../events/model-events\";\nimport { removeModelFromRegistery } from \"../register-model\";\nimport type { ChildModel, Model } from \"../model\";\n\n/**\n * Isolated event emitter registry — one ModelEvents instance per model constructor.\n * Encapsulated here so no other module needs to know it exists.\n */\nconst modelEventsRegistry = new WeakMap<any, any>();\n\nexport function getModelEvents<TModel extends Model>(\n ModelClass: any,\n): ModelEvents<TModel> {\n let events = modelEventsRegistry.get(ModelClass);\n if (!events) {\n events = new ModelEvents<TModel>();\n modelEventsRegistry.set(ModelClass, events);\n }\n return events as ModelEvents<TModel>;\n}\n\n/**\n * Emit a static event to the class's listeners and then to every ancestor\n * class's listeners up to `Model`, so a listener registered on a shared base\n * (`class AppModel extends Model`) fires for its subclasses too. Ancestors are\n * only read, never given an emitter they don't already have.\n */\nexport async function emitStaticEvent<TContext = unknown>(\n ModelClass: any,\n event: ModelEventName,\n model: any,\n context: TContext,\n): Promise<void> {\n await ModelClass.events().emit(event, model, context);\n\n let ancestor = Object.getPrototypeOf(ModelClass);\n\n while (ancestor && ancestor !== Function.prototype) {\n const events = modelEventsRegistry.get(ancestor);\n\n if (events) {\n await events.emit(event, model, context);\n }\n\n ancestor = Object.getPrototypeOf(ancestor);\n }\n}\n\nexport function cleanupModelEvents(ModelClass: any): void {\n modelEventsRegistry.delete(ModelClass);\n removeModelFromRegistery(ModelClass.name);\n}\n\nexport function onStaticEvent<TModel extends Model = Model, TContext = unknown>(\n ModelClass: ChildModel<TModel>,\n event: ModelEventName,\n listener: ModelEventListener<TModel, TContext>,\n): () => void {\n return ModelClass.events<TModel>().on(event, listener);\n}\n\nexport function onceStaticEvent<TModel extends Model = Model, TContext = unknown>(\n ModelClass: ChildModel<TModel>,\n event: ModelEventName,\n listener: ModelEventListener<TModel, TContext>,\n): () => void {\n return ModelClass.events<TModel>().once(event, listener);\n}\n\nexport function offStaticEvent<TModel extends Model = Model, TContext = unknown>(\n ModelClass: ChildModel<TModel>,\n event: ModelEventName,\n listener: ModelEventListener<TModel, TContext>,\n): void {\n ModelClass.events<TModel>().off(event, listener);\n}\n\nexport function getGlobalEvents(): ModelEvents<Model> {\n return globalModelEvents;\n}\n"],"mappings":";;;;;;;;AAaA,MAAM,sCAAsB,IAAI,QAAkB;AAElD,SAAgB,eACd,YACqB;CACrB,IAAI,SAAS,oBAAoB,IAAI,UAAU;CAC/C,IAAI,CAAC,QAAQ;EACX,SAAS,IAAI,YAAoB;EACjC,oBAAoB,IAAI,YAAY,MAAM;CAC5C;CACA,OAAO;AACT;;;;;;;AAQA,eAAsB,gBACpB,YACA,OACA,OACA,SACe;CACf,MAAM,WAAW,OAAO,CAAC,CAAC,KAAK,OAAO,OAAO,OAAO;CAEpD,IAAI,WAAW,OAAO,eAAe,UAAU;CAE/C,OAAO,YAAY,aAAa,SAAS,WAAW;EAClD,MAAM,SAAS,oBAAoB,IAAI,QAAQ;EAE/C,IAAI,QACF,MAAM,OAAO,KAAK,OAAO,OAAO,OAAO;EAGzC,WAAW,OAAO,eAAe,QAAQ;CAC3C;AACF;AAEA,SAAgB,mBAAmB,YAAuB;CACxD,oBAAoB,OAAO,UAAU;CACrC,yBAAyB,WAAW,IAAI;AAC1C;AAEA,SAAgB,cACd,YACA,OACA,UACY;CACZ,OAAO,WAAW,OAAe,CAAC,CAAC,GAAG,OAAO,QAAQ;AACvD;AAEA,SAAgB,gBACd,YACA,OACA,UACY;CACZ,OAAO,WAAW,OAAe,CAAC,CAAC,KAAK,OAAO,QAAQ;AACzD;AAEA,SAAgB,eACd,YACA,OACA,UACM;CACN,WAAW,OAAe,CAAC,CAAC,IAAI,OAAO,QAAQ;AACjD;AAEA,SAAgB,kBAAsC;CACpD,OAAO;AACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"write-methods.d.mts","names":[],"sources":["../../../../../../../../cascade/src/model/methods/write-methods.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"write-methods.d.mts","names":[],"sources":["../../../../../../../../cascade/src/model/methods/write-methods.ts"],"mappings":";;;;KAqBY,iBAAA;;;;;;EAMV,SAAA;;;;;;;;;;;;;;EAeA,IAAI;AAAA"}
|