@bjnstnkvc/db 0.1.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +81 -82
- package/dist/main.cjs +17 -8
- package/dist/main.js +17 -8
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# DB
|
|
2
2
|
|
|
3
|
-
A database layer for IndexedDB, with an API
|
|
3
|
+
A database layer for IndexedDB, with an API modeled on [Laravel's](https://laravel.com/docs/12.x/database): a `DB` class, a fluent query builder, a schema builder and forward-only migrations that run when your app boots.
|
|
4
4
|
|
|
5
5
|
The method names and their semantics follow Laravel closely enough that the docs are worth reading side by side, and each section below links the page it draws from. It is not a port: IndexedDB is a key-value store with no query language, so the places where behaviour has to differ are called out where they arise. This project is not affiliated with the Laravel project.
|
|
6
6
|
|
|
@@ -94,13 +94,13 @@ await DB.migrate('app');
|
|
|
94
94
|
and every example below passes it as `DB.table<User>('users')` so the builder can type its
|
|
95
95
|
constraints, its return values and its aggregate keys.
|
|
96
96
|
|
|
97
|
-
| Option
|
|
98
|
-
|
|
99
|
-
| `default`
|
|
100
|
-
| `connections[name].database`
|
|
101
|
-
| `connections[name].migrations` | Ordered migration classes. Their order **is** the schema version.
|
|
102
|
-
| `connections[name].seeders`
|
|
103
|
-
| `connections[name].strict`
|
|
97
|
+
| Option | Meaning |
|
|
98
|
+
|--------------------------------|---------------------------------------------------------------------------------------------------------|
|
|
99
|
+
| `default` | The connection used when none is named |
|
|
100
|
+
| `connections[name].database` | The IndexedDB database name |
|
|
101
|
+
| `connections[name].migrations` | Ordered migration classes. Their order **is** the schema version. |
|
|
102
|
+
| `connections[name].seeders` | Ordered seeder classes, run by `DB.seed(name)`. See [Seeding](#seeding). |
|
|
103
|
+
| `connections[name].strict` | Defaults to `true`. Nullability violations and uncoercible values throw. `false` writes `null` instead. |
|
|
104
104
|
|
|
105
105
|
`DB.migrate(name)` is idempotent. It opens the database at the version your migrations ask for, and
|
|
106
106
|
when that already matches, nothing runs. Calling it on every boot is the intended usage, and there
|
|
@@ -144,8 +144,10 @@ start over, `DB.fresh(name)` deletes the database and replays every migration.
|
|
|
144
144
|
Migrations may only ever be **appended**. Reordering them, or removing one that already ran, throws
|
|
145
145
|
`MigrationMismatchException` rather than corrupting the schema.
|
|
146
146
|
|
|
147
|
-
The recorded name defaults to the class name
|
|
148
|
-
|
|
147
|
+
The recorded name defaults to the class name in snake case, the way Laravel names a migration file,
|
|
148
|
+
so `CreateUsersTable` is recorded as `create_users_table`. Because it is derived from the class name,
|
|
149
|
+
a bundler that mangles class names will look like a reordered list. If you minify with class-name
|
|
150
|
+
mangling, override `name()`:
|
|
149
151
|
|
|
150
152
|
```ts
|
|
151
153
|
class CreateUsersTable extends Migration {
|
|
@@ -184,15 +186,15 @@ Resolves to one entry per registered migration:
|
|
|
184
186
|
|
|
185
187
|
```
|
|
186
188
|
[
|
|
187
|
-
{ migration: '
|
|
188
|
-
{ migration: '
|
|
189
|
+
{ migration: 'create_users_table', ran: true, at: '2026-08-27T21:00:00.000Z' },
|
|
190
|
+
{ migration: 'add_role_to_users_table', ran: false, at: null }
|
|
189
191
|
]
|
|
190
192
|
```
|
|
191
193
|
|
|
192
194
|
`DB.status(name)` never migrates as a side effect, so you can call it before `DB.migrate(name)` to
|
|
193
195
|
see what is pending.
|
|
194
196
|
|
|
195
|
-
>
|
|
197
|
+
> Modeled on Laravel's [Migrations](https://laravel.com/docs/12.x/migrations). These only run
|
|
196
198
|
> forward, and they are registered in the connection config rather than discovered from a directory.
|
|
197
199
|
|
|
198
200
|
### Seeding
|
|
@@ -478,7 +480,7 @@ await DB.fresh('app');
|
|
|
478
480
|
await DB.fresh('app', { seed: true });
|
|
479
481
|
```
|
|
480
482
|
|
|
481
|
-
>
|
|
483
|
+
> Modeled on Laravel's [Database: Seeding](https://laravel.com/docs/12.x/seeding), down to the
|
|
482
484
|
> seeded connection standing in as the default for the duration of the run.
|
|
483
485
|
|
|
484
486
|
### Defining a schema
|
|
@@ -486,21 +488,21 @@ await DB.fresh('app', { seed: true });
|
|
|
486
488
|
IndexedDB stores whole objects and enforces only a key path, `autoIncrement` and indexes. Column
|
|
487
489
|
types are recorded as metadata and enforced by this package at write time.
|
|
488
490
|
|
|
489
|
-
| Blueprint
|
|
490
|
-
|
|
491
|
-
| `table.id()`
|
|
492
|
-
| `table.uuid('id').primary()`
|
|
493
|
-
| `table.string` / `integer` / `float` / `boolean` / `date` / `datetime` / `json` | Column metadata
|
|
494
|
-
| `table.decimal('price', 2)`
|
|
495
|
-
| `table.enum('role', Role)`
|
|
496
|
-
| `.nullable()`
|
|
497
|
-
| `.default(value)`
|
|
498
|
-
| `.primary()`
|
|
499
|
-
| `.index()`
|
|
500
|
-
| `.unique()`
|
|
501
|
-
| `table.index(['a', 'b'])`
|
|
502
|
-
| `.multiEntry()`
|
|
503
|
-
| `table.timestamps()`
|
|
491
|
+
| Blueprint | Effect |
|
|
492
|
+
|---------------------------------------------------------------------------------|------------------------------------------------------------------------------------|
|
|
493
|
+
| `table.id()` | `keyPath: 'id'`, `autoIncrement: true` |
|
|
494
|
+
| `table.uuid('id').primary()` | `keyPath: 'id'`, no autoIncrement |
|
|
495
|
+
| `table.string` / `integer` / `float` / `boolean` / `date` / `datetime` / `json` | Column metadata |
|
|
496
|
+
| `table.decimal('price', 2)` | Column metadata, stored as a whole number of the smallest unit |
|
|
497
|
+
| `table.enum('role', Role)` | Column metadata, checked at write time. Takes a list, an enum or a constant object |
|
|
498
|
+
| `.nullable()` | Metadata, enforced at write time |
|
|
499
|
+
| `.default(value)` | Applied at write time, and backfilled when added to an existing table |
|
|
500
|
+
| `.primary()` | Makes the column the key path. At most one per table. |
|
|
501
|
+
| `.index()` | `createIndex('users_name_index', 'name')` |
|
|
502
|
+
| `.unique()` | `createIndex('users_email_unique', 'email', { unique: true })` |
|
|
503
|
+
| `table.index(['a', 'b'])` | Compound index |
|
|
504
|
+
| `.multiEntry()` | One index entry per array element |
|
|
505
|
+
| `table.timestamps()` | Nullable `created_at` / `updated_at`, filled automatically |
|
|
504
506
|
|
|
505
507
|
Altering a table also supports `dropColumn`, `renameColumn`, `dropIndex` and `Schema.rename`. The key
|
|
506
508
|
path may not be dropped or renamed, because IndexedDB fixes it when the store is created.
|
|
@@ -665,7 +667,7 @@ await Schema.getIndexes('users');
|
|
|
665
667
|
await Schema.connection('reporting').hasTable('reports');
|
|
666
668
|
```
|
|
667
669
|
|
|
668
|
-
>
|
|
670
|
+
> Modeled on Laravel's [Migrations: Tables](https://laravel.com/docs/12.x/migrations#tables).
|
|
669
671
|
> Column types are metadata this package enforces at write time, since IndexedDB stores whole objects
|
|
670
672
|
> and checks nothing itself.
|
|
671
673
|
|
|
@@ -771,22 +773,22 @@ await DB.table<User>('users').min('age');
|
|
|
771
773
|
await DB.table<User>('users').max('age');
|
|
772
774
|
```
|
|
773
775
|
|
|
774
|
-
| Terminal
|
|
775
|
-
|
|
776
|
-
| `get()`
|
|
777
|
-
| `first()`
|
|
778
|
-
| `firstOrFail()`
|
|
779
|
-
| `find(key)`
|
|
780
|
-
| `findOrFail(key)`
|
|
781
|
-
| `value(column)`
|
|
782
|
-
| `pluck(column)`
|
|
783
|
-
| `pluck(column, key)`
|
|
784
|
-
| `exists()` / `doesntExist()`
|
|
785
|
-
| `count()`
|
|
786
|
-
| `sum(column)`
|
|
787
|
-
| `avg(column)` / `min(column)` / `max(column)` | `number` or `null` when nothing matched
|
|
788
|
-
| `sole()`
|
|
789
|
-
| `paginate(page?, perPage?)`
|
|
776
|
+
| Terminal | Resolves to |
|
|
777
|
+
|-----------------------------------------------|-----------------------------------------------------------------------------|
|
|
778
|
+
| `get()` | `T[]` |
|
|
779
|
+
| `first()` | `T` or `null` |
|
|
780
|
+
| `firstOrFail()` | `T`, or throws `RecordsNotFoundException` |
|
|
781
|
+
| `find(key)` | `T` or `null`, by point lookup on the key path |
|
|
782
|
+
| `findOrFail(key)` | `T`, or throws `RecordsNotFoundException` |
|
|
783
|
+
| `value(column)` | The column of the first matching record, or `null` |
|
|
784
|
+
| `pluck(column)` | `V[]` in result order |
|
|
785
|
+
| `pluck(column, key)` | `Record<string, V>`, keyed by a second column |
|
|
786
|
+
| `exists()` / `doesntExist()` | `boolean` |
|
|
787
|
+
| `count()` | `number` |
|
|
788
|
+
| `sum(column)` | `number` |
|
|
789
|
+
| `avg(column)` / `min(column)` / `max(column)` | `number` or `null` when nothing matched |
|
|
790
|
+
| `sole()` | `T`, or throws `RecordsNotFoundException` / `MultipleRecordsFoundException` |
|
|
791
|
+
| `paginate(page?, perPage?)` | `{ data, total, perPage, currentPage, lastPage }` |
|
|
790
792
|
|
|
791
793
|
`min` and `max` read the answer straight off the index when the column has one and the query is
|
|
792
794
|
unconstrained, so they cost one cursor rather than a full scan.
|
|
@@ -852,7 +854,7 @@ The key path may not be updated, so `update`, `upsert` and `increment` all refus
|
|
|
852
854
|
when an index drives the query and key order otherwise. Pair them with an indexed `orderBy` if you
|
|
853
855
|
need a defined order.
|
|
854
856
|
|
|
855
|
-
>
|
|
857
|
+
> Modeled on Laravel's [Database: Query Builder](https://laravel.com/docs/12.x/queries). The method
|
|
856
858
|
> names and their semantics match, and every terminal is asynchronous because IndexedDB is.
|
|
857
859
|
|
|
858
860
|
### Joins
|
|
@@ -959,7 +961,7 @@ covers it, and `chunk` slices the materialised result rather than walking keys.
|
|
|
959
961
|
await DB.table('users').whereColumn('updated_at', '>', 'created_at').get();
|
|
960
962
|
```
|
|
961
963
|
|
|
962
|
-
>
|
|
964
|
+
> Modeled on Laravel's [Query Builder: Joins](https://laravel.com/docs/12.x/queries#joins). Rows
|
|
963
965
|
> stay flat as they do in Laravel, and the join itself runs in memory because IndexedDB has none.
|
|
964
966
|
|
|
965
967
|
### Grouping
|
|
@@ -994,13 +996,13 @@ Because the alias is an object key rather than a string inside an expression, th
|
|
|
994
996
|
inferred rather than cast. That row is typed `{ role: string; total: number; oldest: number | null }`,
|
|
995
997
|
and reading a column you did not group or aggregate is a compile error.
|
|
996
998
|
|
|
997
|
-
| Aggregate
|
|
998
|
-
|
|
999
|
-
| `{ count: '*' }`
|
|
1000
|
-
| `{ count: 'column' }`
|
|
1001
|
-
| `{ sum: 'column' }`
|
|
1002
|
-
| `{ avg: 'column' }`
|
|
1003
|
-
| `{ min: 'column' }` / `{ max: 'column' }` | The extreme, `null` for a group with no values
|
|
999
|
+
| Aggregate | Meaning |
|
|
1000
|
+
|-------------------------------------------|-------------------------------------------------------|
|
|
1001
|
+
| `{ count: '*' }` | The number of records in the group, always a `number` |
|
|
1002
|
+
| `{ count: 'column' }` | The number of records whose column is not null |
|
|
1003
|
+
| `{ sum: 'column' }` | The total, `0` for a group with no values |
|
|
1004
|
+
| `{ avg: 'column' }` | The mean, `null` for a group with no values |
|
|
1005
|
+
| `{ min: 'column' }` / `{ max: 'column' }` | The extreme, `null` for a group with no values |
|
|
1004
1006
|
|
|
1005
1007
|
Group by several columns by passing several names:
|
|
1006
1008
|
|
|
@@ -1032,7 +1034,7 @@ await DB.table<User>('users')
|
|
|
1032
1034
|
Grouping happens in memory after the records are fetched, so the planner still applies to the
|
|
1033
1035
|
`where` clauses that select them, and a grouped query reports the plan of that underlying fetch.
|
|
1034
1036
|
|
|
1035
|
-
>
|
|
1037
|
+
> Modeled on Laravel's [Query Builder: Grouping](https://laravel.com/docs/12.x/queries#groupby-having),
|
|
1036
1038
|
> with the aggregates named in a typed object instead of raw SQL.
|
|
1037
1039
|
|
|
1038
1040
|
### Query plans
|
|
@@ -1092,7 +1094,7 @@ commits behind your back the first time you await anything outside it, so offeri
|
|
|
1092
1094
|
offering a trap. The same rule as migrations applies here: the callback may only await operations
|
|
1093
1095
|
from this package.
|
|
1094
1096
|
|
|
1095
|
-
>
|
|
1097
|
+
> Modeled on Laravel's [Database: Transactions](https://laravel.com/docs/12.x/database#database-transactions).
|
|
1096
1098
|
> The tables have to be declared up front, because an IndexedDB transaction fixes its scope when it
|
|
1097
1099
|
> opens.
|
|
1098
1100
|
|
|
@@ -1116,9 +1118,6 @@ Available events: `query`, `transaction-beginning`, `transaction-committed`,
|
|
|
1116
1118
|
A connection with no seeders announces nothing, so `seeding-started` firing always means at least
|
|
1117
1119
|
one seeder is about to run.
|
|
1118
1120
|
|
|
1119
|
-
Listeners are **persistent by default**, with an opt-in `{ once: true }`. This is a deliberate
|
|
1120
|
-
departure from `@bjnstnkvc/local-storage`, where every listener fires exactly once.
|
|
1121
|
-
|
|
1122
1121
|
```ts
|
|
1123
1122
|
DB.enableQueryLog();
|
|
1124
1123
|
|
|
@@ -1148,7 +1147,7 @@ DB.disableQueryLog();
|
|
|
1148
1147
|
|
|
1149
1148
|
`DB.logging()` then returns `false`, and `DB.getQueryLog()` an empty array.
|
|
1150
1149
|
|
|
1151
|
-
>
|
|
1150
|
+
> Modeled on Laravel's [Database: Listening for Query Events](https://laravel.com/docs/12.x/database#listening-for-query-events),
|
|
1152
1151
|
> with the same enable, get and flush surface, dispatched as a browser event.
|
|
1153
1152
|
|
|
1154
1153
|
### Multiple tabs
|
|
@@ -1171,14 +1170,14 @@ DB.disconnect('app');
|
|
|
1171
1170
|
DB.purge('app');
|
|
1172
1171
|
```
|
|
1173
1172
|
|
|
1174
|
-
| Call
|
|
1175
|
-
|
|
1176
|
-
| `connection()`
|
|
1177
|
-
| `connection(name)` | A named connection, cached after the first resolve
|
|
1173
|
+
| Call | Effect |
|
|
1174
|
+
|--------------------|----------------------------------------------------------------------------------|
|
|
1175
|
+
| `connection()` | The default connection |
|
|
1176
|
+
| `connection(name)` | A named connection, cached after the first resolve |
|
|
1178
1177
|
| `disconnect(name)` | Close the handle, leaving the connection registered so the next query reopens it |
|
|
1179
|
-
| `purge(name)`
|
|
1178
|
+
| `purge(name)` | Close it and drop it, so the next resolve rebuilds it from configuration |
|
|
1180
1179
|
|
|
1181
|
-
>
|
|
1180
|
+
> Modeled on Laravel's [Database: Multiple Connections](https://laravel.com/docs/12.x/database#using-multiple-database-connections),
|
|
1182
1181
|
> resolved by name and cached, with one IndexedDB database behind each.
|
|
1183
1182
|
|
|
1184
1183
|
### Storage quota
|
|
@@ -1233,21 +1232,21 @@ memory, so writes inside a narrowed transaction still get their defaults.
|
|
|
1233
1232
|
Every exception extends `Error` and sets its own `name`, so `instanceof` and the stack both read
|
|
1234
1233
|
true. All of them are exported from the package root.
|
|
1235
1234
|
|
|
1236
|
-
| Exception
|
|
1237
|
-
|
|
1238
|
-
| `CheckConstraintViolationException`
|
|
1239
|
-
| `ConnectionNotConfiguredException`
|
|
1240
|
-
| `DatabaseBlockedException`
|
|
1241
|
-
| `MigrationMismatchException`
|
|
1242
|
-
| `MigrationTransactionClosedException` | A migration awaited something outside this package, letting the versionchange transaction commit early
|
|
1243
|
-
| `MultipleRecordsFoundException`
|
|
1244
|
-
| `NotNullConstraintViolationException` | A non-nullable column is written as null, or is absent with no default
|
|
1245
|
-
| `QuotaExceededException`
|
|
1246
|
-
| `RecordsNotFoundException`
|
|
1247
|
-
| `ReservedTableException`
|
|
1248
|
-
| `SchemaException`
|
|
1249
|
-
| `TableNotFoundException`
|
|
1250
|
-
| `UniqueConstraintViolationException`
|
|
1235
|
+
| Exception | Thrown when |
|
|
1236
|
+
|---------------------------------------|-------------------------------------------------------------------------------------------------------------|
|
|
1237
|
+
| `CheckConstraintViolationException` | A write gives an enumerated column a value it does not accept |
|
|
1238
|
+
| `ConnectionNotConfiguredException` | A connection is resolved under a name `DB.configure` never declared |
|
|
1239
|
+
| `DatabaseBlockedException` | Another tab holds the database open at an older version, so the upgrade cannot start |
|
|
1240
|
+
| `MigrationMismatchException` | The recorded migration list is not a prefix of the registered one, so one was removed, renamed or reordered |
|
|
1241
|
+
| `MigrationTransactionClosedException` | A migration awaited something outside this package, letting the versionchange transaction commit early |
|
|
1242
|
+
| `MultipleRecordsFoundException` | `sole()` matched more than one record |
|
|
1243
|
+
| `NotNullConstraintViolationException` | A non-nullable column is written as null, or is absent with no default |
|
|
1244
|
+
| `QuotaExceededException` | The origin's storage quota stopped the operation |
|
|
1245
|
+
| `RecordsNotFoundException` | `firstOrFail()`, `sole()` or `findOrFail()` matched nothing |
|
|
1246
|
+
| `ReservedTableException` | A migration tries to create `migrations` or `schema` |
|
|
1247
|
+
| `SchemaException` | A schema or query call the shape of the database cannot support |
|
|
1248
|
+
| `TableNotFoundException` | A query or schema read names a table the database does not have |
|
|
1249
|
+
| `UniqueConstraintViolationException` | A write collides with a unique index, named in the message |
|
|
1251
1250
|
|
|
1252
1251
|
`SchemaException` is the broad one, so here is every case that raises it:
|
|
1253
1252
|
|
package/dist/main.cjs
CHANGED
|
@@ -1162,7 +1162,7 @@ var Request = class {
|
|
|
1162
1162
|
* Name the failure, where the platform reports one this package can say more about.
|
|
1163
1163
|
*/
|
|
1164
1164
|
static translate(error) {
|
|
1165
|
-
if (error
|
|
1165
|
+
if (error?.name === "QuotaExceededError") {
|
|
1166
1166
|
return new QuotaExceededException();
|
|
1167
1167
|
}
|
|
1168
1168
|
return error;
|
|
@@ -1591,13 +1591,13 @@ var Predicate = class {
|
|
|
1591
1591
|
let resume = 0;
|
|
1592
1592
|
while (index < subject.length) {
|
|
1593
1593
|
const current = tokens[token];
|
|
1594
|
-
if (current
|
|
1594
|
+
if (current?.kind === "any") {
|
|
1595
1595
|
wildcard = token;
|
|
1596
1596
|
resume = index;
|
|
1597
1597
|
token++;
|
|
1598
1598
|
continue;
|
|
1599
1599
|
}
|
|
1600
|
-
if (current
|
|
1600
|
+
if (current?.kind === "one" || current?.value === subject[index].toLowerCase()) {
|
|
1601
1601
|
token++;
|
|
1602
1602
|
index++;
|
|
1603
1603
|
continue;
|
|
@@ -1695,7 +1695,7 @@ var Joiner = class {
|
|
|
1695
1695
|
*/
|
|
1696
1696
|
static #hashable(clause) {
|
|
1697
1697
|
const condition = clause.conditions[0];
|
|
1698
|
-
return clause.conditions.length === 1 && condition
|
|
1698
|
+
return clause.conditions.length === 1 && condition?.operator === "=";
|
|
1699
1699
|
}
|
|
1700
1700
|
/**
|
|
1701
1701
|
* Index the other side by the value its join column holds.
|
|
@@ -1871,7 +1871,7 @@ var Planner = class {
|
|
|
1871
1871
|
return null;
|
|
1872
1872
|
}
|
|
1873
1873
|
const column = schema.columns.find((candidate) => candidate.name === order.column);
|
|
1874
|
-
if (column
|
|
1874
|
+
if (column?.nullable) {
|
|
1875
1875
|
return null;
|
|
1876
1876
|
}
|
|
1877
1877
|
return { constraint: { type: "null", column: order.column, conjunction: "and", not: false }, ...target, range: null, values: null };
|
|
@@ -3751,17 +3751,26 @@ var Connection = class {
|
|
|
3751
3751
|
let runner = null;
|
|
3752
3752
|
let failure = null;
|
|
3753
3753
|
request.onupgradeneeded = (event) => {
|
|
3754
|
+
const transaction = request.transaction;
|
|
3755
|
+
let live = true;
|
|
3756
|
+
const closed = () => {
|
|
3757
|
+
live = false;
|
|
3758
|
+
};
|
|
3759
|
+
transaction.addEventListener("complete", closed);
|
|
3760
|
+
transaction.addEventListener("abort", closed);
|
|
3754
3761
|
runner = Migrator.run(
|
|
3755
3762
|
this.#name,
|
|
3756
3763
|
request.result,
|
|
3757
|
-
|
|
3764
|
+
transaction,
|
|
3758
3765
|
migrations,
|
|
3759
3766
|
Migrator.pending(event.oldVersion),
|
|
3760
3767
|
/* @__PURE__ */ new Date()
|
|
3761
3768
|
);
|
|
3762
3769
|
runner.catch((error) => {
|
|
3763
3770
|
failure = error;
|
|
3764
|
-
|
|
3771
|
+
if (live) {
|
|
3772
|
+
transaction.abort();
|
|
3773
|
+
}
|
|
3765
3774
|
});
|
|
3766
3775
|
};
|
|
3767
3776
|
request.onblocked = () => {
|
|
@@ -4132,7 +4141,7 @@ var Migration = class {
|
|
|
4132
4141
|
* Get the name of the migration.
|
|
4133
4142
|
*/
|
|
4134
4143
|
name() {
|
|
4135
|
-
return this.constructor.name;
|
|
4144
|
+
return this.constructor.name.replace(/([a-z\d])([A-Z])/g, "$1_$2").replace(/([A-Z])([A-Z][a-z])/g, "$1_$2").toLowerCase();
|
|
4136
4145
|
}
|
|
4137
4146
|
};
|
|
4138
4147
|
|
package/dist/main.js
CHANGED
|
@@ -1098,7 +1098,7 @@ var Request = class {
|
|
|
1098
1098
|
* Name the failure, where the platform reports one this package can say more about.
|
|
1099
1099
|
*/
|
|
1100
1100
|
static translate(error) {
|
|
1101
|
-
if (error
|
|
1101
|
+
if (error?.name === "QuotaExceededError") {
|
|
1102
1102
|
return new QuotaExceededException();
|
|
1103
1103
|
}
|
|
1104
1104
|
return error;
|
|
@@ -1527,13 +1527,13 @@ var Predicate = class {
|
|
|
1527
1527
|
let resume = 0;
|
|
1528
1528
|
while (index < subject.length) {
|
|
1529
1529
|
const current = tokens[token];
|
|
1530
|
-
if (current
|
|
1530
|
+
if (current?.kind === "any") {
|
|
1531
1531
|
wildcard = token;
|
|
1532
1532
|
resume = index;
|
|
1533
1533
|
token++;
|
|
1534
1534
|
continue;
|
|
1535
1535
|
}
|
|
1536
|
-
if (current
|
|
1536
|
+
if (current?.kind === "one" || current?.value === subject[index].toLowerCase()) {
|
|
1537
1537
|
token++;
|
|
1538
1538
|
index++;
|
|
1539
1539
|
continue;
|
|
@@ -1631,7 +1631,7 @@ var Joiner = class {
|
|
|
1631
1631
|
*/
|
|
1632
1632
|
static #hashable(clause) {
|
|
1633
1633
|
const condition = clause.conditions[0];
|
|
1634
|
-
return clause.conditions.length === 1 && condition
|
|
1634
|
+
return clause.conditions.length === 1 && condition?.operator === "=";
|
|
1635
1635
|
}
|
|
1636
1636
|
/**
|
|
1637
1637
|
* Index the other side by the value its join column holds.
|
|
@@ -1807,7 +1807,7 @@ var Planner = class {
|
|
|
1807
1807
|
return null;
|
|
1808
1808
|
}
|
|
1809
1809
|
const column = schema.columns.find((candidate) => candidate.name === order.column);
|
|
1810
|
-
if (column
|
|
1810
|
+
if (column?.nullable) {
|
|
1811
1811
|
return null;
|
|
1812
1812
|
}
|
|
1813
1813
|
return { constraint: { type: "null", column: order.column, conjunction: "and", not: false }, ...target, range: null, values: null };
|
|
@@ -3687,17 +3687,26 @@ var Connection = class {
|
|
|
3687
3687
|
let runner = null;
|
|
3688
3688
|
let failure = null;
|
|
3689
3689
|
request.onupgradeneeded = (event) => {
|
|
3690
|
+
const transaction = request.transaction;
|
|
3691
|
+
let live = true;
|
|
3692
|
+
const closed = () => {
|
|
3693
|
+
live = false;
|
|
3694
|
+
};
|
|
3695
|
+
transaction.addEventListener("complete", closed);
|
|
3696
|
+
transaction.addEventListener("abort", closed);
|
|
3690
3697
|
runner = Migrator.run(
|
|
3691
3698
|
this.#name,
|
|
3692
3699
|
request.result,
|
|
3693
|
-
|
|
3700
|
+
transaction,
|
|
3694
3701
|
migrations,
|
|
3695
3702
|
Migrator.pending(event.oldVersion),
|
|
3696
3703
|
/* @__PURE__ */ new Date()
|
|
3697
3704
|
);
|
|
3698
3705
|
runner.catch((error) => {
|
|
3699
3706
|
failure = error;
|
|
3700
|
-
|
|
3707
|
+
if (live) {
|
|
3708
|
+
transaction.abort();
|
|
3709
|
+
}
|
|
3701
3710
|
});
|
|
3702
3711
|
};
|
|
3703
3712
|
request.onblocked = () => {
|
|
@@ -4068,7 +4077,7 @@ var Migration = class {
|
|
|
4068
4077
|
* Get the name of the migration.
|
|
4069
4078
|
*/
|
|
4070
4079
|
name() {
|
|
4071
|
-
return this.constructor.name;
|
|
4080
|
+
return this.constructor.name.replace(/([a-z\d])([A-Z])/g, "$1_$2").replace(/([A-Z])([A-Z][a-z])/g, "$1_$2").toLowerCase();
|
|
4072
4081
|
}
|
|
4073
4082
|
};
|
|
4074
4083
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bjnstnkvc/db",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "TypeScript database layer for IndexedDB with an API
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "TypeScript database layer for IndexedDB with an API modeled on Laravel.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/main.cjs",
|
|
7
7
|
"module": "./dist/main.js",
|