@interop/was-react 0.20.0 → 0.21.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 (104) hide show
  1. package/README.md +8 -2
  2. package/dist/dev/provisionDevGrants.js +1 -1
  3. package/dist/dev/provisionDevGrants.js.map +1 -1
  4. package/dist/index.d.ts +2 -1
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +9 -2
  7. package/dist/index.js.map +1 -1
  8. package/dist/session/authStore.d.ts.map +1 -1
  9. package/dist/session/authStore.js +48 -11
  10. package/dist/session/authStore.js.map +1 -1
  11. package/dist/storage/adopt.d.ts.map +1 -1
  12. package/dist/storage/adopt.js +2 -1
  13. package/dist/storage/adopt.js.map +1 -1
  14. package/dist/storage/descriptorManager.d.ts +1 -0
  15. package/dist/storage/descriptorManager.d.ts.map +1 -1
  16. package/dist/storage/descriptorManager.js +39 -21
  17. package/dist/storage/descriptorManager.js.map +1 -1
  18. package/dist/{sync → storage}/docCipher.d.ts +3 -29
  19. package/dist/storage/docCipher.d.ts.map +1 -0
  20. package/dist/{sync → storage}/docCipher.js +0 -20
  21. package/dist/storage/docCipher.js.map +1 -0
  22. package/dist/storage/entityStore.d.ts.map +1 -1
  23. package/dist/storage/entityStore.js +2 -1
  24. package/dist/storage/entityStore.js.map +1 -1
  25. package/dist/storage/localStore.d.ts +1 -1
  26. package/dist/storage/localStore.d.ts.map +1 -1
  27. package/dist/storage/localStore.js +14 -2
  28. package/dist/storage/localStore.js.map +1 -1
  29. package/dist/storage/sharedCollectionReader.d.ts +1 -1
  30. package/dist/storage/sharedCollectionReader.d.ts.map +1 -1
  31. package/dist/storage/sharedCollectionReader.js +1 -1
  32. package/dist/storage/sharedCollectionReader.js.map +1 -1
  33. package/dist/storage/storageContext.d.ts +7 -5
  34. package/dist/storage/storageContext.d.ts.map +1 -1
  35. package/dist/storage/storageContext.js +8 -4
  36. package/dist/storage/storageContext.js.map +1 -1
  37. package/dist/storage/storageManager.d.ts +7 -3
  38. package/dist/storage/storageManager.d.ts.map +1 -1
  39. package/dist/storage/storageManager.js +6 -2
  40. package/dist/storage/storageManager.js.map +1 -1
  41. package/dist/storage/syncController.d.ts +30 -19
  42. package/dist/storage/syncController.d.ts.map +1 -1
  43. package/dist/storage/syncController.js +84 -157
  44. package/dist/storage/syncController.js.map +1 -1
  45. package/dist/storage/syncStatusStore.d.ts +8 -7
  46. package/dist/storage/syncStatusStore.d.ts.map +1 -1
  47. package/dist/storage/syncStatusStore.js.map +1 -1
  48. package/dist/storage/wasRemoteStore.d.ts +24 -10
  49. package/dist/storage/wasRemoteStore.d.ts.map +1 -1
  50. package/dist/storage/wasRemoteStore.js +92 -37
  51. package/dist/storage/wasRemoteStore.js.map +1 -1
  52. package/dist/storage/wasSync.d.ts +25 -10
  53. package/dist/storage/wasSync.d.ts.map +1 -1
  54. package/dist/storage/wasSync.js +53 -22
  55. package/dist/storage/wasSync.js.map +1 -1
  56. package/dist/{sync → storage}/wasSyncPort.d.ts +14 -8
  57. package/dist/storage/wasSyncPort.d.ts.map +1 -0
  58. package/dist/storage/wasSyncPort.js.map +1 -0
  59. package/dist/storage/writerId.d.ts +7 -13
  60. package/dist/storage/writerId.d.ts.map +1 -1
  61. package/dist/storage/writerId.js +30 -46
  62. package/dist/storage/writerId.js.map +1 -1
  63. package/package.json +13 -10
  64. package/dist/sync/changesQuery.d.ts +0 -44
  65. package/dist/sync/changesQuery.d.ts.map +0 -1
  66. package/dist/sync/changesQuery.js +0 -54
  67. package/dist/sync/changesQuery.js.map +0 -1
  68. package/dist/sync/docCipher.d.ts.map +0 -1
  69. package/dist/sync/docCipher.js.map +0 -1
  70. package/dist/sync/feedMasterPort.d.ts +0 -38
  71. package/dist/sync/feedMasterPort.d.ts.map +0 -1
  72. package/dist/sync/feedMasterPort.js +0 -116
  73. package/dist/sync/feedMasterPort.js.map +0 -1
  74. package/dist/sync/index.d.ts +0 -23
  75. package/dist/sync/index.d.ts.map +0 -1
  76. package/dist/sync/index.js +0 -27
  77. package/dist/sync/index.js.map +0 -1
  78. package/dist/sync/lww.d.ts +0 -50
  79. package/dist/sync/lww.d.ts.map +0 -1
  80. package/dist/sync/lww.js +0 -69
  81. package/dist/sync/lww.js.map +0 -1
  82. package/dist/sync/lwwConflictHandler.d.ts +0 -83
  83. package/dist/sync/lwwConflictHandler.d.ts.map +0 -1
  84. package/dist/sync/lwwConflictHandler.js +0 -114
  85. package/dist/sync/lwwConflictHandler.js.map +0 -1
  86. package/dist/sync/pushWrites.d.ts +0 -76
  87. package/dist/sync/pushWrites.d.ts.map +0 -1
  88. package/dist/sync/pushWrites.js +0 -221
  89. package/dist/sync/pushWrites.js.map +0 -1
  90. package/dist/sync/syncedDocSchema.d.ts +0 -22
  91. package/dist/sync/syncedDocSchema.d.ts.map +0 -1
  92. package/dist/sync/syncedDocSchema.js +0 -29
  93. package/dist/sync/syncedDocSchema.js.map +0 -1
  94. package/dist/sync/types.d.ts +0 -319
  95. package/dist/sync/types.d.ts.map +0 -1
  96. package/dist/sync/types.js +0 -58
  97. package/dist/sync/types.js.map +0 -1
  98. package/dist/sync/wasReplication.d.ts +0 -47
  99. package/dist/sync/wasReplication.d.ts.map +0 -1
  100. package/dist/sync/wasReplication.js +0 -92
  101. package/dist/sync/wasReplication.js.map +0 -1
  102. package/dist/sync/wasSyncPort.d.ts.map +0 -1
  103. package/dist/sync/wasSyncPort.js.map +0 -1
  104. /package/dist/{sync → storage}/wasSyncPort.js +0 -0
@@ -1,45 +1,24 @@
1
- import { createWasReplication, createWasSyncPort, withFeedMasterRead, WasSyncAuthError } from '../sync/index.js';
1
+ import { createSyncController } from '@interop/was-sync/rxdb';
2
2
  import { DEFAULT_SYNC_POLL_MS } from '../config.js';
3
+ export { isAuthError } from '@interop/was-sync/rxdb';
3
4
  /**
4
- * Whether a replication error signals expired/revoked storage access. Every WAS
5
- * request the replication makes funnels through the sync port, which maps a
6
- * `401` / `403` to a typed {@link WasSyncAuthError} at the boundary. RxDB then
7
- * wraps that thrown error inside an RxError (nested under `cause` / `errors` /
8
- * `parameters.errors`), so this walks the error graph looking for a
9
- * `WasSyncAuthError` rather than re-extracting raw status codes. RxDB's
10
- * wrapping serializes the handler's thrown error to plain JSON (name, message,
11
- * stack -- `errorToPlainJson`), so the walk matches the serialized `name`
12
- * alongside a live instance; an instance only ever appears when a caller hands
13
- * the port error over directly.
5
+ * The browser reachability source the core polls and subscribes through. An
6
+ * `isOnline` that cannot answer reports `true`, since a platform with no
7
+ * reachability signal must still poll.
14
8
  *
15
- * @param err {unknown}
16
- * @returns {boolean}
9
+ * @returns {object} the core's `onlineSource` port
17
10
  */
18
- export function isAuthError(err) {
19
- const seen = new Set();
20
- const queue = [err];
21
- while (queue.length > 0) {
22
- const current = queue.shift();
23
- if (current === null || typeof current !== 'object' || seen.has(current)) {
24
- continue;
25
- }
26
- seen.add(current);
27
- if (current instanceof WasSyncAuthError ||
28
- current.name === 'WasSyncAuthError') {
29
- return true;
30
- }
31
- const candidate = current;
32
- if (candidate.cause) {
33
- queue.push(candidate.cause);
34
- }
35
- if (Array.isArray(candidate.errors)) {
36
- queue.push(...candidate.errors);
37
- }
38
- if (Array.isArray(candidate.parameters?.errors)) {
39
- queue.push(...candidate.parameters.errors);
11
+ function browserOnlineSource() {
12
+ return {
13
+ isOnline: () => typeof navigator === 'undefined' || navigator.onLine !== false,
14
+ subscribe: onOnline => {
15
+ if (typeof window === 'undefined') {
16
+ return () => { };
17
+ }
18
+ window.addEventListener('online', onOnline);
19
+ return () => window.removeEventListener('online', onOnline);
40
20
  }
41
- }
42
- return false;
21
+ };
43
22
  }
44
23
  /**
45
24
  * A per-session controller around background replication.
@@ -48,10 +27,7 @@ export class SyncController {
48
27
  #collections;
49
28
  #sync;
50
29
  #syncStatus;
51
- #replications = [];
52
- #onlineHandler;
53
- #pollTimer;
54
- #started = false;
30
+ #core;
55
31
  #stopped = false;
56
32
  /**
57
33
  * @param options {object}
@@ -67,7 +43,12 @@ export class SyncController {
67
43
  }
68
44
  /**
69
45
  * Starts background replication for every entity collection covered by the
70
- * grant set. Idempotent (a no-op if already running).
46
+ * grant set. Idempotent (a no-op if already running, or if `stop()` has
47
+ * already run: `stop()` is terminal, so a logout that raced an in-flight
48
+ * session bootstrap never spins up replications against a closed database).
49
+ *
50
+ * A failed bring-up rethrows, with every collection's status left at `error`,
51
+ * so the caller's bootstrap can surface it.
71
52
  *
72
53
  * @param options {object}
73
54
  * @param options.remoteStore {WasRemoteStore}
@@ -80,123 +61,72 @@ export class SyncController {
80
61
  * @returns {Promise<void>}
81
62
  */
82
63
  async start({ remoteStore, localStore, onRemoteChange, onAuthError }) {
83
- // `stop()` is terminal: a controller stopped before `start()` ran (a logout
84
- // that raced an in-flight session bootstrap) must never spin up
85
- // replications against the now-closed database.
86
- if (this.#started || this.#stopped) {
64
+ if (this.#core || this.#stopped) {
87
65
  return;
88
66
  }
89
- this.#started = true;
90
67
  const setStatus = this.#syncStatus.getState().setStatus;
91
- const batchSize = this.#sync.batchSize;
92
- const retryMs = this.#sync.retryMs;
93
- const pollMs = this.#sync.pollMs ?? DEFAULT_SYNC_POLL_MS;
94
- try {
95
- for (const { key, id } of this.#collections) {
96
- const capability = remoteStore.collectionCapability(id);
97
- // A collection the grant set does not cover would otherwise sync with no
98
- // capability and draw a fail-closed 403, tripping the session-wide
99
- // "storage access expired" banner. Skip it (and flag it) instead so an
100
- // uncovered collection never masquerades as expired access.
101
- if (!capability) {
102
- console.warn(`Skipping sync for "${id}": no delegated capability covers it.`);
103
- setStatus(key, 'error');
104
- continue;
105
- }
106
- setStatus(key, 'idle');
107
- // Wrap the verbatim port so the 412 conflict re-read resolves `version`
108
- // from the changes-feed body (CORS hides the `GET` ETag cross-origin).
109
- const wasPort = withFeedMasterRead(createWasSyncPort({
110
- was: remoteStore.was,
111
- spaceId: remoteStore.spaceId,
112
- collectionId: id,
113
- capability
114
- }));
115
- const state = createWasReplication({
116
- rxCollection: localStore.rxCollection(key),
117
- wasPort,
118
- replicationIdentifier: `was-sync:${remoteStore.serverUrl}:${remoteStore.spaceId}:${id}`,
119
- ...(batchSize !== undefined && { batchSize }),
120
- ...(retryMs !== undefined && { retryTime: retryMs })
121
- });
122
- const subscriptions = [
123
- state.active$.subscribe(active => {
124
- setStatus(key, active ? 'syncing' : 'synced');
125
- }),
126
- state.error$.subscribe(err => {
127
- console.error(`Sync error for "${id}":`, err);
128
- setStatus(key, 'error');
129
- if (onAuthError && isAuthError(err)) {
130
- onAuthError();
131
- }
132
- })
133
- ];
134
- if (onRemoteChange) {
135
- subscriptions.push(localStore
136
- .rxCollection(key)
137
- .$.subscribe(event => onRemoteChange(key, event)));
138
- }
139
- this.#replications.push({ state, subscriptions });
68
+ // A collection the grant set does not cover would otherwise sync with no
69
+ // capability and draw a fail-closed 403, tripping the session-wide
70
+ // "storage access expired" banner. Flag it and keep it out of the port
71
+ // instead, so an uncovered collection never masquerades as expired access.
72
+ // The core makes the same call, but only for a port whose OTHER entries
73
+ // carry a capability: it cannot tell an all-uncovered delegated port from
74
+ // a wallet's root-capability port, and every session here is delegated.
75
+ const covered = [];
76
+ for (const { key, id } of this.#collections) {
77
+ const capability = remoteStore.collectionCapability(id);
78
+ if (!capability) {
79
+ console.warn(`Skipping sync for "${id}": no delegated capability covers it.`);
80
+ setStatus(key, 'error');
81
+ continue;
140
82
  }
141
- this.#onlineHandler = () => this.reSync();
142
- window.addEventListener('online', this.#onlineHandler);
143
- // The pull side has no server-side live stream, so an already-open session
144
- // would otherwise never see another device's edits. A low-frequency
145
- // periodic reSync polls the changes feed so multi-device edits converge
146
- // live (the change subscriptions above then patch the stores).
147
- if (pollMs > 0) {
148
- this.#pollTimer = setInterval(() => this.reSync(), pollMs);
83
+ covered.push({ key, id, capability });
84
+ }
85
+ this.#core = createSyncController({
86
+ port: {
87
+ wasClient: remoteStore.was,
88
+ spaceId: remoteStore.spaceId,
89
+ serverUrl: remoteStore.serverUrl,
90
+ collections: covered,
91
+ rxCollection: key => localStore.rxCollection(key),
92
+ // The 412 conflict re-read resolves `version` from the changes-feed
93
+ // body: CORS hides the `GET` ETag cross-origin, which is this
94
+ // library's deployment shape.
95
+ feedPrimaryRead: true,
96
+ // A `401` / `403` / the server's masked `404` must arrive typed, or
97
+ // `onAuthError` could never fire.
98
+ mapAuthErrors: true,
99
+ ...(this.#sync.batchSize !== undefined && {
100
+ batchSize: this.#sync.batchSize
101
+ }),
102
+ ...(this.#sync.retryMs !== undefined && {
103
+ retryTime: this.#sync.retryMs
104
+ })
105
+ },
106
+ // The core delivers the logical key first and the WAS id second; the
107
+ // status store is keyed on the logical key, since two registry entries
108
+ // may share one WAS id.
109
+ onStatus: (key, _collectionId, status) => setStatus(key, status),
110
+ ...(onAuthError && { onAuthError }),
111
+ ...(onRemoteChange && { onRemoteChange }),
112
+ onlineSource: browserOnlineSource(),
113
+ pollMs: this.#sync.pollMs ?? DEFAULT_SYNC_POLL_MS,
114
+ log: {
115
+ warn: (message, meta) => console.warn(message, meta),
116
+ error: (message, meta) => console.error(message, meta)
149
117
  }
118
+ });
119
+ try {
120
+ await this.#core.start();
150
121
  }
151
122
  catch (err) {
152
- console.error('Failed to start sync controller:', err);
153
- // Unwind the partial bring-up WITHOUT `stop()`: its terminal `#stopped`
154
- // latch would permanently prevent a later `start()` on this session, and
155
- // its status reset would leave the rollup reporting "Local only" over a
156
- // wallet-connected session that failed to start replicating. Instead the
157
- // per-collection statuses are left at `error` (visible in the rollup)
158
- // and the error RETHROWS so the caller's bootstrap promise rejects and
159
- // the session can surface it.
160
- await this.#teardownReplications();
161
- this.#started = false;
162
- for (const { key } of this.#collections) {
163
- setStatus(key, 'error');
164
- }
123
+ // The core already unwound and flagged every collection; drop the
124
+ // instance so this session's controller is not left holding a core it
125
+ // can neither start nor usefully stop, and rethrow.
126
+ this.#core = undefined;
165
127
  throw err;
166
128
  }
167
129
  }
168
- /**
169
- * The shared release path behind the failed-bring-up unwind and {@link stop}:
170
- * drops the `online` listener, clears the poll timer, unsubscribes every
171
- * per-collection subscription, and cancels every replication. What each caller
172
- * does AFTER this differs (the unwind leaves the per-collection statuses at
173
- * `error` and rethrows; `stop()` resets the status store behind its terminal
174
- * latch), so the divergence stays at the call sites.
175
- *
176
- * @returns {Promise<void>}
177
- */
178
- async #teardownReplications() {
179
- if (this.#onlineHandler) {
180
- window.removeEventListener('online', this.#onlineHandler);
181
- this.#onlineHandler = undefined;
182
- }
183
- if (this.#pollTimer) {
184
- clearInterval(this.#pollTimer);
185
- this.#pollTimer = undefined;
186
- }
187
- for (const { state, subscriptions } of this.#replications) {
188
- for (const subscription of subscriptions) {
189
- subscription.unsubscribe();
190
- }
191
- try {
192
- await state.cancel();
193
- }
194
- catch (err) {
195
- console.error('Error cancelling replication:', err);
196
- }
197
- }
198
- this.#replications = [];
199
- }
200
130
  /**
201
131
  * Triggers an immediate replication cycle on every running collection, rather
202
132
  * than waiting for RxDB's next scheduled tick. Fire-and-forget.
@@ -204,23 +134,20 @@ export class SyncController {
204
134
  * @returns {void}
205
135
  */
206
136
  reSync() {
207
- for (const { state } of this.#replications) {
208
- state.reSync();
209
- }
137
+ this.#core?.reSync();
210
138
  }
211
139
  /**
212
140
  * Stops replication and releases resources (the database is owned by the
213
- * caller). Idempotent.
141
+ * caller). Idempotent, and terminal: a later `start()` is refused.
214
142
  *
215
143
  * @returns {Promise<void>}
216
144
  */
217
145
  async stop() {
218
- // Latch first so a concurrent `start()` (a logout racing the session
219
- // bootstrap) sees the controller as terminally stopped and bails.
220
146
  this.#stopped = true;
221
- await this.#teardownReplications();
147
+ const core = this.#core;
148
+ this.#core = undefined;
149
+ await core?.stop();
222
150
  this.#syncStatus.getState().reset();
223
- this.#started = false;
224
151
  }
225
152
  }
226
153
  //# sourceMappingURL=syncController.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"syncController.js","sourceRoot":"","sources":["../../src/storage/syncController.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"syncController.js","sourceRoot":"","sources":["../../src/storage/syncController.ts"],"names":[],"mappings":"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"}
@@ -10,14 +10,16 @@
10
10
  * provider. In-memory only, like the session -- reset on logout.
11
11
  */
12
12
  import { type StoreApi } from 'zustand/vanilla';
13
+ import type { SyncStatus } from '@interop/was-client/sync';
13
14
  /**
14
- * A single collection's replication status:
15
- * - `idle` -- configured but no cycle has run yet
16
- * - `syncing` -- a pull/push cycle is in flight
17
- * - `synced` -- last cycle completed without error
18
- * - `error` -- last cycle failed (RxDB is backing off / will retry)
15
+ * A single collection's replication status, re-exported from the client's
16
+ * closed vocabulary (`idle` -- configured but no cycle has run yet; `syncing`
17
+ * -- a pull/push cycle is in flight; `synced` -- last cycle completed without
18
+ * error; `error` -- last cycle failed and RxDB is backing off). The client owns
19
+ * the type so the replication driver and the wallets' own sync engine report
20
+ * the same four strings.
19
21
  */
20
- export type SyncStatus = 'idle' | 'syncing' | 'synced' | 'error';
22
+ export type { SyncStatus };
21
23
  interface SyncStatusState {
22
24
  /**
23
25
  * Keyed by the registry's LOGICAL collection key (`WasCollectionConfig.key`,
@@ -61,5 +63,4 @@ export declare function deriveSyncRollup(statuses: SyncStatus[]): {
61
63
  label: string;
62
64
  title: string;
63
65
  };
64
- export {};
65
66
  //# sourceMappingURL=syncStatusStore.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"syncStatusStore.d.ts","sourceRoot":"","sources":["../../src/storage/syncStatusStore.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;GAOG;AACH,OAAO,EAAe,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAE5D;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAA;AAEhE,UAAU,eAAe;IACvB;;;;;;OAMG;IACH,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;IACpC,SAAS,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,KAAK,IAAI,CAAA;IAC9D,KAAK,EAAE,MAAM,IAAI,CAAA;CAClB;AAED,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC,eAAe,CAAC,CAAA;AAEvD;;;;;GAKG;AACH,wBAAgB,qBAAqB,IAAI,eAAe,CASvD;AAED;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,QAAQ,CAAA;AAEnE;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,UAAU,EAAE,GAAG;IACxD,KAAK,EAAE,UAAU,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CACd,CA2BA"}
1
+ {"version":3,"file":"syncStatusStore.d.ts","sourceRoot":"","sources":["../../src/storage/syncStatusStore.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;GAOG;AACH,OAAO,EAAe,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AAE1D;;;;;;;GAOG;AACH,YAAY,EAAE,UAAU,EAAE,CAAA;AAE1B,UAAU,eAAe;IACvB;;;;;;OAMG;IACH,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;IACpC,SAAS,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,KAAK,IAAI,CAAA;IAC9D,KAAK,EAAE,MAAM,IAAI,CAAA;CAClB;AAED,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC,eAAe,CAAC,CAAA;AAEvD;;;;;GAKG;AACH,wBAAgB,qBAAqB,IAAI,eAAe,CASvD;AAED;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,QAAQ,CAAA;AAEnE;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,UAAU,EAAE,GAAG;IACxD,KAAK,EAAE,UAAU,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CACd,CA2BA"}
@@ -1 +1 @@
1
- {"version":3,"file":"syncStatusStore.js","sourceRoot":"","sources":["../../src/storage/syncStatusStore.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;GAOG;AACH,OAAO,EAAE,WAAW,EAAiB,MAAM,iBAAiB,CAAA;AA0B5D;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB;IACnC,OAAO,WAAW,EAAmB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC5C,QAAQ,EAAE,EAAE;QACZ,SAAS,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,EAAE,CACnC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACZ,QAAQ,EAAE,EAAE,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC,aAAa,CAAC,EAAE,MAAM,EAAE;SACzD,CAAC,CAAC;QACL,KAAK,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;KACnC,CAAC,CAAC,CAAA;AACL,CAAC;AASD;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAsB;IAKrD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO;YACL,KAAK,EAAE,SAAS;YAChB,KAAK,EAAE,YAAY;YACnB,KAAK,EAAE,mDAAmD;SAC3D,CAAA;IACH,CAAC;IACD,IAAI,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QAC/B,OAAO;YACL,KAAK,EAAE,OAAO;YACd,KAAK,EAAE,YAAY;YACnB,KAAK,EAAE,+DAA+D;SACvE,CAAA;IACH,CAAC;IACD,IAAI,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC9D,OAAO;YACL,KAAK,EAAE,SAAS;YAChB,KAAK,EAAE,SAAS;YAChB,KAAK,EAAE,sDAAsD;SAC9D,CAAA;IACH,CAAC;IACD,OAAO;QACL,KAAK,EAAE,QAAQ;QACf,KAAK,EAAE,QAAQ;QACf,KAAK,EAAE,oDAAoD;KAC5D,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"syncStatusStore.js","sourceRoot":"","sources":["../../src/storage/syncStatusStore.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;GAOG;AACH,OAAO,EAAE,WAAW,EAAiB,MAAM,iBAAiB,CAAA;AA4B5D;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB;IACnC,OAAO,WAAW,EAAmB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC5C,QAAQ,EAAE,EAAE;QACZ,SAAS,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,EAAE,CACnC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACZ,QAAQ,EAAE,EAAE,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC,aAAa,CAAC,EAAE,MAAM,EAAE;SACzD,CAAC,CAAC;QACL,KAAK,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;KACnC,CAAC,CAAC,CAAA;AACL,CAAC;AASD;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAsB;IAKrD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO;YACL,KAAK,EAAE,SAAS;YAChB,KAAK,EAAE,YAAY;YACnB,KAAK,EAAE,mDAAmD;SAC3D,CAAA;IACH,CAAC;IACD,IAAI,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QAC/B,OAAO;YACL,KAAK,EAAE,OAAO;YACd,KAAK,EAAE,YAAY;YACnB,KAAK,EAAE,+DAA+D;SACvE,CAAA;IACH,CAAC;IACD,IAAI,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC9D,OAAO;YACL,KAAK,EAAE,SAAS;YAChB,KAAK,EAAE,SAAS;YAChB,KAAK,EAAE,sDAAsD;SAC9D,CAAA;IACH,CAAC;IACD,OAAO;QACL,KAAK,EAAE,QAAQ;QACf,KAAK,EAAE,QAAQ;QACf,KAAK,EAAE,oDAAoD;KAC5D,CAAA;AACH,CAAC"}
@@ -120,10 +120,16 @@ export declare class WasRemoteStore {
120
120
  * Reads one collection's `encryption` descriptor from its Collection Description,
121
121
  * invoked with that collection's delegated zcap. Returns the
122
122
  * {@link CollectionEncryption} block (a multi-recipient descriptor carries key
123
- * epochs) or `undefined` when the collection carries no descriptor. Non-fatal like
124
- * the descriptor PUTs: returns `undefined` rather than throwing when no capability
125
- * covers the collection or the read fails (offline, unauthorized), so a caller
126
- * can fall back to its cached copy.
123
+ * epochs), or `undefined` only when the answer is genuinely "no descriptor":
124
+ * no capability covers the collection, the collection is not found (which is
125
+ * also how WAS answers an unauthorized read), or the description carries no
126
+ * `encryption` member.
127
+ *
128
+ * Any other failure (a dropped connection, a 5xx) is thrown, wrapped with
129
+ * the collection id. It must NOT read as "no descriptor": a caller that takes
130
+ * it for one opens the collection under the fail-closed placeholder cipher,
131
+ * and a correctly provisioned collection then fails its first write or
132
+ * decrypt for a network hiccup.
127
133
  *
128
134
  * @param collectionId {string} the WAS collection id
129
135
  * @returns {Promise<CollectionEncryption | undefined>}
@@ -138,9 +144,13 @@ export declare class WasRemoteStore {
138
144
  * NOT `Collection.meta()`: that one DECODES `custom` to plaintext, which
139
145
  * `applyMeta` cannot consume.
140
146
  *
141
- * Non-fatal like the descriptor read: answers `undefined` rather than
142
- * throwing when no capability covers the collection, the read is unauthorized,
143
- * the app is offline, or the backend has no metadata support.
147
+ * Answers `undefined` for exactly the outcomes that mean "no metadata": no
148
+ * capability covers the collection, the read answered not-found (which is
149
+ * also how WAS answers an unauthorized read), or the backend has no metadata
150
+ * support (`501`). Any other failure is thrown, wrapped with the collection
151
+ * id, like {@link readCollectionEncryption}: a dropped connection is an
152
+ * unknown answer, not an absent one, and a caller that took it for "no
153
+ * schema" would skip the blinded-index install over a guess.
144
154
  *
145
155
  * @param collectionId {string} the WAS collection id
146
156
  * @returns {Promise<{ custom?: unknown } | undefined>}
@@ -301,9 +311,13 @@ export declare class WasRemoteStore {
301
311
  * Deliberately NOT `wasDescriptorSource` from the same subpath: that one
302
312
  * describes through the client's root capability, which a relying-party app
303
313
  * never holds. Every read here must invoke the per-collection grant, which is
304
- * exactly what {@link WasRemoteStore.readCollectionEncryption} does (and, like
305
- * the rest of the descriptor plumbing, it answers `undefined` rather than
306
- * throwing when the read is unauthorized or the app is offline).
314
+ * exactly what {@link WasRemoteStore.readCollectionEncryption} does.
315
+ *
316
+ * A read that fails for a transient reason is warned about and answered as
317
+ * `undefined` here: the refresh guard is already spent by the time the source
318
+ * is consulted, so the decrypt retry fails the same way it would under no
319
+ * refresh at all, and the caller sees the envelope's own unknown-epoch error
320
+ * rather than a network one.
307
321
  *
308
322
  * @param options {object}
309
323
  * @param options.remoteStore {WasRemoteStore}
@@ -1 +1 @@
1
- {"version":3,"file":"wasRemoteStore.d.ts","sourceRoot":"","sources":["../../src/storage/wasRemoteStore.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"wasRemoteStore.d.ts","sourceRoot":"","sources":["../../src/storage/wasRemoteStore.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
- import { WasClient } from '@interop/was-client';
1
+ import { NotFoundError, NotImplementedError, WasClient, mapError } from '@interop/was-client';
2
2
  import { createEdvEncryption } from '@interop/was-client/edv';
3
3
  import { collectionItems, collectionMeta, collectionPath, resourcePath, toUrl } from '@interop/was-client/paths';
4
- import { errorStatus, errorMessage } from '../sync/index.js';
4
+ import { errorStatus, errorMessage } from '@interop/was-client/sync';
5
5
  /**
6
6
  * Maps one configured index attribute name to the blinded-index attribute path
7
7
  * the EDV codec addresses it by. The codec walks the path from the stored
@@ -14,6 +14,36 @@ import { errorStatus, errorMessage } from '../sync/index.js';
14
14
  function blindedAttribute(name) {
15
15
  return `content.${name}`;
16
16
  }
17
+ /**
18
+ * Runs one read whose failure may mean "absent" rather than "unknown". An
19
+ * error `isAbsent` accepts answers `undefined`; any other failure is rethrown,
20
+ * wrapped with `description`, so the caller cannot mistake an unknown answer
21
+ * (a dropped connection, a 5xx) for an absent one. Retrying is not this
22
+ * layer's job: the HTTP client underneath already retries the transient
23
+ * status codes and network errors on every GET.
24
+ *
25
+ * @param options {object}
26
+ * @param options.read {() => Promise<T>}
27
+ * @param options.isAbsent {(err: Error) => boolean} which mapped errors mean
28
+ * "absent"
29
+ * @param options.description {string} what was being read, for the wrap
30
+ * @returns {Promise<T | undefined>}
31
+ */
32
+ async function readOrAbsent({ read, isAbsent, description }) {
33
+ try {
34
+ return await read();
35
+ }
36
+ catch (err) {
37
+ const mapped = mapError(err);
38
+ if (isAbsent(mapped)) {
39
+ return undefined;
40
+ }
41
+ // The mapped (typed) error carries the caught one as its own `cause`, so
42
+ // the chain is intact; the typed one is what callers match on.
43
+ // eslint-disable-next-line preserve-caught-error
44
+ throw new Error(`Failed to read ${description}.`, { cause: mapped });
45
+ }
46
+ }
17
47
  export class WasRemoteStore {
18
48
  was;
19
49
  serverUrl;
@@ -95,10 +125,16 @@ export class WasRemoteStore {
95
125
  * Reads one collection's `encryption` descriptor from its Collection Description,
96
126
  * invoked with that collection's delegated zcap. Returns the
97
127
  * {@link CollectionEncryption} block (a multi-recipient descriptor carries key
98
- * epochs) or `undefined` when the collection carries no descriptor. Non-fatal like
99
- * the descriptor PUTs: returns `undefined` rather than throwing when no capability
100
- * covers the collection or the read fails (offline, unauthorized), so a caller
101
- * can fall back to its cached copy.
128
+ * epochs), or `undefined` only when the answer is genuinely "no descriptor":
129
+ * no capability covers the collection, the collection is not found (which is
130
+ * also how WAS answers an unauthorized read), or the description carries no
131
+ * `encryption` member.
132
+ *
133
+ * Any other failure (a dropped connection, a 5xx) is thrown, wrapped with
134
+ * the collection id. It must NOT read as "no descriptor": a caller that takes
135
+ * it for one opens the collection under the fail-closed placeholder cipher,
136
+ * and a correctly provisioned collection then fails its first write or
137
+ * decrypt for a network hiccup.
102
138
  *
103
139
  * @param collectionId {string} the WAS collection id
104
140
  * @returns {Promise<CollectionEncryption | undefined>}
@@ -108,18 +144,22 @@ export class WasRemoteStore {
108
144
  if (!capability) {
109
145
  return undefined;
110
146
  }
111
- try {
112
- const response = await this.was.request({
113
- capability,
114
- path: collectionPath(this.spaceId, collectionId),
115
- method: 'GET'
116
- });
117
- const description = response.data;
118
- return description?.encryption;
119
- }
120
- catch {
121
- return undefined;
122
- }
147
+ return await readOrAbsent({
148
+ read: async () => {
149
+ const response = await this.was.request({
150
+ capability,
151
+ path: collectionPath(this.spaceId, collectionId),
152
+ method: 'GET'
153
+ });
154
+ const description = response.data;
155
+ return description?.encryption;
156
+ },
157
+ // A not-found (which is also how WAS answers an unauthorized read) is
158
+ // the one outcome that genuinely means "no descriptor visible to this
159
+ // app".
160
+ isAbsent: err => err instanceof NotFoundError,
161
+ description: `the encryption descriptor of collection "${collectionId}"`
162
+ });
123
163
  }
124
164
  /**
125
165
  * Reads one collection's stored `/meta` value RAW, invoked with that
@@ -130,9 +170,13 @@ export class WasRemoteStore {
130
170
  * NOT `Collection.meta()`: that one DECODES `custom` to plaintext, which
131
171
  * `applyMeta` cannot consume.
132
172
  *
133
- * Non-fatal like the descriptor read: answers `undefined` rather than
134
- * throwing when no capability covers the collection, the read is unauthorized,
135
- * the app is offline, or the backend has no metadata support.
173
+ * Answers `undefined` for exactly the outcomes that mean "no metadata": no
174
+ * capability covers the collection, the read answered not-found (which is
175
+ * also how WAS answers an unauthorized read), or the backend has no metadata
176
+ * support (`501`). Any other failure is thrown, wrapped with the collection
177
+ * id, like {@link readCollectionEncryption}: a dropped connection is an
178
+ * unknown answer, not an absent one, and a caller that took it for "no
179
+ * schema" would skip the blinded-index install over a guess.
136
180
  *
137
181
  * @param collectionId {string} the WAS collection id
138
182
  * @returns {Promise<{ custom?: unknown } | undefined>}
@@ -142,18 +186,19 @@ export class WasRemoteStore {
142
186
  if (!capability) {
143
187
  return undefined;
144
188
  }
145
- try {
146
- const response = await this.was.request({
147
- capability,
148
- path: collectionMeta(this.spaceId, collectionId),
149
- method: 'GET'
150
- });
151
- const stored = response.data;
152
- return { custom: stored?.custom };
153
- }
154
- catch {
155
- return undefined;
156
- }
189
+ return await readOrAbsent({
190
+ read: async () => {
191
+ const response = await this.was.request({
192
+ capability,
193
+ path: collectionMeta(this.spaceId, collectionId),
194
+ method: 'GET'
195
+ });
196
+ const stored = response.data;
197
+ return { custom: stored?.custom };
198
+ },
199
+ isAbsent: err => err instanceof NotFoundError || err instanceof NotImplementedError,
200
+ description: `the metadata of collection "${collectionId}"`
201
+ });
157
202
  }
158
203
  /**
159
204
  * Best-effort declaration of the `{ encryption: { scheme: 'edv' } }` descriptor on
@@ -508,9 +553,13 @@ export class WasRemoteStore {
508
553
  * Deliberately NOT `wasDescriptorSource` from the same subpath: that one
509
554
  * describes through the client's root capability, which a relying-party app
510
555
  * never holds. Every read here must invoke the per-collection grant, which is
511
- * exactly what {@link WasRemoteStore.readCollectionEncryption} does (and, like
512
- * the rest of the descriptor plumbing, it answers `undefined` rather than
513
- * throwing when the read is unauthorized or the app is offline).
556
+ * exactly what {@link WasRemoteStore.readCollectionEncryption} does.
557
+ *
558
+ * A read that fails for a transient reason is warned about and answered as
559
+ * `undefined` here: the refresh guard is already spent by the time the source
560
+ * is consulted, so the decrypt retry fails the same way it would under no
561
+ * refresh at all, and the caller sees the envelope's own unknown-epoch error
562
+ * rather than a network one.
514
563
  *
515
564
  * @param options {object}
516
565
  * @param options.remoteStore {WasRemoteStore}
@@ -519,7 +568,13 @@ export class WasRemoteStore {
519
568
  export function remoteDescriptorSource({ remoteStore }) {
520
569
  return {
521
570
  async collectionEncryption({ collectionId }) {
522
- return await remoteStore.readCollectionEncryption(collectionId);
571
+ try {
572
+ return await remoteStore.readCollectionEncryption(collectionId);
573
+ }
574
+ catch (err) {
575
+ console.warn(`Descriptor refresh for collection "${collectionId}" failed:`, err);
576
+ return undefined;
577
+ }
523
578
  }
524
579
  };
525
580
  }