@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,631 @@
1
+ /**
2
+ * @since 4.0.0
3
+ */
4
+ import * as Context from "effect/Context"
5
+ import * as Data from "effect/Data"
6
+ import * as Effect from "effect/Effect"
7
+ import * as Fiber from "effect/Fiber"
8
+ import * as Inspectable from "effect/Inspectable"
9
+ import * as Layer from "effect/Layer"
10
+ import * as MutableRef from "effect/MutableRef"
11
+ import * as Pipeable from "effect/Pipeable"
12
+ import * as Semaphore from "effect/Semaphore"
13
+ import * as Reactivity from "effect/unstable/reactivity/Reactivity"
14
+ import * as Utils from "effect/Utils"
15
+ import * as IndexedDb from "./IndexedDb.ts"
16
+ import * as IndexedDbQueryBuilder from "./IndexedDbQueryBuilder.ts"
17
+ import type * as IndexedDbTable from "./IndexedDbTable.ts"
18
+ import type * as IndexedDbVersion from "./IndexedDbVersion.ts"
19
+
20
+ const TypeId = "~@effect/platform-browser/IndexedDbDatabase"
21
+ const ErrorTypeId = "~@effect/platform-browser/IndexedDbDatabase/IndexedDbDatabaseError"
22
+
23
+ const YieldableProto = {
24
+ [Symbol.iterator]() {
25
+ return new Utils.SingleShotGen(this) as any
26
+ }
27
+ }
28
+
29
+ const PipeInspectableProto = {
30
+ ...Pipeable.Prototype,
31
+ ...Inspectable.BaseProto,
32
+ toJSON(this: any) {
33
+ return { _id: "IndexedDbDatabase" }
34
+ }
35
+ }
36
+
37
+ const CommonProto = {
38
+ [TypeId]: {
39
+ _A: (_: never) => _
40
+ },
41
+ ...PipeInspectableProto,
42
+ ...YieldableProto
43
+ }
44
+
45
+ /**
46
+ * @since 4.0.0
47
+ * @category errors
48
+ */
49
+ export type ErrorReason =
50
+ | "TransactionError"
51
+ | "MissingTable"
52
+ | "OpenError"
53
+ | "UpgradeError"
54
+ | "Aborted"
55
+ | "Blocked"
56
+ | "MissingIndex"
57
+
58
+ /**
59
+ * @since 4.0.0
60
+ * @category errors
61
+ */
62
+ export class IndexedDbDatabaseError extends Data.TaggedError(
63
+ "IndexedDbDatabaseError"
64
+ )<{
65
+ reason: ErrorReason
66
+ cause: unknown
67
+ }> {
68
+ /**
69
+ * @since 4.0.0
70
+ */
71
+ readonly [ErrorTypeId]: typeof ErrorTypeId = ErrorTypeId
72
+ override readonly message = this.reason
73
+ }
74
+
75
+ /**
76
+ * @since 4.0.0
77
+ * @category models
78
+ */
79
+ export class IndexedDbDatabase extends Context.Service<
80
+ IndexedDbDatabase,
81
+ {
82
+ readonly database: MutableRef.MutableRef<globalThis.IDBDatabase>
83
+ readonly IDBKeyRange: typeof globalThis.IDBKeyRange
84
+ readonly reactivity: Reactivity.Reactivity["Service"]
85
+ readonly rebuild: Effect.Effect<void, IndexedDbDatabaseError>
86
+ }
87
+ >()(TypeId) {}
88
+
89
+ /**
90
+ * @since 4.0.0
91
+ * @category models
92
+ */
93
+ export interface IndexedDbSchema<
94
+ in out FromVersion extends IndexedDbVersion.AnyWithProps,
95
+ in out ToVersion extends IndexedDbVersion.AnyWithProps,
96
+ out Error = never
97
+ > extends
98
+ Pipeable.Pipeable,
99
+ Inspectable.Inspectable,
100
+ Effect.YieldableClass<
101
+ IndexedDbQueryBuilder.IndexedDbQueryBuilder<ToVersion>,
102
+ never,
103
+ IndexedDbDatabase
104
+ >
105
+ {
106
+ new(_: never): {}
107
+
108
+ readonly previous: [FromVersion] extends [never] ? undefined
109
+ : IndexedDbSchema<never, FromVersion, Error>
110
+ readonly fromVersion: FromVersion
111
+ readonly version: ToVersion
112
+
113
+ readonly migrate: [FromVersion] extends [never] ? (query: Transaction<ToVersion>) => Effect.Effect<void, Error>
114
+ : (
115
+ fromQuery: Transaction<FromVersion>,
116
+ toQuery: Transaction<ToVersion>
117
+ ) => Effect.Effect<void, Error>
118
+
119
+ readonly add: <Version extends IndexedDbVersion.AnyWithProps, MigrationError>(
120
+ version: Version,
121
+ migrate: (
122
+ fromQuery: Transaction<ToVersion>,
123
+ toQuery: Transaction<Version>
124
+ ) => Effect.Effect<void, MigrationError>
125
+ ) => IndexedDbSchema<ToVersion, Version, MigrationError | Error>
126
+
127
+ readonly getQueryBuilder: Effect.Effect<
128
+ IndexedDbQueryBuilder.IndexedDbQueryBuilder<ToVersion>,
129
+ never,
130
+ IndexedDbDatabase
131
+ >
132
+
133
+ readonly layer: (
134
+ databaseName: string
135
+ ) => Layer.Layer<
136
+ IndexedDbDatabase,
137
+ IndexedDbDatabaseError,
138
+ IndexedDb.IndexedDb
139
+ >
140
+ }
141
+
142
+ /**
143
+ * @since 4.0.0
144
+ * @category models
145
+ */
146
+ export interface Transaction<
147
+ Source extends IndexedDbVersion.AnyWithProps = never
148
+ > extends Pipeable.Pipeable, Omit<IndexedDbQueryBuilder.IndexedDbQueryBuilder<Source>, "transaction"> {
149
+ readonly transaction: globalThis.IDBTransaction
150
+
151
+ readonly createObjectStore: <
152
+ A extends IndexedDbTable.TableName<IndexedDbVersion.Tables<Source>>
153
+ >(
154
+ table: A
155
+ ) => Effect.Effect<globalThis.IDBObjectStore, IndexedDbDatabaseError>
156
+
157
+ readonly deleteObjectStore: <
158
+ A extends IndexedDbTable.TableName<IndexedDbVersion.Tables<Source>>
159
+ >(
160
+ table: A
161
+ ) => Effect.Effect<void, IndexedDbDatabaseError>
162
+
163
+ readonly createIndex: <
164
+ Name extends IndexedDbTable.TableName<IndexedDbVersion.Tables<Source>>
165
+ >(
166
+ table: Name,
167
+ indexName: IndexFromTableName<Source, Name>,
168
+ options?: IDBIndexParameters
169
+ ) => Effect.Effect<globalThis.IDBIndex, IndexedDbDatabaseError>
170
+
171
+ readonly deleteIndex: <
172
+ Name extends IndexedDbTable.TableName<IndexedDbVersion.Tables<Source>>
173
+ >(
174
+ table: Name,
175
+ indexName: IndexFromTableName<Source, Name>
176
+ ) => Effect.Effect<void, IndexedDbDatabaseError>
177
+ }
178
+
179
+ /**
180
+ * @since 4.0.0
181
+ * @category models
182
+ */
183
+ export type IndexFromTable<Table extends IndexedDbTable.AnyWithProps> = IsStringLiteral<
184
+ Extract<keyof IndexedDbTable.Indexes<Table>, string>
185
+ > extends true ? Extract<keyof IndexedDbTable.Indexes<Table>, string>
186
+ : never
187
+
188
+ /**
189
+ * @since 4.0.0
190
+ * @category models
191
+ */
192
+ export type IndexFromTableName<
193
+ Version extends IndexedDbVersion.AnyWithProps,
194
+ Table extends string
195
+ > = IndexFromTable<
196
+ IndexedDbTable.WithName<IndexedDbVersion.Tables<Version>, Table>
197
+ >
198
+
199
+ /**
200
+ * @since 4.0.0
201
+ * @category models
202
+ */
203
+ export interface Any {
204
+ readonly previous?: Any | undefined
205
+ readonly layer: (
206
+ databaseName: string
207
+ ) => Layer.Layer<
208
+ IndexedDbDatabase,
209
+ IndexedDbDatabaseError,
210
+ IndexedDb.IndexedDb
211
+ >
212
+ }
213
+
214
+ /**
215
+ * @since 4.0.0
216
+ * @category models
217
+ */
218
+ export type AnySchema = IndexedDbSchema<
219
+ IndexedDbVersion.AnyWithProps,
220
+ IndexedDbVersion.AnyWithProps,
221
+ any
222
+ >
223
+
224
+ /**
225
+ * @since 4.0.0
226
+ * @category constructors
227
+ */
228
+ export const make = <
229
+ InitialVersion extends IndexedDbVersion.AnyWithProps,
230
+ Error
231
+ >(
232
+ initialVersion: InitialVersion,
233
+ init: (toQuery: Transaction<InitialVersion>) => Effect.Effect<void, Error>
234
+ ): IndexedDbSchema<never, InitialVersion, Error> =>
235
+ (function() {
236
+ // oxlint-disable-next-line typescript/no-extraneous-class
237
+ class Initial {}
238
+ Object.assign(Initial, CommonProto)
239
+ ;(Initial as any).version = initialVersion
240
+ ;(Initial as any).migrate = init
241
+ ;(Initial as any)._tag = "Initial"
242
+ ;(Initial as any).add = <Version extends IndexedDbVersion.AnyWithProps>(
243
+ version: Version,
244
+ migrate: (
245
+ fromQuery: Transaction<InitialVersion>,
246
+ toQuery: Transaction<Version>
247
+ ) => Effect.Effect<void, Error>
248
+ ) =>
249
+ makeProto({
250
+ fromVersion: initialVersion,
251
+ version,
252
+ migrate,
253
+ previous: Initial as any
254
+ })
255
+ ;(Initial as any).getQueryBuilder = Effect.gen(function*() {
256
+ const { IDBKeyRange, database, reactivity } = yield* IndexedDbDatabase
257
+ return IndexedDbQueryBuilder.make({
258
+ database,
259
+ IDBKeyRange,
260
+ tables: initialVersion.tables,
261
+ reactivity
262
+ })
263
+ })
264
+ ;(Initial as any).asEffect = function() {
265
+ return this.getQueryBuilder
266
+ }
267
+ ;(Initial as any).layer = <DatabaseName extends string>(
268
+ databaseName: DatabaseName
269
+ ) => layer(databaseName, Initial as any)
270
+
271
+ return Initial as any
272
+ })()
273
+
274
+ const makeProto = <
275
+ FromVersion extends IndexedDbVersion.AnyWithProps,
276
+ ToVersion extends IndexedDbVersion.AnyWithProps,
277
+ Error
278
+ >(options: {
279
+ readonly previous:
280
+ | IndexedDbSchema<FromVersion, ToVersion, Error>
281
+ | IndexedDbSchema<never, FromVersion, Error>
282
+ readonly fromVersion: FromVersion
283
+ readonly version: ToVersion
284
+ readonly migrate: (
285
+ fromQuery: Transaction<FromVersion>,
286
+ toQuery: Transaction<ToVersion>
287
+ ) => Effect.Effect<void, Error>
288
+ }): IndexedDbSchema<FromVersion, ToVersion, Error> =>
289
+ (function() {
290
+ // oxlint-disable-next-line typescript/no-extraneous-class
291
+ class Migration {}
292
+ Object.assign(Migration, CommonProto)
293
+ ;(Migration as any).previous = options.previous
294
+ ;(Migration as any).fromVersion = options.fromVersion
295
+ ;(Migration as any).version = options.version
296
+ ;(Migration as any).migrate = options.migrate
297
+ ;(Migration as any)._tag = "Migration"
298
+ ;(Migration as any).getQueryBuilder = Effect.gen(function*() {
299
+ const { IDBKeyRange, database, reactivity } = yield* IndexedDbDatabase
300
+ return IndexedDbQueryBuilder.make({
301
+ database,
302
+ IDBKeyRange,
303
+ tables: options.version.tables,
304
+ reactivity
305
+ })
306
+ })
307
+ ;(Migration as any).asEffect = function() {
308
+ return this.getQueryBuilder
309
+ }
310
+ ;(Migration as any).layer = <DatabaseName extends string>(
311
+ databaseName: DatabaseName
312
+ ) => layer(databaseName, Migration as any)
313
+
314
+ return Migration as any
315
+ })()
316
+
317
+ const layer = <DatabaseName extends string>(
318
+ databaseName: DatabaseName,
319
+ migration: Any
320
+ ) =>
321
+ Layer.effect(
322
+ IndexedDbDatabase,
323
+ Effect.gen(function*() {
324
+ const { IDBKeyRange, indexedDB } = yield* IndexedDb.IndexedDb
325
+ const reactivity = yield* Reactivity.Reactivity
326
+ const context = yield* Effect.context()
327
+ const runForkWith = Effect.runForkWith(context)
328
+
329
+ let oldVersion = 0
330
+ const migrations: Array<Any> = []
331
+ let current = migration
332
+ while (current) {
333
+ migrations.unshift(current)
334
+ current = (current as unknown as AnySchema).previous as any
335
+ }
336
+
337
+ const version = migrations.length
338
+ const database = MutableRef.make<globalThis.IDBDatabase>(null as any)
339
+
340
+ const open = Effect.callback<
341
+ void,
342
+ IndexedDbDatabaseError
343
+ >((resume) => {
344
+ const request = indexedDB.open(databaseName, version)
345
+
346
+ request.onblocked = (event) => {
347
+ resume(
348
+ Effect.fail(
349
+ new IndexedDbDatabaseError({
350
+ reason: "Blocked",
351
+ cause: event
352
+ })
353
+ )
354
+ )
355
+ }
356
+
357
+ request.onerror = (event) => {
358
+ const idbRequest = event.target as IDBRequest<IDBDatabase>
359
+
360
+ resume(
361
+ Effect.fail(
362
+ new IndexedDbDatabaseError({
363
+ reason: "OpenError",
364
+ cause: idbRequest.error
365
+ })
366
+ )
367
+ )
368
+ }
369
+
370
+ let fiber: Fiber.Fiber<void, IndexedDbDatabaseError> | undefined
371
+ request.onupgradeneeded = (event) => {
372
+ const idbRequest = event.target as IDBRequest<IDBDatabase>
373
+ const db = idbRequest.result
374
+ const transaction = idbRequest.transaction
375
+ oldVersion = event.oldVersion
376
+
377
+ MutableRef.set(database, db)
378
+
379
+ if (transaction === null) {
380
+ return resume(
381
+ Effect.fail(
382
+ new IndexedDbDatabaseError({
383
+ reason: "TransactionError",
384
+ cause: null
385
+ })
386
+ )
387
+ )
388
+ }
389
+
390
+ transaction.onabort = (event) => {
391
+ resume(
392
+ Effect.fail(
393
+ new IndexedDbDatabaseError({
394
+ reason: "Aborted",
395
+ cause: event
396
+ })
397
+ )
398
+ )
399
+ }
400
+
401
+ transaction.onerror = (event) => {
402
+ resume(
403
+ Effect.fail(
404
+ new IndexedDbDatabaseError({
405
+ reason: "TransactionError",
406
+ cause: event
407
+ })
408
+ )
409
+ )
410
+ }
411
+
412
+ const effect = Effect.forEach(
413
+ migrations.slice(oldVersion),
414
+ (untypedMigration) => {
415
+ if (untypedMigration.previous === undefined) {
416
+ const migration = untypedMigration as any as AnySchema
417
+ const api = makeTransactionProto({
418
+ database,
419
+ IDBKeyRange,
420
+ tables: migration.version.tables,
421
+ transaction,
422
+ reactivity
423
+ })
424
+ return (migration as any).migrate(api) as Effect.Effect<
425
+ void,
426
+ IndexedDbDatabaseError
427
+ >
428
+ } else if (untypedMigration.previous) {
429
+ const migration = untypedMigration as any as AnySchema
430
+ const fromApi = makeTransactionProto({
431
+ database,
432
+ IDBKeyRange,
433
+ tables: migration.fromVersion.tables,
434
+ transaction,
435
+ reactivity
436
+ })
437
+ const toApi = makeTransactionProto({
438
+ database,
439
+ IDBKeyRange,
440
+ tables: migration.version.tables,
441
+ transaction,
442
+ reactivity
443
+ })
444
+ return migration.migrate(fromApi, toApi) as Effect.Effect<
445
+ void,
446
+ IndexedDbDatabaseError
447
+ >
448
+ }
449
+
450
+ return Effect.die(new Error("Invalid migration"))
451
+ },
452
+ { discard: true }
453
+ ).pipe(
454
+ Effect.mapError(
455
+ (cause) =>
456
+ new IndexedDbDatabaseError({
457
+ reason: "UpgradeError",
458
+ cause
459
+ })
460
+ ),
461
+ Effect.provideService(IndexedDbQueryBuilder.IndexedDbTransaction, transaction)
462
+ )
463
+ fiber = runForkWith(effect)
464
+ fiber.currentDispatcher.flush()
465
+ }
466
+
467
+ request.onsuccess = (event) => {
468
+ const idbRequest = event.target as IDBRequest<IDBDatabase>
469
+ const db = idbRequest.result
470
+ MutableRef.set(database, db)
471
+ if (fiber) {
472
+ // ensure migration errors are propagated
473
+ resume(Effect.asVoid(Fiber.join(fiber)))
474
+ } else {
475
+ resume(Effect.void)
476
+ }
477
+ }
478
+ })
479
+
480
+ yield* Effect.addFinalizer(() => {
481
+ database.current?.close()
482
+ return Effect.void
483
+ })
484
+ yield* open
485
+
486
+ const rebuildLock = Semaphore.makeUnsafe(1).withPermit
487
+ const rebuild = Effect.callback<void, IndexedDbDatabaseError>((resume) => {
488
+ database.current?.close()
489
+ const request = indexedDB.deleteDatabase(databaseName)
490
+ request.onerror = (_) => {
491
+ resume(
492
+ Effect.fail(
493
+ new IndexedDbDatabaseError({
494
+ reason: "OpenError",
495
+ cause: request.error
496
+ })
497
+ )
498
+ )
499
+ }
500
+ request.onsuccess = () => {
501
+ resume(Effect.void)
502
+ }
503
+ }).pipe(
504
+ Effect.flatMap(() => open),
505
+ rebuildLock
506
+ )
507
+
508
+ return IndexedDbDatabase.of({ database, IDBKeyRange, rebuild, reactivity })
509
+ })
510
+ ).pipe(
511
+ Layer.provide(Reactivity.layer)
512
+ )
513
+
514
+ // -----------------------------------------------------------------------------
515
+ // Internal
516
+ // -----------------------------------------------------------------------------
517
+
518
+ type IsStringLiteral<T> = T extends string ? string extends T ? false
519
+ : true
520
+ : false
521
+
522
+ const makeTransactionProto = <Source extends IndexedDbVersion.AnyWithProps>({
523
+ IDBKeyRange,
524
+ database,
525
+ tables,
526
+ transaction,
527
+ reactivity
528
+ }: {
529
+ readonly database: MutableRef.MutableRef<globalThis.IDBDatabase>
530
+ readonly IDBKeyRange: typeof globalThis.IDBKeyRange
531
+ readonly tables: ReadonlyMap<string, IndexedDbVersion.Tables<Source>>
532
+ readonly transaction: globalThis.IDBTransaction
533
+ readonly reactivity: Reactivity.Reactivity["Service"]
534
+ }): Transaction<Source> => {
535
+ const migration = IndexedDbQueryBuilder.make({
536
+ database,
537
+ IDBKeyRange,
538
+ tables,
539
+ reactivity
540
+ }) as any
541
+
542
+ migration.transaction = transaction
543
+
544
+ migration.createObjectStore = Effect.fnUntraced(function*(table: string) {
545
+ const createTable = yield* Effect.fromNullishOr(tables.get(table)).pipe(
546
+ Effect.mapError(
547
+ (cause) =>
548
+ new IndexedDbDatabaseError({
549
+ reason: "MissingTable",
550
+ cause
551
+ })
552
+ )
553
+ )
554
+
555
+ return yield* Effect.try({
556
+ try: () =>
557
+ database.current.createObjectStore(createTable.tableName, {
558
+ keyPath: createTable.keyPath,
559
+ autoIncrement: createTable.autoIncrement
560
+ }),
561
+ catch: (cause) =>
562
+ new IndexedDbDatabaseError({
563
+ reason: "TransactionError",
564
+ cause
565
+ })
566
+ })
567
+ })
568
+
569
+ migration.deleteObjectStore = Effect.fnUntraced(function*(table: string) {
570
+ const createTable = yield* Effect.fromNullishOr(tables.get(table)).pipe(
571
+ Effect.mapError(
572
+ (cause) =>
573
+ new IndexedDbDatabaseError({
574
+ reason: "MissingTable",
575
+ cause
576
+ })
577
+ )
578
+ )
579
+
580
+ return yield* Effect.try({
581
+ try: () => database.current.deleteObjectStore(createTable.tableName),
582
+ catch: (cause) =>
583
+ new IndexedDbDatabaseError({
584
+ reason: "TransactionError",
585
+ cause
586
+ })
587
+ })
588
+ })
589
+
590
+ migration.createIndex = Effect.fnUntraced(function*(
591
+ table: string,
592
+ indexName: string,
593
+ options?: IDBIndexParameters
594
+ ) {
595
+ const store = transaction.objectStore(table)
596
+ const sourceTable = tables.get(table)!
597
+
598
+ const keyPath = yield* Effect.fromNullishOr(
599
+ sourceTable.indexes[indexName]
600
+ ).pipe(
601
+ Effect.mapError(
602
+ (cause) =>
603
+ new IndexedDbDatabaseError({
604
+ reason: "MissingIndex",
605
+ cause
606
+ })
607
+ )
608
+ )
609
+
610
+ return yield* Effect.try({
611
+ try: () => store.createIndex(indexName, keyPath, options),
612
+ catch: (cause) =>
613
+ new IndexedDbDatabaseError({
614
+ reason: "TransactionError",
615
+ cause
616
+ })
617
+ })
618
+ })
619
+
620
+ migration.deleteIndex = (table: string, indexName: string) =>
621
+ Effect.try({
622
+ try: () => transaction.objectStore(table).deleteIndex(indexName),
623
+ catch: (cause) =>
624
+ new IndexedDbDatabaseError({
625
+ reason: "TransactionError",
626
+ cause
627
+ })
628
+ })
629
+
630
+ return migration
631
+ }