schema-idb 0.0.3 → 0.0.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/README.md +711 -25
- package/dist/createSchemaDB.d.ts +4 -2
- package/dist/createSchemaDB.js +1 -1
- package/dist/field.d.ts +1 -0
- package/dist/query.d.ts +38 -11
- package/dist/schemaDetection.d.ts +8 -1
- package/dist/schemaDetection.js +3 -3
- package/dist/storeAccessor.js +1 -1
- package/dist/types.d.ts +2 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
A type-safe IndexedDB layer that brings structure to client-side storage.
|
|
4
4
|
|
|
5
|
+
[Live Example](https://stackblitz.com/edit/schema-idb)
|
|
6
|
+
|
|
5
7
|
```ts
|
|
6
8
|
const db = openDB({
|
|
7
9
|
name: "MyApp",
|
|
8
|
-
|
|
10
|
+
versionStrategy: "auto",
|
|
9
11
|
stores: [usersStore] as const,
|
|
10
12
|
});
|
|
11
13
|
|
|
@@ -58,7 +60,7 @@ import { openDB } from "schema-idb";
|
|
|
58
60
|
|
|
59
61
|
const db = openDB({
|
|
60
62
|
name: "MyApp",
|
|
61
|
-
|
|
63
|
+
versionStrategy: "auto",
|
|
62
64
|
stores: [usersStore] as const,
|
|
63
65
|
});
|
|
64
66
|
```
|
|
@@ -221,6 +223,10 @@ const user = await db.users
|
|
|
221
223
|
schema-idb exposes transactions as synchronous write batches across multiple stores.
|
|
222
224
|
|
|
223
225
|
```ts
|
|
226
|
+
// Single store
|
|
227
|
+
const tx = db.startTransaction("accounts");
|
|
228
|
+
|
|
229
|
+
// Multiple stores
|
|
224
230
|
const tx = db.startTransaction(["accounts", "logs"]);
|
|
225
231
|
|
|
226
232
|
// Queue operations (no await between them)
|
|
@@ -233,9 +239,12 @@ await tx.commit();
|
|
|
233
239
|
|
|
234
240
|
// Or abort
|
|
235
241
|
tx.abort();
|
|
242
|
+
|
|
243
|
+
// Access underlying IDBTransaction if needed
|
|
244
|
+
tx.raw;
|
|
236
245
|
```
|
|
237
246
|
|
|
238
|
-
Read operations are not available inside transactions. IndexedDB transactions auto-commit after any `await`, so
|
|
247
|
+
Read operations are not available inside transactions. IndexedDB transactions auto-commit after any `await`, so schema-idb only supports synchronous write batching.
|
|
239
248
|
|
|
240
249
|
---
|
|
241
250
|
|
|
@@ -310,11 +319,38 @@ const db = openDB({
|
|
|
310
319
|
- Index modifications
|
|
311
320
|
- Index deletions
|
|
312
321
|
|
|
313
|
-
### Requires manual migration (throws error)
|
|
322
|
+
### Requires manual migration (throws error by default)
|
|
314
323
|
|
|
315
324
|
- Store deletions (data loss)
|
|
316
325
|
- keyPath changes (requires store recreation)
|
|
317
326
|
|
|
327
|
+
### Handling removed stores
|
|
328
|
+
|
|
329
|
+
When a store is removed from the schema, you can choose how to handle it:
|
|
330
|
+
|
|
331
|
+
```ts
|
|
332
|
+
const db = openDB({
|
|
333
|
+
name: "MyApp",
|
|
334
|
+
versionStrategy: "auto",
|
|
335
|
+
// 'error' (default): Throws an error when stores are removed
|
|
336
|
+
// 'preserve': Renames removed stores to __storeName_deleted__ as backup.
|
|
337
|
+
// Preserved stores are isolated from the typed API to avoid future name collisions.
|
|
338
|
+
removedStoreStrategy: "preserve",
|
|
339
|
+
stores: [usersStore] as const,
|
|
340
|
+
});
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
To explicitly delete a store (including backups), use a migration:
|
|
344
|
+
|
|
345
|
+
```ts
|
|
346
|
+
const usersStore = defineStore("users", {
|
|
347
|
+
// ...
|
|
348
|
+
}).addMigration("003-delete-old-store", (db) => {
|
|
349
|
+
db.deleteObjectStore("oldStore");
|
|
350
|
+
db.deleteObjectStore("__oldStore_deleted__"); // Remove backup too
|
|
351
|
+
});
|
|
352
|
+
```
|
|
353
|
+
|
|
318
354
|
---
|
|
319
355
|
|
|
320
356
|
## Type Inference
|
|
@@ -331,39 +367,689 @@ type User = InferStore<typeof usersStore>;
|
|
|
331
367
|
|
|
332
368
|
## API Reference
|
|
333
369
|
|
|
334
|
-
|
|
370
|
+
> This section is intended as a complete, authoritative reference.
|
|
371
|
+
> Most users will not need to read it top-to-bottom.
|
|
372
|
+
> For a guided introduction and examples, see the sections above.
|
|
373
|
+
|
|
374
|
+
### openDB
|
|
375
|
+
|
|
376
|
+
Opens a database connection with the given configuration.
|
|
377
|
+
|
|
378
|
+
```ts
|
|
379
|
+
function openDB<T extends readonly SchemaStoreDefinition[]>(options: {
|
|
380
|
+
name: string;
|
|
381
|
+
stores: T;
|
|
382
|
+
versionStrategy?: "auto" | "explicit";
|
|
383
|
+
version?: number;
|
|
384
|
+
removedStoreStrategy?: "error" | "preserve";
|
|
385
|
+
}): SchemaDatabase<T>;
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
| Option | Type | Description |
|
|
389
|
+
| ------ | ---- | ----------- |
|
|
390
|
+
| `name` | `string` | Database name |
|
|
391
|
+
| `stores` | `readonly SchemaStoreDefinition[]` | Store definitions created with `defineStore` |
|
|
392
|
+
| `versionStrategy` | `"auto" \| "explicit"` | `"auto"` detects schema changes automatically. Default: `"explicit"` (recommended for production control) |
|
|
393
|
+
| `version` | `number` | Required when `versionStrategy` is `"explicit"` |
|
|
394
|
+
| `removedStoreStrategy` | `"error" \| "preserve"` | How to handle removed stores. Default: `"error"` |
|
|
395
|
+
|
|
396
|
+
### SchemaDatabase
|
|
397
|
+
|
|
398
|
+
The database object returned by `openDB`.
|
|
399
|
+
|
|
400
|
+
| Property | Type | Description |
|
|
401
|
+
| -------- | ---- | ----------- |
|
|
402
|
+
| `name` | `string` | Database name |
|
|
403
|
+
| `version` | `number` | Current schema version |
|
|
404
|
+
| `ready` | `boolean` | Whether the database is ready |
|
|
405
|
+
| `raw` | `IDBDatabase` | Underlying IndexedDB instance |
|
|
406
|
+
| `[storeName]` | `StoreAccessor` | Direct access to stores (e.g., `db.users`) |
|
|
407
|
+
|
|
408
|
+
| Method | Signature | Description |
|
|
409
|
+
| ------ | --------- | ----------- |
|
|
410
|
+
| `waitForReady` | `() => Promise<void>` | Wait for database initialization |
|
|
411
|
+
| `close` | `() => void` | Close the database connection |
|
|
412
|
+
| `startTransaction` | `(stores, options?) => Transaction` | Start a multi-store transaction |
|
|
413
|
+
|
|
414
|
+
### Store Accessor
|
|
415
|
+
|
|
416
|
+
Each store is accessible as a property on the database object (e.g., `db.users`).
|
|
417
|
+
|
|
418
|
+
#### get
|
|
419
|
+
|
|
420
|
+
```ts
|
|
421
|
+
get(key: K): Promise<T | undefined>
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
| Param | Type | Description |
|
|
425
|
+
| ----- | ---- | ----------- |
|
|
426
|
+
| `key` | `K` | Primary key value |
|
|
427
|
+
|
|
428
|
+
Returns the record matching the key, or `undefined` if not found.
|
|
429
|
+
|
|
430
|
+
#### getAll
|
|
431
|
+
|
|
432
|
+
```ts
|
|
433
|
+
getAll(): Promise<T[]>
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
Returns all records in the store.
|
|
437
|
+
|
|
438
|
+
#### getBy
|
|
439
|
+
|
|
440
|
+
```ts
|
|
441
|
+
getBy<I extends IndexedFields>(indexName: I, query: V | IDBKeyRange): Promise<T | undefined>
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
| Param | Type | Description |
|
|
445
|
+
| ----- | ---- | ----------- |
|
|
446
|
+
| `indexName` | `I` | Name of the index to query |
|
|
447
|
+
| `query` | `V \| IDBKeyRange` | Value to match or key range |
|
|
448
|
+
|
|
449
|
+
Returns the first record matching the index value.
|
|
450
|
+
|
|
451
|
+
#### getAllBy
|
|
452
|
+
|
|
453
|
+
```ts
|
|
454
|
+
getAllBy<I extends IndexedFields>(indexName: I, query?: V | IDBKeyRange): Promise<T[]>
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
| Param | Type | Description |
|
|
458
|
+
| ----- | ---- | ----------- |
|
|
459
|
+
| `indexName` | `I` | Name of the index to query |
|
|
460
|
+
| `query` | `V \| IDBKeyRange` | Value to match or key range (optional) |
|
|
461
|
+
|
|
462
|
+
Returns all records matching the index value. If `query` is omitted, returns all records ordered by the index.
|
|
463
|
+
|
|
464
|
+
#### put
|
|
465
|
+
|
|
466
|
+
```ts
|
|
467
|
+
put(value: T, key?: K): Promise<K>
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
| Param | Type | Description |
|
|
471
|
+
| ----- | ---- | ----------- |
|
|
472
|
+
| `value` | `T` | Record to insert or update |
|
|
473
|
+
| `key` | `K` | Optional key (only needed if store has no keyPath) |
|
|
474
|
+
|
|
475
|
+
Inserts a new record or updates an existing one. Returns the primary key.
|
|
476
|
+
|
|
477
|
+
#### add
|
|
478
|
+
|
|
479
|
+
```ts
|
|
480
|
+
add(value: T, key?: K): Promise<K>
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
| Param | Type | Description |
|
|
484
|
+
| ----- | ---- | ----------- |
|
|
485
|
+
| `value` | `T` | Record to insert |
|
|
486
|
+
| `key` | `K` | Optional key (only needed if store has no keyPath) |
|
|
487
|
+
|
|
488
|
+
Inserts a new record. Throws an error if the key already exists.
|
|
489
|
+
|
|
490
|
+
#### delete
|
|
491
|
+
|
|
492
|
+
```ts
|
|
493
|
+
delete(key: K | IDBKeyRange): Promise<void>
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
| Param | Type | Description |
|
|
497
|
+
| ----- | ---- | ----------- |
|
|
498
|
+
| `key` | `K \| IDBKeyRange` | Primary key or key range to delete |
|
|
499
|
+
|
|
500
|
+
Deletes record(s) matching the key or range.
|
|
501
|
+
|
|
502
|
+
#### clear
|
|
503
|
+
|
|
504
|
+
```ts
|
|
505
|
+
clear(): Promise<void>
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
Deletes all records in the store.
|
|
509
|
+
|
|
510
|
+
#### count
|
|
511
|
+
|
|
512
|
+
```ts
|
|
513
|
+
count(query?: IDBKeyRange | IDBValidKey): Promise<number>
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
| Param | Type | Description |
|
|
517
|
+
| ----- | ---- | ----------- |
|
|
518
|
+
| `query` | `IDBKeyRange \| IDBValidKey` | Optional key or range to count |
|
|
519
|
+
|
|
520
|
+
Returns the number of records. If `query` is provided, counts only matching records.
|
|
521
|
+
|
|
522
|
+
#### query
|
|
523
|
+
|
|
524
|
+
```ts
|
|
525
|
+
query(options: QueryOptions): Promise<T[]>
|
|
526
|
+
query(): QueryBuilder
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
| Param | Type | Description |
|
|
530
|
+
| ----- | ---- | ----------- |
|
|
531
|
+
| `options` | `QueryOptions` | Query configuration (optional) |
|
|
532
|
+
|
|
533
|
+
When called with options, executes the query and returns results. When called without arguments, returns a `QueryBuilder` for chaining.
|
|
534
|
+
|
|
535
|
+
### Query Options
|
|
536
|
+
|
|
537
|
+
Used with `db.store.query(options)`.
|
|
538
|
+
|
|
539
|
+
```ts
|
|
540
|
+
interface QueryOptions {
|
|
541
|
+
index: string;
|
|
542
|
+
where?: WhereCondition;
|
|
543
|
+
orderBy?: "asc" | "desc";
|
|
544
|
+
limit?: number;
|
|
545
|
+
offset?: number;
|
|
546
|
+
}
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
| Option | Type | Description |
|
|
550
|
+
| ------ | ---- | ----------- |
|
|
551
|
+
| `index` | `string` | Index name to query on |
|
|
552
|
+
| `where` | `WhereCondition` | Filter conditions (optional) |
|
|
553
|
+
| `orderBy` | `"asc" \| "desc"` | Sort order. Default: `"asc"` |
|
|
554
|
+
| `limit` | `number` | Maximum number of results (optional) |
|
|
555
|
+
| `offset` | `number` | Number of results to skip. Default: `0` |
|
|
556
|
+
|
|
557
|
+
#### WhereCondition
|
|
558
|
+
|
|
559
|
+
```ts
|
|
560
|
+
interface WhereCondition {
|
|
561
|
+
eq?: T;
|
|
562
|
+
gt?: T;
|
|
563
|
+
gte?: T;
|
|
564
|
+
lt?: T;
|
|
565
|
+
lte?: T;
|
|
566
|
+
between?: [T, T];
|
|
567
|
+
startsWith?: string;
|
|
568
|
+
}
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
| Option | Type | Description |
|
|
572
|
+
| ------ | ---- | ----------- |
|
|
573
|
+
| `eq` | `T` | Exact match |
|
|
574
|
+
| `gt` | `T` | Greater than |
|
|
575
|
+
| `gte` | `T` | Greater than or equal |
|
|
576
|
+
| `lt` | `T` | Less than |
|
|
577
|
+
| `lte` | `T` | Less than or equal |
|
|
578
|
+
| `between` | `[T, T]` | Inclusive range `[lower, upper]` |
|
|
579
|
+
| `startsWith` | `string` | Prefix match (string indexes only) |
|
|
580
|
+
|
|
581
|
+
### Query Builder
|
|
582
|
+
|
|
583
|
+
Returned when calling `db.store.query()` without arguments.
|
|
584
|
+
|
|
585
|
+
#### index
|
|
586
|
+
|
|
587
|
+
```ts
|
|
588
|
+
index<I extends IndexedFields>(name: I): IndexQueryBuilder
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
| Param | Type | Description |
|
|
592
|
+
| ----- | ---- | ----------- |
|
|
593
|
+
| `name` | `I` | Index name to query on |
|
|
594
|
+
|
|
595
|
+
Returns an `IndexQueryBuilder` for the specified index.
|
|
596
|
+
|
|
597
|
+
#### key
|
|
598
|
+
|
|
599
|
+
```ts
|
|
600
|
+
key(): IndexQueryBuilder
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
Returns an `IndexQueryBuilder` that queries by primary key.
|
|
604
|
+
|
|
605
|
+
#### findAll
|
|
606
|
+
|
|
607
|
+
```ts
|
|
608
|
+
findAll(): Promise<T[]>
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
Executes the query and returns all matching records.
|
|
612
|
+
|
|
613
|
+
### IndexQueryBuilder
|
|
614
|
+
|
|
615
|
+
Provides condition methods for filtering. All methods return a `FinalQueryBuilder`.
|
|
616
|
+
|
|
617
|
+
#### equals
|
|
618
|
+
|
|
619
|
+
```ts
|
|
620
|
+
equals(value: V): FinalQueryBuilder
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
| Param | Type | Description |
|
|
624
|
+
| ----- | ---- | ----------- |
|
|
625
|
+
| `value` | `V` | Value to match exactly |
|
|
626
|
+
|
|
627
|
+
#### gt / gte / lt / lte
|
|
628
|
+
|
|
629
|
+
```ts
|
|
630
|
+
gt(value: V): FinalQueryBuilder // Greater than
|
|
631
|
+
gte(value: V): FinalQueryBuilder // Greater than or equal
|
|
632
|
+
lt(value: V): FinalQueryBuilder // Less than
|
|
633
|
+
lte(value: V): FinalQueryBuilder // Less than or equal
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
| Param | Type | Description |
|
|
637
|
+
| ----- | ---- | ----------- |
|
|
638
|
+
| `value` | `V` | Boundary value for comparison |
|
|
639
|
+
|
|
640
|
+
#### between
|
|
641
|
+
|
|
642
|
+
```ts
|
|
643
|
+
between(lower: V, upper: V): FinalQueryBuilder
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
| Param | Type | Description |
|
|
647
|
+
| ----- | ---- | ----------- |
|
|
648
|
+
| `lower` | `V` | Lower bound (inclusive) |
|
|
649
|
+
| `upper` | `V` | Upper bound (inclusive) |
|
|
650
|
+
|
|
651
|
+
#### startsWith
|
|
652
|
+
|
|
653
|
+
```ts
|
|
654
|
+
startsWith(prefix: string): FinalQueryBuilder
|
|
655
|
+
```
|
|
656
|
+
|
|
657
|
+
| Param | Type | Description |
|
|
658
|
+
| ----- | ---- | ----------- |
|
|
659
|
+
| `prefix` | `string` | Prefix to match |
|
|
660
|
+
|
|
661
|
+
Only available for string indexes.
|
|
662
|
+
|
|
663
|
+
### FinalQueryBuilder
|
|
664
|
+
|
|
665
|
+
Provides result modifiers and execution methods.
|
|
666
|
+
|
|
667
|
+
#### orderBy
|
|
668
|
+
|
|
669
|
+
```ts
|
|
670
|
+
orderBy(order: "asc" | "desc"): FinalQueryBuilder
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
| Param | Type | Description |
|
|
674
|
+
| ----- | ---- | ----------- |
|
|
675
|
+
| `order` | `"asc" \| "desc"` | Sort direction |
|
|
676
|
+
|
|
677
|
+
#### limit
|
|
678
|
+
|
|
679
|
+
```ts
|
|
680
|
+
limit(count: number): FinalQueryBuilder
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
| Param | Type | Description |
|
|
684
|
+
| ----- | ---- | ----------- |
|
|
685
|
+
| `count` | `number` | Maximum number of results |
|
|
686
|
+
|
|
687
|
+
#### offset
|
|
688
|
+
|
|
689
|
+
```ts
|
|
690
|
+
offset(count: number): FinalQueryBuilder
|
|
691
|
+
```
|
|
692
|
+
|
|
693
|
+
| Param | Type | Description |
|
|
694
|
+
| ----- | ---- | ----------- |
|
|
695
|
+
| `count` | `number` | Number of results to skip |
|
|
696
|
+
|
|
697
|
+
#### findAll
|
|
698
|
+
|
|
699
|
+
```ts
|
|
700
|
+
findAll(): Promise<T[]>
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
Executes the query and returns all matching records.
|
|
704
|
+
|
|
705
|
+
#### find
|
|
706
|
+
|
|
707
|
+
```ts
|
|
708
|
+
find(): Promise<T | undefined>
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
Executes the query and returns the first matching record, or `undefined` if none found.
|
|
712
|
+
|
|
713
|
+
#### count
|
|
714
|
+
|
|
715
|
+
```ts
|
|
716
|
+
count(): Promise<number>
|
|
717
|
+
```
|
|
718
|
+
|
|
719
|
+
Returns the number of matching records without fetching them.
|
|
720
|
+
|
|
721
|
+
### Transaction
|
|
722
|
+
|
|
723
|
+
#### startTransaction
|
|
724
|
+
|
|
725
|
+
```ts
|
|
726
|
+
startTransaction(
|
|
727
|
+
stores: string | string[],
|
|
728
|
+
options?: TransactionOptions
|
|
729
|
+
): Transaction
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
| Param | Type | Description |
|
|
733
|
+
| ----- | ---- | ----------- |
|
|
734
|
+
| `stores` | `string \| string[]` | Store name(s) to include in the transaction |
|
|
735
|
+
| `options` | `TransactionOptions` | Transaction configuration (optional) |
|
|
736
|
+
|
|
737
|
+
##### TransactionOptions
|
|
738
|
+
|
|
739
|
+
| Option | Type | Description |
|
|
740
|
+
| ------ | ---- | ----------- |
|
|
741
|
+
| `mode` | `"write"` | Transaction mode. Currently only `"write"` is supported |
|
|
742
|
+
| `durability` | `"default" \| "strict" \| "relaxed"` | Durability hint for the transaction. Default: `"default"` |
|
|
743
|
+
|
|
744
|
+
#### Transaction Object
|
|
745
|
+
|
|
746
|
+
| Property | Type | Description |
|
|
747
|
+
| -------- | ---- | ----------- |
|
|
748
|
+
| `raw` | `IDBTransaction` | Underlying IndexedDB transaction |
|
|
749
|
+
| `[storeName]` | `TransactionStoreAccessor` | Synchronous store accessor for each included store |
|
|
750
|
+
|
|
751
|
+
##### commit
|
|
752
|
+
|
|
753
|
+
```ts
|
|
754
|
+
commit(): Promise<void>
|
|
755
|
+
```
|
|
756
|
+
|
|
757
|
+
Commits all queued operations and waits for completion.
|
|
758
|
+
|
|
759
|
+
##### abort
|
|
760
|
+
|
|
761
|
+
```ts
|
|
762
|
+
abort(): void
|
|
763
|
+
```
|
|
764
|
+
|
|
765
|
+
Aborts the transaction, discarding all queued operations.
|
|
766
|
+
|
|
767
|
+
#### TransactionStoreAccessor
|
|
768
|
+
|
|
769
|
+
Synchronous operations for use within transactions. Operations are queued and executed when `commit()` is called.
|
|
770
|
+
|
|
771
|
+
##### put
|
|
772
|
+
|
|
773
|
+
```ts
|
|
774
|
+
put(value: T, key?: K): void
|
|
775
|
+
```
|
|
776
|
+
|
|
777
|
+
| Param | Type | Description |
|
|
778
|
+
| ----- | ---- | ----------- |
|
|
779
|
+
| `value` | `T` | Record to insert or update |
|
|
780
|
+
| `key` | `K` | Optional key (only needed if store has no keyPath) |
|
|
781
|
+
|
|
782
|
+
##### add
|
|
783
|
+
|
|
784
|
+
```ts
|
|
785
|
+
add(value: T, key?: K): void
|
|
786
|
+
```
|
|
787
|
+
|
|
788
|
+
| Param | Type | Description |
|
|
789
|
+
| ----- | ---- | ----------- |
|
|
790
|
+
| `value` | `T` | Record to insert |
|
|
791
|
+
| `key` | `K` | Optional key (only needed if store has no keyPath) |
|
|
792
|
+
|
|
793
|
+
##### delete
|
|
794
|
+
|
|
795
|
+
```ts
|
|
796
|
+
delete(key: K | IDBKeyRange): void
|
|
797
|
+
```
|
|
798
|
+
|
|
799
|
+
| Param | Type | Description |
|
|
800
|
+
| ----- | ---- | ----------- |
|
|
801
|
+
| `key` | `K \| IDBKeyRange` | Primary key or key range to delete |
|
|
802
|
+
|
|
803
|
+
##### clear
|
|
804
|
+
|
|
805
|
+
```ts
|
|
806
|
+
clear(): void
|
|
807
|
+
```
|
|
808
|
+
|
|
809
|
+
Queues deletion of all records in the store.
|
|
810
|
+
|
|
811
|
+
### defineStore
|
|
812
|
+
|
|
813
|
+
Creates a store definition with schema.
|
|
814
|
+
|
|
815
|
+
```ts
|
|
816
|
+
defineStore<N extends string, S extends StoreSchema>(
|
|
817
|
+
name: N,
|
|
818
|
+
schema: S
|
|
819
|
+
): SchemaStoreDefinition<N, S>
|
|
820
|
+
```
|
|
821
|
+
|
|
822
|
+
| Param | Type | Description |
|
|
823
|
+
| ----- | ---- | ----------- |
|
|
824
|
+
| `name` | `N` | Store name (used as `db.name` accessor) |
|
|
825
|
+
| `schema` | `S` | Object defining fields using `field` builders |
|
|
826
|
+
|
|
827
|
+
Returns a `SchemaStoreDefinition` with the following method:
|
|
828
|
+
|
|
829
|
+
#### addMigration
|
|
830
|
+
|
|
831
|
+
```ts
|
|
832
|
+
addMigration(
|
|
833
|
+
name: string,
|
|
834
|
+
fn: (db: IDBDatabase, tx: IDBTransaction) => void
|
|
835
|
+
): this
|
|
836
|
+
```
|
|
837
|
+
|
|
838
|
+
| Param | Type | Description |
|
|
839
|
+
| ----- | ---- | ----------- |
|
|
840
|
+
| `name` | `string` | Unique migration identifier (sorted alphabetically) |
|
|
841
|
+
| `fn` | `MigrationFn` | Migration function with access to database and transaction |
|
|
842
|
+
|
|
843
|
+
Migrations run during version upgrades in alphabetical order by name.
|
|
844
|
+
|
|
845
|
+
### field
|
|
846
|
+
|
|
847
|
+
Field type builders for schema definition.
|
|
848
|
+
|
|
849
|
+
#### field.string
|
|
850
|
+
|
|
851
|
+
```ts
|
|
852
|
+
field.string(): FieldBuilder<string>
|
|
853
|
+
```
|
|
854
|
+
|
|
855
|
+
Creates a string field.
|
|
856
|
+
|
|
857
|
+
#### field.number
|
|
858
|
+
|
|
859
|
+
```ts
|
|
860
|
+
field.number(): FieldBuilder<number>
|
|
861
|
+
```
|
|
862
|
+
|
|
863
|
+
Creates a number field.
|
|
864
|
+
|
|
865
|
+
#### field.boolean
|
|
866
|
+
|
|
867
|
+
```ts
|
|
868
|
+
field.boolean(): FieldBuilder<boolean>
|
|
869
|
+
```
|
|
870
|
+
|
|
871
|
+
Creates a boolean field.
|
|
872
|
+
|
|
873
|
+
#### field.date
|
|
874
|
+
|
|
875
|
+
```ts
|
|
876
|
+
field.date(): FieldBuilder<Date>
|
|
877
|
+
```
|
|
878
|
+
|
|
879
|
+
Creates a Date field.
|
|
880
|
+
|
|
881
|
+
#### field.object
|
|
882
|
+
|
|
883
|
+
```ts
|
|
884
|
+
field.object<S>(schema: (t: TypeFactory) => S): FieldBuilder<InferObjectType<S>>
|
|
885
|
+
```
|
|
886
|
+
|
|
887
|
+
| Param | Type | Description |
|
|
888
|
+
| ----- | ---- | ----------- |
|
|
889
|
+
| `schema` | `(t: TypeFactory) => S` | Function returning an object schema using type builders |
|
|
890
|
+
|
|
891
|
+
Creates a nested object field.
|
|
892
|
+
|
|
893
|
+
```ts
|
|
894
|
+
field.object(t => ({
|
|
895
|
+
street: t.string(),
|
|
896
|
+
city: t.string(),
|
|
897
|
+
zipCode: t.number().optional(),
|
|
898
|
+
}))
|
|
899
|
+
```
|
|
900
|
+
|
|
901
|
+
#### field.tuple
|
|
902
|
+
|
|
903
|
+
```ts
|
|
904
|
+
field.tuple<T>(schema: (t: TypeFactory) => T): FieldBuilder<InferTupleType<T>>
|
|
905
|
+
```
|
|
906
|
+
|
|
907
|
+
| Param | Type | Description |
|
|
908
|
+
| ----- | ---- | ----------- |
|
|
909
|
+
| `schema` | `(t: TypeFactory) => T` | Function returning a tuple schema as array |
|
|
910
|
+
|
|
911
|
+
Creates a fixed-length tuple field.
|
|
335
912
|
|
|
336
913
|
```ts
|
|
337
|
-
|
|
914
|
+
field.tuple(t => [t.number(), t.number()]) // [number, number]
|
|
915
|
+
```
|
|
916
|
+
|
|
917
|
+
#### field.enum
|
|
338
918
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
db.close(); // Close connection
|
|
342
|
-
db.version; // Current version number
|
|
343
|
-
db.raw; // Underlying IDBDatabase
|
|
919
|
+
```ts
|
|
920
|
+
field.enum<T extends readonly string[]>(values: T): FieldBuilder<T[number]>
|
|
344
921
|
```
|
|
345
922
|
|
|
346
|
-
|
|
923
|
+
| Param | Type | Description |
|
|
924
|
+
| ----- | ---- | ----------- |
|
|
925
|
+
| `values` | `readonly string[]` | Array of allowed string values |
|
|
926
|
+
|
|
927
|
+
Creates a string union type field.
|
|
347
928
|
|
|
348
929
|
```ts
|
|
349
|
-
|
|
350
|
-
db.store.getAll(); // Get all records
|
|
351
|
-
db.store.getAllByIndex(index, value); // Get by index value
|
|
352
|
-
db.store.put(value); // Insert or update
|
|
353
|
-
db.store.add(value); // Insert (fails if exists)
|
|
354
|
-
db.store.delete(key); // Delete by key
|
|
355
|
-
db.store.clear(); // Delete all
|
|
356
|
-
db.store.count(); // Count records
|
|
357
|
-
db.store.query(options); // Query with conditions
|
|
930
|
+
field.enum(['active', 'inactive', 'pending'] as const)
|
|
358
931
|
```
|
|
359
932
|
|
|
360
|
-
|
|
933
|
+
#### field.nativeEnum
|
|
361
934
|
|
|
362
935
|
```ts
|
|
363
|
-
|
|
936
|
+
field.nativeEnum<T extends Record<string, string | number>>(enumObj: T): FieldBuilder<T[keyof T]>
|
|
937
|
+
```
|
|
938
|
+
|
|
939
|
+
| Param | Type | Description |
|
|
940
|
+
| ----- | ---- | ----------- |
|
|
941
|
+
| `enumObj` | `T` | TypeScript enum object |
|
|
942
|
+
|
|
943
|
+
Creates a field from a TypeScript enum.
|
|
944
|
+
|
|
945
|
+
```ts
|
|
946
|
+
enum Status { Active = 'active', Inactive = 'inactive' }
|
|
947
|
+
field.nativeEnum(Status)
|
|
948
|
+
```
|
|
949
|
+
|
|
950
|
+
### FieldBuilder
|
|
951
|
+
|
|
952
|
+
Methods available on all field builders. All methods return `this` for chaining.
|
|
953
|
+
|
|
954
|
+
#### primaryKey
|
|
955
|
+
|
|
956
|
+
```ts
|
|
957
|
+
primaryKey(): FieldBuilder
|
|
958
|
+
```
|
|
959
|
+
|
|
960
|
+
Marks the field as the store's primary key. Exactly one field per store must be marked as primary key.
|
|
961
|
+
|
|
962
|
+
#### index
|
|
963
|
+
|
|
964
|
+
```ts
|
|
965
|
+
index(options?: IndexOptions): FieldBuilder
|
|
966
|
+
```
|
|
967
|
+
|
|
968
|
+
| Param | Type | Description |
|
|
969
|
+
| ----- | ---- | ----------- |
|
|
970
|
+
| `options.unique` | `boolean` | If `true`, enforces unique values. Default: `false` |
|
|
971
|
+
| `options.multiEntry` | `boolean` | If `true`, indexes each array element separately. Default: `false` |
|
|
972
|
+
|
|
973
|
+
Creates an index on this field, enabling queries via `query()`, `getBy()`, and `getAllBy()`.
|
|
974
|
+
|
|
975
|
+
#### optional
|
|
976
|
+
|
|
977
|
+
```ts
|
|
978
|
+
optional(): FieldBuilder
|
|
979
|
+
```
|
|
980
|
+
|
|
981
|
+
Marks the field as optional, allowing `undefined` values.
|
|
982
|
+
|
|
983
|
+
#### default
|
|
364
984
|
|
|
365
|
-
|
|
366
|
-
|
|
985
|
+
```ts
|
|
986
|
+
default(value: T | (() => T)): FieldBuilder
|
|
987
|
+
```
|
|
988
|
+
|
|
989
|
+
| Param | Type | Description |
|
|
990
|
+
| ----- | ---- | ----------- |
|
|
991
|
+
| `value` | `T \| (() => T)` | Default value or factory function |
|
|
992
|
+
|
|
993
|
+
Sets a default value applied on read when the field is missing. Factory functions are called for each read.
|
|
994
|
+
|
|
995
|
+
```ts
|
|
996
|
+
field.number().default(0)
|
|
997
|
+
field.date().default(() => new Date())
|
|
998
|
+
```
|
|
999
|
+
|
|
1000
|
+
#### array
|
|
1001
|
+
|
|
1002
|
+
```ts
|
|
1003
|
+
array(): FieldBuilder<T[]>
|
|
1004
|
+
```
|
|
1005
|
+
|
|
1006
|
+
Converts the field type to an array.
|
|
1007
|
+
|
|
1008
|
+
```ts
|
|
1009
|
+
field.string().array() // string[]
|
|
1010
|
+
```
|
|
1011
|
+
|
|
1012
|
+
### Utility Functions
|
|
1013
|
+
|
|
1014
|
+
#### deleteDB
|
|
1015
|
+
|
|
1016
|
+
```ts
|
|
1017
|
+
deleteDB(name: string): Promise<void>
|
|
1018
|
+
```
|
|
1019
|
+
|
|
1020
|
+
| Param | Type | Description |
|
|
1021
|
+
| ----- | ---- | ----------- |
|
|
1022
|
+
| `name` | `string` | Database name to delete |
|
|
1023
|
+
|
|
1024
|
+
Deletes the database and all its data.
|
|
1025
|
+
|
|
1026
|
+
#### isIndexedDBAvailable
|
|
1027
|
+
|
|
1028
|
+
```ts
|
|
1029
|
+
isIndexedDBAvailable(): boolean
|
|
1030
|
+
```
|
|
1031
|
+
|
|
1032
|
+
Returns `true` if IndexedDB is available in the current environment.
|
|
1033
|
+
|
|
1034
|
+
### Type Utilities
|
|
1035
|
+
|
|
1036
|
+
#### InferStore
|
|
1037
|
+
|
|
1038
|
+
```ts
|
|
1039
|
+
type InferStore<T> = /* inferred output type from store definition */
|
|
1040
|
+
```
|
|
1041
|
+
|
|
1042
|
+
Extracts the TypeScript type from a store definition:
|
|
1043
|
+
|
|
1044
|
+
```ts
|
|
1045
|
+
const usersStore = defineStore("users", {
|
|
1046
|
+
id: field.string().primaryKey(),
|
|
1047
|
+
name: field.string(),
|
|
1048
|
+
age: field.number().optional().default(0),
|
|
1049
|
+
});
|
|
1050
|
+
|
|
1051
|
+
type User = InferStore<typeof usersStore>;
|
|
1052
|
+
// { id: string; name: string; age: number }
|
|
367
1053
|
```
|
|
368
1054
|
|
|
369
1055
|
---
|
package/dist/createSchemaDB.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { StoreSchema, InferInput, InferOutput, PrimaryKeyType, IndexedFields } from './field.js';
|
|
1
|
+
import type { StoreSchema, InferInput, InferOutput, PrimaryKeyType, IndexedFields, IndexFieldTypes } from './field.js';
|
|
2
2
|
import type { SchemaStoreDefinition } from './schema.js';
|
|
3
3
|
import type { TypedQueryOptions, TypedQueryBuilder } from './query.js';
|
|
4
4
|
import type { Transaction, TransactionOptions } from './transaction.js';
|
|
@@ -7,7 +7,8 @@ type StoreNames<TStores extends readonly AnySchemaStore[]> = TStores[number]['na
|
|
|
7
7
|
export interface SchemaStoreAccessor<S extends StoreSchema> {
|
|
8
8
|
get(key: PrimaryKeyType<S>): Promise<InferOutput<S> | undefined>;
|
|
9
9
|
getAll(): Promise<InferOutput<S>[]>;
|
|
10
|
-
|
|
10
|
+
getBy<I extends IndexedFields<S> & string>(indexName: I, query: IDBKeyRange | IndexFieldTypes<S>[I]): Promise<InferOutput<S> | undefined>;
|
|
11
|
+
getAllBy<I extends IndexedFields<S> & string>(indexName: I, query?: IDBKeyRange | IndexFieldTypes<S>[I]): Promise<InferOutput<S>[]>;
|
|
11
12
|
put(value: InferInput<S>, key?: PrimaryKeyType<S>): Promise<PrimaryKeyType<S>>;
|
|
12
13
|
add(value: InferInput<S>, key?: PrimaryKeyType<S>): Promise<PrimaryKeyType<S>>;
|
|
13
14
|
delete(key: PrimaryKeyType<S> | IDBKeyRange): Promise<void>;
|
|
@@ -20,6 +21,7 @@ export interface SchemaDBConfig<TStores extends readonly AnySchemaStore[]> {
|
|
|
20
21
|
name: string;
|
|
21
22
|
version?: number;
|
|
22
23
|
versionStrategy?: 'explicit' | 'auto';
|
|
24
|
+
removedStoreStrategy?: 'error' | 'preserve';
|
|
23
25
|
stores: TStores;
|
|
24
26
|
onBlocked?: () => void;
|
|
25
27
|
onVersionChange?: () => void;
|
package/dist/createSchemaDB.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var v=Object.defineProperty;var l=(e,r)=>v(e,"name",{value:r,configurable:!0});import{openDatabase as T}from"./utils.js";import{createStoreAccessor as h}from"./storeAccessor.js";import{createStartTransaction as D}from"./transaction.js";import{determineAutoVersion as E,applySafeChanges as A,openDatabaseForSchemaRead as p}from"./schemaDetection.js";import{ensureSchemaHistoryStore as R,getAppliedMigrations as S,recordMigrationApplied as x,initializeSchemaHistory as z}from"./migrationHistory.js";function H(e){return e}l(H,"getKeyPathString");function M(e,r,o){const n=l(async()=>{if(await e.readyPromise,!e.idb)throw new Error("Database initialization failed");return h(e.idb,r,o)},"getAccessor");return new Proxy({},{get(t,i){return i==="query"?c=>c?n().then(d=>d.query(c)):k(e,r,o):async(...c)=>(await n())[i](...c)}})}l(M,"createLazyStoreAccessor");function k(e,r,o){const n=l(async()=>{if(await e.readyPromise,!e.idb)throw new Error("Database initialization failed");return h(e.idb,r,o).query()},"getQueryBuilder"),t=l(i=>new Proxy({},{get(c,d){return d==="findAll"||d==="find"||d==="count"?()=>i().then(s=>s[d]()):(...s)=>t(l(()=>i().then(f=>f[d](...s)),"newGetBuilder"))}}),"createBuilderProxy");return t(n)}l(k,"createLazyQueryBuilder");function j(e,r){const o={get name(){return e.idb?.name??r},get version(){return e.idb?.version??0},get raw(){if(!e.idb)throw new Error("Database not ready. Call waitForReady() first or check ready property.");return e.idb},get ready(){return e.ready},waitForReady(){return e.readyPromise},close(){e.idb?.close()},startTransaction(...n){if(!e.startTransaction){const[t,i]=n,c=Array.isArray(t)?t:[t];return q(e,c,i)}return e.startTransaction(...n)}};for(const n of e.stores)Object.defineProperty(o,n.name,{get(){return M(e,n.name,n.defaults)},enumerable:!0});return o}l(j,"buildSchemaDatabase");function q(e,r,o){const n={get raw(){throw new Error("Transaction raw is not available before ready state. Use await db.waitForReady() before starting transactions.")},async commit(){if(await e.readyPromise,!e.startTransaction)throw new Error("Database initialization failed");return e.startTransaction(r,o).commit()},abort(){}};for(const t of r)e.stores.find(c=>c.name===t)&&Object.defineProperty(n,t,{get(){throw new Error("Transaction operations before ready state are not yet supported. Use await db.waitForReady() before starting transactions.")},enumerable:!0});return n}l(q,"createLazyTransaction");function B(e){const r=[],o=new Set;for(const n of e)for(const t of n.migrations){if(o.has(t.name))throw new Error(`Duplicate migration name "${t.name}" found across stores`);o.add(t.name),r.push(t)}return r.sort((n,t)=>n.name.localeCompare(t.name))}l(B,"collectMigrations");function P(e,r){const o=new Set(r);return e.filter(n=>!o.has(n.name)).sort((n,t)=>n.name.localeCompare(t.name))}l(P,"filterPendingMigrations");function C(e,r,o,n,t,i,c){if(R(e),o===0){for(const s of n){const a=e.createObjectStore(s.name,{keyPath:s.keyPath});for(const f of s.indexes)a.createIndex(f.name,f.keyPath,{unique:f.unique??!1,multiEntry:f.multiEntry??!1})}z(r)}else c&&c.safe.length>0&&A(e,r,c.safe,n);let d=[...i];for(const s of t)try{const a=s.up(e,r);a instanceof Promise&&a.catch(f=>{console.error(`Migration "${s.name}" failed:`,f),r.abort()}),x(r,s.name,d),d=[...d,s.name].sort()}catch(a){throw console.error(`Migration "${s.name}" failed:`,a),r.abort(),a}}l(C,"handleUpgrade");export function openDB(e){const{name:r,version:o,versionStrategy:n="explicit",removedStoreStrategy:t="error",stores:i,onBlocked:c,onVersionChange:d}=e,s=new Set;for(const m of i){if(s.has(m.name))throw new Error(`Duplicate store name: "${m.name}"`);s.add(m.name)}const a=B(i);let f=l(()=>{},"readyResolve"),y=l(()=>{},"readyReject");const g={idb:null,ready:!1,error:null,readyPromise:new Promise((m,w)=>{f=m,y=w}),readyResolve:f,readyReject:y,stores:i,startTransaction:null},u=j(g,r);return F(g,r,o,n,t,i,a,c,d),u}l(openDB,"openDB");async function F(e,r,o,n,t,i,c,d,s){try{let a=[],f=null,y;if(n==="auto"){const u=await E(r,i,{removedStoreStrategy:t});if(y=u.version,f=u.changes,u.version>1){const w=await p(r);w&&(a=await S(w),w.close())}P(c,a).length>0&&!u.needsUpgrade&&(y=u.version+1)}else{if(o===void 0)throw new Error('Version is required when versionStrategy is "explicit"');y=o;const u=await p(r);u&&(a=await S(u),u.close())}const b=P(c,a),g=await T(r,y,(u,m,w)=>{C(u,m,w,i,b,a,f)},d);s&&(g.onversionchange=s),e.idb=g,e.startTransaction=D(g,i),e.ready=!0,e.readyResolve()}catch(a){e.error=a instanceof Error?a:new Error(String(a)),e.readyReject(e.error)}}l(F,"initializeDatabase");
|
package/dist/field.d.ts
CHANGED
|
@@ -92,6 +92,7 @@ export type IndexedFields<S extends StoreSchema> = {
|
|
|
92
92
|
export type IndexFieldTypes<S extends StoreSchema> = {
|
|
93
93
|
[K in IndexedFields<S>]: S[K] extends FieldBuilder<infer T, boolean, boolean, boolean> ? T : never;
|
|
94
94
|
};
|
|
95
|
+
export type FieldType<S extends StoreSchema, K extends keyof S> = S[K] extends FieldBuilder<infer T, boolean, boolean, boolean> ? T : never;
|
|
95
96
|
export type InferStore<TStore> = TStore extends {
|
|
96
97
|
schema: infer S;
|
|
97
98
|
} ? S extends StoreSchema ? InferOutput<S> : never : never;
|
package/dist/query.d.ts
CHANGED
|
@@ -9,6 +9,22 @@ export interface WhereCondition<T = unknown> {
|
|
|
9
9
|
between?: [T, T];
|
|
10
10
|
startsWith?: string;
|
|
11
11
|
}
|
|
12
|
+
export type TypedWhereCondition<T> = T extends string ? {
|
|
13
|
+
eq?: T;
|
|
14
|
+
gt?: T;
|
|
15
|
+
gte?: T;
|
|
16
|
+
lt?: T;
|
|
17
|
+
lte?: T;
|
|
18
|
+
between?: [T, T];
|
|
19
|
+
startsWith?: string;
|
|
20
|
+
} : {
|
|
21
|
+
eq?: T;
|
|
22
|
+
gt?: T;
|
|
23
|
+
gte?: T;
|
|
24
|
+
lt?: T;
|
|
25
|
+
lte?: T;
|
|
26
|
+
between?: [T, T];
|
|
27
|
+
};
|
|
12
28
|
export interface QueryOptions {
|
|
13
29
|
index?: string;
|
|
14
30
|
where?: WhereCondition;
|
|
@@ -16,13 +32,21 @@ export interface QueryOptions {
|
|
|
16
32
|
limit?: number;
|
|
17
33
|
offset?: number;
|
|
18
34
|
}
|
|
19
|
-
export
|
|
20
|
-
|
|
35
|
+
export type TypedQueryOptions<S extends StoreSchema> = {
|
|
36
|
+
[I in IndexedFields<S>]: {
|
|
37
|
+
index: I;
|
|
38
|
+
where?: TypedWhereCondition<IndexFieldTypes<S>[I]>;
|
|
39
|
+
orderBy?: SortOrder;
|
|
40
|
+
limit?: number;
|
|
41
|
+
offset?: number;
|
|
42
|
+
};
|
|
43
|
+
}[IndexedFields<S>] | {
|
|
44
|
+
index?: undefined;
|
|
21
45
|
where?: WhereCondition<unknown>;
|
|
22
46
|
orderBy?: SortOrder;
|
|
23
47
|
limit?: number;
|
|
24
48
|
offset?: number;
|
|
25
|
-
}
|
|
49
|
+
};
|
|
26
50
|
interface QueryState {
|
|
27
51
|
indexName?: string;
|
|
28
52
|
useKey: boolean;
|
|
@@ -125,20 +149,23 @@ export interface IndexQueryBuilder<T> {
|
|
|
125
149
|
findAll(): Promise<T[]>;
|
|
126
150
|
find(): Promise<T | undefined>;
|
|
127
151
|
}
|
|
128
|
-
|
|
129
|
-
equals(value:
|
|
130
|
-
gt(value:
|
|
131
|
-
gte(value:
|
|
132
|
-
lt(value:
|
|
133
|
-
lte(value:
|
|
134
|
-
between(lower:
|
|
135
|
-
startsWith(prefix: IndexFieldTypes<S>[I] extends string ? string : never): FinalQueryBuilder<T>;
|
|
152
|
+
interface BaseIndexQueryBuilder<T, V> {
|
|
153
|
+
equals(value: V): FinalQueryBuilder<T>;
|
|
154
|
+
gt(value: V): FinalQueryBuilder<T>;
|
|
155
|
+
gte(value: V): FinalQueryBuilder<T>;
|
|
156
|
+
lt(value: V): FinalQueryBuilder<T>;
|
|
157
|
+
lte(value: V): FinalQueryBuilder<T>;
|
|
158
|
+
between(lower: V, upper: V): FinalQueryBuilder<T>;
|
|
136
159
|
orderBy(order: SortOrder): FinalQueryBuilder<T>;
|
|
137
160
|
limit(count: number): FinalQueryBuilder<T>;
|
|
138
161
|
offset(count: number): FinalQueryBuilder<T>;
|
|
139
162
|
findAll(): Promise<T[]>;
|
|
140
163
|
find(): Promise<T | undefined>;
|
|
141
164
|
}
|
|
165
|
+
interface StringIndexQueryBuilder<T> {
|
|
166
|
+
startsWith(prefix: string): FinalQueryBuilder<T>;
|
|
167
|
+
}
|
|
168
|
+
export type TypedIndexQueryBuilder<T, S extends StoreSchema, I extends IndexedFields<S>> = BaseIndexQueryBuilder<T, IndexFieldTypes<S>[I]> & (IndexFieldTypes<S>[I] extends string ? StringIndexQueryBuilder<T> : {});
|
|
142
169
|
export interface FinalQueryBuilder<T> {
|
|
143
170
|
orderBy(order: SortOrder): FinalQueryBuilder<T>;
|
|
144
171
|
limit(count: number): FinalQueryBuilder<T>;
|
|
@@ -19,6 +19,10 @@ export type SchemaChangeType = {
|
|
|
19
19
|
} | {
|
|
20
20
|
type: 'store_delete';
|
|
21
21
|
storeName: string;
|
|
22
|
+
} | {
|
|
23
|
+
type: 'store_rename';
|
|
24
|
+
oldName: string;
|
|
25
|
+
newName: string;
|
|
22
26
|
} | {
|
|
23
27
|
type: 'keypath_change';
|
|
24
28
|
storeName: string;
|
|
@@ -69,11 +73,14 @@ export declare function generateSchemaFingerprint(stores: readonly {
|
|
|
69
73
|
export declare function hashFingerprint(fingerprint: string): number;
|
|
70
74
|
export declare function getCurrentDatabaseVersion(dbName: string): Promise<number>;
|
|
71
75
|
export declare function openDatabaseForSchemaRead(dbName: string): Promise<IDBDatabase | null>;
|
|
76
|
+
export interface AutoVersionOptions {
|
|
77
|
+
removedStoreStrategy?: 'error' | 'preserve';
|
|
78
|
+
}
|
|
72
79
|
export declare function determineAutoVersion(dbName: string, stores: readonly {
|
|
73
80
|
name: string;
|
|
74
81
|
keyPath: string | string[] | undefined;
|
|
75
82
|
indexes: IndexDefinition[];
|
|
76
|
-
}[]): Promise<{
|
|
83
|
+
}[], options?: AutoVersionOptions): Promise<{
|
|
77
84
|
version: number;
|
|
78
85
|
changes: SchemaChanges | null;
|
|
79
86
|
needsUpgrade: boolean;
|
package/dist/schemaDetection.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
"use strict";var
|
|
2
|
-
${
|
|
1
|
+
"use strict";var g=Object.defineProperty;var h=(n,t)=>g(n,"name",{value:t,configurable:!0});function p(n){return n.startsWith("__")}h(p,"isInternalStore");export function readExistingSchema(n){const t=new Map,e=Array.from({length:n.objectStoreNames.length},(a,o)=>n.objectStoreNames.item(o)).filter(a=>!p(a));for(const a of e){const o=n.transaction(a,"readonly"),d=o.objectStore(a),s=new Map,c=Array.from({length:d.indexNames.length},(r,i)=>d.indexNames.item(i));for(const r of c){const i=d.index(r);s.set(r,{keyPath:i.keyPath,unique:i.unique,multiEntry:i.multiEntry})}t.set(a,{name:a,keyPath:d.keyPath,indexes:s}),o.abort()}return t}h(readExistingSchema,"readExistingSchema");export function toDesiredSchema(n){const t=new Map;for(const e of n)t.set(e.name,{name:e.name,keyPath:e.keyPath,indexes:e.indexes});return t}h(toDesiredSchema,"toDesiredSchema");function x(n,t){return n===t||n==null&&t==null?!0:n==null||t==null?!1:Array.isArray(n)&&Array.isArray(t)?n.length!==t.length?!1:n.every((e,a)=>e===t[a]):n===t}h(x,"keyPathEquals");export function detectSchemaChanges(n,t){const e=[],a=[];for(const[o,d]of t){const s=n.get(o);if(!s){e.push({type:"store_add",storeName:o});continue}if(!x(s.keyPath,d.keyPath)){a.push({type:"keypath_change",storeName:o,oldKeyPath:s.keyPath,newKeyPath:d.keyPath});continue}for(const r of d.indexes){const i=s.indexes.get(r.name);if(!i)e.push({type:"index_add",storeName:o,indexName:r.name,index:r});else{const m=!x(i.keyPath,r.keyPath),u=i.unique!==(r.unique??!1),l=i.multiEntry!==(r.multiEntry??!1);(m||u||l)&&(e.push({type:"index_delete",storeName:o,indexName:r.name}),e.push({type:"index_add",storeName:o,indexName:r.name,index:r}))}}const c=new Set(d.indexes.map(r=>r.name));for(const r of s.indexes.keys())c.has(r)||e.push({type:"index_delete",storeName:o,indexName:r})}for(const o of n.keys())t.has(o)||a.push({type:"store_delete",storeName:o});return{safe:e,dangerous:a,hasChanges:e.length>0||a.length>0}}h(detectSchemaChanges,"detectSchemaChanges");export function applySafeChanges(n,t,e,a){const o=e.filter(s=>s.type==="store_rename"),d=new Map;for(const s of o){const c=t.objectStore(s.oldName),r=[],i=Array.from({length:c.indexNames.length},(u,l)=>c.indexNames.item(l));for(const u of i){const l=c.index(u);r.push({name:u,keyPath:l.keyPath,unique:l.unique,multiEntry:l.multiEntry})}d.set(s.oldName,{keyPath:c.keyPath,indexes:r,newName:s.newName});const m=c.getAll();m.onsuccess=()=>{const u=m.result,l=d.get(s.oldName),f=n.createObjectStore(l.newName,{keyPath:l.keyPath});for(const y of l.indexes)f.createIndex(y.name,y.keyPath,{unique:y.unique,multiEntry:y.multiEntry});for(const y of u)f.put(y)},n.deleteObjectStore(s.oldName)}for(const s of e)switch(s.type){case"store_add":{const c=a.find(r=>r.name===s.storeName);if(c){const r=n.createObjectStore(c.name,{keyPath:c.keyPath});for(const i of c.indexes)r.createIndex(i.name,i.keyPath,{unique:i.unique??!1,multiEntry:i.multiEntry??!1})}break}case"index_add":{t.objectStore(s.storeName).createIndex(s.indexName,s.index.keyPath,{unique:s.index.unique??!1,multiEntry:s.index.multiEntry??!1});break}case"index_delete":{t.objectStore(s.storeName).deleteIndex(s.indexName);break}}}h(applySafeChanges,"applySafeChanges");export function generateSchemaFingerprint(n){const t=n.map(e=>({name:e.name,keyPath:e.keyPath,indexes:e.indexes.map(a=>({name:a.name,keyPath:a.keyPath,unique:a.unique??!1,multiEntry:a.multiEntry??!1})).sort((a,o)=>a.name.localeCompare(o.name))})).sort((e,a)=>e.name.localeCompare(a.name));return JSON.stringify(t)}h(generateSchemaFingerprint,"generateSchemaFingerprint");export function hashFingerprint(n){let t=0;for(let e=0;e<n.length;e++){const a=n.charCodeAt(e);t=(t<<5)-t+a,t=t&t}return Math.abs(t)}h(hashFingerprint,"hashFingerprint");export async function getCurrentDatabaseVersion(n){return new Promise(t=>{const e=indexedDB.open(n);e.onsuccess=()=>{const a=e.result,o=a.version;a.close(),t(o)},e.onerror=()=>{t(0)}})}h(getCurrentDatabaseVersion,"getCurrentDatabaseVersion");export async function openDatabaseForSchemaRead(n){return new Promise(t=>{const e=indexedDB.open(n);e.onsuccess=()=>{t(e.result)},e.onerror=()=>{t(null)},e.onupgradeneeded=()=>{e.transaction?.abort()}})}h(openDatabaseForSchemaRead,"openDatabaseForSchemaRead");export async function determineAutoVersion(n,t,e={}){const{removedStoreStrategy:a="error"}=e,o=await openDatabaseForSchemaRead(n);if(!o)return{version:1,changes:null,needsUpgrade:!0};const d=o.version,s=readExistingSchema(o),c=toDesiredSchema(t);o.close();const r=detectSchemaChanges(s,c);if(!r.hasChanges)return{version:d,changes:null,needsUpgrade:!1};const i=[];for(const m of r.dangerous)m.type==="store_delete"&&a==="preserve"?r.safe.push({type:"store_rename",oldName:m.storeName,newName:`__${m.storeName}_deleted__`}):i.push(m);if(r.dangerous=i,r.dangerous.length>0){const m=r.dangerous.map(u=>{switch(u.type){case"store_delete":return`Store "${u.storeName}" would be deleted. Use removedStoreStrategy: 'preserve' to backup, or add a migration to explicitly delete it.`;case"keypath_change":return`Store "${u.storeName}" keyPath changed from "${u.oldKeyPath}" to "${u.newKeyPath}". This requires recreating the store with a manual migration.`;default:return"Unknown dangerous change"}});throw new Error(`Dangerous schema changes detected:
|
|
2
|
+
${m.join(`
|
|
3
3
|
`)}
|
|
4
4
|
|
|
5
|
-
Add explicit migrations to handle these changes safely.`)}return{version:
|
|
5
|
+
Add explicit migrations to handle these changes safely.`)}return{version:d+1,changes:r,needsUpgrade:!0}}h(determineAutoVersion,"determineAutoVersion");
|
package/dist/storeAccessor.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var S=Object.defineProperty;var u=(r,t)=>S(r,"name",{value:t,configurable:!0});import{promisifyTransaction as y}from"./utils.js";import{createQueryFunction as p}from"./query.js";async function a(r,t){return await y(r),t.result}u(a,"getResult");function x(r,t){if(r!==void 0)return{...t,...r}}u(x,"applyDefaults");function j(r,t){return Object.keys(t).length===0?r:r.map(s=>({...t,...s}))}u(j,"applyDefaultsToArray");export function createStoreAccessor(r,t,s={}){const l=Object.keys(s).length>0,g=p(r,t,s);return{async get(c){const n=r.transaction(t,"readonly"),o=n.objectStore(t),e=await a(n,o.get(c));return l?x(e,s):e},async getAll(c){const n=r.transaction(t,"readonly"),o=n.objectStore(t),e=await a(n,o.getAll(c?.query,c?.count));return l?j(e,s):e},async getBy(c,n){const o=r.transaction(t,"readonly"),w=o.objectStore(t).index(c),i=await a(o,w.get(n));return l?x(i,s):i},async getAllBy(c,n){const o=r.transaction(t,"readonly"),w=o.objectStore(t).index(c),i=await a(o,w.getAll(n));return l?j(i,s):i},async put(c,n){const o=r.transaction(t,"readwrite"),e=o.objectStore(t);return a(o,e.put(c,n))},async add(c,n){const o=r.transaction(t,"readwrite"),e=o.objectStore(t);return a(o,e.add(c,n))},async delete(c){const n=r.transaction(t,"readwrite");n.objectStore(t).delete(c),await y(n)},async clear(){const c=r.transaction(t,"readwrite");c.objectStore(t).clear(),await y(c)},async count(c){const n=r.transaction(t,"readonly"),e=n.objectStore(t).count(c);return await y(n),e.result},async raw(c){const n=r.transaction(t,"readwrite"),o=n.objectStore(t),e=c(o);return await y(n),e.result},query:g}}u(createStoreAccessor,"createStoreAccessor");
|
package/dist/types.d.ts
CHANGED
|
@@ -83,7 +83,8 @@ export interface WriteChain<TStores extends readonly StoreDefinition[]> {
|
|
|
83
83
|
export interface StoreAccessor<T, K> {
|
|
84
84
|
get(key: K): Promise<T | undefined>;
|
|
85
85
|
getAll(options?: GetAllOptions): Promise<T[]>;
|
|
86
|
-
|
|
86
|
+
getBy(indexName: string, query: IDBKeyRange | IDBValidKey): Promise<T | undefined>;
|
|
87
|
+
getAllBy(indexName: string, query?: IDBKeyRange | IDBValidKey): Promise<T[]>;
|
|
87
88
|
put(value: T, key?: K): Promise<K>;
|
|
88
89
|
add(value: T, key?: K): Promise<K>;
|
|
89
90
|
delete(key: K | IDBKeyRange): Promise<void>;
|