@bjnstnkvc/db 0.1.0 → 1.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 +10 -10
- package/dist/main.cjs +16 -7
- package/dist/main.js +16 -7
- 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
|
|
|
@@ -192,7 +192,7 @@ Resolves to one entry per registered migration:
|
|
|
192
192
|
`DB.status(name)` never migrates as a side effect, so you can call it before `DB.migrate(name)` to
|
|
193
193
|
see what is pending.
|
|
194
194
|
|
|
195
|
-
>
|
|
195
|
+
> modeled on Laravel's [Migrations](https://laravel.com/docs/12.x/migrations). These only run
|
|
196
196
|
> forward, and they are registered in the connection config rather than discovered from a directory.
|
|
197
197
|
|
|
198
198
|
### Seeding
|
|
@@ -478,7 +478,7 @@ await DB.fresh('app');
|
|
|
478
478
|
await DB.fresh('app', { seed: true });
|
|
479
479
|
```
|
|
480
480
|
|
|
481
|
-
>
|
|
481
|
+
> modeled on Laravel's [Database: Seeding](https://laravel.com/docs/12.x/seeding), down to the
|
|
482
482
|
> seeded connection standing in as the default for the duration of the run.
|
|
483
483
|
|
|
484
484
|
### Defining a schema
|
|
@@ -665,7 +665,7 @@ await Schema.getIndexes('users');
|
|
|
665
665
|
await Schema.connection('reporting').hasTable('reports');
|
|
666
666
|
```
|
|
667
667
|
|
|
668
|
-
>
|
|
668
|
+
> modeled on Laravel's [Migrations: Tables](https://laravel.com/docs/12.x/migrations#tables).
|
|
669
669
|
> Column types are metadata this package enforces at write time, since IndexedDB stores whole objects
|
|
670
670
|
> and checks nothing itself.
|
|
671
671
|
|
|
@@ -852,7 +852,7 @@ The key path may not be updated, so `update`, `upsert` and `increment` all refus
|
|
|
852
852
|
when an index drives the query and key order otherwise. Pair them with an indexed `orderBy` if you
|
|
853
853
|
need a defined order.
|
|
854
854
|
|
|
855
|
-
>
|
|
855
|
+
> modeled on Laravel's [Database: Query Builder](https://laravel.com/docs/12.x/queries). The method
|
|
856
856
|
> names and their semantics match, and every terminal is asynchronous because IndexedDB is.
|
|
857
857
|
|
|
858
858
|
### Joins
|
|
@@ -959,7 +959,7 @@ covers it, and `chunk` slices the materialised result rather than walking keys.
|
|
|
959
959
|
await DB.table('users').whereColumn('updated_at', '>', 'created_at').get();
|
|
960
960
|
```
|
|
961
961
|
|
|
962
|
-
>
|
|
962
|
+
> modeled on Laravel's [Query Builder: Joins](https://laravel.com/docs/12.x/queries#joins). Rows
|
|
963
963
|
> stay flat as they do in Laravel, and the join itself runs in memory because IndexedDB has none.
|
|
964
964
|
|
|
965
965
|
### Grouping
|
|
@@ -1032,7 +1032,7 @@ await DB.table<User>('users')
|
|
|
1032
1032
|
Grouping happens in memory after the records are fetched, so the planner still applies to the
|
|
1033
1033
|
`where` clauses that select them, and a grouped query reports the plan of that underlying fetch.
|
|
1034
1034
|
|
|
1035
|
-
>
|
|
1035
|
+
> modeled on Laravel's [Query Builder: Grouping](https://laravel.com/docs/12.x/queries#groupby-having),
|
|
1036
1036
|
> with the aggregates named in a typed object instead of raw SQL.
|
|
1037
1037
|
|
|
1038
1038
|
### Query plans
|
|
@@ -1092,7 +1092,7 @@ commits behind your back the first time you await anything outside it, so offeri
|
|
|
1092
1092
|
offering a trap. The same rule as migrations applies here: the callback may only await operations
|
|
1093
1093
|
from this package.
|
|
1094
1094
|
|
|
1095
|
-
>
|
|
1095
|
+
> modeled on Laravel's [Database: Transactions](https://laravel.com/docs/12.x/database#database-transactions).
|
|
1096
1096
|
> The tables have to be declared up front, because an IndexedDB transaction fixes its scope when it
|
|
1097
1097
|
> opens.
|
|
1098
1098
|
|
|
@@ -1148,7 +1148,7 @@ DB.disableQueryLog();
|
|
|
1148
1148
|
|
|
1149
1149
|
`DB.logging()` then returns `false`, and `DB.getQueryLog()` an empty array.
|
|
1150
1150
|
|
|
1151
|
-
>
|
|
1151
|
+
> modeled on Laravel's [Database: Listening for Query Events](https://laravel.com/docs/12.x/database#listening-for-query-events),
|
|
1152
1152
|
> with the same enable, get and flush surface, dispatched as a browser event.
|
|
1153
1153
|
|
|
1154
1154
|
### Multiple tabs
|
|
@@ -1178,7 +1178,7 @@ DB.purge('app');
|
|
|
1178
1178
|
| `disconnect(name)` | Close the handle, leaving the connection registered so the next query reopens it |
|
|
1179
1179
|
| `purge(name)` | Close it and drop it, so the next resolve rebuilds it from configuration |
|
|
1180
1180
|
|
|
1181
|
-
>
|
|
1181
|
+
> modeled on Laravel's [Database: Multiple Connections](https://laravel.com/docs/12.x/database#using-multiple-database-connections),
|
|
1182
1182
|
> resolved by name and cached, with one IndexedDB database behind each.
|
|
1183
1183
|
|
|
1184
1184
|
### Storage quota
|
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 = () => {
|
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 = () => {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bjnstnkvc/db",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "TypeScript database layer for IndexedDB with an API
|
|
3
|
+
"version": "1.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",
|