@drakkar.software/starfish-client 2.3.0 → 3.0.0-alpha.1

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.
Files changed (62) hide show
  1. package/README.md +219 -0
  2. package/dist/_crypto_helpers.d.ts +4 -0
  3. package/dist/bindings/zustand.d.ts +5 -4
  4. package/dist/bindings/zustand.js +127 -79
  5. package/dist/bindings/zustand.js.map +4 -4
  6. package/dist/cap-mint.d.ts +20 -0
  7. package/dist/cap-mint.js +12 -0
  8. package/dist/cap-mint.js.map +7 -0
  9. package/dist/client.d.ts +52 -3
  10. package/dist/config.d.ts +1 -4
  11. package/dist/directory.d.ts +9 -0
  12. package/dist/directory.js +24 -0
  13. package/dist/directory.js.map +7 -0
  14. package/dist/identity.d.ts +4 -82
  15. package/dist/identity.js +2 -354
  16. package/dist/identity.js.map +4 -4
  17. package/dist/index.d.ts +8 -10
  18. package/dist/index.js +133 -251
  19. package/dist/index.js.map +4 -4
  20. package/dist/keyring.d.ts +6 -0
  21. package/dist/keyring.js +26 -0
  22. package/dist/keyring.js.map +7 -0
  23. package/dist/pairing.d.ts +6 -0
  24. package/dist/pairing.js +26 -0
  25. package/dist/pairing.js.map +7 -0
  26. package/dist/recipients.d.ts +6 -0
  27. package/dist/recipients.js +16 -0
  28. package/dist/recipients.js.map +7 -0
  29. package/dist/sync.d.ts +32 -8
  30. package/dist/testing.d.ts +1 -1
  31. package/dist/testing.js +2 -2
  32. package/dist/testing.js.map +2 -2
  33. package/dist/types.d.ts +55 -9
  34. package/package.json +3 -12
  35. package/dist/background-sync.js +0 -29
  36. package/dist/bindings/suspense.js +0 -49
  37. package/dist/client.js +0 -112
  38. package/dist/config.js +0 -18
  39. package/dist/crypto.js +0 -49
  40. package/dist/debounced-sync.js +0 -120
  41. package/dist/dedup.js +0 -35
  42. package/dist/entitlements.js +0 -41
  43. package/dist/export.js +0 -115
  44. package/dist/group-crypto.d.ts +0 -111
  45. package/dist/group-crypto.js +0 -205
  46. package/dist/group-crypto.js.map +0 -7
  47. package/dist/hash.d.ts +0 -10
  48. package/dist/hash.js +0 -34
  49. package/dist/history.js +0 -61
  50. package/dist/logger.js +0 -80
  51. package/dist/migrate.js +0 -38
  52. package/dist/mobile-lifecycle.js +0 -55
  53. package/dist/multi-store.js +0 -92
  54. package/dist/platform.d.ts +0 -52
  55. package/dist/platform.js +0 -62
  56. package/dist/polling.js +0 -52
  57. package/dist/resolvers.js +0 -223
  58. package/dist/service-worker.js +0 -55
  59. package/dist/storage/indexeddb.js +0 -59
  60. package/dist/sync.js +0 -127
  61. package/dist/types.js +0 -18
  62. package/dist/validate.js +0 -28
package/dist/index.js CHANGED
@@ -1,6 +1,13 @@
1
1
  // src/index.ts
2
2
  import { configurePlatform } from "@drakkar.software/starfish-protocol";
3
- import { stableStringify as stableStringify2, computeHash } from "@drakkar.software/starfish-protocol";
3
+ import { stableStringify as stableStringify3, computeHash } from "@drakkar.software/starfish-protocol";
4
+ import { buildRevocationList, revocationListCanonicalSigningInput } from "@drakkar.software/starfish-protocol";
5
+
6
+ // src/client.ts
7
+ import {
8
+ signRequest,
9
+ stableStringify
10
+ } from "@drakkar.software/starfish-protocol";
4
11
 
5
12
  // src/types.ts
6
13
  var ConflictError = class extends Error {
@@ -20,34 +27,111 @@ var StarfishHttpError = class extends Error {
20
27
 
21
28
  // src/client.ts
22
29
  var APPEND_DEFAULT_FIELD = "items";
30
+ function encodeCapAuth(cap) {
31
+ const json = stableStringify(cap);
32
+ if (typeof btoa === "function") {
33
+ return btoa(json);
34
+ }
35
+ const bufCtor = globalThis.Buffer;
36
+ if (bufCtor) return bufCtor.from(json, "utf-8").toString("base64");
37
+ throw new Error("No base64 encoder available");
38
+ }
23
39
  var StarfishClient = class {
24
40
  baseUrl;
25
- auth;
41
+ capProvider;
26
42
  fetch;
43
+ /**
44
+ * Installed client-side plugins. Currently stored as inert data; no
45
+ * hooks fire yet. Extensions can inspect this list if needed.
46
+ */
47
+ plugins;
27
48
  constructor(options) {
28
49
  this.baseUrl = options.baseUrl.replace(/\/$/, "");
29
- this.auth = options.auth;
50
+ this.capProvider = options.capProvider;
30
51
  this.fetch = options.fetch ?? globalThis.fetch.bind(globalThis);
52
+ this.plugins = options.plugins ? [...options.plugins] : [];
53
+ }
54
+ /**
55
+ * Resolve the host portion of the URL the client will send to. The host
56
+ * is folded into the signed canonical input as the `h` field so the
57
+ * server can refuse a signature that was minted against a different
58
+ * Starfish host (replay-across-servers defence).
59
+ *
60
+ * When `baseUrl` is relative — e.g. the consumer passed a custom `fetch`
61
+ * that resolves relative URLs in its own context — there is no parseable
62
+ * host; we return `""` so signing still proceeds. The server-side
63
+ * verifier will also reconstruct host from its inbound URL, so the
64
+ * empty-host case still verifies symmetrically when both sides agree.
65
+ */
66
+ signingHost() {
67
+ try {
68
+ return new URL(this.baseUrl).host;
69
+ } catch {
70
+ return "";
71
+ }
72
+ }
73
+ /**
74
+ * Build auth headers for a request. When a `capProvider` is set, signs the
75
+ * request with the device's Ed25519 private key and returns the v3 header
76
+ * set (`Authorization: Cap …`, `X-Starfish-Sig`, `X-Starfish-Ts`,
77
+ * `X-Starfish-Nonce`). Empty when no provider is configured (public reads).
78
+ *
79
+ * Body bytes signed MUST equal the bytes sent on the wire — callers pass
80
+ * the already-serialized body string here so signing and transmission agree.
81
+ * The host bound into the signature is derived from `baseUrl` once per call.
82
+ */
83
+ async buildAuthHeaders(method, pathAndQuery, body) {
84
+ if (this.capProvider) {
85
+ const { cap, devEdPrivHex, pubHex } = await this.capProvider.getCap();
86
+ const req = {
87
+ method,
88
+ pathAndQuery,
89
+ body,
90
+ host: this.signingHost()
91
+ };
92
+ const { sig, ts, nonce } = await signRequest(req, devEdPrivHex);
93
+ const headers = {
94
+ Authorization: `Cap ${encodeCapAuth(cap)}`,
95
+ "X-Starfish-Sig": sig,
96
+ "X-Starfish-Ts": String(ts),
97
+ "X-Starfish-Nonce": nonce
98
+ };
99
+ if (pubHex !== void 0) headers["X-Starfish-Pub"] = pubHex;
100
+ return headers;
101
+ }
102
+ return {};
31
103
  }
32
104
  async pull(path, checkpointOrOptions) {
33
- let url = `${this.baseUrl}${path}`;
105
+ let pathAndQuery = path;
34
106
  let appendField;
35
107
  if (typeof checkpointOrOptions === "number") {
36
- if (checkpointOrOptions) url += `?checkpoint=${checkpointOrOptions}`;
108
+ if (checkpointOrOptions) pathAndQuery += `?checkpoint=${checkpointOrOptions}`;
37
109
  } else if (checkpointOrOptions != null) {
38
- appendField = checkpointOrOptions.appendField ?? APPEND_DEFAULT_FIELD;
110
+ const opts = checkpointOrOptions;
111
+ const isPullOptions = opts.withKeyring !== void 0 || opts.checkpoint !== void 0;
39
112
  const params = new URLSearchParams();
40
- if (checkpointOrOptions.since != null) {
41
- if (checkpointOrOptions.since < 0) throw new Error("since must be non-negative");
42
- params.set("checkpoint", String(checkpointOrOptions.since));
43
- }
44
- if (checkpointOrOptions.last != null) {
45
- if (checkpointOrOptions.last < 0) throw new Error("last must be non-negative");
46
- params.set("last", String(checkpointOrOptions.last));
113
+ if (isPullOptions) {
114
+ if (opts.checkpoint != null && opts.checkpoint > 0) {
115
+ params.set("checkpoint", String(opts.checkpoint));
116
+ }
117
+ if (opts.withKeyring) {
118
+ params.set("withKeyring", "1");
119
+ }
120
+ } else {
121
+ appendField = opts.appendField ?? APPEND_DEFAULT_FIELD;
122
+ if (opts.since != null) {
123
+ if (opts.since < 0) throw new Error("since must be non-negative");
124
+ params.set("checkpoint", String(opts.since));
125
+ }
126
+ if (opts.last != null) {
127
+ if (opts.last < 0) throw new Error("last must be non-negative");
128
+ params.set("last", String(opts.last));
129
+ }
47
130
  }
48
- if (params.size > 0) url += `?${params.toString()}`;
131
+ if (params.size > 0) pathAndQuery += `?${params.toString()}`;
49
132
  }
50
- const authHeaders = this.auth ? await this.auth({ method: "GET", path, body: null }) : {};
133
+ const url = `${this.baseUrl}${pathAndQuery}`;
134
+ const authHeaders = await this.buildAuthHeaders("GET", pathAndQuery, void 0);
51
135
  const res = await this.fetch(url, {
52
136
  method: "GET",
53
137
  headers: { Accept: "application/json", ...authHeaders }
@@ -67,16 +151,17 @@ var StarfishClient = class {
67
151
  * @param path - The push endpoint path (e.g. "/push/users/abc/settings")
68
152
  * @param data - The full document data to push
69
153
  * @param baseHash - Hash of the document this push is based on (null for first push)
70
- * @param authorSignature - Optional author signature for provenance
154
+ *
155
+ * v3 author fields (`authorPubkey` + `authorSignature`) live inside `data`
156
+ * and are produced by `SyncManager` when a `signer` is configured.
71
157
  * @throws {ConflictError} if the server detects a hash mismatch (409)
72
158
  */
73
- async push(path, data, baseHash, authorSignature) {
159
+ async push(path, data, baseHash) {
74
160
  const body = JSON.stringify({
75
161
  data,
76
- baseHash,
77
- ...authorSignature && { authorSignature }
162
+ baseHash
78
163
  });
79
- const authHeaders = this.auth ? await this.auth({ method: "POST", path, body }) : {};
164
+ const authHeaders = await this.buildAuthHeaders("POST", path, body);
80
165
  const res = await this.fetch(`${this.baseUrl}${path}`, {
81
166
  method: "POST",
82
167
  headers: {
@@ -99,7 +184,7 @@ var StarfishClient = class {
99
184
  * Returns raw bytes with the content hash from the ETag header.
100
185
  */
101
186
  async pullBlob(path) {
102
- const authHeaders = this.auth ? await this.auth({ method: "GET", path, body: null }) : {};
187
+ const authHeaders = await this.buildAuthHeaders("GET", path, void 0);
103
188
  const res = await this.fetch(`${this.baseUrl}${path}`, {
104
189
  method: "GET",
105
190
  headers: { Accept: "*/*", ...authHeaders }
@@ -117,7 +202,7 @@ var StarfishClient = class {
117
202
  * Binary collections use last-write-wins (no conflict detection).
118
203
  */
119
204
  async pushBlob(path, data, contentType) {
120
- const authHeaders = this.auth ? await this.auth({ method: "POST", path, body: null }) : {};
205
+ const authHeaders = await this.buildAuthHeaders("POST", path, void 0);
121
206
  const res = await this.fetch(`${this.baseUrl}${path}`, {
122
207
  method: "POST",
123
208
  headers: {
@@ -135,51 +220,7 @@ var StarfishClient = class {
135
220
  };
136
221
 
137
222
  // src/sync.ts
138
- import { deepMerge, stableStringify } from "@drakkar.software/starfish-protocol";
139
-
140
- // src/crypto.ts
141
- import { getCrypto, getBase64, IV_BYTES, ENCRYPTED_KEY, deriveKey } from "@drakkar.software/starfish-protocol";
142
- var ALGO = "AES-GCM";
143
- function createEncryptor(secret, salt, info = "starfish-e2e") {
144
- if (!secret) throw new Error("encryptionSecret must not be empty");
145
- if (!salt) throw new Error("encryptionSalt must not be empty");
146
- const keyPromise = deriveKey(secret, salt, info);
147
- return {
148
- async encrypt(data) {
149
- const key = await keyPromise;
150
- const c = getCrypto();
151
- const b64 = getBase64();
152
- const plaintext = new TextEncoder().encode(JSON.stringify(data));
153
- const iv = c.getRandomValues(new Uint8Array(IV_BYTES));
154
- const ciphertext = await c.subtle.encrypt({ name: ALGO, iv }, key, plaintext);
155
- const combined = new Uint8Array(iv.length + ciphertext.byteLength);
156
- combined.set(iv);
157
- combined.set(new Uint8Array(ciphertext), iv.length);
158
- return { [ENCRYPTED_KEY]: b64.encode(combined) };
159
- },
160
- async decrypt(wrapper) {
161
- const encoded = wrapper[ENCRYPTED_KEY];
162
- if (typeof encoded !== "string") {
163
- throw new Error("Expected encrypted data but received unencrypted document");
164
- }
165
- const key = await keyPromise;
166
- const c = getCrypto();
167
- const b64 = getBase64();
168
- const combined = b64.decode(encoded);
169
- if (combined.length < IV_BYTES) {
170
- throw new Error("Encrypted data is too short");
171
- }
172
- const iv = combined.slice(0, IV_BYTES);
173
- const ciphertext = combined.slice(IV_BYTES);
174
- try {
175
- const plaintext = await c.subtle.decrypt({ name: ALGO, iv }, key, ciphertext);
176
- return JSON.parse(new TextDecoder().decode(plaintext));
177
- } catch (err) {
178
- throw new Error("Decryption failed: data may be tampered or key is incorrect", { cause: err });
179
- }
180
- }
181
- };
182
- }
223
+ import { deepMerge, getBase64, stableStringify as stableStringify2 } from "@drakkar.software/starfish-protocol";
183
224
 
184
225
  // src/validate.ts
185
226
  var ValidationError = class extends Error {
@@ -211,7 +252,7 @@ var SyncManager = class {
211
252
  onConflict;
212
253
  maxRetries;
213
254
  encryptor;
214
- signData;
255
+ signer;
215
256
  logger;
216
257
  loggerName;
217
258
  validate;
@@ -225,11 +266,11 @@ var SyncManager = class {
225
266
  this.pushPath = options.pushPath;
226
267
  this.onConflict = options.onConflict ?? deepMerge;
227
268
  this.maxRetries = options.maxRetries ?? 3;
228
- this.signData = options.signData;
269
+ this.signer = options.signer;
229
270
  this.logger = options.logger;
230
271
  this.loggerName = options.loggerName ?? options.pullPath.split("/").filter(Boolean).pop() ?? options.pullPath;
231
272
  this.validate = options.validate;
232
- this.encryptor = options.encryptor ?? (options.encryptionSecret && options.encryptionSalt ? createEncryptor(options.encryptionSecret, options.encryptionSalt, options.encryptionInfo) : null);
273
+ this.encryptor = options.encryptor ?? null;
233
274
  }
234
275
  abort() {
235
276
  this.aborted = true;
@@ -289,15 +330,25 @@ var SyncManager = class {
289
330
  let pendingData = data;
290
331
  while (attempt <= this.maxRetries) {
291
332
  try {
292
- const payload = this.encryptor ? await this.encryptor.encrypt(pendingData) : pendingData;
293
- if (this.aborted) throw new AbortError();
294
- const sig = this.signData ? await this.signData(stableStringify(payload)) : void 0;
333
+ const sealed = this.encryptor ? await this.encryptor.encrypt(pendingData) : pendingData;
295
334
  if (this.aborted) throw new AbortError();
335
+ let payload = sealed;
336
+ if (this.signer) {
337
+ const { devEdPubHex, sign } = await this.signer.getSigner();
338
+ if (this.aborted) throw new AbortError();
339
+ const canonical = stableStringify2(sealed);
340
+ const sigBytes = await sign(new TextEncoder().encode(canonical));
341
+ if (this.aborted) throw new AbortError();
342
+ payload = {
343
+ ...sealed,
344
+ authorPubkey: devEdPubHex,
345
+ authorSignature: getBase64().encode(sigBytes)
346
+ };
347
+ }
296
348
  const result = await this.client.push(
297
349
  this.pushPath,
298
350
  payload,
299
- this.lastHash,
300
- sig
351
+ this.lastHash
301
352
  );
302
353
  if (this.aborted) throw new AbortError();
303
354
  this.lastHash = result.hash;
@@ -339,6 +390,9 @@ var SyncManager = class {
339
390
  }
340
391
  };
341
392
 
393
+ // src/index.ts
394
+ import { ENCRYPTED_KEY } from "@drakkar.software/starfish-protocol";
395
+
342
396
  // src/logger.ts
343
397
  var consoleSyncLogger = {
344
398
  pullStart: (s) => console.log(`[starfish:${s}] pull started`),
@@ -1171,170 +1225,6 @@ function createMultiStoreSync(options) {
1171
1225
  }
1172
1226
  return { serialize, restore, version };
1173
1227
  }
1174
-
1175
- // src/group-crypto.ts
1176
- import { x25519 } from "@noble/curves/ed25519.js";
1177
- import { getCrypto as getCrypto2, getBase64 as getBase642, IV_BYTES as IV_BYTES2, deriveKey as deriveKey2 } from "@drakkar.software/starfish-protocol";
1178
- function bytesToHex(bytes) {
1179
- return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
1180
- }
1181
- function hexToBytes(hex) {
1182
- const bytes = new Uint8Array(hex.length / 2);
1183
- for (let i = 0; i < bytes.length; i++) {
1184
- bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
1185
- }
1186
- return bytes;
1187
- }
1188
- var ALGO2 = "AES-GCM";
1189
- var GROUP_WRAP_SALT = "starfish-group-wrap";
1190
- var GROUP_WRAP_INFO = "starfish-group-wrap";
1191
- var GROUP_ECDH_DOMAIN = "starfish-group-ecdh";
1192
- var GROUP_DATA_INFO = "starfish-group";
1193
- var GEK_BYTES = 32;
1194
- async function deriveGroupKeyPair(passphrase, userId) {
1195
- const c = getCrypto2();
1196
- const enc = new TextEncoder();
1197
- const input = enc.encode(`${passphrase}:${userId}:${GROUP_ECDH_DOMAIN}`);
1198
- const hash = await c.subtle.digest("SHA-256", input);
1199
- const privateKeyBytes = new Uint8Array(hash);
1200
- const publicKeyBytes = x25519.getPublicKey(privateKeyBytes);
1201
- return { privateKey: bytesToHex(privateKeyBytes), publicKey: bytesToHex(publicKeyBytes) };
1202
- }
1203
- function generateGroupKey() {
1204
- const c = getCrypto2();
1205
- return bytesToHex(c.getRandomValues(new Uint8Array(GEK_BYTES)));
1206
- }
1207
- async function wrapGroupKey(gek, memberPublicKey, wrapperPrivateKey) {
1208
- const sharedSecret = x25519.getSharedSecret(hexToBytes(wrapperPrivateKey), hexToBytes(memberPublicKey));
1209
- const wrappingKey = await deriveKey2(bytesToHex(sharedSecret), GROUP_WRAP_SALT, GROUP_WRAP_INFO);
1210
- const c = getCrypto2();
1211
- const b64 = getBase642();
1212
- const iv = c.getRandomValues(new Uint8Array(IV_BYTES2));
1213
- const encrypted = await c.subtle.encrypt({ name: ALGO2, iv }, wrappingKey, hexToBytes(gek).buffer);
1214
- const combined = new Uint8Array(IV_BYTES2 + encrypted.byteLength);
1215
- combined.set(iv);
1216
- combined.set(new Uint8Array(encrypted), IV_BYTES2);
1217
- return b64.encode(combined);
1218
- }
1219
- async function unwrapGroupKey(wrapped, memberPrivateKey, adminPublicKey) {
1220
- const sharedSecret = x25519.getSharedSecret(hexToBytes(memberPrivateKey), hexToBytes(adminPublicKey));
1221
- const wrappingKey = await deriveKey2(bytesToHex(sharedSecret), GROUP_WRAP_SALT, GROUP_WRAP_INFO);
1222
- const b64 = getBase642();
1223
- const c = getCrypto2();
1224
- const combined = b64.decode(wrapped);
1225
- const iv = combined.slice(0, IV_BYTES2);
1226
- const ciphertext = combined.slice(IV_BYTES2);
1227
- try {
1228
- const decrypted = await c.subtle.decrypt({ name: ALGO2, iv }, wrappingKey, ciphertext);
1229
- return bytesToHex(new Uint8Array(decrypted));
1230
- } catch {
1231
- throw new Error("Failed to unwrap group key: decryption failed (wrong keys or corrupted data)");
1232
- }
1233
- }
1234
- async function createGroupKeyring(adminKeyPair, members, gek) {
1235
- const resolvedGek = gek ?? generateGroupKey();
1236
- const wrappedKeys = {};
1237
- for (const [memberId, memberPublicKey] of Object.entries(members)) {
1238
- wrappedKeys[memberId] = await wrapGroupKey(resolvedGek, memberPublicKey, adminKeyPair.privateKey);
1239
- }
1240
- const keyring = {
1241
- currentEpoch: 1,
1242
- epochs: {
1243
- "1": { adminPublicKey: adminKeyPair.publicKey, wrappedKeys }
1244
- }
1245
- };
1246
- return { keyring, gek: resolvedGek };
1247
- }
1248
- async function addGroupMember(keyring, adminKeyPair, currentGek, newMemberId, newMemberPublicKey) {
1249
- const epochKey = String(keyring.currentEpoch);
1250
- const epochKeyring = keyring.epochs[epochKey];
1251
- if (!epochKeyring) throw new Error(`Epoch ${keyring.currentEpoch} not found in keyring`);
1252
- if (epochKeyring.adminPublicKey !== adminKeyPair.publicKey) {
1253
- throw new Error(`Provided key pair does not match the admin public key stored in epoch ${keyring.currentEpoch}`);
1254
- }
1255
- const wrapped = await wrapGroupKey(currentGek, newMemberPublicKey, adminKeyPair.privateKey);
1256
- return {
1257
- ...keyring,
1258
- epochs: {
1259
- ...keyring.epochs,
1260
- [epochKey]: {
1261
- ...epochKeyring,
1262
- wrappedKeys: { ...epochKeyring.wrappedKeys, [newMemberId]: wrapped }
1263
- }
1264
- }
1265
- };
1266
- }
1267
- async function rotateGroupKey(keyring, adminKeyPair, remainingMembers, newGek) {
1268
- const epochKey = String(keyring.currentEpoch);
1269
- const epochKeyring = keyring.epochs[epochKey];
1270
- if (epochKeyring && epochKeyring.adminPublicKey !== adminKeyPair.publicKey) {
1271
- throw new Error(
1272
- `Provided key pair does not match the admin public key stored in epoch ${keyring.currentEpoch}`
1273
- );
1274
- }
1275
- const resolvedGek = newGek ?? generateGroupKey();
1276
- const newEpoch = keyring.currentEpoch + 1;
1277
- const wrappedKeys = {};
1278
- for (const [memberId, memberPublicKey] of Object.entries(remainingMembers)) {
1279
- wrappedKeys[memberId] = await wrapGroupKey(resolvedGek, memberPublicKey, adminKeyPair.privateKey);
1280
- }
1281
- const newKeyring = {
1282
- currentEpoch: newEpoch,
1283
- epochs: {
1284
- ...keyring.epochs,
1285
- [String(newEpoch)]: { adminPublicKey: adminKeyPair.publicKey, wrappedKeys }
1286
- }
1287
- };
1288
- return { keyring: newKeyring, gek: resolvedGek };
1289
- }
1290
- async function createGroupEncryptor(keyring, myIdentity, myPrivateKey) {
1291
- const epochEncryptors = /* @__PURE__ */ new Map();
1292
- for (const [epochStr, epochKeyring] of Object.entries(keyring.epochs)) {
1293
- const epoch = parseInt(epochStr, 10);
1294
- const wrapped = epochKeyring.wrappedKeys[myIdentity];
1295
- if (!wrapped) continue;
1296
- const gek = await unwrapGroupKey(wrapped, myPrivateKey, epochKeyring.adminPublicKey);
1297
- epochEncryptors.set(epoch, createEncryptor(gek, `epoch-${epoch}`, GROUP_DATA_INFO));
1298
- }
1299
- const currentEpoch = keyring.currentEpoch;
1300
- const currentEncryptor = epochEncryptors.get(currentEpoch);
1301
- if (!currentEncryptor) {
1302
- throw new Error(
1303
- `No wrapped key found for identity "${myIdentity}" in epoch ${currentEpoch}. Ensure the admin has added this member to the keyring.`
1304
- );
1305
- }
1306
- return {
1307
- async encrypt(data) {
1308
- const encrypted = await currentEncryptor.encrypt(data);
1309
- return { ...encrypted, _epoch: currentEpoch };
1310
- },
1311
- async decrypt(wrapper) {
1312
- const epoch = typeof wrapper._epoch === "number" ? wrapper._epoch : currentEpoch;
1313
- const encryptor = epochEncryptors.get(epoch);
1314
- if (!encryptor) {
1315
- throw new Error(
1316
- `No key available for epoch ${epoch}. This document was encrypted in a different epoch. Ensure your keyring is up to date.`
1317
- );
1318
- }
1319
- return encryptor.decrypt(wrapper);
1320
- }
1321
- };
1322
- }
1323
-
1324
- // src/entitlements.ts
1325
- async function pullEntitlements(client, userId, opts) {
1326
- const path = (opts?.path ?? "/pull/users/{userId}/entitlements").replace("{userId}", userId);
1327
- const field = opts?.field ?? "features";
1328
- try {
1329
- const result = await client.pull(path);
1330
- const list = result.data?.[field];
1331
- if (!Array.isArray(list)) return [];
1332
- return list.filter((s) => typeof s === "string");
1333
- } catch (err) {
1334
- if (err instanceof StarfishHttpError && err.status === 404) return [];
1335
- throw err;
1336
- }
1337
- }
1338
1228
  export {
1339
1229
  AbortError,
1340
1230
  ConflictError,
@@ -1344,7 +1234,7 @@ export {
1344
1234
  StarfishHttpError,
1345
1235
  SyncManager,
1346
1236
  ValidationError,
1347
- addGroupMember,
1237
+ buildRevocationList,
1348
1238
  classifyError,
1349
1239
  computeHash,
1350
1240
  configurePlatform,
@@ -1352,9 +1242,6 @@ export {
1352
1242
  createDebouncedPush,
1353
1243
  createDebouncedSync,
1354
1244
  createDedupFetch,
1355
- createEncryptor,
1356
- createGroupEncryptor,
1357
- createGroupKeyring,
1358
1245
  createIndexedDBStorage,
1359
1246
  createMetricsCollector,
1360
1247
  createMigrator,
@@ -1364,27 +1251,22 @@ export {
1364
1251
  createSoftDeleteResolver,
1365
1252
  createSuspenseResource,
1366
1253
  createUnionMerge,
1367
- deriveGroupKeyPair,
1368
1254
  exportData,
1369
1255
  exportToBlob,
1370
1256
  fetchServerConfig,
1371
- generateGroupKey,
1372
1257
  importData,
1373
1258
  isBackgroundSyncSupported,
1374
1259
  isServiceWorkerSupported,
1375
1260
  noopSyncLogger,
1376
1261
  pruneTombstones,
1377
- pullEntitlements,
1378
1262
  registerBackgroundSync,
1379
1263
  registerServiceWorker,
1380
- rotateGroupKey,
1381
- stableStringify2 as stableStringify,
1264
+ revocationListCanonicalSigningInput,
1265
+ stableStringify3 as stableStringify,
1382
1266
  startAdaptivePolling,
1383
1267
  startPolling,
1384
1268
  timestampWinner,
1385
1269
  unregisterServiceWorkers,
1386
- unwrapGroupKey,
1387
- withConflictMeta,
1388
- wrapGroupKey
1270
+ withConflictMeta
1389
1271
  };
1390
1272
  //# sourceMappingURL=index.js.map