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