@effect/platform-browser 4.0.0-beta.48 → 4.0.0-beta.49

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.
@@ -0,0 +1,17 @@
1
+ import * as Layer from "effect/Layer";
2
+ import * as Persistence from "effect/unstable/persistence/Persistence";
3
+ /**
4
+ * @since 1.0.0
5
+ * @category layers
6
+ */
7
+ export declare const layerBackingIndexedDb: (options?: {
8
+ readonly database?: string | undefined;
9
+ }) => Layer.Layer<Persistence.BackingPersistence>;
10
+ /**
11
+ * @since 1.0.0
12
+ * @category layers
13
+ */
14
+ export declare const layerIndexedDb: (options?: {
15
+ readonly database?: string | undefined;
16
+ }) => Layer.Layer<Persistence.Persistence>;
17
+ //# sourceMappingURL=BrowserPersistence.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BrowserPersistence.d.ts","sourceRoot":"","sources":["../src/BrowserPersistence.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AACrC,OAAO,KAAK,WAAW,MAAM,yCAAyC,CAAA;AAEtE;;;GAGG;AACH,eAAO,MAAM,qBAAqB,GAAI,UAAU;IAC9C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACvC,KAAG,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,kBAAkB,CAoBzC,CAAA;AAOL;;;GAGG;AACH,eAAO,MAAM,cAAc,GAAI,UAAU;IACvC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACvC,KAAG,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,WAAW,CAGpC,CAAA"}
@@ -0,0 +1,187 @@
1
+ import * as Clock from "effect/Clock";
2
+ import * as Effect from "effect/Effect";
3
+ import * as Layer from "effect/Layer";
4
+ import * as Persistence from "effect/unstable/persistence/Persistence";
5
+ /**
6
+ * @since 1.0.0
7
+ * @category layers
8
+ */
9
+ export const layerBackingIndexedDb = options => Layer.effect(Persistence.BackingPersistence)(Effect.gen(function* () {
10
+ const db = yield* Effect.acquireRelease(openDatabase(options?.database ?? defaultDatabase), db => Effect.sync(() => db.close())).pipe(Effect.orDie);
11
+ return Persistence.BackingPersistence.of({
12
+ make: Effect.fnUntraced(function* (storeId) {
13
+ const clock = yield* Clock.Clock;
14
+ return {
15
+ get: key => get(db, clock, storeId, key),
16
+ getMany: keys => getMany(db, clock, storeId, keys),
17
+ set: (key, value, ttl) => set(db, clock, storeId, key, value, ttl),
18
+ setMany: entries => setMany(db, clock, storeId, entries),
19
+ remove: key => remove(db, storeId, key),
20
+ clear: clear(db, storeId)
21
+ };
22
+ })
23
+ });
24
+ }));
25
+ const defaultDatabase = "effect_persistence";
26
+ const databaseVersion = 1;
27
+ const entriesStoreName = "entries";
28
+ const storeIdIndexName = "storeId";
29
+ /**
30
+ * @since 1.0.0
31
+ * @category layers
32
+ */
33
+ export const layerIndexedDb = options => Persistence.layer.pipe(Layer.provide(layerBackingIndexedDb(options)));
34
+ const openDatabase = database => Effect.gen(function* () {
35
+ const openRequest = yield* Effect.try({
36
+ try: () => globalThis.indexedDB.open(database, databaseVersion),
37
+ catch: cause => new Persistence.PersistenceError({
38
+ message: "Failed to open backing store database",
39
+ cause
40
+ })
41
+ });
42
+ openRequest.onupgradeneeded = () => {
43
+ const db = openRequest.result;
44
+ const entries = db.objectStoreNames.contains(entriesStoreName) ? openRequest.transaction?.objectStore(entriesStoreName) : db.createObjectStore(entriesStoreName, {
45
+ keyPath: ["storeId", "id"]
46
+ });
47
+ if (entries && !entries.indexNames.contains(storeIdIndexName)) {
48
+ entries.createIndex(storeIdIndexName, storeIdIndexName, {
49
+ unique: false
50
+ });
51
+ }
52
+ };
53
+ return yield* idbRequest("Failed to open backing store database", () => openRequest);
54
+ });
55
+ const isExpired = (row, now) => row.expires !== null && row.expires <= now;
56
+ const get = (db, clock, storeId, key) => withEntriesTransaction(db, "readwrite", `Failed to get key ${key} from backing store`, (entries, setResult, fail) => {
57
+ const now = clock.currentTimeMillisUnsafe();
58
+ const id = [storeId, key];
59
+ const request = entries.get(id);
60
+ request.onerror = () => fail(request.error);
61
+ request.onsuccess = () => {
62
+ const row = request.result;
63
+ if (!row || !isExpired(row, now)) {
64
+ setResult(row?.value);
65
+ return;
66
+ }
67
+ const deleteRequest = entries.delete(id);
68
+ deleteRequest.onerror = () => fail(deleteRequest.error);
69
+ deleteRequest.onsuccess = () => setResult(undefined);
70
+ };
71
+ });
72
+ const getMany = (db, clock, storeId, keys) => withEntriesTransaction(db, "readwrite", "Failed to getMany from backing store", (entries, setResult, fail) => {
73
+ const now = clock.currentTimeMillisUnsafe();
74
+ const results = new Array(keys.length);
75
+ setResult(results);
76
+ for (let i = 0; i < keys.length; i++) {
77
+ const key = keys[i];
78
+ const keyPath = [storeId, key];
79
+ const request = entries.get(keyPath);
80
+ request.onerror = () => fail(request.error);
81
+ request.onsuccess = () => {
82
+ const row = request.result;
83
+ if (!row) return;else if (!isExpired(row, now)) {
84
+ results[i] = row.value;
85
+ return;
86
+ }
87
+ const deleteRequest = entries.delete(keyPath);
88
+ deleteRequest.onerror = () => fail(deleteRequest.error);
89
+ };
90
+ }
91
+ });
92
+ const set = (db, clock, storeId, key, value, ttl) => withEntriesTransaction(db, "readwrite", `Failed to set key ${key} in backing store`, (entries, setResult, fail) => {
93
+ const request = entries.put({
94
+ storeId,
95
+ id: key,
96
+ value,
97
+ expires: Persistence.unsafeTtlToExpires(clock, ttl)
98
+ });
99
+ request.onerror = () => fail(request.error);
100
+ request.onsuccess = () => setResult(undefined);
101
+ });
102
+ const setMany = (db, clock, storeId, entries) => withEntriesTransaction(db, "readwrite", "Failed to setMany in backing store", (store, setResult, fail) => {
103
+ for (const [key, value, ttl] of entries) {
104
+ const request = store.put({
105
+ storeId,
106
+ id: key,
107
+ value,
108
+ expires: Persistence.unsafeTtlToExpires(clock, ttl)
109
+ });
110
+ request.onerror = () => fail(request.error);
111
+ request.onsuccess = () => setResult(undefined);
112
+ }
113
+ });
114
+ const remove = (db, storeId, key) => withEntriesTransaction(db, "readwrite", `Failed to remove key ${key} from backing store`, (entries, setResult, fail) => {
115
+ const request = entries.delete([storeId, key]);
116
+ request.onerror = () => fail(request.error);
117
+ request.onsuccess = () => setResult(undefined);
118
+ });
119
+ const clear = (db, storeId) => withEntriesTransaction(db, "readwrite", "Failed to clear backing store", (entries, setResult, fail) => {
120
+ const index = entries.index(storeIdIndexName);
121
+ const cursorRequest = index.openCursor(storeId);
122
+ cursorRequest.onerror = () => fail(cursorRequest.error);
123
+ cursorRequest.onsuccess = () => {
124
+ const cursor = cursorRequest.result;
125
+ if (!cursor) {
126
+ setResult(undefined);
127
+ return;
128
+ }
129
+ const deleteRequest = cursor.delete();
130
+ deleteRequest.onerror = () => fail(deleteRequest.error);
131
+ deleteRequest.onsuccess = () => cursor.continue();
132
+ };
133
+ });
134
+ const withEntriesTransaction = (db, mode, message, run) => Effect.callback(resume => {
135
+ const tx = db.transaction(entriesStoreName, mode);
136
+ const entries = tx.objectStore(entriesStoreName);
137
+ let result;
138
+ let setResult = false;
139
+ let done = false;
140
+ const fail = cause => {
141
+ if (!done) {
142
+ done = true;
143
+ tx.abort();
144
+ }
145
+ resume(Effect.fail(new Persistence.PersistenceError({
146
+ message,
147
+ cause
148
+ })));
149
+ };
150
+ tx.oncomplete = () => {
151
+ done = true;
152
+ if (setResult) resume(Effect.succeed(result));
153
+ };
154
+ tx.onerror = () => {
155
+ done = true;
156
+ fail(tx.error);
157
+ };
158
+ tx.onabort = () => {
159
+ done = true;
160
+ fail(tx.error);
161
+ };
162
+ run(entries, next => {
163
+ if (done) return resume(Effect.succeed(next));
164
+ setResult = true;
165
+ result = next;
166
+ }, fail);
167
+ return Effect.sync(() => {
168
+ tx.abort();
169
+ });
170
+ });
171
+ const idbRequest = (message, evaluate) => Effect.callback(resume => {
172
+ const request = evaluate();
173
+ const fail = cause => {
174
+ resume(Effect.fail(new Persistence.PersistenceError({
175
+ message,
176
+ cause
177
+ })));
178
+ };
179
+ if (request.readyState === "done") {
180
+ resume(Effect.succeed(request.result));
181
+ }
182
+ request.onsuccess = () => {
183
+ resume(Effect.succeed(request.result));
184
+ };
185
+ request.onerror = () => fail(request.error);
186
+ });
187
+ //# sourceMappingURL=BrowserPersistence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BrowserPersistence.js","names":["Clock","Effect","Layer","Persistence","layerBackingIndexedDb","options","effect","BackingPersistence","gen","db","acquireRelease","openDatabase","database","defaultDatabase","sync","close","pipe","orDie","of","make","fnUntraced","storeId","clock","get","key","getMany","keys","set","value","ttl","setMany","entries","remove","clear","databaseVersion","entriesStoreName","storeIdIndexName","layerIndexedDb","layer","provide","openRequest","try","globalThis","indexedDB","open","catch","cause","PersistenceError","message","onupgradeneeded","result","objectStoreNames","contains","transaction","objectStore","createObjectStore","keyPath","indexNames","createIndex","unique","idbRequest","isExpired","row","now","expires","withEntriesTransaction","setResult","fail","currentTimeMillisUnsafe","id","request","onerror","error","onsuccess","deleteRequest","delete","undefined","results","Array","length","i","put","unsafeTtlToExpires","store","index","cursorRequest","openCursor","cursor","continue","mode","run","callback","resume","tx","done","abort","oncomplete","succeed","onabort","next","evaluate","readyState"],"sources":["../src/BrowserPersistence.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
package/dist/index.d.ts CHANGED
@@ -9,6 +9,10 @@ export * as BrowserHttpClient from "./BrowserHttpClient.ts";
9
9
  * @since 1.0.0
10
10
  */
11
11
  export * as BrowserKeyValueStore from "./BrowserKeyValueStore.ts";
12
+ /**
13
+ * @since 1.0.0
14
+ */
15
+ export * as BrowserPersistence from "./BrowserPersistence.ts";
12
16
  /**
13
17
  * @since 1.0.0
14
18
  */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH;;GAEG;AACH,OAAO,KAAK,iBAAiB,MAAM,wBAAwB,CAAA;AAE3D;;GAEG;AACH,OAAO,KAAK,oBAAoB,MAAM,2BAA2B,CAAA;AAEjE;;GAEG;AACH,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAA;AAErD;;GAEG;AACH,OAAO,KAAK,aAAa,MAAM,oBAAoB,CAAA;AAEnD;;GAEG;AACH,OAAO,KAAK,aAAa,MAAM,oBAAoB,CAAA;AAEnD;;GAEG;AACH,OAAO,KAAK,aAAa,MAAM,oBAAoB,CAAA;AAEnD;;GAEG;AACH,OAAO,KAAK,mBAAmB,MAAM,0BAA0B,CAAA;AAE/D;;GAEG;AACH,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAA;AAE3C;;GAEG;AACH,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAA;AAE/C;;GAEG;AACH,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAA;AAE3C;;GAEG;AACH,OAAO,KAAK,iBAAiB,MAAM,wBAAwB,CAAA;AAE3D;;GAEG;AACH,OAAO,KAAK,qBAAqB,MAAM,4BAA4B,CAAA;AAEnE;;GAEG;AACH,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAA;AAErD;;GAEG;AACH,OAAO,KAAK,gBAAgB,MAAM,uBAAuB,CAAA;AAEzD;;GAEG;AACH,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH;;GAEG;AACH,OAAO,KAAK,iBAAiB,MAAM,wBAAwB,CAAA;AAE3D;;GAEG;AACH,OAAO,KAAK,oBAAoB,MAAM,2BAA2B,CAAA;AAEjE;;GAEG;AACH,OAAO,KAAK,kBAAkB,MAAM,yBAAyB,CAAA;AAE7D;;GAEG;AACH,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAA;AAErD;;GAEG;AACH,OAAO,KAAK,aAAa,MAAM,oBAAoB,CAAA;AAEnD;;GAEG;AACH,OAAO,KAAK,aAAa,MAAM,oBAAoB,CAAA;AAEnD;;GAEG;AACH,OAAO,KAAK,aAAa,MAAM,oBAAoB,CAAA;AAEnD;;GAEG;AACH,OAAO,KAAK,mBAAmB,MAAM,0BAA0B,CAAA;AAE/D;;GAEG;AACH,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAA;AAE3C;;GAEG;AACH,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAA;AAE/C;;GAEG;AACH,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAA;AAE3C;;GAEG;AACH,OAAO,KAAK,iBAAiB,MAAM,wBAAwB,CAAA;AAE3D;;GAEG;AACH,OAAO,KAAK,qBAAqB,MAAM,4BAA4B,CAAA;AAEnE;;GAEG;AACH,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAA;AAErD;;GAEG;AACH,OAAO,KAAK,gBAAgB,MAAM,uBAAuB,CAAA;AAEzD;;GAEG;AACH,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAA"}
package/dist/index.js CHANGED
@@ -10,6 +10,10 @@ export * as BrowserHttpClient from "./BrowserHttpClient.js";
10
10
  * @since 1.0.0
11
11
  */
12
12
  export * as BrowserKeyValueStore from "./BrowserKeyValueStore.js";
13
+ /**
14
+ * @since 1.0.0
15
+ */
16
+ export * as BrowserPersistence from "./BrowserPersistence.js";
13
17
  /**
14
18
  * @since 1.0.0
15
19
  */
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["BrowserHttpClient","BrowserKeyValueStore","BrowserRuntime","BrowserSocket","BrowserStream","BrowserWorker","BrowserWorkerRunner","Clipboard","Geolocation","IndexedDb","IndexedDbDatabase","IndexedDbQueryBuilder","IndexedDbTable","IndexedDbVersion","Permissions"],"sources":["../src/index.ts"],"sourcesContent":[null],"mappings":"AAAA;;;AAIA;AAEA;;;AAGA,OAAO,KAAKA,iBAAiB,MAAM,wBAAwB;AAE3D;;;AAGA,OAAO,KAAKC,oBAAoB,MAAM,2BAA2B;AAEjE;;;AAGA,OAAO,KAAKC,cAAc,MAAM,qBAAqB;AAErD;;;AAGA,OAAO,KAAKC,aAAa,MAAM,oBAAoB;AAEnD;;;AAGA,OAAO,KAAKC,aAAa,MAAM,oBAAoB;AAEnD;;;AAGA,OAAO,KAAKC,aAAa,MAAM,oBAAoB;AAEnD;;;AAGA,OAAO,KAAKC,mBAAmB,MAAM,0BAA0B;AAE/D;;;AAGA,OAAO,KAAKC,SAAS,MAAM,gBAAgB;AAE3C;;;AAGA,OAAO,KAAKC,WAAW,MAAM,kBAAkB;AAE/C;;;AAGA,OAAO,KAAKC,SAAS,MAAM,gBAAgB;AAE3C;;;AAGA,OAAO,KAAKC,iBAAiB,MAAM,wBAAwB;AAE3D;;;AAGA,OAAO,KAAKC,qBAAqB,MAAM,4BAA4B;AAEnE;;;AAGA,OAAO,KAAKC,cAAc,MAAM,qBAAqB;AAErD;;;AAGA,OAAO,KAAKC,gBAAgB,MAAM,uBAAuB;AAEzD;;;AAGA,OAAO,KAAKC,WAAW,MAAM,kBAAkB","ignoreList":[]}
1
+ {"version":3,"file":"index.js","names":["BrowserHttpClient","BrowserKeyValueStore","BrowserPersistence","BrowserRuntime","BrowserSocket","BrowserStream","BrowserWorker","BrowserWorkerRunner","Clipboard","Geolocation","IndexedDb","IndexedDbDatabase","IndexedDbQueryBuilder","IndexedDbTable","IndexedDbVersion","Permissions"],"sources":["../src/index.ts"],"sourcesContent":[null],"mappings":"AAAA;;;AAIA;AAEA;;;AAGA,OAAO,KAAKA,iBAAiB,MAAM,wBAAwB;AAE3D;;;AAGA,OAAO,KAAKC,oBAAoB,MAAM,2BAA2B;AAEjE;;;AAGA,OAAO,KAAKC,kBAAkB,MAAM,yBAAyB;AAE7D;;;AAGA,OAAO,KAAKC,cAAc,MAAM,qBAAqB;AAErD;;;AAGA,OAAO,KAAKC,aAAa,MAAM,oBAAoB;AAEnD;;;AAGA,OAAO,KAAKC,aAAa,MAAM,oBAAoB;AAEnD;;;AAGA,OAAO,KAAKC,aAAa,MAAM,oBAAoB;AAEnD;;;AAGA,OAAO,KAAKC,mBAAmB,MAAM,0BAA0B;AAE/D;;;AAGA,OAAO,KAAKC,SAAS,MAAM,gBAAgB;AAE3C;;;AAGA,OAAO,KAAKC,WAAW,MAAM,kBAAkB;AAE/C;;;AAGA,OAAO,KAAKC,SAAS,MAAM,gBAAgB;AAE3C;;;AAGA,OAAO,KAAKC,iBAAiB,MAAM,wBAAwB;AAE3D;;;AAGA,OAAO,KAAKC,qBAAqB,MAAM,4BAA4B;AAEnE;;;AAGA,OAAO,KAAKC,cAAc,MAAM,qBAAqB;AAErD;;;AAGA,OAAO,KAAKC,gBAAgB,MAAM,uBAAuB;AAEzD;;;AAGA,OAAO,KAAKC,WAAW,MAAM,kBAAkB","ignoreList":[]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@effect/platform-browser",
3
3
  "type": "module",
4
- "version": "4.0.0-beta.48",
4
+ "version": "4.0.0-beta.49",
5
5
  "license": "MIT",
6
6
  "description": "Platform specific implementations for the browser",
7
7
  "homepage": "https://effect.website",
@@ -45,12 +45,12 @@
45
45
  "provenance": true
46
46
  },
47
47
  "peerDependencies": {
48
- "effect": "^4.0.0-beta.48"
48
+ "effect": "^4.0.0-beta.49"
49
49
  },
50
50
  "devDependencies": {
51
51
  "mock-xmlhttprequest": "^8.4.1",
52
52
  "fake-indexeddb": "^6.2.5",
53
- "effect": "^4.0.0-beta.48"
53
+ "effect": "^4.0.0-beta.49"
54
54
  },
55
55
  "dependencies": {
56
56
  "multipasta": "^0.2.7"
@@ -0,0 +1,306 @@
1
+ /**
2
+ * @since 1.0.0
3
+ */
4
+ import type * as Arr from "effect/Array"
5
+ import * as Clock from "effect/Clock"
6
+ import type * as Duration from "effect/Duration"
7
+ import * as Effect from "effect/Effect"
8
+ import * as Layer from "effect/Layer"
9
+ import * as Persistence from "effect/unstable/persistence/Persistence"
10
+
11
+ /**
12
+ * @since 1.0.0
13
+ * @category layers
14
+ */
15
+ export const layerBackingIndexedDb = (options?: {
16
+ readonly database?: string | undefined
17
+ }): Layer.Layer<Persistence.BackingPersistence> =>
18
+ Layer.effect(Persistence.BackingPersistence)(Effect.gen(function*() {
19
+ const db = yield* Effect.acquireRelease(
20
+ openDatabase(options?.database ?? defaultDatabase),
21
+ (db) => Effect.sync(() => db.close())
22
+ ).pipe(Effect.orDie)
23
+
24
+ return Persistence.BackingPersistence.of({
25
+ make: Effect.fnUntraced(function*(storeId) {
26
+ const clock = yield* Clock.Clock
27
+ return {
28
+ get: (key) => get(db, clock, storeId, key),
29
+ getMany: (keys) => getMany(db, clock, storeId, keys),
30
+ set: (key, value, ttl) => set(db, clock, storeId, key, value, ttl),
31
+ setMany: (entries) => setMany(db, clock, storeId, entries),
32
+ remove: (key) => remove(db, storeId, key),
33
+ clear: clear(db, storeId)
34
+ }
35
+ })
36
+ })
37
+ }))
38
+
39
+ const defaultDatabase = "effect_persistence"
40
+ const databaseVersion = 1
41
+ const entriesStoreName = "entries"
42
+ const storeIdIndexName = "storeId"
43
+
44
+ /**
45
+ * @since 1.0.0
46
+ * @category layers
47
+ */
48
+ export const layerIndexedDb = (options?: {
49
+ readonly database?: string | undefined
50
+ }): Layer.Layer<Persistence.Persistence> =>
51
+ Persistence.layer.pipe(
52
+ Layer.provide(layerBackingIndexedDb(options))
53
+ )
54
+
55
+ const openDatabase = (database: string): Effect.Effect<IDBDatabase, Persistence.PersistenceError> =>
56
+ Effect.gen(function*() {
57
+ const openRequest = yield* Effect.try({
58
+ try: () => globalThis.indexedDB.open(database, databaseVersion),
59
+ catch: (cause) =>
60
+ new Persistence.PersistenceError({
61
+ message: "Failed to open backing store database",
62
+ cause
63
+ })
64
+ })
65
+
66
+ openRequest.onupgradeneeded = () => {
67
+ const db = openRequest.result
68
+ const entries = db.objectStoreNames.contains(entriesStoreName)
69
+ ? openRequest.transaction?.objectStore(entriesStoreName)
70
+ : db.createObjectStore(entriesStoreName, { keyPath: ["storeId", "id"] })
71
+ if (entries && !entries.indexNames.contains(storeIdIndexName)) {
72
+ entries.createIndex(storeIdIndexName, storeIdIndexName, { unique: false })
73
+ }
74
+ }
75
+
76
+ return yield* idbRequest("Failed to open backing store database", () => openRequest)
77
+ })
78
+
79
+ interface EntryRow {
80
+ readonly storeId: string
81
+ readonly id: string
82
+ readonly value: object
83
+ readonly expires: number | null
84
+ }
85
+
86
+ const isExpired = (row: EntryRow, now: number): boolean => row.expires !== null && row.expires <= now
87
+
88
+ const get = (
89
+ db: IDBDatabase,
90
+ clock: Clock.Clock,
91
+ storeId: string,
92
+ key: string
93
+ ): Effect.Effect<object | undefined, Persistence.PersistenceError> =>
94
+ withEntriesTransaction<object | undefined>(
95
+ db,
96
+ "readwrite",
97
+ `Failed to get key ${key} from backing store`,
98
+ (
99
+ entries,
100
+ setResult,
101
+ fail
102
+ ) => {
103
+ const now = clock.currentTimeMillisUnsafe()
104
+ const id: [string, string] = [storeId, key]
105
+ const request = entries.get(id)
106
+ request.onerror = () => fail(request.error)
107
+ request.onsuccess = () => {
108
+ const row = request.result as EntryRow | undefined
109
+ if (!row || !isExpired(row, now)) {
110
+ setResult(row?.value)
111
+ return
112
+ }
113
+
114
+ const deleteRequest = entries.delete(id)
115
+ deleteRequest.onerror = () => fail(deleteRequest.error)
116
+ deleteRequest.onsuccess = () => setResult(undefined)
117
+ }
118
+ }
119
+ )
120
+
121
+ const getMany = (
122
+ db: IDBDatabase,
123
+ clock: Clock.Clock,
124
+ storeId: string,
125
+ keys: Arr.NonEmptyArray<string>
126
+ ): Effect.Effect<Arr.NonEmptyArray<object | undefined>, Persistence.PersistenceError> =>
127
+ withEntriesTransaction(
128
+ db,
129
+ "readwrite",
130
+ "Failed to getMany from backing store",
131
+ (entries, setResult, fail) => {
132
+ const now = clock.currentTimeMillisUnsafe()
133
+ const results = new Array<object | undefined>(keys.length)
134
+ setResult(results as any)
135
+
136
+ for (let i = 0; i < keys.length; i++) {
137
+ const key = keys[i]
138
+ const keyPath = [storeId, key]
139
+ const request = entries.get(keyPath)
140
+ request.onerror = () => fail(request.error)
141
+ request.onsuccess = () => {
142
+ const row = request.result as EntryRow | undefined
143
+ if (!row) return
144
+ else if (!isExpired(row, now)) {
145
+ results[i] = row.value
146
+ return
147
+ }
148
+ const deleteRequest = entries.delete(keyPath)
149
+ deleteRequest.onerror = () => fail(deleteRequest.error)
150
+ }
151
+ }
152
+ }
153
+ )
154
+
155
+ const set = (
156
+ db: IDBDatabase,
157
+ clock: Clock.Clock,
158
+ storeId: string,
159
+ key: string,
160
+ value: object,
161
+ ttl: Duration.Duration | undefined
162
+ ): Effect.Effect<void, Persistence.PersistenceError> =>
163
+ withEntriesTransaction(
164
+ db,
165
+ "readwrite",
166
+ `Failed to set key ${key} in backing store`,
167
+ (entries, setResult, fail) => {
168
+ const request = entries.put(
169
+ {
170
+ storeId,
171
+ id: key,
172
+ value,
173
+ expires: Persistence.unsafeTtlToExpires(clock, ttl)
174
+ } satisfies EntryRow
175
+ )
176
+ request.onerror = () => fail(request.error)
177
+ request.onsuccess = () => setResult(undefined)
178
+ }
179
+ )
180
+
181
+ const setMany = (
182
+ db: IDBDatabase,
183
+ clock: Clock.Clock,
184
+ storeId: string,
185
+ entries: Arr.NonEmptyArray<readonly [key: string, value: object, ttl: Duration.Duration | undefined]>
186
+ ): Effect.Effect<void, Persistence.PersistenceError> =>
187
+ withEntriesTransaction(
188
+ db,
189
+ "readwrite",
190
+ "Failed to setMany in backing store",
191
+ (store, setResult, fail) => {
192
+ for (const [key, value, ttl] of entries) {
193
+ const request = store.put(
194
+ {
195
+ storeId,
196
+ id: key,
197
+ value,
198
+ expires: Persistence.unsafeTtlToExpires(clock, ttl)
199
+ } satisfies EntryRow
200
+ )
201
+ request.onerror = () => fail(request.error)
202
+ request.onsuccess = () => setResult(undefined)
203
+ }
204
+ }
205
+ )
206
+
207
+ const remove = (
208
+ db: IDBDatabase,
209
+ storeId: string,
210
+ key: string
211
+ ): Effect.Effect<void, Persistence.PersistenceError> =>
212
+ withEntriesTransaction(
213
+ db,
214
+ "readwrite",
215
+ `Failed to remove key ${key} from backing store`,
216
+ (entries, setResult, fail) => {
217
+ const request = entries.delete([storeId, key])
218
+ request.onerror = () => fail(request.error)
219
+ request.onsuccess = () => setResult(undefined)
220
+ }
221
+ )
222
+
223
+ const clear = (db: IDBDatabase, storeId: string): Effect.Effect<void, Persistence.PersistenceError> =>
224
+ withEntriesTransaction(db, "readwrite", "Failed to clear backing store", (entries, setResult, fail) => {
225
+ const index = entries.index(storeIdIndexName)
226
+ const cursorRequest = index.openCursor(storeId)
227
+ cursorRequest.onerror = () => fail(cursorRequest.error)
228
+ cursorRequest.onsuccess = () => {
229
+ const cursor = cursorRequest.result
230
+ if (!cursor) {
231
+ setResult(undefined)
232
+ return
233
+ }
234
+ const deleteRequest = cursor.delete()
235
+ deleteRequest.onerror = () => fail(deleteRequest.error)
236
+ deleteRequest.onsuccess = () => cursor.continue()
237
+ }
238
+ })
239
+
240
+ const withEntriesTransaction = <A>(
241
+ db: IDBDatabase,
242
+ mode: IDBTransactionMode,
243
+ message: string,
244
+ run: (
245
+ entries: IDBObjectStore,
246
+ onResult: (result: A) => void,
247
+ fail: (cause: unknown) => void
248
+ ) => void
249
+ ): Effect.Effect<A, Persistence.PersistenceError> =>
250
+ Effect.callback<A, Persistence.PersistenceError>((resume) => {
251
+ const tx = db.transaction(entriesStoreName, mode)
252
+ const entries = tx.objectStore(entriesStoreName)
253
+
254
+ let result: A | undefined
255
+ let setResult = false
256
+ let done = false
257
+
258
+ const fail = (cause: unknown) => {
259
+ if (!done) {
260
+ done = true
261
+ tx.abort()
262
+ }
263
+ resume(Effect.fail(new Persistence.PersistenceError({ message, cause })))
264
+ }
265
+
266
+ tx.oncomplete = () => {
267
+ done = true
268
+ if (setResult) resume(Effect.succeed(result!))
269
+ }
270
+ tx.onerror = () => {
271
+ done = true
272
+ fail(tx.error)
273
+ }
274
+ tx.onabort = () => {
275
+ done = true
276
+ fail(tx.error)
277
+ }
278
+
279
+ run(entries, (next) => {
280
+ if (done) return resume(Effect.succeed(next))
281
+ setResult = true
282
+ result = next
283
+ }, fail)
284
+
285
+ return Effect.sync(() => {
286
+ tx.abort()
287
+ })
288
+ })
289
+
290
+ const idbRequest = <A>(
291
+ message: string,
292
+ evaluate: () => IDBRequest<A>
293
+ ): Effect.Effect<A, Persistence.PersistenceError> =>
294
+ Effect.callback<A, Persistence.PersistenceError>((resume) => {
295
+ const request = evaluate()
296
+ const fail = (cause: unknown) => {
297
+ resume(Effect.fail(new Persistence.PersistenceError({ message, cause })))
298
+ }
299
+ if (request.readyState === "done") {
300
+ resume(Effect.succeed(request.result))
301
+ }
302
+ request.onsuccess = () => {
303
+ resume(Effect.succeed(request.result))
304
+ }
305
+ request.onerror = () => fail(request.error)
306
+ })
package/src/index.ts CHANGED
@@ -14,6 +14,11 @@ export * as BrowserHttpClient from "./BrowserHttpClient.ts"
14
14
  */
15
15
  export * as BrowserKeyValueStore from "./BrowserKeyValueStore.ts"
16
16
 
17
+ /**
18
+ * @since 1.0.0
19
+ */
20
+ export * as BrowserPersistence from "./BrowserPersistence.ts"
21
+
17
22
  /**
18
23
  * @since 1.0.0
19
24
  */