@drakkar.software/starfish-client 3.0.0-alpha.10 → 3.0.0-alpha.12
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/append-log.d.ts +1 -3
- package/dist/append-log.js +267 -0
- package/dist/background-sync.js +29 -0
- package/dist/bindings/suspense.js +49 -0
- package/dist/bindings/zustand.js +28 -20
- package/dist/bindings/zustand.js.map +2 -2
- package/dist/client.d.ts +30 -13
- package/dist/client.js +391 -0
- package/dist/config.js +18 -0
- package/dist/debounced-sync.js +120 -0
- package/dist/dedup.js +35 -0
- package/dist/export.js +115 -0
- package/dist/history.js +61 -0
- package/dist/index.js +30 -23
- package/dist/index.js.map +3 -3
- package/dist/logger.js +80 -0
- package/dist/migrate.js +38 -0
- package/dist/mobile-lifecycle.js +94 -0
- package/dist/multi-store.js +92 -0
- package/dist/polling.js +52 -0
- package/dist/resolvers.js +223 -0
- package/dist/service-worker.js +55 -0
- package/dist/storage/indexeddb.js +59 -0
- package/dist/sync.js +181 -0
- package/dist/types.d.ts +1 -9
- package/dist/types.js +18 -0
- package/dist/validate.js +28 -0
- package/package.json +2 -2
package/dist/history.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export class SnapshotHistory {
|
|
2
|
+
snapshots = [];
|
|
3
|
+
maxSnapshots;
|
|
4
|
+
storageKey;
|
|
5
|
+
constructor(options) {
|
|
6
|
+
this.maxSnapshots = options?.maxSnapshots ?? 20;
|
|
7
|
+
this.storageKey = options?.storageKey;
|
|
8
|
+
if (this.storageKey) {
|
|
9
|
+
try {
|
|
10
|
+
const raw = localStorage.getItem(this.storageKey);
|
|
11
|
+
if (raw) {
|
|
12
|
+
const parsed = JSON.parse(raw);
|
|
13
|
+
if (Array.isArray(parsed))
|
|
14
|
+
this.snapshots = parsed;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
catch { /* corrupted or unavailable — start fresh */ }
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/** Take a labeled snapshot of the given data. */
|
|
21
|
+
take(label, data) {
|
|
22
|
+
this.snapshots.push({
|
|
23
|
+
timestamp: Date.now(),
|
|
24
|
+
label,
|
|
25
|
+
data: JSON.stringify(data),
|
|
26
|
+
});
|
|
27
|
+
if (this.snapshots.length > this.maxSnapshots) {
|
|
28
|
+
this.snapshots = this.snapshots.slice(-this.maxSnapshots);
|
|
29
|
+
}
|
|
30
|
+
this.persist();
|
|
31
|
+
}
|
|
32
|
+
/** Restore data from a snapshot at the given index. Returns undefined if index is invalid or data is corrupt. */
|
|
33
|
+
restore(index) {
|
|
34
|
+
const snapshot = this.snapshots[index];
|
|
35
|
+
if (!snapshot)
|
|
36
|
+
return undefined;
|
|
37
|
+
try {
|
|
38
|
+
return JSON.parse(snapshot.data);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/** List available snapshots (metadata only, no data payload). */
|
|
45
|
+
list() {
|
|
46
|
+
return this.snapshots.map(({ timestamp, label }) => ({ timestamp, label }));
|
|
47
|
+
}
|
|
48
|
+
/** Clear all snapshots. */
|
|
49
|
+
clear() {
|
|
50
|
+
this.snapshots = [];
|
|
51
|
+
this.persist();
|
|
52
|
+
}
|
|
53
|
+
persist() {
|
|
54
|
+
if (!this.storageKey)
|
|
55
|
+
return;
|
|
56
|
+
try {
|
|
57
|
+
localStorage.setItem(this.storageKey, JSON.stringify(this.snapshots));
|
|
58
|
+
}
|
|
59
|
+
catch { /* quota exceeded — skip silently */ }
|
|
60
|
+
}
|
|
61
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -15,11 +15,9 @@ import {
|
|
|
15
15
|
HEADER_SIG,
|
|
16
16
|
HEADER_TS,
|
|
17
17
|
HEADER_NONCE,
|
|
18
|
-
HEADER_ALG,
|
|
19
18
|
HEADER_PUB,
|
|
20
19
|
HEADER_CONTENT_TYPE,
|
|
21
20
|
HEADER_ACCEPT,
|
|
22
|
-
DEFAULT_ALG,
|
|
23
21
|
signAppendAuthor,
|
|
24
22
|
signRequest,
|
|
25
23
|
stableStringify
|
|
@@ -129,23 +127,19 @@ var StarfishClient = class {
|
|
|
129
127
|
* presenter` bind the server checks.
|
|
130
128
|
*/
|
|
131
129
|
async capRequestHeaders(capCtx, method, pathAndQuery, body) {
|
|
132
|
-
const { cap, devEdPrivHex, pubHex
|
|
130
|
+
const { cap, devEdPrivHex, pubHex } = capCtx;
|
|
133
131
|
const req = {
|
|
134
132
|
method,
|
|
135
133
|
pathAndQuery,
|
|
136
134
|
body,
|
|
137
135
|
host: this.signingHost()
|
|
138
136
|
};
|
|
139
|
-
const
|
|
140
|
-
const { alg, sig, ts, nonce } = await signRequest(req, devEdPrivHex, {
|
|
141
|
-
alg: signAlg
|
|
142
|
-
});
|
|
137
|
+
const { sig, ts, nonce } = await signRequest(req, devEdPrivHex);
|
|
143
138
|
const headers = {
|
|
144
139
|
[HEADER_AUTHORIZATION]: `Cap ${encodeCapAuth(cap)}`,
|
|
145
140
|
[HEADER_SIG]: sig,
|
|
146
141
|
[HEADER_TS]: String(ts),
|
|
147
|
-
[HEADER_NONCE]: nonce
|
|
148
|
-
[HEADER_ALG]: alg
|
|
142
|
+
[HEADER_NONCE]: nonce
|
|
149
143
|
};
|
|
150
144
|
if (pubHex !== void 0) headers[HEADER_PUB] = pubHex;
|
|
151
145
|
return headers;
|
|
@@ -159,11 +153,10 @@ var StarfishClient = class {
|
|
|
159
153
|
* unsigned and a server requiring signatures rejects it.
|
|
160
154
|
*/
|
|
161
155
|
appendAuthorKey(capCtx) {
|
|
162
|
-
const { cap, pubHex
|
|
156
|
+
const { cap, pubHex } = capCtx;
|
|
163
157
|
const authorPubHex = pubHex ?? cap.sub;
|
|
164
158
|
if (authorPubHex === void 0) return null;
|
|
165
|
-
|
|
166
|
-
return { authorPubHex, signAlg };
|
|
159
|
+
return { authorPubHex };
|
|
167
160
|
}
|
|
168
161
|
async pull(path, checkpointOrOptions) {
|
|
169
162
|
let pathAndQuery = this.applyNamespace(path);
|
|
@@ -211,12 +204,16 @@ var StarfishClient = class {
|
|
|
211
204
|
return result;
|
|
212
205
|
}
|
|
213
206
|
/**
|
|
214
|
-
* Pull several
|
|
215
|
-
*
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
*
|
|
219
|
-
*
|
|
207
|
+
* Pull several documents in one round-trip via `/batch/pull`. `collections` is
|
|
208
|
+
* the list of distinct collection names; `opts.params` supplies, per collection,
|
|
209
|
+
* an ARRAY of path-param sets — one per document to read — so the SAME collection
|
|
210
|
+
* can fan in many documents (e.g. many users' `profile`) in a single request.
|
|
211
|
+
* The server auto-fills the `{identity}` param from the authenticated caller for
|
|
212
|
+
* any set that omits it, so a self-doc collection needs no params. Returns a map
|
|
213
|
+
* of collection name → an ARRAY of pulled documents (or per-document `{ error }`),
|
|
214
|
+
* in request order. Honors the configured namespace.
|
|
215
|
+
*
|
|
216
|
+
* For the common "many docs of one collection" case prefer {@link batchPullMany}.
|
|
220
217
|
*
|
|
221
218
|
* Note: not append/checkpoint-aware — for incremental append-only reads use
|
|
222
219
|
* `pull(path, { since })` (or `AppendLogCursor`) per collection.
|
|
@@ -239,6 +236,18 @@ var StarfishClient = class {
|
|
|
239
236
|
}
|
|
240
237
|
return await res.json();
|
|
241
238
|
}
|
|
239
|
+
/**
|
|
240
|
+
* Convenience over {@link batchPull} for reading MANY documents of ONE
|
|
241
|
+
* collection in a single round-trip: pass the per-document param-sets and get
|
|
242
|
+
* back the {@link BatchPullEntry} array aligned to `paramsList` by index (each
|
|
243
|
+
* entry is `{ data, hash, timestamp }` or `{ error }`). An empty `paramsList`
|
|
244
|
+
* issues no request and returns `[]`.
|
|
245
|
+
*/
|
|
246
|
+
async batchPullMany(collection, paramsList) {
|
|
247
|
+
if (paramsList.length === 0) return [];
|
|
248
|
+
const res = await this.batchPull([collection], { params: { [collection]: paramsList } });
|
|
249
|
+
return res.collections[collection] ?? [];
|
|
250
|
+
}
|
|
242
251
|
/**
|
|
243
252
|
* Push synced data to the server.
|
|
244
253
|
* @param path - The push endpoint path (e.g. "/push/users/abc/settings")
|
|
@@ -310,8 +319,7 @@ var StarfishClient = class {
|
|
|
310
319
|
documentKey,
|
|
311
320
|
data,
|
|
312
321
|
authorKey.authorPubHex,
|
|
313
|
-
capCtx.devEdPrivHex
|
|
314
|
-
authorKey.signAlg
|
|
322
|
+
capCtx.devEdPrivHex
|
|
315
323
|
);
|
|
316
324
|
bodyObj[AUTHOR_PUBKEY_FIELD] = authorPubkey;
|
|
317
325
|
bodyObj[AUTHOR_SIGNATURE_FIELD] = authorSignature;
|
|
@@ -555,7 +563,6 @@ var SyncManager = class {
|
|
|
555
563
|
|
|
556
564
|
// src/append-log.ts
|
|
557
565
|
import {
|
|
558
|
-
DEFAULT_ALG as DEFAULT_ALG2,
|
|
559
566
|
verifyAppendAuthor
|
|
560
567
|
} from "@drakkar.software/starfish-protocol";
|
|
561
568
|
var PULL_PATH_PREFIX = "/pull/";
|
|
@@ -702,12 +709,12 @@ var AppendLogCursor = class {
|
|
|
702
709
|
if (policy.expectedAuthorPubkey && authorPubkey.toLowerCase() !== policy.expectedAuthorPubkey.toLowerCase()) {
|
|
703
710
|
throw new AppendAuthorError(el.ts);
|
|
704
711
|
}
|
|
712
|
+
void policy;
|
|
705
713
|
const ok = verifyAppendAuthor(
|
|
706
714
|
this.documentKey,
|
|
707
715
|
el.data,
|
|
708
716
|
authorPubkey,
|
|
709
|
-
authorSignature
|
|
710
|
-
policy.alg ?? DEFAULT_ALG2
|
|
717
|
+
authorSignature
|
|
711
718
|
);
|
|
712
719
|
if (!ok) throw new AppendAuthorError(el.ts);
|
|
713
720
|
}
|