@bjnstnkvc/db 2.0.0 → 2.0.2

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/README.md CHANGED
@@ -16,7 +16,8 @@ The method names and their semantics follow Laravel closely enough that the docs
16
16
  - [Grouping](#grouping)
17
17
  - [Query plans](#query-plans)
18
18
  - [Transactions](#transactions)
19
- - [Events and the query log](#events-and-the-query-log)
19
+ - [Events](#events)
20
+ - [Query log](#query-log)
20
21
  - [Multiple tabs](#multiple-tabs)
21
22
  - [Connections](#connections)
22
23
  - [Storage quota](#storage-quota)
@@ -834,6 +835,7 @@ await DB.table<User>('users').where('id', 1).increment('visits');
834
835
  await DB.table<User>('users').where('id', 1).decrement('credits', 5);
835
836
 
836
837
  await DB.table<User>('users').where('role', 'guest').delete();
838
+ await DB.table<User>('users').oldest('created_at').limit(100).delete();
837
839
  await DB.table<User>('users').truncate();
838
840
  ```
839
841
 
@@ -850,9 +852,10 @@ enforce anything else. Any other column throws `SchemaException`.
850
852
 
851
853
  The key path may not be updated, so `update`, `upsert` and `increment` all refuse it.
852
854
 
853
- `update` and `delete` honour `limit` and `offset` in the order the plan scans, which is index order
854
- when an index drives the query and key order otherwise. Pair them with an indexed `orderBy` if you
855
- need a defined order.
855
+ `update`, `delete`, `increment` and `decrement` honor `orderBy` together with `limit` and
856
+ `offset`, so they touch the same records a read of the query would return, whether or not an index
857
+ serves the order. Without an `orderBy`, they follow the order the plan scans, which is index order
858
+ when an index drives the query and key order otherwise.
856
859
 
857
860
  > Modeled on Laravel's [Database: Query Builder](https://laravel.com/docs/12.x/queries). The method
858
861
  > names and their semantics match, and every terminal is asynchronous because IndexedDB is.
@@ -1098,26 +1101,49 @@ from this package.
1098
1101
  > The tables have to be declared up front, because an IndexedDB transaction fixes its scope when it
1099
1102
  > opens.
1100
1103
 
1101
- ### Events and the query log
1104
+ ### Events
1102
1105
 
1103
- ```ts
1104
- DB.onQueryExecuted((event: QueryExecuted): void => {
1105
- console.log(event.plan, event.duration, event.records);
1106
- });
1106
+ Listen for an event by its key, and the listener receives an instance of the class it maps to:
1107
1107
 
1108
+ ```ts
1108
1109
  DB.listen('migration-started', (event: MigrationStarted): void => console.log(event.migration));
1109
1110
  DB.listen('query', listener, { once: true });
1110
1111
  DB.forget('query', listener);
1111
1112
  ```
1112
1113
 
1113
- Available events: `query`, `transaction-beginning`, `transaction-committed`,
1114
- `transaction-rolled-back`, `migrations-started`, `migration-started`, `migration-ended`,
1115
- `migrations-ended`, `no-pending-migrations`, `seeding-started`, `seeder-started`, `seeder-ended`,
1116
- `seeding-ended`, `database-blocked`.
1114
+ Every event also has a shortcut named after its class, which takes the same options:
1115
+
1116
+ ```ts
1117
+ DB.onQueryExecuted((event: QueryExecuted): void => {
1118
+ console.log(event.plan, event.duration, event.records);
1119
+ });
1120
+ ```
1121
+
1122
+ | Key | Class | Carries |
1123
+ |---------------------------|-------------------------|----------------------------------------------------------------------------------------|
1124
+ | `query` | `QueryExecuted` | `connection`, `table`, `plan`, `constraints`, `orders`, `limit`, `duration`, `records` |
1125
+ | `transaction-beginning` | `TransactionBeginning` | `connection` |
1126
+ | `transaction-committed` | `TransactionCommitted` | `connection` |
1127
+ | `transaction-rolled-back` | `TransactionRolledBack` | `connection`, `reason` |
1128
+ | `migrations-started` | `MigrationsStarted` | `connection`, `migrations` |
1129
+ | `migration-started` | `MigrationStarted` | `migration` |
1130
+ | `migration-ended` | `MigrationEnded` | `migration` |
1131
+ | `migrations-ended` | `MigrationsEnded` | `connection`, `migrations` |
1132
+ | `no-pending-migrations` | `NoPendingMigrations` | `connection` |
1133
+ | `seeding-started` | `SeedingStarted` | `connection`, `seeders` |
1134
+ | `seeder-started` | `SeederStarted` | `seeder` |
1135
+ | `seeder-ended` | `SeederEnded` | `seeder` |
1136
+ | `seeding-ended` | `SeedingEnded` | `connection`, `seeders` |
1137
+ | `database-blocked` | `DatabaseBlocked` | `database` |
1117
1138
 
1118
1139
  A connection with no seeders announces nothing, so `seeding-started` firing always means at least
1119
1140
  one seeder is about to run.
1120
1141
 
1142
+ > Modeled on Laravel's [Migrations: Events](https://laravel.com/docs/12.x/migrations#events), with
1143
+ > each event dispatched as a browser event and listened for by key.
1144
+
1145
+ ### Query log
1146
+
1121
1147
  ```ts
1122
1148
  DB.enableQueryLog();
1123
1149
 
@@ -1126,7 +1152,7 @@ await DB.table<User>('users').where('role', 'admin').get();
1126
1152
  DB.getQueryLog();
1127
1153
  ```
1128
1154
 
1129
- Resolves to one entry per query that ran while the log was enabled:
1155
+ Resolves to one entry per `query` event dispatched while the log was enabled:
1130
1156
 
1131
1157
  ```
1132
1158
  [
package/dist/main.cjs CHANGED
@@ -1170,7 +1170,7 @@ var Request = class {
1170
1170
  };
1171
1171
 
1172
1172
  // src/schema/Coercer.ts
1173
- var FALSY = ["false", "0"];
1173
+ var FALSY = /* @__PURE__ */ new Set(["false", "0"]);
1174
1174
  var Coercer = class {
1175
1175
  /**
1176
1176
  * Coerce a value into its declared column type.
@@ -1187,7 +1187,7 @@ var Coercer = class {
1187
1187
  case "float":
1188
1188
  return this.#numeric(value, strict, false);
1189
1189
  case "boolean":
1190
- return typeof value === "string" && FALSY.includes(value) ? false : Boolean(value);
1190
+ return typeof value === "string" && FALSY.has(value) ? false : Boolean(value);
1191
1191
  case "decimal":
1192
1192
  return this.#scaled(value, strict);
1193
1193
  case "enum":
@@ -1435,15 +1435,9 @@ var Join = class {
1435
1435
  * The conditions the tables are joined on.
1436
1436
  */
1437
1437
  #conditions = [];
1438
- /**
1439
- * Join on a pair of columns.
1440
- */
1441
1438
  on(first, operator, second) {
1442
1439
  return this.#condition("and", first, operator, second);
1443
1440
  }
1444
- /**
1445
- * Join on a pair of columns, disjunctively.
1446
- */
1447
1441
  orOn(first, operator, second) {
1448
1442
  return this.#condition("or", first, operator, second);
1449
1443
  }
@@ -1488,7 +1482,7 @@ var Predicate = class {
1488
1482
  if (index === 0 || constraint.conjunction === "or") {
1489
1483
  groups.push([]);
1490
1484
  }
1491
- groups[groups.length - 1]?.push(constraint);
1485
+ groups.at(-1)?.push(constraint);
1492
1486
  }
1493
1487
  return groups;
1494
1488
  }
@@ -1748,7 +1742,7 @@ var Joiner = class {
1748
1742
  };
1749
1743
 
1750
1744
  // src/query/Planner.ts
1751
- var RANGEABLE = ["=", "==", "===", ">", ">=", "<", "<="];
1745
+ var RANGEABLE = /* @__PURE__ */ new Set(["=", "==", "===", ">", ">=", "<", "<="]);
1752
1746
  var Planner = class {
1753
1747
  /**
1754
1748
  * Compile the constraints and orders into an execution plan.
@@ -1838,7 +1832,7 @@ var Planner = class {
1838
1832
  }
1839
1833
  return { constraint, ...target, range: IDBKeyRange.bound(constraint.from, constraint.to, false, false), values: null };
1840
1834
  }
1841
- if (!RANGEABLE.includes(constraint.operator) || !this.#keyable(constraint.value)) {
1835
+ if (!RANGEABLE.has(constraint.operator) || !this.#keyable(constraint.value)) {
1842
1836
  return null;
1843
1837
  }
1844
1838
  return { constraint, ...target, range: this.#range(constraint.operator, constraint.value), values: null };
@@ -1914,7 +1908,7 @@ var Signature = class {
1914
1908
  * Build a signature identifying a record by every column it holds.
1915
1909
  */
1916
1910
  static of(record) {
1917
- return Object.keys(record).sort().map((column) => this.#segment(column) + this.#segment(this.value(record[column]))).join("");
1911
+ return Object.keys(record).sort((a, b) => a < b ? -1 : 1).map((column) => this.#segment(column) + this.#segment(this.value(record[column]))).join("");
1918
1912
  }
1919
1913
  /**
1920
1914
  * Build a signature identifying an ordered list of values.
@@ -1997,17 +1991,11 @@ var Grouping = class _Grouping {
1997
1991
  grouping.#offset = this.#offset;
1998
1992
  return grouping;
1999
1993
  }
2000
- /**
2001
- * Constrain the groups the query returns.
2002
- */
2003
- having(column, operator, value) {
2004
- return this.#constrain("and", column, operator, value);
1994
+ having(column, ...parameters) {
1995
+ return this.#constrain("and", column, parameters);
2005
1996
  }
2006
- /**
2007
- * Add a disjunctive constraint on the groups the query returns.
2008
- */
2009
- orHaving(column, operator, value) {
2010
- return this.#constrain("or", column, operator, value);
1997
+ orHaving(column, ...parameters) {
1998
+ return this.#constrain("or", column, parameters);
2011
1999
  }
2012
2000
  /**
2013
2001
  * Sort the groups by a column or an aggregate.
@@ -2117,8 +2105,8 @@ var Grouping = class _Grouping {
2117
2105
  /**
2118
2106
  * Add a constraint on the groups the query returns.
2119
2107
  */
2120
- #constrain(conjunction, column, operator, value) {
2121
- const resolved = value === void 0 ? { operator: "=", value: operator } : { operator, value };
2108
+ #constrain(conjunction, column, parameters) {
2109
+ const resolved = parameters.length < 2 ? { operator: "=", value: parameters[0] } : { operator: parameters[0], value: parameters[1] };
2122
2110
  this.#constraints.push({ type: "basic", column, operator: resolved.operator, value: resolved.value, conjunction, not: false });
2123
2111
  return this;
2124
2112
  }
@@ -2186,23 +2174,14 @@ var Builder = class _Builder {
2186
2174
  get table() {
2187
2175
  return this.#table;
2188
2176
  }
2189
- /**
2190
- * Add a constraint to the query.
2191
- */
2192
- where(column, operator, value) {
2193
- return this.#constrain("and", false, column, operator, value);
2177
+ where(column, ...parameters) {
2178
+ return this.#constrain("and", false, column, parameters);
2194
2179
  }
2195
- /**
2196
- * Add a disjunctive constraint to the query.
2197
- */
2198
- orWhere(column, operator, value) {
2199
- return this.#constrain("or", false, column, operator, value);
2180
+ orWhere(column, ...parameters) {
2181
+ return this.#constrain("or", false, column, parameters);
2200
2182
  }
2201
- /**
2202
- * Add a negated constraint to the query.
2203
- */
2204
- whereNot(column, operator, value) {
2205
- return this.#constrain("and", true, column, operator, value);
2183
+ whereNot(column, ...parameters) {
2184
+ return this.#constrain("and", true, column, parameters);
2206
2185
  }
2207
2186
  /**
2208
2187
  * Constrain a column to one of the given values.
@@ -2327,21 +2306,12 @@ var Builder = class _Builder {
2327
2306
  whereDay(column, value) {
2328
2307
  return this.#part("and", column, "day", value);
2329
2308
  }
2330
- /**
2331
- * Join another table, keeping only the rows that match.
2332
- */
2333
2309
  join(table, first, operator, second) {
2334
2310
  return this.#join("inner", table, first, operator, second);
2335
2311
  }
2336
- /**
2337
- * Join another table, keeping every row of this one.
2338
- */
2339
2312
  leftJoin(table, first, operator, second) {
2340
2313
  return this.#join("left", table, first, operator, second);
2341
2314
  }
2342
- /**
2343
- * Join another table, keeping every row of it.
2344
- */
2345
2315
  rightJoin(table, first, operator, second) {
2346
2316
  return this.#join("right", table, first, operator, second);
2347
2317
  }
@@ -2352,15 +2322,9 @@ var Builder = class _Builder {
2352
2322
  this.#joins.push({ table, type: "cross", conditions: [] });
2353
2323
  return this;
2354
2324
  }
2355
- /**
2356
- * Constrain a column against another column of the same row.
2357
- */
2358
2325
  whereColumn(column, operator, other) {
2359
2326
  return this.#compared("and", column, operator, other);
2360
2327
  }
2361
- /**
2362
- * Constrain a column against another column of the same row, disjunctively.
2363
- */
2364
2328
  orWhereColumn(column, operator, other) {
2365
2329
  return this.#compared("or", column, operator, other);
2366
2330
  }
@@ -2946,7 +2910,7 @@ var Builder = class _Builder {
2946
2910
  }
2947
2911
  }
2948
2912
  /**
2949
- * Apply a change to every record matching the query, in the order the plan scans them.
2913
+ * Apply a change to every record matching the query, in the order the query asks for.
2950
2914
  */
2951
2915
  async #modify(apply) {
2952
2916
  const schema = await this.#connection.schema(this.#table);
@@ -2955,8 +2919,20 @@ var Builder = class _Builder {
2955
2919
  const started = performance.now();
2956
2920
  const matches = Predicate.compile(plan.residual);
2957
2921
  const ceiling = this.#limit === null ? null : this.#offset + this.#limit;
2922
+ const collects = !plan.ordered && this.#orders.length > 0 && (this.#limit !== null || this.#offset > 0);
2958
2923
  let seen = 0;
2959
2924
  let affected = 0;
2925
+ if (collects) {
2926
+ const collected = plan.values === null ? await this.#cursored(store, plan, matches) : await this.#points(store, plan, matches);
2927
+ for (const entry of this.#paged(this.#sorted(collected))) {
2928
+ await Request.walk(store.openCursor(IDBKeyRange.only(entry.key)), (cursor) => {
2929
+ apply(cursor);
2930
+ affected++;
2931
+ });
2932
+ }
2933
+ this.#emit(Planner.describe(plan), started, affected);
2934
+ return affected;
2935
+ }
2960
2936
  const visit = (cursor) => {
2961
2937
  if (!matches(cursor.value)) {
2962
2938
  return true;
@@ -2973,6 +2949,9 @@ var Builder = class _Builder {
2973
2949
  await Request.walk(source.openCursor(plan.range, plan.direction), visit);
2974
2950
  } else {
2975
2951
  for (const value of plan.values) {
2952
+ if (ceiling !== null && seen >= ceiling) {
2953
+ break;
2954
+ }
2976
2955
  const source = plan.index === null ? store : store.index(plan.index);
2977
2956
  await Request.walk(source.openCursor(IDBKeyRange.only(value)), visit);
2978
2957
  }
@@ -2983,7 +2962,7 @@ var Builder = class _Builder {
2983
2962
  /**
2984
2963
  * Add a constraint of the given shape to the query.
2985
2964
  */
2986
- #constrain(conjunction, not, column, operator, value) {
2965
+ #constrain(conjunction, not, column, parameters) {
2987
2966
  if (typeof column === "function") {
2988
2967
  const nested = new _Builder(this.#connection, this.#table, this.#transaction);
2989
2968
  column(nested);
@@ -3000,7 +2979,7 @@ var Builder = class _Builder {
3000
2979
  }));
3001
2980
  return this.#push({ type: "nested", constraints, conjunction, not });
3002
2981
  }
3003
- const resolved = value === void 0 ? { operator: "=", value: operator } : { operator, value };
2982
+ const resolved = parameters.length < 2 ? { operator: "=", value: parameters[0] } : { operator: parameters[0], value: parameters[1] };
3004
2983
  return this.#push({ type: "basic", column, operator: resolved.operator, value: resolved.value, conjunction, not });
3005
2984
  }
3006
2985
  /**
@@ -3060,6 +3039,8 @@ var Builder = class _Builder {
3060
3039
  const clause = new Join();
3061
3040
  if (typeof first === "function") {
3062
3041
  first(clause);
3042
+ } else if (second === void 0) {
3043
+ clause.on(first, operator);
3063
3044
  } else {
3064
3045
  clause.on(first, operator, second);
3065
3046
  }
package/dist/main.d.cts CHANGED
@@ -302,11 +302,13 @@ declare class Join {
302
302
  /**
303
303
  * Join on a pair of columns.
304
304
  */
305
- on(first: string, operator: Operator | string, second?: string): this;
305
+ on(first: string, second: string): this;
306
+ on(first: string, operator: Operator, second: string): this;
306
307
  /**
307
308
  * Join on a pair of columns, disjunctively.
308
309
  */
309
- orOn(first: string, operator: Operator | string, second?: string): this;
310
+ orOn(first: string, second: string): this;
311
+ orOn(first: string, operator: Operator, second: string): this;
310
312
  /**
311
313
  * Get the conditions the tables are joined on.
312
314
  */
@@ -327,11 +329,13 @@ declare class Grouping<T, G extends (keyof T & string)[], A extends Aggregations
327
329
  /**
328
330
  * Constrain the groups the query returns.
329
331
  */
330
- having(column: Key<Grouped<T, G, A>>, operator?: Operator | unknown, value?: unknown): this;
332
+ having(column: Key<Grouped<T, G, A>>, value: unknown): this;
333
+ having(column: Key<Grouped<T, G, A>>, operator: Operator, value: unknown): this;
331
334
  /**
332
335
  * Add a disjunctive constraint on the groups the query returns.
333
336
  */
334
- orHaving(column: Key<Grouped<T, G, A>>, operator?: Operator | unknown, value?: unknown): this;
337
+ orHaving(column: Key<Grouped<T, G, A>>, value: unknown): this;
338
+ orHaving(column: Key<Grouped<T, G, A>>, operator: Operator, value: unknown): this;
335
339
  /**
336
340
  * Sort the groups by a column or an aggregate.
337
341
  */
@@ -518,7 +522,6 @@ declare class Connection {
518
522
 
519
523
  type Nested<T> = (query: Builder<T>) => void;
520
524
  type Joining = (join: Join) => void;
521
- type Column<T> = Key<T> | Partial<T> | Nested<T>;
522
525
  declare class Builder<T = Record<string, unknown>> {
523
526
  #private;
524
527
  /**
@@ -532,15 +535,21 @@ declare class Builder<T = Record<string, unknown>> {
532
535
  /**
533
536
  * Add a constraint to the query.
534
537
  */
535
- where(column: Column<T>, operator?: Operator | unknown, value?: unknown): this;
538
+ where(column: Partial<T> | Nested<T>): this;
539
+ where(column: Key<T>, value: unknown): this;
540
+ where(column: Key<T>, operator: Operator, value: unknown): this;
536
541
  /**
537
542
  * Add a disjunctive constraint to the query.
538
543
  */
539
- orWhere(column: Column<T>, operator?: Operator | unknown, value?: unknown): this;
544
+ orWhere(column: Partial<T> | Nested<T>): this;
545
+ orWhere(column: Key<T>, value: unknown): this;
546
+ orWhere(column: Key<T>, operator: Operator, value: unknown): this;
540
547
  /**
541
548
  * Add a negated constraint to the query.
542
549
  */
543
- whereNot(column: Column<T>, operator?: Operator | unknown, value?: unknown): this;
550
+ whereNot(column: Partial<T> | Nested<T>): this;
551
+ whereNot(column: Key<T>, value: unknown): this;
552
+ whereNot(column: Key<T>, operator: Operator, value: unknown): this;
544
553
  /**
545
554
  * Constrain a column to one of the given values.
546
555
  */
@@ -624,15 +633,21 @@ declare class Builder<T = Record<string, unknown>> {
624
633
  /**
625
634
  * Join another table, keeping only the rows that match.
626
635
  */
627
- join<R = Record<string, unknown>>(table: string, first: string | Joining, operator?: Operator | string, second?: string): Builder<R>;
636
+ join<R = Record<string, unknown>>(table: string, first: Joining): Builder<R>;
637
+ join<R = Record<string, unknown>>(table: string, first: string, second: string): Builder<R>;
638
+ join<R = Record<string, unknown>>(table: string, first: string, operator: Operator, second: string): Builder<R>;
628
639
  /**
629
640
  * Join another table, keeping every row of this one.
630
641
  */
631
- leftJoin<R = Record<string, unknown>>(table: string, first: string | Joining, operator?: Operator | string, second?: string): Builder<R>;
642
+ leftJoin<R = Record<string, unknown>>(table: string, first: Joining): Builder<R>;
643
+ leftJoin<R = Record<string, unknown>>(table: string, first: string, second: string): Builder<R>;
644
+ leftJoin<R = Record<string, unknown>>(table: string, first: string, operator: Operator, second: string): Builder<R>;
632
645
  /**
633
646
  * Join another table, keeping every row of it.
634
647
  */
635
- rightJoin<R = Record<string, unknown>>(table: string, first: string | Joining, operator?: Operator | string, second?: string): Builder<R>;
648
+ rightJoin<R = Record<string, unknown>>(table: string, first: Joining): Builder<R>;
649
+ rightJoin<R = Record<string, unknown>>(table: string, first: string, second: string): Builder<R>;
650
+ rightJoin<R = Record<string, unknown>>(table: string, first: string, operator: Operator, second: string): Builder<R>;
636
651
  /**
637
652
  * Pair every row of this table with every row of another.
638
653
  */
@@ -640,11 +655,13 @@ declare class Builder<T = Record<string, unknown>> {
640
655
  /**
641
656
  * Constrain a column against another column of the same row.
642
657
  */
643
- whereColumn(column: Key<T>, operator: Operator | string, other?: string): this;
658
+ whereColumn(column: Key<T>, other: string): this;
659
+ whereColumn(column: Key<T>, operator: Operator, other: string): this;
644
660
  /**
645
661
  * Constrain a column against another column of the same row, disjunctively.
646
662
  */
647
- orWhereColumn(column: Key<T>, operator: Operator | string, other?: string): this;
663
+ orWhereColumn(column: Key<T>, other: string): this;
664
+ orWhereColumn(column: Key<T>, operator: Operator, other: string): this;
648
665
  /**
649
666
  * Project only the given columns, which may alias what they select.
650
667
  */
package/dist/main.d.ts CHANGED
@@ -302,11 +302,13 @@ declare class Join {
302
302
  /**
303
303
  * Join on a pair of columns.
304
304
  */
305
- on(first: string, operator: Operator | string, second?: string): this;
305
+ on(first: string, second: string): this;
306
+ on(first: string, operator: Operator, second: string): this;
306
307
  /**
307
308
  * Join on a pair of columns, disjunctively.
308
309
  */
309
- orOn(first: string, operator: Operator | string, second?: string): this;
310
+ orOn(first: string, second: string): this;
311
+ orOn(first: string, operator: Operator, second: string): this;
310
312
  /**
311
313
  * Get the conditions the tables are joined on.
312
314
  */
@@ -327,11 +329,13 @@ declare class Grouping<T, G extends (keyof T & string)[], A extends Aggregations
327
329
  /**
328
330
  * Constrain the groups the query returns.
329
331
  */
330
- having(column: Key<Grouped<T, G, A>>, operator?: Operator | unknown, value?: unknown): this;
332
+ having(column: Key<Grouped<T, G, A>>, value: unknown): this;
333
+ having(column: Key<Grouped<T, G, A>>, operator: Operator, value: unknown): this;
331
334
  /**
332
335
  * Add a disjunctive constraint on the groups the query returns.
333
336
  */
334
- orHaving(column: Key<Grouped<T, G, A>>, operator?: Operator | unknown, value?: unknown): this;
337
+ orHaving(column: Key<Grouped<T, G, A>>, value: unknown): this;
338
+ orHaving(column: Key<Grouped<T, G, A>>, operator: Operator, value: unknown): this;
335
339
  /**
336
340
  * Sort the groups by a column or an aggregate.
337
341
  */
@@ -518,7 +522,6 @@ declare class Connection {
518
522
 
519
523
  type Nested<T> = (query: Builder<T>) => void;
520
524
  type Joining = (join: Join) => void;
521
- type Column<T> = Key<T> | Partial<T> | Nested<T>;
522
525
  declare class Builder<T = Record<string, unknown>> {
523
526
  #private;
524
527
  /**
@@ -532,15 +535,21 @@ declare class Builder<T = Record<string, unknown>> {
532
535
  /**
533
536
  * Add a constraint to the query.
534
537
  */
535
- where(column: Column<T>, operator?: Operator | unknown, value?: unknown): this;
538
+ where(column: Partial<T> | Nested<T>): this;
539
+ where(column: Key<T>, value: unknown): this;
540
+ where(column: Key<T>, operator: Operator, value: unknown): this;
536
541
  /**
537
542
  * Add a disjunctive constraint to the query.
538
543
  */
539
- orWhere(column: Column<T>, operator?: Operator | unknown, value?: unknown): this;
544
+ orWhere(column: Partial<T> | Nested<T>): this;
545
+ orWhere(column: Key<T>, value: unknown): this;
546
+ orWhere(column: Key<T>, operator: Operator, value: unknown): this;
540
547
  /**
541
548
  * Add a negated constraint to the query.
542
549
  */
543
- whereNot(column: Column<T>, operator?: Operator | unknown, value?: unknown): this;
550
+ whereNot(column: Partial<T> | Nested<T>): this;
551
+ whereNot(column: Key<T>, value: unknown): this;
552
+ whereNot(column: Key<T>, operator: Operator, value: unknown): this;
544
553
  /**
545
554
  * Constrain a column to one of the given values.
546
555
  */
@@ -624,15 +633,21 @@ declare class Builder<T = Record<string, unknown>> {
624
633
  /**
625
634
  * Join another table, keeping only the rows that match.
626
635
  */
627
- join<R = Record<string, unknown>>(table: string, first: string | Joining, operator?: Operator | string, second?: string): Builder<R>;
636
+ join<R = Record<string, unknown>>(table: string, first: Joining): Builder<R>;
637
+ join<R = Record<string, unknown>>(table: string, first: string, second: string): Builder<R>;
638
+ join<R = Record<string, unknown>>(table: string, first: string, operator: Operator, second: string): Builder<R>;
628
639
  /**
629
640
  * Join another table, keeping every row of this one.
630
641
  */
631
- leftJoin<R = Record<string, unknown>>(table: string, first: string | Joining, operator?: Operator | string, second?: string): Builder<R>;
642
+ leftJoin<R = Record<string, unknown>>(table: string, first: Joining): Builder<R>;
643
+ leftJoin<R = Record<string, unknown>>(table: string, first: string, second: string): Builder<R>;
644
+ leftJoin<R = Record<string, unknown>>(table: string, first: string, operator: Operator, second: string): Builder<R>;
632
645
  /**
633
646
  * Join another table, keeping every row of it.
634
647
  */
635
- rightJoin<R = Record<string, unknown>>(table: string, first: string | Joining, operator?: Operator | string, second?: string): Builder<R>;
648
+ rightJoin<R = Record<string, unknown>>(table: string, first: Joining): Builder<R>;
649
+ rightJoin<R = Record<string, unknown>>(table: string, first: string, second: string): Builder<R>;
650
+ rightJoin<R = Record<string, unknown>>(table: string, first: string, operator: Operator, second: string): Builder<R>;
636
651
  /**
637
652
  * Pair every row of this table with every row of another.
638
653
  */
@@ -640,11 +655,13 @@ declare class Builder<T = Record<string, unknown>> {
640
655
  /**
641
656
  * Constrain a column against another column of the same row.
642
657
  */
643
- whereColumn(column: Key<T>, operator: Operator | string, other?: string): this;
658
+ whereColumn(column: Key<T>, other: string): this;
659
+ whereColumn(column: Key<T>, operator: Operator, other: string): this;
644
660
  /**
645
661
  * Constrain a column against another column of the same row, disjunctively.
646
662
  */
647
- orWhereColumn(column: Key<T>, operator: Operator | string, other?: string): this;
663
+ orWhereColumn(column: Key<T>, other: string): this;
664
+ orWhereColumn(column: Key<T>, operator: Operator, other: string): this;
648
665
  /**
649
666
  * Project only the given columns, which may alias what they select.
650
667
  */
package/dist/main.js CHANGED
@@ -1106,7 +1106,7 @@ var Request = class {
1106
1106
  };
1107
1107
 
1108
1108
  // src/schema/Coercer.ts
1109
- var FALSY = ["false", "0"];
1109
+ var FALSY = /* @__PURE__ */ new Set(["false", "0"]);
1110
1110
  var Coercer = class {
1111
1111
  /**
1112
1112
  * Coerce a value into its declared column type.
@@ -1123,7 +1123,7 @@ var Coercer = class {
1123
1123
  case "float":
1124
1124
  return this.#numeric(value, strict, false);
1125
1125
  case "boolean":
1126
- return typeof value === "string" && FALSY.includes(value) ? false : Boolean(value);
1126
+ return typeof value === "string" && FALSY.has(value) ? false : Boolean(value);
1127
1127
  case "decimal":
1128
1128
  return this.#scaled(value, strict);
1129
1129
  case "enum":
@@ -1371,15 +1371,9 @@ var Join = class {
1371
1371
  * The conditions the tables are joined on.
1372
1372
  */
1373
1373
  #conditions = [];
1374
- /**
1375
- * Join on a pair of columns.
1376
- */
1377
1374
  on(first, operator, second) {
1378
1375
  return this.#condition("and", first, operator, second);
1379
1376
  }
1380
- /**
1381
- * Join on a pair of columns, disjunctively.
1382
- */
1383
1377
  orOn(first, operator, second) {
1384
1378
  return this.#condition("or", first, operator, second);
1385
1379
  }
@@ -1424,7 +1418,7 @@ var Predicate = class {
1424
1418
  if (index === 0 || constraint.conjunction === "or") {
1425
1419
  groups.push([]);
1426
1420
  }
1427
- groups[groups.length - 1]?.push(constraint);
1421
+ groups.at(-1)?.push(constraint);
1428
1422
  }
1429
1423
  return groups;
1430
1424
  }
@@ -1684,7 +1678,7 @@ var Joiner = class {
1684
1678
  };
1685
1679
 
1686
1680
  // src/query/Planner.ts
1687
- var RANGEABLE = ["=", "==", "===", ">", ">=", "<", "<="];
1681
+ var RANGEABLE = /* @__PURE__ */ new Set(["=", "==", "===", ">", ">=", "<", "<="]);
1688
1682
  var Planner = class {
1689
1683
  /**
1690
1684
  * Compile the constraints and orders into an execution plan.
@@ -1774,7 +1768,7 @@ var Planner = class {
1774
1768
  }
1775
1769
  return { constraint, ...target, range: IDBKeyRange.bound(constraint.from, constraint.to, false, false), values: null };
1776
1770
  }
1777
- if (!RANGEABLE.includes(constraint.operator) || !this.#keyable(constraint.value)) {
1771
+ if (!RANGEABLE.has(constraint.operator) || !this.#keyable(constraint.value)) {
1778
1772
  return null;
1779
1773
  }
1780
1774
  return { constraint, ...target, range: this.#range(constraint.operator, constraint.value), values: null };
@@ -1850,7 +1844,7 @@ var Signature = class {
1850
1844
  * Build a signature identifying a record by every column it holds.
1851
1845
  */
1852
1846
  static of(record) {
1853
- return Object.keys(record).sort().map((column) => this.#segment(column) + this.#segment(this.value(record[column]))).join("");
1847
+ return Object.keys(record).sort((a, b) => a < b ? -1 : 1).map((column) => this.#segment(column) + this.#segment(this.value(record[column]))).join("");
1854
1848
  }
1855
1849
  /**
1856
1850
  * Build a signature identifying an ordered list of values.
@@ -1933,17 +1927,11 @@ var Grouping = class _Grouping {
1933
1927
  grouping.#offset = this.#offset;
1934
1928
  return grouping;
1935
1929
  }
1936
- /**
1937
- * Constrain the groups the query returns.
1938
- */
1939
- having(column, operator, value) {
1940
- return this.#constrain("and", column, operator, value);
1930
+ having(column, ...parameters) {
1931
+ return this.#constrain("and", column, parameters);
1941
1932
  }
1942
- /**
1943
- * Add a disjunctive constraint on the groups the query returns.
1944
- */
1945
- orHaving(column, operator, value) {
1946
- return this.#constrain("or", column, operator, value);
1933
+ orHaving(column, ...parameters) {
1934
+ return this.#constrain("or", column, parameters);
1947
1935
  }
1948
1936
  /**
1949
1937
  * Sort the groups by a column or an aggregate.
@@ -2053,8 +2041,8 @@ var Grouping = class _Grouping {
2053
2041
  /**
2054
2042
  * Add a constraint on the groups the query returns.
2055
2043
  */
2056
- #constrain(conjunction, column, operator, value) {
2057
- const resolved = value === void 0 ? { operator: "=", value: operator } : { operator, value };
2044
+ #constrain(conjunction, column, parameters) {
2045
+ const resolved = parameters.length < 2 ? { operator: "=", value: parameters[0] } : { operator: parameters[0], value: parameters[1] };
2058
2046
  this.#constraints.push({ type: "basic", column, operator: resolved.operator, value: resolved.value, conjunction, not: false });
2059
2047
  return this;
2060
2048
  }
@@ -2122,23 +2110,14 @@ var Builder = class _Builder {
2122
2110
  get table() {
2123
2111
  return this.#table;
2124
2112
  }
2125
- /**
2126
- * Add a constraint to the query.
2127
- */
2128
- where(column, operator, value) {
2129
- return this.#constrain("and", false, column, operator, value);
2113
+ where(column, ...parameters) {
2114
+ return this.#constrain("and", false, column, parameters);
2130
2115
  }
2131
- /**
2132
- * Add a disjunctive constraint to the query.
2133
- */
2134
- orWhere(column, operator, value) {
2135
- return this.#constrain("or", false, column, operator, value);
2116
+ orWhere(column, ...parameters) {
2117
+ return this.#constrain("or", false, column, parameters);
2136
2118
  }
2137
- /**
2138
- * Add a negated constraint to the query.
2139
- */
2140
- whereNot(column, operator, value) {
2141
- return this.#constrain("and", true, column, operator, value);
2119
+ whereNot(column, ...parameters) {
2120
+ return this.#constrain("and", true, column, parameters);
2142
2121
  }
2143
2122
  /**
2144
2123
  * Constrain a column to one of the given values.
@@ -2263,21 +2242,12 @@ var Builder = class _Builder {
2263
2242
  whereDay(column, value) {
2264
2243
  return this.#part("and", column, "day", value);
2265
2244
  }
2266
- /**
2267
- * Join another table, keeping only the rows that match.
2268
- */
2269
2245
  join(table, first, operator, second) {
2270
2246
  return this.#join("inner", table, first, operator, second);
2271
2247
  }
2272
- /**
2273
- * Join another table, keeping every row of this one.
2274
- */
2275
2248
  leftJoin(table, first, operator, second) {
2276
2249
  return this.#join("left", table, first, operator, second);
2277
2250
  }
2278
- /**
2279
- * Join another table, keeping every row of it.
2280
- */
2281
2251
  rightJoin(table, first, operator, second) {
2282
2252
  return this.#join("right", table, first, operator, second);
2283
2253
  }
@@ -2288,15 +2258,9 @@ var Builder = class _Builder {
2288
2258
  this.#joins.push({ table, type: "cross", conditions: [] });
2289
2259
  return this;
2290
2260
  }
2291
- /**
2292
- * Constrain a column against another column of the same row.
2293
- */
2294
2261
  whereColumn(column, operator, other) {
2295
2262
  return this.#compared("and", column, operator, other);
2296
2263
  }
2297
- /**
2298
- * Constrain a column against another column of the same row, disjunctively.
2299
- */
2300
2264
  orWhereColumn(column, operator, other) {
2301
2265
  return this.#compared("or", column, operator, other);
2302
2266
  }
@@ -2882,7 +2846,7 @@ var Builder = class _Builder {
2882
2846
  }
2883
2847
  }
2884
2848
  /**
2885
- * Apply a change to every record matching the query, in the order the plan scans them.
2849
+ * Apply a change to every record matching the query, in the order the query asks for.
2886
2850
  */
2887
2851
  async #modify(apply) {
2888
2852
  const schema = await this.#connection.schema(this.#table);
@@ -2891,8 +2855,20 @@ var Builder = class _Builder {
2891
2855
  const started = performance.now();
2892
2856
  const matches = Predicate.compile(plan.residual);
2893
2857
  const ceiling = this.#limit === null ? null : this.#offset + this.#limit;
2858
+ const collects = !plan.ordered && this.#orders.length > 0 && (this.#limit !== null || this.#offset > 0);
2894
2859
  let seen = 0;
2895
2860
  let affected = 0;
2861
+ if (collects) {
2862
+ const collected = plan.values === null ? await this.#cursored(store, plan, matches) : await this.#points(store, plan, matches);
2863
+ for (const entry of this.#paged(this.#sorted(collected))) {
2864
+ await Request.walk(store.openCursor(IDBKeyRange.only(entry.key)), (cursor) => {
2865
+ apply(cursor);
2866
+ affected++;
2867
+ });
2868
+ }
2869
+ this.#emit(Planner.describe(plan), started, affected);
2870
+ return affected;
2871
+ }
2896
2872
  const visit = (cursor) => {
2897
2873
  if (!matches(cursor.value)) {
2898
2874
  return true;
@@ -2909,6 +2885,9 @@ var Builder = class _Builder {
2909
2885
  await Request.walk(source.openCursor(plan.range, plan.direction), visit);
2910
2886
  } else {
2911
2887
  for (const value of plan.values) {
2888
+ if (ceiling !== null && seen >= ceiling) {
2889
+ break;
2890
+ }
2912
2891
  const source = plan.index === null ? store : store.index(plan.index);
2913
2892
  await Request.walk(source.openCursor(IDBKeyRange.only(value)), visit);
2914
2893
  }
@@ -2919,7 +2898,7 @@ var Builder = class _Builder {
2919
2898
  /**
2920
2899
  * Add a constraint of the given shape to the query.
2921
2900
  */
2922
- #constrain(conjunction, not, column, operator, value) {
2901
+ #constrain(conjunction, not, column, parameters) {
2923
2902
  if (typeof column === "function") {
2924
2903
  const nested = new _Builder(this.#connection, this.#table, this.#transaction);
2925
2904
  column(nested);
@@ -2936,7 +2915,7 @@ var Builder = class _Builder {
2936
2915
  }));
2937
2916
  return this.#push({ type: "nested", constraints, conjunction, not });
2938
2917
  }
2939
- const resolved = value === void 0 ? { operator: "=", value: operator } : { operator, value };
2918
+ const resolved = parameters.length < 2 ? { operator: "=", value: parameters[0] } : { operator: parameters[0], value: parameters[1] };
2940
2919
  return this.#push({ type: "basic", column, operator: resolved.operator, value: resolved.value, conjunction, not });
2941
2920
  }
2942
2921
  /**
@@ -2996,6 +2975,8 @@ var Builder = class _Builder {
2996
2975
  const clause = new Join();
2997
2976
  if (typeof first === "function") {
2998
2977
  first(clause);
2978
+ } else if (second === void 0) {
2979
+ clause.on(first, operator);
2999
2980
  } else {
3000
2981
  clause.on(first, operator, second);
3001
2982
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bjnstnkvc/db",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "description": "TypeScript database layer for IndexedDB with an API modeled on Laravel.",
5
5
  "type": "module",
6
6
  "main": "./dist/main.cjs",
@@ -8,8 +8,14 @@
8
8
  "types": "./dist/main.d.ts",
9
9
  "exports": {
10
10
  ".": {
11
- "import": { "types": "./dist/main.d.ts", "default": "./dist/main.js" },
12
- "require": { "types": "./dist/main.d.cts", "default": "./dist/main.cjs" }
11
+ "import": {
12
+ "types": "./dist/main.d.ts",
13
+ "default": "./dist/main.js"
14
+ },
15
+ "require": {
16
+ "types": "./dist/main.d.cts",
17
+ "default": "./dist/main.cjs"
18
+ }
13
19
  }
14
20
  },
15
21
  "files": [