@holo-js/db 0.2.4 → 0.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.d.ts +292 -6
  2. package/dist/index.mjs +2753 -188
  3. package/package.json +7 -7
package/dist/index.mjs CHANGED
@@ -122,6 +122,11 @@ function redactSql(sql, policy) {
122
122
  // src/cache.ts
123
123
  import { createHash } from "crypto";
124
124
  import { AsyncLocalStorage } from "async_hooks";
125
+ var DATABASE_DEPENDENCY_PREFIX = "db:";
126
+ var DATABASE_DEPENDENCY_METADATA_KEY = "__holoDatabaseDependencyMetadata__";
127
+ var MUTATION_DEPENDENCY_SUFFIX = "mutation";
128
+ var WHERE_DEPENDENCY_PREFIX = "where:";
129
+ var WHERE_EXACT_DEPENDENCY_PREFIX = "where-exact:";
125
130
  var databaseDependencyCollector = new AsyncLocalStorage();
126
131
  function getQueryCacheBridgeState() {
127
132
  const runtime = globalThis;
@@ -140,34 +145,268 @@ function resetDatabaseQueryCacheBridge() {
140
145
  function onDatabaseDependencyInvalidated(listener) {
141
146
  const state = getQueryCacheBridgeState();
142
147
  state.dependencyInvalidationListeners ??= /* @__PURE__ */ new Set();
143
- state.dependencyInvalidationListeners.add(listener);
148
+ const internalListener = listener;
149
+ state.dependencyInvalidationListeners.add(internalListener);
144
150
  return () => {
145
- state.dependencyInvalidationListeners?.delete(listener);
151
+ state.dependencyInvalidationListeners?.delete(internalListener);
146
152
  };
147
153
  }
148
154
  function resetDatabaseDependencyInvalidationListeners() {
149
155
  getQueryCacheBridgeState().dependencyInvalidationListeners = void 0;
150
156
  }
151
157
  async function collectDatabaseQueryDependencies(callback) {
152
- const dependencies = /* @__PURE__ */ new Set();
153
- const value = await databaseDependencyCollector.run(dependencies, callback);
158
+ const result = await collectDatabaseQueryDependenciesInternal(callback);
159
+ return Object.freeze({
160
+ value: result.value,
161
+ dependencies: result.dependencies
162
+ });
163
+ }
164
+ async function collectDatabaseQueryDependenciesInternal(callback) {
165
+ const state = {
166
+ dependencies: /* @__PURE__ */ new Set(),
167
+ queries: []
168
+ };
169
+ const value = await databaseDependencyCollector.run(state, callback);
154
170
  return Object.freeze({
155
171
  value,
156
- dependencies: Object.freeze([...dependencies])
172
+ dependencies: Object.freeze([...state.dependencies]),
173
+ queries: Object.freeze([...state.queries])
157
174
  });
158
175
  }
176
+ function hasActiveDatabaseDependencyCollector() {
177
+ return typeof databaseDependencyCollector.getStore() !== "undefined";
178
+ }
159
179
  function recordDatabaseQueryDependencies(dependencies) {
160
180
  if (!dependencies || dependencies.length === 0) {
161
181
  return;
162
182
  }
163
- const active = databaseDependencyCollector.getStore();
164
- if (!active) {
183
+ const state = databaseDependencyCollector.getStore();
184
+ if (!state) {
165
185
  return;
166
186
  }
167
187
  for (const dependency of dependencies) {
168
- active.add(dependency);
188
+ state.dependencies.add(dependency);
189
+ }
190
+ }
191
+ function recordDatabaseQueryObservation(observation) {
192
+ if (!observation) {
193
+ return;
194
+ }
195
+ const state = databaseDependencyCollector.getStore();
196
+ if (!state) {
197
+ return;
198
+ }
199
+ state.queries.push(observation);
200
+ }
201
+ function readDatabaseQueryObservationCount() {
202
+ return databaseDependencyCollector.getStore()?.queries.length ?? 0;
203
+ }
204
+ function truncateDatabaseQueryObservations(count) {
205
+ const state = databaseDependencyCollector.getStore();
206
+ if (!state || count >= state.queries.length) {
207
+ return;
208
+ }
209
+ state.queries.length = Math.max(0, count);
210
+ }
211
+ function rebindDatabaseQueryObservationResult(source, result) {
212
+ rebindDatabaseQueryObservation(source, result);
213
+ }
214
+ function rebindDatabaseQueryObservationHydratedResult(source, result, belongsToHydrations, relatedHydrations) {
215
+ rebindDatabaseQueryObservation(source, result, void 0, void 0, void 0, void 0, belongsToHydrations, relatedHydrations);
216
+ }
217
+ function rebindDatabaseQueryObservationPagination(source, data, meta, pagination, offset, belongsToHydrations, relatedHydrations) {
218
+ const state = databaseDependencyCollector.getStore();
219
+ if (!state) {
220
+ return;
221
+ }
222
+ for (let index = state.queries.length - 1; index >= 0; index -= 1) {
223
+ const query = state.queries[index];
224
+ if (!query || query.result !== source) {
225
+ continue;
226
+ }
227
+ state.queries[index] = Object.freeze({
228
+ aggregate: void 0,
229
+ belongsToHydrations: belongsToHydrations ?? query.belongsToHydrations,
230
+ connectionName: query.connectionName,
231
+ tableName: query.tableName,
232
+ dependencies: query.dependencies,
233
+ limit: pagination.perPage,
234
+ offset,
235
+ orderBy: query.orderBy,
236
+ patchable: query.patchable,
237
+ pagination: void 0,
238
+ predicates: query.predicates,
239
+ relation: query.relation,
240
+ result: data,
241
+ resultPath: query.resultPath,
242
+ relatedHydrations: relatedHydrations ?? query.relatedHydrations,
243
+ scalarColumn: void 0,
244
+ scalarListColumn: void 0,
245
+ scalarListRows: void 0,
246
+ selections: query.selections
247
+ });
248
+ state.queries.push(Object.freeze({
249
+ aggregate: void 0,
250
+ belongsToHydrations: void 0,
251
+ connectionName: query.connectionName,
252
+ tableName: query.tableName,
253
+ dependencies: query.dependencies,
254
+ limit: void 0,
255
+ offset: void 0,
256
+ orderBy: query.orderBy,
257
+ patchable: query.patchable,
258
+ pagination,
259
+ predicates: query.predicates,
260
+ relation: query.relation,
261
+ result: meta,
262
+ resultPath: void 0,
263
+ relatedHydrations: void 0,
264
+ scalarColumn: void 0,
265
+ scalarListColumn: void 0,
266
+ scalarListRows: void 0,
267
+ selections: query.selections
268
+ }));
269
+ return;
270
+ }
271
+ }
272
+ function rebindDatabaseQueryObservationCursorPagination(source, data, meta, pagination, belongsToHydrations, relatedHydrations) {
273
+ const state = databaseDependencyCollector.getStore();
274
+ if (!state) {
275
+ return;
276
+ }
277
+ for (let index = state.queries.length - 1; index >= 0; index -= 1) {
278
+ const query = state.queries[index];
279
+ if (!query || query.result !== source) {
280
+ continue;
281
+ }
282
+ state.queries[index] = Object.freeze({
283
+ aggregate: void 0,
284
+ belongsToHydrations: belongsToHydrations ?? query.belongsToHydrations,
285
+ connectionName: query.connectionName,
286
+ tableName: query.tableName,
287
+ cursorRowCount: pagination.rowCount,
288
+ cursorRows: pagination.rows,
289
+ dependencies: query.dependencies,
290
+ limit: pagination.perPage,
291
+ offset: void 0,
292
+ orderBy: query.orderBy,
293
+ patchable: query.patchable,
294
+ pagination: void 0,
295
+ predicates: query.predicates,
296
+ relation: query.relation,
297
+ result: data,
298
+ resultPath: query.resultPath,
299
+ relatedHydrations: relatedHydrations ?? query.relatedHydrations,
300
+ scalarColumn: void 0,
301
+ scalarListColumn: void 0,
302
+ scalarListRows: void 0,
303
+ selections: query.selections
304
+ });
305
+ state.queries.push(Object.freeze({
306
+ aggregate: void 0,
307
+ belongsToHydrations: void 0,
308
+ connectionName: query.connectionName,
309
+ tableName: query.tableName,
310
+ dependencies: query.dependencies,
311
+ limit: void 0,
312
+ offset: void 0,
313
+ orderBy: query.orderBy,
314
+ patchable: query.patchable,
315
+ pagination,
316
+ predicates: query.predicates,
317
+ relation: query.relation,
318
+ result: meta.nextCursor,
319
+ resultPath: Object.freeze(["nextCursor"]),
320
+ relatedHydrations: void 0,
321
+ scalarColumn: void 0,
322
+ scalarListColumn: void 0,
323
+ scalarListRows: void 0,
324
+ selections: query.selections
325
+ }));
326
+ return;
327
+ }
328
+ }
329
+ function disableDatabaseQueryObservationPatching(source) {
330
+ const state = databaseDependencyCollector.getStore();
331
+ if (!state) {
332
+ return;
333
+ }
334
+ for (let index = state.queries.length - 1; index >= 0; index -= 1) {
335
+ const query = state.queries[index];
336
+ if (!query || query.result !== source) {
337
+ continue;
338
+ }
339
+ state.queries[index] = Object.freeze({
340
+ aggregate: query.aggregate,
341
+ belongsToHydrations: query.belongsToHydrations,
342
+ connectionName: query.connectionName,
343
+ tableName: query.tableName,
344
+ dependencies: query.dependencies,
345
+ limit: query.limit,
346
+ offset: query.offset,
347
+ orderBy: query.orderBy,
348
+ patchable: false,
349
+ pagination: query.pagination,
350
+ predicates: query.predicates,
351
+ relation: query.relation,
352
+ result: query.result,
353
+ resultPath: query.resultPath,
354
+ relatedHydrations: query.relatedHydrations,
355
+ scalarColumn: query.scalarColumn,
356
+ scalarListColumn: query.scalarListColumn,
357
+ scalarListRows: query.scalarListRows,
358
+ selections: query.selections
359
+ });
360
+ return;
361
+ }
362
+ }
363
+ function rebindDatabaseQueryObservationAggregate(source, result, aggregate) {
364
+ rebindDatabaseQueryObservation(source, result, aggregate);
365
+ }
366
+ function rebindDatabaseQueryObservationScalar(source, result, column2) {
367
+ rebindDatabaseQueryObservation(source, result, void 0, column2);
368
+ }
369
+ function rebindDatabaseQueryObservationScalarList(rows, result, column2) {
370
+ rebindDatabaseQueryObservation(rows, result, void 0, void 0, column2, rows);
371
+ }
372
+ function rebindDatabaseQueryObservation(source, result, aggregate, scalarColumn, scalarListColumn, scalarListRows, belongsToHydrations, relatedHydrations) {
373
+ const state = databaseDependencyCollector.getStore();
374
+ if (!state) {
375
+ return;
376
+ }
377
+ for (let index = state.queries.length - 1; index >= 0; index -= 1) {
378
+ const query = state.queries[index];
379
+ if (!query || query.result !== source) {
380
+ continue;
381
+ }
382
+ state.queries[index] = Object.freeze({
383
+ aggregate,
384
+ belongsToHydrations,
385
+ connectionName: query.connectionName,
386
+ tableName: query.tableName,
387
+ dependencies: query.dependencies,
388
+ limit: query.limit,
389
+ offset: query.offset,
390
+ orderBy: query.orderBy,
391
+ patchable: query.patchable,
392
+ pagination: query.pagination,
393
+ predicates: query.predicates,
394
+ relation: query.relation,
395
+ result,
396
+ resultPath: query.resultPath,
397
+ relatedHydrations,
398
+ scalarColumn,
399
+ scalarListColumn,
400
+ scalarListRows,
401
+ selections: query.selections
402
+ });
403
+ return;
169
404
  }
170
405
  }
406
+ function hasDatabaseDependencyInvalidationListeners() {
407
+ const listeners = getQueryCacheBridgeState().dependencyInvalidationListeners;
408
+ return typeof listeners !== "undefined" && listeners.size > 0;
409
+ }
171
410
  async function notifyDatabaseDependencyInvalidationListeners(event) {
172
411
  const listeners = getQueryCacheBridgeState().dependencyInvalidationListeners;
173
412
  if (!listeners || listeners.size === 0) {
@@ -232,6 +471,173 @@ function resolveQueryCacheKey(statement, connectionName, config) {
232
471
  function createTableCacheDependency(connectionName, tableName) {
233
472
  return `db:${connectionName}:${tableName}`;
234
473
  }
474
+ function encodeDependencyValue(value) {
475
+ return encodeURIComponent(JSON.stringify(value));
476
+ }
477
+ function createPredicateMetadata(connectionName, tableName, columnName, value) {
478
+ if (typeof value === "undefined") {
479
+ return void 0;
480
+ }
481
+ return Object.freeze({
482
+ tableKey: createTableCacheDependency(connectionName, tableName),
483
+ columnName: getColumnName(columnName),
484
+ encodedValue: encodeDependencyValue(value)
485
+ });
486
+ }
487
+ function createPredicateDependencyFromMetadata(metadata, exact) {
488
+ return `${metadata.tableKey}:${exact ? WHERE_EXACT_DEPENDENCY_PREFIX : WHERE_DEPENDENCY_PREFIX}${metadata.columnName}:${metadata.encodedValue}`;
489
+ }
490
+ function createPredicateDependencyEntry(connectionName, tableName, columnName, value, exact = false) {
491
+ const metadata = createPredicateMetadata(connectionName, tableName, columnName, value);
492
+ if (!metadata) {
493
+ return void 0;
494
+ }
495
+ return {
496
+ dependency: createPredicateDependencyFromMetadata(metadata, exact),
497
+ metadata
498
+ };
499
+ }
500
+ function readExactPredicateValues(predicate) {
501
+ if (predicate.kind !== "comparison") {
502
+ return Object.freeze([]);
503
+ }
504
+ if (predicate.operator === "=") {
505
+ return Object.freeze([predicate.value]);
506
+ }
507
+ if (predicate.operator === "in" && Array.isArray(predicate.value)) {
508
+ return Object.freeze([...predicate.value]);
509
+ }
510
+ return Object.freeze([]);
511
+ }
512
+ function createTableRowCacheDependency(connectionName, tableName, columnName, value) {
513
+ if (typeof value === "undefined") {
514
+ return void 0;
515
+ }
516
+ return `db:${connectionName}:${tableName}:row:${columnName}:${encodeDependencyValue(value)}`;
517
+ }
518
+ function createTableRowWildcardCacheDependency(connectionName, tableName) {
519
+ return `db:${connectionName}:${tableName}:row:*`;
520
+ }
521
+ function createTablePredicateCacheDependency(connectionName, tableName, columnName, value) {
522
+ return createPredicateDependencyEntry(connectionName, tableName, columnName, value)?.dependency;
523
+ }
524
+ function createTableMutationCacheDependency(connectionName, tableName) {
525
+ return `db:${connectionName}:${tableName}:mutation`;
526
+ }
527
+ function createTableExactPredicateCacheDependency(connectionName, tableName, columnName, value) {
528
+ return createPredicateDependencyEntry(connectionName, tableName, columnName, value, true)?.dependency;
529
+ }
530
+ function parseDatabaseDependency(dependency) {
531
+ if (!dependency.startsWith(DATABASE_DEPENDENCY_PREFIX)) {
532
+ return void 0;
533
+ }
534
+ const connectionEnd = dependency.indexOf(":", DATABASE_DEPENDENCY_PREFIX.length);
535
+ if (connectionEnd <= DATABASE_DEPENDENCY_PREFIX.length) {
536
+ return void 0;
537
+ }
538
+ const tableStart = connectionEnd + 1;
539
+ const tableEnd = dependency.indexOf(":", tableStart);
540
+ if (tableEnd < 0) {
541
+ return tableStart === dependency.length ? void 0 : { tableKey: dependency };
542
+ }
543
+ if (tableEnd === tableStart) {
544
+ return void 0;
545
+ }
546
+ const suffixStart = tableEnd + 1;
547
+ return {
548
+ suffix: suffixStart === dependency.length ? "" : dependency.slice(suffixStart),
549
+ tableKey: dependency.slice(0, tableEnd)
550
+ };
551
+ }
552
+ function parsePredicateDependencySuffix(tableKey, suffix, prefix) {
553
+ if (!suffix.startsWith(prefix)) {
554
+ return void 0;
555
+ }
556
+ const columnStart = prefix.length;
557
+ const valueSeparator = suffix.indexOf(":", columnStart);
558
+ if (valueSeparator <= columnStart || valueSeparator === suffix.length - 1) {
559
+ return void 0;
560
+ }
561
+ return Object.freeze({
562
+ tableKey,
563
+ columnName: suffix.slice(columnStart, valueSeparator),
564
+ encodedValue: suffix.slice(valueSeparator + 1)
565
+ });
566
+ }
567
+ function createDatabaseDependencyInvalidationMetadata(dependencies, mutations) {
568
+ const directDependencies = [];
569
+ const exactPredicates = [];
570
+ const predicates = [];
571
+ const tableDependencyCandidates = [];
572
+ let hasMutationDependency = mutations.some((mutation) => mutation.kind === "upsert");
573
+ for (const dependency of dependencies) {
574
+ const parsed = parseDatabaseDependency(dependency);
575
+ if (!parsed) {
576
+ directDependencies.push(dependency);
577
+ continue;
578
+ }
579
+ if (parsed.suffix === void 0) {
580
+ tableDependencyCandidates.push(parsed.tableKey);
581
+ continue;
582
+ }
583
+ if (parsed.suffix === MUTATION_DEPENDENCY_SUFFIX) {
584
+ hasMutationDependency = true;
585
+ directDependencies.push(dependency);
586
+ continue;
587
+ }
588
+ const exactPredicate = parsePredicateDependencySuffix(
589
+ parsed.tableKey,
590
+ parsed.suffix,
591
+ WHERE_EXACT_DEPENDENCY_PREFIX
592
+ );
593
+ if (exactPredicate) {
594
+ exactPredicates.push(exactPredicate);
595
+ directDependencies.push(dependency);
596
+ continue;
597
+ }
598
+ const predicate = parsePredicateDependencySuffix(
599
+ parsed.tableKey,
600
+ parsed.suffix,
601
+ WHERE_DEPENDENCY_PREFIX
602
+ );
603
+ if (predicate) {
604
+ predicates.push(predicate);
605
+ directDependencies.push(dependency);
606
+ continue;
607
+ }
608
+ directDependencies.push(dependency);
609
+ }
610
+ const exactPredicateTables = new Set(exactPredicates.map((predicate) => predicate.tableKey));
611
+ const tableDependencies = [];
612
+ for (const tableKey of tableDependencyCandidates) {
613
+ if (exactPredicateTables.has(tableKey)) {
614
+ tableDependencies.push(tableKey);
615
+ continue;
616
+ }
617
+ directDependencies.push(tableKey);
618
+ }
619
+ return Object.freeze({
620
+ directDependencies: Object.freeze(directDependencies),
621
+ exactPredicates: Object.freeze(exactPredicates),
622
+ hasMutationDependency,
623
+ predicates: Object.freeze(predicates),
624
+ tableDependencies: Object.freeze(tableDependencies)
625
+ });
626
+ }
627
+ function createDatabaseDependencyInvalidationEvent(connectionName, dependencies, mutations, metadata) {
628
+ const event = {
629
+ connectionName,
630
+ dependencies,
631
+ mutations
632
+ };
633
+ Object.defineProperty(event, DATABASE_DEPENDENCY_METADATA_KEY, {
634
+ configurable: false,
635
+ enumerable: false,
636
+ value: metadata ?? createDatabaseDependencyInvalidationMetadata(dependencies, mutations),
637
+ writable: false
638
+ });
639
+ return Object.freeze(event);
640
+ }
235
641
  function normalizeQueryCacheDependencies(connectionName, dependencies) {
236
642
  return Object.freeze(dependencies.map((dependency) => {
237
643
  return dependency.startsWith("db:") ? dependency : createTableCacheDependency(connectionName, dependency);
@@ -257,8 +663,403 @@ function supportsAutomaticPredicateInvalidation(predicate) {
257
663
  return false;
258
664
  }
259
665
  }
666
+ function getColumnName(column2) {
667
+ const segments = column2.split(".");
668
+ return segments[segments.length - 1] ?? column2;
669
+ }
670
+ function getPrimaryKeyColumn(plan) {
671
+ const columns = plan.source.table?.columns;
672
+ if (!columns) {
673
+ return "id";
674
+ }
675
+ return Object.values(columns).find((column2) => column2.primaryKey)?.name ?? "id";
676
+ }
677
+ function hasDisjunctivePredicate(predicates) {
678
+ return predicates.some((predicate) => {
679
+ if (predicate.boolean === "or") {
680
+ return true;
681
+ }
682
+ if (predicate.kind !== "group") {
683
+ return false;
684
+ }
685
+ return predicate.negated === true || hasDisjunctivePredicate(predicate.predicates);
686
+ });
687
+ }
688
+ function findExactPrimaryKeyValue(predicates, primaryKeyColumn) {
689
+ for (const predicate of predicates) {
690
+ if (predicate.kind === "comparison" && predicate.operator === "=" && getColumnName(predicate.column) === primaryKeyColumn) {
691
+ return predicate.value;
692
+ }
693
+ if (predicate.kind === "group" && !predicate.negated && predicate.boolean !== "or") {
694
+ const value = findExactPrimaryKeyValue(predicate.predicates, primaryKeyColumn);
695
+ if (typeof value !== "undefined") {
696
+ return value;
697
+ }
698
+ }
699
+ }
700
+ return void 0;
701
+ }
702
+ function collectExactPredicateDependencies(predicates, connectionName, tableName, exact = false) {
703
+ const dependencies = /* @__PURE__ */ new Set();
704
+ for (const predicate of predicates) {
705
+ const values = readExactPredicateValues(predicate);
706
+ if (values.length > 0 && predicate.kind === "comparison") {
707
+ for (const value of values) {
708
+ const dependency = exact ? createTableExactPredicateCacheDependency(
709
+ connectionName,
710
+ tableName,
711
+ getColumnName(predicate.column),
712
+ value
713
+ ) : createTablePredicateCacheDependency(
714
+ connectionName,
715
+ tableName,
716
+ getColumnName(predicate.column),
717
+ value
718
+ );
719
+ if (dependency) {
720
+ dependencies.add(dependency);
721
+ }
722
+ }
723
+ continue;
724
+ }
725
+ if (predicate.kind === "group" && !predicate.negated && predicate.boolean !== "or") {
726
+ for (const dependency of collectExactPredicateDependencies(predicate.predicates, connectionName, tableName, exact)) {
727
+ dependencies.add(dependency);
728
+ }
729
+ }
730
+ }
731
+ return Object.freeze([...dependencies]);
732
+ }
733
+ function appendExactPredicateDependencyEntries(target, predicates, connectionName, tableName, exact = false) {
734
+ for (const predicate of predicates) {
735
+ const values = readExactPredicateValues(predicate);
736
+ if (values.length > 0 && predicate.kind === "comparison") {
737
+ for (const value of values) {
738
+ const entry = createPredicateDependencyEntry(
739
+ connectionName,
740
+ tableName,
741
+ getColumnName(predicate.column),
742
+ value,
743
+ exact
744
+ );
745
+ if (entry) {
746
+ target.set(entry.dependency, entry);
747
+ }
748
+ }
749
+ continue;
750
+ }
751
+ if (predicate.kind === "group" && !predicate.negated && predicate.boolean !== "or") {
752
+ appendExactPredicateDependencyEntries(target, predicate.predicates, connectionName, tableName, exact);
753
+ }
754
+ }
755
+ }
756
+ function collectRecordPredicateDependencies(connectionName, tableName, record, exact = false) {
757
+ const dependencies = /* @__PURE__ */ new Set();
758
+ for (const [columnName, value] of Object.entries(record)) {
759
+ const dependency = exact ? createTableExactPredicateCacheDependency(connectionName, tableName, getColumnName(columnName), value) : createTablePredicateCacheDependency(connectionName, tableName, getColumnName(columnName), value);
760
+ if (dependency) {
761
+ dependencies.add(dependency);
762
+ }
763
+ }
764
+ return Object.freeze([...dependencies]);
765
+ }
766
+ function appendRecordPredicateDependencyEntries(target, connectionName, tableName, record, exact = false) {
767
+ for (const [columnName, value] of Object.entries(record)) {
768
+ const entry = createPredicateDependencyEntry(connectionName, tableName, getColumnName(columnName), value, exact);
769
+ if (entry) {
770
+ target.set(entry.dependency, entry);
771
+ }
772
+ }
773
+ }
774
+ function createInvalidationMetadataFromParts(directDependencies, exactPredicates, predicates, tableDependencyCandidates, hasMutationDependency) {
775
+ const exactPredicateTables = new Set(exactPredicates.map((predicate) => predicate.tableKey));
776
+ const nextDirectDependencies = [...directDependencies];
777
+ const tableDependencies = [];
778
+ for (const tableKey of tableDependencyCandidates) {
779
+ if (exactPredicateTables.has(tableKey)) {
780
+ tableDependencies.push(tableKey);
781
+ continue;
782
+ }
783
+ nextDirectDependencies.push(tableKey);
784
+ }
785
+ return Object.freeze({
786
+ directDependencies: Object.freeze(nextDirectDependencies),
787
+ exactPredicates: Object.freeze([...exactPredicates]),
788
+ hasMutationDependency,
789
+ predicates: Object.freeze([...predicates]),
790
+ tableDependencies: Object.freeze(tableDependencies)
791
+ });
792
+ }
793
+ function collectQueryPredicateObservations(predicates) {
794
+ const observations = [];
795
+ for (const predicate of predicates) {
796
+ if (predicate.kind === "comparison") {
797
+ observations.push(Object.freeze({
798
+ column: getColumnName(predicate.column),
799
+ operator: predicate.operator,
800
+ value: predicate.value
801
+ }));
802
+ continue;
803
+ }
804
+ if (predicate.kind === "group" && !predicate.negated && predicate.boolean !== "or") {
805
+ observations.push(...collectQueryPredicateObservations(predicate.predicates));
806
+ }
807
+ }
808
+ return Object.freeze(observations);
809
+ }
810
+ function collectQueryOrderObservations(plan) {
811
+ return Object.freeze(plan.orderBy.flatMap((order) => {
812
+ if (order.kind !== "column") {
813
+ return [];
814
+ }
815
+ return [Object.freeze({
816
+ column: getColumnName(order.column),
817
+ direction: order.direction
818
+ })];
819
+ }));
820
+ }
821
+ function collectQuerySelectionObservations(plan) {
822
+ return Object.freeze(plan.selections.flatMap((selection) => {
823
+ if (selection.kind !== "column") {
824
+ return [];
825
+ }
826
+ const column2 = getColumnName(selection.column);
827
+ return [Object.freeze({
828
+ column: column2,
829
+ resultKey: selection.alias ?? column2
830
+ })];
831
+ }));
832
+ }
833
+ function isPatchablePredicateObservation(predicate) {
834
+ if (predicate.kind === "comparison" && predicate.boolean !== "or") {
835
+ return true;
836
+ }
837
+ if (predicate.kind !== "group" || predicate.negated || predicate.boolean === "or") {
838
+ return false;
839
+ }
840
+ return predicate.predicates.every((child) => isPatchablePredicateObservation(child));
841
+ }
842
+ function isColumnSelection(selection) {
843
+ return selection.kind === "column";
844
+ }
845
+ function hasRequiredProjectionSelection(selections, column2) {
846
+ return selections.some((selection) => selection.column === column2 && selection.resultKey === column2);
847
+ }
848
+ function isExactPrimaryKeySingleResultProjection(plan) {
849
+ return plan.limit === 1 && typeof findExactPrimaryKeyValue(plan.predicates, getPrimaryKeyColumn(plan)) !== "undefined";
850
+ }
851
+ function isPatchableProjectionObservation(plan, selections, predicates, orderBy) {
852
+ if (plan.selections.length === 0) {
853
+ return true;
854
+ }
855
+ if (plan.selections.every((selection) => selection.kind === "aggregate")) {
856
+ return true;
857
+ }
858
+ if (!plan.selections.every((selection) => isColumnSelection(selection))) {
859
+ return false;
860
+ }
861
+ const needsProjectedPrimaryKey = !isExactPrimaryKeySingleResultProjection(plan);
862
+ const requiredColumns = /* @__PURE__ */ new Set([
863
+ ...orderBy.map((order) => order.column)
864
+ ]);
865
+ if (needsProjectedPrimaryKey) {
866
+ requiredColumns.add(getPrimaryKeyColumn(plan));
867
+ }
868
+ for (const column2 of requiredColumns) {
869
+ if (!hasRequiredProjectionSelection(selections, column2)) {
870
+ return false;
871
+ }
872
+ }
873
+ return true;
874
+ }
875
+ function isPatchableOffsetObservation(plan, orderBy) {
876
+ return typeof plan.offset === "undefined" || typeof plan.limit === "number" && plan.offset > 0 && orderBy.length > 0;
877
+ }
878
+ function findGroupedAggregateObservation(plan, averageStates, aggregateStates) {
879
+ if (plan.groupBy.length !== 1 || plan.selections.length !== 2) {
880
+ return void 0;
881
+ }
882
+ const groupColumn = getColumnName(plan.groupBy[0]);
883
+ const groupSelection = plan.selections.find((selection) => {
884
+ return selection.kind === "column" && getColumnName(selection.column) === groupColumn;
885
+ });
886
+ const aggregateSelection = plan.selections.find((selection) => {
887
+ return selection.kind === "aggregate" && (selection.aggregate === "count" && selection.column === "*" || selection.aggregate === "avg" && selection.column !== "*" || selection.aggregate === "sum" && selection.column !== "*" || selection.aggregate === "min" && selection.column !== "*" || selection.aggregate === "max" && selection.column !== "*");
888
+ });
889
+ if (!groupSelection || !aggregateSelection) {
890
+ return void 0;
891
+ }
892
+ const orderBy = collectQueryOrderObservations(plan);
893
+ if (orderBy.some((order) => order.column !== groupColumn)) {
894
+ return void 0;
895
+ }
896
+ const aggregateKind = aggregateSelection.aggregate === "count" ? "count" : aggregateSelection.aggregate;
897
+ const having = readGroupedCountHavingObservation(plan);
898
+ if (plan.having.length > 0 && !isSupportedGroupedCountHaving(plan)) {
899
+ return void 0;
900
+ }
901
+ const groupedAggregate = {
902
+ aggregateColumn: aggregateKind === "count" ? void 0 : getColumnName(aggregateSelection.column),
903
+ aggregateResultKey: aggregateSelection.alias,
904
+ ...(aggregateKind === "count" || aggregateKind === "sum" || aggregateKind === "min" || aggregateKind === "max") && aggregateStates ? { aggregateStates: Object.freeze([...aggregateStates]) } : {},
905
+ ...aggregateKind === "avg" && averageStates ? { averageStates: Object.freeze([...averageStates]) } : {},
906
+ groupColumn,
907
+ groupResultKey: groupSelection.alias ?? groupColumn,
908
+ having,
909
+ kind: aggregateKind
910
+ };
911
+ return Object.freeze(groupedAggregate);
912
+ }
913
+ function isSupportedGroupedCountHaving(plan) {
914
+ if (plan.having.length === 0) {
915
+ return true;
916
+ }
917
+ if (plan.groupBy.length === 0 || plan.having.length !== 1) {
918
+ return false;
919
+ }
920
+ const clause = plan.having[0];
921
+ return typeof clause !== "undefined" && clause.expression.replace(/\s+/g, "").toLowerCase() === "count(*)" && typeof clause.value === "number" && Number.isFinite(clause.value) && isGroupedCountHavingOperator(clause.operator);
922
+ }
923
+ function readGroupedCountHavingObservation(plan) {
924
+ if (!isSupportedGroupedCountHaving(plan) || hasOnlyRedundantGroupedCountHaving(plan)) {
925
+ return void 0;
926
+ }
927
+ const clause = plan.having[0];
928
+ if (!clause || !isGroupedCountHavingOperator(clause.operator) || typeof clause.value !== "number" || !Number.isFinite(clause.value)) {
929
+ return void 0;
930
+ }
931
+ return Object.freeze({
932
+ operator: clause.operator,
933
+ value: clause.value
934
+ });
935
+ }
936
+ function isGroupedCountHavingOperator(operator) {
937
+ return operator === "<" || operator === "<=" || operator === "=" || operator === ">" || operator === ">=";
938
+ }
939
+ function hasOnlyRedundantGroupedCountHaving(plan) {
940
+ if (plan.having.length === 0) {
941
+ return true;
942
+ }
943
+ if (plan.groupBy.length === 0 || plan.having.length !== 1) {
944
+ return false;
945
+ }
946
+ const clause = plan.having[0];
947
+ return typeof clause !== "undefined" && clause.expression.replace(/\s+/g, "").toLowerCase() === "count(*)" && clause.operator === ">=" && clause.value === 1;
948
+ }
949
+ function isPatchableGroupedAggregateObservation(plan) {
950
+ return supportsAutomaticQueryCacheInvalidation(plan) && Boolean(findGroupedAggregateObservation(plan)) && !plan.distinct && !plan.lockMode && !plan.source.alias && plan.joins.length === 0 && typeof plan.limit === "undefined" && typeof plan.offset === "undefined" && plan.unions.length === 0 && plan.orderBy.every((order) => order.kind === "column") && plan.predicates.every((predicate) => isPatchablePredicateObservation(predicate));
951
+ }
952
+ function isPatchableQueryObservation(plan) {
953
+ const predicates = collectQueryPredicateObservations(plan.predicates);
954
+ const orderBy = collectQueryOrderObservations(plan);
955
+ const selections = collectQuerySelectionObservations(plan);
956
+ return isPatchableGroupedAggregateObservation(plan) || supportsAutomaticQueryCacheInvalidation(plan) && !plan.distinct && !plan.lockMode && !plan.source.alias && plan.groupBy.length === 0 && plan.having.length === 0 && plan.joins.length === 0 && isPatchableOffsetObservation(plan, orderBy) && isPatchableProjectionObservation(plan, selections, predicates, orderBy) && plan.unions.length === 0 && plan.orderBy.every((order) => order.kind === "column") && plan.predicates.every((predicate) => isPatchablePredicateObservation(predicate));
957
+ }
958
+ function appendObservableQueryDependencies(plan, connectionName, dependencies, seen) {
959
+ if (seen.has(plan)) {
960
+ return;
961
+ }
962
+ seen.add(plan);
963
+ dependencies.add(createTableCacheDependency(connectionName, plan.source.tableName));
964
+ for (const join of plan.joins) {
965
+ if (join.table) {
966
+ dependencies.add(createTableCacheDependency(connectionName, join.table));
967
+ }
968
+ if (join.subquery) {
969
+ appendObservableQueryDependencies(join.subquery, connectionName, dependencies, seen);
970
+ }
971
+ }
972
+ for (const union of plan.unions) {
973
+ appendObservableQueryDependencies(union.query, connectionName, dependencies, seen);
974
+ }
975
+ for (const selection of plan.selections) {
976
+ if (selection.kind === "subquery") {
977
+ appendObservableQueryDependencies(selection.query, connectionName, dependencies, seen);
978
+ }
979
+ }
980
+ for (const predicate of plan.predicates) {
981
+ appendObservablePredicateDependencies(predicate, connectionName, dependencies, seen);
982
+ }
983
+ }
984
+ function appendObservablePredicateDependencies(predicate, connectionName, dependencies, seen) {
985
+ if (predicate.kind === "group") {
986
+ for (const child of predicate.predicates) {
987
+ appendObservablePredicateDependencies(child, connectionName, dependencies, seen);
988
+ }
989
+ return;
990
+ }
991
+ if (predicate.kind === "exists" || predicate.kind === "subquery") {
992
+ appendObservableQueryDependencies(predicate.subquery, connectionName, dependencies, seen);
993
+ }
994
+ }
995
+ function hasObservationDependencyFallbackPredicate(predicate) {
996
+ if (predicate.kind === "group") {
997
+ return predicate.predicates.some((child) => hasObservationDependencyFallbackPredicate(child));
998
+ }
999
+ return predicate.kind === "exists" || predicate.kind === "subquery" || predicate.kind === "raw" || predicate.kind === "vector" || predicate.kind === "fulltext";
1000
+ }
1001
+ function hasObservationDependencyFallbackShape(plan) {
1002
+ return plan.joins.length > 0 || plan.unions.length > 0 || !isSupportedGroupedCountHaving(plan) || plan.selections.some((selection) => selection.kind === "raw" || selection.kind === "subquery") || plan.orderBy.some((order) => order.kind === "raw") || plan.predicates.some((predicate) => hasObservationDependencyFallbackPredicate(predicate));
1003
+ }
1004
+ function inferDatabaseQueryObservationDependencies(plan, connectionName) {
1005
+ if (!hasObservationDependencyFallbackShape(plan)) {
1006
+ return void 0;
1007
+ }
1008
+ const dependencies = /* @__PURE__ */ new Set();
1009
+ appendObservableQueryDependencies(plan, connectionName, dependencies, /* @__PURE__ */ new Set());
1010
+ return Object.freeze([...dependencies]);
1011
+ }
1012
+ function createDatabaseQueryObservationFromPlan(plan, connectionName, dependencies, result, patchable = isPatchableQueryObservation(plan), groupedAverageStates, groupedAggregateStates) {
1013
+ return Object.freeze({
1014
+ connectionName,
1015
+ belongsToHydrations: void 0,
1016
+ relatedHydrations: void 0,
1017
+ tableName: plan.source.tableName,
1018
+ dependencies: Object.freeze([...dependencies]),
1019
+ groupedAggregate: findGroupedAggregateObservation(plan, groupedAverageStates, groupedAggregateStates),
1020
+ limit: plan.limit,
1021
+ offset: plan.offset,
1022
+ orderBy: collectQueryOrderObservations(plan),
1023
+ patchable,
1024
+ pagination: void 0,
1025
+ predicates: collectQueryPredicateObservations(plan.predicates),
1026
+ result,
1027
+ selections: collectQuerySelectionObservations(plan)
1028
+ });
1029
+ }
1030
+ function createDatabaseQueryObservation(plan, connectionName, dependencies, result, groupedAverageStates, groupedAggregateStates) {
1031
+ if (!supportsAutomaticQueryCacheInvalidation(plan)) {
1032
+ return void 0;
1033
+ }
1034
+ return createDatabaseQueryObservationFromPlan(
1035
+ plan,
1036
+ connectionName,
1037
+ dependencies,
1038
+ result,
1039
+ void 0,
1040
+ groupedAverageStates,
1041
+ groupedAggregateStates
1042
+ );
1043
+ }
1044
+ function createDatabaseQueryFallbackObservation(plan, connectionName, dependencies, result) {
1045
+ if (dependencies.length === 0) {
1046
+ return void 0;
1047
+ }
1048
+ return createDatabaseQueryObservationFromPlan(plan, connectionName, dependencies, result, false);
1049
+ }
1050
+ function createDatabaseMutationEvent(kind, connectionName, tableName, predicates = [], values, rows, previousRows) {
1051
+ return Object.freeze({
1052
+ connectionName,
1053
+ tableName,
1054
+ kind,
1055
+ predicates: collectQueryPredicateObservations(predicates),
1056
+ previousRows: previousRows ? Object.freeze([...previousRows]) : void 0,
1057
+ rows: rows ? Object.freeze([...rows]) : void 0,
1058
+ values
1059
+ });
1060
+ }
260
1061
  function supportsAutomaticQueryCacheInvalidation(plan) {
261
- if (plan.joins.length > 0 || plan.unions.length > 0 || plan.having.length > 0) {
1062
+ if (plan.joins.length > 0 || plan.unions.length > 0 || !isSupportedGroupedCountHaving(plan)) {
262
1063
  return false;
263
1064
  }
264
1065
  if (plan.selections.some((selection) => selection.kind === "raw" || selection.kind === "subquery")) {
@@ -273,27 +1074,203 @@ function inferAutomaticQueryCacheDependencies(plan, connectionName) {
273
1074
  if (!supportsAutomaticQueryCacheInvalidation(plan)) {
274
1075
  return void 0;
275
1076
  }
1077
+ if (!hasDisjunctivePredicate(plan.predicates)) {
1078
+ const primaryKeyColumn = getPrimaryKeyColumn(plan);
1079
+ const primaryKeyValue = findExactPrimaryKeyValue(plan.predicates, primaryKeyColumn);
1080
+ const primaryKeyDependency = createTableRowCacheDependency(
1081
+ connectionName,
1082
+ plan.source.tableName,
1083
+ primaryKeyColumn,
1084
+ primaryKeyValue
1085
+ );
1086
+ if (primaryKeyDependency) {
1087
+ return Object.freeze([
1088
+ primaryKeyDependency,
1089
+ createTableRowWildcardCacheDependency(connectionName, plan.source.tableName)
1090
+ ]);
1091
+ }
1092
+ }
1093
+ if (!hasDisjunctivePredicate(plan.predicates)) {
1094
+ const predicateDependencies = collectExactPredicateDependencies(
1095
+ plan.predicates,
1096
+ connectionName,
1097
+ plan.source.tableName
1098
+ );
1099
+ if (predicateDependencies.length > 0) {
1100
+ return Object.freeze([
1101
+ createTableCacheDependency(connectionName, plan.source.tableName),
1102
+ ...predicateDependencies
1103
+ ]);
1104
+ }
1105
+ }
276
1106
  return Object.freeze([
277
1107
  createTableCacheDependency(connectionName, plan.source.tableName)
278
1108
  ]);
279
1109
  }
1110
+ function inferAutomaticQueryCacheInvalidationDependencies(plan, connectionName, values = {}) {
1111
+ return inferAutomaticQueryCacheInvalidationPlan(plan, connectionName, values).dependencies;
1112
+ }
1113
+ function inferAutomaticQueryCacheInvalidationPlan(plan, connectionName, values = {}, includeMetadata = false) {
1114
+ const tableKey = createTableCacheDependency(connectionName, plan.source.tableName);
1115
+ const dependencies = [tableKey];
1116
+ if (hasDisjunctivePredicate(plan.predicates)) {
1117
+ dependencies.push(createTableRowWildcardCacheDependency(connectionName, plan.source.tableName));
1118
+ return Object.freeze({
1119
+ dependencies: Object.freeze(dependencies),
1120
+ metadata: includeMetadata ? createInvalidationMetadataFromParts(
1121
+ dependencies,
1122
+ Object.freeze([]),
1123
+ Object.freeze([]),
1124
+ Object.freeze([]),
1125
+ false
1126
+ ) : void 0
1127
+ });
1128
+ }
1129
+ const directDependencies = [];
1130
+ const exactPredicates = [];
1131
+ const predicates = [];
1132
+ const mutationDependency = createTableMutationCacheDependency(connectionName, plan.source.tableName);
1133
+ dependencies.push(mutationDependency);
1134
+ directDependencies.push(mutationDependency);
1135
+ const primaryKeyColumn = getPrimaryKeyColumn(plan);
1136
+ const primaryKeyValue = findExactPrimaryKeyValue(plan.predicates, primaryKeyColumn);
1137
+ const primaryKeyDependency = createTableRowCacheDependency(
1138
+ connectionName,
1139
+ plan.source.tableName,
1140
+ primaryKeyColumn,
1141
+ primaryKeyValue
1142
+ );
1143
+ const rowDependency = primaryKeyDependency ?? createTableRowWildcardCacheDependency(connectionName, plan.source.tableName);
1144
+ dependencies.push(rowDependency);
1145
+ directDependencies.push(rowDependency);
1146
+ if (includeMetadata) {
1147
+ const predicateEntries = /* @__PURE__ */ new Map();
1148
+ appendExactPredicateDependencyEntries(predicateEntries, plan.predicates, connectionName, plan.source.tableName);
1149
+ for (const entry of predicateEntries.values()) {
1150
+ dependencies.push(entry.dependency);
1151
+ directDependencies.push(entry.dependency);
1152
+ predicates.push(entry.metadata);
1153
+ }
1154
+ const exactPredicateEntries = /* @__PURE__ */ new Map();
1155
+ appendExactPredicateDependencyEntries(exactPredicateEntries, plan.predicates, connectionName, plan.source.tableName, true);
1156
+ for (const entry of exactPredicateEntries.values()) {
1157
+ dependencies.push(entry.dependency);
1158
+ directDependencies.push(entry.dependency);
1159
+ exactPredicates.push(entry.metadata);
1160
+ }
1161
+ const recordPredicateEntries = /* @__PURE__ */ new Map();
1162
+ appendRecordPredicateDependencyEntries(recordPredicateEntries, connectionName, plan.source.tableName, values);
1163
+ for (const entry of recordPredicateEntries.values()) {
1164
+ dependencies.push(entry.dependency);
1165
+ directDependencies.push(entry.dependency);
1166
+ predicates.push(entry.metadata);
1167
+ }
1168
+ const exactRecordPredicateEntries = /* @__PURE__ */ new Map();
1169
+ appendRecordPredicateDependencyEntries(exactRecordPredicateEntries, connectionName, plan.source.tableName, values, true);
1170
+ for (const entry of exactRecordPredicateEntries.values()) {
1171
+ dependencies.push(entry.dependency);
1172
+ directDependencies.push(entry.dependency);
1173
+ exactPredicates.push(entry.metadata);
1174
+ }
1175
+ return Object.freeze({
1176
+ dependencies: Object.freeze(dependencies),
1177
+ metadata: createInvalidationMetadataFromParts(
1178
+ directDependencies,
1179
+ exactPredicates,
1180
+ predicates,
1181
+ Object.freeze([tableKey]),
1182
+ true
1183
+ )
1184
+ });
1185
+ }
1186
+ dependencies.push(...collectExactPredicateDependencies(plan.predicates, connectionName, plan.source.tableName));
1187
+ dependencies.push(...collectExactPredicateDependencies(plan.predicates, connectionName, plan.source.tableName, true));
1188
+ dependencies.push(...collectRecordPredicateDependencies(connectionName, plan.source.tableName, values));
1189
+ dependencies.push(...collectRecordPredicateDependencies(connectionName, plan.source.tableName, values, true));
1190
+ return Object.freeze({
1191
+ dependencies: Object.freeze(dependencies)
1192
+ });
1193
+ }
1194
+ function inferAutomaticInsertCacheInvalidationDependencies(connectionName, tableName, rows, lastInsertId) {
1195
+ return inferAutomaticInsertCacheInvalidationPlan(connectionName, tableName, rows, lastInsertId).dependencies;
1196
+ }
1197
+ function inferAutomaticInsertCacheInvalidationPlan(connectionName, tableName, rows, lastInsertId, includeMetadata = false) {
1198
+ const tableKey = createTableCacheDependency(connectionName, tableName);
1199
+ const dependencies = /* @__PURE__ */ new Set([
1200
+ tableKey
1201
+ ]);
1202
+ const directDependencies = /* @__PURE__ */ new Set();
1203
+ const exactPredicates = /* @__PURE__ */ new Map();
1204
+ const predicates = /* @__PURE__ */ new Map();
1205
+ for (const row of rows) {
1206
+ const dependency = createTableRowCacheDependency(connectionName, tableName, "id", row.id);
1207
+ if (dependency) {
1208
+ dependencies.add(dependency);
1209
+ directDependencies.add(dependency);
1210
+ }
1211
+ if (includeMetadata) {
1212
+ const rowPredicateEntries = /* @__PURE__ */ new Map();
1213
+ appendRecordPredicateDependencyEntries(rowPredicateEntries, connectionName, tableName, row);
1214
+ for (const entry of rowPredicateEntries.values()) {
1215
+ dependencies.add(entry.dependency);
1216
+ directDependencies.add(entry.dependency);
1217
+ predicates.set(entry.dependency, entry);
1218
+ }
1219
+ const rowExactPredicateEntries = /* @__PURE__ */ new Map();
1220
+ appendRecordPredicateDependencyEntries(rowExactPredicateEntries, connectionName, tableName, row, true);
1221
+ for (const entry of rowExactPredicateEntries.values()) {
1222
+ dependencies.add(entry.dependency);
1223
+ directDependencies.add(entry.dependency);
1224
+ exactPredicates.set(entry.dependency, entry);
1225
+ }
1226
+ continue;
1227
+ }
1228
+ for (const dependency2 of collectRecordPredicateDependencies(connectionName, tableName, row)) {
1229
+ dependencies.add(dependency2);
1230
+ }
1231
+ for (const dependency2 of collectRecordPredicateDependencies(connectionName, tableName, row, true)) {
1232
+ dependencies.add(dependency2);
1233
+ }
1234
+ }
1235
+ const lastInsertDependency = createTableRowCacheDependency(connectionName, tableName, "id", lastInsertId);
1236
+ if (lastInsertDependency) {
1237
+ dependencies.add(lastInsertDependency);
1238
+ directDependencies.add(lastInsertDependency);
1239
+ }
1240
+ if (dependencies.size === 1) {
1241
+ const rowWildcardDependency = createTableRowWildcardCacheDependency(connectionName, tableName);
1242
+ dependencies.add(rowWildcardDependency);
1243
+ directDependencies.add(rowWildcardDependency);
1244
+ }
1245
+ return Object.freeze({
1246
+ dependencies: Object.freeze([...dependencies]),
1247
+ metadata: includeMetadata ? createInvalidationMetadataFromParts(
1248
+ [...directDependencies],
1249
+ [...exactPredicates.values()].map((entry) => entry.metadata),
1250
+ [...predicates.values()].map((entry) => entry.metadata),
1251
+ Object.freeze([tableKey]),
1252
+ false
1253
+ ) : void 0
1254
+ });
1255
+ }
280
1256
  function resolveQueryCacheDependencies(plan, connectionName, explicit) {
281
1257
  if (explicit && explicit.length > 0) {
282
1258
  return normalizeQueryCacheDependencies(connectionName, explicit);
283
1259
  }
284
1260
  return inferAutomaticQueryCacheDependencies(plan, connectionName);
285
1261
  }
286
- async function invalidateQueryCacheDependencies(connection, dependencies) {
1262
+ async function invalidateQueryCacheDependencies(connection, dependencies, mutations = [], plan) {
287
1263
  if (dependencies.length === 0) {
288
1264
  return;
289
1265
  }
290
1266
  const invalidate = async () => {
291
1267
  const bridge = getDatabaseQueryCacheBridge();
292
1268
  await bridge?.invalidateDependencies(dependencies);
293
- await notifyDatabaseDependencyInvalidationListeners({
294
- connectionName: connection.getConnectionName(),
295
- dependencies
296
- });
1269
+ if (hasDatabaseDependencyInvalidationListeners()) {
1270
+ await notifyDatabaseDependencyInvalidationListeners(
1271
+ createDatabaseDependencyInvalidationEvent(connection.getConnectionName(), dependencies, mutations, plan?.metadata)
1272
+ );
1273
+ }
297
1274
  };
298
1275
  if (connection.getScope().kind === "root") {
299
1276
  await invalidate();
@@ -302,17 +1279,33 @@ async function invalidateQueryCacheDependencies(connection, dependencies) {
302
1279
  connection.afterCommit(invalidate);
303
1280
  }
304
1281
  var queryCacheInternals = {
305
- collectDatabaseQueryDependencies,
1282
+ collectDatabaseQueryDependencies: collectDatabaseQueryDependenciesInternal,
306
1283
  configureDatabaseQueryCacheBridge,
1284
+ createDatabaseMutationEvent,
1285
+ createDatabaseQueryObservation,
307
1286
  createDeterministicQueryCacheKey,
1287
+ createTablePredicateCacheDependency,
308
1288
  createTableCacheDependency,
1289
+ createTableRowCacheDependency,
1290
+ createTableRowWildcardCacheDependency,
309
1291
  getQueryCacheBridgeState,
1292
+ hasActiveDatabaseDependencyCollector,
1293
+ hasDatabaseDependencyInvalidationListeners,
1294
+ inferAutomaticInsertCacheInvalidationDependencies,
1295
+ inferAutomaticInsertCacheInvalidationPlan,
310
1296
  inferAutomaticQueryCacheDependencies,
1297
+ inferAutomaticQueryCacheInvalidationDependencies,
1298
+ inferAutomaticQueryCacheInvalidationPlan,
311
1299
  normalizeQueryCacheConfig,
312
1300
  normalizeQueryCacheDependencies,
313
1301
  notifyDatabaseDependencyInvalidationListeners,
1302
+ disableDatabaseQueryObservationPatching,
1303
+ rebindDatabaseQueryObservationResult,
1304
+ rebindDatabaseQueryObservationScalar,
1305
+ rebindDatabaseQueryObservationScalarList,
314
1306
  onDatabaseDependencyInvalidated,
315
1307
  recordDatabaseQueryDependencies,
1308
+ recordDatabaseQueryObservation,
316
1309
  resolveQueryCacheDependencies,
317
1310
  resolveQueryCacheKey,
318
1311
  resetDatabaseDependencyInvalidationListeners,
@@ -688,34 +1681,47 @@ function createInsertQueryPlan(source, values, options = {}) {
688
1681
  kind: "insert",
689
1682
  source,
690
1683
  ignoreConflicts: options.ignoreConflicts ?? false,
1684
+ returning: options.returning === true,
691
1685
  values: Object.freeze(values.map((value) => Object.freeze({ ...value })))
692
1686
  });
693
1687
  }
694
- function createUpsertQueryPlan(source, values, uniqueBy, updateColumns) {
1688
+ function createUpsertQueryPlan(source, values, uniqueBy, updateColumns, options = {}) {
695
1689
  return Object.freeze({
696
1690
  kind: "upsert",
697
1691
  source,
1692
+ returning: options.returning === true,
698
1693
  values: Object.freeze(values.map((value) => Object.freeze({ ...value }))),
699
1694
  uniqueBy: Object.freeze([...uniqueBy]),
700
1695
  updateColumns: Object.freeze([...updateColumns])
701
1696
  });
702
1697
  }
703
- function createUpdateQueryPlan(source, predicates, values) {
1698
+ function createUpdateQueryPlan(source, predicates, values, options = {}) {
704
1699
  return Object.freeze({
705
1700
  kind: "update",
706
1701
  source,
707
1702
  predicates: Object.freeze([...predicates]),
1703
+ returning: options.returning === true,
708
1704
  values: Object.freeze({ ...values })
709
1705
  });
710
1706
  }
711
- function createDeleteQueryPlan(source, predicates) {
1707
+ function createDeleteQueryPlan(source, predicates, options = {}) {
712
1708
  return Object.freeze({
713
1709
  kind: "delete",
714
1710
  source,
715
- predicates: Object.freeze([...predicates])
1711
+ predicates: Object.freeze([...predicates]),
1712
+ returning: options.returning === true
716
1713
  });
717
1714
  }
718
1715
 
1716
+ // src/query/aggregateValueCounts.ts
1717
+ function createAggregateValueCounts(values) {
1718
+ const counts = /* @__PURE__ */ new Map();
1719
+ for (const value of values) {
1720
+ counts.set(value, (counts.get(value) ?? 0) + 1);
1721
+ }
1722
+ return Object.freeze([...counts.entries()].sort(([left], [right]) => left - right).map(([value, count]) => Object.freeze({ count, value })));
1723
+ }
1724
+
719
1725
  // src/query/validator.ts
720
1726
  var IDENTIFIER_PATTERN = /^[A-Z_]\w*$/i;
721
1727
  var OPERATORS = /* @__PURE__ */ new Set([
@@ -1352,8 +2358,9 @@ var SQLQueryCompiler = class {
1352
2358
  return `${this.quoteIdentifier(column2)} = ${this.compileUpdateValue(column2, plan.values[column2], bindings)}`;
1353
2359
  }).join(", ");
1354
2360
  const whereClause = this.compilePredicates(plan.predicates, bindings);
2361
+ const returningClause = this.compileReturningClause(plan.returning === true);
1355
2362
  return this.withMetadata({
1356
- sql: `UPDATE ${this.quoteIdentifier(plan.source.tableName)} SET ${assignments}${whereClause}`,
2363
+ sql: `UPDATE ${this.quoteIdentifier(plan.source.tableName)} SET ${assignments}${whereClause}${returningClause}`,
1357
2364
  bindings,
1358
2365
  source: `query:update:${plan.source.tableName}`
1359
2366
  }, this.createWriteMetadata("update", plan.source.tableName, this.calculatePlanComplexity(plan)));
@@ -1361,8 +2368,9 @@ var SQLQueryCompiler = class {
1361
2368
  compileDelete(plan) {
1362
2369
  const bindings = [];
1363
2370
  const whereClause = this.compilePredicates(plan.predicates, bindings);
2371
+ const returningClause = this.compileReturningClause(plan.returning === true);
1364
2372
  return this.withMetadata({
1365
- sql: `DELETE FROM ${this.quoteIdentifier(plan.source.tableName)}${whereClause}`,
2373
+ sql: `DELETE FROM ${this.quoteIdentifier(plan.source.tableName)}${whereClause}${returningClause}`,
1366
2374
  bindings,
1367
2375
  source: `query:delete:${plan.source.tableName}`
1368
2376
  }, this.createWriteMetadata("delete", plan.source.tableName, this.calculatePlanComplexity(plan)));
@@ -1639,15 +2647,19 @@ var SQLQueryCompiler = class {
1639
2647
  return "INSERT INTO";
1640
2648
  }
1641
2649
  compileInsertSuffix(_plan) {
1642
- return "";
2650
+ return _plan.returning === true ? " RETURNING *" : "";
1643
2651
  }
1644
2652
  compileUpsertSuffix(plan, _insertColumns) {
1645
2653
  const conflictColumns = plan.uniqueBy.map((column2) => this.quoteIdentifier(column2)).join(", ");
2654
+ const returning = plan.returning === true ? " RETURNING *" : "";
1646
2655
  if (plan.updateColumns.length === 0) {
1647
- return ` ON CONFLICT (${conflictColumns}) DO NOTHING`;
2656
+ return ` ON CONFLICT (${conflictColumns}) DO NOTHING${returning}`;
1648
2657
  }
1649
2658
  const updates = plan.updateColumns.map((column2) => `${this.quoteIdentifier(column2)} = EXCLUDED.${this.quoteIdentifier(column2)}`).join(", ");
1650
- return ` ON CONFLICT (${conflictColumns}) DO UPDATE SET ${updates}`;
2659
+ return ` ON CONFLICT (${conflictColumns}) DO UPDATE SET ${updates}${returning}`;
2660
+ }
2661
+ compileReturningClause(returning) {
2662
+ return returning ? " RETURNING *" : "";
1651
2663
  }
1652
2664
  compileHavingExpression(expression) {
1653
2665
  const aggregateMatch = expression.match(/^(count|sum|avg|min|max)\((\*|[A-Z_]\w*)\)$/i);
@@ -1829,12 +2841,18 @@ var PostgresQueryCompiler = class extends SQLQueryCompiler {
1829
2841
  return lockMode === "update" ? "FOR UPDATE" : "FOR SHARE";
1830
2842
  }
1831
2843
  compileInsertSuffix(plan) {
2844
+ if (plan.returning === true) {
2845
+ return `${plan.ignoreConflicts ? " ON CONFLICT DO NOTHING" : ""} RETURNING *`;
2846
+ }
1832
2847
  const primaryKey = Object.values(plan.source.table?.columns ?? {}).find((column2) => column2.primaryKey)?.name;
1833
2848
  const returning = primaryKey ? ` RETURNING ${this.quoteIdentifier(primaryKey)}` : "";
1834
2849
  return `${plan.ignoreConflicts ? " ON CONFLICT DO NOTHING" : ""}${returning}`;
1835
2850
  }
1836
2851
  compileUpsertSuffix(plan, insertColumns) {
1837
2852
  const suffix = super.compileUpsertSuffix(plan, insertColumns);
2853
+ if (plan.returning === true) {
2854
+ return suffix;
2855
+ }
1838
2856
  const primaryKey = Object.values(plan.source.table?.columns ?? {}).find((column2) => column2.primaryKey)?.name;
1839
2857
  return primaryKey ? `${suffix} RETURNING ${this.quoteIdentifier(primaryKey)}` : suffix;
1840
2858
  }
@@ -2112,6 +3130,15 @@ function normalizeDialectWriteValue(dialect, column2, value) {
2112
3130
  }
2113
3131
 
2114
3132
  // src/query/TableQueryBuilder.ts
3133
+ var GROUPED_AVERAGE_COUNT_KEY = "__holo_grouped_average_count";
3134
+ var GROUPED_AVERAGE_ROW_COUNT_KEY = "__holo_grouped_average_row_count";
3135
+ var GROUPED_AVERAGE_SUM_KEY = "__holo_grouped_average_sum";
3136
+ var GROUPED_AVERAGE_GROUP_KEY = "__holo_grouped_average_group";
3137
+ var GROUPED_AGGREGATE_VALUE_KEY = "__holo_grouped_aggregate_state_value";
3138
+ var GROUPED_AGGREGATE_ROW_COUNT_KEY = "__holo_grouped_aggregate_state_row_count";
3139
+ var GROUPED_AGGREGATE_GROUP_KEY = "__holo_grouped_aggregate_state_group";
3140
+ var GROUPED_AGGREGATE_VALUE_COUNT_VALUE_KEY = "__holo_grouped_aggregate_state_count_value";
3141
+ var GROUPED_AGGREGATE_VALUE_COUNT_KEY = "__holo_grouped_aggregate_state_value_count";
2115
3142
  function normalizeAtomicQueryCacheTtl(ttl) {
2116
3143
  if (ttl instanceof Date) {
2117
3144
  const expiresAt = ttl.getTime();
@@ -2809,14 +3836,23 @@ var TableQueryBuilder = class _TableQueryBuilder {
2809
3836
  async get() {
2810
3837
  const statement = this.toSQL();
2811
3838
  const cacheConfig = this.queryCacheConfig;
2812
- const dependencies = this.plan.lockMode ? void 0 : resolveQueryCacheDependencies(
3839
+ const activeDependencyCollector = hasActiveDatabaseDependencyCollector();
3840
+ const dependencies = this.plan.lockMode || !cacheConfig && !activeDependencyCollector ? void 0 : resolveQueryCacheDependencies(
2813
3841
  this.plan,
2814
3842
  this.connection.getConnectionName(),
2815
3843
  cacheConfig?.invalidate
2816
3844
  );
2817
- recordDatabaseQueryDependencies(dependencies);
3845
+ const observationDependencies = activeDependencyCollector && !this.plan.lockMode ? dependencies ?? inferDatabaseQueryObservationDependencies(this.plan, this.connection.getConnectionName()) : dependencies;
3846
+ const createObservation = dependencies ? createDatabaseQueryObservation : createDatabaseQueryFallbackObservation;
3847
+ recordDatabaseQueryDependencies(observationDependencies);
2818
3848
  if (!cacheConfig || this.plan.lockMode) {
2819
3849
  const result = await this.connection.queryCompiled(statement);
3850
+ await this.recordCollectedQueryObservation(
3851
+ activeDependencyCollector,
3852
+ observationDependencies,
3853
+ createObservation,
3854
+ result.rows
3855
+ );
2820
3856
  return result.rows;
2821
3857
  }
2822
3858
  const bridge = getDatabaseQueryCacheBridge();
@@ -2825,7 +3861,7 @@ var TableQueryBuilder = class _TableQueryBuilder {
2825
3861
  }
2826
3862
  const cacheKey = resolveQueryCacheKey(statement, this.connection.getConnectionName(), cacheConfig);
2827
3863
  if (cacheConfig.flexible) {
2828
- return bridge.flexible(
3864
+ const rows2 = await bridge.flexible(
2829
3865
  cacheKey,
2830
3866
  cacheConfig.flexible,
2831
3867
  async () => {
@@ -2837,11 +3873,18 @@ var TableQueryBuilder = class _TableQueryBuilder {
2837
3873
  dependencies
2838
3874
  }
2839
3875
  );
3876
+ await this.recordCollectedQueryObservation(
3877
+ activeDependencyCollector,
3878
+ observationDependencies,
3879
+ createObservation,
3880
+ rows2
3881
+ );
3882
+ return rows2;
2840
3883
  }
2841
3884
  if (typeof cacheConfig.ttl === "undefined") {
2842
3885
  throw new ConfigurationError('[@holo-js/db] Query cache config requires "ttl" or "flexible".');
2843
3886
  }
2844
- return bridge.flexible(
3887
+ const rows = await bridge.flexible(
2845
3888
  cacheKey,
2846
3889
  normalizeAtomicQueryCacheTtl(cacheConfig.ttl),
2847
3890
  async () => {
@@ -2853,17 +3896,227 @@ var TableQueryBuilder = class _TableQueryBuilder {
2853
3896
  dependencies
2854
3897
  }
2855
3898
  );
3899
+ await this.recordCollectedQueryObservation(
3900
+ activeDependencyCollector,
3901
+ observationDependencies,
3902
+ createObservation,
3903
+ rows
3904
+ );
3905
+ return rows;
3906
+ }
3907
+ async recordCollectedQueryObservation(activeDependencyCollector, observationDependencies, createObservation, rows) {
3908
+ if (!activeDependencyCollector || !observationDependencies) {
3909
+ return;
3910
+ }
3911
+ const observation = createObservation(
3912
+ this.plan,
3913
+ this.connection.getConnectionName(),
3914
+ observationDependencies,
3915
+ rows
3916
+ );
3917
+ const groupedAggregate = observation?.groupedAggregate;
3918
+ if (!observation?.patchable || !groupedAggregate) {
3919
+ recordDatabaseQueryObservation(observation);
3920
+ return;
3921
+ }
3922
+ const averageStates = groupedAggregate.kind === "avg" && groupedAggregate.aggregateColumn ? await this.readGroupedAverageStates(groupedAggregate) : void 0;
3923
+ const aggregateStates = (groupedAggregate.kind === "count" || groupedAggregate.kind === "sum" || groupedAggregate.kind === "min" || groupedAggregate.kind === "max") && groupedAggregate.having ? await this.readGroupedAggregateStates(groupedAggregate) : void 0;
3924
+ recordDatabaseQueryObservation(averageStates || aggregateStates ? createObservation(
3925
+ this.plan,
3926
+ this.connection.getConnectionName(),
3927
+ observationDependencies,
3928
+ rows,
3929
+ averageStates,
3930
+ aggregateStates
3931
+ ) : observation);
3932
+ }
3933
+ async readGroupedAverageStates(groupedAggregate) {
3934
+ const aggregateColumn = groupedAggregate.aggregateColumn;
3935
+ if (!aggregateColumn) {
3936
+ return void 0;
3937
+ }
3938
+ const selections = [
3939
+ Object.freeze({
3940
+ alias: GROUPED_AVERAGE_GROUP_KEY,
3941
+ column: groupedAggregate.groupColumn,
3942
+ kind: "column"
3943
+ }),
3944
+ this.createAggregateSelection("count", GROUPED_AVERAGE_COUNT_KEY, aggregateColumn),
3945
+ this.createAggregateSelection("count", GROUPED_AVERAGE_ROW_COUNT_KEY, "*"),
3946
+ this.createAggregateSelection("sum", GROUPED_AVERAGE_SUM_KEY, aggregateColumn)
3947
+ ];
3948
+ const metadataPlan = Object.freeze({
3949
+ ...this.plan,
3950
+ having: Object.freeze([]),
3951
+ selections: Object.freeze(selections)
3952
+ });
3953
+ const result = await this.connection.queryCompiled(this.getCompiler().compile(metadataPlan));
3954
+ const states = [];
3955
+ for (const row of result.rows) {
3956
+ const count = this.normalizeAggregateMetadataNumber(row[GROUPED_AVERAGE_COUNT_KEY]);
3957
+ const rowCount = this.normalizeAggregateMetadataNumber(row[GROUPED_AVERAGE_ROW_COUNT_KEY]);
3958
+ const sum = this.normalizeAggregateMetadataNumber(row[GROUPED_AVERAGE_SUM_KEY] ?? 0);
3959
+ if (typeof count === "undefined" || typeof rowCount === "undefined" || typeof sum === "undefined" || count < 0 || rowCount < 0) {
3960
+ return void 0;
3961
+ }
3962
+ states.push(Object.freeze({
3963
+ count,
3964
+ groupValue: row[GROUPED_AVERAGE_GROUP_KEY],
3965
+ rowCount,
3966
+ sum
3967
+ }));
3968
+ }
3969
+ return Object.freeze(states);
3970
+ }
3971
+ async readGroupedAggregateStates(groupedAggregate) {
3972
+ const aggregateSelection = this.createGroupedAggregateStateValueSelection(groupedAggregate);
3973
+ if (!aggregateSelection) {
3974
+ return void 0;
3975
+ }
3976
+ const valueCounts = await this.readGroupedAggregateValueCounts(groupedAggregate);
3977
+ const selections = [
3978
+ Object.freeze({
3979
+ alias: GROUPED_AGGREGATE_GROUP_KEY,
3980
+ column: groupedAggregate.groupColumn,
3981
+ kind: "column"
3982
+ }),
3983
+ aggregateSelection,
3984
+ this.createAggregateSelection("count", GROUPED_AGGREGATE_ROW_COUNT_KEY, "*")
3985
+ ];
3986
+ const metadataPlan = Object.freeze({
3987
+ ...this.plan,
3988
+ having: Object.freeze([]),
3989
+ selections: Object.freeze(selections)
3990
+ });
3991
+ const result = await this.connection.queryCompiled(this.getCompiler().compile(metadataPlan));
3992
+ const states = [];
3993
+ for (const row of result.rows) {
3994
+ const aggregateValue = this.normalizeGroupedAggregateStateValue(
3995
+ groupedAggregate,
3996
+ row[GROUPED_AGGREGATE_VALUE_KEY]
3997
+ );
3998
+ const rowCount = this.normalizeAggregateMetadataNumber(row[GROUPED_AGGREGATE_ROW_COUNT_KEY]);
3999
+ if (typeof aggregateValue === "undefined" || typeof rowCount === "undefined" || rowCount < 0) {
4000
+ return void 0;
4001
+ }
4002
+ states.push(Object.freeze({
4003
+ aggregateValue,
4004
+ groupValue: row[GROUPED_AGGREGATE_GROUP_KEY],
4005
+ rowCount,
4006
+ ...valueCounts ? { valueCounts: this.readGroupedAggregateValueCountsForGroup(valueCounts, row[GROUPED_AGGREGATE_GROUP_KEY]) } : {}
4007
+ }));
4008
+ }
4009
+ return Object.freeze(states);
4010
+ }
4011
+ async readGroupedAggregateValueCounts(groupedAggregate) {
4012
+ if (groupedAggregate.kind !== "min" && groupedAggregate.kind !== "max") {
4013
+ return void 0;
4014
+ }
4015
+ const aggregateColumn = groupedAggregate.aggregateColumn;
4016
+ if (!aggregateColumn) {
4017
+ return void 0;
4018
+ }
4019
+ const metadataPlan = Object.freeze({
4020
+ ...this.plan,
4021
+ groupBy: Object.freeze([groupedAggregate.groupColumn, aggregateColumn]),
4022
+ having: Object.freeze([]),
4023
+ selections: Object.freeze([
4024
+ Object.freeze({
4025
+ alias: GROUPED_AGGREGATE_GROUP_KEY,
4026
+ column: groupedAggregate.groupColumn,
4027
+ kind: "column"
4028
+ }),
4029
+ Object.freeze({
4030
+ alias: GROUPED_AGGREGATE_VALUE_COUNT_VALUE_KEY,
4031
+ column: aggregateColumn,
4032
+ kind: "column"
4033
+ }),
4034
+ this.createAggregateSelection("count", GROUPED_AGGREGATE_VALUE_COUNT_KEY, "*")
4035
+ ])
4036
+ });
4037
+ const result = await this.connection.queryCompiled(this.getCompiler().compile(metadataPlan));
4038
+ const groups = [];
4039
+ for (const row of result.rows) {
4040
+ const groupValue = row[GROUPED_AGGREGATE_GROUP_KEY];
4041
+ const value = this.normalizeGroupedAggregateStateValue(
4042
+ groupedAggregate,
4043
+ row[GROUPED_AGGREGATE_VALUE_COUNT_VALUE_KEY]
4044
+ );
4045
+ const count = this.normalizeAggregateMetadataNumber(row[GROUPED_AGGREGATE_VALUE_COUNT_KEY]);
4046
+ if (typeof value === "undefined" || typeof count === "undefined" || count <= 0) {
4047
+ return void 0;
4048
+ }
4049
+ this.pushGroupedAggregateValueCount(groups, groupValue, Object.freeze({ count, value }));
4050
+ }
4051
+ return Object.freeze(groups.map((group) => Object.freeze({
4052
+ groupValue: group.groupValue,
4053
+ valueCounts: [...group.valueCounts].sort((left, right) => left.value - right.value)
4054
+ })));
4055
+ }
4056
+ pushGroupedAggregateValueCount(groups, groupValue, valueCount) {
4057
+ const group = groups.find((candidate) => Object.is(candidate.groupValue, groupValue));
4058
+ if (group) {
4059
+ group.valueCounts.push(valueCount);
4060
+ return;
4061
+ }
4062
+ groups.push({
4063
+ groupValue,
4064
+ valueCounts: [valueCount]
4065
+ });
4066
+ }
4067
+ readGroupedAggregateValueCountsForGroup(groups, groupValue) {
4068
+ return groups.find((group) => Object.is(group.groupValue, groupValue))?.valueCounts ?? Object.freeze([]);
4069
+ }
4070
+ createGroupedAggregateStateValueSelection(groupedAggregate) {
4071
+ if (groupedAggregate.kind === "count") {
4072
+ return this.createAggregateSelection("count", GROUPED_AGGREGATE_VALUE_KEY, "*");
4073
+ }
4074
+ if ((groupedAggregate.kind === "sum" || groupedAggregate.kind === "min" || groupedAggregate.kind === "max") && groupedAggregate.aggregateColumn) {
4075
+ return this.createAggregateSelection(
4076
+ groupedAggregate.kind,
4077
+ GROUPED_AGGREGATE_VALUE_KEY,
4078
+ groupedAggregate.aggregateColumn
4079
+ );
4080
+ }
4081
+ return void 0;
4082
+ }
4083
+ normalizeGroupedAggregateStateValue(groupedAggregate, value) {
4084
+ if ((groupedAggregate.kind === "min" || groupedAggregate.kind === "max") && value === null) {
4085
+ return void 0;
4086
+ }
4087
+ return this.normalizeAggregateMetadataNumber(value);
4088
+ }
4089
+ normalizeAggregateMetadataNumber(value) {
4090
+ if (value === null) {
4091
+ return 0;
4092
+ }
4093
+ if (typeof value === "number") {
4094
+ return Number.isFinite(value) ? value : void 0;
4095
+ }
4096
+ if (typeof value === "bigint") {
4097
+ const numericValue = Number(value);
4098
+ return Number.isSafeInteger(numericValue) ? numericValue : void 0;
4099
+ }
4100
+ if (typeof value === "string" && value.trim()) {
4101
+ const numericValue = Number(value);
4102
+ return Number.isFinite(numericValue) ? numericValue : void 0;
4103
+ }
4104
+ return void 0;
2856
4105
  }
2857
4106
  async first() {
2858
4107
  const rows = await this.limit(1).get();
2859
- return rows[0];
4108
+ const row = rows[0];
4109
+ rebindDatabaseQueryObservationResult(rows, row);
4110
+ return row;
2860
4111
  }
2861
4112
  async sole() {
2862
4113
  const rows = await this.limit(2).get();
2863
4114
  if (rows.length !== 1) {
2864
4115
  throw new CompilerError(`Query expected exactly one row but found ${rows.length}.`);
2865
4116
  }
2866
- return rows[0];
4117
+ const row = rows[0];
4118
+ rebindDatabaseQueryObservationResult(rows, row);
4119
+ return row;
2867
4120
  }
2868
4121
  async paginate(perPage = 15, page = 1, options = {}) {
2869
4122
  assertPositiveInteger(perPage, "Per-page value", (message) => new SecurityError(message));
@@ -2875,7 +4128,7 @@ var TableQueryBuilder = class _TableQueryBuilder {
2875
4128
  const data = rows.slice(offset, offset + perPage);
2876
4129
  const from = data.length === 0 ? null : offset + 1;
2877
4130
  const to = data.length === 0 ? null : offset + data.length;
2878
- return createPaginator(data, {
4131
+ const result = createPaginator(data, {
2879
4132
  total,
2880
4133
  perPage,
2881
4134
  pageName,
@@ -2885,6 +4138,14 @@ var TableQueryBuilder = class _TableQueryBuilder {
2885
4138
  to,
2886
4139
  hasMorePages: offset + data.length < total
2887
4140
  });
4141
+ rebindDatabaseQueryObservationPagination(rows, result.data, result.meta, Object.freeze({
4142
+ currentPage: page,
4143
+ kind: "standard",
4144
+ pageName,
4145
+ perPage,
4146
+ total
4147
+ }), offset);
4148
+ return result;
2888
4149
  }
2889
4150
  async simplePaginate(perPage = 15, page = 1, options = {}) {
2890
4151
  assertPositiveInteger(perPage, "Per-page value", (message) => new SecurityError(message));
@@ -2897,7 +4158,7 @@ var TableQueryBuilder = class _TableQueryBuilder {
2897
4158
  const data = hasMorePages ? pageRows.slice(0, perPage) : pageRows;
2898
4159
  const from = data.length === 0 ? null : offset + 1;
2899
4160
  const to = data.length === 0 ? null : offset + data.length;
2900
- return createSimplePaginator(data, {
4161
+ const result = createSimplePaginator(data, {
2901
4162
  perPage,
2902
4163
  pageName,
2903
4164
  currentPage: page,
@@ -2905,6 +4166,15 @@ var TableQueryBuilder = class _TableQueryBuilder {
2905
4166
  to,
2906
4167
  hasMorePages
2907
4168
  });
4169
+ rebindDatabaseQueryObservationPagination(rows, result.data, result.meta, Object.freeze({
4170
+ currentPage: page,
4171
+ hasMorePages,
4172
+ kind: "simple",
4173
+ pageName,
4174
+ perPage,
4175
+ rowCount: rows.length
4176
+ }), offset);
4177
+ return result;
2908
4178
  }
2909
4179
  async cursorPaginate(perPage = 15, cursor = null, options = {}) {
2910
4180
  assertPositiveInteger(perPage, "Per-page value", (message) => new SecurityError(message));
@@ -2922,12 +4192,32 @@ var TableQueryBuilder = class _TableQueryBuilder {
2922
4192
  const hasMorePages = pageRows.length > perPage;
2923
4193
  const data = hasMorePages ? pageRows.slice(0, perPage) : pageRows;
2924
4194
  const lastRow = data.at(-1);
2925
- return createCursorPaginator(data, {
4195
+ const result = createCursorPaginator(data, {
2926
4196
  perPage,
2927
4197
  cursorName,
2928
4198
  nextCursor: hasMorePages && lastRow ? encodeValueCursor(cursorOrders.map((order) => orderedQuery.readCursorColumnValue(lastRow, order.column))) : null,
2929
4199
  prevCursor: cursor
2930
4200
  });
4201
+ if (cursor === null) {
4202
+ rebindDatabaseQueryObservationCursorPagination(rows, result.data, {
4203
+ cursorName: result.cursorName,
4204
+ nextCursor: result.nextCursor,
4205
+ perPage: result.perPage,
4206
+ prevCursor: result.prevCursor
4207
+ }, Object.freeze({
4208
+ cursorName: result.cursorName,
4209
+ hasMorePages,
4210
+ kind: "cursor",
4211
+ nextCursor: result.nextCursor,
4212
+ perPage: result.perPage,
4213
+ prevCursor: result.prevCursor,
4214
+ rows: pageRows,
4215
+ rowCount: rows.length
4216
+ }));
4217
+ } else {
4218
+ rebindDatabaseQueryObservationResult(rows, data);
4219
+ }
4220
+ return result;
2931
4221
  }
2932
4222
  async chunk(size, callback) {
2933
4223
  assertPositiveInteger(size, "Chunk size", (message) => new SecurityError(message));
@@ -2991,35 +4281,66 @@ var TableQueryBuilder = class _TableQueryBuilder {
2991
4281
  }
2992
4282
  }
2993
4283
  async count() {
2994
- return (await this.get()).length;
4284
+ const rows = await this.get();
4285
+ const result = rows.length;
4286
+ if (hasActiveDatabaseDependencyCollector()) {
4287
+ rebindDatabaseQueryObservationAggregate(rows, result, Object.freeze({ kind: "count" }));
4288
+ }
4289
+ return result;
2995
4290
  }
2996
4291
  async exists() {
2997
- return await this.count() > 0;
4292
+ const count = await this.count();
4293
+ const result = count > 0;
4294
+ if (hasActiveDatabaseDependencyCollector()) {
4295
+ rebindDatabaseQueryObservationAggregate(count, result, Object.freeze({
4296
+ count,
4297
+ kind: "count",
4298
+ output: "boolean"
4299
+ }));
4300
+ }
4301
+ return result;
2998
4302
  }
2999
4303
  async doesntExist() {
3000
- return !await this.exists();
4304
+ const count = await this.count();
4305
+ const result = count === 0;
4306
+ if (hasActiveDatabaseDependencyCollector()) {
4307
+ rebindDatabaseQueryObservationAggregate(count, result, Object.freeze({
4308
+ count,
4309
+ kind: "count",
4310
+ output: "inverseBoolean"
4311
+ }));
4312
+ }
4313
+ return result;
3001
4314
  }
3002
4315
  async pluck(column2) {
3003
4316
  const rows = await this.get();
3004
- return rows.map((row) => row[column2]);
4317
+ const result = rows.map((row) => row[column2]);
4318
+ rebindDatabaseQueryObservationScalarList(rows, result, column2);
4319
+ return result;
3005
4320
  }
3006
4321
  async value(column2) {
3007
4322
  const row = await this.first();
3008
- return row?.[column2];
4323
+ const result = row?.[column2];
4324
+ rebindDatabaseQueryObservationScalar(row, result, column2);
4325
+ return result;
3009
4326
  }
3010
4327
  async valueOrFail(column2) {
3011
4328
  const row = await this.first();
3012
4329
  if (!row || typeof row[column2] === "undefined") {
3013
4330
  throw new CompilerError(`Query returned no value for column "${column2}".`);
3014
4331
  }
3015
- return row[column2];
4332
+ const result = row[column2];
4333
+ rebindDatabaseQueryObservationScalar(row, result, column2);
4334
+ return result;
3016
4335
  }
3017
4336
  async soleValue(column2) {
3018
4337
  const row = await this.sole();
3019
4338
  if (typeof row[column2] === "undefined") {
3020
4339
  throw new CompilerError(`Query returned no value for column "${column2}".`);
3021
4340
  }
3022
- return row[column2];
4341
+ const result = row[column2];
4342
+ rebindDatabaseQueryObservationScalar(row, result, column2);
4343
+ return result;
3023
4344
  }
3024
4345
  async sum(column2) {
3025
4346
  return this.aggregateNumeric(column2, "sum");
@@ -3027,45 +4348,95 @@ var TableQueryBuilder = class _TableQueryBuilder {
3027
4348
  async avg(column2) {
3028
4349
  const rows = await this.get();
3029
4350
  if (rows.length === 0) {
4351
+ if (hasActiveDatabaseDependencyCollector()) {
4352
+ rebindDatabaseQueryObservationAggregate(rows, null, Object.freeze({ column: column2, count: 0, kind: "avg", sum: 0 }));
4353
+ }
3030
4354
  return null;
3031
4355
  }
3032
4356
  const values = this.extractNumericValues(rows, column2, "avg");
3033
- return values.reduce((sum, value) => sum + value, 0) / values.length;
4357
+ const sum = values.reduce((total, value) => total + value, 0);
4358
+ const result = sum / values.length;
4359
+ if (hasActiveDatabaseDependencyCollector()) {
4360
+ rebindDatabaseQueryObservationAggregate(rows, result, Object.freeze({ column: column2, count: values.length, kind: "avg", sum }));
4361
+ }
4362
+ return result;
3034
4363
  }
3035
4364
  async min(column2) {
3036
4365
  const rows = await this.get();
3037
4366
  if (rows.length === 0) {
4367
+ if (hasActiveDatabaseDependencyCollector()) {
4368
+ rebindDatabaseQueryObservationAggregate(rows, null, Object.freeze({ column: column2, kind: "min" }));
4369
+ }
3038
4370
  return null;
3039
4371
  }
3040
4372
  const values = this.extractNumericValues(rows, column2, "min");
3041
- return Math.min(...values);
4373
+ const result = Math.min(...values);
4374
+ if (hasActiveDatabaseDependencyCollector()) {
4375
+ rebindDatabaseQueryObservationAggregate(rows, result, Object.freeze({
4376
+ column: column2,
4377
+ currentValueCount: values.filter((value) => value === result).length,
4378
+ kind: "min",
4379
+ valueCounts: createAggregateValueCounts(values)
4380
+ }));
4381
+ }
4382
+ return result;
3042
4383
  }
3043
4384
  async max(column2) {
3044
4385
  const rows = await this.get();
3045
4386
  if (rows.length === 0) {
4387
+ if (hasActiveDatabaseDependencyCollector()) {
4388
+ rebindDatabaseQueryObservationAggregate(rows, null, Object.freeze({ column: column2, kind: "max" }));
4389
+ }
3046
4390
  return null;
3047
4391
  }
3048
4392
  const values = this.extractNumericValues(rows, column2, "max");
3049
- return Math.max(...values);
4393
+ const result = Math.max(...values);
4394
+ if (hasActiveDatabaseDependencyCollector()) {
4395
+ rebindDatabaseQueryObservationAggregate(rows, result, Object.freeze({
4396
+ column: column2,
4397
+ currentValueCount: values.filter((value) => value === result).length,
4398
+ kind: "max",
4399
+ valueCounts: createAggregateValueCounts(values)
4400
+ }));
4401
+ }
4402
+ return result;
3050
4403
  }
3051
4404
  async find(value, column2 = "id") {
3052
4405
  return this.where(column2, value).limit(1).first();
3053
4406
  }
3054
4407
  async insert(values) {
3055
4408
  const rows = Array.isArray(values) ? values.map((value) => this.normalizeWriteRecord(value)) : [this.normalizeWriteRecord(values)];
3056
- const result = await this.connection.executeCompiled(this.getCompiler().compile(
4409
+ const hasInvalidationListeners = hasDatabaseDependencyInvalidationListeners();
4410
+ const useReturningRows = this.shouldUseReturningMutationRows(hasInvalidationListeners);
4411
+ const result = useReturningRows ? await this.queryReturningMutationRows(
4412
+ createInsertQueryPlan(this.source, rows, { returning: true }),
4413
+ true
4414
+ ) : await this.connection.executeCompiled(this.getCompiler().compile(
3057
4415
  createInsertQueryPlan(this.source, rows)
3058
4416
  ));
3059
- await this.invalidateSourceTableQueries();
3060
- return result;
4417
+ await this.invalidateInsertQueries("insert", result.rows?.rows ?? rows, result.lastInsertId);
4418
+ return {
4419
+ affectedRows: result.affectedRows,
4420
+ lastInsertId: result.lastInsertId
4421
+ };
3061
4422
  }
3062
4423
  async insertOrIgnore(values) {
3063
4424
  const rows = Array.isArray(values) ? values.map((value) => this.normalizeWriteRecord(value)) : [this.normalizeWriteRecord(values)];
3064
- const result = await this.connection.executeCompiled(this.getCompiler().compile(
4425
+ const hasInvalidationListeners = hasDatabaseDependencyInvalidationListeners();
4426
+ const useReturningRows = this.shouldUseReturningMutationRows(hasInvalidationListeners);
4427
+ const result = useReturningRows ? await this.queryReturningMutationRows(
4428
+ createInsertQueryPlan(this.source, rows, { ignoreConflicts: true, returning: true }),
4429
+ true
4430
+ ) : await this.connection.executeCompiled(this.getCompiler().compile(
3065
4431
  createInsertQueryPlan(this.source, rows, { ignoreConflicts: true })
3066
4432
  ));
3067
- await this.invalidateSourceTableQueries();
3068
- return result;
4433
+ if (!useReturningRows || result.affectedRows !== 0) {
4434
+ await this.invalidateInsertQueries("insert", result.rows?.rows ?? rows, result.lastInsertId);
4435
+ }
4436
+ return {
4437
+ affectedRows: result.affectedRows,
4438
+ lastInsertId: result.lastInsertId
4439
+ };
3069
4440
  }
3070
4441
  async insertGetId(values) {
3071
4442
  const result = await this.insert(values);
@@ -3073,61 +4444,273 @@ var TableQueryBuilder = class _TableQueryBuilder {
3073
4444
  }
3074
4445
  async upsert(values, uniqueBy, updateColumns = []) {
3075
4446
  const rows = Array.isArray(values) ? values.map((value) => this.normalizeWriteRecord(value)) : [this.normalizeWriteRecord(values)];
3076
- const result = await this.connection.executeCompiled(this.getCompiler().compile(
4447
+ const hasInvalidationListeners = hasDatabaseDependencyInvalidationListeners();
4448
+ const previousRows = hasInvalidationListeners ? await this.captureUpsertPreviousRows(rows, uniqueBy) : void 0;
4449
+ const useReturningRows = this.shouldUseReturningMutationRows(hasInvalidationListeners);
4450
+ const result = useReturningRows ? await this.queryReturningMutationRows(
4451
+ createUpsertQueryPlan(this.source, rows, uniqueBy, updateColumns, { returning: true }),
4452
+ true
4453
+ ) : await this.connection.executeCompiled(this.getCompiler().compile(
3077
4454
  createUpsertQueryPlan(this.source, rows, uniqueBy, updateColumns)
3078
4455
  ));
3079
- await this.invalidateSourceTableQueries();
3080
- return result;
4456
+ if (!useReturningRows || result.affectedRows !== 0) {
4457
+ await this.invalidateInsertQueries("upsert", result.rows?.rows ?? rows, result.lastInsertId, previousRows);
4458
+ }
4459
+ return {
4460
+ affectedRows: result.affectedRows,
4461
+ lastInsertId: result.lastInsertId
4462
+ };
3081
4463
  }
3082
4464
  async increment(column2, amount = 1, extraValues = {}) {
3083
4465
  return this.adjustNumericColumn(column2, amount, extraValues);
3084
4466
  }
3085
- async decrement(column2, amount = 1, extraValues = {}) {
3086
- return this.adjustNumericColumn(column2, -amount, extraValues);
4467
+ async decrement(column2, amount = 1, extraValues = {}) {
4468
+ return this.adjustNumericColumn(column2, -amount, extraValues);
4469
+ }
4470
+ async update(values) {
4471
+ const normalizedValues = this.normalizeUpdateValues(values);
4472
+ const hasInvalidationListeners = hasDatabaseDependencyInvalidationListeners();
4473
+ const useReturningRows = this.shouldUseReturningMutationRows(hasInvalidationListeners);
4474
+ const mutationRows = hasInvalidationListeners && !useReturningRows ? await this.captureUpdatedMutationRows(normalizedValues) : void 0;
4475
+ const result = useReturningRows ? await this.queryReturningMutationRows(
4476
+ createUpdateQueryPlan(this.source, this.plan.predicates, normalizedValues, { returning: true })
4477
+ ) : await this.connection.executeCompiled(this.getCompiler().compile(
4478
+ createUpdateQueryPlan(this.source, this.plan.predicates, normalizedValues)
4479
+ ));
4480
+ const capturedRows = result.rows ?? mutationRows;
4481
+ if (result.affectedRows !== 0) {
4482
+ await this.invalidateMutationQueries(normalizedValues, capturedRows);
4483
+ }
4484
+ return {
4485
+ affectedRows: result.affectedRows,
4486
+ lastInsertId: result.lastInsertId
4487
+ };
4488
+ }
4489
+ async updateJson(columnPath, value) {
4490
+ return this.update({ [columnPath]: value });
4491
+ }
4492
+ async delete() {
4493
+ const hasInvalidationListeners = hasDatabaseDependencyInvalidationListeners();
4494
+ const useReturningRows = this.shouldUseReturningMutationRows(hasInvalidationListeners);
4495
+ const mutationRows = hasInvalidationListeners && !useReturningRows ? await this.captureDeletedMutationRows() : void 0;
4496
+ const result = useReturningRows ? await this.queryReturningMutationRows(
4497
+ createDeleteQueryPlan(this.source, this.plan.predicates, { returning: true })
4498
+ ) : await this.connection.executeCompiled(this.getCompiler().compile(
4499
+ createDeleteQueryPlan(this.source, this.plan.predicates)
4500
+ ));
4501
+ const capturedRows = result.rows ?? mutationRows;
4502
+ if (result.affectedRows !== 0) {
4503
+ await this.invalidateMutationQueries({}, capturedRows);
4504
+ }
4505
+ return {
4506
+ affectedRows: result.affectedRows,
4507
+ lastInsertId: result.lastInsertId
4508
+ };
4509
+ }
4510
+ async unsafeQuery(statement) {
4511
+ return this.connection.unsafeQuery({
4512
+ ...statement,
4513
+ unsafe: true,
4514
+ source: `table:${this.source.tableName}`
4515
+ });
4516
+ }
4517
+ async unsafeExecute(statement) {
4518
+ return this.connection.unsafeExecute({
4519
+ ...statement,
4520
+ unsafe: true,
4521
+ source: `table:${this.source.tableName}`
4522
+ });
4523
+ }
4524
+ clone(plan) {
4525
+ const table = this.source.table ?? this.source.tableName;
4526
+ return new _TableQueryBuilder(table, this.connection, plan, this.queryCacheConfig);
4527
+ }
4528
+ async captureUpdatedMutationRows(values) {
4529
+ const plan = Object.freeze({
4530
+ ...createSelectQueryPlan(this.source),
4531
+ predicates: this.plan.predicates
4532
+ });
4533
+ const result = await this.connection.queryCompiled(this.getCompiler().compile(plan));
4534
+ if (result.rows.length === 0) {
4535
+ return void 0;
4536
+ }
4537
+ const previousRows = Object.freeze(result.rows.map((row) => Object.freeze({ ...row })));
4538
+ const rows = Object.freeze(previousRows.map((row) => Object.freeze(
4539
+ this.applyCapturedUpdateValues(row, values)
4540
+ )));
4541
+ return Object.freeze({
4542
+ previousRows,
4543
+ rows
4544
+ });
4545
+ }
4546
+ async captureDeletedMutationRows() {
4547
+ const plan = Object.freeze({
4548
+ ...createSelectQueryPlan(this.source),
4549
+ predicates: this.plan.predicates
4550
+ });
4551
+ const result = await this.connection.queryCompiled(this.getCompiler().compile(plan));
4552
+ if (result.rows.length === 0) {
4553
+ return void 0;
4554
+ }
4555
+ return Object.freeze({
4556
+ rows: Object.freeze(result.rows.map((row) => Object.freeze({ ...row })))
4557
+ });
4558
+ }
4559
+ applyCapturedUpdateValues(row, values) {
4560
+ const nextRow = { ...row };
4561
+ for (const [column2, value] of Object.entries(values)) {
4562
+ nextRow[column2] = this.applyCapturedUpdateValue(row[column2], value);
4563
+ }
4564
+ return nextRow;
4565
+ }
4566
+ applyCapturedUpdateValue(currentValue, value) {
4567
+ if (!Array.isArray(value) || !value.every((item) => this.isCapturedJsonUpdateOperation(item))) {
4568
+ return value;
4569
+ }
4570
+ let nextValue = currentValue;
4571
+ for (const operation of value) {
4572
+ nextValue = this.applyCapturedJsonUpdateOperation(nextValue, operation);
4573
+ }
4574
+ return nextValue;
4575
+ }
4576
+ applyCapturedJsonUpdateOperation(currentValue, operation) {
4577
+ return this.applyCapturedJsonPathValue(currentValue, operation.path, operation.value);
4578
+ }
4579
+ isCapturedJsonUpdateOperation(value) {
4580
+ return typeof value === "object" && value !== null && !Array.isArray(value) && value.kind === "json-set" && Array.isArray(value.path);
4581
+ }
4582
+ applyCapturedJsonPathValue(currentValue, path, value) {
4583
+ const segment = path[0];
4584
+ if (typeof segment === "undefined") {
4585
+ return value;
4586
+ }
4587
+ const currentRecord = this.isJsonRecord(currentValue) ? currentValue : {};
4588
+ const childValue = this.applyCapturedJsonPathValue(currentRecord[segment], path.slice(1), value);
4589
+ return Object.freeze({
4590
+ ...currentRecord,
4591
+ [segment]: childValue
4592
+ });
4593
+ }
4594
+ isJsonRecord(value) {
4595
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3087
4596
  }
3088
- async update(values) {
3089
- const result = await this.connection.executeCompiled(this.getCompiler().compile(
3090
- createUpdateQueryPlan(this.source, this.plan.predicates, this.normalizeUpdateValues(values))
3091
- ));
3092
- if (result.affectedRows !== 0) {
3093
- await this.invalidateSourceTableQueries();
4597
+ async captureUpsertPreviousRows(rows, uniqueBy) {
4598
+ const firstUniqueColumn = uniqueBy[0];
4599
+ if (!firstUniqueColumn || rows.length === 0 || !rows.every((row) => this.hasUniqueByValues(row, uniqueBy))) {
4600
+ return void 0;
3094
4601
  }
3095
- return result;
4602
+ const firstColumnValues = Object.freeze([...new Set(rows.map((row) => row[firstUniqueColumn]))]);
4603
+ const query = new _TableQueryBuilder(this.source.tableName, this.connection).whereIn(firstUniqueColumn, firstColumnValues);
4604
+ const result = await this.connection.queryCompiled(this.getCompiler().compile(query.getPlan()));
4605
+ const previousRows = [];
4606
+ const usedResultIndexes = /* @__PURE__ */ new Set();
4607
+ for (const row of rows) {
4608
+ const resultIndex = result.rows.findIndex((candidate, index) => {
4609
+ return !usedResultIndexes.has(index) && this.rowsMatchUniqueBy(candidate, row, uniqueBy);
4610
+ });
4611
+ if (resultIndex < 0) {
4612
+ continue;
4613
+ }
4614
+ const previousRow = result.rows[resultIndex];
4615
+ if (!previousRow) {
4616
+ continue;
4617
+ }
4618
+ usedResultIndexes.add(resultIndex);
4619
+ previousRows.push(Object.freeze({ ...previousRow }));
4620
+ }
4621
+ return Object.freeze(previousRows);
3096
4622
  }
3097
- async updateJson(columnPath, value) {
3098
- return this.update({ [columnPath]: value });
4623
+ hasUniqueByValues(row, uniqueBy) {
4624
+ return uniqueBy.every((column2) => Object.prototype.hasOwnProperty.call(row, column2));
3099
4625
  }
3100
- async delete() {
3101
- const result = await this.connection.executeCompiled(this.getCompiler().compile(
3102
- createDeleteQueryPlan(this.source, this.plan.predicates)
3103
- ));
3104
- if (result.affectedRows !== 0) {
3105
- await this.invalidateSourceTableQueries();
3106
- }
3107
- return result;
4626
+ rowsMatchUniqueBy(left, right, uniqueBy) {
4627
+ return uniqueBy.every((column2) => left[column2] === right[column2]);
3108
4628
  }
3109
- async unsafeQuery(statement) {
3110
- return this.connection.unsafeQuery({
3111
- ...statement,
3112
- unsafe: true,
3113
- source: `table:${this.source.tableName}`
3114
- });
4629
+ shouldUseReturningMutationRows(hasInvalidationListeners) {
4630
+ return hasInvalidationListeners && this.connection.getCapabilities().returning;
3115
4631
  }
3116
- async unsafeExecute(statement) {
3117
- return this.connection.unsafeExecute({
3118
- ...statement,
3119
- unsafe: true,
3120
- source: `table:${this.source.tableName}`
3121
- });
4632
+ async queryReturningMutationRows(plan, includeLastInsertId = false) {
4633
+ const result = await this.connection.queryCompiled(this.getCompiler().compile(plan));
4634
+ const rows = this.freezeMutationRows(result.rows);
4635
+ return {
4636
+ affectedRows: result.rowCount,
4637
+ lastInsertId: includeLastInsertId ? this.readReturnedLastInsertId(rows) : void 0,
4638
+ rows: rows.length === 0 ? void 0 : Object.freeze({
4639
+ rows
4640
+ })
4641
+ };
3122
4642
  }
3123
- clone(plan) {
3124
- const table = this.source.table ?? this.source.tableName;
3125
- return new _TableQueryBuilder(table, this.connection, plan, this.queryCacheConfig);
4643
+ freezeMutationRows(rows) {
4644
+ return Object.freeze(rows.map((row) => Object.freeze({ ...row })));
3126
4645
  }
3127
- async invalidateSourceTableQueries() {
3128
- await invalidateQueryCacheDependencies(this.connection, [
3129
- `db:${this.connection.getConnectionName()}:${this.source.tableName}`
3130
- ]);
4646
+ readReturnedLastInsertId(rows) {
4647
+ if (rows.length !== 1) {
4648
+ return void 0;
4649
+ }
4650
+ const value = rows[0]?.[this.resolvePrimaryKeyColumn()];
4651
+ return typeof value === "number" || typeof value === "string" ? value : void 0;
4652
+ }
4653
+ async invalidateInsertQueries(kind, rows, lastInsertId, previousRows) {
4654
+ const hasInvalidationListeners = hasDatabaseDependencyInvalidationListeners();
4655
+ if (!getDatabaseQueryCacheBridge() && !hasInvalidationListeners) {
4656
+ return;
4657
+ }
4658
+ const mutations = hasInvalidationListeners ? [
4659
+ createDatabaseMutationEvent(
4660
+ kind,
4661
+ this.connection.getConnectionName(),
4662
+ this.source.tableName,
4663
+ [],
4664
+ void 0,
4665
+ rows.length === 1 && typeof lastInsertId !== "undefined" && !Object.prototype.hasOwnProperty.call(rows[0], "id") ? Object.freeze([Object.freeze({
4666
+ ...rows[0],
4667
+ id: lastInsertId
4668
+ })]) : rows,
4669
+ previousRows
4670
+ )
4671
+ ] : [];
4672
+ const invalidation = inferAutomaticInsertCacheInvalidationPlan(
4673
+ this.connection.getConnectionName(),
4674
+ this.source.tableName,
4675
+ rows,
4676
+ lastInsertId,
4677
+ hasInvalidationListeners
4678
+ );
4679
+ await invalidateQueryCacheDependencies(
4680
+ this.connection,
4681
+ invalidation.dependencies,
4682
+ mutations,
4683
+ invalidation
4684
+ );
4685
+ }
4686
+ async invalidateMutationQueries(values = {}, capturedRows) {
4687
+ const hasInvalidationListeners = hasDatabaseDependencyInvalidationListeners();
4688
+ if (!getDatabaseQueryCacheBridge() && !hasInvalidationListeners) {
4689
+ return;
4690
+ }
4691
+ const hasValues = Object.keys(values).length > 0;
4692
+ const invalidation = inferAutomaticQueryCacheInvalidationPlan(
4693
+ this.plan,
4694
+ this.connection.getConnectionName(),
4695
+ values,
4696
+ hasInvalidationListeners
4697
+ );
4698
+ await invalidateQueryCacheDependencies(
4699
+ this.connection,
4700
+ invalidation.dependencies,
4701
+ hasInvalidationListeners ? [
4702
+ createDatabaseMutationEvent(
4703
+ hasValues ? "update" : "delete",
4704
+ this.connection.getConnectionName(),
4705
+ this.source.tableName,
4706
+ this.plan.predicates,
4707
+ hasValues ? values : void 0,
4708
+ capturedRows?.rows,
4709
+ capturedRows?.previousRows
4710
+ )
4711
+ ] : [],
4712
+ invalidation
4713
+ );
3131
4714
  }
3132
4715
  getCompiler() {
3133
4716
  const dialect = this.connection.getDialect();
@@ -3270,10 +4853,11 @@ var TableQueryBuilder = class _TableQueryBuilder {
3270
4853
  }
3271
4854
  async aggregateNumeric(column2, kind) {
3272
4855
  const rows = await this.get();
3273
- if (rows.length === 0) {
3274
- return 0;
4856
+ const result = rows.length === 0 ? 0 : this.extractNumericValues(rows, column2, kind).reduce((sum, value) => sum + value, 0);
4857
+ if (hasActiveDatabaseDependencyCollector()) {
4858
+ rebindDatabaseQueryObservationAggregate(rows, result, Object.freeze({ column: column2, kind }));
3275
4859
  }
3276
- return this.extractNumericValues(rows, column2, kind).reduce((sum, value) => sum + value, 0);
4860
+ return result;
3277
4861
  }
3278
4862
  extractNumericValues(rows, column2, kind) {
3279
4863
  return rows.map((row) => {
@@ -8089,6 +9673,13 @@ var ModelQueryBuilder = class _ModelQueryBuilder {
8089
9673
  }
8090
9674
  async get() {
8091
9675
  const rows = await this.tableQuery.get();
9676
+ const collection = await this.hydrateRows(rows);
9677
+ this.recordCollectionRelationObservations(collection);
9678
+ this.recordCollectionRelationAggregateObservations(collection);
9679
+ this.rebindRowsToSerializedResult(rows, collection);
9680
+ return collection;
9681
+ }
9682
+ async hydrateRows(rows) {
8092
9683
  const hasQueryCasts = Object.keys(this.queryCasts).length > 0;
8093
9684
  let entities = await Promise.all(
8094
9685
  rows.map((row) => hasQueryCasts ? this.repository.retrieveWithCasts(row, this.queryCasts) : this.repository.retrieve(row))
@@ -8100,39 +9691,76 @@ var ModelQueryBuilder = class _ModelQueryBuilder {
8100
9691
  return this.repository.createCollection(entities);
8101
9692
  }
8102
9693
  async getJson() {
8103
- return (await this.get()).toJSON();
9694
+ const rows = await this.tableQuery.get();
9695
+ const collection = await this.hydrateRows(rows);
9696
+ this.recordCollectionRelationObservations(collection);
9697
+ this.recordCollectionRelationAggregateObservations(collection);
9698
+ const data = collection.toJSON();
9699
+ this.rebindRowsToSerializedResult(rows, data);
9700
+ return data;
8104
9701
  }
8105
9702
  async first() {
8106
- const [entity] = await this.limit(1).get();
9703
+ const query = this.limit(1);
9704
+ const { collection, rows } = await query.getRowsAndEntities();
9705
+ const entity = collection[0];
9706
+ query.recordSingleRelationObservations(entity);
9707
+ query.recordSingleRelationAggregateObservations(entity);
9708
+ query.rebindRowsToSerializedResult(rows, entity);
8107
9709
  return entity;
8108
9710
  }
8109
9711
  async firstJson() {
8110
- return (await this.first())?.toJSON();
9712
+ const query = this.limit(1);
9713
+ const { collection, rows } = await query.getRowsAndEntities();
9714
+ const data = collection[0]?.toJSON();
9715
+ query.recordSingleRelationObservations(collection[0]);
9716
+ query.recordSingleRelationAggregateObservations(collection[0]);
9717
+ query.rebindRowsToSerializedResult(rows, data);
9718
+ return data;
8111
9719
  }
8112
9720
  async sole() {
8113
- const entities = await this.limit(2).get();
8114
- if (entities.length === 0) {
9721
+ const query = this.limit(2);
9722
+ const { collection, rows } = await query.getRowsAndEntities();
9723
+ if (collection.length === 0) {
8115
9724
  throw new ModelNotFoundException(this.repository.definition.name, `${this.repository.definition.name} query expected exactly one result but found 0.`);
8116
9725
  }
8117
- if (entities.length !== 1) {
8118
- throw new HydrationError(`${this.repository.definition.name} query expected exactly one result but found ${entities.length}.`);
9726
+ if (collection.length !== 1) {
9727
+ throw new HydrationError(`${this.repository.definition.name} query expected exactly one result but found ${collection.length}.`);
8119
9728
  }
8120
- return entities[0];
9729
+ const entity = collection[0];
9730
+ query.recordSingleRelationObservations(entity);
9731
+ query.recordSingleRelationAggregateObservations(entity);
9732
+ query.rebindRowsToSerializedResult(rows, entity);
9733
+ return entity;
8121
9734
  }
8122
9735
  async soleJson() {
8123
- return (await this.sole()).toJSON();
9736
+ const query = this.limit(2);
9737
+ const { collection, rows } = await query.getRowsAndEntities();
9738
+ if (collection.length === 0) {
9739
+ throw new ModelNotFoundException(this.repository.definition.name, `${this.repository.definition.name} query expected exactly one result but found 0.`);
9740
+ }
9741
+ if (collection.length !== 1) {
9742
+ throw new HydrationError(`${this.repository.definition.name} query expected exactly one result but found ${collection.length}.`);
9743
+ }
9744
+ const data = collection[0].toJSON();
9745
+ query.recordSingleRelationObservations(collection[0]);
9746
+ query.recordSingleRelationAggregateObservations(collection[0]);
9747
+ query.rebindRowsToSerializedResult(rows, data);
9748
+ return data;
8124
9749
  }
8125
9750
  async paginate(perPage = 15, page = 1, options = {}) {
8126
9751
  assertPositiveInteger(perPage, "Per-page value", (message) => new HydrationError(message));
8127
9752
  assertPositiveInteger(page, "Page", (message) => new HydrationError(message));
8128
9753
  const pageName = normalizePaginationParameterName(options.pageName, "page", (message) => new HydrationError(message));
8129
- const entities = await this.getUnpaginatedEntities();
9754
+ const { collection: entities, rows } = await this.getUnpaginatedRowsAndEntities();
8130
9755
  const total = entities.length;
8131
9756
  const offset = (page - 1) * perPage;
8132
9757
  const data = entities.slice(offset, offset + perPage);
8133
9758
  const from = data.length === 0 ? null : offset + 1;
8134
9759
  const to = data.length === 0 ? null : offset + data.length;
8135
- return createPaginator(this.repository.createCollection(data), {
9760
+ const collection = this.repository.createCollection(data);
9761
+ this.recordPaginatedRelationObservations(collection);
9762
+ this.recordPaginatedRelationAggregateObservations(collection);
9763
+ const result = createPaginator(collection, {
8136
9764
  total,
8137
9765
  perPage,
8138
9766
  pageName,
@@ -8142,22 +9770,50 @@ var ModelQueryBuilder = class _ModelQueryBuilder {
8142
9770
  to,
8143
9771
  hasMorePages: offset + data.length < total
8144
9772
  });
9773
+ this.rebindRowsToPaginatedResult(rows, result.data, result.meta, page, pageName, perPage, total, offset);
9774
+ return result;
8145
9775
  }
8146
9776
  async paginateJson(perPage = 15, page = 1, options = {}) {
8147
- return (await this.paginate(perPage, page, options)).toJSON();
9777
+ assertPositiveInteger(perPage, "Per-page value", (message) => new HydrationError(message));
9778
+ assertPositiveInteger(page, "Page", (message) => new HydrationError(message));
9779
+ const pageName = normalizePaginationParameterName(options.pageName, "page", (message) => new HydrationError(message));
9780
+ const { collection, rows } = await this.getUnpaginatedRowsAndEntities();
9781
+ const total = collection.length;
9782
+ const offset = (page - 1) * perPage;
9783
+ const pageCollection = this.repository.createCollection(collection.slice(offset, offset + perPage));
9784
+ const data = pageCollection.toJSON();
9785
+ this.recordPaginatedRelationObservations(pageCollection);
9786
+ this.recordPaginatedRelationAggregateObservations(pageCollection);
9787
+ const from = data.length === 0 ? null : offset + 1;
9788
+ const to = data.length === 0 ? null : offset + data.length;
9789
+ const result = createPaginator(data, {
9790
+ total,
9791
+ perPage,
9792
+ pageName,
9793
+ currentPage: page,
9794
+ lastPage: Math.max(1, Math.ceil(total / perPage)),
9795
+ from,
9796
+ to,
9797
+ hasMorePages: offset + data.length < total
9798
+ }).toJSON();
9799
+ this.rebindRowsToPaginatedResult(rows, result.data, result.meta, page, pageName, perPage, total, offset);
9800
+ return result;
8148
9801
  }
8149
9802
  async simplePaginate(perPage = 15, page = 1, options = {}) {
8150
9803
  assertPositiveInteger(perPage, "Per-page value", (message) => new HydrationError(message));
8151
9804
  assertPositiveInteger(page, "Page", (message) => new HydrationError(message));
8152
9805
  const pageName = normalizePaginationParameterName(options.pageName, "page", (message) => new HydrationError(message));
8153
- const entities = await this.getUnpaginatedEntities();
9806
+ const { collection: entities, rows } = await this.getUnpaginatedRowsAndEntities();
8154
9807
  const offset = (page - 1) * perPage;
8155
9808
  const pageEntities = entities.slice(offset, offset + perPage + 1);
8156
9809
  const hasMorePages = pageEntities.length > perPage;
8157
9810
  const data = hasMorePages ? pageEntities.slice(0, perPage) : pageEntities;
8158
9811
  const from = data.length === 0 ? null : offset + 1;
8159
9812
  const to = data.length === 0 ? null : offset + data.length;
8160
- return createSimplePaginator(this.repository.createCollection(data), {
9813
+ const collection = this.repository.createCollection(data);
9814
+ this.recordPaginatedRelationObservations(collection);
9815
+ this.recordPaginatedRelationAggregateObservations(collection);
9816
+ const result = createSimplePaginator(collection, {
8161
9817
  perPage,
8162
9818
  pageName,
8163
9819
  currentPage: page,
@@ -8165,9 +9821,33 @@ var ModelQueryBuilder = class _ModelQueryBuilder {
8165
9821
  to,
8166
9822
  hasMorePages
8167
9823
  });
9824
+ this.rebindRowsToSimplePaginatedResult(rows, result.data, result.meta, page, pageName, perPage, entities.length, hasMorePages, offset);
9825
+ return result;
8168
9826
  }
8169
9827
  async simplePaginateJson(perPage = 15, page = 1, options = {}) {
8170
- return (await this.simplePaginate(perPage, page, options)).toJSON();
9828
+ assertPositiveInteger(perPage, "Per-page value", (message) => new HydrationError(message));
9829
+ assertPositiveInteger(page, "Page", (message) => new HydrationError(message));
9830
+ const pageName = normalizePaginationParameterName(options.pageName, "page", (message) => new HydrationError(message));
9831
+ const { collection, rows } = await this.getUnpaginatedRowsAndEntities();
9832
+ const offset = (page - 1) * perPage;
9833
+ const pageEntities = collection.slice(offset, offset + perPage + 1);
9834
+ const hasMorePages = pageEntities.length > perPage;
9835
+ const pageCollection = this.repository.createCollection(hasMorePages ? pageEntities.slice(0, perPage) : pageEntities);
9836
+ const data = pageCollection.toJSON();
9837
+ this.recordPaginatedRelationObservations(pageCollection);
9838
+ this.recordPaginatedRelationAggregateObservations(pageCollection);
9839
+ const from = data.length === 0 ? null : offset + 1;
9840
+ const to = data.length === 0 ? null : offset + data.length;
9841
+ const result = createSimplePaginator(data, {
9842
+ perPage,
9843
+ pageName,
9844
+ currentPage: page,
9845
+ from,
9846
+ to,
9847
+ hasMorePages
9848
+ }).toJSON();
9849
+ this.rebindRowsToSimplePaginatedResult(rows, result.data, result.meta, page, pageName, perPage, collection.length, hasMorePages, offset);
9850
+ return result;
8171
9851
  }
8172
9852
  async cursorPaginate(perPage = 15, cursor = null, options = {}) {
8173
9853
  assertPositiveInteger(perPage, "Per-page value", (message) => new HydrationError(message));
@@ -8175,7 +9855,7 @@ var ModelQueryBuilder = class _ModelQueryBuilder {
8175
9855
  const decodedCursor = decodeValueCursor(cursor, (message) => new HydrationError(message));
8176
9856
  const orderedQuery = this.prepareCursorPaginationQuery();
8177
9857
  const cursorOrders = orderedQuery.resolveCursorOrders();
8178
- const entities = await orderedQuery.getUnpaginatedEntities();
9858
+ const { collection: entities, rows } = await orderedQuery.getUnpaginatedRowsAndEntities();
8179
9859
  const filteredEntities = decodedCursor ? entities.filter((entity) => isRowAfterCursor(
8180
9860
  cursorOrders.map((order) => orderedQuery.readCursorColumnValue(entity, order.column)),
8181
9861
  decodedCursor.values,
@@ -8185,15 +9865,65 @@ var ModelQueryBuilder = class _ModelQueryBuilder {
8185
9865
  const hasMorePages = pageEntities.length > perPage;
8186
9866
  const data = hasMorePages ? pageEntities.slice(0, perPage) : pageEntities;
8187
9867
  const lastEntity = data.at(-1);
8188
- return createCursorPaginator(this.repository.createCollection(data), {
9868
+ const collection = orderedQuery.repository.createCollection(data);
9869
+ const observedPageRows = orderedQuery.repository.createCollection(pageEntities).toJSON();
9870
+ orderedQuery.recordPaginatedRelationObservations(collection);
9871
+ orderedQuery.recordPaginatedRelationAggregateObservations(collection);
9872
+ const result = createCursorPaginator(collection, {
8189
9873
  perPage,
8190
9874
  cursorName,
8191
9875
  nextCursor: hasMorePages && lastEntity ? encodeValueCursor(cursorOrders.map((order) => orderedQuery.readCursorColumnValue(lastEntity, order.column))) : null,
8192
9876
  prevCursor: cursor
8193
9877
  });
9878
+ if (cursor === null) {
9879
+ orderedQuery.rebindRowsToCursorPaginatedResult(rows, result.data, {
9880
+ cursorName: result.cursorName,
9881
+ nextCursor: result.nextCursor,
9882
+ perPage: result.perPage,
9883
+ prevCursor: result.prevCursor
9884
+ }, observedPageRows, entities.length, hasMorePages);
9885
+ } else {
9886
+ orderedQuery.rebindRowsToSerializedResult(rows, collection);
9887
+ }
9888
+ return result;
8194
9889
  }
8195
9890
  async cursorPaginateJson(perPage = 15, cursor = null, options = {}) {
8196
- return (await this.cursorPaginate(perPage, cursor, options)).toJSON();
9891
+ assertPositiveInteger(perPage, "Per-page value", (message) => new HydrationError(message));
9892
+ const cursorName = normalizePaginationParameterName(options.cursorName, "cursor", (message) => new HydrationError(message));
9893
+ const decodedCursor = decodeValueCursor(cursor, (message) => new HydrationError(message));
9894
+ const orderedQuery = this.prepareCursorPaginationQuery();
9895
+ const cursorOrders = orderedQuery.resolveCursorOrders();
9896
+ const { collection, rows } = await orderedQuery.getUnpaginatedRowsAndEntities();
9897
+ const filteredEntities = decodedCursor ? collection.filter((entity) => isRowAfterCursor(
9898
+ cursorOrders.map((order) => orderedQuery.readCursorColumnValue(entity, order.column)),
9899
+ decodedCursor.values,
9900
+ cursorOrders
9901
+ )) : collection;
9902
+ const pageEntities = filteredEntities.slice(0, perPage + 1);
9903
+ const hasMorePages = pageEntities.length > perPage;
9904
+ const pageCollection = orderedQuery.repository.createCollection(hasMorePages ? pageEntities.slice(0, perPage) : pageEntities);
9905
+ const data = pageCollection.toJSON();
9906
+ const observedPageRows = orderedQuery.repository.createCollection(pageEntities).toJSON();
9907
+ const lastEntity = pageCollection.at(-1);
9908
+ orderedQuery.recordPaginatedRelationObservations(pageCollection);
9909
+ orderedQuery.recordPaginatedRelationAggregateObservations(pageCollection);
9910
+ const result = createCursorPaginator(data, {
9911
+ perPage,
9912
+ cursorName,
9913
+ nextCursor: hasMorePages && lastEntity ? encodeValueCursor(cursorOrders.map((order) => orderedQuery.readCursorColumnValue(lastEntity, order.column))) : null,
9914
+ prevCursor: cursor
9915
+ }).toJSON();
9916
+ if (cursor === null) {
9917
+ orderedQuery.rebindRowsToCursorPaginatedResult(rows, result.data, {
9918
+ cursorName: result.cursorName,
9919
+ nextCursor: result.nextCursor,
9920
+ perPage: result.perPage,
9921
+ prevCursor: result.prevCursor
9922
+ }, observedPageRows, collection.length, hasMorePages);
9923
+ } else {
9924
+ orderedQuery.rebindRowsToSerializedResult(rows, result.data);
9925
+ }
9926
+ return result;
8197
9927
  }
8198
9928
  async chunk(size, callback) {
8199
9929
  assertPositiveInteger(size, "Chunk size", (message) => new HydrationError(message));
@@ -8257,21 +9987,42 @@ var ModelQueryBuilder = class _ModelQueryBuilder {
8257
9987
  }
8258
9988
  }
8259
9989
  async count() {
8260
- return (await this.get()).length;
9990
+ const collection = await this.get();
9991
+ const result = collection.length;
9992
+ rebindDatabaseQueryObservationAggregate(collection, result, Object.freeze({ kind: "count" }));
9993
+ return result;
8261
9994
  }
8262
9995
  async exists() {
8263
- return await this.count() > 0;
9996
+ const count = await this.count();
9997
+ const result = count > 0;
9998
+ rebindDatabaseQueryObservationAggregate(count, result, Object.freeze({
9999
+ count,
10000
+ kind: "count",
10001
+ output: "boolean"
10002
+ }));
10003
+ return result;
8264
10004
  }
8265
10005
  async doesntExist() {
8266
- return !await this.exists();
10006
+ const count = await this.count();
10007
+ const result = count === 0;
10008
+ rebindDatabaseQueryObservationAggregate(count, result, Object.freeze({
10009
+ count,
10010
+ kind: "count",
10011
+ output: "inverseBoolean"
10012
+ }));
10013
+ return result;
8267
10014
  }
8268
10015
  async pluck(column2) {
8269
- const entities = await this.get();
8270
- return entities.map((entity) => entity.get(column2));
10016
+ const { collection, rows } = await this.getRowsAndEntities();
10017
+ const result = collection.map((entity) => entity.get(column2));
10018
+ rebindDatabaseQueryObservationScalarList(rows, result, column2);
10019
+ return result;
8271
10020
  }
8272
10021
  async value(column2) {
8273
10022
  const entity = await this.first();
8274
- return entity?.get(column2);
10023
+ const value = entity?.get(column2);
10024
+ rebindDatabaseQueryObservationScalar(entity, value, column2);
10025
+ return value;
8275
10026
  }
8276
10027
  async valueOrFail(column2) {
8277
10028
  const entity = await this.first();
@@ -8282,6 +10033,7 @@ var ModelQueryBuilder = class _ModelQueryBuilder {
8282
10033
  if (typeof value === "undefined") {
8283
10034
  throw new HydrationError(`${this.repository.definition.name} query returned no value for column "${column2}".`);
8284
10035
  }
10036
+ rebindDatabaseQueryObservationScalar(entity, value, column2);
8285
10037
  return value;
8286
10038
  }
8287
10039
  async soleValue(column2) {
@@ -8290,36 +10042,78 @@ var ModelQueryBuilder = class _ModelQueryBuilder {
8290
10042
  if (typeof value === "undefined") {
8291
10043
  throw new HydrationError(`${this.repository.definition.name} query returned no value for column "${column2}".`);
8292
10044
  }
10045
+ rebindDatabaseQueryObservationScalar(entity, value, column2);
8293
10046
  return value;
8294
10047
  }
8295
10048
  async sum(column2) {
8296
10049
  const entities = await this.get();
8297
10050
  if (entities.length === 0) {
10051
+ if (hasActiveDatabaseDependencyCollector()) {
10052
+ rebindDatabaseQueryObservationAggregate(entities, 0, Object.freeze({ column: column2, kind: "sum" }));
10053
+ }
8298
10054
  return 0;
8299
10055
  }
8300
- return this.extractNumericValues(entities, column2, "sum").reduce((sum, value) => sum + value, 0);
10056
+ const result = this.extractNumericValues(entities, column2, "sum").reduce((sum, value) => sum + value, 0);
10057
+ if (hasActiveDatabaseDependencyCollector()) {
10058
+ rebindDatabaseQueryObservationAggregate(entities, result, Object.freeze({ column: column2, kind: "sum" }));
10059
+ }
10060
+ return result;
8301
10061
  }
8302
10062
  async avg(column2) {
8303
10063
  const entities = await this.get();
8304
10064
  if (entities.length === 0) {
10065
+ if (hasActiveDatabaseDependencyCollector()) {
10066
+ rebindDatabaseQueryObservationAggregate(entities, null, Object.freeze({ column: column2, count: 0, kind: "avg", sum: 0 }));
10067
+ }
8305
10068
  return null;
8306
10069
  }
8307
10070
  const values = this.extractNumericValues(entities, column2, "avg");
8308
- return values.reduce((sum, value) => sum + value, 0) / values.length;
10071
+ const sum = values.reduce((total, value) => total + value, 0);
10072
+ const result = sum / values.length;
10073
+ if (hasActiveDatabaseDependencyCollector()) {
10074
+ rebindDatabaseQueryObservationAggregate(entities, result, Object.freeze({ column: column2, count: values.length, kind: "avg", sum }));
10075
+ }
10076
+ return result;
8309
10077
  }
8310
10078
  async min(column2) {
8311
10079
  const entities = await this.get();
8312
10080
  if (entities.length === 0) {
10081
+ if (hasActiveDatabaseDependencyCollector()) {
10082
+ rebindDatabaseQueryObservationAggregate(entities, null, Object.freeze({ column: column2, kind: "min" }));
10083
+ }
8313
10084
  return null;
8314
10085
  }
8315
- return Math.min(...this.extractNumericValues(entities, column2, "min"));
10086
+ const values = this.extractNumericValues(entities, column2, "min");
10087
+ const result = Math.min(...values);
10088
+ if (hasActiveDatabaseDependencyCollector()) {
10089
+ rebindDatabaseQueryObservationAggregate(entities, result, Object.freeze({
10090
+ column: column2,
10091
+ currentValueCount: values.filter((value) => value === result).length,
10092
+ kind: "min",
10093
+ valueCounts: createAggregateValueCounts(values)
10094
+ }));
10095
+ }
10096
+ return result;
8316
10097
  }
8317
10098
  async max(column2) {
8318
10099
  const entities = await this.get();
8319
10100
  if (entities.length === 0) {
10101
+ if (hasActiveDatabaseDependencyCollector()) {
10102
+ rebindDatabaseQueryObservationAggregate(entities, null, Object.freeze({ column: column2, kind: "max" }));
10103
+ }
8320
10104
  return null;
8321
10105
  }
8322
- return Math.max(...this.extractNumericValues(entities, column2, "max"));
10106
+ const values = this.extractNumericValues(entities, column2, "max");
10107
+ const result = Math.max(...values);
10108
+ if (hasActiveDatabaseDependencyCollector()) {
10109
+ rebindDatabaseQueryObservationAggregate(entities, result, Object.freeze({
10110
+ column: column2,
10111
+ currentValueCount: values.filter((value) => value === result).length,
10112
+ kind: "max",
10113
+ valueCounts: createAggregateValueCounts(values)
10114
+ }));
10115
+ }
10116
+ return result;
8323
10117
  }
8324
10118
  async firstOrFail() {
8325
10119
  const entity = await this.first();
@@ -8592,68 +10386,296 @@ var ModelQueryBuilder = class _ModelQueryBuilder {
8592
10386
  column: column2
8593
10387
  }));
8594
10388
  }
8595
- parseAggregateRelation(spec) {
8596
- const [relationPart, aliasPart] = spec.split(/\s+as\s+/i);
8597
- const relation = relationPart?.trim();
8598
- const alias = aliasPart?.trim();
8599
- if (!relation) {
8600
- throw new HydrationError("Aggregate relation names cannot be empty.");
10389
+ parseAggregateRelation(spec) {
10390
+ const [relationPart, aliasPart] = spec.split(/\s+as\s+/i);
10391
+ const relation = relationPart?.trim();
10392
+ const alias = aliasPart?.trim();
10393
+ if (!relation) {
10394
+ throw new HydrationError("Aggregate relation names cannot be empty.");
10395
+ }
10396
+ if (typeof aliasPart !== "undefined" && !alias) {
10397
+ throw new HydrationError("Aggregate relation aliases cannot be empty.");
10398
+ }
10399
+ return alias ? { relation, alias } : { relation };
10400
+ }
10401
+ resolveBelongsToRelation(relatedEntity, relationName) {
10402
+ const resolvedName = this.resolveBelongsToRelationName(relatedEntity, relationName);
10403
+ const relation = this.repository.getRelationDefinition(resolvedName);
10404
+ if (relation.kind !== "belongsTo") {
10405
+ throw new RelationError(`Relation "${resolvedName}" on model "${this.repository.definition.name}" is not a belongs-to relation.`);
10406
+ }
10407
+ return relation;
10408
+ }
10409
+ applyBelongsToFilter(relatedEntity, relationName, boolean) {
10410
+ const resolvedRelationName = this.resolveBelongsToRelationName(relatedEntity, relationName);
10411
+ const relation = this.resolveBelongsToRelation(relatedEntity, resolvedRelationName);
10412
+ const ownerValue = relatedEntity.get(relation.ownerKey);
10413
+ if (ownerValue === null || typeof ownerValue === "undefined") {
10414
+ return boolean === "and" ? this.whereDoesntHave(resolvedRelationName) : this.orWhereDoesntHave(resolvedRelationName);
10415
+ }
10416
+ return boolean === "and" ? this.whereHas(resolvedRelationName, (query) => query.where(relation.ownerKey, ownerValue)) : this.orWhereHas(resolvedRelationName, (query) => query.where(relation.ownerKey, ownerValue));
10417
+ }
10418
+ resolveBelongsToRelationName(relatedEntity, relationName) {
10419
+ if (relationName) {
10420
+ return relationName;
10421
+ }
10422
+ const relatedDefinition = relatedEntity.getRepository().definition;
10423
+ const candidates = Object.entries(this.repository.definition.relations).filter(([, relation]) => relation.kind === "belongsTo").map(([name, relation]) => [name, relation]).filter(([, relation]) => {
10424
+ const ref = relation.related();
10425
+ const definition = "definition" in ref ? ref.definition : ref;
10426
+ return definition.table.tableName === relatedDefinition.table.tableName;
10427
+ }).map(([name]) => name);
10428
+ if (candidates.length === 1) {
10429
+ return candidates[0];
10430
+ }
10431
+ if (candidates.length === 0) {
10432
+ throw new RelationError(
10433
+ `No belongs-to relation on model "${this.repository.definition.name}" matches related model "${relatedDefinition.name}".`
10434
+ );
10435
+ }
10436
+ throw new RelationError(
10437
+ `Multiple belongs-to relations on model "${this.repository.definition.name}" match related model "${relatedDefinition.name}". Specify the relation name explicitly.`
10438
+ );
10439
+ }
10440
+ extractNumericValues(entities, column2, kind) {
10441
+ return entities.map((entity) => {
10442
+ const value = entity.get(column2);
10443
+ if (typeof value !== "number" || Number.isNaN(value)) {
10444
+ throw new HydrationError(`Model aggregate "${kind}" requires numeric values for column "${column2}".`);
10445
+ }
10446
+ return value;
10447
+ });
10448
+ }
10449
+ async getUnpaginatedEntities() {
10450
+ return this.clone(this.tableQuery.limit(void 0).offset(void 0)).get();
10451
+ }
10452
+ async getUnpaginatedRowsAndEntities() {
10453
+ const query = this.clone(this.tableQuery.limit(void 0).offset(void 0));
10454
+ return await query.getRowsAndEntities();
10455
+ }
10456
+ async getRowsAndEntities() {
10457
+ const rows = await this.tableQuery.get();
10458
+ return {
10459
+ collection: await this.hydrateRows(rows),
10460
+ rows
10461
+ };
10462
+ }
10463
+ recordCollectionRelationAggregateObservations(collection) {
10464
+ this.repository.recordRelationAggregateObservations(
10465
+ collection,
10466
+ this.aggregateLoads,
10467
+ (index, key) => Object.freeze([index, key])
10468
+ );
10469
+ }
10470
+ recordCollectionRelationObservations(collection) {
10471
+ this.repository.recordRelationObservations(
10472
+ collection,
10473
+ this.eagerLoads,
10474
+ (index, relationName) => Object.freeze([index, relationName])
10475
+ );
10476
+ }
10477
+ recordSingleRelationObservations(entity) {
10478
+ if (!entity) {
10479
+ return;
10480
+ }
10481
+ this.repository.recordRelationObservations(
10482
+ [entity],
10483
+ this.eagerLoads,
10484
+ (_index, relationName) => Object.freeze([relationName])
10485
+ );
10486
+ }
10487
+ recordPaginatedRelationObservations(collection) {
10488
+ this.repository.recordRelationObservations(
10489
+ collection,
10490
+ this.eagerLoads,
10491
+ (index, relationName) => Object.freeze(["data", index, relationName])
10492
+ );
10493
+ }
10494
+ recordSingleRelationAggregateObservations(entity) {
10495
+ if (!entity) {
10496
+ return;
10497
+ }
10498
+ this.repository.recordRelationAggregateObservations(
10499
+ [entity],
10500
+ this.aggregateLoads,
10501
+ (_index, key) => Object.freeze([key])
10502
+ );
10503
+ }
10504
+ recordPaginatedRelationAggregateObservations(collection) {
10505
+ this.repository.recordRelationAggregateObservations(
10506
+ collection,
10507
+ this.aggregateLoads,
10508
+ (index, key) => Object.freeze(["data", index, key])
10509
+ );
10510
+ }
10511
+ canBindRowsToSerializedResultBase() {
10512
+ return Object.keys(this.repository.definition.accessors).length === 0 && Object.keys(this.repository.definition.casts).length === 0 && Object.keys(this.queryCasts).length === 0 && this.repository.definition.appended.length === 0 && this.repository.definition.hidden.length === 0 && this.repository.definition.visible.length === 0 && this.relationFilters.length === 0 && this.aggregateLoads.length === 0;
10513
+ }
10514
+ canBindRowsToSerializedResult() {
10515
+ return this.canBindRowsToSerializedResultBase() && this.eagerLoads.length === 0;
10516
+ }
10517
+ createPatchableEagerRelationHydrations() {
10518
+ if (!this.canBindRowsToSerializedResultBase() || this.eagerLoads.length === 0) {
10519
+ return void 0;
10520
+ }
10521
+ const belongsToHydrations = [];
10522
+ const relatedHydrations = [];
10523
+ for (const load of this.eagerLoads) {
10524
+ if (load.relation.includes(".")) {
10525
+ return void 0;
10526
+ }
10527
+ const relation = this.repository.getRelationDefinition(load.relation);
10528
+ if (relation.kind === "belongsTo") {
10529
+ if (relation.constraint || load.constraint) {
10530
+ return void 0;
10531
+ }
10532
+ const related = relation.related();
10533
+ const relatedDefinition = "definition" in related ? related.definition : related;
10534
+ belongsToHydrations.push(Object.freeze({
10535
+ foreignKey: relation.foreignKey,
10536
+ ownerKey: relation.ownerKey,
10537
+ relationKey: load.relation,
10538
+ relatedConnectionName: relatedDefinition.connectionName ?? this.getConnectionName(),
10539
+ relatedTableName: relatedDefinition.table.tableName
10540
+ }));
10541
+ continue;
10542
+ }
10543
+ if (relation.kind === "hasMany" || relation.kind === "hasOne") {
10544
+ const hydrationQuery = this.createPatchableRelatedHydrationQuery(relation, load.constraint);
10545
+ if (!hydrationQuery) {
10546
+ return void 0;
10547
+ }
10548
+ const related = relation.related();
10549
+ const relatedDefinition = "definition" in related ? related.definition : related;
10550
+ relatedHydrations.push(Object.freeze({
10551
+ foreignKey: relation.foreignKey,
10552
+ kind: relation.kind,
10553
+ localKey: relation.localKey,
10554
+ orderBy: hydrationQuery.orderBy,
10555
+ predicates: hydrationQuery.predicates,
10556
+ relationKey: load.relation,
10557
+ relatedConnectionName: relatedDefinition.connectionName ?? this.getConnectionName(),
10558
+ relatedTableName: relatedDefinition.table.tableName
10559
+ }));
10560
+ continue;
10561
+ }
10562
+ return void 0;
10563
+ }
10564
+ return Object.freeze({
10565
+ belongsToHydrations: belongsToHydrations.length > 0 ? Object.freeze(belongsToHydrations) : void 0,
10566
+ relatedHydrations: relatedHydrations.length > 0 ? Object.freeze(relatedHydrations) : void 0
10567
+ });
10568
+ }
10569
+ createPatchableRelatedHydrationQuery(relation, constraint) {
10570
+ if (!relation.constraint && !constraint) {
10571
+ return Object.freeze({
10572
+ orderBy: Object.freeze([]),
10573
+ predicates: Object.freeze([])
10574
+ });
8601
10575
  }
8602
- if (typeof aliasPart !== "undefined" && !alias) {
8603
- throw new HydrationError("Aggregate relation aliases cannot be empty.");
10576
+ const related = relation.related();
10577
+ if (!this.isQueryableModelReference(related)) {
10578
+ return void 0;
8604
10579
  }
8605
- return alias ? { relation, alias } : { relation };
8606
- }
8607
- resolveBelongsToRelation(relatedEntity, relationName) {
8608
- const resolvedName = this.resolveBelongsToRelationName(relatedEntity, relationName);
8609
- const relation = this.repository.getRelationDefinition(resolvedName);
8610
- if (relation.kind !== "belongsTo") {
8611
- throw new RelationError(`Relation "${resolvedName}" on model "${this.repository.definition.name}" is not a belongs-to relation.`);
10580
+ let query = related.query();
10581
+ const relationResult = relation.constraint?.(query);
10582
+ query = relationResult instanceof _ModelQueryBuilder ? relationResult : query;
10583
+ if (constraint) {
10584
+ const result = constraint(query);
10585
+ query = result instanceof _ModelQueryBuilder ? result : query;
10586
+ }
10587
+ const plan = query.getTableQueryBuilder().getPlan();
10588
+ const relatedDefinition = "definition" in related ? related.definition : related;
10589
+ const observation = createDatabaseQueryObservation(
10590
+ plan,
10591
+ relatedDefinition.connectionName ?? this.getConnectionName(),
10592
+ Object.freeze([])
10593
+ );
10594
+ if (!observation?.patchable || typeof observation.limit !== "undefined" || typeof observation.offset !== "undefined") {
10595
+ return void 0;
8612
10596
  }
8613
- return relation;
10597
+ return Object.freeze({
10598
+ orderBy: observation.orderBy,
10599
+ predicates: observation.predicates
10600
+ });
8614
10601
  }
8615
- applyBelongsToFilter(relatedEntity, relationName, boolean) {
8616
- const resolvedRelationName = this.resolveBelongsToRelationName(relatedEntity, relationName);
8617
- const relation = this.resolveBelongsToRelation(relatedEntity, resolvedRelationName);
8618
- const ownerValue = relatedEntity.get(relation.ownerKey);
8619
- if (ownerValue === null || typeof ownerValue === "undefined") {
8620
- return boolean === "and" ? this.whereDoesntHave(resolvedRelationName) : this.orWhereDoesntHave(resolvedRelationName);
8621
- }
8622
- return boolean === "and" ? this.whereHas(resolvedRelationName, (query) => query.where(relation.ownerKey, ownerValue)) : this.orWhereHas(resolvedRelationName, (query) => query.where(relation.ownerKey, ownerValue));
10602
+ isQueryableModelReference(value) {
10603
+ return "query" in value && typeof value.query === "function";
8623
10604
  }
8624
- resolveBelongsToRelationName(relatedEntity, relationName) {
8625
- if (relationName) {
8626
- return relationName;
10605
+ rebindRowsToSerializedResult(rows, result) {
10606
+ if (!hasActiveDatabaseDependencyCollector()) {
10607
+ return;
8627
10608
  }
8628
- const relatedDefinition = relatedEntity.getRepository().definition;
8629
- const candidates = Object.entries(this.repository.definition.relations).filter(([, relation]) => relation.kind === "belongsTo").map(([name, relation]) => [name, relation]).filter(([, relation]) => {
8630
- const ref = relation.related();
8631
- const definition = "definition" in ref ? ref.definition : ref;
8632
- return definition.table.tableName === relatedDefinition.table.tableName;
8633
- }).map(([name]) => name);
8634
- if (candidates.length === 1) {
8635
- return candidates[0];
10609
+ if (this.canBindRowsToSerializedResult()) {
10610
+ rebindDatabaseQueryObservationResult(rows, result);
10611
+ return;
8636
10612
  }
8637
- if (candidates.length === 0) {
8638
- throw new RelationError(
8639
- `No belongs-to relation on model "${this.repository.definition.name}" matches related model "${relatedDefinition.name}".`
10613
+ const hydrations = this.createPatchableEagerRelationHydrations();
10614
+ if (hydrations) {
10615
+ rebindDatabaseQueryObservationHydratedResult(
10616
+ rows,
10617
+ result,
10618
+ hydrations.belongsToHydrations ?? Object.freeze([]),
10619
+ hydrations.relatedHydrations
8640
10620
  );
10621
+ return;
8641
10622
  }
8642
- throw new RelationError(
8643
- `Multiple belongs-to relations on model "${this.repository.definition.name}" match related model "${relatedDefinition.name}". Specify the relation name explicitly.`
8644
- );
10623
+ disableDatabaseQueryObservationPatching(rows);
8645
10624
  }
8646
- extractNumericValues(entities, column2, kind) {
8647
- return entities.map((entity) => {
8648
- const value = entity.get(column2);
8649
- if (typeof value !== "number" || Number.isNaN(value)) {
8650
- throw new HydrationError(`Model aggregate "${kind}" requires numeric values for column "${column2}".`);
8651
- }
8652
- return value;
8653
- });
10625
+ rebindRowsToPaginatedResult(rows, data, meta, currentPage, pageName, perPage, total, offset) {
10626
+ if (!hasActiveDatabaseDependencyCollector()) {
10627
+ return;
10628
+ }
10629
+ const hydrations = this.createPatchableEagerRelationHydrations();
10630
+ if (!this.canBindRowsToSerializedResult() && !hydrations) {
10631
+ disableDatabaseQueryObservationPatching(rows);
10632
+ return;
10633
+ }
10634
+ rebindDatabaseQueryObservationPagination(rows, data, meta, Object.freeze({
10635
+ currentPage,
10636
+ kind: "standard",
10637
+ pageName,
10638
+ perPage,
10639
+ total
10640
+ }), offset, hydrations?.belongsToHydrations, hydrations?.relatedHydrations);
8654
10641
  }
8655
- async getUnpaginatedEntities() {
8656
- return this.clone(this.tableQuery.limit(void 0).offset(void 0)).get();
10642
+ rebindRowsToSimplePaginatedResult(rows, data, meta, currentPage, pageName, perPage, rowCount, hasMorePages, offset) {
10643
+ if (!hasActiveDatabaseDependencyCollector()) {
10644
+ return;
10645
+ }
10646
+ const hydrations = this.createPatchableEagerRelationHydrations();
10647
+ if (!this.canBindRowsToSerializedResult() && !hydrations) {
10648
+ disableDatabaseQueryObservationPatching(rows);
10649
+ return;
10650
+ }
10651
+ rebindDatabaseQueryObservationPagination(rows, data, meta, Object.freeze({
10652
+ currentPage,
10653
+ hasMorePages,
10654
+ kind: "simple",
10655
+ pageName,
10656
+ perPage,
10657
+ rowCount
10658
+ }), offset, hydrations?.belongsToHydrations, hydrations?.relatedHydrations);
10659
+ }
10660
+ rebindRowsToCursorPaginatedResult(rows, data, meta, pageRows, rowCount, hasMorePages) {
10661
+ if (!hasActiveDatabaseDependencyCollector()) {
10662
+ return;
10663
+ }
10664
+ const hydrations = this.createPatchableEagerRelationHydrations();
10665
+ if (!this.canBindRowsToSerializedResult() && !hydrations) {
10666
+ disableDatabaseQueryObservationPatching(rows);
10667
+ return;
10668
+ }
10669
+ rebindDatabaseQueryObservationCursorPagination(rows, data, meta, Object.freeze({
10670
+ cursorName: meta.cursorName,
10671
+ hasMorePages,
10672
+ kind: "cursor",
10673
+ nextCursor: meta.nextCursor,
10674
+ perPage: meta.perPage,
10675
+ prevCursor: meta.prevCursor,
10676
+ rows: pageRows,
10677
+ rowCount
10678
+ }), hydrations?.belongsToHydrations, hydrations?.relatedHydrations);
8657
10679
  }
8658
10680
  prepareCursorPaginationQuery() {
8659
10681
  const plan = this.tableQuery.getPlan();
@@ -8716,6 +10738,8 @@ function setAutomaticEagerLoading(definition, value) {
8716
10738
  }
8717
10739
 
8718
10740
  // src/model/ModelRepository.ts
10741
+ var relationAggregateObservationMetadata = /* @__PURE__ */ new WeakMap();
10742
+ var relationQueryObservationMetadata = /* @__PURE__ */ new WeakMap();
8719
10743
  function hasDefinition(reference) {
8720
10744
  return "definition" in reference;
8721
10745
  }
@@ -9060,6 +11084,7 @@ var ModelRepository = class _ModelRepository {
9060
11084
  if (entities.length === 0 || aggregates.length === 0) {
9061
11085
  return;
9062
11086
  }
11087
+ const observationCount = hasActiveDatabaseDependencyCollector() ? readDatabaseQueryObservationCount() : void 0;
9063
11088
  for (const aggregate of aggregates) {
9064
11089
  if (aggregate.relation.includes(".")) {
9065
11090
  throw new SecurityError("Nested relation aggregates are not supported yet.");
@@ -9072,6 +11097,12 @@ var ModelRepository = class _ModelRepository {
9072
11097
  const parentKey = this.getRelationParentValue(entity, relation);
9073
11098
  const count = counts.get(parentKey) ?? 0;
9074
11099
  entity.setComputed(key, aggregate.kind === "count" ? count : count > 0);
11100
+ if (aggregate.kind === "exists" && typeof observationCount === "number") {
11101
+ this.setRelationAggregateObservationMetadata(entity, key, Object.freeze({
11102
+ count,
11103
+ kind: "count"
11104
+ }));
11105
+ }
9075
11106
  }
9076
11107
  continue;
9077
11108
  }
@@ -9081,8 +11112,489 @@ var ModelRepository = class _ModelRepository {
9081
11112
  const values = await this.getRelationAggregateValues(entities, relation, aggregate);
9082
11113
  for (const entity of entities) {
9083
11114
  const parentKey = this.getRelationParentValue(entity, relation);
9084
- entity.setComputed(key, values.get(parentKey) ?? null);
11115
+ const computation = values.get(parentKey);
11116
+ entity.setComputed(key, computation?.value ?? null);
11117
+ if (computation?.metadata && typeof observationCount === "number") {
11118
+ this.setRelationAggregateObservationMetadata(entity, key, computation.metadata);
11119
+ }
11120
+ }
11121
+ }
11122
+ if (typeof observationCount === "number") {
11123
+ truncateDatabaseQueryObservations(observationCount);
11124
+ }
11125
+ }
11126
+ recordRelationAggregateObservations(entities, aggregates, createPath) {
11127
+ if (!hasActiveDatabaseDependencyCollector() || entities.length === 0 || aggregates.length === 0) {
11128
+ return;
11129
+ }
11130
+ for (const aggregate of aggregates) {
11131
+ if (aggregate.relation.includes(".")) {
11132
+ continue;
11133
+ }
11134
+ const relation = this.getRelationDefinition(aggregate.relation);
11135
+ const key = this.getAggregateAttributeKey(aggregate);
11136
+ for (let index = 0; index < entities.length; index += 1) {
11137
+ const entity = entities[index];
11138
+ if (!entity) {
11139
+ continue;
11140
+ }
11141
+ const target = this.createRelationAggregateObservationTarget(entity, relation);
11142
+ if (!target) {
11143
+ continue;
11144
+ }
11145
+ const aggregateObservation = this.createRelationAggregateObservation(aggregate, entity, key);
11146
+ if (!aggregateObservation) {
11147
+ continue;
11148
+ }
11149
+ const predicateDependency = createTablePredicateCacheDependency(
11150
+ target.connectionName,
11151
+ target.tableName,
11152
+ target.column,
11153
+ target.value
11154
+ );
11155
+ const dependencies = predicateDependency ? Object.freeze([
11156
+ createTableCacheDependency(target.connectionName, target.tableName),
11157
+ predicateDependency
11158
+ ]) : Object.freeze([createTableCacheDependency(target.connectionName, target.tableName)]);
11159
+ const resultPath = createPath(index, key);
11160
+ const data = entity.toJSON();
11161
+ recordDatabaseQueryObservation(Object.freeze({
11162
+ aggregate: aggregate.kind === "exists" ? Object.freeze({
11163
+ ...aggregateObservation,
11164
+ output: "boolean"
11165
+ }) : aggregateObservation,
11166
+ connectionName: target.connectionName,
11167
+ dependencies,
11168
+ orderBy: [],
11169
+ patchable: true,
11170
+ predicates: Object.freeze([Object.freeze({
11171
+ column: target.column,
11172
+ operator: "=",
11173
+ value: target.value
11174
+ })]),
11175
+ result: data[key],
11176
+ resultPath,
11177
+ selections: [],
11178
+ tableName: target.tableName
11179
+ }));
11180
+ }
11181
+ }
11182
+ }
11183
+ recordRelationObservations(entities, relations, createPath) {
11184
+ if (!hasActiveDatabaseDependencyCollector() || entities.length === 0 || relations.length === 0) {
11185
+ return;
11186
+ }
11187
+ for (const entry of this.normalizeEagerLoads(relations)) {
11188
+ if (entry.relation.includes(".")) {
11189
+ continue;
11190
+ }
11191
+ const relationName = entry.relation;
11192
+ const relation = this.getRelationDefinition(relationName);
11193
+ if (relation.kind === "hasMany") {
11194
+ this.recordHasManyRelationObservations(entities, relationName, relation, createPath);
11195
+ continue;
11196
+ }
11197
+ if (relation.kind === "hasOne") {
11198
+ this.recordHasOneRelationObservations(entities, relationName, relation, createPath);
11199
+ continue;
11200
+ }
11201
+ if (relation.kind === "belongsTo") {
11202
+ this.recordBelongsToRelationObservations(entities, relationName, relation, createPath);
11203
+ continue;
11204
+ }
11205
+ if (relation.constraint) {
11206
+ continue;
11207
+ }
11208
+ if (relation.kind === "belongsToMany") {
11209
+ this.recordBelongsToManyRelationObservations(entities, relationName, relation, createPath);
11210
+ }
11211
+ }
11212
+ }
11213
+ recordHasManyRelationObservations(entities, relationName, relation, createPath) {
11214
+ const related = this.resolveRelatedRepository(relation.related);
11215
+ for (let index = 0; index < entities.length; index += 1) {
11216
+ const entity = entities[index];
11217
+ if (!entity || !entity.hasRelation(relationName)) {
11218
+ continue;
11219
+ }
11220
+ const observed = this.getRelationQueryObservation(entity, relationName);
11221
+ if (observed) {
11222
+ recordDatabaseQueryObservation(Object.freeze({
11223
+ ...observed,
11224
+ resultPath: createPath(index, relationName)
11225
+ }));
11226
+ continue;
11227
+ }
11228
+ if (relation.constraint) {
11229
+ continue;
9085
11230
  }
11231
+ const parentKey = this.getRelationParentValue(entity, relation);
11232
+ const predicateDependency = createTablePredicateCacheDependency(
11233
+ related.getConnectionName(),
11234
+ related.definition.table.tableName,
11235
+ relation.foreignKey,
11236
+ parentKey
11237
+ );
11238
+ const dependencies = predicateDependency ? Object.freeze([
11239
+ createTableCacheDependency(related.getConnectionName(), related.definition.table.tableName),
11240
+ predicateDependency
11241
+ ]) : Object.freeze([createTableCacheDependency(related.getConnectionName(), related.definition.table.tableName)]);
11242
+ recordDatabaseQueryObservation(Object.freeze({
11243
+ connectionName: related.getConnectionName(),
11244
+ dependencies,
11245
+ orderBy: [],
11246
+ patchable: true,
11247
+ predicates: Object.freeze([Object.freeze({
11248
+ column: relation.foreignKey,
11249
+ operator: "=",
11250
+ value: parentKey
11251
+ })]),
11252
+ resultPath: createPath(index, relationName),
11253
+ selections: [],
11254
+ tableName: related.definition.table.tableName
11255
+ }));
11256
+ }
11257
+ }
11258
+ recordHasOneRelationObservations(entities, relationName, relation, createPath) {
11259
+ const related = this.resolveRelatedRepository(relation.related);
11260
+ for (let index = 0; index < entities.length; index += 1) {
11261
+ const entity = entities[index];
11262
+ if (!entity || !entity.hasRelation(relationName)) {
11263
+ continue;
11264
+ }
11265
+ const resultPath = createPath(index, relationName);
11266
+ const observed = this.getRelationQueryObservation(entity, relationName);
11267
+ if (observed) {
11268
+ recordDatabaseQueryObservation(this.createNullableSingleRecordRelationObservation(observed, resultPath));
11269
+ continue;
11270
+ }
11271
+ if (relation.constraint) {
11272
+ continue;
11273
+ }
11274
+ const parentKey = this.getRelationParentValue(entity, relation);
11275
+ const predicateDependency = createTablePredicateCacheDependency(
11276
+ related.getConnectionName(),
11277
+ related.definition.table.tableName,
11278
+ relation.foreignKey,
11279
+ parentKey
11280
+ );
11281
+ const dependencies = predicateDependency ? Object.freeze([
11282
+ createTableCacheDependency(related.getConnectionName(), related.definition.table.tableName),
11283
+ predicateDependency
11284
+ ]) : Object.freeze([createTableCacheDependency(related.getConnectionName(), related.definition.table.tableName)]);
11285
+ recordDatabaseQueryObservation(this.createNullableSingleRecordRelationObservation(Object.freeze({
11286
+ connectionName: related.getConnectionName(),
11287
+ dependencies,
11288
+ orderBy: [],
11289
+ patchable: true,
11290
+ predicates: Object.freeze([Object.freeze({
11291
+ column: relation.foreignKey,
11292
+ operator: "=",
11293
+ value: parentKey
11294
+ })]),
11295
+ selections: [],
11296
+ tableName: related.definition.table.tableName
11297
+ }), resultPath));
11298
+ }
11299
+ }
11300
+ recordBelongsToRelationObservations(entities, relationName, relation, createPath) {
11301
+ const related = this.resolveRelatedRepository(relation.related);
11302
+ for (let index = 0; index < entities.length; index += 1) {
11303
+ const entity = entities[index];
11304
+ if (!entity || !entity.hasRelation(relationName)) {
11305
+ continue;
11306
+ }
11307
+ const resultPath = createPath(index, relationName);
11308
+ const observed = this.getRelationQueryObservation(entity, relationName);
11309
+ recordDatabaseQueryObservation(this.createBelongsToParentKeyRelationObservation(
11310
+ entity,
11311
+ relationName,
11312
+ relation,
11313
+ resultPath.slice(0, -1),
11314
+ !relation.constraint
11315
+ ));
11316
+ if (observed) {
11317
+ recordDatabaseQueryObservation(this.createNullableSingleRecordRelationObservation(observed, resultPath));
11318
+ continue;
11319
+ }
11320
+ if (relation.constraint) {
11321
+ continue;
11322
+ }
11323
+ const ownerKey = this.getRelationParentValue(entity, relation);
11324
+ if (ownerKey === null || typeof ownerKey === "undefined") {
11325
+ continue;
11326
+ }
11327
+ const predicateDependency = createTablePredicateCacheDependency(
11328
+ related.getConnectionName(),
11329
+ related.definition.table.tableName,
11330
+ relation.ownerKey,
11331
+ ownerKey
11332
+ );
11333
+ const dependencies = predicateDependency ? Object.freeze([
11334
+ createTableCacheDependency(related.getConnectionName(), related.definition.table.tableName),
11335
+ predicateDependency
11336
+ ]) : Object.freeze([createTableCacheDependency(related.getConnectionName(), related.definition.table.tableName)]);
11337
+ recordDatabaseQueryObservation(this.createNullableSingleRecordRelationObservation(Object.freeze({
11338
+ connectionName: related.getConnectionName(),
11339
+ dependencies,
11340
+ orderBy: [],
11341
+ patchable: true,
11342
+ predicates: Object.freeze([Object.freeze({
11343
+ column: relation.ownerKey,
11344
+ operator: "=",
11345
+ value: ownerKey
11346
+ })]),
11347
+ selections: [],
11348
+ tableName: related.definition.table.tableName
11349
+ }), resultPath));
11350
+ }
11351
+ }
11352
+ createBelongsToParentKeyRelationObservation(entity, relationName, relation, resultPath, patchable) {
11353
+ const parentKey = entity.toAttributes()[this.definition.primaryKey];
11354
+ if (parentKey === null || typeof parentKey === "undefined") {
11355
+ return void 0;
11356
+ }
11357
+ const predicateDependency = createTablePredicateCacheDependency(
11358
+ this.getConnectionName(),
11359
+ this.definition.table.tableName,
11360
+ this.definition.primaryKey,
11361
+ parentKey
11362
+ );
11363
+ const dependencies = predicateDependency ? Object.freeze([
11364
+ createTableCacheDependency(this.getConnectionName(), this.definition.table.tableName),
11365
+ predicateDependency
11366
+ ]) : Object.freeze([createTableCacheDependency(this.getConnectionName(), this.definition.table.tableName)]);
11367
+ const related = this.resolveRelatedRepository(relation.related);
11368
+ return Object.freeze({
11369
+ connectionName: this.getConnectionName(),
11370
+ dependencies,
11371
+ orderBy: [],
11372
+ patchable,
11373
+ predicates: Object.freeze([Object.freeze({
11374
+ column: this.definition.primaryKey,
11375
+ operator: "=",
11376
+ value: parentKey
11377
+ })]),
11378
+ relation: Object.freeze({
11379
+ foreignKey: relation.foreignKey,
11380
+ kind: "belongsToParentKey",
11381
+ ownerKey: relation.ownerKey,
11382
+ relationKey: relationName,
11383
+ relatedConnectionName: related.getConnectionName(),
11384
+ relatedTableName: related.definition.table.tableName
11385
+ }),
11386
+ resultPath,
11387
+ selections: Object.freeze([Object.freeze({
11388
+ column: relation.foreignKey,
11389
+ resultKey: relation.foreignKey
11390
+ })]),
11391
+ tableName: this.definition.table.tableName
11392
+ });
11393
+ }
11394
+ createNullableSingleRecordRelationObservation(observation, resultPath) {
11395
+ return Object.freeze({
11396
+ ...observation,
11397
+ emptyRecordValue: null,
11398
+ limit: 1,
11399
+ resultPath,
11400
+ rowWindowMode: "single"
11401
+ });
11402
+ }
11403
+ rememberHasManyRelationObservation(entity, relationName, relation, related, constrainedPlan, parentKey) {
11404
+ const observation = this.createHasManyRelationObservation(relation, related, constrainedPlan, parentKey);
11405
+ if (!observation) {
11406
+ return;
11407
+ }
11408
+ const observations = relationQueryObservationMetadata.get(entity) ?? /* @__PURE__ */ new Map();
11409
+ observations.set(relationName, observation);
11410
+ relationQueryObservationMetadata.set(entity, observations);
11411
+ }
11412
+ rememberBelongsToRelationObservation(entity, relationName, relation, related, constrainedPlan, ownerKey) {
11413
+ const observation = this.createBelongsToRelationObservation(relation, related, constrainedPlan, ownerKey);
11414
+ if (!observation) {
11415
+ return;
11416
+ }
11417
+ const observations = relationQueryObservationMetadata.get(entity) ?? /* @__PURE__ */ new Map();
11418
+ observations.set(relationName, observation);
11419
+ relationQueryObservationMetadata.set(entity, observations);
11420
+ }
11421
+ getRelationQueryObservation(entity, relationName) {
11422
+ return relationQueryObservationMetadata.get(entity)?.get(relationName);
11423
+ }
11424
+ createHasManyRelationObservation(relation, related, constrainedPlan, parentKey) {
11425
+ if (parentKey === null || typeof parentKey === "undefined") {
11426
+ return void 0;
11427
+ }
11428
+ const parentPlan = withPredicate(constrainedPlan, {
11429
+ kind: "comparison",
11430
+ column: relation.foreignKey,
11431
+ operator: "=",
11432
+ value: parentKey
11433
+ });
11434
+ const dependencies = resolveQueryCacheDependencies(parentPlan, related.getConnectionName());
11435
+ return dependencies ? createDatabaseQueryObservation(parentPlan, related.getConnectionName(), dependencies) : void 0;
11436
+ }
11437
+ createBelongsToRelationObservation(relation, related, constrainedPlan, ownerKey) {
11438
+ if (ownerKey === null || typeof ownerKey === "undefined") {
11439
+ return void 0;
11440
+ }
11441
+ const ownerPlan = withPredicate(constrainedPlan, {
11442
+ kind: "comparison",
11443
+ column: relation.ownerKey,
11444
+ operator: "=",
11445
+ value: ownerKey
11446
+ });
11447
+ const dependencies = resolveQueryCacheDependencies(ownerPlan, related.getConnectionName());
11448
+ return dependencies ? createDatabaseQueryObservation(ownerPlan, related.getConnectionName(), dependencies) : void 0;
11449
+ }
11450
+ recordBelongsToManyRelationObservations(entities, relationName, relation, createPath) {
11451
+ const related = this.resolveRelatedRepository(relation.related);
11452
+ const pivotTableName = this.resolvePivotTableName(relation.pivotTable);
11453
+ for (let index = 0; index < entities.length; index += 1) {
11454
+ const entity = entities[index];
11455
+ if (!entity || !entity.hasRelation(relationName)) {
11456
+ continue;
11457
+ }
11458
+ const parentKey = this.getRelationParentValue(entity, relation);
11459
+ const predicateDependency = createTablePredicateCacheDependency(
11460
+ this.getConnectionName(),
11461
+ pivotTableName,
11462
+ relation.foreignPivotKey,
11463
+ parentKey
11464
+ );
11465
+ const relatedMetadata = this.createBelongsToManyRelatedObservationMetadata(entity, relationName, relation, related);
11466
+ const dependencies = predicateDependency ? Object.freeze([
11467
+ createTableCacheDependency(this.getConnectionName(), pivotTableName),
11468
+ predicateDependency,
11469
+ ...relatedMetadata.dependencies
11470
+ ]) : Object.freeze([
11471
+ createTableCacheDependency(this.getConnectionName(), pivotTableName),
11472
+ ...relatedMetadata.dependencies
11473
+ ]);
11474
+ const relationObservation = Object.freeze({
11475
+ foreignPivotKey: relation.foreignPivotKey,
11476
+ kind: "belongsToMany",
11477
+ pivotAccessor: relation.pivotAccessor,
11478
+ pivotColumns: relation.pivotColumns,
11479
+ pivotOrderBy: relation.pivotOrderBy,
11480
+ relatedConnectionName: related.getConnectionName(),
11481
+ relatedKey: relation.relatedKey,
11482
+ relatedPivotKey: relation.relatedPivotKey,
11483
+ relatedTableName: related.definition.table.tableName
11484
+ });
11485
+ recordDatabaseQueryObservation(Object.freeze({
11486
+ connectionName: this.getConnectionName(),
11487
+ dependencies,
11488
+ orderBy: relation.pivotOrderBy,
11489
+ patchable: relation.pivotWheres.length === 0,
11490
+ predicates: Object.freeze([Object.freeze({
11491
+ column: relation.foreignPivotKey,
11492
+ operator: "=",
11493
+ value: parentKey
11494
+ })]),
11495
+ relation: relationObservation,
11496
+ resultPath: createPath(index, relationName),
11497
+ selections: [],
11498
+ tableName: pivotTableName
11499
+ }));
11500
+ if (relatedMetadata.values.length === 0) {
11501
+ continue;
11502
+ }
11503
+ recordDatabaseQueryObservation(Object.freeze({
11504
+ connectionName: related.getConnectionName(),
11505
+ dependencies: Object.freeze([
11506
+ createTableCacheDependency(related.getConnectionName(), related.definition.table.tableName),
11507
+ ...relatedMetadata.dependencies
11508
+ ]),
11509
+ orderBy: [],
11510
+ patchable: true,
11511
+ predicates: Object.freeze([Object.freeze({
11512
+ column: relation.relatedKey,
11513
+ operator: "in",
11514
+ value: relatedMetadata.values
11515
+ })]),
11516
+ relation: relationObservation,
11517
+ resultPath: createPath(index, relationName),
11518
+ selections: [],
11519
+ tableName: related.definition.table.tableName
11520
+ }));
11521
+ }
11522
+ }
11523
+ createBelongsToManyRelatedObservationMetadata(entity, relationName, relation, related) {
11524
+ const loaded = entity.getRelation(relationName);
11525
+ const dependencies = [];
11526
+ const values = [];
11527
+ for (const relatedEntity of loaded) {
11528
+ if (!isEntity2(relatedEntity)) {
11529
+ continue;
11530
+ }
11531
+ const relatedValue = relatedEntity.get(relation.relatedKey);
11532
+ values.push(relatedValue);
11533
+ const dependency = createTablePredicateCacheDependency(
11534
+ related.getConnectionName(),
11535
+ related.definition.table.tableName,
11536
+ relation.relatedKey,
11537
+ relatedValue
11538
+ );
11539
+ if (dependency) {
11540
+ dependencies.push(dependency);
11541
+ }
11542
+ }
11543
+ return Object.freeze({
11544
+ dependencies: Object.freeze(dependencies),
11545
+ values: Object.freeze(values)
11546
+ });
11547
+ }
11548
+ createRelationAggregateObservation(aggregate, entity, key) {
11549
+ const metadata = this.getRelationAggregateObservationMetadata(entity, key);
11550
+ if (metadata) {
11551
+ return metadata;
11552
+ }
11553
+ if (aggregate.kind === "count" || aggregate.kind === "exists") {
11554
+ return Object.freeze({
11555
+ kind: "count"
11556
+ });
11557
+ }
11558
+ if (!aggregate.column) {
11559
+ return void 0;
11560
+ }
11561
+ return Object.freeze({
11562
+ column: aggregate.column,
11563
+ kind: aggregate.kind
11564
+ });
11565
+ }
11566
+ setRelationAggregateObservationMetadata(entity, key, metadata) {
11567
+ const byKey = relationAggregateObservationMetadata.get(entity) ?? /* @__PURE__ */ new Map();
11568
+ byKey.set(key, metadata);
11569
+ relationAggregateObservationMetadata.set(entity, byKey);
11570
+ }
11571
+ getRelationAggregateObservationMetadata(entity, key) {
11572
+ return relationAggregateObservationMetadata.get(entity)?.get(key);
11573
+ }
11574
+ createRelationAggregateObservationTarget(entity, relation) {
11575
+ switch (relation.kind) {
11576
+ case "belongsTo": {
11577
+ const related = this.resolveRelatedRepository(relation.related);
11578
+ return {
11579
+ column: relation.ownerKey,
11580
+ connectionName: related.getConnectionName(),
11581
+ tableName: related.definition.table.tableName,
11582
+ value: this.getRelationParentValue(entity, relation)
11583
+ };
11584
+ }
11585
+ case "hasMany":
11586
+ case "hasOne":
11587
+ case "hasOneOfMany": {
11588
+ const related = this.resolveRelatedRepository(relation.related);
11589
+ return {
11590
+ column: relation.foreignKey,
11591
+ connectionName: related.getConnectionName(),
11592
+ tableName: related.definition.table.tableName,
11593
+ value: this.getRelationParentValue(entity, relation)
11594
+ };
11595
+ }
11596
+ default:
11597
+ return void 0;
9086
11598
  }
9087
11599
  }
9088
11600
  async create(values) {
@@ -9771,13 +12283,22 @@ var ModelRepository = class _ModelRepository {
9771
12283
  return;
9772
12284
  }
9773
12285
  const related = this.resolveRelatedRepository(relation.related);
9774
- const relatedEntities = await this.applyRelationConstraint(relation, related, constraint).where(relation.ownerKey, "in", foreignKeys).get();
12286
+ const observationCount = hasActiveDatabaseDependencyCollector() ? readDatabaseQueryObservationCount() : void 0;
12287
+ const constrainedQuery = this.applyRelationConstraint(relation, related, constraint);
12288
+ const constrainedPlan = typeof observationCount === "number" ? constrainedQuery.getTableQueryBuilder().getPlan() : void 0;
12289
+ const relatedEntities = await constrainedQuery.where(relation.ownerKey, "in", foreignKeys).get();
12290
+ if (typeof observationCount === "number") {
12291
+ truncateDatabaseQueryObservations(observationCount);
12292
+ }
9775
12293
  const relatedMap = new Map(
9776
12294
  relatedEntities.map((entity) => [entity.get(relation.ownerKey), entity])
9777
12295
  );
9778
12296
  for (const entity of entities) {
9779
12297
  const foreignKey = entity.toAttributes()[relation.foreignKey];
9780
12298
  entity.setRelation(relationName, relatedMap.get(foreignKey) ?? null);
12299
+ if (constrainedPlan) {
12300
+ this.rememberBelongsToRelationObservation(entity, relationName, relation, related, constrainedPlan, foreignKey);
12301
+ }
9781
12302
  }
9782
12303
  }
9783
12304
  async loadHasManyRelation(entities, relationName, relation, constraint) {
@@ -9791,7 +12312,13 @@ var ModelRepository = class _ModelRepository {
9791
12312
  return;
9792
12313
  }
9793
12314
  const related = this.resolveRelatedRepository(relation.related);
9794
- const relatedEntities = await this.applyRelationConstraint(relation, related, constraint).where(relation.foreignKey, "in", localKeys).get();
12315
+ const observationCount = hasActiveDatabaseDependencyCollector() ? readDatabaseQueryObservationCount() : void 0;
12316
+ const constrainedQuery = this.applyRelationConstraint(relation, related, constraint);
12317
+ const constrainedPlan = typeof observationCount === "number" ? constrainedQuery.getTableQueryBuilder().getPlan() : void 0;
12318
+ const relatedEntities = await constrainedQuery.where(relation.foreignKey, "in", localKeys).get();
12319
+ if (typeof observationCount === "number") {
12320
+ truncateDatabaseQueryObservations(observationCount);
12321
+ }
9795
12322
  const grouped = /* @__PURE__ */ new Map();
9796
12323
  for (const relatedEntity of relatedEntities) {
9797
12324
  const foreignKey = relatedEntity.get(relation.foreignKey);
@@ -9802,6 +12329,9 @@ var ModelRepository = class _ModelRepository {
9802
12329
  for (const entity of entities) {
9803
12330
  const localKey = entity.toAttributes()[relation.localKey];
9804
12331
  entity.setRelation(relationName, grouped.get(localKey) ?? []);
12332
+ if (constrainedPlan) {
12333
+ this.rememberHasManyRelationObservation(entity, relationName, relation, related, constrainedPlan, localKey);
12334
+ }
9805
12335
  }
9806
12336
  }
9807
12337
  async loadHasOneRelation(entities, relationName, relation, constraint) {
@@ -9873,8 +12403,12 @@ var ModelRepository = class _ModelRepository {
9873
12403
  }
9874
12404
  return;
9875
12405
  }
12406
+ const observationCount = hasActiveDatabaseDependencyCollector() ? readDatabaseQueryObservationCount() : void 0;
9876
12407
  const pivotRows = await this.createBelongsToManyPivotQuery(relation, this.getConnection()).where(relation.foreignPivotKey, "in", parentKeys).get();
9877
12408
  if (pivotRows.length === 0) {
12409
+ if (typeof observationCount === "number") {
12410
+ truncateDatabaseQueryObservations(observationCount);
12411
+ }
9878
12412
  for (const entity of entities) {
9879
12413
  entity.setRelation(relationName, []);
9880
12414
  }
@@ -9884,6 +12418,9 @@ var ModelRepository = class _ModelRepository {
9884
12418
  pivotRows.map((row) => row[relation.relatedPivotKey]).filter((value) => value !== null && typeof value !== "undefined")
9885
12419
  )];
9886
12420
  if (relatedIds.length === 0) {
12421
+ if (typeof observationCount === "number") {
12422
+ truncateDatabaseQueryObservations(observationCount);
12423
+ }
9887
12424
  for (const entity of entities) {
9888
12425
  entity.setRelation(relationName, []);
9889
12426
  }
@@ -9891,6 +12428,9 @@ var ModelRepository = class _ModelRepository {
9891
12428
  }
9892
12429
  const related = this.resolveRelatedRepository(relation.related);
9893
12430
  const relatedEntities = await this.applyRelationConstraint(relation, related, constraint).where(relation.relatedKey, "in", relatedIds).get();
12431
+ if (typeof observationCount === "number") {
12432
+ truncateDatabaseQueryObservations(observationCount);
12433
+ }
9894
12434
  const relatedMap = new Map(
9895
12435
  relatedEntities.map((entity) => [entity.get(relation.relatedKey), entity])
9896
12436
  );
@@ -10203,31 +12743,53 @@ var ModelRepository = class _ModelRepository {
10203
12743
  switch (kind) {
10204
12744
  case "sum": {
10205
12745
  const numbers = values.map((value) => this.assertNumericAggregateValue(value, kind, column2));
10206
- return numbers.reduce((sum, value) => sum + value, 0);
12746
+ return Object.freeze({
12747
+ value: numbers.reduce((sum, value) => sum + value, 0)
12748
+ });
10207
12749
  }
10208
12750
  case "avg": {
10209
12751
  const numbers = values.map((value) => this.assertNumericAggregateValue(value, kind, column2));
12752
+ const sum = numbers.reduce((total, value) => total + value, 0);
12753
+ const metadata = Object.freeze({
12754
+ column: column2,
12755
+ count: numbers.length,
12756
+ kind,
12757
+ sum
12758
+ });
10210
12759
  if (numbers.length === 0) {
10211
- return null;
12760
+ return Object.freeze({
12761
+ metadata,
12762
+ value: null
12763
+ });
10212
12764
  }
10213
- return numbers.reduce((sum, value) => sum + value, 0) / numbers.length;
12765
+ return Object.freeze({
12766
+ metadata,
12767
+ value: sum / numbers.length
12768
+ });
10214
12769
  }
10215
12770
  case "min": {
10216
12771
  const numbers = values.map((value) => this.assertNumericAggregateValue(value, kind, column2));
10217
- if (numbers.length === 0) {
10218
- return null;
10219
- }
10220
- return Math.min(...numbers);
12772
+ return this.computeExtremeAggregateValue(kind, column2, numbers);
10221
12773
  }
10222
12774
  case "max": {
10223
12775
  const numbers = values.map((value) => this.assertNumericAggregateValue(value, kind, column2));
10224
- if (numbers.length === 0) {
10225
- return null;
10226
- }
10227
- return Math.max(...numbers);
12776
+ return this.computeExtremeAggregateValue(kind, column2, numbers);
10228
12777
  }
10229
12778
  }
10230
12779
  }
12780
+ computeExtremeAggregateValue(kind, column2, numbers) {
12781
+ const value = numbers.length === 0 ? null : kind === "min" ? Math.min(...numbers) : Math.max(...numbers);
12782
+ const metadata = Object.freeze({
12783
+ column: column2,
12784
+ currentValueCount: typeof value === "number" ? numbers.filter((number) => number === value).length : 0,
12785
+ kind,
12786
+ valueCounts: createAggregateValueCounts(numbers)
12787
+ });
12788
+ return Object.freeze({
12789
+ metadata,
12790
+ value
12791
+ });
12792
+ }
10231
12793
  assertNumericAggregateValue(value, kind, column2) {
10232
12794
  if (typeof value !== "number" || Number.isNaN(value)) {
10233
12795
  throw new DatabaseError(`Relation aggregate "${kind}" requires numeric values for column "${column2}".`);
@@ -10795,6 +13357,9 @@ var ModelRepository = class _ModelRepository {
10795
13357
  createBelongsToManyPivotQuery(relation, connection) {
10796
13358
  return this.applyPivotQueryConfig(new TableQueryBuilder(relation.pivotTable, connection), relation);
10797
13359
  }
13360
+ resolvePivotTableName(pivotTable) {
13361
+ return typeof pivotTable === "string" ? pivotTable : pivotTable.tableName;
13362
+ }
10798
13363
  createMorphToManyPivotQuery(relation, connection) {
10799
13364
  return this.applyPivotQueryConfig(new TableQueryBuilder(relation.pivotTable, connection), relation);
10800
13365
  }
@@ -14298,14 +16863,14 @@ function getHoloModelReferenceRegistry() {
14298
16863
 
14299
16864
  // src/model/serialize.ts
14300
16865
  function isSerializableModel(value) {
14301
- return Boolean(value && typeof value === "object" && typeof value.toJSON === "function");
16866
+ return typeof value.toJSON === "function";
14302
16867
  }
14303
16868
  function serializeModels(value) {
14304
16869
  if (value instanceof Date || value === null || typeof value !== "object") {
14305
16870
  return value;
14306
16871
  }
14307
16872
  if (isSerializableModel(value)) {
14308
- return value.toJSON();
16873
+ return serializeModels(value.toJSON());
14309
16874
  }
14310
16875
  if (Array.isArray(value)) {
14311
16876
  return value.map((item) => serializeModels(item));