@holo-js/db 0.2.4 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -692,24 +692,36 @@ declare function resetDatabaseQueryCacheBridge(): void;
692
692
  declare function onDatabaseDependencyInvalidated(listener: DatabaseDependencyInvalidationListener): () => void;
693
693
  declare function resetDatabaseDependencyInvalidationListeners(): void;
694
694
  declare function collectDatabaseQueryDependencies<TValue>(callback: () => TValue | Promise<TValue>): Promise<DatabaseDependencyCollectionResult<TValue>>;
695
+ declare function hasActiveDatabaseDependencyCollector(): boolean;
695
696
  declare function recordDatabaseQueryDependencies(dependencies: readonly string[] | undefined): void;
697
+ declare function hasDatabaseDependencyInvalidationListeners(): boolean;
696
698
  declare function notifyDatabaseDependencyInvalidationListeners(event: DatabaseDependencyInvalidationEvent): Promise<void>;
697
699
  declare function normalizeQueryCacheConfig(input: QueryCacheTtlInput | QueryCacheConfig): NormalizedQueryCacheConfig;
698
700
  declare function createDeterministicQueryCacheKey(statement: CompiledStatement, connectionName: string): string;
699
701
  declare function resolveQueryCacheKey(statement: CompiledStatement, connectionName: string, config: NormalizedQueryCacheConfig): string;
700
702
  declare function createTableCacheDependency(connectionName: string, tableName: string): string;
703
+ declare function createTableRowCacheDependency(connectionName: string, tableName: string, columnName: string, value: unknown): string | undefined;
704
+ declare function createTableRowWildcardCacheDependency(connectionName: string, tableName: string): string;
701
705
  declare function normalizeQueryCacheDependencies(connectionName: string, dependencies: readonly string[]): readonly string[];
702
706
  declare function supportsAutomaticPredicateInvalidation(predicate: QueryPredicateNode): boolean;
703
707
  declare function supportsAutomaticQueryCacheInvalidation(plan: SelectQueryPlan): boolean;
704
708
  declare function inferAutomaticQueryCacheDependencies(plan: SelectQueryPlan, connectionName: string): readonly string[] | undefined;
709
+ declare function inferAutomaticQueryCacheInvalidationDependencies(plan: SelectQueryPlan, connectionName: string): readonly string[];
710
+ declare function inferAutomaticInsertCacheInvalidationDependencies(connectionName: string, tableName: string, rows: readonly Readonly<Record<string, unknown>>[], lastInsertId?: number | string): readonly string[];
705
711
  declare function resolveQueryCacheDependencies(plan: SelectQueryPlan, connectionName: string, explicit?: readonly string[]): readonly string[] | undefined;
706
712
  declare const queryCacheInternals: {
707
713
  collectDatabaseQueryDependencies: typeof collectDatabaseQueryDependencies;
708
714
  configureDatabaseQueryCacheBridge: typeof configureDatabaseQueryCacheBridge;
709
715
  createDeterministicQueryCacheKey: typeof createDeterministicQueryCacheKey;
710
716
  createTableCacheDependency: typeof createTableCacheDependency;
717
+ createTableRowCacheDependency: typeof createTableRowCacheDependency;
718
+ createTableRowWildcardCacheDependency: typeof createTableRowWildcardCacheDependency;
711
719
  getQueryCacheBridgeState: typeof getQueryCacheBridgeState;
720
+ hasActiveDatabaseDependencyCollector: typeof hasActiveDatabaseDependencyCollector;
721
+ hasDatabaseDependencyInvalidationListeners: typeof hasDatabaseDependencyInvalidationListeners;
722
+ inferAutomaticInsertCacheInvalidationDependencies: typeof inferAutomaticInsertCacheInvalidationDependencies;
712
723
  inferAutomaticQueryCacheDependencies: typeof inferAutomaticQueryCacheDependencies;
724
+ inferAutomaticQueryCacheInvalidationDependencies: typeof inferAutomaticQueryCacheInvalidationDependencies;
713
725
  normalizeQueryCacheConfig: typeof normalizeQueryCacheConfig;
714
726
  normalizeQueryCacheDependencies: typeof normalizeQueryCacheDependencies;
715
727
  notifyDatabaseDependencyInvalidationListeners: typeof notifyDatabaseDependencyInvalidationListeners;
@@ -1017,7 +1029,8 @@ declare class TableQueryBuilder<TTableOrName extends TableReference = string, TS
1017
1029
  unsafeQuery<TRow extends Record<string, unknown> = Record<string, unknown>>(statement: Omit<UnsafeStatement, 'source'>): Promise<DriverQueryResult<TRow>>;
1018
1030
  unsafeExecute(statement: Omit<UnsafeStatement, 'source'>): Promise<DriverExecutionResult>;
1019
1031
  private clone;
1020
- private invalidateSourceTableQueries;
1032
+ private invalidateInsertQueries;
1033
+ private invalidateMutationQueries;
1021
1034
  private getCompiler;
1022
1035
  private getUnpaginatedRows;
1023
1036
  private resolvePrimaryKeyColumn;
package/dist/index.mjs CHANGED
@@ -156,6 +156,9 @@ async function collectDatabaseQueryDependencies(callback) {
156
156
  dependencies: Object.freeze([...dependencies])
157
157
  });
158
158
  }
159
+ function hasActiveDatabaseDependencyCollector() {
160
+ return typeof databaseDependencyCollector.getStore() !== "undefined";
161
+ }
159
162
  function recordDatabaseQueryDependencies(dependencies) {
160
163
  if (!dependencies || dependencies.length === 0) {
161
164
  return;
@@ -168,6 +171,10 @@ function recordDatabaseQueryDependencies(dependencies) {
168
171
  active.add(dependency);
169
172
  }
170
173
  }
174
+ function hasDatabaseDependencyInvalidationListeners() {
175
+ const listeners = getQueryCacheBridgeState().dependencyInvalidationListeners;
176
+ return typeof listeners !== "undefined" && listeners.size > 0;
177
+ }
171
178
  async function notifyDatabaseDependencyInvalidationListeners(event) {
172
179
  const listeners = getQueryCacheBridgeState().dependencyInvalidationListeners;
173
180
  if (!listeners || listeners.size === 0) {
@@ -232,6 +239,15 @@ function resolveQueryCacheKey(statement, connectionName, config) {
232
239
  function createTableCacheDependency(connectionName, tableName) {
233
240
  return `db:${connectionName}:${tableName}`;
234
241
  }
242
+ function createTableRowCacheDependency(connectionName, tableName, columnName, value) {
243
+ if (typeof value === "undefined") {
244
+ return void 0;
245
+ }
246
+ return `db:${connectionName}:${tableName}:row:${columnName}:${encodeURIComponent(JSON.stringify(value))}`;
247
+ }
248
+ function createTableRowWildcardCacheDependency(connectionName, tableName) {
249
+ return `db:${connectionName}:${tableName}:row:*`;
250
+ }
235
251
  function normalizeQueryCacheDependencies(connectionName, dependencies) {
236
252
  return Object.freeze(dependencies.map((dependency) => {
237
253
  return dependency.startsWith("db:") ? dependency : createTableCacheDependency(connectionName, dependency);
@@ -257,6 +273,42 @@ function supportsAutomaticPredicateInvalidation(predicate) {
257
273
  return false;
258
274
  }
259
275
  }
276
+ function getColumnName(column2) {
277
+ const segments = column2.split(".");
278
+ return segments[segments.length - 1] ?? column2;
279
+ }
280
+ function getPrimaryKeyColumn(plan) {
281
+ const columns = plan.source.table?.columns;
282
+ if (!columns) {
283
+ return "id";
284
+ }
285
+ return Object.values(columns).find((column2) => column2.primaryKey)?.name ?? "id";
286
+ }
287
+ function hasDisjunctivePredicate(predicates) {
288
+ return predicates.some((predicate) => {
289
+ if (predicate.boolean === "or") {
290
+ return true;
291
+ }
292
+ if (predicate.kind !== "group") {
293
+ return false;
294
+ }
295
+ return predicate.negated === true || hasDisjunctivePredicate(predicate.predicates);
296
+ });
297
+ }
298
+ function findExactPrimaryKeyValue(predicates, primaryKeyColumn) {
299
+ for (const predicate of predicates) {
300
+ if (predicate.kind === "comparison" && predicate.operator === "=" && getColumnName(predicate.column) === primaryKeyColumn) {
301
+ return predicate.value;
302
+ }
303
+ if (predicate.kind === "group" && !predicate.negated && predicate.boolean !== "or") {
304
+ const value = findExactPrimaryKeyValue(predicate.predicates, primaryKeyColumn);
305
+ if (typeof value !== "undefined") {
306
+ return value;
307
+ }
308
+ }
309
+ }
310
+ return void 0;
311
+ }
260
312
  function supportsAutomaticQueryCacheInvalidation(plan) {
261
313
  if (plan.joins.length > 0 || plan.unions.length > 0 || plan.having.length > 0) {
262
314
  return false;
@@ -273,10 +325,64 @@ function inferAutomaticQueryCacheDependencies(plan, connectionName) {
273
325
  if (!supportsAutomaticQueryCacheInvalidation(plan)) {
274
326
  return void 0;
275
327
  }
328
+ if (!hasDisjunctivePredicate(plan.predicates)) {
329
+ const primaryKeyColumn = getPrimaryKeyColumn(plan);
330
+ const primaryKeyValue = findExactPrimaryKeyValue(plan.predicates, primaryKeyColumn);
331
+ const primaryKeyDependency = createTableRowCacheDependency(
332
+ connectionName,
333
+ plan.source.tableName,
334
+ primaryKeyColumn,
335
+ primaryKeyValue
336
+ );
337
+ if (primaryKeyDependency) {
338
+ return Object.freeze([
339
+ primaryKeyDependency,
340
+ createTableRowWildcardCacheDependency(connectionName, plan.source.tableName)
341
+ ]);
342
+ }
343
+ }
276
344
  return Object.freeze([
277
345
  createTableCacheDependency(connectionName, plan.source.tableName)
278
346
  ]);
279
347
  }
348
+ function inferAutomaticQueryCacheInvalidationDependencies(plan, connectionName) {
349
+ const dependencies = [
350
+ createTableCacheDependency(connectionName, plan.source.tableName)
351
+ ];
352
+ if (hasDisjunctivePredicate(plan.predicates)) {
353
+ dependencies.push(createTableRowWildcardCacheDependency(connectionName, plan.source.tableName));
354
+ return Object.freeze(dependencies);
355
+ }
356
+ const primaryKeyColumn = getPrimaryKeyColumn(plan);
357
+ const primaryKeyValue = findExactPrimaryKeyValue(plan.predicates, primaryKeyColumn);
358
+ const primaryKeyDependency = createTableRowCacheDependency(
359
+ connectionName,
360
+ plan.source.tableName,
361
+ primaryKeyColumn,
362
+ primaryKeyValue
363
+ );
364
+ dependencies.push(primaryKeyDependency ?? createTableRowWildcardCacheDependency(connectionName, plan.source.tableName));
365
+ return Object.freeze(dependencies);
366
+ }
367
+ function inferAutomaticInsertCacheInvalidationDependencies(connectionName, tableName, rows, lastInsertId) {
368
+ const dependencies = /* @__PURE__ */ new Set([
369
+ createTableCacheDependency(connectionName, tableName)
370
+ ]);
371
+ for (const row of rows) {
372
+ const dependency = createTableRowCacheDependency(connectionName, tableName, "id", row.id);
373
+ if (dependency) {
374
+ dependencies.add(dependency);
375
+ }
376
+ }
377
+ const lastInsertDependency = createTableRowCacheDependency(connectionName, tableName, "id", lastInsertId);
378
+ if (lastInsertDependency) {
379
+ dependencies.add(lastInsertDependency);
380
+ }
381
+ if (dependencies.size === 1) {
382
+ dependencies.add(createTableRowWildcardCacheDependency(connectionName, tableName));
383
+ }
384
+ return Object.freeze([...dependencies]);
385
+ }
280
386
  function resolveQueryCacheDependencies(plan, connectionName, explicit) {
281
387
  if (explicit && explicit.length > 0) {
282
388
  return normalizeQueryCacheDependencies(connectionName, explicit);
@@ -306,8 +412,14 @@ var queryCacheInternals = {
306
412
  configureDatabaseQueryCacheBridge,
307
413
  createDeterministicQueryCacheKey,
308
414
  createTableCacheDependency,
415
+ createTableRowCacheDependency,
416
+ createTableRowWildcardCacheDependency,
309
417
  getQueryCacheBridgeState,
418
+ hasActiveDatabaseDependencyCollector,
419
+ hasDatabaseDependencyInvalidationListeners,
420
+ inferAutomaticInsertCacheInvalidationDependencies,
310
421
  inferAutomaticQueryCacheDependencies,
422
+ inferAutomaticQueryCacheInvalidationDependencies,
311
423
  normalizeQueryCacheConfig,
312
424
  normalizeQueryCacheDependencies,
313
425
  notifyDatabaseDependencyInvalidationListeners,
@@ -2809,7 +2921,7 @@ var TableQueryBuilder = class _TableQueryBuilder {
2809
2921
  async get() {
2810
2922
  const statement = this.toSQL();
2811
2923
  const cacheConfig = this.queryCacheConfig;
2812
- const dependencies = this.plan.lockMode ? void 0 : resolveQueryCacheDependencies(
2924
+ const dependencies = this.plan.lockMode || !cacheConfig && !hasActiveDatabaseDependencyCollector() ? void 0 : resolveQueryCacheDependencies(
2813
2925
  this.plan,
2814
2926
  this.connection.getConnectionName(),
2815
2927
  cacheConfig?.invalidate
@@ -3056,7 +3168,7 @@ var TableQueryBuilder = class _TableQueryBuilder {
3056
3168
  const result = await this.connection.executeCompiled(this.getCompiler().compile(
3057
3169
  createInsertQueryPlan(this.source, rows)
3058
3170
  ));
3059
- await this.invalidateSourceTableQueries();
3171
+ await this.invalidateInsertQueries(rows, result.lastInsertId);
3060
3172
  return result;
3061
3173
  }
3062
3174
  async insertOrIgnore(values) {
@@ -3064,7 +3176,7 @@ var TableQueryBuilder = class _TableQueryBuilder {
3064
3176
  const result = await this.connection.executeCompiled(this.getCompiler().compile(
3065
3177
  createInsertQueryPlan(this.source, rows, { ignoreConflicts: true })
3066
3178
  ));
3067
- await this.invalidateSourceTableQueries();
3179
+ await this.invalidateInsertQueries(rows, result.lastInsertId);
3068
3180
  return result;
3069
3181
  }
3070
3182
  async insertGetId(values) {
@@ -3076,7 +3188,7 @@ var TableQueryBuilder = class _TableQueryBuilder {
3076
3188
  const result = await this.connection.executeCompiled(this.getCompiler().compile(
3077
3189
  createUpsertQueryPlan(this.source, rows, uniqueBy, updateColumns)
3078
3190
  ));
3079
- await this.invalidateSourceTableQueries();
3191
+ await this.invalidateInsertQueries(rows, result.lastInsertId);
3080
3192
  return result;
3081
3193
  }
3082
3194
  async increment(column2, amount = 1, extraValues = {}) {
@@ -3090,7 +3202,7 @@ var TableQueryBuilder = class _TableQueryBuilder {
3090
3202
  createUpdateQueryPlan(this.source, this.plan.predicates, this.normalizeUpdateValues(values))
3091
3203
  ));
3092
3204
  if (result.affectedRows !== 0) {
3093
- await this.invalidateSourceTableQueries();
3205
+ await this.invalidateMutationQueries();
3094
3206
  }
3095
3207
  return result;
3096
3208
  }
@@ -3102,7 +3214,7 @@ var TableQueryBuilder = class _TableQueryBuilder {
3102
3214
  createDeleteQueryPlan(this.source, this.plan.predicates)
3103
3215
  ));
3104
3216
  if (result.affectedRows !== 0) {
3105
- await this.invalidateSourceTableQueries();
3217
+ await this.invalidateMutationQueries();
3106
3218
  }
3107
3219
  return result;
3108
3220
  }
@@ -3124,10 +3236,28 @@ var TableQueryBuilder = class _TableQueryBuilder {
3124
3236
  const table = this.source.table ?? this.source.tableName;
3125
3237
  return new _TableQueryBuilder(table, this.connection, plan, this.queryCacheConfig);
3126
3238
  }
3127
- async invalidateSourceTableQueries() {
3128
- await invalidateQueryCacheDependencies(this.connection, [
3129
- `db:${this.connection.getConnectionName()}:${this.source.tableName}`
3130
- ]);
3239
+ async invalidateInsertQueries(rows, lastInsertId) {
3240
+ if (!getDatabaseQueryCacheBridge() && !hasDatabaseDependencyInvalidationListeners()) {
3241
+ return;
3242
+ }
3243
+ await invalidateQueryCacheDependencies(
3244
+ this.connection,
3245
+ inferAutomaticInsertCacheInvalidationDependencies(
3246
+ this.connection.getConnectionName(),
3247
+ this.source.tableName,
3248
+ rows,
3249
+ lastInsertId
3250
+ )
3251
+ );
3252
+ }
3253
+ async invalidateMutationQueries() {
3254
+ if (!getDatabaseQueryCacheBridge() && !hasDatabaseDependencyInvalidationListeners()) {
3255
+ return;
3256
+ }
3257
+ await invalidateQueryCacheDependencies(
3258
+ this.connection,
3259
+ inferAutomaticQueryCacheInvalidationDependencies(this.plan, this.connection.getConnectionName())
3260
+ );
3131
3261
  }
3132
3262
  getCompiler() {
3133
3263
  const dialect = this.connection.getDialect();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@holo-js/db",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "description": "Holo-JS Framework - portable database, ORM, migrations, factories, and seeders",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -28,9 +28,9 @@
28
28
  "uuid": "^12.0.0"
29
29
  },
30
30
  "peerDependencies": {
31
- "@holo-js/db-mysql": "^0.2.4",
32
- "@holo-js/db-postgres": "^0.2.4",
33
- "@holo-js/db-sqlite": "^0.2.4"
31
+ "@holo-js/db-mysql": "^0.2.5",
32
+ "@holo-js/db-postgres": "^0.2.5",
33
+ "@holo-js/db-sqlite": "^0.2.5"
34
34
  },
35
35
  "peerDependenciesMeta": {
36
36
  "@holo-js/db-mysql": {
@@ -44,9 +44,9 @@
44
44
  }
45
45
  },
46
46
  "devDependencies": {
47
- "@holo-js/db-mysql": "^0.2.4",
48
- "@holo-js/db-postgres": "^0.2.4",
49
- "@holo-js/db-sqlite": "^0.2.4",
47
+ "@holo-js/db-mysql": "^0.2.5",
48
+ "@holo-js/db-postgres": "^0.2.5",
49
+ "@holo-js/db-sqlite": "^0.2.5",
50
50
  "tsup": "^8.3.5",
51
51
  "typescript": "^5.7.2"
52
52
  }