@effect/platform-browser 4.0.0-beta.6 → 4.0.0-beta.62

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