@chester-hill-solutions/stow-s3 0.3.5 → 0.3.7
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/README.md +23 -12
- package/dist/async-streams.d.ts.map +1 -1
- package/dist/async-streams.js +7 -12
- package/dist/async-streams.js.map +1 -1
- package/dist/indexeddb-lifecycle.d.ts.map +1 -1
- package/dist/indexeddb-lifecycle.js +21 -3
- package/dist/indexeddb-lifecycle.js.map +1 -1
- package/dist/indexeddb-persistence.d.ts +3 -6
- package/dist/indexeddb-persistence.d.ts.map +1 -1
- package/dist/indexeddb-persistence.js +69 -65
- package/dist/indexeddb-persistence.js.map +1 -1
- package/package.json +5 -5
- package/src/async-streams.ts +14 -11
- package/src/indexeddb-lifecycle.ts +15 -3
- package/src/indexeddb-persistence.ts +76 -63
package/README.md
CHANGED
|
@@ -4,9 +4,22 @@ TypeScript APIs for native S3 sessions, workspaces and embedded browser storage.
|
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
Install the public npm package:
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install @chester-hill-solutions/stow-s3@0.3.7
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Node.js 20 or newer is required. Linux and macOS on x64 or ARM64 receive their native carrier automatically.
|
|
14
|
+
Native process APIs need no Go installation or `STOW_BIN` override.
|
|
15
|
+
Embedded and browser APIs use the included WASM assets.
|
|
16
|
+
Remove any old scope override to `npm.pkg.github.com`; this package is published on npmjs.
|
|
17
|
+
|
|
18
|
+
Standalone v0.3.7 binaries are also available as
|
|
19
|
+
[GitHub downloads](https://github.com/chester-hill-solutions/stow-s3/releases/tag/v0.3.7).
|
|
20
|
+
Use WSL on Windows.
|
|
21
|
+
|
|
22
|
+
For local development, build a source checkout:
|
|
10
23
|
|
|
11
24
|
```sh
|
|
12
25
|
git clone https://github.com/chester-hill-solutions/stow-s3
|
|
@@ -17,11 +30,9 @@ npm run build --prefix packages/stow-s3
|
|
|
17
30
|
export STOW_BIN="$PWD/bin/stow-s3"
|
|
18
31
|
```
|
|
19
32
|
|
|
20
|
-
Use `STOW_BIN`
|
|
21
|
-
Embedded WASM APIs use the built package assets.
|
|
22
|
-
Registry publication is deferred. Do not rely on a registry install for v0.3.3.
|
|
33
|
+
Use `STOW_BIN` to select the source-built native binary during development.
|
|
23
34
|
<!-- repo-only:start -->
|
|
24
|
-
See the [
|
|
35
|
+
See the [npm publication record](../../docs/development/npm-publishing.md#published-037) for release verification.
|
|
25
36
|
<!-- repo-only:end -->
|
|
26
37
|
|
|
27
38
|
## Usage
|
|
@@ -124,7 +135,7 @@ const summary = await withStow(async (session) => {
|
|
|
124
135
|
|
|
125
136
|
- Source checkouts use `STOW_BIN`. A platform package can supply the binary in a published distribution.
|
|
126
137
|
<!-- repo-only:start -->
|
|
127
|
-
See the [release checkpoint](../../docs/release-verification-2026-10-14.md) before using registry installation.
|
|
138
|
+
See the [release checkpoint](../../docs/history/releases/release-verification-2026-10-14.md) before using registry installation.
|
|
128
139
|
<!-- repo-only:end -->
|
|
129
140
|
- `STOW_*`, `S3_*`, and `AWS_*` are stripped from the child environment, so
|
|
130
141
|
ambient cloud configuration cannot turn a local session into a run-through one.
|
|
@@ -167,13 +178,13 @@ The wrapper also exports resume, same-machine handoff, diff, restore, portable
|
|
|
167
178
|
checkpoint export, and import helpers. These calls return the CLI's JSON result
|
|
168
179
|
and use the same validation and limits as the Go implementation. They require
|
|
169
180
|
the package's platform binary or `STOW_BIN` when developing from a checkout.
|
|
170
|
-
See [`docs/task-manifest.md`](../../docs/task-manifest.md) for the schema,
|
|
181
|
+
See [`docs/contracts/task-manifest.md`](../../docs/contracts/task-manifest.md) for the schema,
|
|
171
182
|
archive rules, and the filesystem-isolation boundary. This is a persistent
|
|
172
183
|
workspace API; it is separate from the disposable S3 session APIs above.
|
|
173
184
|
|
|
174
185
|
### Browser persistence profile
|
|
175
186
|
|
|
176
|
-
The additive [`@chester-hill-solutions/stow-s3/browser`](../../docs/browser-persistence.md) entry
|
|
187
|
+
The additive [`@chester-hill-solutions/stow-s3/browser`](../../docs/guides/browser-persistence.md) entry
|
|
177
188
|
point coordinates durable commits around the embedded memory profile. The
|
|
178
189
|
`openBrowserEmbeddedStow` wrapper replays the committed generation during async
|
|
179
190
|
open, serializes operations FIFO, and commits each mutation before its promise
|
|
@@ -212,7 +223,7 @@ unchanged.
|
|
|
212
223
|
this profile. Undo/redo and pending sync receipts survive reopen; remote writes
|
|
213
224
|
use an expected ETag or expected absence, and conflicts retain local edits.
|
|
214
225
|
`S3ObjectSyncRemote` is available from the separate `./browser-s3` entry point.
|
|
215
|
-
See [the API and limits](../../docs/browser-file-history.md), or run
|
|
226
|
+
See [the API and limits](../../docs/guides/browser-file-history.md), or run
|
|
216
227
|
`make browser-editor` from the repository root for the offline editor demo.
|
|
217
228
|
|
|
218
229
|
CLI equivalent:
|
|
@@ -246,7 +257,7 @@ It supports shared chunks, atomic revisions, bounded range reads and conditional
|
|
|
246
257
|
`/worker` exports `AsyncEmbeddedStow`.
|
|
247
258
|
`/browser-wasm` provides `loadBrowserPersistentWasmHost` for durable host storage.
|
|
248
259
|
|
|
249
|
-
Use the [project guide](../../docs/browser-project-storage.md) or [worker guide](../../docs/browser-worker-runtime.md) to get started.
|
|
260
|
+
Use the [project guide](../../docs/guides/browser-project-storage.md) or [worker guide](../../docs/guides/browser-worker-runtime.md) to get started.
|
|
250
261
|
|
|
251
262
|
<!-- repo-only:start -->
|
|
252
263
|
See the [1 GiB test record](../../docs/benchmarks/browser-project-2026-10-12.json).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"async-streams.d.ts","sourceRoot":"","sources":["../src/async-streams.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0D,KAAK,cAAc,EAAE,KAAK,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACrI,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,eAAO,MAAM,2BAA2B,QAAa,CAAC;AAEtD,MAAM,WAAW,wBAAyB,SAAQ,kBAAkB;IAClE,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;IAClC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,MAAM,WAAW,qBAAqB;IACpC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CACnG;
|
|
1
|
+
{"version":3,"file":"async-streams.d.ts","sourceRoot":"","sources":["../src/async-streams.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0D,KAAK,cAAc,EAAE,KAAK,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACrI,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,eAAO,MAAM,2BAA2B,QAAa,CAAC;AAEtD,MAAM,WAAW,wBAAyB,SAAQ,kBAAkB;IAClE,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;IAClC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,MAAM,WAAW,qBAAqB;IACpC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CACnG;AAOD,qBAAa,wBAAwB;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,qBAAqB;IAEpD,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,aAAa,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,cAAc,CAAC;IA6B5I,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAO,GAAG,OAAO,CAAC,oBAAoB,CAAC;YAyCpG,OAAO;CAOtB"}
|
package/dist/async-streams.js
CHANGED
|
@@ -46,18 +46,13 @@ export class EmbeddedStreamOperations {
|
|
|
46
46
|
const opened = await this.invoke({ op: "openObjectRead", bucket, key });
|
|
47
47
|
const token = opened.token;
|
|
48
48
|
const object = fromBridgeObject(opened.object);
|
|
49
|
-
let
|
|
50
|
-
let closePromise;
|
|
49
|
+
let state = { kind: "open" };
|
|
51
50
|
const close = () => {
|
|
52
|
-
if (
|
|
53
|
-
return
|
|
54
|
-
closed =
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
closePromise = undefined;
|
|
58
|
-
throw error;
|
|
59
|
-
});
|
|
60
|
-
return closePromise;
|
|
51
|
+
if (state.kind !== "open")
|
|
52
|
+
return state.completion;
|
|
53
|
+
const completion = this.cleanup("closeObjectRead", token).then(() => { state = { kind: "closed", completion }; }, (error) => { state = { kind: "open" }; throw error; });
|
|
54
|
+
state = { kind: "closing", completion };
|
|
55
|
+
return completion;
|
|
61
56
|
};
|
|
62
57
|
if (options.signal?.aborted) {
|
|
63
58
|
await close();
|
|
@@ -67,7 +62,7 @@ export class EmbeddedStreamOperations {
|
|
|
67
62
|
const chunks = (async function* () {
|
|
68
63
|
let read = 0;
|
|
69
64
|
try {
|
|
70
|
-
while (
|
|
65
|
+
while (state.kind === "open" && read < object.size) {
|
|
71
66
|
options.signal?.throwIfAborted();
|
|
72
67
|
const length = Math.min(EMBEDDED_STREAM_CHUNK_BYTES, object.size - read);
|
|
73
68
|
const result = await invoke({ op: "readObjectChunk", token, length }, undefined, options);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"async-streams.js","sourceRoot":"","sources":["../src/async-streams.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAmE,MAAM,eAAe,CAAC;AAGrI,MAAM,CAAC,MAAM,2BAA2B,GAAG,GAAG,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"async-streams.js","sourceRoot":"","sources":["../src/async-streams.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"indexeddb-lifecycle.d.ts","sourceRoot":"","sources":["../src/indexeddb-lifecycle.ts"],"names":[],"mappings":"AACA,UAAU,mBAAmB;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,QAAQ,EAAE,WAAW,GAAG,IAAI,CAAC;IACrC,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;CACxB;AACD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,WAAW,CAAC,
|
|
1
|
+
{"version":3,"file":"indexeddb-lifecycle.d.ts","sourceRoot":"","sources":["../src/indexeddb-lifecycle.ts"],"names":[],"mappings":"AACA,UAAU,mBAAmB;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,QAAQ,EAAE,WAAW,GAAG,IAAI,CAAC;IACrC,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;CACxB;AACD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,WAAW,CAAC,CAuB3G"}
|
|
@@ -1,14 +1,32 @@
|
|
|
1
1
|
import { BrowserPersistenceError } from "./browser-types.js";
|
|
2
2
|
export function openIndexedDatabase(factory, options) {
|
|
3
3
|
return new Promise((resolve, reject) => {
|
|
4
|
+
let state = "pending";
|
|
4
5
|
const request = factory.open(options.name, options.version);
|
|
5
|
-
request.onupgradeneeded = () =>
|
|
6
|
+
request.onupgradeneeded = () => {
|
|
7
|
+
if (state !== "pending") {
|
|
8
|
+
request.transaction?.abort();
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
options.upgrade(request.result);
|
|
12
|
+
};
|
|
6
13
|
request.onsuccess = () => {
|
|
14
|
+
if (state !== "pending") {
|
|
15
|
+
request.result.close();
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
state = "opened";
|
|
7
19
|
request.result.onversionchange = () => request.result.close();
|
|
8
20
|
resolve(request.result);
|
|
9
21
|
};
|
|
10
|
-
request.onerror = () =>
|
|
11
|
-
|
|
22
|
+
request.onerror = () => {
|
|
23
|
+
state = "rejected";
|
|
24
|
+
reject(request.error ?? new Error(options.failedMessage));
|
|
25
|
+
};
|
|
26
|
+
request.onblocked = () => {
|
|
27
|
+
state = "rejected";
|
|
28
|
+
reject(new BrowserPersistenceError("persistence_denied", options.blockedMessage));
|
|
29
|
+
};
|
|
12
30
|
});
|
|
13
31
|
}
|
|
14
32
|
//# sourceMappingURL=indexeddb-lifecycle.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"indexeddb-lifecycle.js","sourceRoot":"","sources":["../src/indexeddb-lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAQ7D,MAAM,UAAU,mBAAmB,CAAC,OAAmB,EAAE,OAA4B;IACnF,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QAC5D,OAAO,CAAC,eAAe,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"indexeddb-lifecycle.js","sourceRoot":"","sources":["../src/indexeddb-lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAQ7D,MAAM,UAAU,mBAAmB,CAAC,OAAmB,EAAE,OAA4B;IACnF,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,IAAI,KAAK,GAAsC,SAAS,CAAC;QACzD,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QAC5D,OAAO,CAAC,eAAe,GAAG,GAAG,EAAE;YAC7B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBAAC,OAAO,CAAC,WAAW,EAAE,KAAK,EAAE,CAAC;gBAAC,OAAO;YAAC,CAAC;YAClE,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAClC,CAAC,CAAC;QACF,OAAO,CAAC,SAAS,GAAG,GAAG,EAAE;YACvB,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;gBAAC,OAAO;YAAC,CAAC;YAC5D,KAAK,GAAG,QAAQ,CAAC;YACjB,OAAO,CAAC,MAAM,CAAC,eAAe,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YAC9D,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC1B,CAAC,CAAC;QACF,OAAO,CAAC,OAAO,GAAG,GAAG,EAAE;YACrB,KAAK,GAAG,UAAU,CAAC;YACnB,MAAM,CAAC,OAAO,CAAC,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC;QAC5D,CAAC,CAAC;QACF,OAAO,CAAC,SAAS,GAAG,GAAG,EAAE;YACvB,KAAK,GAAG,UAAU,CAAC;YACnB,MAAM,CAAC,IAAI,uBAAuB,CAAC,oBAAoB,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC;QACpF,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -7,19 +7,16 @@ export declare class IndexedDbPersistenceAdapter implements PersistenceAdapter {
|
|
|
7
7
|
private readonly objectStoreName;
|
|
8
8
|
private readonly lockManager;
|
|
9
9
|
private readonly lockName;
|
|
10
|
-
private
|
|
11
|
-
private namespace;
|
|
12
|
-
private ownership;
|
|
13
|
-
private closePromise;
|
|
14
|
-
private closed;
|
|
10
|
+
private lifecycle;
|
|
15
11
|
constructor(options?: IndexedDbPersistenceAdapterOptions);
|
|
16
12
|
open(namespace: string): Promise<void>;
|
|
13
|
+
private acquireResources;
|
|
17
14
|
private get stores();
|
|
18
15
|
load(namespace: string): Promise<PersistenceSnapshot | null>;
|
|
19
16
|
commit(namespace: string, expectedGeneration: number, changes: PersistenceChange[]): Promise<number>;
|
|
20
17
|
clear(namespace: string, expectedGeneration: number): Promise<number>;
|
|
21
18
|
close(): Promise<void>;
|
|
22
19
|
private closeInternal;
|
|
23
|
-
private
|
|
20
|
+
private resourcesFor;
|
|
24
21
|
}
|
|
25
22
|
//# sourceMappingURL=indexeddb-persistence.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"indexeddb-persistence.d.ts","sourceRoot":"","sources":["../src/indexeddb-persistence.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,kCAAkC,EAEvC,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EAEzB,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"indexeddb-persistence.d.ts","sourceRoot":"","sources":["../src/indexeddb-persistence.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,kCAAkC,EAEvC,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EAEzB,MAAM,oBAAoB,CAAC;AAoC5B,qBAAa,2BAA4B,YAAW,kBAAkB;IACpE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyB;IACjD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA8B;IAC1D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgC;IACzD,OAAO,CAAC,SAAS,CAAsC;gBAE3C,OAAO,GAAE,kCAAuC;IAUtD,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAqB9B,gBAAgB;IAqB9B,OAAO,KAAK,MAAM,GAMjB;IAEK,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC;IAU5D,MAAM,CACV,SAAS,EAAE,MAAM,EACjB,kBAAkB,EAAE,MAAM,EAC1B,OAAO,EAAE,iBAAiB,EAAE,GAC3B,OAAO,CAAC,MAAM,CAAC;IAWZ,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAU3E,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAWR,aAAa;IAuB3B,OAAO,CAAC,YAAY;CAarB"}
|
|
@@ -14,11 +14,7 @@ export class IndexedDbPersistenceAdapter {
|
|
|
14
14
|
objectStoreName;
|
|
15
15
|
lockManager;
|
|
16
16
|
lockName;
|
|
17
|
-
|
|
18
|
-
namespace;
|
|
19
|
-
ownership;
|
|
20
|
-
closePromise;
|
|
21
|
-
closed = false;
|
|
17
|
+
lifecycle = { kind: "idle" };
|
|
22
18
|
constructor(options = {}) {
|
|
23
19
|
this.factory = options.indexedDB ?? globalIndexedDb();
|
|
24
20
|
this.databaseName = options.databaseName ?? DEFAULT_DATABASE_NAME;
|
|
@@ -30,28 +26,39 @@ export class IndexedDbPersistenceAdapter {
|
|
|
30
26
|
}
|
|
31
27
|
async open(namespace) {
|
|
32
28
|
validatePersistenceNamespace(namespace);
|
|
33
|
-
if (this.
|
|
29
|
+
if (this.lifecycle.kind !== "idle") {
|
|
30
|
+
if (this.lifecycle.kind === "opening" || this.lifecycle.kind === "open") {
|
|
31
|
+
throw new BrowserPersistenceError("already_open", "persistence namespace is already open or opening");
|
|
32
|
+
}
|
|
34
33
|
throw new BrowserPersistenceError("closed", "persistence adapter is closed");
|
|
35
34
|
}
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
const completion = this.acquireResources(namespace);
|
|
36
|
+
this.lifecycle = { kind: "opening", completion };
|
|
37
|
+
try {
|
|
38
|
+
const resources = await completion;
|
|
39
|
+
// Close owns the pending acquisition once it transitions to closing.
|
|
40
|
+
if (this.lifecycle.kind !== "opening")
|
|
41
|
+
throw new BrowserPersistenceError("closed", "persistence adapter is closed");
|
|
42
|
+
this.lifecycle = { kind: "open", resources };
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
if (this.lifecycle.kind === "opening")
|
|
46
|
+
this.lifecycle = { kind: "idle" };
|
|
47
|
+
throw mapAdapterError(error);
|
|
38
48
|
}
|
|
39
|
-
|
|
40
|
-
|
|
49
|
+
}
|
|
50
|
+
async acquireResources(namespace) {
|
|
51
|
+
const ownership = await acquireOwnership(this.lockName(namespace), this.lockManager);
|
|
41
52
|
try {
|
|
42
|
-
|
|
43
|
-
|
|
53
|
+
const database = await openDatabase(this.factory, this.databaseName, this.manifestStoreName, this.bucketStoreName, this.objectStoreName);
|
|
54
|
+
return { database, namespace, ownership };
|
|
44
55
|
}
|
|
45
56
|
catch (error) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
catch {
|
|
53
|
-
// Preserve the database-open failure.
|
|
54
|
-
}
|
|
57
|
+
try {
|
|
58
|
+
await ownership.release();
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// Preserve the database-open failure.
|
|
55
62
|
}
|
|
56
63
|
throw mapAdapterError(error);
|
|
57
64
|
}
|
|
@@ -64,11 +71,7 @@ export class IndexedDbPersistenceAdapter {
|
|
|
64
71
|
};
|
|
65
72
|
}
|
|
66
73
|
async load(namespace) {
|
|
67
|
-
this.
|
|
68
|
-
const database = this.database;
|
|
69
|
-
if (database === undefined) {
|
|
70
|
-
throw new BrowserPersistenceError("persistence_error", "persistence database is not open");
|
|
71
|
-
}
|
|
74
|
+
const { database } = this.resourcesFor(namespace);
|
|
72
75
|
try {
|
|
73
76
|
const raw = await readNamespace(database, namespace, this.stores);
|
|
74
77
|
return raw === null ? null : normalizeSnapshot(raw);
|
|
@@ -78,13 +81,9 @@ export class IndexedDbPersistenceAdapter {
|
|
|
78
81
|
}
|
|
79
82
|
}
|
|
80
83
|
async commit(namespace, expectedGeneration, changes) {
|
|
81
|
-
this.
|
|
84
|
+
const { database } = this.resourcesFor(namespace);
|
|
82
85
|
validateGeneration(expectedGeneration);
|
|
83
86
|
const normalizedChanges = changes.map(normalizeChange);
|
|
84
|
-
const database = this.database;
|
|
85
|
-
if (database === undefined) {
|
|
86
|
-
throw new BrowserPersistenceError("persistence_error", "persistence database is not open");
|
|
87
|
-
}
|
|
88
87
|
try {
|
|
89
88
|
return await commitChanges(database, namespace, expectedGeneration, normalizedChanges, this.stores);
|
|
90
89
|
}
|
|
@@ -93,12 +92,8 @@ export class IndexedDbPersistenceAdapter {
|
|
|
93
92
|
}
|
|
94
93
|
}
|
|
95
94
|
async clear(namespace, expectedGeneration) {
|
|
96
|
-
this.
|
|
95
|
+
const { database } = this.resourcesFor(namespace);
|
|
97
96
|
validateGeneration(expectedGeneration);
|
|
98
|
-
const database = this.database;
|
|
99
|
-
if (database === undefined) {
|
|
100
|
-
throw new BrowserPersistenceError("persistence_error", "persistence database is not open");
|
|
101
|
-
}
|
|
102
97
|
try {
|
|
103
98
|
return await clearNamespace(database, namespace, expectedGeneration, this.stores);
|
|
104
99
|
}
|
|
@@ -106,48 +101,57 @@ export class IndexedDbPersistenceAdapter {
|
|
|
106
101
|
throw mapAdapterError(error);
|
|
107
102
|
}
|
|
108
103
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
104
|
+
close() {
|
|
105
|
+
const previous = this.lifecycle;
|
|
106
|
+
if (previous.kind === "closing" || previous.kind === "closed" || previous.kind === "failed")
|
|
107
|
+
return previous.completion;
|
|
108
|
+
const completion = Promise.resolve().then(() => this.closeInternal(previous)).then(() => { this.lifecycle = { kind: "closed", completion }; }, (error) => { this.lifecycle = { kind: "failed", completion }; throw mapAdapterError(error); });
|
|
109
|
+
this.lifecycle = { kind: "closing", completion };
|
|
110
|
+
return completion;
|
|
112
111
|
}
|
|
113
|
-
async closeInternal() {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
this.namespace = undefined;
|
|
119
|
-
this.ownership = undefined;
|
|
120
|
-
let firstError;
|
|
121
|
-
if (database !== undefined) {
|
|
112
|
+
async closeInternal(previous) {
|
|
113
|
+
if (previous.kind === "idle")
|
|
114
|
+
return;
|
|
115
|
+
let resources;
|
|
116
|
+
if (previous.kind === "opening") {
|
|
122
117
|
try {
|
|
123
|
-
|
|
124
|
-
}
|
|
125
|
-
catch (error) {
|
|
126
|
-
firstError = error;
|
|
118
|
+
resources = await previous.completion;
|
|
127
119
|
}
|
|
120
|
+
catch {
|
|
121
|
+
return;
|
|
122
|
+
} // Failed acquisition releases its own ownership.
|
|
128
123
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
124
|
+
else
|
|
125
|
+
resources = previous.resources;
|
|
126
|
+
let firstError;
|
|
127
|
+
try {
|
|
128
|
+
resources.database.close();
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
firstError = error;
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
await resources.ownership.release();
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
firstError ??= error;
|
|
136
138
|
}
|
|
137
139
|
if (firstError !== undefined) {
|
|
138
140
|
throw mapAdapterError(firstError);
|
|
139
141
|
}
|
|
140
142
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
+
resourcesFor(namespace) {
|
|
144
|
+
const state = this.lifecycle;
|
|
145
|
+
if (state.kind !== "open") {
|
|
146
|
+
if (state.kind === "idle" || state.kind === "opening") {
|
|
147
|
+
throw new BrowserPersistenceError("persistence_error", "persistence namespace is not open");
|
|
148
|
+
}
|
|
143
149
|
throw new BrowserPersistenceError("closed", "persistence adapter is closed");
|
|
144
150
|
}
|
|
145
|
-
if (
|
|
146
|
-
throw new BrowserPersistenceError("persistence_error", "persistence namespace is not open");
|
|
147
|
-
}
|
|
148
|
-
if (this.namespace !== namespace) {
|
|
151
|
+
if (state.resources.namespace !== namespace) {
|
|
149
152
|
throw new BrowserPersistenceError("persistence_error", "persistence namespace does not match");
|
|
150
153
|
}
|
|
154
|
+
return state.resources;
|
|
151
155
|
}
|
|
152
156
|
}
|
|
153
157
|
function globalIndexedDb() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"indexeddb-persistence.js","sourceRoot":"","sources":["../src/indexeddb-persistence.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EAMvB,4BAA4B,GAC7B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,cAAc,EACd,aAAa,EACb,YAAY,EACZ,aAAa,GAEd,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAE7E,MAAM,qBAAqB,GAAG,UAAU,CAAC;AACzC,MAAM,sBAAsB,GAAG,UAAU,CAAC;AAC1C,MAAM,oBAAoB,GAAG,SAAS,CAAC;AACvC,MAAM,oBAAoB,GAAG,SAAS,CAAC;AACvC,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"indexeddb-persistence.js","sourceRoot":"","sources":["../src/indexeddb-persistence.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chester-hill-solutions/stow-s3",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.7",
|
|
4
4
|
"description": "TypeScript client for the stow local S3-compatible dev bucket service",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -71,10 +71,10 @@
|
|
|
71
71
|
"@smithy/types": "^4.1.0"
|
|
72
72
|
},
|
|
73
73
|
"optionalDependencies": {
|
|
74
|
-
"@chester-hill-solutions/stow-s3-darwin-arm64": "0.3.
|
|
75
|
-
"@chester-hill-solutions/stow-s3-darwin-x64": "0.3.
|
|
76
|
-
"@chester-hill-solutions/stow-s3-linux-arm64": "0.3.
|
|
77
|
-
"@chester-hill-solutions/stow-s3-linux-x64": "0.3.
|
|
74
|
+
"@chester-hill-solutions/stow-s3-darwin-arm64": "0.3.7",
|
|
75
|
+
"@chester-hill-solutions/stow-s3-darwin-x64": "0.3.7",
|
|
76
|
+
"@chester-hill-solutions/stow-s3-linux-arm64": "0.3.7",
|
|
77
|
+
"@chester-hill-solutions/stow-s3-linux-x64": "0.3.7"
|
|
78
78
|
},
|
|
79
79
|
"devDependencies": {
|
|
80
80
|
"@aws-sdk/s3-request-presigner": "3.1160.0",
|
package/src/async-streams.ts
CHANGED
|
@@ -18,6 +18,11 @@ export interface AsyncOperationInvoker {
|
|
|
18
18
|
<T>(request: Record<string, unknown>, data?: Uint8Array, options?: WorkerCallOptions): Promise<T>;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
// Cleanup failure returns to open so callers can retry releasing the host token.
|
|
22
|
+
type ReadStreamState =
|
|
23
|
+
| { kind: "open" }
|
|
24
|
+
| { kind: "closing" | "closed"; completion: Promise<void> };
|
|
25
|
+
|
|
21
26
|
export class EmbeddedStreamOperations {
|
|
22
27
|
constructor(private readonly invoke: AsyncOperationInvoker) {}
|
|
23
28
|
|
|
@@ -55,17 +60,15 @@ export class EmbeddedStreamOperations {
|
|
|
55
60
|
const opened = await this.invoke<{ token: string; object: BridgeObject }>({ op: "openObjectRead", bucket, key });
|
|
56
61
|
const token = opened.token;
|
|
57
62
|
const object = fromBridgeObject(opened.object);
|
|
58
|
-
let
|
|
59
|
-
let closePromise: Promise<void> | undefined;
|
|
63
|
+
let state: ReadStreamState = { kind: "open" };
|
|
60
64
|
const close = () => {
|
|
61
|
-
if (
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
return closePromise;
|
|
65
|
+
if (state.kind !== "open") return state.completion;
|
|
66
|
+
const completion: Promise<void> = this.cleanup("closeObjectRead", token).then(
|
|
67
|
+
() => { state = { kind: "closed", completion }; },
|
|
68
|
+
(error: unknown) => { state = { kind: "open" }; throw error; },
|
|
69
|
+
);
|
|
70
|
+
state = { kind: "closing", completion };
|
|
71
|
+
return completion;
|
|
69
72
|
};
|
|
70
73
|
if (options.signal?.aborted) {
|
|
71
74
|
await close();
|
|
@@ -75,7 +78,7 @@ export class EmbeddedStreamOperations {
|
|
|
75
78
|
const chunks = (async function* () {
|
|
76
79
|
let read = 0;
|
|
77
80
|
try {
|
|
78
|
-
while (
|
|
81
|
+
while (state.kind === "open" && read < object.size) {
|
|
79
82
|
options.signal?.throwIfAborted();
|
|
80
83
|
const length = Math.min(EMBEDDED_STREAM_CHUNK_BYTES, object.size - read);
|
|
81
84
|
const result = await invoke<{ eof: boolean; data?: Uint8Array }>({ op: "readObjectChunk", token, length }, undefined, options);
|
|
@@ -8,13 +8,25 @@ interface DatabaseOpenOptions {
|
|
|
8
8
|
}
|
|
9
9
|
export function openIndexedDatabase(factory: IDBFactory, options: DatabaseOpenOptions): Promise<IDBDatabase> {
|
|
10
10
|
return new Promise((resolve, reject) => {
|
|
11
|
+
let state: "pending" | "opened" | "rejected" = "pending";
|
|
11
12
|
const request = factory.open(options.name, options.version);
|
|
12
|
-
request.onupgradeneeded = () =>
|
|
13
|
+
request.onupgradeneeded = () => {
|
|
14
|
+
if (state !== "pending") { request.transaction?.abort(); return; }
|
|
15
|
+
options.upgrade(request.result);
|
|
16
|
+
};
|
|
13
17
|
request.onsuccess = () => {
|
|
18
|
+
if (state !== "pending") { request.result.close(); return; }
|
|
19
|
+
state = "opened";
|
|
14
20
|
request.result.onversionchange = () => request.result.close();
|
|
15
21
|
resolve(request.result);
|
|
16
22
|
};
|
|
17
|
-
request.onerror = () =>
|
|
18
|
-
|
|
23
|
+
request.onerror = () => {
|
|
24
|
+
state = "rejected";
|
|
25
|
+
reject(request.error ?? new Error(options.failedMessage));
|
|
26
|
+
};
|
|
27
|
+
request.onblocked = () => {
|
|
28
|
+
state = "rejected";
|
|
29
|
+
reject(new BrowserPersistenceError("persistence_denied", options.blockedMessage));
|
|
30
|
+
};
|
|
19
31
|
});
|
|
20
32
|
}
|
|
@@ -26,6 +26,22 @@ interface Ownership {
|
|
|
26
26
|
release(): Promise<void>;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
interface OpenPersistence {
|
|
30
|
+
database: IDBDatabase;
|
|
31
|
+
namespace: string;
|
|
32
|
+
ownership: Ownership;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
type ActiveLifecycle =
|
|
36
|
+
| { kind: "idle" }
|
|
37
|
+
| { kind: "opening"; completion: Promise<OpenPersistence> }
|
|
38
|
+
| { kind: "open"; resources: OpenPersistence };
|
|
39
|
+
|
|
40
|
+
type AdapterLifecycle = ActiveLifecycle
|
|
41
|
+
| { kind: "closing"; completion: Promise<void> }
|
|
42
|
+
| { kind: "closed"; completion: Promise<void> }
|
|
43
|
+
| { kind: "failed"; completion: Promise<void> };
|
|
44
|
+
|
|
29
45
|
export class IndexedDbPersistenceAdapter implements PersistenceAdapter {
|
|
30
46
|
private readonly factory: IDBFactory | undefined;
|
|
31
47
|
private readonly databaseName: string;
|
|
@@ -34,11 +50,7 @@ export class IndexedDbPersistenceAdapter implements PersistenceAdapter {
|
|
|
34
50
|
private readonly objectStoreName: string;
|
|
35
51
|
private readonly lockManager: LockManagerLike | undefined;
|
|
36
52
|
private readonly lockName: (namespace: string) => string;
|
|
37
|
-
private
|
|
38
|
-
private namespace: string | undefined;
|
|
39
|
-
private ownership: Ownership | undefined;
|
|
40
|
-
private closePromise: Promise<void> | undefined;
|
|
41
|
-
private closed = false;
|
|
53
|
+
private lifecycle: AdapterLifecycle = { kind: "idle" };
|
|
42
54
|
|
|
43
55
|
constructor(options: IndexedDbPersistenceAdapterOptions = {}) {
|
|
44
56
|
this.factory = options.indexedDB ?? globalIndexedDb();
|
|
@@ -52,32 +64,41 @@ export class IndexedDbPersistenceAdapter implements PersistenceAdapter {
|
|
|
52
64
|
|
|
53
65
|
async open(namespace: string): Promise<void> {
|
|
54
66
|
validatePersistenceNamespace(namespace);
|
|
55
|
-
if (this.
|
|
67
|
+
if (this.lifecycle.kind !== "idle") {
|
|
68
|
+
if (this.lifecycle.kind === "opening" || this.lifecycle.kind === "open") {
|
|
69
|
+
throw new BrowserPersistenceError("already_open", "persistence namespace is already open or opening");
|
|
70
|
+
}
|
|
56
71
|
throw new BrowserPersistenceError("closed", "persistence adapter is closed");
|
|
57
72
|
}
|
|
58
|
-
|
|
59
|
-
|
|
73
|
+
const completion = this.acquireResources(namespace);
|
|
74
|
+
this.lifecycle = { kind: "opening", completion };
|
|
75
|
+
try {
|
|
76
|
+
const resources = await completion;
|
|
77
|
+
// Close owns the pending acquisition once it transitions to closing.
|
|
78
|
+
if (this.lifecycle.kind !== "opening") throw new BrowserPersistenceError("closed", "persistence adapter is closed");
|
|
79
|
+
this.lifecycle = { kind: "open", resources };
|
|
80
|
+
} catch (error) {
|
|
81
|
+
if (this.lifecycle.kind === "opening") this.lifecycle = { kind: "idle" };
|
|
82
|
+
throw mapAdapterError(error);
|
|
60
83
|
}
|
|
61
|
-
|
|
62
|
-
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private async acquireResources(namespace: string): Promise<OpenPersistence> {
|
|
87
|
+
const ownership = await acquireOwnership(this.lockName(namespace), this.lockManager);
|
|
63
88
|
try {
|
|
64
|
-
|
|
89
|
+
const database = await openDatabase(
|
|
65
90
|
this.factory,
|
|
66
91
|
this.databaseName,
|
|
67
92
|
this.manifestStoreName,
|
|
68
93
|
this.bucketStoreName,
|
|
69
94
|
this.objectStoreName,
|
|
70
95
|
);
|
|
71
|
-
|
|
96
|
+
return { database, namespace, ownership };
|
|
72
97
|
} catch (error) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
await ownership.release();
|
|
78
|
-
} catch {
|
|
79
|
-
// Preserve the database-open failure.
|
|
80
|
-
}
|
|
98
|
+
try {
|
|
99
|
+
await ownership.release();
|
|
100
|
+
} catch {
|
|
101
|
+
// Preserve the database-open failure.
|
|
81
102
|
}
|
|
82
103
|
throw mapAdapterError(error);
|
|
83
104
|
}
|
|
@@ -92,11 +113,7 @@ export class IndexedDbPersistenceAdapter implements PersistenceAdapter {
|
|
|
92
113
|
}
|
|
93
114
|
|
|
94
115
|
async load(namespace: string): Promise<PersistenceSnapshot | null> {
|
|
95
|
-
this.
|
|
96
|
-
const database = this.database;
|
|
97
|
-
if (database === undefined) {
|
|
98
|
-
throw new BrowserPersistenceError("persistence_error", "persistence database is not open");
|
|
99
|
-
}
|
|
116
|
+
const { database } = this.resourcesFor(namespace);
|
|
100
117
|
try {
|
|
101
118
|
const raw = await readNamespace(database, namespace, this.stores);
|
|
102
119
|
return raw === null ? null : normalizeSnapshot(raw);
|
|
@@ -110,13 +127,9 @@ export class IndexedDbPersistenceAdapter implements PersistenceAdapter {
|
|
|
110
127
|
expectedGeneration: number,
|
|
111
128
|
changes: PersistenceChange[],
|
|
112
129
|
): Promise<number> {
|
|
113
|
-
this.
|
|
130
|
+
const { database } = this.resourcesFor(namespace);
|
|
114
131
|
validateGeneration(expectedGeneration);
|
|
115
132
|
const normalizedChanges = changes.map(normalizeChange);
|
|
116
|
-
const database = this.database;
|
|
117
|
-
if (database === undefined) {
|
|
118
|
-
throw new BrowserPersistenceError("persistence_error", "persistence database is not open");
|
|
119
|
-
}
|
|
120
133
|
try {
|
|
121
134
|
return await commitChanges(database, namespace, expectedGeneration, normalizedChanges, this.stores);
|
|
122
135
|
} catch (error) {
|
|
@@ -125,12 +138,8 @@ export class IndexedDbPersistenceAdapter implements PersistenceAdapter {
|
|
|
125
138
|
}
|
|
126
139
|
|
|
127
140
|
async clear(namespace: string, expectedGeneration: number): Promise<number> {
|
|
128
|
-
this.
|
|
141
|
+
const { database } = this.resourcesFor(namespace);
|
|
129
142
|
validateGeneration(expectedGeneration);
|
|
130
|
-
const database = this.database;
|
|
131
|
-
if (database === undefined) {
|
|
132
|
-
throw new BrowserPersistenceError("persistence_error", "persistence database is not open");
|
|
133
|
-
}
|
|
134
143
|
try {
|
|
135
144
|
return await clearNamespace(database, namespace, expectedGeneration, this.stores);
|
|
136
145
|
} catch (error) {
|
|
@@ -138,48 +147,52 @@ export class IndexedDbPersistenceAdapter implements PersistenceAdapter {
|
|
|
138
147
|
}
|
|
139
148
|
}
|
|
140
149
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
return
|
|
150
|
+
close(): Promise<void> {
|
|
151
|
+
const previous = this.lifecycle;
|
|
152
|
+
if (previous.kind === "closing" || previous.kind === "closed" || previous.kind === "failed") return previous.completion;
|
|
153
|
+
const completion: Promise<void> = Promise.resolve().then(() => this.closeInternal(previous)).then(
|
|
154
|
+
() => { this.lifecycle = { kind: "closed", completion }; },
|
|
155
|
+
(error: unknown) => { this.lifecycle = { kind: "failed", completion }; throw mapAdapterError(error); },
|
|
156
|
+
);
|
|
157
|
+
this.lifecycle = { kind: "closing", completion };
|
|
158
|
+
return completion;
|
|
144
159
|
}
|
|
145
160
|
|
|
146
|
-
private async closeInternal(): Promise<void> {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
161
|
+
private async closeInternal(previous: ActiveLifecycle): Promise<void> {
|
|
162
|
+
if (previous.kind === "idle") return;
|
|
163
|
+
let resources: OpenPersistence;
|
|
164
|
+
if (previous.kind === "opening") {
|
|
165
|
+
try { resources = await previous.completion; }
|
|
166
|
+
catch { return; } // Failed acquisition releases its own ownership.
|
|
167
|
+
} else resources = previous.resources;
|
|
153
168
|
let firstError: unknown;
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
firstError = error;
|
|
159
|
-
}
|
|
169
|
+
try {
|
|
170
|
+
resources.database.close();
|
|
171
|
+
} catch (error) {
|
|
172
|
+
firstError = error;
|
|
160
173
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
firstError ??= error;
|
|
166
|
-
}
|
|
174
|
+
try {
|
|
175
|
+
await resources.ownership.release();
|
|
176
|
+
} catch (error) {
|
|
177
|
+
firstError ??= error;
|
|
167
178
|
}
|
|
168
179
|
if (firstError !== undefined) {
|
|
169
180
|
throw mapAdapterError(firstError);
|
|
170
181
|
}
|
|
171
182
|
}
|
|
172
183
|
|
|
173
|
-
private
|
|
174
|
-
|
|
184
|
+
private resourcesFor(namespace: string): OpenPersistence {
|
|
185
|
+
const state = this.lifecycle;
|
|
186
|
+
if (state.kind !== "open") {
|
|
187
|
+
if (state.kind === "idle" || state.kind === "opening") {
|
|
188
|
+
throw new BrowserPersistenceError("persistence_error", "persistence namespace is not open");
|
|
189
|
+
}
|
|
175
190
|
throw new BrowserPersistenceError("closed", "persistence adapter is closed");
|
|
176
191
|
}
|
|
177
|
-
if (
|
|
178
|
-
throw new BrowserPersistenceError("persistence_error", "persistence namespace is not open");
|
|
179
|
-
}
|
|
180
|
-
if (this.namespace !== namespace) {
|
|
192
|
+
if (state.resources.namespace !== namespace) {
|
|
181
193
|
throw new BrowserPersistenceError("persistence_error", "persistence namespace does not match");
|
|
182
194
|
}
|
|
195
|
+
return state.resources;
|
|
183
196
|
}
|
|
184
197
|
}
|
|
185
198
|
|