@warlock.js/cascade 5.24.0 → 5.25.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 +18 -5
- package/README.md +101 -101
- package/bin/cascade.js +0 -0
- package/cjs/index.cjs +201 -71
- 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/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.map +1 -1
- package/esm/drivers/mongodb/mongodb-query-builder.mjs +2 -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-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 +3 -2
- package/esm/index.mjs +2 -1
- package/esm/migration/migration-runner.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 +25 -2
- package/esm/model/methods/query-methods.mjs.map +1 -1
- package/esm/model/methods/write-methods.mjs.map +1 -1
- package/esm/query-builder/query-builder.mjs +1 -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 +2901 -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.
|
|
@@ -5319,6 +5310,26 @@ function toDriverOptions(options) {
|
|
|
5319
5310
|
return rest;
|
|
5320
5311
|
}
|
|
5321
5312
|
/**
|
|
5313
|
+
* Add the same generated `id` that normal Mongo inserts receive to an upsert's
|
|
5314
|
+
* insert branch. `$setOnInsert` keeps an existing document's id immutable.
|
|
5315
|
+
*/
|
|
5316
|
+
async function withAutoGeneratedUpsertId(ModelClass, filter, operations, options) {
|
|
5317
|
+
if (!options?.upsert || !ModelClass.autoGenerateId || ModelClass.getDriver().name !== "mongodb" || Array.isArray(operations)) return operations;
|
|
5318
|
+
const filterId = filter.id;
|
|
5319
|
+
const filterConstrainsId = Object.hasOwn(filter, "id") && (filterId === null || typeof filterId !== "object" || !Array.isArray(filterId) && Object.hasOwn(filterId, "$eq"));
|
|
5320
|
+
const updateSuppliesId = Object.hasOwn(operations.$set ?? {}, "id") || Object.hasOwn(operations.$setOnInsert ?? {}, "id");
|
|
5321
|
+
if (filterConstrainsId || updateSuppliesId) return operations;
|
|
5322
|
+
const id = await generateModelId(ModelClass);
|
|
5323
|
+
if (id === void 0) return operations;
|
|
5324
|
+
return {
|
|
5325
|
+
...operations,
|
|
5326
|
+
$setOnInsert: {
|
|
5327
|
+
...operations.$setOnInsert,
|
|
5328
|
+
id
|
|
5329
|
+
}
|
|
5330
|
+
};
|
|
5331
|
+
}
|
|
5332
|
+
/**
|
|
5322
5333
|
* Pin a driver-level write to the rows the model's global scopes (tenant, soft
|
|
5323
5334
|
* delete) allow. The driver write APIs take a raw filter and never see scopes,
|
|
5324
5335
|
* so the target ids are resolved through the scoped query builder and added to
|
|
@@ -5352,7 +5363,8 @@ async function performAtomic(ModelClass, filter, operations, options) {
|
|
|
5352
5363
|
const resolved = resolveFilter(filter, options);
|
|
5353
5364
|
const scoped = options?.upsert ? await scopeUpsertFilter(ModelClass, resolved) : await scopeWriteFilter(ModelClass, resolved, false);
|
|
5354
5365
|
if (!scoped) return 0;
|
|
5355
|
-
const
|
|
5366
|
+
const upsertOperations = await withAutoGeneratedUpsertId(ModelClass, scoped, operations, options);
|
|
5367
|
+
const result = await ModelClass.getDriver().atomic(ModelClass.table, scoped, upsertOperations, toDriverOptions(options));
|
|
5356
5368
|
return result.modifiedCount + (result.upsertedCount ?? 0);
|
|
5357
5369
|
}
|
|
5358
5370
|
/**
|
|
@@ -5389,7 +5401,8 @@ async function findOneAndUpdateRecord(ModelClass, filter, update, options) {
|
|
|
5389
5401
|
const resolved = resolveFilter(filter, options);
|
|
5390
5402
|
const scoped = options?.upsert ? await scopeUpsertFilter(ModelClass, resolved) : await scopeWriteFilter(ModelClass, resolved, true);
|
|
5391
5403
|
if (!scoped) return null;
|
|
5392
|
-
const
|
|
5404
|
+
const upsertUpdate = await withAutoGeneratedUpsertId(ModelClass, scoped, update, options);
|
|
5405
|
+
const result = await ModelClass.getDriver().findOneAndUpdate(ModelClass.table, scoped, upsertUpdate, toDriverOptions(options));
|
|
5393
5406
|
if (!result) return null;
|
|
5394
5407
|
return ModelClass.hydrate(result);
|
|
5395
5408
|
}
|
|
@@ -10466,7 +10479,7 @@ var QueryBuilder = class QueryBuilder {
|
|
|
10466
10479
|
}
|
|
10467
10480
|
/** Raw GROUP BY expression. */
|
|
10468
10481
|
groupByRaw(expression, bindings) {
|
|
10469
|
-
this.addOperation("
|
|
10482
|
+
this.addOperation("groupByRaw", {
|
|
10470
10483
|
expression,
|
|
10471
10484
|
bindings: bindings ?? []
|
|
10472
10485
|
});
|
|
@@ -13583,7 +13596,7 @@ var MongoQueryBuilder = class MongoQueryBuilder extends QueryBuilder {
|
|
|
13583
13596
|
* keeps a queue-claim pattern from silently running unlocked.
|
|
13584
13597
|
*/
|
|
13585
13598
|
lockForUpdate() {
|
|
13586
|
-
throw new
|
|
13599
|
+
throw new UnsupportedLockForUpdateError();
|
|
13587
13600
|
}
|
|
13588
13601
|
/**
|
|
13589
13602
|
* Executes a callback with the query builder without breaking the chain.
|
|
@@ -14390,6 +14403,7 @@ var MongoDbDriver = class {
|
|
|
14390
14403
|
transactionOptions;
|
|
14391
14404
|
idGeneratorInstance;
|
|
14392
14405
|
_blueprint;
|
|
14406
|
+
monitoredCommands = /* @__PURE__ */ new Map();
|
|
14393
14407
|
get blueprint() {
|
|
14394
14408
|
if (!this._blueprint) this._blueprint = new MongoDBBlueprint(this.database);
|
|
14395
14409
|
return this._blueprint;
|
|
@@ -14517,13 +14531,18 @@ var MongoDbDriver = class {
|
|
|
14517
14531
|
_warlock_js_logger.log.warn("database.mongodb", "connection", "Disconnected from database");
|
|
14518
14532
|
}
|
|
14519
14533
|
});
|
|
14534
|
+
client.on("commandStarted", (event) => this.commandStarted(event));
|
|
14535
|
+
client.on("commandSucceeded", (event) => this.commandSucceeded(event));
|
|
14536
|
+
client.on("commandFailed", (event) => this.commandFailed(event));
|
|
14520
14537
|
if (this.config.logging) {
|
|
14521
14538
|
const ignoredCommands = [
|
|
14522
14539
|
"isMaster",
|
|
14523
14540
|
"hello",
|
|
14524
14541
|
"ping",
|
|
14525
14542
|
"saslStart",
|
|
14526
|
-
"saslContinue"
|
|
14543
|
+
"saslContinue",
|
|
14544
|
+
"endSessions",
|
|
14545
|
+
"buildInfo"
|
|
14527
14546
|
];
|
|
14528
14547
|
client.on("commandStarted", (event) => {
|
|
14529
14548
|
if (ignoredCommands.includes(event.commandName)) return;
|
|
@@ -14580,6 +14599,10 @@ var MongoDbDriver = class {
|
|
|
14580
14599
|
on(event, listener) {
|
|
14581
14600
|
this.events.on(event, listener);
|
|
14582
14601
|
}
|
|
14602
|
+
/** Remove a previously registered driver event listener. */
|
|
14603
|
+
off(event, listener) {
|
|
14604
|
+
this.events.off(event, listener);
|
|
14605
|
+
}
|
|
14583
14606
|
/**
|
|
14584
14607
|
* Insert a single document into the given collection.
|
|
14585
14608
|
*/
|
|
@@ -14896,7 +14919,7 @@ var MongoDbDriver = class {
|
|
|
14896
14919
|
*/
|
|
14897
14920
|
buildClientOptions() {
|
|
14898
14921
|
const baseOptions = { ...this.config.clientOptions ?? {} };
|
|
14899
|
-
if (
|
|
14922
|
+
if (baseOptions.monitorCommands === void 0) baseOptions.monitorCommands = true;
|
|
14900
14923
|
if (this.config.username && !baseOptions.auth) baseOptions.auth = {
|
|
14901
14924
|
username: this.config.username,
|
|
14902
14925
|
password: this.config.password
|
|
@@ -14910,6 +14933,65 @@ var MongoDbDriver = class {
|
|
|
14910
14933
|
emit(event, ...args) {
|
|
14911
14934
|
this.events.emit(event, ...args);
|
|
14912
14935
|
}
|
|
14936
|
+
commandStarted(event) {
|
|
14937
|
+
if (this.ignoredCommand(event.commandName) || this.events.listenerCount("query") === 0) return;
|
|
14938
|
+
const started = performance.now();
|
|
14939
|
+
const { lsid: _lsid, $clusterTime: _clusterTime, $db: _database, ...pipeline } = event.command;
|
|
14940
|
+
const collection = event.command[event.commandName];
|
|
14941
|
+
this.monitoredCommands.set(event.requestId, {
|
|
14942
|
+
connection: this.name,
|
|
14943
|
+
driver: "mongodb",
|
|
14944
|
+
command: event.commandName,
|
|
14945
|
+
collection: typeof collection === "string" ? collection : void 0,
|
|
14946
|
+
pipeline,
|
|
14947
|
+
started,
|
|
14948
|
+
startedAt: performance.timeOrigin + started
|
|
14949
|
+
});
|
|
14950
|
+
}
|
|
14951
|
+
commandSucceeded(event) {
|
|
14952
|
+
const command = this.monitoredCommands.get(event.requestId);
|
|
14953
|
+
if (!command) return;
|
|
14954
|
+
this.monitoredCommands.delete(event.requestId);
|
|
14955
|
+
const { started, ...query } = command;
|
|
14956
|
+
const rowCount = event.reply?.n ?? event.reply?.cursor?.firstBatch?.length;
|
|
14957
|
+
this.emitQueryEvent({
|
|
14958
|
+
...query,
|
|
14959
|
+
durationMs: performance.now() - started,
|
|
14960
|
+
...typeof rowCount === "number" ? { rowCount } : {}
|
|
14961
|
+
});
|
|
14962
|
+
}
|
|
14963
|
+
commandFailed(event) {
|
|
14964
|
+
const command = this.monitoredCommands.get(event.requestId);
|
|
14965
|
+
if (!command) return;
|
|
14966
|
+
this.monitoredCommands.delete(event.requestId);
|
|
14967
|
+
const { started, ...query } = command;
|
|
14968
|
+
this.emitQueryEvent({
|
|
14969
|
+
...query,
|
|
14970
|
+
durationMs: performance.now() - started,
|
|
14971
|
+
error: event.failure
|
|
14972
|
+
});
|
|
14973
|
+
}
|
|
14974
|
+
/**
|
|
14975
|
+
* Emit a `query` event. It runs inside the MongoDB client's own monitoring
|
|
14976
|
+
* callback, and an observer must never break the client, so a throwing
|
|
14977
|
+
* listener is swallowed here.
|
|
14978
|
+
*/
|
|
14979
|
+
emitQueryEvent(event) {
|
|
14980
|
+
try {
|
|
14981
|
+
this.emit("query", event);
|
|
14982
|
+
} catch {}
|
|
14983
|
+
}
|
|
14984
|
+
ignoredCommand(command) {
|
|
14985
|
+
return [
|
|
14986
|
+
"isMaster",
|
|
14987
|
+
"hello",
|
|
14988
|
+
"ping",
|
|
14989
|
+
"saslStart",
|
|
14990
|
+
"saslContinue",
|
|
14991
|
+
"endSessions",
|
|
14992
|
+
"buildInfo"
|
|
14993
|
+
].includes(command);
|
|
14994
|
+
}
|
|
14913
14995
|
/**
|
|
14914
14996
|
* Ensure MongoDB is running as a replica set (required for transactions).
|
|
14915
14997
|
*
|
|
@@ -17315,12 +17397,11 @@ var PostgresQueryParser = class PostgresQueryParser {
|
|
|
17315
17397
|
const columns = Array.isArray(fields) ? fields : [fields];
|
|
17316
17398
|
this.groupColumns.push(...columns);
|
|
17317
17399
|
}
|
|
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
|
-
*/
|
|
17400
|
+
/** Process a raw GROUP BY expression, binding `?` placeholders positionally. */
|
|
17322
17401
|
processGroupByRaw(data) {
|
|
17323
|
-
|
|
17402
|
+
let expression = data.expression;
|
|
17403
|
+
const bindings = data.bindings ?? [];
|
|
17404
|
+
for (const binding of bindings) expression = expression.replace("?", this.addParam(binding));
|
|
17324
17405
|
this.groupRawExpressions.push(expression);
|
|
17325
17406
|
}
|
|
17326
17407
|
/**
|
|
@@ -19323,6 +19404,13 @@ var PostgresDriver = class {
|
|
|
19323
19404
|
if (!this._eventListeners.has(event)) this._eventListeners.set(event, /* @__PURE__ */ new Set());
|
|
19324
19405
|
this._eventListeners.get(event).add(listener);
|
|
19325
19406
|
}
|
|
19407
|
+
/** Remove a previously registered driver event listener. */
|
|
19408
|
+
off(event, listener) {
|
|
19409
|
+
const listeners = this._eventListeners.get(event);
|
|
19410
|
+
if (!listeners) return;
|
|
19411
|
+
listeners.delete(listener);
|
|
19412
|
+
if (listeners.size === 0) this._eventListeners.delete(event);
|
|
19413
|
+
}
|
|
19326
19414
|
/**
|
|
19327
19415
|
* Serialize data for storage in PostgreSQL.
|
|
19328
19416
|
*
|
|
@@ -19824,12 +19912,12 @@ var PostgresDriver = class {
|
|
|
19824
19912
|
if (options?.isolationLevel) beginSql += ` ISOLATION LEVEL ${options.isolationLevel.toUpperCase()}`;
|
|
19825
19913
|
if (options?.readOnly) beginSql += " READ ONLY";
|
|
19826
19914
|
if (options?.deferrable) beginSql += " DEFERRABLE";
|
|
19827
|
-
await client.query(beginSql);
|
|
19915
|
+
await this.executePostgresQuery(() => client.query(beginSql), beginSql);
|
|
19828
19916
|
transactionStarted = true;
|
|
19829
|
-
for (const [name, value] of settings) await client.query("SELECT set_config($1, $2, true)", [name, String(value)]);
|
|
19917
|
+
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
19918
|
} catch (error) {
|
|
19831
19919
|
if (transactionStarted) try {
|
|
19832
|
-
await client.query("ROLLBACK");
|
|
19920
|
+
await this.executePostgresQuery(() => client.query("ROLLBACK"), "ROLLBACK");
|
|
19833
19921
|
} catch {}
|
|
19834
19922
|
client.release(error instanceof Error ? error : new Error(String(error)));
|
|
19835
19923
|
throw error;
|
|
@@ -19838,7 +19926,7 @@ var PostgresDriver = class {
|
|
|
19838
19926
|
context: client,
|
|
19839
19927
|
commit: async () => {
|
|
19840
19928
|
try {
|
|
19841
|
-
await client.query("COMMIT");
|
|
19929
|
+
await this.executePostgresQuery(() => client.query("COMMIT"), "COMMIT");
|
|
19842
19930
|
} catch (error) {
|
|
19843
19931
|
client.release(error instanceof Error ? error : new Error(String(error)));
|
|
19844
19932
|
throw error;
|
|
@@ -19847,7 +19935,7 @@ var PostgresDriver = class {
|
|
|
19847
19935
|
},
|
|
19848
19936
|
rollback: async () => {
|
|
19849
19937
|
try {
|
|
19850
|
-
await client.query("ROLLBACK");
|
|
19938
|
+
await this.executePostgresQuery(() => client.query("ROLLBACK"), "ROLLBACK");
|
|
19851
19939
|
} catch (error) {
|
|
19852
19940
|
client.release(error instanceof Error ? error : new Error(String(error)));
|
|
19853
19941
|
throw error;
|
|
@@ -20080,8 +20168,8 @@ var PostgresDriver = class {
|
|
|
20080
20168
|
paramCount: params.length
|
|
20081
20169
|
}
|
|
20082
20170
|
});
|
|
20083
|
-
if (txClient) result = await txClient.query(sql, params);
|
|
20084
|
-
else result = await this.pool.query(sql, params);
|
|
20171
|
+
if (txClient) result = await this.executePostgresQuery(() => txClient.query(sql, params), sql, params);
|
|
20172
|
+
else result = await this.executePostgresQuery(() => this.pool.query(sql, params), sql, params);
|
|
20085
20173
|
if (this.config.logging) {
|
|
20086
20174
|
const duration = (performance.now() - startTime).toFixed(2);
|
|
20087
20175
|
_warlock_js_logger.log.success({
|
|
@@ -20114,6 +20202,47 @@ var PostgresDriver = class {
|
|
|
20114
20202
|
throw error;
|
|
20115
20203
|
}
|
|
20116
20204
|
}
|
|
20205
|
+
/** Execute SQL and emit its public query event when the event is observed. */
|
|
20206
|
+
async executePostgresQuery(execute, sql, bindings = []) {
|
|
20207
|
+
if (!this._eventListeners.get("query")?.size) return execute();
|
|
20208
|
+
const started = performance.now();
|
|
20209
|
+
const startedAt = performance.timeOrigin + started;
|
|
20210
|
+
let result;
|
|
20211
|
+
try {
|
|
20212
|
+
result = await execute();
|
|
20213
|
+
} catch (error) {
|
|
20214
|
+
this.emitQueryEvent({
|
|
20215
|
+
connection: this.name,
|
|
20216
|
+
driver: "postgres",
|
|
20217
|
+
sql,
|
|
20218
|
+
bindings,
|
|
20219
|
+
durationMs: performance.now() - started,
|
|
20220
|
+
startedAt,
|
|
20221
|
+
error
|
|
20222
|
+
});
|
|
20223
|
+
throw error;
|
|
20224
|
+
}
|
|
20225
|
+
const event = {
|
|
20226
|
+
connection: this.name,
|
|
20227
|
+
driver: "postgres",
|
|
20228
|
+
sql,
|
|
20229
|
+
bindings,
|
|
20230
|
+
durationMs: performance.now() - started,
|
|
20231
|
+
startedAt
|
|
20232
|
+
};
|
|
20233
|
+
if (typeof result.rowCount === "number") event.rowCount = result.rowCount;
|
|
20234
|
+
this.emitQueryEvent(event);
|
|
20235
|
+
return result;
|
|
20236
|
+
}
|
|
20237
|
+
/**
|
|
20238
|
+
* Emit a `query` event. An observer is never allowed to fail the query it
|
|
20239
|
+
* observes, so a throwing listener is swallowed here.
|
|
20240
|
+
*/
|
|
20241
|
+
emitQueryEvent(event) {
|
|
20242
|
+
try {
|
|
20243
|
+
this.emit("query", event);
|
|
20244
|
+
} catch {}
|
|
20245
|
+
}
|
|
20117
20246
|
/**
|
|
20118
20247
|
* Emit an event to all registered listeners.
|
|
20119
20248
|
*
|
|
@@ -25321,6 +25450,7 @@ exports.UndefinedWhereValueError = UndefinedWhereValueError;
|
|
|
25321
25450
|
exports.UnsafeFilterError = UnsafeFilterError;
|
|
25322
25451
|
exports.UnsafeRawExpressionError = UnsafeRawExpressionError;
|
|
25323
25452
|
exports.UnsupportedLeanOperationError = UnsupportedLeanOperationError;
|
|
25453
|
+
exports.UnsupportedLockForUpdateError = UnsupportedLockForUpdateError;
|
|
25324
25454
|
exports.UnsupportedQueryOperationError = UnsupportedQueryOperationError;
|
|
25325
25455
|
exports.UnsupportedUpdateOperationError = UnsupportedUpdateOperationError;
|
|
25326
25456
|
exports.afterCommit = afterCommit;
|