@effect/platform-browser 4.0.0-beta.42 → 4.0.0-beta.44

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.
Files changed (54) hide show
  1. package/dist/BrowserHttpClient.d.ts +3 -3
  2. package/dist/BrowserHttpClient.d.ts.map +1 -1
  3. package/dist/BrowserHttpClient.js +5 -5
  4. package/dist/BrowserHttpClient.js.map +1 -1
  5. package/dist/BrowserWorkerRunner.js +1 -1
  6. package/dist/BrowserWorkerRunner.js.map +1 -1
  7. package/dist/Clipboard.d.ts +5 -2
  8. package/dist/Clipboard.d.ts.map +1 -1
  9. package/dist/Clipboard.js +2 -2
  10. package/dist/Clipboard.js.map +1 -1
  11. package/dist/Geolocation.d.ts +2 -2
  12. package/dist/Geolocation.d.ts.map +1 -1
  13. package/dist/Geolocation.js +2 -2
  14. package/dist/Geolocation.js.map +1 -1
  15. package/dist/IndexedDb.d.ts +50 -0
  16. package/dist/IndexedDb.d.ts.map +1 -0
  17. package/dist/IndexedDb.js +65 -0
  18. package/dist/IndexedDb.js.map +1 -0
  19. package/dist/IndexedDbDatabase.d.ts +105 -0
  20. package/dist/IndexedDbDatabase.d.ts.map +1 -0
  21. package/dist/IndexedDbDatabase.js +321 -0
  22. package/dist/IndexedDbDatabase.js.map +1 -0
  23. package/dist/IndexedDbQueryBuilder.d.ts +342 -0
  24. package/dist/IndexedDbQueryBuilder.d.ts.map +1 -0
  25. package/dist/IndexedDbQueryBuilder.js +869 -0
  26. package/dist/IndexedDbQueryBuilder.js.map +1 -0
  27. package/dist/IndexedDbTable.d.ts +109 -0
  28. package/dist/IndexedDbTable.d.ts.map +1 -0
  29. package/dist/IndexedDbTable.js +38 -0
  30. package/dist/IndexedDbTable.js.map +1 -0
  31. package/dist/IndexedDbVersion.d.ts +50 -0
  32. package/dist/IndexedDbVersion.d.ts.map +1 -0
  33. package/dist/IndexedDbVersion.js +23 -0
  34. package/dist/IndexedDbVersion.js.map +1 -0
  35. package/dist/Permissions.d.ts +5 -2
  36. package/dist/Permissions.d.ts.map +1 -1
  37. package/dist/Permissions.js +2 -2
  38. package/dist/Permissions.js.map +1 -1
  39. package/dist/index.d.ts +20 -0
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +20 -0
  42. package/dist/index.js.map +1 -1
  43. package/package.json +4 -3
  44. package/src/BrowserHttpClient.ts +6 -6
  45. package/src/BrowserWorkerRunner.ts +1 -1
  46. package/src/Clipboard.ts +2 -2
  47. package/src/Geolocation.ts +2 -2
  48. package/src/IndexedDb.ts +97 -0
  49. package/src/IndexedDbDatabase.ts +631 -0
  50. package/src/IndexedDbQueryBuilder.ts +1890 -0
  51. package/src/IndexedDbTable.ts +203 -0
  52. package/src/IndexedDbVersion.ts +89 -0
  53. package/src/Permissions.ts +2 -2
  54. package/src/index.ts +25 -0
@@ -0,0 +1,1890 @@
1
+ /**
2
+ * @since 4.0.0
3
+ */
4
+ import type { NonEmptyReadonlyArray } from "effect/Array"
5
+ import * as Context from "effect/Context"
6
+ import * as Data from "effect/Data"
7
+ import * as Effect from "effect/Effect"
8
+ import * as Fiber from "effect/Fiber"
9
+ import type { Inspectable } from "effect/Inspectable"
10
+ import { BaseProto } from "effect/Inspectable"
11
+ import type * as MutableRef from "effect/MutableRef"
12
+ import * as Option from "effect/Option"
13
+ import * as Pipeable from "effect/Pipeable"
14
+ import type * as Queue from "effect/Queue"
15
+ import type * as Record from "effect/Record"
16
+ import * as References from "effect/References"
17
+ import * as Schema from "effect/Schema"
18
+ import * as SchemaIssue from "effect/SchemaIssue"
19
+ import * as SchemaParser from "effect/SchemaParser"
20
+ import type * as Scope from "effect/Scope"
21
+ import * as Stream from "effect/Stream"
22
+ import type * as Reactivity from "effect/unstable/reactivity/Reactivity"
23
+ import * as Utils from "effect/Utils"
24
+ import type * as IndexedDb from "./IndexedDb.ts"
25
+ import type * as IndexedDbDatabase from "./IndexedDbDatabase.ts"
26
+ import type * as IndexedDbTable from "./IndexedDbTable.ts"
27
+ import type * as IndexedDbVersion from "./IndexedDbVersion.ts"
28
+
29
+ const ErrorTypeId = "~@effect/platform-browser/IndexedDbQueryBuilder/IndexedDbQueryError"
30
+
31
+ const CommonProto = {
32
+ [Symbol.iterator]() {
33
+ return new Utils.SingleShotGen(this) as any
34
+ },
35
+ ...Pipeable.Prototype,
36
+ ...BaseProto,
37
+ toJSON(this: any) {
38
+ return {
39
+ _id: "IndexedDbQueryBuilder"
40
+ }
41
+ }
42
+ }
43
+
44
+ /**
45
+ * @since 4.0.0
46
+ * @category errors
47
+ */
48
+ export type ErrorReason =
49
+ | "NotFoundError"
50
+ | "UnknownError"
51
+ | "DecodeError"
52
+ | "EncodeError"
53
+ | "TransactionError"
54
+
55
+ /**
56
+ * @since 4.0.0
57
+ * @category errors
58
+ */
59
+ export class IndexedDbQueryError extends Data.TaggedError(
60
+ "IndexedDbQueryError"
61
+ )<{
62
+ reason: ErrorReason
63
+ cause: unknown
64
+ }> {
65
+ /**
66
+ * @since 4.0.0
67
+ */
68
+ readonly [ErrorTypeId]: typeof ErrorTypeId = ErrorTypeId
69
+
70
+ override readonly message = this.reason
71
+ }
72
+
73
+ /**
74
+ * @since 4.0.0
75
+ * @category models
76
+ */
77
+ export interface IndexedDbQueryBuilder<
78
+ Source extends IndexedDbVersion.AnyWithProps
79
+ > extends Pipeable.Pipeable, Inspectable {
80
+ readonly tables: ReadonlyMap<string, IndexedDbVersion.Tables<Source>>
81
+ readonly database: MutableRef.MutableRef<globalThis.IDBDatabase>
82
+ readonly reactivity: Reactivity.Reactivity["Service"]
83
+ readonly IDBKeyRange: typeof globalThis.IDBKeyRange
84
+ readonly IDBTransaction: globalThis.IDBTransaction | undefined
85
+
86
+ readonly use: <A = unknown>(
87
+ f: (database: globalThis.IDBDatabase) => A
88
+ ) => Effect.Effect<A, IndexedDbQueryError>
89
+
90
+ readonly from: <
91
+ const Name extends IndexedDbTable.TableName<
92
+ IndexedDbVersion.Tables<Source>
93
+ >
94
+ >(
95
+ table: Name
96
+ ) => IndexedDbQuery.From<IndexedDbVersion.TableWithName<Source, Name>>
97
+
98
+ readonly clearAll: Effect.Effect<void, IndexedDbQueryError>
99
+
100
+ readonly withTransaction: <
101
+ Tables extends NonEmptyReadonlyArray<
102
+ IndexedDbTable.TableName<IndexedDbVersion.Tables<Source>>
103
+ >,
104
+ Mode extends "readonly" | "readwrite"
105
+ >(options: {
106
+ readonly tables: Tables
107
+ readonly mode: Mode
108
+ readonly durability?: IDBTransactionDurability
109
+ }) => <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, IndexedDbTransaction>>
110
+ }
111
+
112
+ /**
113
+ * @since 4.0.0
114
+ * @category models
115
+ */
116
+ export type KeyPath<TableSchema extends IndexedDbTable.AnySchemaStruct> =
117
+ | IndexedDbValidKeys<TableSchema>
118
+ | NonEmptyReadonlyArray<IndexedDbValidKeys<TableSchema>>
119
+
120
+ /**
121
+ * @since 4.0.0
122
+ * @category models
123
+ */
124
+ export type KeyPathNumber<TableSchema extends IndexedDbTable.AnySchemaStruct> =
125
+ | IndexedDbValidNumberKeys<TableSchema>
126
+ | NonEmptyReadonlyArray<IndexedDbValidNumberKeys<TableSchema>>
127
+
128
+ /**
129
+ * @since 4.0.0
130
+ * @category models
131
+ */
132
+ export declare namespace IndexedDbQuery {
133
+ /**
134
+ * @since 4.0.0
135
+ * @category models
136
+ */
137
+ export type SelectType<
138
+ Table extends IndexedDbTable.AnyWithProps
139
+ > = [IndexedDbTable.KeyPath<Table>] extends [undefined] ? IndexedDbTable.TableSchema<Table>["Type"] & {
140
+ readonly key: (typeof IndexedDb.IDBValidKey)["Type"]
141
+ } :
142
+ IndexedDbTable.TableSchema<Table>["Type"]
143
+
144
+ /**
145
+ * @since 4.0.0
146
+ * @category models
147
+ */
148
+ export type ModifyType<
149
+ Table extends IndexedDbTable.AnyWithProps
150
+ > =
151
+ & (IndexedDbTable.AutoIncrement<Table> extends true ?
152
+ & {
153
+ [
154
+ key in keyof Schema.Struct.MakeIn<
155
+ Omit<
156
+ IndexedDbTable.TableSchema<Table>["fields"],
157
+ IndexedDbTable.KeyPath<Table>
158
+ >
159
+ >
160
+ ]: key extends keyof Schema.Struct.MakeIn<
161
+ IndexedDbTable.TableSchema<Table>["fields"]
162
+ > ? Schema.Struct.MakeIn<
163
+ IndexedDbTable.TableSchema<Table>["fields"]
164
+ >[key]
165
+ : never
166
+ }
167
+ & {
168
+ [key in IndexedDbTable.KeyPath<Table>]?: number | undefined
169
+ }
170
+ : Schema.Struct.MakeIn<IndexedDbTable.TableSchema<Table>["fields"]>)
171
+ & ([IndexedDbTable.KeyPath<Table>] extends [undefined] ? {
172
+ key: IDBValidKey
173
+ }
174
+ : {})
175
+
176
+ /**
177
+ * @since 4.0.0
178
+ * @category models
179
+ */
180
+ export type EqualsType<
181
+ Table extends IndexedDbTable.AnyWithProps,
182
+ Index extends keyof Table["indexes"],
183
+ KeyPath = [Index] extends [never] ? Table["keyPath"] : Table["indexes"][Index],
184
+ Type = Table["tableSchema"]["Encoded"]
185
+ > = KeyPath extends keyof Type ? Type[KeyPath]
186
+ : { [I in keyof KeyPath]: KeyPath[I] extends keyof Type ? Type[KeyPath[I]] | [] : never }
187
+
188
+ /**
189
+ * @since 4.0.0
190
+ * @category models
191
+ */
192
+ export type ExtractIndexType<
193
+ Table extends IndexedDbTable.AnyWithProps,
194
+ Index extends keyof Table["indexes"],
195
+ KeyPath = [Index] extends [never] ? Table["keyPath"] : Table["indexes"][Index],
196
+ Type = Table["tableSchema"]["Encoded"]
197
+ > = KeyPath extends keyof Type ? Type[KeyPath]
198
+ : KeyPath extends readonly [infer K, ...infer Rest] ? K extends keyof Type ? [
199
+ Type[K],
200
+ ...{ [P in keyof Rest]?: Rest[P] extends keyof Type ? Type[Rest[P]] | [] : never }
201
+ ] :
202
+ never :
203
+ never
204
+
205
+ /**
206
+ * @since 4.0.0
207
+ * @category models
208
+ */
209
+ export type ModifyWithKey<Table extends IndexedDbTable.AnyWithProps> = ModifyType<Table>
210
+
211
+ /**
212
+ * @since 4.0.0
213
+ * @category models
214
+ */
215
+ export interface From<Table extends IndexedDbTable.AnyWithProps> {
216
+ readonly table: Table
217
+ readonly database: MutableRef.MutableRef<globalThis.IDBDatabase>
218
+ readonly IDBKeyRange: typeof globalThis.IDBKeyRange
219
+ readonly reactivity: Reactivity.Reactivity["Service"]
220
+
221
+ readonly clear: Effect.Effect<void, IndexedDbQueryError>
222
+
223
+ readonly select: {
224
+ <Index extends IndexedDbDatabase.IndexFromTable<Table>>(
225
+ index: Index
226
+ ): Select<Table, Index>
227
+ (): Select<Table, never>
228
+ }
229
+
230
+ readonly count: {
231
+ <Index extends IndexedDbDatabase.IndexFromTable<Table>>(
232
+ index: Index
233
+ ): Count<Table, Index>
234
+ (): Count<Table, never>
235
+ }
236
+
237
+ readonly delete: {
238
+ <Index extends IndexedDbDatabase.IndexFromTable<Table>>(
239
+ index: Index
240
+ ): DeletePartial<Table, Index>
241
+ (): DeletePartial<Table, never>
242
+ }
243
+
244
+ readonly insert: (value: ModifyWithKey<Table>) => Modify<Table>
245
+ readonly insertAll: (
246
+ values: Array<ModifyWithKey<Table>>
247
+ ) => ModifyAll<Table>
248
+ readonly upsert: (value: ModifyWithKey<Table>) => Modify<Table>
249
+ readonly upsertAll: (
250
+ values: Array<ModifyWithKey<Table>>
251
+ ) => ModifyAll<Table>
252
+ }
253
+
254
+ /**
255
+ * @since 4.0.0
256
+ * @category models
257
+ */
258
+ export interface Clear<
259
+ Table extends IndexedDbTable.AnyWithProps
260
+ > extends Pipeable.Pipeable, Effect.YieldableClass<void, IndexedDbQueryError> {
261
+ readonly from: From<Table>
262
+ }
263
+
264
+ type ComparisonKeys = "equals" | "gte" | "lte" | "gt" | "lt" | "between"
265
+
266
+ /**
267
+ * @since 4.0.0
268
+ * @category models
269
+ */
270
+ export interface Count<
271
+ Table extends IndexedDbTable.AnyWithProps,
272
+ Index extends IndexedDbDatabase.IndexFromTable<Table>
273
+ > extends Pipeable.Pipeable, Effect.YieldableClass<number, IndexedDbQueryError> {
274
+ readonly from: From<Table>
275
+ readonly index?: Index
276
+ readonly only?: ExtractIndexType<Table, Index>
277
+ readonly lowerBound?: ExtractIndexType<Table, Index>
278
+ readonly upperBound?: ExtractIndexType<Table, Index>
279
+ readonly excludeLowerBound?: boolean
280
+ readonly excludeUpperBound?: boolean
281
+
282
+ readonly equals: (
283
+ value: EqualsType<Table, Index>
284
+ ) => Omit<Count<Table, Index>, ComparisonKeys>
285
+
286
+ readonly gte: (
287
+ value: ExtractIndexType<Table, Index>
288
+ ) => Omit<Count<Table, Index>, ComparisonKeys>
289
+
290
+ readonly lte: (
291
+ value: ExtractIndexType<Table, Index>
292
+ ) => Omit<Count<Table, Index>, ComparisonKeys>
293
+
294
+ readonly gt: (
295
+ value: ExtractIndexType<Table, Index>
296
+ ) => Omit<Count<Table, Index>, ComparisonKeys>
297
+
298
+ readonly lt: (
299
+ value: ExtractIndexType<Table, Index>
300
+ ) => Omit<Count<Table, Index>, ComparisonKeys>
301
+
302
+ readonly between: (
303
+ lowerBound: ExtractIndexType<Table, Index>,
304
+ upperBound: ExtractIndexType<Table, Index>,
305
+ options?: { excludeLowerBound?: boolean; excludeUpperBound?: boolean }
306
+ ) => Omit<Count<Table, Index>, ComparisonKeys>
307
+ }
308
+
309
+ /**
310
+ * @since 4.0.0
311
+ * @category models
312
+ */
313
+ export interface DeletePartial<
314
+ Table extends IndexedDbTable.AnyWithProps,
315
+ Index extends IndexedDbDatabase.IndexFromTable<Table>
316
+ > {
317
+ readonly from: From<Table>
318
+ readonly index?: Index
319
+
320
+ readonly equals: (
321
+ value: EqualsType<Table, Index>
322
+ ) => Delete<Table, Index>
323
+
324
+ readonly gte: (
325
+ value: ExtractIndexType<Table, Index>
326
+ ) => Delete<Table, Index>
327
+
328
+ readonly lte: (
329
+ value: ExtractIndexType<Table, Index>
330
+ ) => Delete<Table, Index>
331
+
332
+ readonly gt: (
333
+ value: ExtractIndexType<Table, Index>
334
+ ) => Delete<Table, Index>
335
+
336
+ readonly lt: (
337
+ value: ExtractIndexType<Table, Index>
338
+ ) => Delete<Table, Index>
339
+
340
+ readonly between: (
341
+ lowerBound: ExtractIndexType<Table, Index>,
342
+ upperBound: ExtractIndexType<Table, Index>,
343
+ options?: { excludeLowerBound?: boolean; excludeUpperBound?: boolean }
344
+ ) => Delete<Table, Index>
345
+
346
+ readonly limit: (
347
+ limit: number
348
+ ) => DeleteWithout<Table, Index, "limit">
349
+ }
350
+
351
+ type DeleteWithout<
352
+ Table extends IndexedDbTable.AnyWithProps,
353
+ Index extends IndexedDbDatabase.IndexFromTable<Table>,
354
+ ExcludedKeys extends string
355
+ > = Omit<Delete<Table, Index, ExcludedKeys>, ExcludedKeys>
356
+
357
+ /**
358
+ * @since 4.0.0
359
+ * @category models
360
+ */
361
+ export interface Delete<
362
+ Table extends IndexedDbTable.AnyWithProps,
363
+ Index extends IndexedDbDatabase.IndexFromTable<Table>,
364
+ ExcludedKeys extends string = never
365
+ > extends Pipeable.Pipeable, Effect.YieldableClass<void, IndexedDbQueryError> {
366
+ readonly delete: DeletePartial<Table, Index>
367
+ readonly index?: Index
368
+ readonly limitValue?: number
369
+ readonly only?: ExtractIndexType<Table, Index>
370
+ readonly lowerBound?: ExtractIndexType<Table, Index>
371
+ readonly upperBound?: ExtractIndexType<Table, Index>
372
+ readonly excludeLowerBound?: boolean
373
+ readonly excludeUpperBound?: boolean
374
+ readonly predicate?: (item: IndexedDbTable.Encoded<Table>) => boolean
375
+
376
+ readonly limit: (
377
+ limit: number
378
+ ) => DeleteWithout<Table, Index, ExcludedKeys | "limit">
379
+
380
+ readonly filter: (
381
+ f: (value: IndexedDbTable.Encoded<Table>) => boolean
382
+ ) => DeleteWithout<Table, Index, ExcludedKeys>
383
+
384
+ /**
385
+ * Invalidate any queries using Reactivity service with the provided keys.
386
+ *
387
+ * Defaults to using the table name as a key if no keys are provided.
388
+ */
389
+ readonly invalidate: (
390
+ keys?: ReadonlyArray<unknown> | Record.ReadonlyRecord<string, ReadonlyArray<unknown>> | undefined
391
+ ) => Effect.Effect<void, IndexedDbQueryError, IndexedDbTable.Context<Table>>
392
+ }
393
+
394
+ type SelectWithout<
395
+ Table extends IndexedDbTable.AnyWithProps,
396
+ Index extends IndexedDbDatabase.IndexFromTable<Table>,
397
+ ExcludedKeys extends string
398
+ > = Omit<Select<Table, Index, ExcludedKeys>, ExcludedKeys>
399
+
400
+ /**
401
+ * @since 4.0.0
402
+ * @category models
403
+ */
404
+ export interface Select<
405
+ Table extends IndexedDbTable.AnyWithProps,
406
+ Index extends IndexedDbDatabase.IndexFromTable<Table>,
407
+ ExcludedKeys extends string = never
408
+ > extends
409
+ Pipeable.Pipeable,
410
+ Effect.YieldableClass<
411
+ Array<SelectType<Table>>,
412
+ IndexedDbQueryError,
413
+ IndexedDbTable.Context<Table>
414
+ >
415
+ {
416
+ readonly from: From<Table>
417
+ readonly index?: Index
418
+ readonly limitValue?: number
419
+ readonly offsetValue?: number
420
+ readonly reverseValue?: boolean
421
+ readonly only?: ExtractIndexType<Table, Index>
422
+ readonly lowerBound?: ExtractIndexType<Table, Index>
423
+ readonly upperBound?: ExtractIndexType<Table, Index>
424
+ readonly excludeLowerBound?: boolean
425
+ readonly excludeUpperBound?: boolean
426
+ readonly predicate?: (item: IndexedDbTable.Encoded<Table>) => boolean
427
+
428
+ readonly equals: (
429
+ value: EqualsType<Table, Index>
430
+ ) => SelectWithout<Table, Index, ExcludedKeys | ComparisonKeys>
431
+
432
+ readonly gte: (
433
+ value: ExtractIndexType<Table, Index>
434
+ ) => SelectWithout<Table, Index, ExcludedKeys | ComparisonKeys>
435
+
436
+ readonly lte: (
437
+ value: ExtractIndexType<Table, Index>
438
+ ) => SelectWithout<Table, Index, ExcludedKeys | ComparisonKeys>
439
+
440
+ readonly gt: (
441
+ value: ExtractIndexType<Table, Index>
442
+ ) => SelectWithout<Table, Index, ExcludedKeys | ComparisonKeys>
443
+
444
+ readonly lt: (
445
+ value: ExtractIndexType<Table, Index>
446
+ ) => SelectWithout<Table, Index, ExcludedKeys | ComparisonKeys>
447
+
448
+ readonly between: (
449
+ lowerBound: ExtractIndexType<Table, Index>,
450
+ upperBound: ExtractIndexType<Table, Index>,
451
+ options?: { excludeLowerBound?: boolean; excludeUpperBound?: boolean }
452
+ ) => SelectWithout<Table, Index, ExcludedKeys | ComparisonKeys>
453
+
454
+ readonly limit: (
455
+ limit: number
456
+ ) => SelectWithout<Table, Index, ExcludedKeys | "limit" | "first">
457
+
458
+ readonly offset: (
459
+ offset: number
460
+ ) => SelectWithout<Table, Index, ExcludedKeys | "offset" | "first">
461
+
462
+ readonly reverse: () => SelectWithout<Table, Index, ExcludedKeys | "reverse" | "first">
463
+
464
+ readonly filter: (
465
+ f: (value: IndexedDbTable.Encoded<Table>) => boolean
466
+ ) => SelectWithout<Table, Index, ExcludedKeys | "first">
467
+
468
+ readonly first: () => First<Table, Index>
469
+
470
+ /**
471
+ * Stream the selected data.
472
+ *
473
+ * Defaults to a chunk size of 100.
474
+ */
475
+ readonly stream: (options?: {
476
+ readonly chunkSize?: number | undefined
477
+ }) => Stream.Stream<
478
+ SelectType<Table>,
479
+ IndexedDbQueryError,
480
+ IndexedDbTable.Context<Table>
481
+ >
482
+
483
+ /**
484
+ * Use the Reactivity service to react to changes to the selected data.
485
+ *
486
+ * By default it uses the table name as a key.
487
+ */
488
+ readonly reactive: (
489
+ keys?: ReadonlyArray<unknown> | Record.ReadonlyRecord<string, ReadonlyArray<unknown>> | undefined
490
+ ) => Stream.Stream<
491
+ Array<SelectType<Table>>,
492
+ IndexedDbQueryError,
493
+ IndexedDbTable.Context<Table>
494
+ >
495
+ readonly reactiveQueue: (
496
+ keys?: ReadonlyArray<unknown> | Record.ReadonlyRecord<string, ReadonlyArray<unknown>> | undefined
497
+ ) => Effect.Effect<
498
+ Queue.Dequeue<Array<SelectType<Table>>, IndexedDbQueryError>,
499
+ never,
500
+ Scope.Scope | IndexedDbTable.Context<Table>
501
+ >
502
+ }
503
+
504
+ /**
505
+ * @since 4.0.0
506
+ * @category models
507
+ */
508
+ export interface First<
509
+ Table extends IndexedDbTable.AnyWithProps,
510
+ Index extends IndexedDbDatabase.IndexFromTable<Table>
511
+ > extends
512
+ Pipeable.Pipeable,
513
+ Effect.YieldableClass<
514
+ SelectType<Table>,
515
+ IndexedDbQueryError,
516
+ IndexedDbTable.Context<Table>
517
+ >
518
+ {
519
+ readonly select: Select<Table, Index>
520
+
521
+ /**
522
+ * Use the Reactivity service to react to changes to the selected data.
523
+ *
524
+ * By default it uses the table name as a key.
525
+ */
526
+ readonly reactive: (
527
+ keys?: ReadonlyArray<unknown> | Record.ReadonlyRecord<string, ReadonlyArray<unknown>> | undefined
528
+ ) => Stream.Stream<
529
+ SelectType<Table>,
530
+ IndexedDbQueryError,
531
+ IndexedDbTable.Context<Table>
532
+ >
533
+
534
+ /**
535
+ * Use the Reactivity service to react to changes to the selected data.
536
+ *
537
+ * By default it uses the table name as a key.
538
+ */
539
+ readonly reactiveQueue: (
540
+ keys: ReadonlyArray<unknown> | Record.ReadonlyRecord<string, ReadonlyArray<unknown>>
541
+ ) => Effect.Effect<
542
+ Queue.Dequeue<SelectType<Table>, IndexedDbQueryError>,
543
+ never,
544
+ Scope.Scope | IndexedDbTable.Context<Table>
545
+ >
546
+ }
547
+
548
+ /**
549
+ * @since 4.0.0
550
+ * @category models
551
+ */
552
+ export interface Filter<
553
+ Table extends IndexedDbTable.AnyWithProps,
554
+ Index extends IndexedDbDatabase.IndexFromTable<Table>
555
+ > extends
556
+ Pipeable.Pipeable,
557
+ Effect.YieldableClass<
558
+ Array<SelectType<Table>>,
559
+ IndexedDbQueryError,
560
+ IndexedDbTable.Context<Table>
561
+ >
562
+ {
563
+ readonly select: Select<Table, Index>
564
+ readonly predicate: (item: IndexedDbTable.Encoded<Table>) => boolean
565
+ readonly filter: (
566
+ f: (value: IndexedDbTable.Encoded<Table>) => boolean
567
+ ) => Filter<Table, Index>
568
+ }
569
+
570
+ /**
571
+ * @since 4.0.0
572
+ * @category models
573
+ */
574
+ export interface Modify<
575
+ Table extends IndexedDbTable.AnyWithProps
576
+ > extends
577
+ Pipeable.Pipeable,
578
+ Effect.YieldableClass<
579
+ globalThis.IDBValidKey,
580
+ IndexedDbQueryError,
581
+ IndexedDbTable.Context<Table>
582
+ >
583
+ {
584
+ readonly operation: "add" | "put"
585
+ readonly from: From<Table>
586
+ readonly value: ModifyWithKey<Table>
587
+
588
+ /**
589
+ * Invalidate any queries using Reactivity service with the provided keys.
590
+ *
591
+ * Defaults to using the table name as a key if no keys are provided.
592
+ */
593
+ readonly invalidate: (
594
+ keys?: ReadonlyArray<unknown> | Record.ReadonlyRecord<string, ReadonlyArray<unknown>> | undefined
595
+ ) => Effect.Effect<globalThis.IDBValidKey, IndexedDbQueryError, IndexedDbTable.Context<Table>>
596
+ }
597
+
598
+ /**
599
+ * @since 4.0.0
600
+ * @category models
601
+ */
602
+ export interface ModifyAll<
603
+ Table extends IndexedDbTable.AnyWithProps
604
+ > extends
605
+ Pipeable.Pipeable,
606
+ Effect.YieldableClass<
607
+ Array<globalThis.IDBValidKey>,
608
+ IndexedDbQueryError,
609
+ IndexedDbTable.Context<Table>
610
+ >
611
+ {
612
+ readonly operation: "add" | "put"
613
+ readonly from: From<Table>
614
+ readonly values: Array<ModifyWithKey<Table>>
615
+
616
+ /**
617
+ * Invalidate any queries using Reactivity service with the provided keys.
618
+ *
619
+ * Defaults to using the table name as a key if no keys are provided.
620
+ */
621
+ readonly invalidate: (
622
+ keys?: ReadonlyArray<unknown> | Record.ReadonlyRecord<string, ReadonlyArray<unknown>> | undefined
623
+ ) => Effect.Effect<globalThis.IDBValidKey, IndexedDbQueryError, IndexedDbTable.Context<Table>>
624
+ }
625
+ }
626
+
627
+ /**
628
+ * @since 4.0.0
629
+ * @category models
630
+ */
631
+ export class IndexedDbTransaction extends Context.Service<IndexedDbTransaction, globalThis.IDBTransaction>()(
632
+ "@effect/platform-browser/IndexedDbQueryBuilder/IndexedDbTransaction"
633
+ ) {}
634
+
635
+ // -----------------------------------------------------------------------------
636
+ // internal
637
+ // -----------------------------------------------------------------------------
638
+
639
+ type IndexedDbValidKeys<TableSchema extends IndexedDbTable.AnySchemaStruct> = keyof TableSchema["Encoded"] extends
640
+ infer K ? K extends keyof TableSchema["Encoded"] ? TableSchema["Encoded"][K] extends Readonly<IDBValidKey> ? K
641
+ : never
642
+ : never
643
+ : never
644
+
645
+ type IndexedDbValidNumberKeys<
646
+ TableSchema extends IndexedDbTable.AnySchemaStruct
647
+ > = keyof TableSchema["Encoded"] extends infer K
648
+ ? K extends keyof TableSchema["Encoded"] ? [TableSchema["Encoded"][K]] extends [number | undefined] ? K
649
+ : never
650
+ : never
651
+ : never
652
+
653
+ const applyDelete = (query: IndexedDbQuery.Delete<any, never>) =>
654
+ Effect.callback<any, IndexedDbQueryError>((resume) => {
655
+ const database = query.delete.from.database
656
+ const IDBKeyRange = query.delete.from.IDBKeyRange
657
+ const transaction = getOrCreateTransaction(database.current, [query.delete.from.table.tableName], "readwrite", {
658
+ durability: query.delete.from.table.durability
659
+ })
660
+ const objectStore = transaction.objectStore(query.delete.from.table.tableName)
661
+ const predicate = query.predicate
662
+
663
+ let keyRange: globalThis.IDBKeyRange | undefined = undefined
664
+
665
+ if (query.only !== undefined) {
666
+ keyRange = IDBKeyRange.only(query.only)
667
+ } else if (
668
+ query.lowerBound !== undefined &&
669
+ query.upperBound !== undefined
670
+ ) {
671
+ keyRange = IDBKeyRange.bound(
672
+ query.lowerBound,
673
+ query.upperBound,
674
+ query.excludeLowerBound,
675
+ query.excludeUpperBound
676
+ )
677
+ } else if (query.lowerBound !== undefined) {
678
+ keyRange = IDBKeyRange.lowerBound(
679
+ query.lowerBound,
680
+ query.excludeLowerBound
681
+ )
682
+ } else if (query.upperBound !== undefined) {
683
+ keyRange = IDBKeyRange.upperBound(
684
+ query.upperBound,
685
+ query.excludeUpperBound
686
+ )
687
+ }
688
+
689
+ let request: globalThis.IDBRequest
690
+
691
+ if (query.limitValue !== undefined || predicate) {
692
+ const cursorRequest = objectStore.openCursor()
693
+ let count = 0
694
+
695
+ cursorRequest.onerror = () => {
696
+ resume(
697
+ Effect.fail(
698
+ new IndexedDbQueryError({
699
+ reason: "TransactionError",
700
+ cause: cursorRequest.error
701
+ })
702
+ )
703
+ )
704
+ }
705
+
706
+ cursorRequest.onsuccess = () => {
707
+ const cursor = cursorRequest.result
708
+ if (cursor === null) {
709
+ return resume(Effect.void)
710
+ }
711
+
712
+ if (predicate === undefined || predicate(cursor.value)) {
713
+ const deleteRequest = cursor.delete()
714
+ deleteRequest.onerror = () => {
715
+ resume(
716
+ Effect.fail(
717
+ new IndexedDbQueryError({
718
+ reason: "TransactionError",
719
+ cause: deleteRequest.error
720
+ })
721
+ )
722
+ )
723
+ }
724
+ count += 1
725
+ }
726
+
727
+ if (query.limitValue === undefined || count < query.limitValue) {
728
+ return cursor.continue()
729
+ }
730
+
731
+ resume(Effect.void)
732
+ }
733
+ } else if (keyRange !== undefined) {
734
+ request = objectStore.delete(keyRange)
735
+
736
+ request.onerror = (event) => {
737
+ resume(
738
+ Effect.fail(
739
+ new IndexedDbQueryError({
740
+ reason: "TransactionError",
741
+ cause: event
742
+ })
743
+ )
744
+ )
745
+ }
746
+
747
+ request.onsuccess = () => {
748
+ resume(Effect.succeed(request.result))
749
+ }
750
+ } else {
751
+ resume(
752
+ Effect.die(new Error("No key range provided for delete operation"))
753
+ )
754
+ }
755
+ })
756
+
757
+ const getReadonlyObjectStore = (
758
+ query: IndexedDbQuery.Select<any, never> | IndexedDbQuery.Count<any, never>
759
+ ) => {
760
+ const database = query.from.database
761
+ const IDBKeyRange = query.from.IDBKeyRange
762
+ const transaction = getOrCreateTransaction(database.current, [query.from.table.tableName], "readonly", {
763
+ durability: query.from.table.durability
764
+ })
765
+ const objectStore = transaction.objectStore(query.from.table.tableName)
766
+
767
+ let keyRange: globalThis.IDBKeyRange | undefined = undefined
768
+ let store: globalThis.IDBObjectStore | globalThis.IDBIndex
769
+
770
+ if (query.only !== undefined) {
771
+ keyRange = IDBKeyRange.only(query.only)
772
+ } else if (query.lowerBound !== undefined && query.upperBound !== undefined) {
773
+ keyRange = IDBKeyRange.bound(
774
+ query.lowerBound,
775
+ query.upperBound,
776
+ query.excludeLowerBound,
777
+ query.excludeUpperBound
778
+ )
779
+ } else if (query.lowerBound !== undefined) {
780
+ keyRange = IDBKeyRange.lowerBound(
781
+ query.lowerBound,
782
+ query.excludeLowerBound
783
+ )
784
+ } else if (query.upperBound !== undefined) {
785
+ keyRange = IDBKeyRange.upperBound(
786
+ query.upperBound,
787
+ query.excludeUpperBound
788
+ )
789
+ }
790
+
791
+ if (query.index !== undefined) {
792
+ store = objectStore.index(query.index)
793
+ } else {
794
+ store = objectStore
795
+ }
796
+
797
+ return { store, keyRange }
798
+ }
799
+
800
+ const applySelect = Effect.fnUntraced(function*(
801
+ query: IndexedDbQuery.Select<any, never, any>
802
+ ): Effect.fn.Return<Array<any>, IndexedDbQueryError, unknown> {
803
+ const keyPath = query.from.table.keyPath
804
+ const predicate = query.predicate
805
+
806
+ const data = predicate || keyPath === undefined || query.offsetValue !== undefined ?
807
+ yield* Effect.callback<Array<any>, IndexedDbQueryError>((resume) => {
808
+ const { keyRange, store } = getReadonlyObjectStore(query)
809
+
810
+ const cursorRequest = store.openCursor(keyRange, query.reverseValue ? "prev" : "next")
811
+ const results: Array<any> = []
812
+ let count = 0
813
+ let offsetApplied = false
814
+
815
+ cursorRequest.onerror = () => {
816
+ resume(
817
+ Effect.fail(
818
+ new IndexedDbQueryError({
819
+ reason: "TransactionError",
820
+ cause: cursorRequest.error
821
+ })
822
+ )
823
+ )
824
+ }
825
+
826
+ cursorRequest.onsuccess = () => {
827
+ const cursor = cursorRequest.result
828
+ if (cursor === null) {
829
+ return resume(Effect.succeed(results))
830
+ }
831
+
832
+ if (query.offsetValue && !offsetApplied) {
833
+ offsetApplied = true
834
+ return cursor.advance(query.offsetValue)
835
+ }
836
+
837
+ if (predicate === undefined || predicate(cursor.value)) {
838
+ results.push(
839
+ keyPath === undefined
840
+ ? { ...cursor.value, key: cursor.key }
841
+ : cursor.value
842
+ )
843
+ count += 1
844
+ }
845
+
846
+ if (query.limitValue === undefined || count < query.limitValue) {
847
+ return cursor.continue()
848
+ }
849
+
850
+ resume(Effect.succeed(results))
851
+ }
852
+ }) :
853
+ yield* Effect.callback<Array<any>, IndexedDbQueryError>((resume) => {
854
+ const { keyRange, store } = getReadonlyObjectStore(query)
855
+ const request = store.getAll(keyRange, query.limitValue)
856
+ request.onerror = (event) => {
857
+ resume(
858
+ Effect.fail(
859
+ new IndexedDbQueryError({
860
+ reason: "TransactionError",
861
+ cause: event
862
+ })
863
+ )
864
+ )
865
+ }
866
+ request.onsuccess = () => {
867
+ if (query.reverseValue) {
868
+ request.result.reverse()
869
+ }
870
+ resume(Effect.succeed(request.result))
871
+ }
872
+ })
873
+
874
+ const tableSchema = (query.from.table as IndexedDbTable.AnyWithProps).arraySchema
875
+
876
+ return yield* Schema.decodeUnknownEffect(tableSchema)(data).pipe(
877
+ Effect.mapError(
878
+ (error) =>
879
+ new IndexedDbQueryError({
880
+ reason: "DecodeError",
881
+ cause: error
882
+ })
883
+ )
884
+ ) as Effect.Effect<Array<any>, IndexedDbQueryError, unknown>
885
+ })
886
+
887
+ const getFirst = Effect.fnUntraced(function*(
888
+ query: IndexedDbQuery.First<any, never>
889
+ ) {
890
+ const keyPath = query.select.from.table.keyPath
891
+
892
+ const data = yield* Effect.callback<any, IndexedDbQueryError>((resume) => {
893
+ const { keyRange, store } = getReadonlyObjectStore(query.select)
894
+
895
+ if (keyRange !== undefined) {
896
+ const request = store.get(keyRange)
897
+
898
+ request.onerror = (event) => {
899
+ resume(
900
+ Effect.fail(
901
+ new IndexedDbQueryError({
902
+ reason: "TransactionError",
903
+ cause: event
904
+ })
905
+ )
906
+ )
907
+ }
908
+
909
+ request.onsuccess = () => {
910
+ resume(Effect.succeed(request.result))
911
+ }
912
+ } else {
913
+ const request = store.openCursor()
914
+
915
+ request.onerror = (event) => {
916
+ resume(
917
+ Effect.fail(
918
+ new IndexedDbQueryError({
919
+ reason: "TransactionError",
920
+ cause: event
921
+ })
922
+ )
923
+ )
924
+ }
925
+
926
+ request.onsuccess = () => {
927
+ const value = request.result?.value
928
+ const key = request.result?.key
929
+
930
+ if (value === undefined) {
931
+ resume(
932
+ Effect.fail(
933
+ new IndexedDbQueryError({
934
+ reason: "NotFoundError",
935
+ cause: request.error
936
+ })
937
+ )
938
+ )
939
+ } else {
940
+ resume(
941
+ Effect.succeed(keyPath === undefined ? { ...value, key } : value)
942
+ )
943
+ }
944
+ }
945
+ }
946
+ })
947
+
948
+ return yield* Schema.decodeUnknownEffect(query.select.from.table.readSchema)(
949
+ data
950
+ ).pipe(
951
+ Effect.mapError(
952
+ (error) =>
953
+ new IndexedDbQueryError({
954
+ reason: "DecodeError",
955
+ cause: error
956
+ })
957
+ )
958
+ )
959
+ })
960
+
961
+ const applyModify = Effect.fnUntraced(function*({
962
+ query,
963
+ value
964
+ }: {
965
+ query: IndexedDbQuery.Modify<any>
966
+ value: any
967
+ }) {
968
+ const autoIncrement = query.from.table.autoIncrement as boolean
969
+ const keyPath = query.from.table.keyPath
970
+ const table = query.from.table
971
+ const schema = autoIncrement && value[keyPath] === undefined
972
+ ? table.autoincrementSchema
973
+ : table.tableSchema
974
+
975
+ const encodedValue = yield* SchemaParser.makeEffect(
976
+ autoIncrement && value[keyPath] === undefined
977
+ ? table.autoincrementSchema
978
+ : table.tableSchema
979
+ )(value).pipe(
980
+ Effect.flatMap(Schema.encodeUnknownEffect(schema)),
981
+ Effect.mapError(
982
+ (error) =>
983
+ new IndexedDbQueryError({
984
+ reason: "EncodeError",
985
+ cause: error
986
+ })
987
+ )
988
+ )
989
+
990
+ return yield* Effect.callback<any, IndexedDbQueryError>((resume) => {
991
+ const database = query.from.database
992
+ const transaction = getOrCreateTransaction(database.current, [query.from.table.tableName], "readwrite", {
993
+ durability: query.from.table.durability
994
+ })
995
+ const objectStore = transaction.objectStore(query.from.table.tableName)
996
+
997
+ let request: globalThis.IDBRequest<IDBValidKey>
998
+
999
+ if (query.operation === "add") {
1000
+ request = objectStore.add(
1001
+ encodedValue,
1002
+ keyPath === undefined ? value["key"] : undefined
1003
+ )
1004
+ } else if (query.operation === "put") {
1005
+ request = objectStore.put(
1006
+ encodedValue,
1007
+ keyPath === undefined ? value["key"] : undefined
1008
+ )
1009
+ } else {
1010
+ return resume(Effect.die(new Error("Invalid modify operation")))
1011
+ }
1012
+
1013
+ request.onerror = (event) => {
1014
+ resume(
1015
+ Effect.fail(
1016
+ new IndexedDbQueryError({
1017
+ reason: "TransactionError",
1018
+ cause: event
1019
+ })
1020
+ )
1021
+ )
1022
+ }
1023
+
1024
+ request.onsuccess = () => {
1025
+ resume(Effect.succeed(request.result))
1026
+ }
1027
+ })
1028
+ })
1029
+
1030
+ const applyModifyAll = Effect.fnUntraced(
1031
+ function*({
1032
+ query,
1033
+ values
1034
+ }: {
1035
+ query: IndexedDbQuery.ModifyAll<any>
1036
+ values: Array<any>
1037
+ }) {
1038
+ const autoIncrement = query.from.table.autoIncrement as boolean
1039
+ const keyPath = query.from.table.keyPath
1040
+ const schema = query.from.table.tableSchema
1041
+ const encodedValues = new Array(values.length)
1042
+ const makeValue = SchemaParser.makeEffect(schema)
1043
+ const encodeValue = SchemaParser.encodeUnknownEffect(schema)
1044
+ const makeValueAutoincrement = SchemaParser.makeEffect(query.from.table.autoincrementSchema)
1045
+ const encodeValueAutoincrement = SchemaParser.encodeUnknownEffect(query.from.table.autoincrementSchema)
1046
+
1047
+ for (let i = 0; i < values.length; i++) {
1048
+ const value = values[i]
1049
+ if (autoIncrement && value[keyPath] === undefined) {
1050
+ encodedValues[i] = yield* encodeValueAutoincrement(yield* makeValueAutoincrement(value))
1051
+ } else {
1052
+ encodedValues[i] = yield* encodeValue(yield* makeValue(value))
1053
+ }
1054
+ }
1055
+
1056
+ return yield* Effect.callback<
1057
+ Array<globalThis.IDBValidKey>,
1058
+ IndexedDbQueryError
1059
+ >((resume) => {
1060
+ const database = query.from.database
1061
+ const transaction = getOrCreateTransaction(database.current, [query.from.table.tableName], "readwrite", {
1062
+ durability: query.from.table.durability
1063
+ })
1064
+ const objectStore = transaction.objectStore(query.from.table.tableName)
1065
+
1066
+ const results: Array<globalThis.IDBValidKey> = []
1067
+
1068
+ if (query.operation === "add") {
1069
+ for (let i = 0; i < encodedValues.length; i++) {
1070
+ const request = objectStore.add(
1071
+ encodedValues[i],
1072
+ keyPath === undefined ? values[i]["key"] : undefined
1073
+ )
1074
+
1075
+ request.onerror = () => {
1076
+ resume(
1077
+ Effect.fail(
1078
+ new IndexedDbQueryError({
1079
+ reason: "TransactionError",
1080
+ cause: request.error
1081
+ })
1082
+ )
1083
+ )
1084
+ }
1085
+
1086
+ request.onsuccess = () => {
1087
+ results.push(request.result)
1088
+ }
1089
+ }
1090
+ } else if (query.operation === "put") {
1091
+ for (let i = 0; i < encodedValues.length; i++) {
1092
+ const request = objectStore.put(
1093
+ encodedValues[i],
1094
+ keyPath === undefined ? values[i]["key"] : undefined
1095
+ )
1096
+
1097
+ request.onerror = () => {
1098
+ resume(
1099
+ Effect.fail(
1100
+ new IndexedDbQueryError({
1101
+ reason: "TransactionError",
1102
+ cause: request.error
1103
+ })
1104
+ )
1105
+ )
1106
+ }
1107
+
1108
+ request.onsuccess = () => {
1109
+ results.push(request.result)
1110
+ }
1111
+ }
1112
+ } else {
1113
+ return resume(Effect.die(new Error("Invalid modify all operation")))
1114
+ }
1115
+
1116
+ objectStore.transaction.onerror = () => {
1117
+ resume(
1118
+ Effect.fail(
1119
+ new IndexedDbQueryError({
1120
+ reason: "TransactionError",
1121
+ cause: objectStore.transaction.error
1122
+ })
1123
+ )
1124
+ )
1125
+ }
1126
+
1127
+ objectStore.transaction.oncomplete = () => {
1128
+ resume(Effect.succeed(results))
1129
+ }
1130
+ })
1131
+ },
1132
+ Effect.catchIf(
1133
+ SchemaIssue.isIssue,
1134
+ (issue) => Effect.fail(new IndexedDbQueryError({ reason: "EncodeError", cause: new Schema.SchemaError(issue) }))
1135
+ )
1136
+ )
1137
+
1138
+ const applyClear = (options: {
1139
+ readonly database: globalThis.IDBDatabase
1140
+ readonly table: IndexedDbTable.AnyWithProps
1141
+ }) =>
1142
+ Effect.callback<void, IndexedDbQueryError>((resume) => {
1143
+ const database = options.database
1144
+ const transaction = getOrCreateTransaction(database, [options.table.tableName], "readwrite", {
1145
+ durability: options.table.durability
1146
+ })
1147
+ const objectStore = transaction.objectStore(options.table.tableName)
1148
+
1149
+ const request = objectStore.clear()
1150
+
1151
+ request.onerror = (event) => {
1152
+ resume(
1153
+ Effect.fail(
1154
+ new IndexedDbQueryError({
1155
+ reason: "TransactionError",
1156
+ cause: event
1157
+ })
1158
+ )
1159
+ )
1160
+ }
1161
+
1162
+ request.onsuccess = () => {
1163
+ resume(Effect.void)
1164
+ }
1165
+ })
1166
+
1167
+ const applyClearAll = (options: {
1168
+ readonly database: globalThis.IDBDatabase
1169
+ }) =>
1170
+ Effect.callback<void, IndexedDbQueryError>((resume) => {
1171
+ const database = options.database
1172
+ const tables = database.objectStoreNames
1173
+ const transaction = getOrCreateTransaction(database, [...tables], "readwrite")
1174
+
1175
+ for (let t = 0; t < tables.length; t++) {
1176
+ const objectStore = transaction.objectStore(tables[t])
1177
+ const request = objectStore.clear()
1178
+
1179
+ request.onerror = () => {
1180
+ resume(
1181
+ Effect.fail(
1182
+ new IndexedDbQueryError({
1183
+ reason: "TransactionError",
1184
+ cause: request.error
1185
+ })
1186
+ )
1187
+ )
1188
+ }
1189
+ }
1190
+
1191
+ transaction.onerror = () => {
1192
+ resume(
1193
+ Effect.fail(
1194
+ new IndexedDbQueryError({
1195
+ reason: "TransactionError",
1196
+ cause: transaction.error
1197
+ })
1198
+ )
1199
+ )
1200
+ }
1201
+
1202
+ transaction.oncomplete = () => {
1203
+ resume(Effect.void)
1204
+ }
1205
+ })
1206
+
1207
+ const getCount = (query: IndexedDbQuery.Count<any, never>) =>
1208
+ Effect.callback<number, IndexedDbQueryError>((resume) => {
1209
+ const { keyRange, store } = getReadonlyObjectStore(query)
1210
+
1211
+ const request = store.count(keyRange)
1212
+
1213
+ request.onerror = (event) => {
1214
+ resume(
1215
+ Effect.fail(
1216
+ new IndexedDbQueryError({
1217
+ reason: "TransactionError",
1218
+ cause: event
1219
+ })
1220
+ )
1221
+ )
1222
+ }
1223
+
1224
+ request.onsuccess = () => {
1225
+ resume(Effect.succeed(request.result))
1226
+ }
1227
+ })
1228
+
1229
+ const FromProto: Omit<
1230
+ IndexedDbQuery.From<any>,
1231
+ | "table"
1232
+ | "database"
1233
+ | "IDBKeyRange"
1234
+ | "transaction"
1235
+ | "reactivity"
1236
+ > = {
1237
+ ...CommonProto,
1238
+ select<Index extends IndexedDbDatabase.IndexFromTable<any>>(
1239
+ this: IndexedDbQuery.From<any>,
1240
+ index?: Index
1241
+ ) {
1242
+ return makeSelect({
1243
+ from: this,
1244
+ index
1245
+ }) as any
1246
+ },
1247
+ count<Index extends IndexedDbDatabase.IndexFromTable<any>>(
1248
+ this: IndexedDbQuery.From<any>,
1249
+ index?: Index
1250
+ ) {
1251
+ return makeCount({
1252
+ from: this,
1253
+ index
1254
+ }) as any
1255
+ },
1256
+ delete<Index extends IndexedDbDatabase.IndexFromTable<any>>(
1257
+ this: IndexedDbQuery.From<any>,
1258
+ index?: Index
1259
+ ) {
1260
+ return makeDeletePartial({
1261
+ from: this,
1262
+ index
1263
+ }) as any
1264
+ },
1265
+ insert(this: IndexedDbQuery.From<any>, value: any) {
1266
+ return makeModify({ from: this, value, operation: "add" })
1267
+ },
1268
+ upsert(this: IndexedDbQuery.From<any>, value: any) {
1269
+ return makeModify({ from: this, value, operation: "put" })
1270
+ },
1271
+ insertAll(this: IndexedDbQuery.From<any>, values: Array<any>) {
1272
+ return makeModifyAll({ from: this, values, operation: "add" })
1273
+ },
1274
+ upsertAll(this: IndexedDbQuery.From<any>, values: Array<any>) {
1275
+ return makeModifyAll({ from: this, values, operation: "put" })
1276
+ },
1277
+ get clear() {
1278
+ const self = this as IndexedDbQuery.From<any>
1279
+ return applyClear({
1280
+ database: self.database.current,
1281
+ table: self.table
1282
+ })
1283
+ }
1284
+ }
1285
+
1286
+ const makeFrom = <
1287
+ const Table extends IndexedDbTable.AnyWithProps
1288
+ >(options: {
1289
+ readonly table: Table
1290
+ readonly database: MutableRef.MutableRef<globalThis.IDBDatabase>
1291
+ readonly IDBKeyRange: typeof globalThis.IDBKeyRange
1292
+ readonly reactivity: Reactivity.Reactivity["Service"]
1293
+ }): IndexedDbQuery.From<Table> => {
1294
+ const self = Object.create(FromProto)
1295
+ self.table = options.table
1296
+ self.database = options.database
1297
+ self.IDBKeyRange = options.IDBKeyRange
1298
+ self.reactivity = options.reactivity
1299
+ return self
1300
+ }
1301
+
1302
+ const DeletePartialProto: Omit<
1303
+ IndexedDbQuery.DeletePartial<any, never>,
1304
+ | "from"
1305
+ | "index"
1306
+ > = {
1307
+ ...CommonProto,
1308
+ limit(this: IndexedDbQuery.DeletePartial<any, never>, limit: number) {
1309
+ return makeDelete({
1310
+ delete: this as any,
1311
+ limitValue: limit
1312
+ })
1313
+ },
1314
+ equals(this: IndexedDbQuery.DeletePartial<any, never>, value: IndexedDbQuery.ExtractIndexType<any, never>) {
1315
+ return makeDelete({
1316
+ delete: this as any,
1317
+ only: value
1318
+ })
1319
+ },
1320
+ gte(this: IndexedDbQuery.DeletePartial<any, never>, value: IndexedDbQuery.ExtractIndexType<any, never>) {
1321
+ return makeDelete({
1322
+ delete: this as any,
1323
+ lowerBound: value,
1324
+ excludeLowerBound: false
1325
+ })
1326
+ },
1327
+ lte(this: IndexedDbQuery.DeletePartial<any, never>, value: IndexedDbQuery.ExtractIndexType<any, never>) {
1328
+ return makeDelete({
1329
+ delete: this as any,
1330
+ upperBound: value,
1331
+ excludeUpperBound: false
1332
+ })
1333
+ },
1334
+ gt(this: IndexedDbQuery.DeletePartial<any, never>, value: IndexedDbQuery.ExtractIndexType<any, never>) {
1335
+ return makeDelete({
1336
+ delete: this as any,
1337
+ lowerBound: value,
1338
+ excludeLowerBound: true
1339
+ })
1340
+ },
1341
+ lt(this: IndexedDbQuery.DeletePartial<any, never>, value: IndexedDbQuery.ExtractIndexType<any, never>) {
1342
+ return makeDelete({
1343
+ delete: this as any,
1344
+ upperBound: value,
1345
+ excludeUpperBound: true
1346
+ })
1347
+ },
1348
+ between(
1349
+ this: IndexedDbQuery.DeletePartial<any, never>,
1350
+ lowerBound: IndexedDbQuery.ExtractIndexType<any, never>,
1351
+ upperBound: IndexedDbQuery.ExtractIndexType<any, never>,
1352
+ queryOptions?: { excludeLowerBound?: boolean; excludeUpperBound?: boolean }
1353
+ ) {
1354
+ return makeDelete({
1355
+ delete: this as any,
1356
+ lowerBound,
1357
+ upperBound,
1358
+ excludeLowerBound: queryOptions?.excludeLowerBound ?? false,
1359
+ excludeUpperBound: queryOptions?.excludeUpperBound ?? false
1360
+ })
1361
+ }
1362
+ }
1363
+
1364
+ const makeDeletePartial = <
1365
+ Table extends IndexedDbTable.AnyWithProps,
1366
+ Index extends IndexedDbDatabase.IndexFromTable<Table>
1367
+ >(options: {
1368
+ readonly from: IndexedDbQuery.From<Table>
1369
+ readonly index: Index | undefined
1370
+ }): IndexedDbQuery.DeletePartial<Table, Index> => {
1371
+ const self = Object.create(DeletePartialProto)
1372
+ self.from = options.from
1373
+ self.index = options.index
1374
+ return self as any
1375
+ }
1376
+
1377
+ const DeleteProto: Omit<
1378
+ IndexedDbQuery.Delete<any, never>,
1379
+ | "delete"
1380
+ | "limitValue"
1381
+ | "only"
1382
+ | "lowerBound"
1383
+ | "upperBound"
1384
+ | "excludeLowerBound"
1385
+ | "excludeUpperBound"
1386
+ | "predicate"
1387
+ > = {
1388
+ ...CommonProto,
1389
+ asEffect(this: IndexedDbQuery.Delete<any, never>) {
1390
+ return applyDelete(this) as any
1391
+ },
1392
+ limit(this: IndexedDbQuery.Delete<any, never>, limit: number) {
1393
+ return makeDelete({
1394
+ ...this,
1395
+ limitValue: limit
1396
+ })
1397
+ },
1398
+ filter(this: IndexedDbQuery.Delete<any, never>, filter: (value: IndexedDbTable.Encoded<any>) => boolean) {
1399
+ const prev = this.predicate
1400
+ return makeDelete({
1401
+ delete: this.delete,
1402
+ predicate: prev ? (item) => prev(item) && filter(item) : filter
1403
+ })
1404
+ },
1405
+ invalidate(
1406
+ this: IndexedDbQuery.Delete<any, never>,
1407
+ keys?: ReadonlyArray<unknown> | Record.ReadonlyRecord<string, ReadonlyArray<unknown>> | undefined
1408
+ ) {
1409
+ keys ??= this.only !== undefined
1410
+ ? { [this.delete.from.table.tableName]: [this.only] }
1411
+ : [this.delete.from.table.tableName]
1412
+ return this.delete.from.reactivity.mutation(keys, this.asEffect())
1413
+ }
1414
+ }
1415
+
1416
+ const makeDelete = <
1417
+ Table extends IndexedDbTable.AnyWithProps,
1418
+ Index extends IndexedDbDatabase.IndexFromTable<Table>
1419
+ >(options: {
1420
+ readonly delete: IndexedDbQuery.DeletePartial<Table, Index>
1421
+ readonly limitValue?: number | undefined
1422
+ readonly only?: IndexedDbQuery.ExtractIndexType<Table, Index> | undefined
1423
+ readonly lowerBound?:
1424
+ | IndexedDbQuery.ExtractIndexType<Table, Index>
1425
+ | undefined
1426
+ readonly upperBound?:
1427
+ | IndexedDbQuery.ExtractIndexType<Table, Index>
1428
+ | undefined
1429
+ readonly excludeLowerBound?: boolean | undefined
1430
+ readonly excludeUpperBound?: boolean | undefined
1431
+ readonly predicate?: ((item: IndexedDbTable.Encoded<Table>) => boolean) | undefined
1432
+ }): IndexedDbQuery.Delete<Table, Index> => {
1433
+ const self = Object.create(DeleteProto)
1434
+ self.delete = options.delete
1435
+ self.limitValue = options.limitValue
1436
+ self.only = options.only
1437
+ self.lowerBound = options.lowerBound
1438
+ self.upperBound = options.upperBound
1439
+ self.excludeLowerBound = options.excludeLowerBound ?? false
1440
+ self.excludeUpperBound = options.excludeUpperBound ?? false
1441
+ self.predicate = options.predicate
1442
+ return self
1443
+ }
1444
+
1445
+ const CountProto: Omit<
1446
+ IndexedDbQuery.Count<any, never>,
1447
+ | "from"
1448
+ | "index"
1449
+ | "only"
1450
+ | "lowerBound"
1451
+ | "upperBound"
1452
+ | "excludeLowerBound"
1453
+ | "excludeUpperBound"
1454
+ > = {
1455
+ ...CommonProto,
1456
+ asEffect(this: IndexedDbQuery.Count<any, never>) {
1457
+ return getCount(this) as any
1458
+ },
1459
+ equals(this: IndexedDbQuery.Count<any, never>, value: IndexedDbQuery.ExtractIndexType<any, never>) {
1460
+ return makeCount({
1461
+ from: this.from,
1462
+ index: this.index,
1463
+ only: value
1464
+ })
1465
+ },
1466
+ gte(this: IndexedDbQuery.Count<any, never>, value: IndexedDbQuery.ExtractIndexType<any, never>) {
1467
+ return makeCount({
1468
+ from: this.from,
1469
+ index: this.index,
1470
+ lowerBound: value,
1471
+ excludeLowerBound: false
1472
+ })
1473
+ },
1474
+ lte(this: IndexedDbQuery.Count<any, never>, value: IndexedDbQuery.ExtractIndexType<any, never>) {
1475
+ return makeCount({
1476
+ from: this.from,
1477
+ index: this.index,
1478
+ upperBound: value,
1479
+ excludeUpperBound: false
1480
+ })
1481
+ },
1482
+ gt(this: IndexedDbQuery.Count<any, never>, value: IndexedDbQuery.ExtractIndexType<any, never>) {
1483
+ return makeCount({
1484
+ from: this.from,
1485
+ index: this.index,
1486
+ lowerBound: value,
1487
+ excludeLowerBound: true
1488
+ })
1489
+ },
1490
+ lt(this: IndexedDbQuery.Count<any, never>, value: IndexedDbQuery.ExtractIndexType<any, never>) {
1491
+ return makeCount({
1492
+ from: this.from,
1493
+ index: this.index,
1494
+ upperBound: value,
1495
+ excludeUpperBound: true
1496
+ })
1497
+ },
1498
+ between(
1499
+ this: IndexedDbQuery.Count<any, never>,
1500
+ lowerBound: IndexedDbQuery.ExtractIndexType<any, never>,
1501
+ upperBound: IndexedDbQuery.ExtractIndexType<any, never>,
1502
+ queryOptions?: { excludeLowerBound?: boolean; excludeUpperBound?: boolean }
1503
+ ) {
1504
+ return makeCount({
1505
+ from: this.from,
1506
+ index: this.index,
1507
+ lowerBound,
1508
+ upperBound,
1509
+ excludeLowerBound: queryOptions?.excludeLowerBound ?? false,
1510
+ excludeUpperBound: queryOptions?.excludeUpperBound ?? false
1511
+ })
1512
+ }
1513
+ }
1514
+
1515
+ const makeCount = <
1516
+ Table extends IndexedDbTable.AnyWithProps,
1517
+ Index extends IndexedDbDatabase.IndexFromTable<Table>
1518
+ >(options: {
1519
+ readonly from: IndexedDbQuery.From<Table>
1520
+ readonly index: Index | undefined
1521
+ readonly only?: IndexedDbQuery.ExtractIndexType<Table, Index> | undefined
1522
+ readonly lowerBound?:
1523
+ | IndexedDbQuery.ExtractIndexType<Table, Index>
1524
+ | undefined
1525
+ readonly upperBound?:
1526
+ | IndexedDbQuery.ExtractIndexType<Table, Index>
1527
+ | undefined
1528
+ readonly excludeLowerBound?: boolean | undefined
1529
+ readonly excludeUpperBound?: boolean | undefined
1530
+ }): IndexedDbQuery.Count<Table, Index> => {
1531
+ const self = Object.create(CountProto)
1532
+ self.from = options.from
1533
+ self.index = options.index
1534
+ self.only = options.only
1535
+ self.lowerBound = options.lowerBound
1536
+ self.upperBound = options.upperBound
1537
+ self.excludeLowerBound = options.excludeLowerBound
1538
+ self.excludeUpperBound = options.excludeUpperBound
1539
+ return self
1540
+ }
1541
+
1542
+ const SelectProto: Omit<
1543
+ IndexedDbQuery.Select<any, never>,
1544
+ | "from"
1545
+ | "index"
1546
+ | "limitValue"
1547
+ | "reverseValue"
1548
+ | "only"
1549
+ | "lowerBound"
1550
+ | "upperBound"
1551
+ | "excludeLowerBound"
1552
+ | "excludeUpperBound"
1553
+ > = {
1554
+ ...CommonProto,
1555
+ limit(this: IndexedDbQuery.Select<any, never>, limit: number) {
1556
+ return makeSelect({
1557
+ ...this,
1558
+ limitValue: limit
1559
+ })
1560
+ },
1561
+ offset(this: IndexedDbQuery.Select<any, never>, offset: number) {
1562
+ return makeSelect({
1563
+ ...this,
1564
+ offsetValue: offset
1565
+ })
1566
+ },
1567
+ equals(this: IndexedDbQuery.Select<any, never>, value: IndexedDbQuery.ExtractIndexType<any, never>) {
1568
+ return makeSelect({
1569
+ ...this,
1570
+ only: value
1571
+ })
1572
+ },
1573
+ gte(this: IndexedDbQuery.Select<any, never>, value: IndexedDbQuery.ExtractIndexType<any, never>) {
1574
+ return makeSelect({
1575
+ ...this,
1576
+ lowerBound: value,
1577
+ excludeLowerBound: false
1578
+ })
1579
+ },
1580
+ lte(this: IndexedDbQuery.Select<any, never>, value: IndexedDbQuery.ExtractIndexType<any, never>) {
1581
+ return makeSelect({
1582
+ ...this,
1583
+ upperBound: value,
1584
+ excludeUpperBound: false
1585
+ })
1586
+ },
1587
+ gt(this: IndexedDbQuery.Select<any, never>, value: IndexedDbQuery.ExtractIndexType<any, never>) {
1588
+ return makeSelect({
1589
+ ...this,
1590
+ lowerBound: value,
1591
+ excludeLowerBound: true
1592
+ })
1593
+ },
1594
+ lt(this: IndexedDbQuery.Select<any, never>, value: IndexedDbQuery.ExtractIndexType<any, never>) {
1595
+ return makeSelect({
1596
+ ...this,
1597
+ upperBound: value,
1598
+ excludeUpperBound: true
1599
+ })
1600
+ },
1601
+ between(
1602
+ this: IndexedDbQuery.Select<any, never>,
1603
+ lowerBound: IndexedDbQuery.ExtractIndexType<any, never>,
1604
+ upperBound: IndexedDbQuery.ExtractIndexType<any, never>,
1605
+ queryOptions?: { excludeLowerBound?: boolean; excludeUpperBound?: boolean }
1606
+ ) {
1607
+ return makeSelect({
1608
+ ...this,
1609
+ lowerBound,
1610
+ upperBound,
1611
+ excludeLowerBound: queryOptions?.excludeLowerBound ?? false,
1612
+ excludeUpperBound: queryOptions?.excludeUpperBound ?? false
1613
+ })
1614
+ },
1615
+ reverse(this: IndexedDbQuery.Select<any, never>) {
1616
+ return makeSelect({
1617
+ ...this,
1618
+ reverseValue: true
1619
+ })
1620
+ },
1621
+ first(this: IndexedDbQuery.Select<any, never>) {
1622
+ return makeFirst({ select: this })
1623
+ },
1624
+ filter(this: IndexedDbQuery.Select<any, never>, filter: (value: IndexedDbTable.Encoded<any>) => boolean) {
1625
+ const prev = this.predicate
1626
+ return makeSelect({
1627
+ ...this,
1628
+ predicate: prev ? (item) => prev(item) && filter(item) : filter
1629
+ })
1630
+ },
1631
+ asEffect(this: IndexedDbQuery.Select<any, never>) {
1632
+ return applySelect(this) as any
1633
+ },
1634
+ stream(this: IndexedDbQuery.Select<any, never>, options?: {
1635
+ readonly chunkSize?: number | undefined
1636
+ }) {
1637
+ const limit = this.limitValue
1638
+ const chunkSize = Math.min(options?.chunkSize ?? 100, limit ?? Number.MAX_SAFE_INTEGER)
1639
+ const initial = this.limit(chunkSize)
1640
+ return Stream.suspend(() => {
1641
+ let total = 0
1642
+ return Stream.paginate(initial, (select) =>
1643
+ Effect.map(
1644
+ applySelect(select as any),
1645
+ (data) => {
1646
+ total += data.length
1647
+ ;(select as any).offsetValue = total
1648
+ const reachedLimit = limit && total >= limit
1649
+ const isPartial = data.length < chunkSize
1650
+ return [data, isPartial || reachedLimit ? Option.none() : Option.some(select)] as const
1651
+ }
1652
+ ))
1653
+ })
1654
+ },
1655
+ reactive(
1656
+ this: IndexedDbQuery.Select<any, never>,
1657
+ keys?: ReadonlyArray<unknown> | Record.ReadonlyRecord<string, ReadonlyArray<unknown>> | undefined
1658
+ ) {
1659
+ keys ??= [this.from.table.tableName]
1660
+ return this.from.reactivity.stream(keys, this.asEffect())
1661
+ },
1662
+ reactiveQueue(
1663
+ this: IndexedDbQuery.Select<any, never>,
1664
+ keys?: ReadonlyArray<unknown> | Record.ReadonlyRecord<string, ReadonlyArray<unknown>> | undefined
1665
+ ) {
1666
+ keys ??= [this.from.table.tableName]
1667
+ return this.from.reactivity.query(keys, this.asEffect())
1668
+ }
1669
+ }
1670
+
1671
+ const makeSelect = <
1672
+ Table extends IndexedDbTable.AnyWithProps,
1673
+ Index extends IndexedDbDatabase.IndexFromTable<Table>
1674
+ >(options: {
1675
+ readonly from: IndexedDbQuery.From<Table>
1676
+ readonly index?: Index | undefined
1677
+ readonly limitValue?: number | undefined
1678
+ readonly offsetValue?: number | undefined
1679
+ readonly reverseValue?: boolean | undefined
1680
+ readonly only?: IndexedDbQuery.ExtractIndexType<Table, Index> | undefined
1681
+ readonly lowerBound?:
1682
+ | IndexedDbQuery.ExtractIndexType<Table, Index>
1683
+ | undefined
1684
+ readonly upperBound?:
1685
+ | IndexedDbQuery.ExtractIndexType<Table, Index>
1686
+ | undefined
1687
+ readonly excludeLowerBound?: boolean | undefined
1688
+ readonly excludeUpperBound?: boolean | undefined
1689
+ readonly predicate?: ((item: IndexedDbTable.Encoded<Table>) => boolean) | undefined
1690
+ }): IndexedDbQuery.Select<Table, Index> => {
1691
+ const self = Object.create(SelectProto)
1692
+ self.from = options.from
1693
+ self.index = options.index
1694
+ self.only = options.only
1695
+ self.limitValue = options.limitValue
1696
+ self.offsetValue = options.offsetValue
1697
+ self.reverseValue = options.reverseValue
1698
+ self.lowerBound = options.lowerBound
1699
+ self.upperBound = options.upperBound
1700
+ self.excludeLowerBound = options.excludeLowerBound
1701
+ self.excludeUpperBound = options.excludeUpperBound
1702
+ self.predicate = options.predicate
1703
+ return self as any
1704
+ }
1705
+
1706
+ const FirstProto: Omit<
1707
+ IndexedDbQuery.First<any, never>,
1708
+ "select"
1709
+ > = {
1710
+ ...CommonProto,
1711
+ asEffect(this: IndexedDbQuery.First<any, never>) {
1712
+ return getFirst(this) as any
1713
+ },
1714
+ reactive(
1715
+ this: IndexedDbQuery.First<any, never>,
1716
+ keys?: ReadonlyArray<unknown> | Record.ReadonlyRecord<string, ReadonlyArray<unknown>> | undefined
1717
+ ) {
1718
+ keys ??= this.select.only !== undefined
1719
+ ? [`${this.select.from.table.tableName}:${String(this.select.only)}`]
1720
+ : [this.select.from.table.tableName]
1721
+ return this.select.from.reactivity.stream(keys, this.asEffect())
1722
+ },
1723
+ reactiveQueue(
1724
+ this: IndexedDbQuery.First<any, never>,
1725
+ keys?: ReadonlyArray<unknown> | Record.ReadonlyRecord<string, ReadonlyArray<unknown>> | undefined
1726
+ ) {
1727
+ keys ??= this.select.only !== undefined
1728
+ ? [`${this.select.from.table.tableName}:${this.select.only}`]
1729
+ : [this.select.from.table.tableName]
1730
+ return this.select.from.reactivity.query(keys, this.asEffect())
1731
+ }
1732
+ }
1733
+
1734
+ const makeFirst = <
1735
+ Table extends IndexedDbTable.AnyWithProps,
1736
+ Index extends IndexedDbDatabase.IndexFromTable<Table>
1737
+ >(options: {
1738
+ readonly select: IndexedDbQuery.Select<Table, Index>
1739
+ }): IndexedDbQuery.First<Table, Index> => {
1740
+ const self = Object.create(FirstProto)
1741
+ self.select = options.select
1742
+ return self as any
1743
+ }
1744
+
1745
+ const ModifyProto: Omit<
1746
+ IndexedDbQuery.Modify<any>,
1747
+ | "from"
1748
+ | "value"
1749
+ | "operation"
1750
+ > = {
1751
+ ...CommonProto,
1752
+ asEffect(this: IndexedDbQuery.Modify<any>) {
1753
+ return applyModify({ query: this, value: this.value }) as any
1754
+ },
1755
+ invalidate(
1756
+ this: IndexedDbQuery.Modify<any>,
1757
+ keys?: ReadonlyArray<unknown> | Record.ReadonlyRecord<string, ReadonlyArray<unknown>> | undefined
1758
+ ) {
1759
+ const keyPath = this.from.table.keyPath
1760
+ keys ??= typeof keyPath === "string" && this.value[keyPath] !== undefined
1761
+ ? { [this.from.table.tableName]: [this.value[keyPath]] }
1762
+ : [this.from.table.tableName]
1763
+ return this.from.reactivity.mutation(keys, this.asEffect())
1764
+ }
1765
+ }
1766
+
1767
+ const makeModify = <Table extends IndexedDbTable.AnyWithProps>(options: {
1768
+ readonly from: IndexedDbQuery.From<Table>
1769
+ readonly value: IndexedDbTable.TableSchema<Table>["Type"]
1770
+ readonly operation: "add" | "put"
1771
+ }): IndexedDbQuery.Modify<Table> => {
1772
+ const self = Object.create(ModifyProto)
1773
+ self.from = options.from
1774
+ self.value = options.value
1775
+ self.operation = options.operation
1776
+ return self as any
1777
+ }
1778
+
1779
+ const ModifyAllProto: Omit<
1780
+ IndexedDbQuery.ModifyAll<any>,
1781
+ | "from"
1782
+ | "values"
1783
+ | "operation"
1784
+ > = {
1785
+ ...CommonProto,
1786
+ asEffect(this: IndexedDbQuery.ModifyAll<any>) {
1787
+ return applyModifyAll({ query: this, values: this.values }) as any
1788
+ },
1789
+ invalidate(
1790
+ this: IndexedDbQuery.ModifyAll<any>,
1791
+ keys?: ReadonlyArray<unknown> | Record.ReadonlyRecord<string, ReadonlyArray<unknown>> | undefined
1792
+ ) {
1793
+ keys ??= [this.from.table.tableName]
1794
+ return this.from.reactivity.mutation(keys, this.asEffect())
1795
+ }
1796
+ }
1797
+
1798
+ const makeModifyAll = <
1799
+ Table extends IndexedDbTable.AnyWithProps
1800
+ >(options: {
1801
+ readonly from: IndexedDbQuery.From<Table>
1802
+ readonly values: Array<IndexedDbTable.TableSchema<Table>["Type"]>
1803
+ readonly operation: "add" | "put"
1804
+ }): IndexedDbQuery.ModifyAll<Table> => {
1805
+ const self = Object.create(ModifyAllProto)
1806
+ self.from = options.from
1807
+ self.values = options.values
1808
+ self.operation = options.operation
1809
+ return self as any
1810
+ }
1811
+
1812
+ const QueryBuilderProto: Omit<
1813
+ IndexedDbQueryBuilder<any>,
1814
+ | "tables"
1815
+ | "database"
1816
+ | "IDBKeyRange"
1817
+ | "IDBTransaction"
1818
+ | "reactivity"
1819
+ > = {
1820
+ ...CommonProto,
1821
+ use(this: IndexedDbQueryBuilder<any>, f: (database: globalThis.IDBDatabase) => any) {
1822
+ return Effect.try({
1823
+ try: () => f(this.database.current),
1824
+ catch: (error) =>
1825
+ new IndexedDbQueryError({
1826
+ reason: "UnknownError",
1827
+ cause: error
1828
+ })
1829
+ })
1830
+ },
1831
+ from(this: IndexedDbQueryBuilder<any>, table: any) {
1832
+ return makeFrom({
1833
+ database: this.database,
1834
+ IDBKeyRange: this.IDBKeyRange,
1835
+ table: this.tables.get(table)!,
1836
+ reactivity: this.reactivity
1837
+ }) as any
1838
+ },
1839
+ get clearAll() {
1840
+ const self = this as IndexedDbQueryBuilder<any>
1841
+ return applyClearAll({ database: self.database.current })
1842
+ },
1843
+ withTransaction(this: IndexedDbQueryBuilder<any>, options: {
1844
+ readonly tables: NonEmptyReadonlyArray<any>
1845
+ readonly mode: globalThis.IDBTransactionMode
1846
+ readonly durability?: IDBTransactionDurability
1847
+ }) {
1848
+ return (effect) =>
1849
+ Effect.suspend(() => {
1850
+ const transaction = this.database.current.transaction(options.tables, options.mode, options)
1851
+ return Effect.provideService(effect, IndexedDbTransaction, transaction)
1852
+ }).pipe(
1853
+ // To prevent async gaps between transaction queries
1854
+ Effect.provideService(References.PreventSchedulerYield, true)
1855
+ )
1856
+ }
1857
+ }
1858
+
1859
+ /**
1860
+ * @since 4.0.0
1861
+ * @category constructors
1862
+ */
1863
+ export const make = <Source extends IndexedDbVersion.AnyWithProps>({
1864
+ IDBKeyRange,
1865
+ database,
1866
+ tables,
1867
+ reactivity
1868
+ }: {
1869
+ readonly database: MutableRef.MutableRef<globalThis.IDBDatabase>
1870
+ readonly IDBKeyRange: typeof globalThis.IDBKeyRange
1871
+ readonly tables: ReadonlyMap<string, IndexedDbVersion.Tables<Source>>
1872
+ readonly reactivity: Reactivity.Reactivity["Service"]
1873
+ }): IndexedDbQueryBuilder<Source> => {
1874
+ const self = Object.create(QueryBuilderProto)
1875
+ self.tables = tables
1876
+ self.database = database
1877
+ self.reactivity = reactivity
1878
+ self.IDBKeyRange = IDBKeyRange
1879
+ return self
1880
+ }
1881
+
1882
+ const getOrCreateTransaction = (
1883
+ database: globalThis.IDBDatabase,
1884
+ tables: ReadonlyArray<string>,
1885
+ mode: globalThis.IDBTransactionMode,
1886
+ options?: IDBTransactionOptions
1887
+ ) => {
1888
+ const fiber = Fiber.getCurrent()!
1889
+ return Context.getOrUndefined(fiber.context, IndexedDbTransaction) ?? database.transaction(tables, mode, options)
1890
+ }