@oxy.so/services 9.0.0 → 9.1.0

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 (42) hide show
  1. package/lib/commonjs/ui/components/OxyProvider.js +2 -2
  2. package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
  3. package/lib/commonjs/ui/context/OxyContext.js +4 -0
  4. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  5. package/lib/commonjs/ui/hooks/accountQueryPersistence.js +110 -80
  6. package/lib/commonjs/ui/hooks/accountQueryPersistence.js.map +1 -1
  7. package/lib/commonjs/ui/hooks/persistedQueryCache.js +101 -0
  8. package/lib/commonjs/ui/hooks/persistedQueryCache.js.map +1 -0
  9. package/lib/commonjs/ui/hooks/queryClient.js +43 -80
  10. package/lib/commonjs/ui/hooks/queryClient.js.map +1 -1
  11. package/lib/module/ui/components/OxyProvider.js +2 -2
  12. package/lib/module/ui/components/OxyProvider.js.map +1 -1
  13. package/lib/module/ui/context/OxyContext.js +4 -0
  14. package/lib/module/ui/context/OxyContext.js.map +1 -1
  15. package/lib/module/ui/hooks/accountQueryPersistence.js +109 -80
  16. package/lib/module/ui/hooks/accountQueryPersistence.js.map +1 -1
  17. package/lib/module/ui/hooks/persistedQueryCache.js +94 -0
  18. package/lib/module/ui/hooks/persistedQueryCache.js.map +1 -0
  19. package/lib/module/ui/hooks/queryClient.js +44 -81
  20. package/lib/module/ui/hooks/queryClient.js.map +1 -1
  21. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
  22. package/lib/typescript/commonjs/ui/hooks/accountQueryPersistence.d.ts +31 -13
  23. package/lib/typescript/commonjs/ui/hooks/accountQueryPersistence.d.ts.map +1 -1
  24. package/lib/typescript/commonjs/ui/hooks/persistedQueryCache.d.ts +44 -0
  25. package/lib/typescript/commonjs/ui/hooks/persistedQueryCache.d.ts.map +1 -0
  26. package/lib/typescript/commonjs/ui/hooks/queryClient.d.ts +15 -20
  27. package/lib/typescript/commonjs/ui/hooks/queryClient.d.ts.map +1 -1
  28. package/lib/typescript/commonjs/ui/icons/MaterialCommunityIcons.d.ts +1 -1
  29. package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
  30. package/lib/typescript/module/ui/hooks/accountQueryPersistence.d.ts +31 -13
  31. package/lib/typescript/module/ui/hooks/accountQueryPersistence.d.ts.map +1 -1
  32. package/lib/typescript/module/ui/hooks/persistedQueryCache.d.ts +44 -0
  33. package/lib/typescript/module/ui/hooks/persistedQueryCache.d.ts.map +1 -0
  34. package/lib/typescript/module/ui/hooks/queryClient.d.ts +15 -20
  35. package/lib/typescript/module/ui/hooks/queryClient.d.ts.map +1 -1
  36. package/lib/typescript/module/ui/icons/MaterialCommunityIcons.d.ts +1 -1
  37. package/package.json +1 -1
  38. package/src/ui/components/OxyProvider.tsx +2 -2
  39. package/src/ui/context/OxyContext.tsx +4 -0
  40. package/src/ui/hooks/accountQueryPersistence.ts +138 -91
  41. package/src/ui/hooks/persistedQueryCache.ts +126 -0
  42. package/src/ui/hooks/queryClient.ts +55 -98
@@ -8,24 +8,32 @@
8
8
  *
9
9
  * - Storage is keyed by the account (`oxy_account_queries:<accountId>`), so a
10
10
  * restore can only ever hydrate the account that is signed in.
11
- * - The buster is the build (`getOxyBuildId`), so no cache written by an older
12
- * bundle, in an older shape, is ever served.
13
- * - On a subject change the declared roots, memory-only ones included, and the
14
- * account's paused mutations are dropped
15
- * from memory synchronously, before anyone is woken, and the new account's
16
- * blob is restored. `isReady()` is false until that restore settles, and
11
+ * - Queries written by another build are dropped on restore; queued (paused)
12
+ * mutations are kept (`persistedQueryCache.ts`).
13
+ * - On a subject change the account's queries, memory-only ones included, and
14
+ * its paused mutations are dropped from memory synchronously, before anyone
15
+ * is woken. The leaving account's latest state is written first, a sign-out
16
+ * then deletes it, and only then is the next account's blob read.
17
+ * - `isReady()` is false until the signed-in account's blob is restored, and
17
18
  * `RequireOxyAuth` waits on it, so another account's rows never render.
18
- * - On sign-out the signed-out account's blob is deleted.
19
+ *
20
+ * Every storage operation runs through one queue, in order, and a restore
21
+ * checks that its account is still the active one in the same tick it hydrates.
19
22
  */
20
- import { type Mutation, type QueryClient, type QueryKey } from '@tanstack/react-query';
23
+ import { type Mutation, type Query, type QueryClient, type QueryKey } from '@tanstack/react-query';
21
24
  import type { StorageInterface } from '../utils/storageHelpers';
22
25
  /** An app's declaration of which data is private to the signed-in account. */
23
26
  export interface AccountQueriesConfig {
24
27
  /**
25
28
  * Query-key roots (`queryKey[0]`) whose data belongs to the signed-in account
26
- * and survives a restart.
29
+ * and survives a restart. `'all'`: every query the app runs, for an app whose
30
+ * every read may depend on who is signed in, so no root can be forgotten.
31
+ *
32
+ * The roots `accounts`, `sessions` and `devices` are reserved for the SDK's
33
+ * device-level data: an app must not use them. `users` and `privacy` are the
34
+ * SDK's too, and with `'all'` they are dropped on a switch like the app's.
27
35
  */
28
- roots: readonly string[];
36
+ roots: readonly string[] | 'all';
29
37
  /**
30
38
  * Roots that belong to the account too, but must not be written to disk
31
39
  * (signed URLs, AI output, search results): dropped on a switch, never persisted.
@@ -37,18 +45,28 @@ export interface AccountQueriesConfig {
37
45
  */
38
46
  mutationKeys?: readonly QueryKey[];
39
47
  }
48
+ /** Which queries and mutations an `AccountQueriesConfig` makes the account's. */
49
+ export interface AccountQueryOwnership {
50
+ /** Dropped from memory on a subject change. */
51
+ ownsQuery: (query: Query) => boolean;
52
+ /** Written to the account's blob. */
53
+ persistsQuery: (query: Query) => boolean;
54
+ ownsMutation: (mutation: Mutation) => boolean;
55
+ }
56
+ export declare function accountQueryOwnership(config: AccountQueriesConfig): AccountQueryOwnership;
40
57
  export declare const ACCOUNT_QUERY_CACHE_KEY = "oxy_account_queries";
41
58
  export interface AccountQueryPersistence {
42
59
  /** Storage became available (it initializes asynchronously). */
43
60
  setStorage(storage: StorageInterface): void;
44
61
  /** The signed-in account changed. `null` is a sign-out. Synchronous in-memory reset. */
45
62
  activate(accountId: string | null): void;
63
+ /** The owning provider mounted: restore and persist the active account. */
64
+ attach(): void;
65
+ /** The owning provider unmounted: stop touching the client and storage. */
66
+ detach(): void;
46
67
  /** True once the active account's cache is restored (always true signed out). */
47
68
  isReady(): boolean;
48
69
  subscribe(listener: () => void): () => void;
49
- /** Whether a mutation is account-scoped (so the SDK's own persister skips it). */
50
- ownsMutation(mutation: Mutation): boolean;
51
- dispose(): void;
52
70
  }
53
71
  export declare function createAccountQueryPersistence(queryClient: QueryClient, config: AccountQueriesConfig): AccountQueryPersistence;
54
72
  //# sourceMappingURL=accountQueryPersistence.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"accountQueryPersistence.d.ts","sourceRoot":"","sources":["../../../../../src/ui/hooks/accountQueryPersistence.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAW,KAAK,QAAQ,EAAc,KAAK,WAAW,EAAE,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAG5G,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAGhE,8EAA8E;AAC9E,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB;;;OAGG;IACH,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC;;;OAGG;IACH,YAAY,CAAC,EAAE,SAAS,QAAQ,EAAE,CAAC;CACpC;AAED,eAAO,MAAM,uBAAuB,wBAAwB,CAAC;AAI7D,MAAM,WAAW,uBAAuB;IACtC,gEAAgE;IAChE,UAAU,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC5C,wFAAwF;IACxF,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;IACzC,iFAAiF;IACjF,OAAO,IAAI,OAAO,CAAC;IACnB,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;IAC5C,kFAAkF;IAClF,YAAY,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC;IAC1C,OAAO,IAAI,IAAI,CAAC;CACjB;AAMD,wBAAgB,6BAA6B,CAC3C,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,oBAAoB,GAC3B,uBAAuB,CAmIzB"}
1
+ {"version":3,"file":"accountQueryPersistence.d.ts","sourceRoot":"","sources":["../../../../../src/ui/hooks/accountQueryPersistence.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAW,KAAK,QAAQ,EAAE,KAAK,KAAK,EAAE,KAAK,WAAW,EAAE,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAC5G,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAWhE,8EAA8E;AAC9E,MAAM,WAAW,oBAAoB;IACnC;;;;;;;;OAQG;IACH,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,KAAK,CAAC;IACjC;;;OAGG;IACH,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC;;;OAGG;IACH,YAAY,CAAC,EAAE,SAAS,QAAQ,EAAE,CAAC;CACpC;AAED,iFAAiF;AACjF,MAAM,WAAW,qBAAqB;IACpC,+CAA+C;IAC/C,SAAS,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC;IACrC,qCAAqC;IACrC,aAAa,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC;IACzC,YAAY,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,OAAO,CAAC;CAC/C;AAKD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,oBAAoB,GAAG,qBAAqB,CA0BzF;AAED,eAAO,MAAM,uBAAuB,wBAAwB,CAAC;AAI7D,MAAM,WAAW,uBAAuB;IACtC,gEAAgE;IAChE,UAAU,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC5C,wFAAwF;IACxF,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;IACzC,2EAA2E;IAC3E,MAAM,IAAI,IAAI,CAAC;IACf,2EAA2E;IAC3E,MAAM,IAAI,IAAI,CAAC;IACf,iFAAiF;IACjF,OAAO,IAAI,OAAO,CAAC;IACnB,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;CAC7C;AAMD,wBAAgB,6BAA6B,CAC3C,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,oBAAoB,GAC3B,uBAAuB,CA2HzB"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * The storage format and restore rules shared by the SDK's two query caches:
3
+ * the account-agnostic one (`attachQueryPersistence`) and the per-account one
4
+ * (`createAccountQueryPersistence`).
5
+ *
6
+ * A blob is `{ buster, timestamp, clientState }`, TanStack's persisted-client
7
+ * shape. Restoring it treats its two halves differently:
8
+ *
9
+ * - Queries are served before any refetch, so a query written by another
10
+ * build, in whatever shape that build used, is dropped (`getOxyBuildId`).
11
+ * - Paused mutations are the user's queued actions (a star, a send made
12
+ * offline). A deploy must not lose them, so they are restored whatever build
13
+ * wrote them; the current build's `setMutationDefaults` replays them.
14
+ */
15
+ import { type Mutation, type Query, type QueryClient } from '@tanstack/react-query';
16
+ /** The SDK's own account-agnostic query roots, persisted by `attachQueryPersistence`. */
17
+ export declare const PERSISTED_QUERY_PREFIXES: ReadonlyArray<string>;
18
+ /**
19
+ * SDK queries whose data (or key) carries a signed, expiring URL. Never
20
+ * written to disk: a restored one would serve an expired URL.
21
+ */
22
+ export declare const SDK_MEMORY_ONLY_ROOTS: ReadonlyArray<string>;
23
+ export interface PersistFilters {
24
+ shouldDehydrateQuery: (query: Query) => boolean;
25
+ shouldDehydrateMutation: (mutation: Mutation) => boolean;
26
+ }
27
+ export declare function rootOf(query: Query): string | null;
28
+ /** Serialize what the filters select, stamped with this build. */
29
+ export declare function snapshot(queryClient: QueryClient, filters: PersistFilters): string;
30
+ /**
31
+ * Hydrate a stored blob into the client, synchronously, so a caller can check
32
+ * it is still wanted immediately before. Returns false when there was nothing
33
+ * usable (absent, unreadable or expired), in which case the caller removes it.
34
+ */
35
+ export declare function hydrateSnapshot(queryClient: QueryClient, raw: string | null, maxAge: number): boolean;
36
+ /**
37
+ * Write the filtered cache on every change, at most once per `throttleMs`.
38
+ * `write` receives the serialized snapshot; `flush()` writes now.
39
+ */
40
+ export declare function subscribeSnapshots(queryClient: QueryClient, filters: PersistFilters, throttleMs: number, write: (serialized: string) => void): {
41
+ flush: () => void;
42
+ unsubscribe: () => void;
43
+ };
44
+ //# sourceMappingURL=persistedQueryCache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persistedQueryCache.d.ts","sourceRoot":"","sources":["../../../../../src/ui/hooks/persistedQueryCache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAIL,KAAK,QAAQ,EACb,KAAK,KAAK,EACV,KAAK,WAAW,EACjB,MAAM,uBAAuB,CAAC;AAG/B,yFAAyF;AACzF,eAAO,MAAM,wBAAwB,EAAE,aAAa,CAAC,MAAM,CAM1D,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,aAAa,CAAC,MAAM,CAKvD,CAAC;AAEF,MAAM,WAAW,cAAc;IAC7B,oBAAoB,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC;IAChD,uBAAuB,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,OAAO,CAAC;CAC1D;AAQD,wBAAgB,MAAM,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,GAAG,IAAI,CAGlD;AAED,kEAAkE;AAClE,wBAAgB,QAAQ,CAAC,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,cAAc,GAAG,MAAM,CAOlF;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAiBrG;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,WAAW,EACxB,OAAO,EAAE,cAAc,EACvB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,GAClC;IAAE,KAAK,EAAE,MAAM,IAAI,CAAC;IAAC,WAAW,EAAE,MAAM,IAAI,CAAA;CAAE,CAqBhD"}
@@ -5,27 +5,25 @@
5
5
  * - TanStack Query with `networkMode: 'offlineFirst'` for queries and mutations
6
6
  * so cached data is served immediately and mutations are queued (paused) while
7
7
  * the browser/device reports offline.
8
- * - `persistQueryClient(...)` from `@tanstack/react-query-persist-client` so that
9
- * query results AND paused mutations survive a cold restart (kill-and-relaunch).
8
+ * - `attachQueryPersistence(...)` so that query results AND paused mutations
9
+ * survive a cold restart (kill-and-relaunch); see `persistedQueryCache.ts`.
10
10
  * - `onlineManager` resume hook so paused mutations replay the moment the
11
11
  * network is reported back, even if the host app swapped in a custom
12
12
  * onlineManager implementation.
13
13
  *
14
- * Storage layer:
15
- * - React Native -> AsyncStorage via `createAsyncStoragePersister`.
16
- * - Web -> localStorage via `createSyncStoragePersister` (wrapped in the
17
- * async persister API for a single call site).
18
- * - Both adapters are content-shape compatible with our `StorageInterface`.
14
+ * Storage layer: the platform `StorageInterface` (AsyncStorage on native,
15
+ * localStorage on web).
19
16
  *
20
17
  * Whitelist policy:
21
18
  * - Persist every account/user/session/privacy query and every queued mutation.
22
19
  * - DO NOT persist large list queries (e.g. activity feeds) — they go stale
23
20
  * fast and would balloon storage. Add new keys to `PERSISTED_QUERY_PREFIXES`
24
- * when introducing reads that should survive restart.
21
+ * (`persistedQueryCache.ts`) when introducing reads that should survive restart.
25
22
  */
26
- import { QueryClient, type QueryKey } from '@tanstack/react-query';
27
- import { type PersistedClient } from '@tanstack/react-query-persist-client';
23
+ import { QueryClient } from '@tanstack/react-query';
24
+ import type { PersistedClient } from '@tanstack/react-query-persist-client';
28
25
  import type { StorageInterface } from '../utils/storageHelpers';
26
+ import { type AccountQueriesConfig } from './accountQueryPersistence';
29
27
  /** Retry only failures that another attempt can plausibly change. */
30
28
  export declare function shouldRetryQuery(failureCount: number, error: unknown): boolean;
31
29
  /**
@@ -47,21 +45,18 @@ export interface AttachPersistenceResult {
47
45
  unsubscribe: () => void;
48
46
  }
49
47
  /**
50
- * Wire `persistQueryClient` to the supplied storage adapter.
48
+ * Restore, then keep writing, the account-agnostic cache.
51
49
  *
52
- * Returns once the in-flight restore promise is available so callers can
53
- * `await result.restored` before rendering UI that depends on cached data.
50
+ * Queries written by another build are dropped; queued mutations are kept
51
+ * (`persistedQueryCache.ts`). Whatever the app declared account-scoped
52
+ * (`accountQueries`) is left to `createAccountQueryPersistence`, even under
53
+ * one of the SDK's prefixes, so private rows never land in the shared blob and
54
+ * a paused mutation cannot replay twice or under another account.
54
55
  *
55
56
  * Safe to no-op if `storage` is null/undefined (e.g. server-side render
56
57
  * with no host storage).
57
58
  */
58
- export declare const attachQueryPersistence: (queryClient: QueryClient, storage: StorageInterface | null | undefined,
59
- /**
60
- * Mutation keys the app declared account-scoped (`accountQueries`). Those are
61
- * persisted per account by `createAccountQueryPersistence`, never here, so a
62
- * paused mutation cannot replay twice or under another account.
63
- */
64
- accountMutationKeys?: readonly QueryKey[]) => AttachPersistenceResult;
59
+ export declare const attachQueryPersistence: (queryClient: QueryClient, storage: StorageInterface | null | undefined, accountQueries?: AccountQueriesConfig) => AttachPersistenceResult;
65
60
  /**
66
61
  * Remove the persisted query+mutation cache (used on full sign-out / data reset).
67
62
  * Safe to call even if persistence was never attached.
@@ -1 +1 @@
1
- {"version":3,"file":"queryClient.d.ts","sourceRoot":"","sources":["../../../../../src/ui/hooks/queryClient.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,WAAW,EAAqD,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtH,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,sCAAsC,CAAC;AAG9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAehE,qEAAqE;AACrE,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAK9E;AAwDD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,QAAO,WA6CpC,CAAC;AAEF,MAAM,WAAW,uBAAuB;IACtC,wEAAwE;IACxE,QAAQ,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,8DAA8D;IAC9D,WAAW,EAAE,MAAM,IAAI,CAAC;CACzB;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,GACjC,aAAa,WAAW,EACxB,SAAS,gBAAgB,GAAG,IAAI,GAAG,SAAS;AAC5C;;;;GAIG;AACH,sBAAqB,SAAS,QAAQ,EAAO,KAC5C,uBAwCF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,eAAe,GAAU,SAAS,gBAAgB,KAAG,OAAO,CAAC,IAAI,CAQ7E,CAAC;AAEF;;GAEG;AACH,YAAY,EAAE,eAAe,EAAE,CAAC"}
1
+ {"version":3,"file":"queryClient.d.ts","sourceRoot":"","sources":["../../../../../src/ui/hooks/queryClient.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,WAAW,EAA4C,MAAM,uBAAuB,CAAC;AAC9F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AAE5E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAyB,KAAK,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAe7F,qEAAqE;AACrE,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAK9E;AAeD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,QAAO,WA6CpC,CAAC;AAEF,MAAM,WAAW,uBAAuB;IACtC,wEAAwE;IACxE,QAAQ,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,8DAA8D;IAC9D,WAAW,EAAE,MAAM,IAAI,CAAC;CACzB;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,sBAAsB,GACjC,aAAa,WAAW,EACxB,SAAS,gBAAgB,GAAG,IAAI,GAAG,SAAS,EAC5C,iBAAiB,oBAAoB,KACpC,uBA8CF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,eAAe,GAAU,SAAS,gBAAgB,KAAG,OAAO,CAAC,IAAI,CAQ7E,CAAC;AAEF;;GAEG;AACH,YAAY,EAAE,eAAe,EAAE,CAAC"}
@@ -1,4 +1,4 @@
1
1
  /** Glyphs are decorative: hidden from assistive technology (see `decorativeIconSet`). */
2
- declare const MaterialCommunityIcons: import("@expo/vector-icons/build/createIconSet").Icon<"function" | "sort" | "at" | "label" | "link" | "ellipse" | "image" | "switch" | "text" | "alert" | "alert-circle" | "alert-circle-outline" | "apps" | "archive" | "arrow-down" | "arrow-up" | "briefcase-outline" | "bug" | "calendar" | "calendar-outline" | "camera" | "camera-outline" | "card" | "card-outline" | "cart-outline" | "chevron-down" | "chevron-up" | "close" | "close-circle" | "cloud-outline" | "cloud-upload" | "cloud-upload-outline" | "cog" | "cog-outline" | "cube-outline" | "download" | "download-outline" | "earth" | "ellipse-outline" | "eye" | "eye-off-outline" | "filter-outline" | "flash" | "folder" | "folder-open-outline" | "folder-outline" | "gift" | "gift-outline" | "grid" | "heart" | "heart-outline" | "help-circle" | "help-circle-outline" | "image-outline" | "information" | "information-outline" | "key" | "leaf" | "library" | "pencil-outline" | "play" | "pulse" | "radio" | "receipt-outline" | "resize" | "rocket" | "scale" | "school-outline" | "server" | "share-outline" | "shield" | "star" | "sync" | "trophy" | "trophy-outline" | "volume-off" | "wallet" | "wallet-outline" | "watch" | "wifi" | "solid" | "opacity" | "wrap" | "account" | "account-circle" | "account-cog-outline" | "account-eye-outline" | "account-group" | "account-multiple" | "account-multiple-outline" | "account-outline" | "account-plus" | "account-search" | "account-switch" | "android" | "archive-arrow-down" | "arrow-left" | "bell" | "bell-outline" | "bio" | "bullhorn" | "cake-variant" | "cake-variant-outline" | "cancel" | "cellphone" | "cellphone-basic" | "cellphone-message" | "cellphone-screenshot" | "chart-line" | "check-all" | "check-circle" | "chip" | "circle-outline" | "clock" | "clock-outline" | "code-tags" | "contain" | "content-copy" | "controller" | "cookie" | "credit-card" | "database-outline" | "delete" | "delete-outline" | "devices" | "dots-horizontal" | "email" | "email-outline" | "file" | "file-document" | "file-document-edit-outline" | "file-document-outline" | "file-excel-outline" | "file-outline" | "file-pdf-box" | "folder-image" | "folder-zip-outline" | "ghost" | "history" | "image-multiple-outline" | "link-variant" | "loading" | "lock-outline" | "logout" | "magnify" | "map-marker" | "map-marker-multiple" | "map-marker-outline" | "message" | "message-outline" | "minus-circle" | "motion" | "music-note-outline" | "network" | "null" | "numeric" | "open-in-app" | "openid" | "palette" | "palette-outline" | "pan" | "phone" | "phone-outline" | "plus" | "plus-circle" | "post" | "presentation" | "react" | "read" | "security" | "shield-account-outline" | "shield-check" | "shield-search" | "signature" | "sort-alphabetical-variant" | "sort-numeric-variant" | "store" | "tag" | "tag-outline" | "text-box-outline" | "theme-light-dark" | "transfer" | "translate" | "trash-can-outline" | "tray-arrow-down" | "update" | "video-outline" | "web" | "wifi-off" | "wrench" | "xml", "OxyServicesMaterialCommunityIcons">;
2
+ declare const MaterialCommunityIcons: import("@expo/vector-icons/build/createIconSet").Icon<"function" | "devices" | "sort" | "at" | "label" | "link" | "ellipse" | "image" | "switch" | "text" | "alert" | "alert-circle" | "alert-circle-outline" | "apps" | "archive" | "arrow-down" | "arrow-up" | "briefcase-outline" | "bug" | "calendar" | "calendar-outline" | "camera" | "camera-outline" | "card" | "card-outline" | "cart-outline" | "chevron-down" | "chevron-up" | "close" | "close-circle" | "cloud-outline" | "cloud-upload" | "cloud-upload-outline" | "cog" | "cog-outline" | "cube-outline" | "download" | "download-outline" | "earth" | "ellipse-outline" | "eye" | "eye-off-outline" | "filter-outline" | "flash" | "folder" | "folder-open-outline" | "folder-outline" | "gift" | "gift-outline" | "grid" | "heart" | "heart-outline" | "help-circle" | "help-circle-outline" | "image-outline" | "information" | "information-outline" | "key" | "leaf" | "library" | "pencil-outline" | "play" | "pulse" | "radio" | "receipt-outline" | "resize" | "rocket" | "scale" | "school-outline" | "server" | "share-outline" | "shield" | "star" | "sync" | "trophy" | "trophy-outline" | "volume-off" | "wallet" | "wallet-outline" | "watch" | "wifi" | "solid" | "opacity" | "wrap" | "account" | "account-circle" | "account-cog-outline" | "account-eye-outline" | "account-group" | "account-multiple" | "account-multiple-outline" | "account-outline" | "account-plus" | "account-search" | "account-switch" | "android" | "archive-arrow-down" | "arrow-left" | "bell" | "bell-outline" | "bio" | "bullhorn" | "cake-variant" | "cake-variant-outline" | "cancel" | "cellphone" | "cellphone-basic" | "cellphone-message" | "cellphone-screenshot" | "chart-line" | "check-all" | "check-circle" | "chip" | "circle-outline" | "clock" | "clock-outline" | "code-tags" | "contain" | "content-copy" | "controller" | "cookie" | "credit-card" | "database-outline" | "delete" | "delete-outline" | "dots-horizontal" | "email" | "email-outline" | "file" | "file-document" | "file-document-edit-outline" | "file-document-outline" | "file-excel-outline" | "file-outline" | "file-pdf-box" | "folder-image" | "folder-zip-outline" | "ghost" | "history" | "image-multiple-outline" | "link-variant" | "loading" | "lock-outline" | "logout" | "magnify" | "map-marker" | "map-marker-multiple" | "map-marker-outline" | "message" | "message-outline" | "minus-circle" | "motion" | "music-note-outline" | "network" | "null" | "numeric" | "open-in-app" | "openid" | "palette" | "palette-outline" | "pan" | "phone" | "phone-outline" | "plus" | "plus-circle" | "post" | "presentation" | "react" | "read" | "security" | "shield-account-outline" | "shield-check" | "shield-search" | "signature" | "sort-alphabetical-variant" | "sort-numeric-variant" | "store" | "tag" | "tag-outline" | "text-box-outline" | "theme-light-dark" | "transfer" | "translate" | "trash-can-outline" | "tray-arrow-down" | "update" | "video-outline" | "web" | "wifi-off" | "wrench" | "xml", "OxyServicesMaterialCommunityIcons">;
3
3
  export default MaterialCommunityIcons;
4
4
  //# sourceMappingURL=MaterialCommunityIcons.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"OxyContext.d.ts","sourceRoot":"","sources":["../../../../../src/ui/context/OxyContext.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAgF/B,OAAO,KAAK,EACV,eAAe,EACf,uBAAuB,EAExB,MAAM,sBAAmB,CAAC;AAU3B,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,sBAAmB,CAAC;AAMhG,KAAK,0BAA0B,GAAG,IAAI,CACpC,eAAe,EACb,QAAQ,GACR,QAAQ,GACR,WAAW,GACX,iBAAiB,GACjB,aAAa,GACb,iBAAiB,GACjB,aAAa,GACb,cAAc,GACd,iBAAiB,GACjB,kBAAkB,GAClB,mBAAmB,CACtB,CAAC;AAIF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,uBAAuB,CAixChE,CAAC;AAMF,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,QAAQ,CAAC,IAAI,0BAA0B;;CAMxC;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,MAAM,QAAO,eAMzB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,cAAc,QAAO,eAAe,GAAG,IAAqC,CAAC;AAE1F,kFAAkF;AAClF,eAAO,MAAM,iBAAiB,QAAO,0BAMpC,CAAC"}
1
+ {"version":3,"file":"OxyContext.d.ts","sourceRoot":"","sources":["../../../../../src/ui/context/OxyContext.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAgF/B,OAAO,KAAK,EACV,eAAe,EACf,uBAAuB,EAExB,MAAM,sBAAmB,CAAC;AAU3B,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,sBAAmB,CAAC;AAMhG,KAAK,0BAA0B,GAAG,IAAI,CACpC,eAAe,EACb,QAAQ,GACR,QAAQ,GACR,WAAW,GACX,iBAAiB,GACjB,aAAa,GACb,iBAAiB,GACjB,aAAa,GACb,cAAc,GACd,iBAAiB,GACjB,kBAAkB,GAClB,mBAAmB,CACtB,CAAC;AAIF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,uBAAuB,CAqxChE,CAAC;AAMF,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,QAAQ,CAAC,IAAI,0BAA0B;;CAMxC;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,MAAM,QAAO,eAMzB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,cAAc,QAAO,eAAe,GAAG,IAAqC,CAAC;AAE1F,kFAAkF;AAClF,eAAO,MAAM,iBAAiB,QAAO,0BAMpC,CAAC"}
@@ -8,24 +8,32 @@
8
8
  *
9
9
  * - Storage is keyed by the account (`oxy_account_queries:<accountId>`), so a
10
10
  * restore can only ever hydrate the account that is signed in.
11
- * - The buster is the build (`getOxyBuildId`), so no cache written by an older
12
- * bundle, in an older shape, is ever served.
13
- * - On a subject change the declared roots, memory-only ones included, and the
14
- * account's paused mutations are dropped
15
- * from memory synchronously, before anyone is woken, and the new account's
16
- * blob is restored. `isReady()` is false until that restore settles, and
11
+ * - Queries written by another build are dropped on restore; queued (paused)
12
+ * mutations are kept (`persistedQueryCache.ts`).
13
+ * - On a subject change the account's queries, memory-only ones included, and
14
+ * its paused mutations are dropped from memory synchronously, before anyone
15
+ * is woken. The leaving account's latest state is written first, a sign-out
16
+ * then deletes it, and only then is the next account's blob read.
17
+ * - `isReady()` is false until the signed-in account's blob is restored, and
17
18
  * `RequireOxyAuth` waits on it, so another account's rows never render.
18
- * - On sign-out the signed-out account's blob is deleted.
19
+ *
20
+ * Every storage operation runs through one queue, in order, and a restore
21
+ * checks that its account is still the active one in the same tick it hydrates.
19
22
  */
20
- import { type Mutation, type QueryClient, type QueryKey } from '@tanstack/react-query';
23
+ import { type Mutation, type Query, type QueryClient, type QueryKey } from '@tanstack/react-query';
21
24
  import type { StorageInterface } from '../utils/storageHelpers.js';
22
25
  /** An app's declaration of which data is private to the signed-in account. */
23
26
  export interface AccountQueriesConfig {
24
27
  /**
25
28
  * Query-key roots (`queryKey[0]`) whose data belongs to the signed-in account
26
- * and survives a restart.
29
+ * and survives a restart. `'all'`: every query the app runs, for an app whose
30
+ * every read may depend on who is signed in, so no root can be forgotten.
31
+ *
32
+ * The roots `accounts`, `sessions` and `devices` are reserved for the SDK's
33
+ * device-level data: an app must not use them. `users` and `privacy` are the
34
+ * SDK's too, and with `'all'` they are dropped on a switch like the app's.
27
35
  */
28
- roots: readonly string[];
36
+ roots: readonly string[] | 'all';
29
37
  /**
30
38
  * Roots that belong to the account too, but must not be written to disk
31
39
  * (signed URLs, AI output, search results): dropped on a switch, never persisted.
@@ -37,18 +45,28 @@ export interface AccountQueriesConfig {
37
45
  */
38
46
  mutationKeys?: readonly QueryKey[];
39
47
  }
48
+ /** Which queries and mutations an `AccountQueriesConfig` makes the account's. */
49
+ export interface AccountQueryOwnership {
50
+ /** Dropped from memory on a subject change. */
51
+ ownsQuery: (query: Query) => boolean;
52
+ /** Written to the account's blob. */
53
+ persistsQuery: (query: Query) => boolean;
54
+ ownsMutation: (mutation: Mutation) => boolean;
55
+ }
56
+ export declare function accountQueryOwnership(config: AccountQueriesConfig): AccountQueryOwnership;
40
57
  export declare const ACCOUNT_QUERY_CACHE_KEY = "oxy_account_queries";
41
58
  export interface AccountQueryPersistence {
42
59
  /** Storage became available (it initializes asynchronously). */
43
60
  setStorage(storage: StorageInterface): void;
44
61
  /** The signed-in account changed. `null` is a sign-out. Synchronous in-memory reset. */
45
62
  activate(accountId: string | null): void;
63
+ /** The owning provider mounted: restore and persist the active account. */
64
+ attach(): void;
65
+ /** The owning provider unmounted: stop touching the client and storage. */
66
+ detach(): void;
46
67
  /** True once the active account's cache is restored (always true signed out). */
47
68
  isReady(): boolean;
48
69
  subscribe(listener: () => void): () => void;
49
- /** Whether a mutation is account-scoped (so the SDK's own persister skips it). */
50
- ownsMutation(mutation: Mutation): boolean;
51
- dispose(): void;
52
70
  }
53
71
  export declare function createAccountQueryPersistence(queryClient: QueryClient, config: AccountQueriesConfig): AccountQueryPersistence;
54
72
  //# sourceMappingURL=accountQueryPersistence.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"accountQueryPersistence.d.ts","sourceRoot":"","sources":["../../../../../src/ui/hooks/accountQueryPersistence.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAW,KAAK,QAAQ,EAAc,KAAK,WAAW,EAAE,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAG5G,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAAyB,CAAC;AAGhE,8EAA8E;AAC9E,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB;;;OAGG;IACH,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC;;;OAGG;IACH,YAAY,CAAC,EAAE,SAAS,QAAQ,EAAE,CAAC;CACpC;AAED,eAAO,MAAM,uBAAuB,wBAAwB,CAAC;AAI7D,MAAM,WAAW,uBAAuB;IACtC,gEAAgE;IAChE,UAAU,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC5C,wFAAwF;IACxF,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;IACzC,iFAAiF;IACjF,OAAO,IAAI,OAAO,CAAC;IACnB,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;IAC5C,kFAAkF;IAClF,YAAY,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC;IAC1C,OAAO,IAAI,IAAI,CAAC;CACjB;AAMD,wBAAgB,6BAA6B,CAC3C,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,oBAAoB,GAC3B,uBAAuB,CAmIzB"}
1
+ {"version":3,"file":"accountQueryPersistence.d.ts","sourceRoot":"","sources":["../../../../../src/ui/hooks/accountQueryPersistence.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAW,KAAK,QAAQ,EAAE,KAAK,KAAK,EAAE,KAAK,WAAW,EAAE,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAC5G,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAAyB,CAAC;AAWhE,8EAA8E;AAC9E,MAAM,WAAW,oBAAoB;IACnC;;;;;;;;OAQG;IACH,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,KAAK,CAAC;IACjC;;;OAGG;IACH,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC;;;OAGG;IACH,YAAY,CAAC,EAAE,SAAS,QAAQ,EAAE,CAAC;CACpC;AAED,iFAAiF;AACjF,MAAM,WAAW,qBAAqB;IACpC,+CAA+C;IAC/C,SAAS,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC;IACrC,qCAAqC;IACrC,aAAa,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC;IACzC,YAAY,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,OAAO,CAAC;CAC/C;AAKD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,oBAAoB,GAAG,qBAAqB,CA0BzF;AAED,eAAO,MAAM,uBAAuB,wBAAwB,CAAC;AAI7D,MAAM,WAAW,uBAAuB;IACtC,gEAAgE;IAChE,UAAU,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC5C,wFAAwF;IACxF,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;IACzC,2EAA2E;IAC3E,MAAM,IAAI,IAAI,CAAC;IACf,2EAA2E;IAC3E,MAAM,IAAI,IAAI,CAAC;IACf,iFAAiF;IACjF,OAAO,IAAI,OAAO,CAAC;IACnB,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;CAC7C;AAMD,wBAAgB,6BAA6B,CAC3C,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,oBAAoB,GAC3B,uBAAuB,CA2HzB"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * The storage format and restore rules shared by the SDK's two query caches:
3
+ * the account-agnostic one (`attachQueryPersistence`) and the per-account one
4
+ * (`createAccountQueryPersistence`).
5
+ *
6
+ * A blob is `{ buster, timestamp, clientState }`, TanStack's persisted-client
7
+ * shape. Restoring it treats its two halves differently:
8
+ *
9
+ * - Queries are served before any refetch, so a query written by another
10
+ * build, in whatever shape that build used, is dropped (`getOxyBuildId`).
11
+ * - Paused mutations are the user's queued actions (a star, a send made
12
+ * offline). A deploy must not lose them, so they are restored whatever build
13
+ * wrote them; the current build's `setMutationDefaults` replays them.
14
+ */
15
+ import { type Mutation, type Query, type QueryClient } from '@tanstack/react-query';
16
+ /** The SDK's own account-agnostic query roots, persisted by `attachQueryPersistence`. */
17
+ export declare const PERSISTED_QUERY_PREFIXES: ReadonlyArray<string>;
18
+ /**
19
+ * SDK queries whose data (or key) carries a signed, expiring URL. Never
20
+ * written to disk: a restored one would serve an expired URL.
21
+ */
22
+ export declare const SDK_MEMORY_ONLY_ROOTS: ReadonlyArray<string>;
23
+ export interface PersistFilters {
24
+ shouldDehydrateQuery: (query: Query) => boolean;
25
+ shouldDehydrateMutation: (mutation: Mutation) => boolean;
26
+ }
27
+ export declare function rootOf(query: Query): string | null;
28
+ /** Serialize what the filters select, stamped with this build. */
29
+ export declare function snapshot(queryClient: QueryClient, filters: PersistFilters): string;
30
+ /**
31
+ * Hydrate a stored blob into the client, synchronously, so a caller can check
32
+ * it is still wanted immediately before. Returns false when there was nothing
33
+ * usable (absent, unreadable or expired), in which case the caller removes it.
34
+ */
35
+ export declare function hydrateSnapshot(queryClient: QueryClient, raw: string | null, maxAge: number): boolean;
36
+ /**
37
+ * Write the filtered cache on every change, at most once per `throttleMs`.
38
+ * `write` receives the serialized snapshot; `flush()` writes now.
39
+ */
40
+ export declare function subscribeSnapshots(queryClient: QueryClient, filters: PersistFilters, throttleMs: number, write: (serialized: string) => void): {
41
+ flush: () => void;
42
+ unsubscribe: () => void;
43
+ };
44
+ //# sourceMappingURL=persistedQueryCache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persistedQueryCache.d.ts","sourceRoot":"","sources":["../../../../../src/ui/hooks/persistedQueryCache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAIL,KAAK,QAAQ,EACb,KAAK,KAAK,EACV,KAAK,WAAW,EACjB,MAAM,uBAAuB,CAAC;AAG/B,yFAAyF;AACzF,eAAO,MAAM,wBAAwB,EAAE,aAAa,CAAC,MAAM,CAM1D,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,aAAa,CAAC,MAAM,CAKvD,CAAC;AAEF,MAAM,WAAW,cAAc;IAC7B,oBAAoB,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC;IAChD,uBAAuB,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,OAAO,CAAC;CAC1D;AAQD,wBAAgB,MAAM,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,GAAG,IAAI,CAGlD;AAED,kEAAkE;AAClE,wBAAgB,QAAQ,CAAC,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,cAAc,GAAG,MAAM,CAOlF;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAiBrG;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,WAAW,EACxB,OAAO,EAAE,cAAc,EACvB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,GAClC;IAAE,KAAK,EAAE,MAAM,IAAI,CAAC;IAAC,WAAW,EAAE,MAAM,IAAI,CAAA;CAAE,CAqBhD"}
@@ -5,27 +5,25 @@
5
5
  * - TanStack Query with `networkMode: 'offlineFirst'` for queries and mutations
6
6
  * so cached data is served immediately and mutations are queued (paused) while
7
7
  * the browser/device reports offline.
8
- * - `persistQueryClient(...)` from `@tanstack/react-query-persist-client` so that
9
- * query results AND paused mutations survive a cold restart (kill-and-relaunch).
8
+ * - `attachQueryPersistence(...)` so that query results AND paused mutations
9
+ * survive a cold restart (kill-and-relaunch); see `persistedQueryCache.ts`.
10
10
  * - `onlineManager` resume hook so paused mutations replay the moment the
11
11
  * network is reported back, even if the host app swapped in a custom
12
12
  * onlineManager implementation.
13
13
  *
14
- * Storage layer:
15
- * - React Native -> AsyncStorage via `createAsyncStoragePersister`.
16
- * - Web -> localStorage via `createSyncStoragePersister` (wrapped in the
17
- * async persister API for a single call site).
18
- * - Both adapters are content-shape compatible with our `StorageInterface`.
14
+ * Storage layer: the platform `StorageInterface` (AsyncStorage on native,
15
+ * localStorage on web).
19
16
  *
20
17
  * Whitelist policy:
21
18
  * - Persist every account/user/session/privacy query and every queued mutation.
22
19
  * - DO NOT persist large list queries (e.g. activity feeds) — they go stale
23
20
  * fast and would balloon storage. Add new keys to `PERSISTED_QUERY_PREFIXES`
24
- * when introducing reads that should survive restart.
21
+ * (`persistedQueryCache.ts`) when introducing reads that should survive restart.
25
22
  */
26
- import { QueryClient, type QueryKey } from '@tanstack/react-query';
27
- import { type PersistedClient } from '@tanstack/react-query-persist-client';
23
+ import { QueryClient } from '@tanstack/react-query';
24
+ import type { PersistedClient } from '@tanstack/react-query-persist-client';
28
25
  import type { StorageInterface } from '../utils/storageHelpers.js';
26
+ import { type AccountQueriesConfig } from './accountQueryPersistence.js';
29
27
  /** Retry only failures that another attempt can plausibly change. */
30
28
  export declare function shouldRetryQuery(failureCount: number, error: unknown): boolean;
31
29
  /**
@@ -47,21 +45,18 @@ export interface AttachPersistenceResult {
47
45
  unsubscribe: () => void;
48
46
  }
49
47
  /**
50
- * Wire `persistQueryClient` to the supplied storage adapter.
48
+ * Restore, then keep writing, the account-agnostic cache.
51
49
  *
52
- * Returns once the in-flight restore promise is available so callers can
53
- * `await result.restored` before rendering UI that depends on cached data.
50
+ * Queries written by another build are dropped; queued mutations are kept
51
+ * (`persistedQueryCache.ts`). Whatever the app declared account-scoped
52
+ * (`accountQueries`) is left to `createAccountQueryPersistence`, even under
53
+ * one of the SDK's prefixes, so private rows never land in the shared blob and
54
+ * a paused mutation cannot replay twice or under another account.
54
55
  *
55
56
  * Safe to no-op if `storage` is null/undefined (e.g. server-side render
56
57
  * with no host storage).
57
58
  */
58
- export declare const attachQueryPersistence: (queryClient: QueryClient, storage: StorageInterface | null | undefined,
59
- /**
60
- * Mutation keys the app declared account-scoped (`accountQueries`). Those are
61
- * persisted per account by `createAccountQueryPersistence`, never here, so a
62
- * paused mutation cannot replay twice or under another account.
63
- */
64
- accountMutationKeys?: readonly QueryKey[]) => AttachPersistenceResult;
59
+ export declare const attachQueryPersistence: (queryClient: QueryClient, storage: StorageInterface | null | undefined, accountQueries?: AccountQueriesConfig) => AttachPersistenceResult;
65
60
  /**
66
61
  * Remove the persisted query+mutation cache (used on full sign-out / data reset).
67
62
  * Safe to call even if persistence was never attached.
@@ -1 +1 @@
1
- {"version":3,"file":"queryClient.d.ts","sourceRoot":"","sources":["../../../../../src/ui/hooks/queryClient.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,WAAW,EAAqD,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtH,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,sCAAsC,CAAC;AAG9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAAyB,CAAC;AAehE,qEAAqE;AACrE,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAK9E;AAwDD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,QAAO,WA6CpC,CAAC;AAEF,MAAM,WAAW,uBAAuB;IACtC,wEAAwE;IACxE,QAAQ,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,8DAA8D;IAC9D,WAAW,EAAE,MAAM,IAAI,CAAC;CACzB;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,GACjC,aAAa,WAAW,EACxB,SAAS,gBAAgB,GAAG,IAAI,GAAG,SAAS;AAC5C;;;;GAIG;AACH,sBAAqB,SAAS,QAAQ,EAAO,KAC5C,uBAwCF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,eAAe,GAAU,SAAS,gBAAgB,KAAG,OAAO,CAAC,IAAI,CAQ7E,CAAC;AAEF;;GAEG;AACH,YAAY,EAAE,eAAe,EAAE,CAAC"}
1
+ {"version":3,"file":"queryClient.d.ts","sourceRoot":"","sources":["../../../../../src/ui/hooks/queryClient.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,WAAW,EAA4C,MAAM,uBAAuB,CAAC;AAC9F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AAE5E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAAyB,CAAC;AAChE,OAAO,EAAyB,KAAK,oBAAoB,EAAE,MAAM,8BAA2B,CAAC;AAe7F,qEAAqE;AACrE,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAK9E;AAeD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,QAAO,WA6CpC,CAAC;AAEF,MAAM,WAAW,uBAAuB;IACtC,wEAAwE;IACxE,QAAQ,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,8DAA8D;IAC9D,WAAW,EAAE,MAAM,IAAI,CAAC;CACzB;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,sBAAsB,GACjC,aAAa,WAAW,EACxB,SAAS,gBAAgB,GAAG,IAAI,GAAG,SAAS,EAC5C,iBAAiB,oBAAoB,KACpC,uBA8CF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,eAAe,GAAU,SAAS,gBAAgB,KAAG,OAAO,CAAC,IAAI,CAQ7E,CAAC;AAEF;;GAEG;AACH,YAAY,EAAE,eAAe,EAAE,CAAC"}
@@ -1,4 +1,4 @@
1
1
  /** Glyphs are decorative: hidden from assistive technology (see `decorativeIconSet`). */
2
- declare const MaterialCommunityIcons: import("@expo/vector-icons/build/createIconSet").Icon<"function" | "sort" | "at" | "label" | "link" | "ellipse" | "image" | "switch" | "text" | "alert" | "alert-circle" | "alert-circle-outline" | "apps" | "archive" | "arrow-down" | "arrow-up" | "briefcase-outline" | "bug" | "calendar" | "calendar-outline" | "camera" | "camera-outline" | "card" | "card-outline" | "cart-outline" | "chevron-down" | "chevron-up" | "close" | "close-circle" | "cloud-outline" | "cloud-upload" | "cloud-upload-outline" | "cog" | "cog-outline" | "cube-outline" | "download" | "download-outline" | "earth" | "ellipse-outline" | "eye" | "eye-off-outline" | "filter-outline" | "flash" | "folder" | "folder-open-outline" | "folder-outline" | "gift" | "gift-outline" | "grid" | "heart" | "heart-outline" | "help-circle" | "help-circle-outline" | "image-outline" | "information" | "information-outline" | "key" | "leaf" | "library" | "pencil-outline" | "play" | "pulse" | "radio" | "receipt-outline" | "resize" | "rocket" | "scale" | "school-outline" | "server" | "share-outline" | "shield" | "star" | "sync" | "trophy" | "trophy-outline" | "volume-off" | "wallet" | "wallet-outline" | "watch" | "wifi" | "solid" | "opacity" | "wrap" | "account" | "account-circle" | "account-cog-outline" | "account-eye-outline" | "account-group" | "account-multiple" | "account-multiple-outline" | "account-outline" | "account-plus" | "account-search" | "account-switch" | "android" | "archive-arrow-down" | "arrow-left" | "bell" | "bell-outline" | "bio" | "bullhorn" | "cake-variant" | "cake-variant-outline" | "cancel" | "cellphone" | "cellphone-basic" | "cellphone-message" | "cellphone-screenshot" | "chart-line" | "check-all" | "check-circle" | "chip" | "circle-outline" | "clock" | "clock-outline" | "code-tags" | "contain" | "content-copy" | "controller" | "cookie" | "credit-card" | "database-outline" | "delete" | "delete-outline" | "devices" | "dots-horizontal" | "email" | "email-outline" | "file" | "file-document" | "file-document-edit-outline" | "file-document-outline" | "file-excel-outline" | "file-outline" | "file-pdf-box" | "folder-image" | "folder-zip-outline" | "ghost" | "history" | "image-multiple-outline" | "link-variant" | "loading" | "lock-outline" | "logout" | "magnify" | "map-marker" | "map-marker-multiple" | "map-marker-outline" | "message" | "message-outline" | "minus-circle" | "motion" | "music-note-outline" | "network" | "null" | "numeric" | "open-in-app" | "openid" | "palette" | "palette-outline" | "pan" | "phone" | "phone-outline" | "plus" | "plus-circle" | "post" | "presentation" | "react" | "read" | "security" | "shield-account-outline" | "shield-check" | "shield-search" | "signature" | "sort-alphabetical-variant" | "sort-numeric-variant" | "store" | "tag" | "tag-outline" | "text-box-outline" | "theme-light-dark" | "transfer" | "translate" | "trash-can-outline" | "tray-arrow-down" | "update" | "video-outline" | "web" | "wifi-off" | "wrench" | "xml", "OxyServicesMaterialCommunityIcons">;
2
+ declare const MaterialCommunityIcons: import("@expo/vector-icons/build/createIconSet").Icon<"function" | "devices" | "sort" | "at" | "label" | "link" | "ellipse" | "image" | "switch" | "text" | "alert" | "alert-circle" | "alert-circle-outline" | "apps" | "archive" | "arrow-down" | "arrow-up" | "briefcase-outline" | "bug" | "calendar" | "calendar-outline" | "camera" | "camera-outline" | "card" | "card-outline" | "cart-outline" | "chevron-down" | "chevron-up" | "close" | "close-circle" | "cloud-outline" | "cloud-upload" | "cloud-upload-outline" | "cog" | "cog-outline" | "cube-outline" | "download" | "download-outline" | "earth" | "ellipse-outline" | "eye" | "eye-off-outline" | "filter-outline" | "flash" | "folder" | "folder-open-outline" | "folder-outline" | "gift" | "gift-outline" | "grid" | "heart" | "heart-outline" | "help-circle" | "help-circle-outline" | "image-outline" | "information" | "information-outline" | "key" | "leaf" | "library" | "pencil-outline" | "play" | "pulse" | "radio" | "receipt-outline" | "resize" | "rocket" | "scale" | "school-outline" | "server" | "share-outline" | "shield" | "star" | "sync" | "trophy" | "trophy-outline" | "volume-off" | "wallet" | "wallet-outline" | "watch" | "wifi" | "solid" | "opacity" | "wrap" | "account" | "account-circle" | "account-cog-outline" | "account-eye-outline" | "account-group" | "account-multiple" | "account-multiple-outline" | "account-outline" | "account-plus" | "account-search" | "account-switch" | "android" | "archive-arrow-down" | "arrow-left" | "bell" | "bell-outline" | "bio" | "bullhorn" | "cake-variant" | "cake-variant-outline" | "cancel" | "cellphone" | "cellphone-basic" | "cellphone-message" | "cellphone-screenshot" | "chart-line" | "check-all" | "check-circle" | "chip" | "circle-outline" | "clock" | "clock-outline" | "code-tags" | "contain" | "content-copy" | "controller" | "cookie" | "credit-card" | "database-outline" | "delete" | "delete-outline" | "dots-horizontal" | "email" | "email-outline" | "file" | "file-document" | "file-document-edit-outline" | "file-document-outline" | "file-excel-outline" | "file-outline" | "file-pdf-box" | "folder-image" | "folder-zip-outline" | "ghost" | "history" | "image-multiple-outline" | "link-variant" | "loading" | "lock-outline" | "logout" | "magnify" | "map-marker" | "map-marker-multiple" | "map-marker-outline" | "message" | "message-outline" | "minus-circle" | "motion" | "music-note-outline" | "network" | "null" | "numeric" | "open-in-app" | "openid" | "palette" | "palette-outline" | "pan" | "phone" | "phone-outline" | "plus" | "plus-circle" | "post" | "presentation" | "react" | "read" | "security" | "shield-account-outline" | "shield-check" | "shield-search" | "signature" | "sort-alphabetical-variant" | "sort-numeric-variant" | "store" | "tag" | "tag-outline" | "text-box-outline" | "theme-light-dark" | "transfer" | "translate" | "trash-can-outline" | "tray-arrow-down" | "update" | "video-outline" | "web" | "wifi-off" | "wrench" | "xml", "OxyServicesMaterialCommunityIcons">;
3
3
  export default MaterialCommunityIcons;
4
4
  //# sourceMappingURL=MaterialCommunityIcons.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxy.so/services",
3
- "version": "9.0.0",
3
+ "version": "9.1.0",
4
4
  "description": "OxyHQ Expo/React Native SDK — UI components, screens, and native features",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "module": "lib/module/index.js",
@@ -105,7 +105,7 @@ const OxyProvider: FC<OxyProviderProps> = ({
105
105
  const persistenceUnsubRef = useRef<(() => void) | null>(null);
106
106
  const ownsQueryClientRef = useRef(providedQueryClient === undefined);
107
107
  // `accountQueries` is read once, at mount, like the client itself.
108
- const accountMutationKeysRef = useRef(accountQueries?.mutationKeys);
108
+ const accountQueriesRef = useRef(accountQueries);
109
109
  const [platformStorage, setPlatformStorage] = useState<StorageInterface | null>(null);
110
110
 
111
111
  // If the consumer supplied their own QueryClient we use it as-is and skip
@@ -139,7 +139,7 @@ const OxyProvider: FC<OxyProviderProps> = ({
139
139
  setPlatformStorage(storage);
140
140
  const client = queryClientRef.current;
141
141
  if (!client || !ownsQueryClientRef.current) return;
142
- const persistence = attachQueryPersistence(client, storage, accountMutationKeysRef.current);
142
+ const persistence = attachQueryPersistence(client, storage, accountQueriesRef.current);
143
143
  persistenceUnsubRef.current = persistence.unsubscribe;
144
144
  await persistence.restored;
145
145
  };
@@ -300,6 +300,10 @@ export const OxyRuntimeProvider: React.FC<OxyRuntimeProviderProps> = ({
300
300
  : null;
301
301
  }
302
302
  const accountQueryPersistence = accountQueryPersistenceRef.current;
303
+ useEffect(() => {
304
+ accountQueryPersistence?.attach();
305
+ return () => accountQueryPersistence?.detach();
306
+ }, [accountQueryPersistence]);
303
307
  const isAccountCacheReady = useSyncExternalStore(
304
308
  accountQueryPersistence ? accountQueryPersistence.subscribe : subscribeNever,
305
309
  () => accountQueryPersistence?.isReady() ?? true,