@warlock.js/cascade 5.24.0 → 5.26.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 +30 -5
- package/README.md +101 -101
- package/bin/cascade.js +0 -0
- package/cjs/index.cjs +307 -88
- package/cjs/index.cjs.map +1 -1
- package/esm/contracts/database-driver.contract.d.mts +22 -1
- package/esm/contracts/database-driver.contract.d.mts.map +1 -1
- package/esm/contracts/index.d.mts +1 -1
- package/esm/contracts/query-builder.contract.d.mts +7 -0
- package/esm/contracts/query-builder.contract.d.mts.map +1 -1
- package/esm/data-source/data-source-registry.d.mts +8 -6
- package/esm/data-source/data-source-registry.d.mts.map +1 -1
- package/esm/data-source/data-source-registry.mjs +7 -1
- package/esm/data-source/data-source-registry.mjs.map +1 -1
- package/esm/drivers/mongodb/mongodb-driver.d.mts +13 -0
- package/esm/drivers/mongodb/mongodb-driver.d.mts.map +1 -1
- package/esm/drivers/mongodb/mongodb-driver.mjs +71 -2
- package/esm/drivers/mongodb/mongodb-driver.mjs.map +1 -1
- package/esm/drivers/mongodb/mongodb-query-builder.d.mts +5 -0
- package/esm/drivers/mongodb/mongodb-query-builder.d.mts.map +1 -1
- package/esm/drivers/mongodb/mongodb-query-builder.mjs +11 -1
- package/esm/drivers/mongodb/mongodb-query-builder.mjs.map +1 -1
- package/esm/drivers/postgres/postgres-driver.d.mts +9 -0
- package/esm/drivers/postgres/postgres-driver.d.mts.map +1 -1
- package/esm/drivers/postgres/postgres-driver.mjs +55 -7
- package/esm/drivers/postgres/postgres-driver.mjs.map +1 -1
- package/esm/drivers/postgres/postgres-query-builder.d.mts +5 -0
- package/esm/drivers/postgres/postgres-query-builder.d.mts.map +1 -1
- package/esm/drivers/postgres/postgres-query-builder.mjs +15 -0
- package/esm/drivers/postgres/postgres-query-builder.mjs.map +1 -1
- package/esm/drivers/postgres/postgres-query-parser.d.mts +1 -4
- package/esm/drivers/postgres/postgres-query-parser.d.mts.map +1 -1
- package/esm/drivers/postgres/postgres-query-parser.mjs +4 -5
- package/esm/drivers/postgres/postgres-query-parser.mjs.map +1 -1
- package/esm/errors/unsafe-filter.error.mjs.map +1 -1
- package/esm/errors/unsafe-raw-expression.error.mjs.map +1 -1
- package/esm/errors/unsupported-lock-for-update.error.d.mts +11 -0
- package/esm/errors/unsupported-lock-for-update.error.d.mts.map +1 -0
- package/esm/errors/unsupported-lock-for-update.error.mjs +16 -0
- package/esm/errors/unsupported-lock-for-update.error.mjs.map +1 -0
- package/esm/events/model-events.mjs.map +1 -1
- package/esm/index.d.mts +4 -3
- package/esm/index.mjs +2 -1
- package/esm/migration/index.d.mts +1 -1
- package/esm/migration/migration-order.mjs +6 -3
- package/esm/migration/migration-order.mjs.map +1 -1
- package/esm/migration/migration-runner.d.mts +5 -3
- package/esm/migration/migration-runner.d.mts.map +1 -1
- package/esm/migration/migration-runner.mjs +3 -2
- package/esm/migration/migration-runner.mjs.map +1 -1
- package/esm/migration/migration.d.mts +11 -1
- package/esm/migration/migration.d.mts.map +1 -1
- package/esm/migration/migration.mjs +45 -2
- package/esm/migration/migration.mjs.map +1 -1
- package/esm/migration/sql-grammar.mjs.map +1 -1
- package/esm/model/generate-model-id.mjs +23 -0
- package/esm/model/generate-model-id.mjs.map +1 -0
- package/esm/model/methods/delete-methods.mjs.map +1 -1
- package/esm/model/methods/meta-methods.mjs.map +1 -1
- package/esm/model/methods/query-methods.mjs +38 -11
- package/esm/model/methods/query-methods.mjs.map +1 -1
- package/esm/model/methods/write-methods.mjs.map +1 -1
- package/esm/model/model.d.mts +2 -2
- package/esm/model/model.d.mts.map +1 -1
- package/esm/model/model.mjs +1 -1
- package/esm/model/model.mjs.map +1 -1
- package/esm/query-builder/query-builder.d.mts +5 -0
- package/esm/query-builder/query-builder.d.mts.map +1 -1
- package/esm/query-builder/query-builder.mjs +16 -1
- package/esm/query-builder/query-builder.mjs.map +1 -1
- package/esm/utils/sanitize-filter.mjs.map +1 -1
- package/esm/validation/plugins/database-rules-plugin.mjs.map +1 -1
- package/esm/writer/database-writer.d.mts +0 -33
- package/esm/writer/database-writer.d.mts.map +1 -1
- package/esm/writer/database-writer.mjs +3 -52
- package/esm/writer/database-writer.mjs.map +1 -1
- package/llms-full.txt +2911 -2864
- package/package.json +5 -5
- 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/manage-data-sources/SKILL.md +177 -140
- package/skills/manage-transactions/SKILL.md +215 -205
- 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/cjs/index.cjs
CHANGED
|
@@ -317,7 +317,7 @@ var DataSourceRegistry = class {
|
|
|
317
317
|
defaultSource;
|
|
318
318
|
defaultIsExplicit = false;
|
|
319
319
|
events = new node_events.EventEmitter();
|
|
320
|
-
/** Replaced sources; their forwarded driver events are ignored
|
|
320
|
+
/** Replaced sources; their forwarded driver events are ignored. */
|
|
321
321
|
detached = /* @__PURE__ */ new WeakSet();
|
|
322
322
|
/** In-flight `getOrRegister` factories, so concurrent callers share one creation. */
|
|
323
323
|
pending = /* @__PURE__ */ new Map();
|
|
@@ -368,6 +368,12 @@ var DataSourceRegistry = class {
|
|
|
368
368
|
source.driver.on("disconnected", () => {
|
|
369
369
|
if (!this.detached.has(source)) this.events.emit("disconnected", source);
|
|
370
370
|
});
|
|
371
|
+
source.driver.on("query", (event) => {
|
|
372
|
+
if (!this.detached.has(source)) this.events.emit("query", {
|
|
373
|
+
...event,
|
|
374
|
+
connection: source.name
|
|
375
|
+
});
|
|
376
|
+
});
|
|
371
377
|
return source;
|
|
372
378
|
}
|
|
373
379
|
/**
|
|
@@ -670,6 +676,20 @@ var UnsupportedLeanOperationError = class UnsupportedLeanOperationError extends
|
|
|
670
676
|
}
|
|
671
677
|
};
|
|
672
678
|
|
|
679
|
+
//#endregion
|
|
680
|
+
//#region ../cascade/src/errors/unsupported-lock-for-update.error.ts
|
|
681
|
+
/**
|
|
682
|
+
* Error thrown when `lockForUpdate()` is requested on MongoDB, whose document
|
|
683
|
+
* model has no row-level `SELECT ... FOR UPDATE` equivalent.
|
|
684
|
+
*/
|
|
685
|
+
var UnsupportedLockForUpdateError = class UnsupportedLockForUpdateError extends Error {
|
|
686
|
+
constructor() {
|
|
687
|
+
super("lockForUpdate() is unsupported on MongoDB. MongoDB has no row-level SELECT locking. Use a conditional atomic update instead, e.g. Model.atomic({ id, leaseUntil: { $lt: now } }, { $set: { status: \"processing\" } }, { trustedFilter: true }).");
|
|
688
|
+
this.name = "UnsupportedLockForUpdateError";
|
|
689
|
+
if (Error.captureStackTrace) Error.captureStackTrace(this, UnsupportedLockForUpdateError);
|
|
690
|
+
}
|
|
691
|
+
};
|
|
692
|
+
|
|
673
693
|
//#endregion
|
|
674
694
|
//#region ../cascade/src/errors/unsupported-query-operation.error.ts
|
|
675
695
|
/**
|
|
@@ -4045,6 +4065,27 @@ function offModelEvent(model, event, listener) {
|
|
|
4045
4065
|
model.events.off(event, listener);
|
|
4046
4066
|
}
|
|
4047
4067
|
|
|
4068
|
+
//#endregion
|
|
4069
|
+
//#region ../cascade/src/model/generate-model-id.ts
|
|
4070
|
+
/**
|
|
4071
|
+
* Generate the next model id using the model's configured sequence options.
|
|
4072
|
+
*
|
|
4073
|
+
* This is shared by normal inserts and MongoDB upserts so both write paths
|
|
4074
|
+
* resolve random and configured sequence settings identically.
|
|
4075
|
+
*/
|
|
4076
|
+
async function generateModelId(ModelClass) {
|
|
4077
|
+
const idGenerator = ModelClass.getDataSource().idGenerator;
|
|
4078
|
+
if (!idGenerator) return;
|
|
4079
|
+
const randomInt = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;
|
|
4080
|
+
const initialId = ModelClass.initialId ? ModelClass.initialId : ModelClass.randomInitialId ? typeof ModelClass.randomInitialId === "function" ? ModelClass.randomInitialId() : randomInt(1e4, 499999) : 1;
|
|
4081
|
+
const incrementIdBy = ModelClass.randomIncrement ? typeof ModelClass.randomIncrement === "function" ? ModelClass.randomIncrement() : randomInt(1, 10) : ModelClass.incrementIdBy || 1;
|
|
4082
|
+
return await idGenerator.generateNextId({
|
|
4083
|
+
table: ModelClass.table,
|
|
4084
|
+
initialId,
|
|
4085
|
+
incrementIdBy
|
|
4086
|
+
});
|
|
4087
|
+
}
|
|
4088
|
+
|
|
4048
4089
|
//#endregion
|
|
4049
4090
|
//#region ../cascade/src/validation/database-writer-validation-error.ts
|
|
4050
4091
|
/**
|
|
@@ -4708,15 +4749,8 @@ var DatabaseWriter = class {
|
|
|
4708
4749
|
*/
|
|
4709
4750
|
async generateNextId() {
|
|
4710
4751
|
if (!this.ctor.autoGenerateId || this.model.get("id")) return;
|
|
4711
|
-
const
|
|
4712
|
-
if (
|
|
4713
|
-
const initialId = this.resolveInitialId();
|
|
4714
|
-
const incrementIdBy = this.resolveIncrementBy();
|
|
4715
|
-
const id = await idGenerator.generateNextId({
|
|
4716
|
-
table: this.table,
|
|
4717
|
-
initialId,
|
|
4718
|
-
incrementIdBy
|
|
4719
|
-
});
|
|
4752
|
+
const id = await generateModelId(this.ctor);
|
|
4753
|
+
if (id === void 0) return;
|
|
4720
4754
|
this.model.set("id", id);
|
|
4721
4755
|
}
|
|
4722
4756
|
/**
|
|
@@ -4763,49 +4797,6 @@ var DatabaseWriter = class {
|
|
|
4763
4797
|
return operations;
|
|
4764
4798
|
}
|
|
4765
4799
|
/**
|
|
4766
|
-
* Resolve the initial ID from model configuration.
|
|
4767
|
-
*
|
|
4768
|
-
* Priority:
|
|
4769
|
-
* 1. Model.initialId (explicit value)
|
|
4770
|
-
* 2. Model.randomInitialId (random or function)
|
|
4771
|
-
* 3. Default: 1
|
|
4772
|
-
*
|
|
4773
|
-
* @returns The initial ID value
|
|
4774
|
-
* @private
|
|
4775
|
-
*/
|
|
4776
|
-
resolveInitialId() {
|
|
4777
|
-
if (this.ctor.initialId) return this.ctor.initialId;
|
|
4778
|
-
if (this.ctor.randomInitialId) return typeof this.ctor.randomInitialId === "function" ? this.ctor.randomInitialId() : this.randomInt(1e4, 499999);
|
|
4779
|
-
return 1;
|
|
4780
|
-
}
|
|
4781
|
-
/**
|
|
4782
|
-
* Resolve the increment value from model configuration.
|
|
4783
|
-
*
|
|
4784
|
-
* Priority:
|
|
4785
|
-
* 1. Model.incrementIdBy (explicit value)
|
|
4786
|
-
* 2. Model.randomIncrement (random or function)
|
|
4787
|
-
* 3. Default: 1
|
|
4788
|
-
*
|
|
4789
|
-
* @returns The increment value
|
|
4790
|
-
* @private
|
|
4791
|
-
*/
|
|
4792
|
-
resolveIncrementBy() {
|
|
4793
|
-
if (this.ctor.randomIncrement) return typeof this.ctor.randomIncrement === "function" ? this.ctor.randomIncrement() : this.randomInt(1, 10);
|
|
4794
|
-
if (this.ctor.incrementIdBy) return this.ctor.incrementIdBy;
|
|
4795
|
-
return 1;
|
|
4796
|
-
}
|
|
4797
|
-
/**
|
|
4798
|
-
* Generate a random integer between min and max (inclusive).
|
|
4799
|
-
*
|
|
4800
|
-
* @param min - Minimum value
|
|
4801
|
-
* @param max - Maximum value
|
|
4802
|
-
* @returns Random integer
|
|
4803
|
-
* @private
|
|
4804
|
-
*/
|
|
4805
|
-
randomInt(min, max) {
|
|
4806
|
-
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
4807
|
-
}
|
|
4808
|
-
/**
|
|
4809
4800
|
* Trigger sync operations after successful save.
|
|
4810
4801
|
*
|
|
4811
4802
|
* Emits a model.updated event that ModelSyncOperation listens to.
|
|
@@ -5318,15 +5309,39 @@ function toDriverOptions(options) {
|
|
|
5318
5309
|
const { trustedFilter: _trustedFilter, ...rest } = options;
|
|
5319
5310
|
return rest;
|
|
5320
5311
|
}
|
|
5312
|
+
function applyWriteFilter(query, filter, trustedFilter) {
|
|
5313
|
+
if (trustedFilter && typeof query.whereTrusted === "function") return query.whereTrusted(filter);
|
|
5314
|
+
return query.where(filter);
|
|
5315
|
+
}
|
|
5316
|
+
/**
|
|
5317
|
+
* Add the same generated `id` that normal Mongo inserts receive to an upsert's
|
|
5318
|
+
* insert branch. `$setOnInsert` keeps an existing document's id immutable.
|
|
5319
|
+
*/
|
|
5320
|
+
async function withAutoGeneratedUpsertId(ModelClass, filter, operations, options) {
|
|
5321
|
+
if (!options?.upsert || !ModelClass.autoGenerateId || ModelClass.getDriver().name !== "mongodb" || Array.isArray(operations)) return operations;
|
|
5322
|
+
const filterId = filter.id;
|
|
5323
|
+
const filterConstrainsId = Object.hasOwn(filter, "id") && (filterId === null || typeof filterId !== "object" || !Array.isArray(filterId) && Object.hasOwn(filterId, "$eq"));
|
|
5324
|
+
const updateSuppliesId = Object.hasOwn(operations.$set ?? {}, "id") || Object.hasOwn(operations.$setOnInsert ?? {}, "id");
|
|
5325
|
+
if (filterConstrainsId || updateSuppliesId) return operations;
|
|
5326
|
+
const id = await generateModelId(ModelClass);
|
|
5327
|
+
if (id === void 0) return operations;
|
|
5328
|
+
return {
|
|
5329
|
+
...operations,
|
|
5330
|
+
$setOnInsert: {
|
|
5331
|
+
...operations.$setOnInsert,
|
|
5332
|
+
id
|
|
5333
|
+
}
|
|
5334
|
+
};
|
|
5335
|
+
}
|
|
5321
5336
|
/**
|
|
5322
5337
|
* Pin a driver-level write to the rows the model's global scopes (tenant, soft
|
|
5323
5338
|
* delete) allow. The driver write APIs take a raw filter and never see scopes,
|
|
5324
5339
|
* so the target ids are resolved through the scoped query builder and added to
|
|
5325
5340
|
* the filter. `null` means no visible row matches: the write must be a no-op.
|
|
5326
5341
|
*/
|
|
5327
|
-
async function scopeWriteFilter(ModelClass, filter, single) {
|
|
5342
|
+
async function scopeWriteFilter(ModelClass, filter, single, trustedFilter = false) {
|
|
5328
5343
|
const primaryKey = ModelClass.primaryKey;
|
|
5329
|
-
const ids = await ModelClass.query()
|
|
5344
|
+
const ids = await applyWriteFilter(ModelClass.query(), filter, trustedFilter).pluck(primaryKey);
|
|
5330
5345
|
if (ids.length === 0) return null;
|
|
5331
5346
|
return {
|
|
5332
5347
|
...filter,
|
|
@@ -5338,10 +5353,10 @@ async function scopeWriteFilter(ModelClass, filter, single) {
|
|
|
5338
5353
|
* the driver would insert or, worse, update a row a scope hides (another
|
|
5339
5354
|
* tenant's, a soft-deleted one), so a hidden match is refused.
|
|
5340
5355
|
*/
|
|
5341
|
-
async function scopeUpsertFilter(ModelClass, filter) {
|
|
5342
|
-
const scoped = await scopeWriteFilter(ModelClass, filter, true);
|
|
5356
|
+
async function scopeUpsertFilter(ModelClass, filter, trustedFilter = false) {
|
|
5357
|
+
const scoped = await scopeWriteFilter(ModelClass, filter, true, trustedFilter);
|
|
5343
5358
|
if (scoped) return scoped;
|
|
5344
|
-
if (await ModelClass.query().withoutGlobalScopes()
|
|
5359
|
+
if (await applyWriteFilter(ModelClass.query().withoutGlobalScopes(), filter, trustedFilter).exists()) throw new Error(`${ModelClass.name}: upsert matches a row hidden by a global scope (tenant or soft delete); refusing to touch it.`);
|
|
5345
5360
|
return filter;
|
|
5346
5361
|
}
|
|
5347
5362
|
/**
|
|
@@ -5350,9 +5365,10 @@ async function scopeUpsertFilter(ModelClass, filter) {
|
|
|
5350
5365
|
*/
|
|
5351
5366
|
async function performAtomic(ModelClass, filter, operations, options) {
|
|
5352
5367
|
const resolved = resolveFilter(filter, options);
|
|
5353
|
-
const scoped = options?.upsert ? await scopeUpsertFilter(ModelClass, resolved) : await scopeWriteFilter(ModelClass, resolved, false);
|
|
5368
|
+
const scoped = options?.upsert ? await scopeUpsertFilter(ModelClass, resolved, options?.trustedFilter) : await scopeWriteFilter(ModelClass, resolved, false, options?.trustedFilter);
|
|
5354
5369
|
if (!scoped) return 0;
|
|
5355
|
-
const
|
|
5370
|
+
const upsertOperations = await withAutoGeneratedUpsertId(ModelClass, scoped, operations, options);
|
|
5371
|
+
const result = await ModelClass.getDriver().atomic(ModelClass.table, scoped, upsertOperations, toDriverOptions(options));
|
|
5356
5372
|
return result.modifiedCount + (result.upsertedCount ?? 0);
|
|
5357
5373
|
}
|
|
5358
5374
|
/**
|
|
@@ -5369,8 +5385,8 @@ async function updateById(ModelClass, id, data) {
|
|
|
5369
5385
|
}
|
|
5370
5386
|
async function findAndUpdateRecords(ModelClass, filter, update, options) {
|
|
5371
5387
|
const primaryKey = ModelClass.primaryKey;
|
|
5372
|
-
const safeFilter =
|
|
5373
|
-
const ids = await ModelClass.query()
|
|
5388
|
+
const safeFilter = resolveFilter(filter, options);
|
|
5389
|
+
const ids = await applyWriteFilter(ModelClass.query(), safeFilter, options?.trustedFilter ?? false).pluck(primaryKey);
|
|
5374
5390
|
if (ids.length === 0) {
|
|
5375
5391
|
if (!options?.upsert) return [];
|
|
5376
5392
|
await performAtomic(ModelClass, safeFilter, update, {
|
|
@@ -5387,9 +5403,10 @@ async function findAndUpdateRecords(ModelClass, filter, update, options) {
|
|
|
5387
5403
|
}
|
|
5388
5404
|
async function findOneAndUpdateRecord(ModelClass, filter, update, options) {
|
|
5389
5405
|
const resolved = resolveFilter(filter, options);
|
|
5390
|
-
const scoped = options?.upsert ? await scopeUpsertFilter(ModelClass, resolved) : await scopeWriteFilter(ModelClass, resolved, true);
|
|
5406
|
+
const scoped = options?.upsert ? await scopeUpsertFilter(ModelClass, resolved, options?.trustedFilter) : await scopeWriteFilter(ModelClass, resolved, true, options?.trustedFilter);
|
|
5391
5407
|
if (!scoped) return null;
|
|
5392
|
-
const
|
|
5408
|
+
const upsertUpdate = await withAutoGeneratedUpsertId(ModelClass, scoped, update, options);
|
|
5409
|
+
const result = await ModelClass.getDriver().findOneAndUpdate(ModelClass.table, scoped, upsertUpdate, toDriverOptions(options));
|
|
5393
5410
|
if (!result) return null;
|
|
5394
5411
|
return ModelClass.hydrate(result);
|
|
5395
5412
|
}
|
|
@@ -7283,7 +7300,7 @@ var Model = class Model {
|
|
|
7283
7300
|
* Find one and update multiple records that matches the provided filter and return the updated record
|
|
7284
7301
|
* @param filter - Filter conditions
|
|
7285
7302
|
* @param update - Update operators, or an aggregation pipeline (MongoDB only)
|
|
7286
|
-
* @param options - `upsert`, `arrayFilters` (MongoDB only)
|
|
7303
|
+
* @param options - `upsert`, `arrayFilters` (MongoDB only), `trustedFilter`
|
|
7287
7304
|
* @returns The updated records
|
|
7288
7305
|
*/
|
|
7289
7306
|
static async findAndUpdate(filter, update, options) {
|
|
@@ -9492,6 +9509,21 @@ var QueryBuilder = class QueryBuilder {
|
|
|
9492
9509
|
}
|
|
9493
9510
|
return this;
|
|
9494
9511
|
}
|
|
9512
|
+
/**
|
|
9513
|
+
* Add a code-authored object filter without the `$`-operator safety guard.
|
|
9514
|
+
* Never use this with request input; callers must use where() for that.
|
|
9515
|
+
*/
|
|
9516
|
+
whereTrusted(conditions) {
|
|
9517
|
+
for (const [field, value] of Object.entries(conditions)) {
|
|
9518
|
+
assertDefined(value, field);
|
|
9519
|
+
this.addOperation("where", {
|
|
9520
|
+
field,
|
|
9521
|
+
operator: "=",
|
|
9522
|
+
value
|
|
9523
|
+
});
|
|
9524
|
+
}
|
|
9525
|
+
return this;
|
|
9526
|
+
}
|
|
9495
9527
|
orWhere(...args) {
|
|
9496
9528
|
if (args.length === 1 && typeof args[0] === "function") {
|
|
9497
9529
|
const sub = this.subQuery();
|
|
@@ -10466,7 +10498,7 @@ var QueryBuilder = class QueryBuilder {
|
|
|
10466
10498
|
}
|
|
10467
10499
|
/** Raw GROUP BY expression. */
|
|
10468
10500
|
groupByRaw(expression, bindings) {
|
|
10469
|
-
this.addOperation("
|
|
10501
|
+
this.addOperation("groupByRaw", {
|
|
10470
10502
|
expression,
|
|
10471
10503
|
bindings: bindings ?? []
|
|
10472
10504
|
});
|
|
@@ -12362,6 +12394,15 @@ var MongoQueryBuilder = class MongoQueryBuilder extends QueryBuilder {
|
|
|
12362
12394
|
this.addWhereClause("where", args);
|
|
12363
12395
|
return this;
|
|
12364
12396
|
}
|
|
12397
|
+
/**
|
|
12398
|
+
* Add a code-authored MongoDB filter without the `$`-operator safety guard.
|
|
12399
|
+
* Never use this with request input; callers must use where() for that.
|
|
12400
|
+
*/
|
|
12401
|
+
whereTrusted(conditions) {
|
|
12402
|
+
for (const [field, value] of Object.entries(conditions)) assertDefined(value, field);
|
|
12403
|
+
this.operationsHelper.addMatchOperation("where:object", conditions);
|
|
12404
|
+
return this;
|
|
12405
|
+
}
|
|
12365
12406
|
orWhere(...args) {
|
|
12366
12407
|
this.addWhereClause("orWhere", args);
|
|
12367
12408
|
return this;
|
|
@@ -13583,7 +13624,7 @@ var MongoQueryBuilder = class MongoQueryBuilder extends QueryBuilder {
|
|
|
13583
13624
|
* keeps a queue-claim pattern from silently running unlocked.
|
|
13584
13625
|
*/
|
|
13585
13626
|
lockForUpdate() {
|
|
13586
|
-
throw new
|
|
13627
|
+
throw new UnsupportedLockForUpdateError();
|
|
13587
13628
|
}
|
|
13588
13629
|
/**
|
|
13589
13630
|
* Executes a callback with the query builder without breaking the chain.
|
|
@@ -14390,6 +14431,7 @@ var MongoDbDriver = class {
|
|
|
14390
14431
|
transactionOptions;
|
|
14391
14432
|
idGeneratorInstance;
|
|
14392
14433
|
_blueprint;
|
|
14434
|
+
monitoredCommands = /* @__PURE__ */ new Map();
|
|
14393
14435
|
get blueprint() {
|
|
14394
14436
|
if (!this._blueprint) this._blueprint = new MongoDBBlueprint(this.database);
|
|
14395
14437
|
return this._blueprint;
|
|
@@ -14517,13 +14559,18 @@ var MongoDbDriver = class {
|
|
|
14517
14559
|
_warlock_js_logger.log.warn("database.mongodb", "connection", "Disconnected from database");
|
|
14518
14560
|
}
|
|
14519
14561
|
});
|
|
14562
|
+
client.on("commandStarted", (event) => this.commandStarted(event));
|
|
14563
|
+
client.on("commandSucceeded", (event) => this.commandSucceeded(event));
|
|
14564
|
+
client.on("commandFailed", (event) => this.commandFailed(event));
|
|
14520
14565
|
if (this.config.logging) {
|
|
14521
14566
|
const ignoredCommands = [
|
|
14522
14567
|
"isMaster",
|
|
14523
14568
|
"hello",
|
|
14524
14569
|
"ping",
|
|
14525
14570
|
"saslStart",
|
|
14526
|
-
"saslContinue"
|
|
14571
|
+
"saslContinue",
|
|
14572
|
+
"endSessions",
|
|
14573
|
+
"buildInfo"
|
|
14527
14574
|
];
|
|
14528
14575
|
client.on("commandStarted", (event) => {
|
|
14529
14576
|
if (ignoredCommands.includes(event.commandName)) return;
|
|
@@ -14580,6 +14627,10 @@ var MongoDbDriver = class {
|
|
|
14580
14627
|
on(event, listener) {
|
|
14581
14628
|
this.events.on(event, listener);
|
|
14582
14629
|
}
|
|
14630
|
+
/** Remove a previously registered driver event listener. */
|
|
14631
|
+
off(event, listener) {
|
|
14632
|
+
this.events.off(event, listener);
|
|
14633
|
+
}
|
|
14583
14634
|
/**
|
|
14584
14635
|
* Insert a single document into the given collection.
|
|
14585
14636
|
*/
|
|
@@ -14896,7 +14947,7 @@ var MongoDbDriver = class {
|
|
|
14896
14947
|
*/
|
|
14897
14948
|
buildClientOptions() {
|
|
14898
14949
|
const baseOptions = { ...this.config.clientOptions ?? {} };
|
|
14899
|
-
if (
|
|
14950
|
+
if (baseOptions.monitorCommands === void 0) baseOptions.monitorCommands = true;
|
|
14900
14951
|
if (this.config.username && !baseOptions.auth) baseOptions.auth = {
|
|
14901
14952
|
username: this.config.username,
|
|
14902
14953
|
password: this.config.password
|
|
@@ -14910,6 +14961,65 @@ var MongoDbDriver = class {
|
|
|
14910
14961
|
emit(event, ...args) {
|
|
14911
14962
|
this.events.emit(event, ...args);
|
|
14912
14963
|
}
|
|
14964
|
+
commandStarted(event) {
|
|
14965
|
+
if (this.ignoredCommand(event.commandName) || this.events.listenerCount("query") === 0) return;
|
|
14966
|
+
const started = performance.now();
|
|
14967
|
+
const { lsid: _lsid, $clusterTime: _clusterTime, $db: _database, ...pipeline } = event.command;
|
|
14968
|
+
const collection = event.command[event.commandName];
|
|
14969
|
+
this.monitoredCommands.set(event.requestId, {
|
|
14970
|
+
connection: this.name,
|
|
14971
|
+
driver: "mongodb",
|
|
14972
|
+
command: event.commandName,
|
|
14973
|
+
collection: typeof collection === "string" ? collection : void 0,
|
|
14974
|
+
pipeline,
|
|
14975
|
+
started,
|
|
14976
|
+
startedAt: performance.timeOrigin + started
|
|
14977
|
+
});
|
|
14978
|
+
}
|
|
14979
|
+
commandSucceeded(event) {
|
|
14980
|
+
const command = this.monitoredCommands.get(event.requestId);
|
|
14981
|
+
if (!command) return;
|
|
14982
|
+
this.monitoredCommands.delete(event.requestId);
|
|
14983
|
+
const { started, ...query } = command;
|
|
14984
|
+
const rowCount = event.reply?.n ?? event.reply?.cursor?.firstBatch?.length;
|
|
14985
|
+
this.emitQueryEvent({
|
|
14986
|
+
...query,
|
|
14987
|
+
durationMs: performance.now() - started,
|
|
14988
|
+
...typeof rowCount === "number" ? { rowCount } : {}
|
|
14989
|
+
});
|
|
14990
|
+
}
|
|
14991
|
+
commandFailed(event) {
|
|
14992
|
+
const command = this.monitoredCommands.get(event.requestId);
|
|
14993
|
+
if (!command) return;
|
|
14994
|
+
this.monitoredCommands.delete(event.requestId);
|
|
14995
|
+
const { started, ...query } = command;
|
|
14996
|
+
this.emitQueryEvent({
|
|
14997
|
+
...query,
|
|
14998
|
+
durationMs: performance.now() - started,
|
|
14999
|
+
error: event.failure
|
|
15000
|
+
});
|
|
15001
|
+
}
|
|
15002
|
+
/**
|
|
15003
|
+
* Emit a `query` event. It runs inside the MongoDB client's own monitoring
|
|
15004
|
+
* callback, and an observer must never break the client, so a throwing
|
|
15005
|
+
* listener is swallowed here.
|
|
15006
|
+
*/
|
|
15007
|
+
emitQueryEvent(event) {
|
|
15008
|
+
try {
|
|
15009
|
+
this.emit("query", event);
|
|
15010
|
+
} catch {}
|
|
15011
|
+
}
|
|
15012
|
+
ignoredCommand(command) {
|
|
15013
|
+
return [
|
|
15014
|
+
"isMaster",
|
|
15015
|
+
"hello",
|
|
15016
|
+
"ping",
|
|
15017
|
+
"saslStart",
|
|
15018
|
+
"saslContinue",
|
|
15019
|
+
"endSessions",
|
|
15020
|
+
"buildInfo"
|
|
15021
|
+
].includes(command);
|
|
15022
|
+
}
|
|
14913
15023
|
/**
|
|
14914
15024
|
* Ensure MongoDB is running as a replica set (required for transactions).
|
|
14915
15025
|
*
|
|
@@ -17315,12 +17425,11 @@ var PostgresQueryParser = class PostgresQueryParser {
|
|
|
17315
17425
|
const columns = Array.isArray(fields) ? fields : [fields];
|
|
17316
17426
|
this.groupColumns.push(...columns);
|
|
17317
17427
|
}
|
|
17318
|
-
/**
|
|
17319
|
-
* Process a raw GROUP BY expression (emitted verbatim, no quoting). Used by
|
|
17320
|
-
* `groupByDate` to add a `date_trunc(...)` bucket to the GROUP BY clause.
|
|
17321
|
-
*/
|
|
17428
|
+
/** Process a raw GROUP BY expression, binding `?` placeholders positionally. */
|
|
17322
17429
|
processGroupByRaw(data) {
|
|
17323
|
-
|
|
17430
|
+
let expression = data.expression;
|
|
17431
|
+
const bindings = data.bindings ?? [];
|
|
17432
|
+
for (const binding of bindings) expression = expression.replace("?", this.addParam(binding));
|
|
17324
17433
|
this.groupRawExpressions.push(expression);
|
|
17325
17434
|
}
|
|
17326
17435
|
/**
|
|
@@ -17394,6 +17503,13 @@ function parseAggregateValue(value) {
|
|
|
17394
17503
|
function toParserOps(ops) {
|
|
17395
17504
|
return ops;
|
|
17396
17505
|
}
|
|
17506
|
+
function containsOperatorKey(value) {
|
|
17507
|
+
if (Array.isArray(value)) return value.some(containsOperatorKey);
|
|
17508
|
+
if (value === null || typeof value !== "object") return false;
|
|
17509
|
+
const prototype = Object.getPrototypeOf(value);
|
|
17510
|
+
if (prototype !== Object.prototype && prototype !== null) return false;
|
|
17511
|
+
return Object.entries(value).some(([key, nested]) => key.startsWith("$") || containsOperatorKey(nested));
|
|
17512
|
+
}
|
|
17397
17513
|
/**
|
|
17398
17514
|
* PostgreSQL Query Builder.
|
|
17399
17515
|
*
|
|
@@ -17414,6 +17530,14 @@ var PostgresQueryBuilder = class PostgresQueryBuilder extends QueryBuilder {
|
|
|
17414
17530
|
table;
|
|
17415
17531
|
/** Data source backing this builder. */
|
|
17416
17532
|
dataSource;
|
|
17533
|
+
/**
|
|
17534
|
+
* PostgreSQL has no parser for MongoDB-style object operators such as
|
|
17535
|
+
* `{ id: { $in: [1, 2] } }`; accepting one would bind the object as `=`.
|
|
17536
|
+
*/
|
|
17537
|
+
whereTrusted(conditions) {
|
|
17538
|
+
if (containsOperatorKey(conditions)) throw new UnsupportedQueryOperationError("whereTrusted with MongoDB-style operators", "postgres", "Use the portable whereIn()/where(field, operator, value) APIs instead.");
|
|
17539
|
+
return super.whereTrusted(conditions);
|
|
17540
|
+
}
|
|
17417
17541
|
/** Hydration callback for transforming result rows into model instances. */
|
|
17418
17542
|
hydrateCallback;
|
|
17419
17543
|
/** Invoked before query execution. */
|
|
@@ -19323,6 +19447,13 @@ var PostgresDriver = class {
|
|
|
19323
19447
|
if (!this._eventListeners.has(event)) this._eventListeners.set(event, /* @__PURE__ */ new Set());
|
|
19324
19448
|
this._eventListeners.get(event).add(listener);
|
|
19325
19449
|
}
|
|
19450
|
+
/** Remove a previously registered driver event listener. */
|
|
19451
|
+
off(event, listener) {
|
|
19452
|
+
const listeners = this._eventListeners.get(event);
|
|
19453
|
+
if (!listeners) return;
|
|
19454
|
+
listeners.delete(listener);
|
|
19455
|
+
if (listeners.size === 0) this._eventListeners.delete(event);
|
|
19456
|
+
}
|
|
19326
19457
|
/**
|
|
19327
19458
|
* Serialize data for storage in PostgreSQL.
|
|
19328
19459
|
*
|
|
@@ -19824,12 +19955,12 @@ var PostgresDriver = class {
|
|
|
19824
19955
|
if (options?.isolationLevel) beginSql += ` ISOLATION LEVEL ${options.isolationLevel.toUpperCase()}`;
|
|
19825
19956
|
if (options?.readOnly) beginSql += " READ ONLY";
|
|
19826
19957
|
if (options?.deferrable) beginSql += " DEFERRABLE";
|
|
19827
|
-
await client.query(beginSql);
|
|
19958
|
+
await this.executePostgresQuery(() => client.query(beginSql), beginSql);
|
|
19828
19959
|
transactionStarted = true;
|
|
19829
|
-
for (const [name, value] of settings) await client.query("SELECT set_config($1, $2, true)", [name, String(value)]);
|
|
19960
|
+
for (const [name, value] of settings) await this.executePostgresQuery(() => client.query("SELECT set_config($1, $2, true)", [name, String(value)]), "SELECT set_config($1, $2, true)", [name, String(value)]);
|
|
19830
19961
|
} catch (error) {
|
|
19831
19962
|
if (transactionStarted) try {
|
|
19832
|
-
await client.query("ROLLBACK");
|
|
19963
|
+
await this.executePostgresQuery(() => client.query("ROLLBACK"), "ROLLBACK");
|
|
19833
19964
|
} catch {}
|
|
19834
19965
|
client.release(error instanceof Error ? error : new Error(String(error)));
|
|
19835
19966
|
throw error;
|
|
@@ -19838,7 +19969,7 @@ var PostgresDriver = class {
|
|
|
19838
19969
|
context: client,
|
|
19839
19970
|
commit: async () => {
|
|
19840
19971
|
try {
|
|
19841
|
-
await client.query("COMMIT");
|
|
19972
|
+
await this.executePostgresQuery(() => client.query("COMMIT"), "COMMIT");
|
|
19842
19973
|
} catch (error) {
|
|
19843
19974
|
client.release(error instanceof Error ? error : new Error(String(error)));
|
|
19844
19975
|
throw error;
|
|
@@ -19847,7 +19978,7 @@ var PostgresDriver = class {
|
|
|
19847
19978
|
},
|
|
19848
19979
|
rollback: async () => {
|
|
19849
19980
|
try {
|
|
19850
|
-
await client.query("ROLLBACK");
|
|
19981
|
+
await this.executePostgresQuery(() => client.query("ROLLBACK"), "ROLLBACK");
|
|
19851
19982
|
} catch (error) {
|
|
19852
19983
|
client.release(error instanceof Error ? error : new Error(String(error)));
|
|
19853
19984
|
throw error;
|
|
@@ -20080,8 +20211,8 @@ var PostgresDriver = class {
|
|
|
20080
20211
|
paramCount: params.length
|
|
20081
20212
|
}
|
|
20082
20213
|
});
|
|
20083
|
-
if (txClient) result = await txClient.query(sql, params);
|
|
20084
|
-
else result = await this.pool.query(sql, params);
|
|
20214
|
+
if (txClient) result = await this.executePostgresQuery(() => txClient.query(sql, params), sql, params);
|
|
20215
|
+
else result = await this.executePostgresQuery(() => this.pool.query(sql, params), sql, params);
|
|
20085
20216
|
if (this.config.logging) {
|
|
20086
20217
|
const duration = (performance.now() - startTime).toFixed(2);
|
|
20087
20218
|
_warlock_js_logger.log.success({
|
|
@@ -20114,6 +20245,47 @@ var PostgresDriver = class {
|
|
|
20114
20245
|
throw error;
|
|
20115
20246
|
}
|
|
20116
20247
|
}
|
|
20248
|
+
/** Execute SQL and emit its public query event when the event is observed. */
|
|
20249
|
+
async executePostgresQuery(execute, sql, bindings = []) {
|
|
20250
|
+
if (!this._eventListeners.get("query")?.size) return execute();
|
|
20251
|
+
const started = performance.now();
|
|
20252
|
+
const startedAt = performance.timeOrigin + started;
|
|
20253
|
+
let result;
|
|
20254
|
+
try {
|
|
20255
|
+
result = await execute();
|
|
20256
|
+
} catch (error) {
|
|
20257
|
+
this.emitQueryEvent({
|
|
20258
|
+
connection: this.name,
|
|
20259
|
+
driver: "postgres",
|
|
20260
|
+
sql,
|
|
20261
|
+
bindings,
|
|
20262
|
+
durationMs: performance.now() - started,
|
|
20263
|
+
startedAt,
|
|
20264
|
+
error
|
|
20265
|
+
});
|
|
20266
|
+
throw error;
|
|
20267
|
+
}
|
|
20268
|
+
const event = {
|
|
20269
|
+
connection: this.name,
|
|
20270
|
+
driver: "postgres",
|
|
20271
|
+
sql,
|
|
20272
|
+
bindings,
|
|
20273
|
+
durationMs: performance.now() - started,
|
|
20274
|
+
startedAt
|
|
20275
|
+
};
|
|
20276
|
+
if (typeof result.rowCount === "number") event.rowCount = result.rowCount;
|
|
20277
|
+
this.emitQueryEvent(event);
|
|
20278
|
+
return result;
|
|
20279
|
+
}
|
|
20280
|
+
/**
|
|
20281
|
+
* Emit a `query` event. An observer is never allowed to fail the query it
|
|
20282
|
+
* observes, so a throwing listener is swallowed here.
|
|
20283
|
+
*/
|
|
20284
|
+
emitQueryEvent(event) {
|
|
20285
|
+
try {
|
|
20286
|
+
this.emit("query", event);
|
|
20287
|
+
} catch {}
|
|
20288
|
+
}
|
|
20117
20289
|
/**
|
|
20118
20290
|
* Emit an event to all registered listeners.
|
|
20119
20291
|
*
|
|
@@ -22110,6 +22282,7 @@ var Migration = class {
|
|
|
22110
22282
|
* @internal
|
|
22111
22283
|
*/
|
|
22112
22284
|
async execute() {
|
|
22285
|
+
this.assertNoGeneratedIndexNameCollisions();
|
|
22113
22286
|
for (const op of this.pendingOperations) await this.executeOperation(op);
|
|
22114
22287
|
this.pendingOperations.length = 0;
|
|
22115
22288
|
}
|
|
@@ -22146,6 +22319,7 @@ var Migration = class {
|
|
|
22146
22319
|
* @internal
|
|
22147
22320
|
*/
|
|
22148
22321
|
toSteps() {
|
|
22322
|
+
this.assertNoGeneratedIndexNameCollisions();
|
|
22149
22323
|
const serializer = this.driver.driver.getSQLSerializer();
|
|
22150
22324
|
const steps = [];
|
|
22151
22325
|
let schemaRun = [];
|
|
@@ -22293,6 +22467,40 @@ var Migration = class {
|
|
|
22293
22467
|
}
|
|
22294
22468
|
}
|
|
22295
22469
|
/**
|
|
22470
|
+
* Reject two unnamed indexes that a driver would give the same name before
|
|
22471
|
+
* either DDL command is sent. MongoDB's default names include directions;
|
|
22472
|
+
* PostgreSQL's include the table and columns.
|
|
22473
|
+
*/
|
|
22474
|
+
assertNoGeneratedIndexNameCollisions() {
|
|
22475
|
+
const engine = this.databaseEngine;
|
|
22476
|
+
if (engine !== "mongodb" && engine !== "postgres") return;
|
|
22477
|
+
const unnamedIndexes = this.pendingOperations.flatMap((operation) => {
|
|
22478
|
+
if (operation.type === "createIndex") {
|
|
22479
|
+
const index = operation.payload;
|
|
22480
|
+
return index.name ? [] : [index];
|
|
22481
|
+
}
|
|
22482
|
+
if (operation.type === "createUniqueIndex") {
|
|
22483
|
+
const { columns, name } = operation.payload;
|
|
22484
|
+
return name ? [] : [{
|
|
22485
|
+
columns,
|
|
22486
|
+
unique: true
|
|
22487
|
+
}];
|
|
22488
|
+
}
|
|
22489
|
+
return [];
|
|
22490
|
+
});
|
|
22491
|
+
const seen = /* @__PURE__ */ new Map();
|
|
22492
|
+
for (const index of unnamedIndexes) {
|
|
22493
|
+
const generatedName = engine === "mongodb" ? index.columns.map((column, i) => `${column}_${index.directions?.[i] === "desc" ? -1 : 1}`).join("_") : `idx_${this.table}_${index.columns.join("_")}`;
|
|
22494
|
+
const first = seen.get(generatedName);
|
|
22495
|
+
if (first) throw new Error(`Migration queued two ${engine} indexes with the generated name "${generatedName}": ${this.describeIndex(first)} and ${this.describeIndex(index)}. Give one index an explicit name, for example { name: "..." }.`);
|
|
22496
|
+
seen.set(generatedName, index);
|
|
22497
|
+
}
|
|
22498
|
+
}
|
|
22499
|
+
describeIndex(index) {
|
|
22500
|
+
const partial = index.where ? JSON.stringify(index.where) : "none";
|
|
22501
|
+
return `columns [${index.columns.join(", ")}] (unique: ${!!index.unique}, partial: ${partial})`;
|
|
22502
|
+
}
|
|
22503
|
+
/**
|
|
22296
22504
|
* Check if a table exists.
|
|
22297
22505
|
*
|
|
22298
22506
|
* Useful for conditional migrations and idempotent operations.
|
|
@@ -23999,13 +24207,18 @@ Migration.create = function createMigration(model, columns, options = {}) {
|
|
|
23999
24207
|
*/
|
|
24000
24208
|
Migration.alter = function alterMigration(model, schema, options = {}) {
|
|
24001
24209
|
const { order = 0, createdAt, transactional } = options;
|
|
24002
|
-
|
|
24210
|
+
let missingDownWarningEmitted = false;
|
|
24211
|
+
class AlterMigration extends Migration {
|
|
24003
24212
|
static order = order;
|
|
24004
24213
|
static createdAt = createdAt;
|
|
24005
24214
|
static transactional = transactional;
|
|
24006
24215
|
table = model.table;
|
|
24007
24216
|
dataSource = model.dataSource;
|
|
24008
24217
|
async up() {
|
|
24218
|
+
if (options.up && !options.down && !missingDownWarningEmitted) {
|
|
24219
|
+
missingDownWarningEmitted = true;
|
|
24220
|
+
_warlock_js_logger.log.warn("database", "migration", `Migration "${AlterMigration.migrationName ?? this.table}" defines a custom up() without a down() rollback.`);
|
|
24221
|
+
}
|
|
24009
24222
|
if (schema.add) wireColumns(this, schema.add);
|
|
24010
24223
|
if (schema.drop) for (const col of schema.drop) this.dropColumn(col);
|
|
24011
24224
|
if (schema.rename) for (const [from, to] of Object.entries(schema.rename)) this.renameColumn(from, to);
|
|
@@ -24058,7 +24271,8 @@ Migration.alter = function alterMigration(model, schema, options = {}) {
|
|
|
24058
24271
|
async down() {
|
|
24059
24272
|
if (options.down) await options.down.call(this);
|
|
24060
24273
|
}
|
|
24061
|
-
}
|
|
24274
|
+
}
|
|
24275
|
+
return AlterMigration;
|
|
24062
24276
|
};
|
|
24063
24277
|
Migration.__declarativeFactoriesAttached = true;
|
|
24064
24278
|
|
|
@@ -24134,11 +24348,14 @@ function compareCreatedAt(a, b) {
|
|
|
24134
24348
|
* Comparator for applying migrations — oldest first.
|
|
24135
24349
|
*
|
|
24136
24350
|
* Priority:
|
|
24137
|
-
* 1.
|
|
24138
|
-
* 2. `
|
|
24139
|
-
* 3.
|
|
24351
|
+
* 1. package origin before app origin
|
|
24352
|
+
* 2. `order` override (lower = earlier, default 0)
|
|
24353
|
+
* 3. `createdAt` timestamp (older = earlier)
|
|
24354
|
+
* 4. Alphabetical by migration name (last resort)
|
|
24140
24355
|
*/
|
|
24141
24356
|
function sortMigrations(a, b) {
|
|
24357
|
+
const byOrigin = Number(a.origin === "app") - Number(b.origin === "app");
|
|
24358
|
+
if (byOrigin !== 0) return byOrigin;
|
|
24142
24359
|
const byOrder = (a.order ?? 0) - (b.order ?? 0);
|
|
24143
24360
|
if (byOrder !== 0) return byOrder;
|
|
24144
24361
|
const byCreatedAt = compareCreatedAt(a.createdAt, b.createdAt);
|
|
@@ -24514,7 +24731,7 @@ var MigrationRunner = class {
|
|
|
24514
24731
|
* The migration name is read from `MigrationClass.migrationName`.
|
|
24515
24732
|
*
|
|
24516
24733
|
* @param MigrationClass - Migration class (must have static `name` set)
|
|
24517
|
-
* @param
|
|
24734
|
+
* @param origin - Registration source; defaults to `"app"` for direct registrations.
|
|
24518
24735
|
* @returns This runner for chaining
|
|
24519
24736
|
*
|
|
24520
24737
|
* @example
|
|
@@ -24523,7 +24740,7 @@ var MigrationRunner = class {
|
|
|
24523
24740
|
* runner.register(CreateUsersTable);
|
|
24524
24741
|
* ```
|
|
24525
24742
|
*/
|
|
24526
|
-
register(MigrationClass) {
|
|
24743
|
+
register(MigrationClass, origin = "app") {
|
|
24527
24744
|
const name = MigrationClass.migrationName;
|
|
24528
24745
|
if (!name) throw new Error("Migration class must have a static 'migrationName' property set. Set it in CLI after importing: MigrationClass.migrationName = \"filename\";");
|
|
24529
24746
|
const existing = this.migrations.find((m) => m.migrationName === name);
|
|
@@ -24531,6 +24748,7 @@ var MigrationRunner = class {
|
|
|
24531
24748
|
if (existing !== MigrationClass) throw new Error(`Duplicate migration name "${name}": two different migration classes resolve to the same name. Rename one of the files (or set a unique static 'migrationName').`);
|
|
24532
24749
|
return this;
|
|
24533
24750
|
}
|
|
24751
|
+
MigrationClass.origin = origin;
|
|
24534
24752
|
this.migrations.push(MigrationClass);
|
|
24535
24753
|
return this;
|
|
24536
24754
|
}
|
|
@@ -25321,6 +25539,7 @@ exports.UndefinedWhereValueError = UndefinedWhereValueError;
|
|
|
25321
25539
|
exports.UnsafeFilterError = UnsafeFilterError;
|
|
25322
25540
|
exports.UnsafeRawExpressionError = UnsafeRawExpressionError;
|
|
25323
25541
|
exports.UnsupportedLeanOperationError = UnsupportedLeanOperationError;
|
|
25542
|
+
exports.UnsupportedLockForUpdateError = UnsupportedLockForUpdateError;
|
|
25324
25543
|
exports.UnsupportedQueryOperationError = UnsupportedQueryOperationError;
|
|
25325
25544
|
exports.UnsupportedUpdateOperationError = UnsupportedUpdateOperationError;
|
|
25326
25545
|
exports.afterCommit = afterCommit;
|