@drakkar.software/starfish-client 3.0.0-alpha.2 → 3.0.0-alpha.4

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 (54) hide show
  1. package/dist/_crypto_helpers.d.ts +4 -0
  2. package/dist/bindings/zustand.js +8 -3
  3. package/dist/bindings/zustand.js.map +2 -2
  4. package/dist/cap-mint.d.ts +20 -0
  5. package/dist/cap-mint.js +12 -0
  6. package/dist/cap-mint.js.map +7 -0
  7. package/dist/directory.d.ts +9 -0
  8. package/dist/directory.js +24 -0
  9. package/dist/directory.js.map +7 -0
  10. package/dist/identity.d.ts +4 -82
  11. package/dist/identity.js +2 -354
  12. package/dist/identity.js.map +4 -4
  13. package/dist/index.js +8 -3
  14. package/dist/index.js.map +2 -2
  15. package/dist/keyring.d.ts +6 -0
  16. package/dist/keyring.js +26 -0
  17. package/dist/keyring.js.map +7 -0
  18. package/dist/pairing.d.ts +6 -0
  19. package/dist/pairing.js +26 -0
  20. package/dist/pairing.js.map +7 -0
  21. package/dist/recipients.d.ts +6 -0
  22. package/dist/recipients.js +16 -0
  23. package/dist/recipients.js.map +7 -0
  24. package/dist/types.d.ts +9 -1
  25. package/package.json +2 -2
  26. package/dist/append.d.ts +0 -50
  27. package/dist/background-sync.js +0 -29
  28. package/dist/bindings/broadcast.d.ts +0 -19
  29. package/dist/bindings/broadcast.js +0 -65
  30. package/dist/bindings/react.d.ts +0 -12
  31. package/dist/bindings/react.js +0 -25
  32. package/dist/bindings/suspense.js +0 -49
  33. package/dist/client.js +0 -112
  34. package/dist/config.js +0 -18
  35. package/dist/crypto.js +0 -49
  36. package/dist/debounced-sync.js +0 -120
  37. package/dist/dedup.js +0 -35
  38. package/dist/entitlements.js +0 -41
  39. package/dist/export.js +0 -115
  40. package/dist/group-crypto.d.ts +0 -111
  41. package/dist/group-crypto.js +0 -205
  42. package/dist/group-crypto.js.map +0 -7
  43. package/dist/history.js +0 -61
  44. package/dist/logger.js +0 -80
  45. package/dist/migrate.js +0 -38
  46. package/dist/mobile-lifecycle.js +0 -55
  47. package/dist/multi-store.js +0 -92
  48. package/dist/polling.js +0 -52
  49. package/dist/resolvers.js +0 -223
  50. package/dist/service-worker.js +0 -55
  51. package/dist/storage/indexeddb.js +0 -59
  52. package/dist/sync.js +0 -127
  53. package/dist/types.js +0 -18
  54. package/dist/validate.js +0 -28
@@ -1,55 +0,0 @@
1
- /**
2
- * Service Worker utilities for offline support and PWA functionality.
3
- */
4
- /** Check if service workers are supported in the current environment. */
5
- export function isServiceWorkerSupported() {
6
- return typeof navigator !== "undefined" && "serviceWorker" in navigator;
7
- }
8
- /**
9
- * Register a service worker for offline support.
10
- * Returns the registration, or null if not supported.
11
- */
12
- export async function registerServiceWorker(scriptUrl, opts) {
13
- if (!isServiceWorkerSupported())
14
- return null;
15
- try {
16
- const registration = await navigator.serviceWorker.register(scriptUrl, {
17
- scope: opts?.scope,
18
- });
19
- if (opts?.onUpdate) {
20
- registration.onupdatefound = () => {
21
- const installingWorker = registration.installing;
22
- if (installingWorker) {
23
- installingWorker.onstatechange = () => {
24
- if (installingWorker.state === "installed" &&
25
- navigator.serviceWorker.controller) {
26
- opts.onUpdate(registration);
27
- }
28
- };
29
- }
30
- };
31
- }
32
- return registration;
33
- }
34
- catch {
35
- return null;
36
- }
37
- }
38
- /** Unregister all service worker registrations. Returns true if any were unregistered. */
39
- export async function unregisterServiceWorkers() {
40
- if (!isServiceWorkerSupported())
41
- return false;
42
- try {
43
- const registrations = await navigator.serviceWorker.getRegistrations();
44
- let unregistered = false;
45
- for (const registration of registrations) {
46
- const result = await registration.unregister();
47
- if (result)
48
- unregistered = true;
49
- }
50
- return unregistered;
51
- }
52
- catch {
53
- return false;
54
- }
55
- }
@@ -1,59 +0,0 @@
1
- /**
2
- * IndexedDB-based storage adapter for Zustand persistence.
3
- * Implements the same interface as Zustand's StateStorage (getItem/setItem/removeItem).
4
- * Supports larger data than localStorage (typically 50MB+).
5
- */
6
- function openDB(dbName, storeName) {
7
- return new Promise((resolve, reject) => {
8
- const request = indexedDB.open(dbName, 1);
9
- request.onupgradeneeded = () => {
10
- const db = request.result;
11
- if (!db.objectStoreNames.contains(storeName)) {
12
- db.createObjectStore(storeName);
13
- }
14
- };
15
- request.onsuccess = () => resolve(request.result);
16
- request.onerror = () => reject(request.error);
17
- });
18
- }
19
- function idbRequest(request) {
20
- return new Promise((resolve, reject) => {
21
- request.onsuccess = () => resolve(request.result);
22
- request.onerror = () => reject(request.error);
23
- });
24
- }
25
- export function createIndexedDBStorage(opts) {
26
- const dbName = opts?.dbName ?? "starfish";
27
- const storeName = opts?.storeName ?? "state";
28
- let dbPromise = null;
29
- function getDB() {
30
- if (!dbPromise) {
31
- dbPromise = openDB(dbName, storeName).catch((err) => {
32
- dbPromise = null; // Reset so next call retries
33
- throw err;
34
- });
35
- }
36
- return dbPromise;
37
- }
38
- return {
39
- async getItem(name) {
40
- const db = await getDB();
41
- const tx = db.transaction(storeName, "readonly");
42
- const store = tx.objectStore(storeName);
43
- const result = await idbRequest(store.get(name));
44
- return result ?? null;
45
- },
46
- async setItem(name, value) {
47
- const db = await getDB();
48
- const tx = db.transaction(storeName, "readwrite");
49
- const store = tx.objectStore(storeName);
50
- await idbRequest(store.put(value, name));
51
- },
52
- async removeItem(name) {
53
- const db = await getDB();
54
- const tx = db.transaction(storeName, "readwrite");
55
- const store = tx.objectStore(storeName);
56
- await idbRequest(store.delete(name));
57
- },
58
- };
59
- }
package/dist/sync.js DELETED
@@ -1,127 +0,0 @@
1
- import { deepMerge, stableStringify } from "@drakkar.software/starfish-protocol";
2
- import { ConflictError } from "./types.js";
3
- import { createEncryptor } from "./crypto.js";
4
- import { ValidationError } from "./validate.js";
5
- export class SyncManager {
6
- client;
7
- pullPath;
8
- pushPath;
9
- onConflict;
10
- maxRetries;
11
- encryptor;
12
- signData;
13
- logger;
14
- loggerName;
15
- validate;
16
- lastHash = null;
17
- lastCheckpoint = 0;
18
- localData = {};
19
- constructor(options) {
20
- this.client = options.client;
21
- this.pullPath = options.pullPath;
22
- this.pushPath = options.pushPath;
23
- this.onConflict = options.onConflict ?? deepMerge;
24
- this.maxRetries = options.maxRetries ?? 3;
25
- this.signData = options.signData;
26
- this.logger = options.logger;
27
- this.loggerName = options.loggerName ?? options.pullPath.split("/").filter(Boolean).pop() ?? options.pullPath;
28
- this.validate = options.validate;
29
- this.encryptor =
30
- options.encryptor ??
31
- (options.encryptionSecret && options.encryptionSalt
32
- ? createEncryptor(options.encryptionSecret, options.encryptionSalt, options.encryptionInfo)
33
- : null);
34
- }
35
- getData() {
36
- return { ...this.localData };
37
- }
38
- getHash() {
39
- return this.lastHash;
40
- }
41
- getCheckpoint() {
42
- return this.lastCheckpoint;
43
- }
44
- async pull() {
45
- this.logger?.pullStart(this.loggerName);
46
- const start = performance.now();
47
- try {
48
- const result = await this.client.pull(this.pullPath, this.lastCheckpoint);
49
- if (this.encryptor) {
50
- const decrypted = await this.encryptor.decrypt(result.data);
51
- this.localData = decrypted;
52
- result.data = decrypted;
53
- }
54
- else if (this.lastCheckpoint > 0) {
55
- this.localData = deepMerge(this.localData, result.data);
56
- result.data = this.localData;
57
- }
58
- else {
59
- this.localData = result.data;
60
- }
61
- this.lastHash = result.hash;
62
- this.lastCheckpoint = result.timestamp;
63
- this.logger?.pullSuccess(this.loggerName, Math.round(performance.now() - start));
64
- return result;
65
- }
66
- catch (err) {
67
- this.logger?.pullError(this.loggerName, err instanceof Error ? err.message : String(err));
68
- throw err;
69
- }
70
- }
71
- async push(data) {
72
- if (this.validate) {
73
- const result = this.validate(data);
74
- if (result !== true)
75
- throw new ValidationError(result);
76
- }
77
- this.logger?.pushStart(this.loggerName);
78
- const start = performance.now();
79
- let attempt = 0;
80
- let pendingData = data;
81
- while (attempt <= this.maxRetries) {
82
- try {
83
- const payload = this.encryptor
84
- ? await this.encryptor.encrypt(pendingData)
85
- : pendingData;
86
- const sig = this.signData
87
- ? await this.signData(stableStringify(payload))
88
- : undefined;
89
- const result = await this.client.push(this.pushPath, payload, this.lastHash, sig);
90
- this.lastHash = result.hash;
91
- this.lastCheckpoint = result.timestamp;
92
- this.localData = pendingData;
93
- this.logger?.pushSuccess(this.loggerName, Math.round(performance.now() - start));
94
- return result;
95
- }
96
- catch (err) {
97
- if (!(err instanceof ConflictError) || attempt >= this.maxRetries) {
98
- this.logger?.pushError(this.loggerName, err instanceof Error ? err.message : String(err));
99
- throw err;
100
- }
101
- this.logger?.conflict(this.loggerName, attempt + 1);
102
- try {
103
- const remote = await this.client.pull(this.pullPath);
104
- const remoteData = this.encryptor
105
- ? await this.encryptor.decrypt(remote.data)
106
- : remote.data;
107
- this.lastHash = remote.hash;
108
- this.lastCheckpoint = remote.timestamp;
109
- pendingData = this.onConflict(pendingData, remoteData);
110
- }
111
- catch (resolveErr) {
112
- const msg = resolveErr instanceof Error ? resolveErr.message : String(resolveErr);
113
- this.logger?.pushError(this.loggerName, `Conflict resolution failed (attempt ${attempt + 1}): ${msg}`);
114
- throw resolveErr;
115
- }
116
- await new Promise(resolve => setTimeout(resolve, Math.min(100 * Math.pow(2, attempt), 2000) + Math.random() * 100));
117
- attempt++;
118
- }
119
- }
120
- throw new ConflictError();
121
- }
122
- async update(modifier) {
123
- await this.pull();
124
- const updated = modifier(this.localData);
125
- return this.push(updated);
126
- }
127
- }
package/dist/types.js DELETED
@@ -1,18 +0,0 @@
1
- /** Push conflict error (HTTP 409). */
2
- export class ConflictError extends Error {
3
- constructor() {
4
- super("hash_mismatch");
5
- this.name = "ConflictError";
6
- }
7
- }
8
- /** HTTP error from the Starfish server. */
9
- export class StarfishHttpError extends Error {
10
- status;
11
- body;
12
- constructor(status, body) {
13
- super(`HTTP ${status}: ${body}`);
14
- this.status = status;
15
- this.body = body;
16
- this.name = "StarfishHttpError";
17
- }
18
- }
package/dist/validate.js DELETED
@@ -1,28 +0,0 @@
1
- /** Error thrown when pre-push validation fails. */
2
- export class ValidationError extends Error {
3
- errors;
4
- constructor(errors) {
5
- super(`Validation failed: ${errors.join("; ")}`);
6
- this.errors = errors;
7
- this.name = "ValidationError";
8
- }
9
- }
10
- /**
11
- * Creates a validator from a JSON Schema object.
12
- * Requires an Ajv-compatible validate function.
13
- *
14
- * @example
15
- * ```ts
16
- * import Ajv from "ajv"
17
- * const ajv = new Ajv()
18
- * const validator = createSchemaValidator(ajv, mySchema)
19
- * ```
20
- */
21
- export function createSchemaValidator(ajv, schema) {
22
- const validate = ajv.compile(schema);
23
- return (data) => {
24
- if (validate(data))
25
- return true;
26
- return [ajv.errorsText(validate.errors)];
27
- };
28
- }