@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.
- package/dist/BrowserPersistence.d.ts +17 -0
- package/dist/BrowserPersistence.d.ts.map +1 -0
- package/dist/BrowserPersistence.js +187 -0
- package/dist/BrowserPersistence.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/BrowserPersistence.ts +306 -0
- package/src/index.ts +5 -0
|
@@ -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
|
*/
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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.
|
|
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
|
+
"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.
|
|
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
|
*/
|