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

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 (53) hide show
  1. package/README.md +44 -0
  2. package/dist/_crypto_helpers.d.ts +4 -0
  3. package/dist/append-log.d.ts +228 -0
  4. package/dist/append-log.js +267 -0
  5. package/dist/bindings/legend.d.ts +23 -0
  6. package/dist/bindings/legend.js +32 -0
  7. package/dist/bindings/legend.js.map +2 -2
  8. package/dist/bindings/zustand.d.ts +72 -1
  9. package/dist/bindings/zustand.js +427 -63
  10. package/dist/bindings/zustand.js.map +3 -3
  11. package/dist/cap-mint.d.ts +20 -0
  12. package/dist/cap-mint.js +12 -0
  13. package/dist/cap-mint.js.map +7 -0
  14. package/dist/client.d.ts +128 -5
  15. package/dist/client.js +316 -37
  16. package/dist/config.d.ts +9 -0
  17. package/dist/directory.d.ts +9 -0
  18. package/dist/directory.js +24 -0
  19. package/dist/directory.js.map +7 -0
  20. package/dist/identity.d.ts +4 -82
  21. package/dist/identity.js +2 -354
  22. package/dist/identity.js.map +4 -4
  23. package/dist/index.d.ts +9 -5
  24. package/dist/index.js +578 -60
  25. package/dist/index.js.map +4 -4
  26. package/dist/keyring.d.ts +6 -0
  27. package/dist/keyring.js +26 -0
  28. package/dist/keyring.js.map +7 -0
  29. package/dist/logger.d.ts +3 -0
  30. package/dist/mobile-lifecycle.d.ts +28 -1
  31. package/dist/mobile-lifecycle.js +41 -2
  32. package/dist/mutate.d.ts +39 -0
  33. package/dist/pairing.d.ts +6 -0
  34. package/dist/pairing.js +26 -0
  35. package/dist/pairing.js.map +7 -0
  36. package/dist/polling.js +2 -2
  37. package/dist/recipients.d.ts +6 -0
  38. package/dist/recipients.js +16 -0
  39. package/dist/recipients.js.map +7 -0
  40. package/dist/sync.d.ts +28 -0
  41. package/dist/sync.js +68 -14
  42. package/dist/types.d.ts +62 -0
  43. package/package.json +2 -2
  44. package/dist/append.d.ts +0 -50
  45. package/dist/bindings/broadcast.d.ts +0 -19
  46. package/dist/bindings/broadcast.js +0 -65
  47. package/dist/bindings/react.d.ts +0 -12
  48. package/dist/bindings/react.js +0 -25
  49. package/dist/crypto.js +0 -49
  50. package/dist/entitlements.js +0 -41
  51. package/dist/group-crypto.d.ts +0 -111
  52. package/dist/group-crypto.js +0 -205
  53. package/dist/group-crypto.js.map +0 -7
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/bindings/legend.ts"],
4
- "sourcesContent": ["import { observable } from \"@legendapp/state\"\nimport type { Observable } from \"@legendapp/state\"\nimport type { SyncManager } from \"../sync.js\"\n\nexport interface StarfishLegendState {\n data: Record<string, unknown>\n syncing: boolean\n online: boolean\n dirty: boolean\n error: string | null\n}\n\nexport interface StarfishLegendStore {\n /** The observable state tree \u2014 read fields with `.get()` inside `observer` components. */\n state: Observable<StarfishLegendState>\n pull: () => Promise<void>\n set: (modifier: (current: Record<string, unknown>) => Record<string, unknown>) => void\n flush: () => Promise<void>\n setOnline: (online: boolean) => void\n}\n\nexport interface CreateStarfishObservableOptions {\n /** Unique name for this collection (used for persistence keys when applicable). */\n name: string\n syncManager: SyncManager\n /** Pass `produce` from `immer` to enable draft-based mutations in `set()`. */\n produce?: <T>(base: T, recipe: (draft: T) => T | void) => T\n}\n\nexport function createStarfishObservable(\n options: CreateStarfishObservableOptions,\n): StarfishLegendStore {\n const state = observable<StarfishLegendState>({\n data: {},\n syncing: false,\n online: true,\n dirty: false,\n error: null,\n })\n\n const flush = async (): Promise<void> => {\n if (state.syncing.get() || !state.dirty.get()) return\n state.syncing.set(true)\n state.error.set(null)\n try {\n await options.syncManager.push(state.data.get())\n state.data.set(options.syncManager.getData())\n state.dirty.set(false)\n } catch (err) {\n state.error.set(err instanceof Error ? err.message : String(err))\n } finally {\n state.syncing.set(false)\n }\n }\n\n const pull = async (): Promise<void> => {\n state.syncing.set(true)\n state.error.set(null)\n try {\n await options.syncManager.pull()\n state.data.set(options.syncManager.getData())\n } catch (err) {\n state.error.set(err instanceof Error ? err.message : String(err))\n } finally {\n state.syncing.set(false)\n }\n }\n\n const set = (\n modifier: (current: Record<string, unknown>) => Record<string, unknown>,\n ): void => {\n try {\n const current = state.data.get()\n const next = options.produce\n ? options.produce(\n current,\n modifier as (draft: Record<string, unknown>) => Record<string, unknown> | void,\n )\n : modifier(current)\n state.data.set(next)\n state.dirty.set(true)\n state.error.set(null)\n if (state.online.get()) flush().catch(() => {})\n } catch (err) {\n state.error.set(err instanceof Error ? err.message : String(err))\n }\n }\n\n const setOnline = (online: boolean): void => {\n state.online.set(online)\n if (online && state.dirty.get()) flush().catch(() => {})\n }\n\n return { state, pull, set, flush, setOnline }\n}\n"],
5
- "mappings": ";AAAA,SAAS,kBAAkB;AA6BpB,SAAS,yBACd,SACqB;AACrB,QAAM,QAAQ,WAAgC;AAAA,IAC5C,MAAM,CAAC;AAAA,IACP,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,OAAO;AAAA,EACT,CAAC;AAED,QAAM,QAAQ,YAA2B;AACvC,QAAI,MAAM,QAAQ,IAAI,KAAK,CAAC,MAAM,MAAM,IAAI,EAAG;AAC/C,UAAM,QAAQ,IAAI,IAAI;AACtB,UAAM,MAAM,IAAI,IAAI;AACpB,QAAI;AACF,YAAM,QAAQ,YAAY,KAAK,MAAM,KAAK,IAAI,CAAC;AAC/C,YAAM,KAAK,IAAI,QAAQ,YAAY,QAAQ,CAAC;AAC5C,YAAM,MAAM,IAAI,KAAK;AAAA,IACvB,SAAS,KAAK;AACZ,YAAM,MAAM,IAAI,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,IAClE,UAAE;AACA,YAAM,QAAQ,IAAI,KAAK;AAAA,IACzB;AAAA,EACF;AAEA,QAAM,OAAO,YAA2B;AACtC,UAAM,QAAQ,IAAI,IAAI;AACtB,UAAM,MAAM,IAAI,IAAI;AACpB,QAAI;AACF,YAAM,QAAQ,YAAY,KAAK;AAC/B,YAAM,KAAK,IAAI,QAAQ,YAAY,QAAQ,CAAC;AAAA,IAC9C,SAAS,KAAK;AACZ,YAAM,MAAM,IAAI,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,IAClE,UAAE;AACA,YAAM,QAAQ,IAAI,KAAK;AAAA,IACzB;AAAA,EACF;AAEA,QAAM,MAAM,CACV,aACS;AACT,QAAI;AACF,YAAM,UAAU,MAAM,KAAK,IAAI;AAC/B,YAAM,OAAO,QAAQ,UACjB,QAAQ;AAAA,QACN;AAAA,QACA;AAAA,MACF,IACA,SAAS,OAAO;AACpB,YAAM,KAAK,IAAI,IAAI;AACnB,YAAM,MAAM,IAAI,IAAI;AACpB,YAAM,MAAM,IAAI,IAAI;AACpB,UAAI,MAAM,OAAO,IAAI,EAAG,OAAM,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA,IAChD,SAAS,KAAK;AACZ,YAAM,MAAM,IAAI,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,IAClE;AAAA,EACF;AAEA,QAAM,YAAY,CAAC,WAA0B;AAC3C,UAAM,OAAO,IAAI,MAAM;AACvB,QAAI,UAAU,MAAM,MAAM,IAAI,EAAG,OAAM,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EACzD;AAEA,SAAO,EAAE,OAAO,MAAM,KAAK,OAAO,UAAU;AAC9C;",
4
+ "sourcesContent": ["import { observable } from \"@legendapp/state\"\nimport type { Observable } from \"@legendapp/state\"\nimport type { SyncManager } from \"../sync.js\"\nimport type { AppendLogCursor, AppendElement } from \"../append-log.js\"\n\nexport interface StarfishLegendState {\n data: Record<string, unknown>\n syncing: boolean\n online: boolean\n dirty: boolean\n error: string | null\n}\n\nexport interface StarfishLegendStore {\n /** The observable state tree \u2014 read fields with `.get()` inside `observer` components. */\n state: Observable<StarfishLegendState>\n pull: () => Promise<void>\n set: (modifier: (current: Record<string, unknown>) => Record<string, unknown>) => void\n flush: () => Promise<void>\n setOnline: (online: boolean) => void\n}\n\nexport interface CreateStarfishObservableOptions {\n /** Unique name for this collection (used for persistence keys when applicable). */\n name: string\n syncManager: SyncManager\n /** Pass `produce` from `immer` to enable draft-based mutations in `set()`. */\n produce?: <T>(base: T, recipe: (draft: T) => T | void) => T\n}\n\nexport function createStarfishObservable(\n options: CreateStarfishObservableOptions,\n): StarfishLegendStore {\n const state = observable<StarfishLegendState>({\n data: {},\n syncing: false,\n online: true,\n dirty: false,\n error: null,\n })\n\n const flush = async (): Promise<void> => {\n if (state.syncing.get() || !state.dirty.get()) return\n state.syncing.set(true)\n state.error.set(null)\n try {\n await options.syncManager.push(state.data.get())\n state.data.set(options.syncManager.getData())\n state.dirty.set(false)\n } catch (err) {\n state.error.set(err instanceof Error ? err.message : String(err))\n } finally {\n state.syncing.set(false)\n }\n }\n\n const pull = async (): Promise<void> => {\n state.syncing.set(true)\n state.error.set(null)\n try {\n await options.syncManager.pull()\n state.data.set(options.syncManager.getData())\n } catch (err) {\n state.error.set(err instanceof Error ? err.message : String(err))\n } finally {\n state.syncing.set(false)\n }\n }\n\n const set = (\n modifier: (current: Record<string, unknown>) => Record<string, unknown>,\n ): void => {\n try {\n const current = state.data.get()\n const next = options.produce\n ? options.produce(\n current,\n modifier as (draft: Record<string, unknown>) => Record<string, unknown> | void,\n )\n : modifier(current)\n state.data.set(next)\n state.dirty.set(true)\n state.error.set(null)\n if (state.online.get()) flush().catch(() => {})\n } catch (err) {\n state.error.set(err instanceof Error ? err.message : String(err))\n }\n }\n\n const setOnline = (online: boolean): void => {\n state.online.set(online)\n if (online && state.dirty.get()) flush().catch(() => {})\n }\n\n return { state, pull, set, flush, setOnline }\n}\n\n// \u2500\u2500 Append-only log binding \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// The reactive counterpart for an append-only collection, backed by an\n// `AppendLogCursor`. Read-only (a log only grows): no `set`/`flush`/`dirty`.\n// The cursor owns the items + checkpoint; persist via `getItems()` and\n// rehydrate by constructing the cursor with `initialItems`.\n//\n// The store assumes it is the SOLE driver of its cursor (it seeds from\n// `cursor.getItems()` at construction and updates only via its own `pull()`);\n// don't also call `cursor.pull()` directly, or the observable will go stale.\n\nexport interface StarfishLogObservableState {\n /** The full accumulated log, newest appended last. */\n items: AppendElement[]\n /** A `pull()` is in flight. */\n loading: boolean\n online: boolean\n error: string | null\n /** The cursor's checkpoint (max `ts` held). */\n checkpoint: number\n}\n\nexport interface StarfishLogObservableStore {\n /** The observable state tree \u2014 read fields with `.get()` inside `observer` components. */\n state: Observable<StarfishLogObservableState>\n /** Pull elements newer than the checkpoint, append them, return the new batch.\n * Errors are captured into `state.error`. */\n pull: () => Promise<AppendElement[]>\n setOnline: (online: boolean) => void\n}\n\nexport interface CreateStarfishLogObservableOptions {\n cursor: AppendLogCursor\n}\n\nexport function createStarfishLogObservable(\n options: CreateStarfishLogObservableOptions,\n): StarfishLogObservableStore {\n const { cursor } = options\n const state = observable<StarfishLogObservableState>({\n // Seed from the cursor so a warm-started cursor's items show immediately.\n items: cursor.getItems(),\n loading: false,\n online: true,\n error: null,\n checkpoint: cursor.getCheckpoint(),\n })\n\n const pull = async (): Promise<AppendElement[]> => {\n if (state.loading.get()) return []\n state.loading.set(true)\n state.error.set(null)\n try {\n const batch = await cursor.pull()\n state.items.set(cursor.getItems())\n state.checkpoint.set(cursor.getCheckpoint())\n return batch\n } catch (err) {\n state.error.set(err instanceof Error ? err.message : String(err))\n return []\n } finally {\n state.loading.set(false)\n }\n }\n\n const setOnline = (online: boolean): void => {\n state.online.set(online)\n }\n\n return { state, pull, setOnline }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -3,7 +3,8 @@ import { type StateStorage } from "zustand/middleware";
3
3
  import type { DevtoolsOptions } from "zustand/middleware";
4
4
  import type { Encryptor } from "@drakkar.software/starfish-protocol";
5
5
  import { SyncManager } from "../sync.js";
6
- import type { StarfishCapProvider, ConflictResolver } from "../types.js";
6
+ import { AppendLogCursor, type AppendElement } from "../append-log.js";
7
+ import type { StarfishCapProvider, ConflictResolver, PullCache } from "../types.js";
7
8
  import type { SyncLogger } from "../logger.js";
8
9
  import type { Validator } from "../validate.js";
9
10
  export interface StarfishState {
@@ -14,6 +15,14 @@ export interface StarfishState {
14
15
  error: string | null;
15
16
  /** Last-known server hash, persisted alongside `data`/`dirty`. Restored into the bound SyncManager on hydration. */
16
17
  hash: string | null;
18
+ /**
19
+ * True when the currently-shown `data` came from the offline read-through
20
+ * cache (a cache-first {@link StarfishActions.seed} or a {@link StarfishActions.pull}
21
+ * the client served from cache because the transport was unreachable) rather
22
+ * than a live server response. A successful live pull/flush clears it. Use it
23
+ * to drive an "offline / showing last-synced data" indicator.
24
+ */
25
+ stale: boolean;
17
26
  }
18
27
  export interface StarfishActions {
19
28
  pull: () => Promise<void>;
@@ -22,6 +31,14 @@ export interface StarfishActions {
22
31
  restore: (data: Record<string, unknown>) => void;
23
32
  flush: () => Promise<void>;
24
33
  setOnline: (online: boolean) => void;
34
+ /**
35
+ * Cache-first paint: populate `data` from the client's offline read-through
36
+ * cache (decrypting in memory for E2E collections) without touching the
37
+ * network. A no-op when the client has no cache configured or there's no
38
+ * (unexpired) entry. {@link useSyncInit} calls this once before the initial
39
+ * pull; the live pull then supersedes the seeded snapshot.
40
+ */
41
+ seed: () => Promise<void>;
25
42
  }
26
43
  export type StarfishStore = StarfishState & StarfishActions;
27
44
  export interface CreateStarfishStoreOptions {
@@ -104,6 +121,13 @@ export declare function useConnectivity(store: StoreApi<StarfishStore>): void;
104
121
  export declare function useLastSynced(store: StoreApi<StarfishStore>): string;
105
122
  export interface SyncInitConfig {
106
123
  serverUrl: string;
124
+ /**
125
+ * Optional server namespace, forwarded to the underlying {@link StarfishClient}
126
+ * so `pullPath`/`pushPath` are rewritten to `/v1/<namespace>/…` (signed AND sent).
127
+ * Leave unset for a root-mounted server. Pass the bare name (e.g. `"octochat"`),
128
+ * not `/v1/octochat` — the `/v1/` is added by the client.
129
+ */
130
+ namespace?: string;
107
131
  capProvider?: StarfishCapProvider;
108
132
  pullPath: string;
109
133
  pushPath: string;
@@ -115,6 +139,16 @@ export interface SyncInitConfig {
115
139
  storeName?: string;
116
140
  storage?: StateStorage | false;
117
141
  fetch?: typeof globalThis.fetch;
142
+ /**
143
+ * Offline-first read-through cache for the underlying {@link StarfishClient}
144
+ * (see {@link StarfishClientOptions.cache}). When set, the store seeds from the
145
+ * last-synced ciphertext on creation (cache-first paint, decrypted in memory)
146
+ * and the live pull falls back to it when the transport is unreachable; the
147
+ * store's `stale` flag reflects whether the shown data is from cache.
148
+ */
149
+ cache?: PullCache;
150
+ /** Max age (ms) for {@link cache} entries; see {@link StarfishClientOptions.cacheMaxAgeMs}. */
151
+ cacheMaxAgeMs?: number;
118
152
  logger?: SyncLogger;
119
153
  validate?: Validator;
120
154
  }
@@ -128,3 +162,40 @@ export interface SyncInitConfig {
128
162
  * Pass `null` to disable sync (returns `null`).
129
163
  */
130
164
  export declare function useSyncInit(config: SyncInitConfig | null): StoreApi<StarfishStore> | null;
165
+ export interface StarfishLogState {
166
+ /** The full accumulated log, newest appended last. */
167
+ items: AppendElement[];
168
+ /** A `pull()` is in flight. */
169
+ loading: boolean;
170
+ online: boolean;
171
+ error: string | null;
172
+ /** The cursor's checkpoint (max `ts` held). */
173
+ checkpoint: number;
174
+ }
175
+ export interface StarfishLogActions {
176
+ /** Pull elements newer than the checkpoint, append them, and return the new
177
+ * batch. Errors are captured into `error` (mirroring the SyncManager store). */
178
+ pull: () => Promise<AppendElement[]>;
179
+ setOnline: (online: boolean) => void;
180
+ }
181
+ export type StarfishLogStore = StarfishLogState & StarfishLogActions;
182
+ export interface CreateStarfishLogOptions {
183
+ cursor: AppendLogCursor;
184
+ devtools?: (storeCreator: any) => any;
185
+ }
186
+ export declare function createStarfishLog(options: CreateStarfishLogOptions): StoreApi<StarfishLogStore>;
187
+ /** Derived status for an append-log store. */
188
+ export type LogStatus = "idle" | "loading" | "error" | "offline";
189
+ /** Derive a single status from log store state. */
190
+ export declare function deriveLogStatus(state: StarfishLogState): LogStatus;
191
+ /** Use the full append-log store state and actions. */
192
+ export declare function useStarfishLog(store: StoreApi<StarfishLogStore>): StarfishLogStore;
193
+ /** Use only the accumulated items, with an optional selector for fine-grained subscriptions. */
194
+ export declare function useStarfishLogItems<T = AppendElement[]>(store: StoreApi<StarfishLogStore>, selector?: (items: AppendElement[]) => T): T;
195
+ /** Use the derived log status (idle | loading | error | offline). */
196
+ export declare function useLogStatus(store: StoreApi<StarfishLogStore>): LogStatus;
197
+ /** Subscribe to log status changes outside of React. Invoked immediately with the
198
+ * current status, then on every change. Returns an unsubscribe function. */
199
+ export declare function subscribeLogStatus(store: StoreApi<StarfishLogStore>, callback: (status: LogStatus) => void): () => void;
200
+ /** Binds browser online/offline events to the log store's setOnline action. Cleans up on unmount. */
201
+ export declare function useLogConnectivity(store: StoreApi<StarfishLogStore>): void;