@proappstore/sdk 1.16.95 → 1.16.96
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 +6 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/offline/downloads.d.ts +44 -0
- package/dist/offline/downloads.d.ts.map +1 -0
- package/dist/offline/downloads.js +221 -0
- package/dist/offline/downloads.js.map +1 -0
- package/dist/offline/jobs.d.ts +78 -0
- package/dist/offline/jobs.d.ts.map +1 -0
- package/dist/offline/jobs.js +204 -0
- package/dist/offline/jobs.js.map +1 -0
- package/dist/offline/locks.d.ts +6 -0
- package/dist/offline/locks.d.ts.map +1 -0
- package/dist/offline/locks.js +47 -0
- package/dist/offline/locks.js.map +1 -0
- package/dist/offline/store.d.ts +94 -0
- package/dist/offline/store.d.ts.map +1 -0
- package/dist/offline/store.js +260 -0
- package/dist/offline/store.js.map +1 -0
- package/dist/offline.d.ts +8 -0
- package/dist/offline.d.ts.map +1 -0
- package/dist/offline.js +5 -0
- package/dist/offline.js.map +1 -0
- package/dist/sdk-version.js +1 -1
- package/dist/shell-resilience.d.ts +3 -1
- package/dist/shell-resilience.d.ts.map +1 -1
- package/dist/shell-resilience.js +2 -2
- package/dist/shell-resilience.js.map +1 -1
- package/dist/shell.d.ts +11 -2
- package/dist/shell.d.ts.map +1 -1
- package/dist/shell.js +30 -11
- package/dist/shell.js.map +1 -1
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -29,6 +29,12 @@ Options:
|
|
|
29
29
|
| `proApiBase` | `https://api.proappstore.online` | Platform API base URL |
|
|
30
30
|
| `dataApiBase` | `https://data-{appId}.proappstore.online`, or `/.pas/data` in `platform-cookie` mode | Per-app data worker URL |
|
|
31
31
|
|
|
32
|
+
## Offline content (opt-in)
|
|
33
|
+
|
|
34
|
+
Import `createOfflineStore`, `createDownloadManager`, `createLocalJobs` and `createSyncQueue` from `@proappstore/sdk/offline`. Local records and blobs use transactional IndexedDB, explicit device/account scope and retention; public model downloads require version, size and SHA-256 pins. Durable jobs recover checkpoints, and the optional queue flushes only explicitly idempotent operations. Account access callbacks are local policy, not server authorization.
|
|
35
|
+
|
|
36
|
+
`<AppShell access="local" app={app}>` renders local features without inventing an authenticated user; the default remains authenticated. Keep auth responses out of service-worker caches and checkpoint active work before updates. See the [offline guide](https://docs.proappstore.online/offline-apps/) for API usage, retention, quota, update and browser/device limits.
|
|
37
|
+
|
|
32
38
|
## Types
|
|
33
39
|
|
|
34
40
|
```ts
|
package/dist/index.d.ts
CHANGED
|
@@ -58,7 +58,7 @@ export { AdminConsole, AdminErrorBoundary, useAdminContext, useAction } from './
|
|
|
58
58
|
export type { AdminConsoleProps, AdminContextValue, UseActionOptions, ActionInvoker } from './admin.js';
|
|
59
59
|
export type { ProProviderProps } from './provider.js';
|
|
60
60
|
export { AppShell, ProShell } from './shell.js';
|
|
61
|
-
export type { AppShellProps, AppShellRenderContext, AppShellNavContext, ProShellProps, ProShellRenderContext, ProShellNavContext, MenuItem, ShellErrorContext } from './shell.js';
|
|
61
|
+
export type { AppShellProps, AppShellRenderContext, LocalAppShellRenderContext, AppShellNavContext, ProShellProps, ProShellRenderContext, ProShellNavContext, MenuItem, ShellErrorContext } from './shell.js';
|
|
62
62
|
export { useToast, useOnline } from './shell-resilience.js';
|
|
63
63
|
export type { ToastApi, ToastOptions, ShellToastVariant } from './shell-resilience.js';
|
|
64
64
|
export { PageHeader, useDocumentTitle } from './page.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { type OfflineStore } from './store.js';
|
|
2
|
+
export interface DownloadManifest {
|
|
3
|
+
id: string;
|
|
4
|
+
version: string;
|
|
5
|
+
url: string;
|
|
6
|
+
sha256: string;
|
|
7
|
+
bytes: number;
|
|
8
|
+
}
|
|
9
|
+
export interface DownloadProgress {
|
|
10
|
+
loaded: number;
|
|
11
|
+
total: number;
|
|
12
|
+
resumed: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface DownloadStatus {
|
|
15
|
+
state: 'partial' | 'ready';
|
|
16
|
+
loaded: number;
|
|
17
|
+
total: number;
|
|
18
|
+
}
|
|
19
|
+
export interface DownloadOptions {
|
|
20
|
+
signal?: AbortSignal;
|
|
21
|
+
onProgress?: (progress: DownloadProgress) => void;
|
|
22
|
+
}
|
|
23
|
+
export interface DownloadManagerOptions {
|
|
24
|
+
fetch?: typeof fetch;
|
|
25
|
+
maxBytes?: number;
|
|
26
|
+
checkpointBytes?: number;
|
|
27
|
+
}
|
|
28
|
+
export declare class DownloadManager {
|
|
29
|
+
private readonly store;
|
|
30
|
+
private readonly fetcher;
|
|
31
|
+
private readonly maxBytes;
|
|
32
|
+
private readonly checkpointBytes;
|
|
33
|
+
constructor(store: OfflineStore, options?: DownloadManagerOptions);
|
|
34
|
+
status(manifest: DownloadManifest): Promise<DownloadStatus | null>;
|
|
35
|
+
get(manifest: DownloadManifest): Promise<Blob | null>;
|
|
36
|
+
private verify;
|
|
37
|
+
private readReady;
|
|
38
|
+
remove(manifest: DownloadManifest): Promise<void>;
|
|
39
|
+
private removeByKey;
|
|
40
|
+
download(manifest: DownloadManifest, options?: DownloadOptions): Promise<Blob>;
|
|
41
|
+
private transfer;
|
|
42
|
+
}
|
|
43
|
+
export declare function createDownloadManager(store: OfflineStore, options?: DownloadManagerOptions): DownloadManager;
|
|
44
|
+
//# sourceMappingURL=downloads.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"downloads.d.ts","sourceRoot":"","sources":["../../src/offline/downloads.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAM7D,MAAM,WAAW,gBAAgB;IAAG,EAAE,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE;AAC7G,MAAM,WAAW,gBAAgB;IAAG,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE;AACrF,MAAM,WAAW,cAAc;IAAG,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE;AAE7F,MAAM,WAAW,eAAe;IAAG,MAAM,CAAC,EAAE,WAAW,CAAC;IAAC,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,gBAAgB,KAAK,IAAI,CAAA;CAAE;AAC5G,MAAM,WAAW,sBAAsB;IAAG,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE;AAsB7G,qBAAa,eAAe;IAId,OAAO,CAAC,QAAQ,CAAC,KAAK;IAHlC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAe;IACvC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;gBACZ,KAAK,EAAE,YAAY,EAAE,OAAO,GAAE,sBAA2B;IAShF,MAAM,CAAC,QAAQ,EAAE,gBAAgB,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAKlE,GAAG,CAAC,QAAQ,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;YAK7C,MAAM;YAON,SAAS;IAUjB,MAAM,CAAC,QAAQ,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAKvD,OAAO,CAAC,WAAW;IAUb,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;YAK1E,QAAQ;CAgFvB;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,YAAY,EAAE,OAAO,GAAE,sBAA2B,GAAG,eAAe,CAAgD"}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { OfflineError } from './store.js';
|
|
2
|
+
import { offlineLock } from './locks.js';
|
|
3
|
+
function assetKey(manifest, maxBytes) {
|
|
4
|
+
if (!manifest.id || manifest.id.length > 128 || !manifest.version || manifest.version.length > 128 ||
|
|
5
|
+
!/^[a-f0-9]{64}$/i.test(manifest.sha256) || !Number.isSafeInteger(manifest.bytes) || manifest.bytes <= 0 || manifest.bytes > maxBytes) {
|
|
6
|
+
throw new OfflineError('invalid', 'Download requires an id, version, SHA-256 and byte size within the configured limit.');
|
|
7
|
+
}
|
|
8
|
+
let url;
|
|
9
|
+
try {
|
|
10
|
+
url = new URL(manifest.url);
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
throw new OfflineError('invalid', 'Download URL must be absolute.');
|
|
14
|
+
}
|
|
15
|
+
const local = ['localhost', '127.0.0.1', '[::1]'].includes(url.hostname);
|
|
16
|
+
if ((url.protocol !== 'https:' && !(url.protocol === 'http:' && local)) || url.username || url.password ||
|
|
17
|
+
url.pathname.startsWith('/.pas/') || url.hostname === 'api.proappstore.online' || url.hostname.startsWith('data-')) {
|
|
18
|
+
throw new OfflineError('invalid', 'Only public immutable assets may use the download manager; authenticated endpoints are excluded.');
|
|
19
|
+
}
|
|
20
|
+
return JSON.stringify([manifest.id, manifest.version, manifest.sha256.toLowerCase(), manifest.bytes, url.href]);
|
|
21
|
+
}
|
|
22
|
+
const partKey = (id, n) => `download-part:${id}:${n}`;
|
|
23
|
+
const readyKey = (id) => `download-ready:${id}`;
|
|
24
|
+
const abort = (signal) => { if (signal?.aborted)
|
|
25
|
+
throw new DOMException('Download cancelled.', 'AbortError'); };
|
|
26
|
+
const strongEtag = (value) => value && /^"[^"\r\n]+"$/.test(value) ? value : null;
|
|
27
|
+
export class DownloadManager {
|
|
28
|
+
store;
|
|
29
|
+
fetcher;
|
|
30
|
+
maxBytes;
|
|
31
|
+
checkpointBytes;
|
|
32
|
+
constructor(store, options = {}) {
|
|
33
|
+
this.store = store;
|
|
34
|
+
if (!options.fetch && typeof globalThis.fetch !== 'function')
|
|
35
|
+
throw new OfflineError('unavailable', 'Fetch is unavailable on this device.');
|
|
36
|
+
this.fetcher = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
37
|
+
this.maxBytes = options.maxBytes ?? 512 * 1024 * 1024;
|
|
38
|
+
this.checkpointBytes = options.checkpointBytes ?? 1024 * 1024;
|
|
39
|
+
if (!Number.isSafeInteger(this.maxBytes) || this.maxBytes <= 0 || !Number.isSafeInteger(this.checkpointBytes) || this.checkpointBytes <= 0) {
|
|
40
|
+
throw new OfflineError('invalid', 'Download byte limits must be positive integers.');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
async status(manifest) {
|
|
44
|
+
manifest = Object.freeze({ ...manifest });
|
|
45
|
+
const row = await this.store.read('downloads', assetKey(manifest, this.maxBytes));
|
|
46
|
+
return row ? { state: row.state, loaded: row.loaded, total: row.total } : null;
|
|
47
|
+
}
|
|
48
|
+
async get(manifest) {
|
|
49
|
+
manifest = Object.freeze({ ...manifest });
|
|
50
|
+
const id = assetKey(manifest, this.maxBytes);
|
|
51
|
+
return this.store.runWork(() => offlineLock(`${this.store.databaseName}:${id}`, () => this.readReady(id, manifest), true));
|
|
52
|
+
}
|
|
53
|
+
async verify(blob, manifest) {
|
|
54
|
+
if (!globalThis.crypto?.subtle)
|
|
55
|
+
throw new OfflineError('unavailable', 'SHA-256 verification requires a secure browser context.');
|
|
56
|
+
const digest = await crypto.subtle.digest('SHA-256', await blob.arrayBuffer());
|
|
57
|
+
const hash = Array.from(new Uint8Array(digest), (n) => n.toString(16).padStart(2, '0')).join('');
|
|
58
|
+
this.store.assertAccess();
|
|
59
|
+
if (blob.size !== manifest.bytes || hash !== manifest.sha256.toLowerCase())
|
|
60
|
+
throw new OfflineError('integrity', 'Downloaded bytes do not match the pinned model.');
|
|
61
|
+
}
|
|
62
|
+
async readReady(id, manifest) {
|
|
63
|
+
const row = await this.store.read('downloads', id);
|
|
64
|
+
if (row?.state !== 'ready')
|
|
65
|
+
return null;
|
|
66
|
+
const blob = await this.store.read('blobs', readyKey(id));
|
|
67
|
+
try {
|
|
68
|
+
if (!blob)
|
|
69
|
+
throw new OfflineError('integrity', 'The downloaded asset is missing.');
|
|
70
|
+
await this.verify(blob, manifest);
|
|
71
|
+
return blob;
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
await this.removeByKey(id);
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async remove(manifest) {
|
|
79
|
+
manifest = Object.freeze({ ...manifest });
|
|
80
|
+
const id = assetKey(manifest, this.maxBytes);
|
|
81
|
+
return this.store.runWork(() => offlineLock(`${this.store.databaseName}:${id}`, () => this.removeByKey(id), true));
|
|
82
|
+
}
|
|
83
|
+
removeByKey(id) {
|
|
84
|
+
return this.store.transaction(['downloads', 'blobs'], 'readwrite', (tx, done) => {
|
|
85
|
+
const request = tx.objectStore('downloads').get(id);
|
|
86
|
+
request.onsuccess = () => {
|
|
87
|
+
const row = request.result;
|
|
88
|
+
for (let n = 0; n < (row?.chunks ?? 0); n++)
|
|
89
|
+
tx.objectStore('blobs').delete(partKey(id, n));
|
|
90
|
+
tx.objectStore('blobs').delete(readyKey(id));
|
|
91
|
+
tx.objectStore('downloads').delete(id);
|
|
92
|
+
done(undefined);
|
|
93
|
+
};
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
async download(manifest, options = {}) {
|
|
97
|
+
manifest = Object.freeze({ ...manifest });
|
|
98
|
+
const id = assetKey(manifest, this.maxBytes);
|
|
99
|
+
return this.store.runWork(() => offlineLock(`${this.store.databaseName}:${id}`, () => this.transfer(id, manifest, options), true, options.signal ? { signal: options.signal } : {}), options.signal);
|
|
100
|
+
}
|
|
101
|
+
async transfer(id, manifest, options) {
|
|
102
|
+
abort(options.signal);
|
|
103
|
+
const cached = await this.readReady(id, manifest);
|
|
104
|
+
if (cached) {
|
|
105
|
+
abort(options.signal);
|
|
106
|
+
options.onProgress?.({ loaded: cached.size, total: cached.size, resumed: false });
|
|
107
|
+
return cached;
|
|
108
|
+
}
|
|
109
|
+
let row = await this.store.read('downloads', id);
|
|
110
|
+
if (row && row.loaded < manifest.bytes && !row.etag) {
|
|
111
|
+
await this.removeByKey(id);
|
|
112
|
+
row = null;
|
|
113
|
+
}
|
|
114
|
+
let current = row ?? { state: 'partial', loaded: 0, total: manifest.bytes, chunks: 0, etag: null };
|
|
115
|
+
let resumed = current.loaded > 0;
|
|
116
|
+
let pending = [];
|
|
117
|
+
let pendingBytes = 0;
|
|
118
|
+
const checkpoint = async () => {
|
|
119
|
+
if (!pendingBytes)
|
|
120
|
+
return;
|
|
121
|
+
const blob = new Blob(pending);
|
|
122
|
+
const next = { ...current, loaded: current.loaded + pendingBytes, chunks: current.chunks + 1 };
|
|
123
|
+
await this.store.transaction(['downloads', 'blobs'], 'readwrite', (tx, done) => {
|
|
124
|
+
tx.objectStore('blobs').put(blob, partKey(id, current.chunks));
|
|
125
|
+
tx.objectStore('downloads').put(next, id);
|
|
126
|
+
done(undefined);
|
|
127
|
+
});
|
|
128
|
+
current = next;
|
|
129
|
+
pending = [];
|
|
130
|
+
pendingBytes = 0;
|
|
131
|
+
};
|
|
132
|
+
try {
|
|
133
|
+
if (current.loaded < manifest.bytes) {
|
|
134
|
+
const headers = new Headers();
|
|
135
|
+
if (current.loaded && current.etag) {
|
|
136
|
+
headers.set('Range', `bytes=${current.loaded}-`);
|
|
137
|
+
headers.set('If-Range', current.etag);
|
|
138
|
+
}
|
|
139
|
+
const response = await this.fetcher(manifest.url, { headers, credentials: 'omit', redirect: 'error', cache: 'no-store', ...(options.signal ? { signal: options.signal } : {}) });
|
|
140
|
+
if (response.status === 200 && current.loaded) {
|
|
141
|
+
resumed = false;
|
|
142
|
+
await this.removeByKey(id);
|
|
143
|
+
current = { state: 'partial', loaded: 0, total: manifest.bytes, chunks: 0, etag: null };
|
|
144
|
+
}
|
|
145
|
+
else if (response.status === 206) {
|
|
146
|
+
const range = /^bytes (\d+)-(\d+)\/(\d+)$/.exec(response.headers.get('Content-Range') ?? '');
|
|
147
|
+
if (!current.loaded || !current.etag || strongEtag(response.headers.get('ETag')) !== current.etag || !range ||
|
|
148
|
+
Number(range[1]) !== current.loaded || Number(range[2]) !== manifest.bytes - 1 || Number(range[3]) !== manifest.bytes) {
|
|
149
|
+
await response.body?.cancel();
|
|
150
|
+
await this.removeByKey(id);
|
|
151
|
+
throw new OfflineError('integrity', 'The resumed response does not match the saved asset version and range.');
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
else if (response.status !== 200) {
|
|
155
|
+
await response.body?.cancel();
|
|
156
|
+
throw new OfflineError('network', `Download failed with HTTP ${response.status}.`);
|
|
157
|
+
}
|
|
158
|
+
current.etag = strongEtag(response.headers.get('ETag'));
|
|
159
|
+
if (!response.body)
|
|
160
|
+
throw new OfflineError('network', 'Download has no response body.');
|
|
161
|
+
const reader = response.body.getReader();
|
|
162
|
+
try {
|
|
163
|
+
for (;;) {
|
|
164
|
+
abort(options.signal);
|
|
165
|
+
this.store.assertAccess();
|
|
166
|
+
const result = await reader.read();
|
|
167
|
+
if (result.done)
|
|
168
|
+
break;
|
|
169
|
+
if (current.loaded + pendingBytes + result.value.byteLength > manifest.bytes)
|
|
170
|
+
throw new OfflineError('integrity', 'Download exceeds its declared byte size.');
|
|
171
|
+
// Store independent chunks, never rewrite the already downloaded prefix.
|
|
172
|
+
pending.push(result.value.slice().buffer);
|
|
173
|
+
pendingBytes += result.value.byteLength;
|
|
174
|
+
if (pendingBytes >= this.checkpointBytes)
|
|
175
|
+
await checkpoint();
|
|
176
|
+
options.onProgress?.({ loaded: current.loaded + pendingBytes, total: manifest.bytes, resumed });
|
|
177
|
+
}
|
|
178
|
+
await checkpoint();
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
await reader.cancel().catch(() => { });
|
|
182
|
+
if (!(error instanceof OfflineError && error.code === 'integrity'))
|
|
183
|
+
await checkpoint();
|
|
184
|
+
throw error;
|
|
185
|
+
}
|
|
186
|
+
finally {
|
|
187
|
+
reader.releaseLock();
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
abort(options.signal);
|
|
191
|
+
if (current.loaded !== manifest.bytes)
|
|
192
|
+
throw new OfflineError('network', 'Download ended before the declared byte size. Retry to resume.');
|
|
193
|
+
const parts = [];
|
|
194
|
+
for (let n = 0; n < current.chunks; n++) {
|
|
195
|
+
const part = await this.store.read('blobs', partKey(id, n));
|
|
196
|
+
if (!part)
|
|
197
|
+
throw new OfflineError('integrity', 'A saved download chunk is missing.');
|
|
198
|
+
parts.push(part);
|
|
199
|
+
}
|
|
200
|
+
const complete = new Blob(parts, { type: 'application/octet-stream' });
|
|
201
|
+
await this.verify(complete, manifest);
|
|
202
|
+
abort(options.signal);
|
|
203
|
+
await this.store.transaction(['downloads', 'blobs'], 'readwrite', (tx, done) => {
|
|
204
|
+
tx.objectStore('blobs').put(complete, readyKey(id));
|
|
205
|
+
for (let n = 0; n < current.chunks; n++)
|
|
206
|
+
tx.objectStore('blobs').delete(partKey(id, n));
|
|
207
|
+
tx.objectStore('downloads').put({ ...current, state: 'ready', chunks: 0 }, id);
|
|
208
|
+
done(undefined);
|
|
209
|
+
});
|
|
210
|
+
options.onProgress?.({ loaded: manifest.bytes, total: manifest.bytes, resumed });
|
|
211
|
+
return complete;
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
if (error instanceof OfflineError && error.code === 'integrity')
|
|
215
|
+
await this.removeByKey(id);
|
|
216
|
+
throw error;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
export function createDownloadManager(store, options = {}) { return new DownloadManager(store, options); }
|
|
221
|
+
//# sourceMappingURL=downloads.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"downloads.js","sourceRoot":"","sources":["../../src/offline/downloads.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAqB,MAAM,YAAY,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAYzC,SAAS,QAAQ,CAAC,QAA0B,EAAE,QAAgB;IAC5D,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,QAAQ,CAAC,EAAE,CAAC,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,IAAI,QAAQ,CAAC,OAAO,CAAC,MAAM,GAAG,GAAG;QAC9F,CAAC,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,QAAQ,CAAC,KAAK,IAAI,CAAC,IAAI,QAAQ,CAAC,KAAK,GAAG,QAAQ,EAAE,CAAC;QAC1I,MAAM,IAAI,YAAY,CAAC,SAAS,EAAE,sFAAsF,CAAC,CAAC;IAC5H,CAAC;IACD,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QAAC,GAAG,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,MAAM,IAAI,YAAY,CAAC,SAAS,EAAE,gCAAgC,CAAC,CAAC;IAAC,CAAC;IACnH,MAAM,KAAK,GAAG,CAAC,WAAW,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACzE,IAAI,CAAC,GAAG,CAAC,QAAQ,KAAK,QAAQ,IAAI,CAAC,CAAC,GAAG,CAAC,QAAQ,KAAK,OAAO,IAAI,KAAK,CAAC,CAAC,IAAI,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,QAAQ;QACnG,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,QAAQ,KAAK,wBAAwB,IAAI,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QACvH,MAAM,IAAI,YAAY,CAAC,SAAS,EAAE,kGAAkG,CAAC,CAAC;IACxI,CAAC;IACD,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE,EAAE,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;AAClH,CAAC;AAED,MAAM,OAAO,GAAG,CAAC,EAAU,EAAE,CAAS,EAAE,EAAE,CAAC,iBAAiB,EAAE,IAAI,CAAC,EAAE,CAAC;AACtE,MAAM,QAAQ,GAAG,CAAC,EAAU,EAAE,EAAE,CAAC,kBAAkB,EAAE,EAAE,CAAC;AACxD,MAAM,KAAK,GAAG,CAAC,MAAoB,EAAE,EAAE,GAAG,IAAI,MAAM,EAAE,OAAO;IAAE,MAAM,IAAI,YAAY,CAAC,qBAAqB,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;AAC9H,MAAM,UAAU,GAAG,CAAC,KAAoB,EAAE,EAAE,CAAC,KAAK,IAAI,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;AAEjG,MAAM,OAAO,eAAe;IAIG;IAHZ,OAAO,CAAe;IACtB,QAAQ,CAAS;IACjB,eAAe,CAAS;IACzC,YAA6B,KAAmB,EAAE,UAAkC,EAAE;QAAzD,UAAK,GAAL,KAAK,CAAc;QAC9C,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,OAAO,UAAU,CAAC,KAAK,KAAK,UAAU;YAAE,MAAM,IAAI,YAAY,CAAC,aAAa,EAAE,sCAAsC,CAAC,CAAC;QAC5I,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAClE,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,GAAG,GAAG,IAAI,GAAG,IAAI,CAAC;QACtD,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,IAAI,GAAG,IAAI,CAAC;QAC9D,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,IAAI,CAAC,eAAe,IAAI,CAAC,EAAE,CAAC;YAC3I,MAAM,IAAI,YAAY,CAAC,SAAS,EAAE,iDAAiD,CAAC,CAAC;QACvF,CAAC;IACH,CAAC;IACD,KAAK,CAAC,MAAM,CAAC,QAA0B;QACrC,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,QAAQ,EAAE,CAAC,CAAC;QAC1C,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAiB,WAAW,EAAE,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QAClG,OAAO,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACjF,CAAC;IACD,KAAK,CAAC,GAAG,CAAC,QAA0B;QAClC,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,QAAQ,EAAE,CAAC,CAAC;QAC1C,MAAM,EAAE,GAAG,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC7C,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,IAAI,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;IAC7H,CAAC;IACO,KAAK,CAAC,MAAM,CAAC,IAAU,EAAE,QAA0B;QACzD,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM;YAAE,MAAM,IAAI,YAAY,CAAC,aAAa,EAAE,yDAAyD,CAAC,CAAC;QACjI,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;QAC/E,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACjG,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC;QAC1B,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,KAAK,IAAI,IAAI,KAAK,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE;YAAE,MAAM,IAAI,YAAY,CAAC,WAAW,EAAE,iDAAiD,CAAC,CAAC;IACrK,CAAC;IACO,KAAK,CAAC,SAAS,CAAC,EAAU,EAAE,QAA0B;QAC5D,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAiB,WAAW,EAAE,EAAE,CAAC,CAAC;QACnE,IAAI,GAAG,EAAE,KAAK,KAAK,OAAO;YAAE,OAAO,IAAI,CAAC;QACxC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAO,OAAO,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;QAChE,IAAI,CAAC;YACH,IAAI,CAAC,IAAI;gBAAE,MAAM,IAAI,YAAY,CAAC,WAAW,EAAE,kCAAkC,CAAC,CAAC;YACnF,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;YAClC,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAAC,MAAM,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;YAAC,MAAM,KAAK,CAAC;QAAC,CAAC;IAC9D,CAAC;IACD,KAAK,CAAC,MAAM,CAAC,QAA0B;QACrC,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,QAAQ,EAAE,CAAC,CAAC;QAC1C,MAAM,EAAE,GAAG,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC7C,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,IAAI,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;IACrH,CAAC;IACO,WAAW,CAAC,EAAU;QAC5B,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,WAAW,EAAE,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE;YAC9E,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACpD,OAAO,CAAC,SAAS,GAAG,GAAG,EAAE;gBACvB,MAAM,GAAG,GAAG,OAAO,CAAC,MAAoC,CAAC;gBACzD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE;oBAAE,EAAE,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;gBAC5F,EAAE,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;gBAAC,EAAE,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACxG,CAAC,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;IACD,KAAK,CAAC,QAAQ,CAAC,QAA0B,EAAE,UAA2B,EAAE;QACtE,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,QAAQ,EAAE,CAAC,CAAC;QAC1C,MAAM,EAAE,GAAG,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC7C,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,IAAI,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IACvM,CAAC;IACO,KAAK,CAAC,QAAQ,CAAC,EAAU,EAAE,QAA0B,EAAE,OAAwB;QACrF,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACtB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;QAClD,IAAI,MAAM,EAAE,CAAC;YAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAAC,OAAO,CAAC,UAAU,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;YAAC,OAAO,MAAM,CAAC;QAAC,CAAC;QACxI,IAAI,GAAG,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAiB,WAAW,EAAE,EAAE,CAAC,CAAC;QACjE,IAAI,GAAG,IAAI,GAAG,CAAC,MAAM,GAAG,QAAQ,CAAC,KAAK,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;YAAC,MAAM,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;YAAC,GAAG,GAAG,IAAI,CAAC;QAAC,CAAC;QAChG,IAAI,OAAO,GAAmB,GAAG,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;QACnH,IAAI,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;QACjC,IAAI,OAAO,GAAe,EAAE,CAAC;QAC7B,IAAI,YAAY,GAAG,CAAC,CAAC;QACrB,MAAM,UAAU,GAAG,KAAK,IAAI,EAAE;YAC5B,IAAI,CAAC,YAAY;gBAAE,OAAO;YAC1B,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC;YAC/B,MAAM,IAAI,GAAG,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,GAAG,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC/F,MAAM,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,WAAW,EAAE,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE;gBAC7E,EAAE,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;gBAC/D,EAAE,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC7D,CAAC,CAAC,CAAC;YACH,OAAO,GAAG,IAAI,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;YAAC,YAAY,GAAG,CAAC,CAAC;QACjD,CAAC,CAAC;QACF,IAAI,CAAC;YACH,IAAI,OAAO,CAAC,MAAM,GAAG,QAAQ,CAAC,KAAK,EAAE,CAAC;gBACpC,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;gBAC9B,IAAI,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;oBAAC,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,SAAS,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;oBAAC,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;gBAAC,CAAC;gBAChI,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;gBACjL,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;oBAC9C,OAAO,GAAG,KAAK,CAAC;oBAAC,MAAM,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;oBAAC,OAAO,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;gBACvI,CAAC;qBAAM,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;oBACnC,MAAM,KAAK,GAAG,4BAA4B,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC,CAAC;oBAC7F,IAAI,CAAC,OAAO,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,IAAI,CAAC,KAAK;wBACvG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,MAAM,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,KAAK,GAAG,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,KAAK,EAAE,CAAC;wBAC1H,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;wBAAC,MAAM,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;wBAC1D,MAAM,IAAI,YAAY,CAAC,WAAW,EAAE,wEAAwE,CAAC,CAAC;oBAChH,CAAC;gBACH,CAAC;qBAAM,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;oBACnC,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;oBAAC,MAAM,IAAI,YAAY,CAAC,SAAS,EAAE,6BAA6B,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;gBACpH,CAAC;gBACD,OAAO,CAAC,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;gBACxD,IAAI,CAAC,QAAQ,CAAC,IAAI;oBAAE,MAAM,IAAI,YAAY,CAAC,SAAS,EAAE,gCAAgC,CAAC,CAAC;gBACxF,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;gBACzC,IAAI,CAAC;oBACH,SAAS,CAAC;wBACR,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;wBAAC,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC;wBACjD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;wBACnC,IAAI,MAAM,CAAC,IAAI;4BAAE,MAAM;wBACvB,IAAI,OAAO,CAAC,MAAM,GAAG,YAAY,GAAG,MAAM,CAAC,KAAK,CAAC,UAAU,GAAG,QAAQ,CAAC,KAAK;4BAAE,MAAM,IAAI,YAAY,CAAC,WAAW,EAAE,0CAA0C,CAAC,CAAC;wBAC9J,yEAAyE;wBACzE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,MAAM,CAAC,CAAC;wBAAC,YAAY,IAAI,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC;wBACnF,IAAI,YAAY,IAAI,IAAI,CAAC,eAAe;4BAAE,MAAM,UAAU,EAAE,CAAC;wBAC7D,OAAO,CAAC,UAAU,EAAE,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,GAAG,YAAY,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;oBAClG,CAAC;oBACD,MAAM,UAAU,EAAE,CAAC;gBACrB,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;oBACtC,IAAI,CAAC,CAAC,KAAK,YAAY,YAAY,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW,CAAC;wBAAE,MAAM,UAAU,EAAE,CAAC;oBACvF,MAAM,KAAK,CAAC;gBACd,CAAC;wBAAS,CAAC;oBAAC,MAAM,CAAC,WAAW,EAAE,CAAC;gBAAC,CAAC;YACrC,CAAC;YACD,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YACtB,IAAI,OAAO,CAAC,MAAM,KAAK,QAAQ,CAAC,KAAK;gBAAE,MAAM,IAAI,YAAY,CAAC,SAAS,EAAE,gEAAgE,CAAC,CAAC;YAC3I,MAAM,KAAK,GAAW,EAAE,CAAC;YACzB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBACxC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAO,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;gBAClE,IAAI,CAAC,IAAI;oBAAE,MAAM,IAAI,YAAY,CAAC,WAAW,EAAE,oCAAoC,CAAC,CAAC;gBACrF,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACnB,CAAC;YACD,MAAM,QAAQ,GAAG,IAAI,IAAI,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,0BAA0B,EAAE,CAAC,CAAC;YACvE,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAC7D,MAAM,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,WAAW,EAAE,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE;gBAC7E,EAAE,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;gBACpD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE;oBAAE,EAAE,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;gBACxF,EAAE,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;gBAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAClG,CAAC,CAAC,CAAC;YACH,OAAO,CAAC,UAAU,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;YACjF,OAAO,QAAQ,CAAC;QAClB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,YAAY,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW;gBAAE,MAAM,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;YAC5F,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;CACF;AAED,MAAM,UAAU,qBAAqB,CAAC,KAAmB,EAAE,UAAkC,EAAE,IAAqB,OAAO,IAAI,eAAe,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { type LocalPage, type OfflineStore } from './store.js';
|
|
2
|
+
export type LocalJobStatus = 'pending' | 'running' | 'paused' | 'interrupted' | 'completed' | 'failed';
|
|
3
|
+
export interface LocalJob<I = unknown, C = unknown, R = unknown> {
|
|
4
|
+
id: string;
|
|
5
|
+
kind: string;
|
|
6
|
+
input: I;
|
|
7
|
+
checkpoint: C | null;
|
|
8
|
+
result: R | null;
|
|
9
|
+
status: LocalJobStatus;
|
|
10
|
+
attempts: number;
|
|
11
|
+
updatedAt: number;
|
|
12
|
+
error: string | null;
|
|
13
|
+
}
|
|
14
|
+
export interface LocalJobContext<C = unknown> {
|
|
15
|
+
savedCheckpoint: C | null;
|
|
16
|
+
checkpoint: (value: C) => Promise<void>;
|
|
17
|
+
signal: AbortSignal | undefined;
|
|
18
|
+
}
|
|
19
|
+
export type LocalJobHandler<I = unknown, C = unknown, R = unknown> = (input: I, context: LocalJobContext<C>) => Promise<R>;
|
|
20
|
+
export declare class LocalJobs {
|
|
21
|
+
private readonly store;
|
|
22
|
+
constructor(store: OfflineStore);
|
|
23
|
+
create<I>(input: {
|
|
24
|
+
id: string;
|
|
25
|
+
kind: string;
|
|
26
|
+
input: I;
|
|
27
|
+
}): Promise<LocalJob<I>>;
|
|
28
|
+
get<I = unknown, C = unknown, R = unknown>(id: string): Promise<LocalJob<I, C, R> | null>;
|
|
29
|
+
list(options?: LocalPage): Promise<LocalJob[]>;
|
|
30
|
+
private update;
|
|
31
|
+
checkpoint<C>(id: string, value: C): Promise<void>;
|
|
32
|
+
setStatus(id: string, status: LocalJobStatus): Promise<void>;
|
|
33
|
+
/** No automatic execution. Re-entering run() passes the last committed checkpoint. */
|
|
34
|
+
run<I = unknown, C = unknown, R = unknown>(id: string, handler: LocalJobHandler<I, C, R>, options?: {
|
|
35
|
+
signal?: AbortSignal;
|
|
36
|
+
}): Promise<LocalJob<I, C, R>>;
|
|
37
|
+
/** The same Web Lock excludes active SDK runners before recovery. */
|
|
38
|
+
recover(): Promise<number>;
|
|
39
|
+
}
|
|
40
|
+
export interface SyncOperation {
|
|
41
|
+
id: string;
|
|
42
|
+
kind: string;
|
|
43
|
+
payload: unknown;
|
|
44
|
+
status: 'pending' | 'running' | 'failed' | 'completed';
|
|
45
|
+
attempts: number;
|
|
46
|
+
updatedAt: number;
|
|
47
|
+
error: string | null;
|
|
48
|
+
}
|
|
49
|
+
export type SyncHandler = (payload: unknown, context: {
|
|
50
|
+
operationId: string;
|
|
51
|
+
signal: AbortSignal | undefined;
|
|
52
|
+
}) => Promise<void>;
|
|
53
|
+
/** At-least-once delivery. Handlers MUST enforce server idempotency using operationId. */
|
|
54
|
+
export declare class SyncQueue {
|
|
55
|
+
private readonly store;
|
|
56
|
+
constructor(store: OfflineStore);
|
|
57
|
+
enqueue(input: {
|
|
58
|
+
id: string;
|
|
59
|
+
kind: string;
|
|
60
|
+
payload: unknown;
|
|
61
|
+
idempotent: true;
|
|
62
|
+
}): Promise<SyncOperation>;
|
|
63
|
+
get(id: string): Promise<SyncOperation | null>;
|
|
64
|
+
list(options?: LocalPage): Promise<SyncOperation[]>;
|
|
65
|
+
private change;
|
|
66
|
+
retry(id: string): Promise<void>;
|
|
67
|
+
/** Explicit caller action; never triggered automatically by an online event. */
|
|
68
|
+
flush(handlers: Record<string, SyncHandler>, options?: {
|
|
69
|
+
signal?: AbortSignal;
|
|
70
|
+
limit?: number;
|
|
71
|
+
}): Promise<{
|
|
72
|
+
completed: number;
|
|
73
|
+
failed: number;
|
|
74
|
+
}>;
|
|
75
|
+
}
|
|
76
|
+
export declare function createLocalJobs(store: OfflineStore): LocalJobs;
|
|
77
|
+
export declare function createSyncQueue(store: OfflineStore): SyncQueue;
|
|
78
|
+
//# sourceMappingURL=jobs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jobs.d.ts","sourceRoot":"","sources":["../../src/offline/jobs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { OfflineError } from './store.js';
|
|
2
|
+
import { offlineLock } from './locks.js';
|
|
3
|
+
const validId = (id) => {
|
|
4
|
+
if (!id || id.length > 256)
|
|
5
|
+
throw new OfflineError('invalid', 'Work ids must contain 1–256 characters.');
|
|
6
|
+
};
|
|
7
|
+
const errorText = (error) => (error instanceof Error ? error.message : String(error)).slice(0, 1000);
|
|
8
|
+
const checkAbort = (signal) => { if (signal?.aborted)
|
|
9
|
+
throw new DOMException('Work cancelled.', 'AbortError'); };
|
|
10
|
+
export class LocalJobs {
|
|
11
|
+
store;
|
|
12
|
+
constructor(store) {
|
|
13
|
+
this.store = store;
|
|
14
|
+
}
|
|
15
|
+
create(input) {
|
|
16
|
+
validId(input.id);
|
|
17
|
+
validId(input.kind);
|
|
18
|
+
return this.store.transaction(['jobs'], 'readwrite', (tx, done) => {
|
|
19
|
+
const request = tx.objectStore('jobs').get(input.id);
|
|
20
|
+
request.onsuccess = () => {
|
|
21
|
+
if (request.result) {
|
|
22
|
+
tx.abort();
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const row = { ...input, checkpoint: null, result: null, status: 'pending', attempts: 0, updatedAt: Date.now(), error: null };
|
|
26
|
+
tx.objectStore('jobs').put(row, row.id);
|
|
27
|
+
done(row);
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
get(id) { return this.store.read('jobs', id); }
|
|
32
|
+
list(options = {}) { return this.store.page('jobs', options); }
|
|
33
|
+
update(id, change) {
|
|
34
|
+
return this.store.transaction(['jobs'], 'readwrite', (tx, done) => {
|
|
35
|
+
const request = tx.objectStore('jobs').get(id);
|
|
36
|
+
request.onsuccess = () => {
|
|
37
|
+
if (!request.result) {
|
|
38
|
+
tx.abort();
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const next = { ...change(request.result), updatedAt: Date.now() };
|
|
42
|
+
tx.objectStore('jobs').put(next, id);
|
|
43
|
+
done(next);
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
async checkpoint(id, value) { await this.update(id, (row) => ({ ...row, checkpoint: value })); }
|
|
48
|
+
async setStatus(id, status) {
|
|
49
|
+
if (!['pending', 'running', 'paused', 'interrupted', 'completed', 'failed'].includes(status))
|
|
50
|
+
throw new OfflineError('invalid', 'Unknown local job status.');
|
|
51
|
+
await this.update(id, (row) => ({ ...row, status }));
|
|
52
|
+
}
|
|
53
|
+
/** No automatic execution. Re-entering run() passes the last committed checkpoint. */
|
|
54
|
+
async run(id, handler, options = {}) {
|
|
55
|
+
return this.store.runWork(() => offlineLock(`${this.store.databaseName}:jobs`, async () => {
|
|
56
|
+
checkAbort(options.signal);
|
|
57
|
+
const found = await this.get(id);
|
|
58
|
+
if (!found)
|
|
59
|
+
throw new OfflineError('invalid', 'Local job does not exist.');
|
|
60
|
+
if (found.status === 'completed')
|
|
61
|
+
return found;
|
|
62
|
+
await this.update(id, (row) => ({ ...row, status: 'running', attempts: row.attempts + 1, error: null }));
|
|
63
|
+
try {
|
|
64
|
+
const result = await handler(found.input, {
|
|
65
|
+
savedCheckpoint: found.checkpoint, signal: options.signal,
|
|
66
|
+
checkpoint: async (value) => { checkAbort(options.signal); await this.checkpoint(id, value); },
|
|
67
|
+
});
|
|
68
|
+
checkAbort(options.signal);
|
|
69
|
+
return await this.update(id, (row) => ({ ...row, result, status: 'completed' }));
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
// A locked account deliberately prevents further writes; its running job
|
|
73
|
+
// can be recovered after the owner explicitly opens it again.
|
|
74
|
+
try {
|
|
75
|
+
await this.update(id, (row) => ({ ...row, status: options.signal?.aborted ? 'paused' : 'failed', error: errorText(error) }));
|
|
76
|
+
}
|
|
77
|
+
catch { /* Preserve the original failure; no unlocked-account fallback. */ }
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
}, true, options.signal ? { signal: options.signal } : {}), options.signal);
|
|
81
|
+
}
|
|
82
|
+
/** The same Web Lock excludes active SDK runners before recovery. */
|
|
83
|
+
async recover() {
|
|
84
|
+
return this.store.runWork(() => offlineLock(`${this.store.databaseName}:jobs`, () => this.store.transaction(['jobs'], 'readwrite', (tx, done) => {
|
|
85
|
+
let count = 0;
|
|
86
|
+
const request = tx.objectStore('jobs').openCursor();
|
|
87
|
+
request.onsuccess = () => {
|
|
88
|
+
const cursor = request.result;
|
|
89
|
+
if (!cursor) {
|
|
90
|
+
done(count);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const row = cursor.value;
|
|
94
|
+
if (row.status === 'running') {
|
|
95
|
+
cursor.update({ ...row, status: 'interrupted', updatedAt: Date.now() });
|
|
96
|
+
count++;
|
|
97
|
+
}
|
|
98
|
+
cursor.continue();
|
|
99
|
+
};
|
|
100
|
+
}), true));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/** At-least-once delivery. Handlers MUST enforce server idempotency using operationId. */
|
|
104
|
+
export class SyncQueue {
|
|
105
|
+
store;
|
|
106
|
+
constructor(store) {
|
|
107
|
+
this.store = store;
|
|
108
|
+
}
|
|
109
|
+
async enqueue(input) {
|
|
110
|
+
validId(input.id);
|
|
111
|
+
validId(input.kind);
|
|
112
|
+
if (input.idempotent !== true)
|
|
113
|
+
throw new OfflineError('invalid', 'Only explicitly idempotent operations may be queued.');
|
|
114
|
+
return this.store.transaction(['outbox'], 'readwrite', (tx, done) => {
|
|
115
|
+
const request = tx.objectStore('outbox').get(input.id);
|
|
116
|
+
request.onsuccess = () => {
|
|
117
|
+
// A duplicate id cannot replace another payload (including after delivery).
|
|
118
|
+
if (request.result) {
|
|
119
|
+
tx.abort();
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const row = { id: input.id, kind: input.kind, payload: input.payload, status: 'pending', attempts: 0, updatedAt: Date.now(), error: null };
|
|
123
|
+
tx.objectStore('outbox').put(row, row.id);
|
|
124
|
+
done(row);
|
|
125
|
+
};
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
get(id) { return this.store.read('outbox', id); }
|
|
129
|
+
list(options = {}) { return this.store.page('outbox', options); }
|
|
130
|
+
async change(id, status, error, increment = false) {
|
|
131
|
+
return this.store.transaction(['outbox'], 'readwrite', (tx, done) => {
|
|
132
|
+
const request = tx.objectStore('outbox').get(id);
|
|
133
|
+
request.onsuccess = () => {
|
|
134
|
+
if (!request.result) {
|
|
135
|
+
tx.abort();
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const row = request.result;
|
|
139
|
+
tx.objectStore('outbox').put({ ...row, status, error, attempts: row.attempts + Number(increment), updatedAt: Date.now() }, id);
|
|
140
|
+
done(undefined);
|
|
141
|
+
};
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
retry(id) {
|
|
145
|
+
return this.store.runWork(() => offlineLock(`${this.store.databaseName}:sync`, async () => {
|
|
146
|
+
const row = await this.get(id);
|
|
147
|
+
if (!row || row.status !== 'failed')
|
|
148
|
+
throw new OfflineError('invalid', 'Only a failed operation can be retried.');
|
|
149
|
+
await this.change(id, 'pending', null);
|
|
150
|
+
}, true));
|
|
151
|
+
}
|
|
152
|
+
/** Explicit caller action; never triggered automatically by an online event. */
|
|
153
|
+
flush(handlers, options = {}) {
|
|
154
|
+
const limit = options.limit ?? 50;
|
|
155
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 200)
|
|
156
|
+
throw new OfflineError('invalid', 'Flush limit must be between 1 and 200.');
|
|
157
|
+
return this.store.runWork(() => offlineLock(`${this.store.databaseName}:sync`, async () => {
|
|
158
|
+
checkAbort(options.signal);
|
|
159
|
+
const pending = await this.store.transaction(['outbox'], 'readonly', (tx, done) => {
|
|
160
|
+
const rows = [];
|
|
161
|
+
const request = tx.objectStore('outbox').openCursor();
|
|
162
|
+
request.onsuccess = () => {
|
|
163
|
+
const cursor = request.result;
|
|
164
|
+
if (!cursor || rows.length >= limit) {
|
|
165
|
+
done(rows);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
const row = cursor.value;
|
|
169
|
+
// Running after acquiring the lock means a previous tab terminated.
|
|
170
|
+
if (row.status === 'pending' || row.status === 'running')
|
|
171
|
+
rows.push(row);
|
|
172
|
+
cursor.continue();
|
|
173
|
+
};
|
|
174
|
+
});
|
|
175
|
+
let completed = 0, failed = 0;
|
|
176
|
+
for (const row of pending) {
|
|
177
|
+
checkAbort(options.signal);
|
|
178
|
+
this.store.assertAccess();
|
|
179
|
+
const handler = Object.hasOwn(handlers, row.kind) ? handlers[row.kind] : undefined;
|
|
180
|
+
if (!handler) {
|
|
181
|
+
await this.change(row.id, 'failed', 'No handler registered for this operation.');
|
|
182
|
+
failed++;
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
await this.change(row.id, 'running', null, true);
|
|
186
|
+
try {
|
|
187
|
+
await handler(row.payload, { operationId: row.id, signal: options.signal });
|
|
188
|
+
await this.change(row.id, 'completed', null);
|
|
189
|
+
completed++;
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
await this.change(row.id, options.signal?.aborted ? 'pending' : 'failed', errorText(error));
|
|
193
|
+
if (options.signal?.aborted)
|
|
194
|
+
throw error;
|
|
195
|
+
failed++;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return { completed, failed };
|
|
199
|
+
}, true, options.signal ? { signal: options.signal } : {}), options.signal);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
export function createLocalJobs(store) { return new LocalJobs(store); }
|
|
203
|
+
export function createSyncQueue(store) { return new SyncQueue(store); }
|
|
204
|
+
//# sourceMappingURL=jobs.js.map
|