@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.
@@ -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, presenterAlg } = capCtx;
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 signAlg = cap.kind === "audience" ? presenterAlg ?? DEFAULT_ALG : cap.subAlg ?? cap.issAlg;
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, presenterAlg } = capCtx;
156
+ const { cap, pubHex } = capCtx;
163
157
  const authorPubHex = pubHex ?? cap.sub;
164
158
  if (authorPubHex === void 0) return null;
165
- const signAlg = cap.kind === "audience" ? presenterAlg ?? DEFAULT_ALG : cap.subAlg ?? cap.issAlg;
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 collections in one round-trip via `/batch/pull`. `collections`
215
- * is the list of collection names; `opts.params` supplies path params per
216
- * collection (serialized to a URL-encoded JSON `params` query). The server
217
- * auto-fills the `{identity}` param from the authenticated caller, so per-user
218
- * collections need no params. Returns a map of collection name → its pulled
219
- * document or a per-collection `{ error }`. Honors the configured namespace.
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
  }