@interop/was-react 0.8.3 → 0.10.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 (149) hide show
  1. package/README.md +50 -44
  2. package/dist/auth/chapi.d.ts.map +1 -1
  3. package/dist/auth/chapi.js +5 -4
  4. package/dist/auth/chapi.js.map +1 -1
  5. package/dist/auth/loginFlow.d.ts.map +1 -1
  6. package/dist/auth/loginFlow.js +48 -50
  7. package/dist/auth/loginFlow.js.map +1 -1
  8. package/dist/auth/loginRequest.d.ts +38 -5
  9. package/dist/auth/loginRequest.d.ts.map +1 -1
  10. package/dist/auth/loginRequest.js +66 -5
  11. package/dist/auth/loginRequest.js.map +1 -1
  12. package/dist/auth/verifyResponse.d.ts +7 -4
  13. package/dist/auth/verifyResponse.d.ts.map +1 -1
  14. package/dist/auth/verifyResponse.js +23 -13
  15. package/dist/auth/verifyResponse.js.map +1 -1
  16. package/dist/auth/walletRequestTypes.d.ts +9 -90
  17. package/dist/auth/walletRequestTypes.d.ts.map +1 -1
  18. package/dist/config.d.ts +9 -9
  19. package/dist/config.js +0 -0
  20. package/dist/dev/provisionDevGrants.d.ts.map +1 -1
  21. package/dist/dev/provisionDevGrants.js +18 -19
  22. package/dist/dev/provisionDevGrants.js.map +1 -1
  23. package/dist/grants.d.ts +19 -9
  24. package/dist/grants.d.ts.map +1 -1
  25. package/dist/grants.js +17 -2
  26. package/dist/grants.js.map +1 -1
  27. package/dist/identity/agents.d.ts +1 -1
  28. package/dist/identity/agents.d.ts.map +1 -1
  29. package/dist/identity/agents.js +10 -25
  30. package/dist/identity/agents.js.map +1 -1
  31. package/dist/identity/appSession.d.ts +27 -0
  32. package/dist/identity/appSession.d.ts.map +1 -1
  33. package/dist/identity/appSession.js +51 -13
  34. package/dist/identity/appSession.js.map +1 -1
  35. package/dist/identity/seedCredential.d.ts +7 -0
  36. package/dist/identity/seedCredential.d.ts.map +1 -1
  37. package/dist/identity/seedCredential.js +11 -14
  38. package/dist/identity/seedCredential.js.map +1 -1
  39. package/dist/identity/seedStore.d.ts +19 -0
  40. package/dist/identity/seedStore.d.ts.map +1 -1
  41. package/dist/identity/seedStore.js +40 -13
  42. package/dist/identity/seedStore.js.map +1 -1
  43. package/dist/index.d.ts +5 -6
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +4 -5
  46. package/dist/index.js.map +1 -1
  47. package/dist/jsonLd.d.ts +19 -0
  48. package/dist/jsonLd.d.ts.map +1 -0
  49. package/dist/jsonLd.js +24 -0
  50. package/dist/jsonLd.js.map +1 -0
  51. package/dist/mui/AdoptDialog.d.ts.map +1 -1
  52. package/dist/mui/AdoptDialog.js +7 -3
  53. package/dist/mui/AdoptDialog.js.map +1 -1
  54. package/dist/mui/ClearDataDialog.d.ts.map +1 -1
  55. package/dist/mui/ClearDataDialog.js +12 -11
  56. package/dist/mui/ClearDataDialog.js.map +1 -1
  57. package/dist/mui/ConfirmDialog.d.ts +35 -0
  58. package/dist/mui/ConfirmDialog.d.ts.map +1 -0
  59. package/dist/mui/ConfirmDialog.js +18 -0
  60. package/dist/mui/ConfirmDialog.js.map +1 -0
  61. package/dist/mui/LogoutDialog.d.ts.map +1 -1
  62. package/dist/mui/LogoutDialog.js +7 -3
  63. package/dist/mui/LogoutDialog.js.map +1 -1
  64. package/dist/react/documentApp.d.ts +29 -0
  65. package/dist/react/documentApp.d.ts.map +1 -1
  66. package/dist/react/documentApp.js +9 -38
  67. package/dist/react/documentApp.js.map +1 -1
  68. package/dist/react/hooks.d.ts +13 -7
  69. package/dist/react/hooks.d.ts.map +1 -1
  70. package/dist/react/hooks.js +17 -11
  71. package/dist/react/hooks.js.map +1 -1
  72. package/dist/session/authStore.d.ts +9 -9
  73. package/dist/session/authStore.d.ts.map +1 -1
  74. package/dist/session/authStore.js +100 -96
  75. package/dist/session/authStore.js.map +1 -1
  76. package/dist/storage/adopt.d.ts +5 -1
  77. package/dist/storage/adopt.d.ts.map +1 -1
  78. package/dist/storage/adopt.js +8 -24
  79. package/dist/storage/adopt.js.map +1 -1
  80. package/dist/storage/entityStore.d.ts.map +1 -1
  81. package/dist/storage/entityStore.js +27 -20
  82. package/dist/storage/entityStore.js.map +1 -1
  83. package/dist/storage/localStore.d.ts +12 -6
  84. package/dist/storage/localStore.d.ts.map +1 -1
  85. package/dist/storage/localStore.js +118 -74
  86. package/dist/storage/localStore.js.map +1 -1
  87. package/dist/storage/sharedCollectionReader.d.ts +1 -1
  88. package/dist/storage/sharedCollectionReader.d.ts.map +1 -1
  89. package/dist/storage/sharedCollectionReader.js +79 -81
  90. package/dist/storage/sharedCollectionReader.js.map +1 -1
  91. package/dist/storage/storageManager.d.ts +3 -1
  92. package/dist/storage/storageManager.d.ts.map +1 -1
  93. package/dist/storage/storageManager.js +20 -6
  94. package/dist/storage/storageManager.js.map +1 -1
  95. package/dist/storage/syncController.d.ts +1 -16
  96. package/dist/storage/syncController.d.ts.map +1 -1
  97. package/dist/storage/syncController.js +38 -58
  98. package/dist/storage/syncController.js.map +1 -1
  99. package/dist/storage/syncStatusStore.d.ts +8 -6
  100. package/dist/storage/syncStatusStore.d.ts.map +1 -1
  101. package/dist/storage/syncStatusStore.js +6 -6
  102. package/dist/storage/syncStatusStore.js.map +1 -1
  103. package/dist/storage/wasRemoteStore.d.ts +34 -2
  104. package/dist/storage/wasRemoteStore.d.ts.map +1 -1
  105. package/dist/storage/wasRemoteStore.js +45 -7
  106. package/dist/storage/wasRemoteStore.js.map +1 -1
  107. package/dist/storage/wasSync.d.ts.map +1 -1
  108. package/dist/storage/wasSync.js +47 -38
  109. package/dist/storage/wasSync.js.map +1 -1
  110. package/dist/sync/changesQuery.d.ts.map +1 -1
  111. package/dist/sync/changesQuery.js +2 -12
  112. package/dist/sync/changesQuery.js.map +1 -1
  113. package/dist/sync/docCipher.d.ts +17 -9
  114. package/dist/sync/docCipher.d.ts.map +1 -1
  115. package/dist/sync/docCipher.js +17 -11
  116. package/dist/sync/docCipher.js.map +1 -1
  117. package/dist/sync/feedMasterPort.d.ts +6 -0
  118. package/dist/sync/feedMasterPort.d.ts.map +1 -1
  119. package/dist/sync/feedMasterPort.js +82 -40
  120. package/dist/sync/feedMasterPort.js.map +1 -1
  121. package/dist/sync/index.d.ts +6 -5
  122. package/dist/sync/index.d.ts.map +1 -1
  123. package/dist/sync/index.js +6 -3
  124. package/dist/sync/index.js.map +1 -1
  125. package/dist/sync/lww.d.ts +15 -14
  126. package/dist/sync/lww.d.ts.map +1 -1
  127. package/dist/sync/lww.js +3 -3
  128. package/dist/sync/lww.js.map +1 -1
  129. package/dist/sync/lwwConflictHandler.d.ts +3 -10
  130. package/dist/sync/lwwConflictHandler.d.ts.map +1 -1
  131. package/dist/sync/lwwConflictHandler.js +7 -17
  132. package/dist/sync/lwwConflictHandler.js.map +1 -1
  133. package/dist/sync/pushWrites.d.ts +6 -8
  134. package/dist/sync/pushWrites.d.ts.map +1 -1
  135. package/dist/sync/pushWrites.js +56 -83
  136. package/dist/sync/pushWrites.js.map +1 -1
  137. package/dist/sync/types.d.ts +80 -4
  138. package/dist/sync/types.d.ts.map +1 -1
  139. package/dist/sync/types.js +36 -18
  140. package/dist/sync/types.js.map +1 -1
  141. package/dist/sync/wasSyncPort.d.ts +0 -11
  142. package/dist/sync/wasSyncPort.d.ts.map +1 -1
  143. package/dist/sync/wasSyncPort.js +8 -33
  144. package/dist/sync/wasSyncPort.js.map +1 -1
  145. package/package.json +5 -5
  146. package/dist/identity/initAppSession.d.ts +0 -27
  147. package/dist/identity/initAppSession.d.ts.map +0 -1
  148. package/dist/identity/initAppSession.js +0 -30
  149. package/dist/identity/initAppSession.js.map +0 -1
@@ -1,68 +1,20 @@
1
- import { WasSyncAuthError, WasSyncConflictError } from './types.js';
1
+ import { formatEtag } from '@interop/was-client/sync';
2
+ import { bodiesEqual, copyOptionalBodyFields, WasSyncAuthError, WasSyncConflictError } from './types.js';
2
3
  /**
3
- * Formats a master revision (`version` or `metaVersion`) as the quoted strong
4
- * ETag the server compares `If-Match` against (revision `3` becomes `"3"`).
5
- *
6
- * @param revision {number}
7
- * @returns {string}
8
- */
9
- export function formatEtag(revision) {
10
- return `"${revision}"`;
11
- }
12
- /**
13
- * Structural equality over two opaque bodies, by canonical-free JSON string.
14
- * Used only to decide whether the content or the metadata half changed and thus
15
- * which endpoint(s) to write. Content-addressed collections never mutate `data`
16
- * for a given id (so the content half only fires on create/delete), and a real
17
- * metadata edit re-encrypts to fresh bytes, so this coarse comparison is
18
- * sufficient for routing.
19
- *
20
- * @param left {Json | undefined}
21
- * @param right {Json | undefined}
22
- * @returns {boolean}
23
- */
24
- function bodiesEqual(left, right) {
25
- return JSON.stringify(left ?? null) === JSON.stringify(right ?? null);
26
- }
27
- /**
28
- * Maps a re-read master state into the RxDB conflict entry for one row.
29
- */
30
- function masterToConflict(id, master) {
31
- const conflict = {
32
- id,
33
- updatedAt: master.updatedAt,
34
- version: master.version,
35
- _deleted: master.deleted
36
- };
37
- if (master.data !== undefined) {
38
- conflict.data = master.data;
39
- }
40
- if (master.metaVersion !== undefined) {
41
- conflict.metaVersion = master.metaVersion;
42
- }
43
- if (master.custom !== undefined) {
44
- conflict.custom = master.custom;
45
- }
46
- if (master.epoch !== undefined) {
47
- conflict.epoch = master.epoch;
48
- }
49
- return conflict;
50
- }
51
- /**
52
- * Builds the RxDB conflict entry (the real current master state) for a row whose
53
- * conditional write was rejected with `412`. Re-reads the resource; when it is
54
- * genuinely absent (a delete/delete race) the master is a tombstone synthesized
55
- * from what we know locally.
4
+ * Maps a re-read master state into the RxDB conflict entry for one row, or --
5
+ * when the re-read found the resource genuinely absent (`master === null`, a
6
+ * delete/delete race) -- synthesizes the tombstone conflict entry from what we
7
+ * know locally. Shared by the `412` assembler and the `/meta` 404 recovery, so
8
+ * both report an absent master identically.
56
9
  *
57
10
  * @param options {object}
58
- * @param options.port {WasSyncPort}
59
11
  * @param options.id {string}
12
+ * @param options.master {MasterState | null}
60
13
  * @param options.fallbackUpdatedAt {string} used if the resource is now absent
61
14
  * @param options.fallbackVersion {number} used if the resource is now absent
62
- * @returns {Promise<WithDeleted<SyncedDoc>>}
15
+ * @returns {WithDeleted<SyncedDoc>}
63
16
  */
64
- async function assembleConflict({ port, id, fallbackUpdatedAt, fallbackVersion }) {
65
- const master = await port.get({ id });
17
+ function masterOrTombstone({ id, master, fallbackUpdatedAt, fallbackVersion }) {
66
18
  if (master === null) {
67
19
  return {
68
20
  id,
@@ -71,7 +23,14 @@ async function assembleConflict({ port, id, fallbackUpdatedAt, fallbackVersion }
71
23
  _deleted: true
72
24
  };
73
25
  }
74
- return masterToConflict(id, master);
26
+ const conflict = {
27
+ id,
28
+ updatedAt: master.updatedAt,
29
+ version: master.version,
30
+ _deleted: master.deleted
31
+ };
32
+ copyOptionalBodyFields({ source: master, target: conflict });
33
+ return conflict;
75
34
  }
76
35
  /**
77
36
  * Sends one local change to the remote Collection as up to two conditional
@@ -85,29 +44,43 @@ async function assembleConflict({ port, id, fallbackUpdatedAt, fallbackVersion }
85
44
  * @param options.port {WasSyncPort}
86
45
  * @param options.newDocumentState {WithDeleted<SyncedDoc>}
87
46
  * @param [options.assumedMasterState] {WithDeleted<SyncedDoc>}
47
+ * @param [options.cache] {MasterReadCache} the push batch's shared
48
+ * master-read memo
88
49
  * @returns {Promise<{ conflict: WithDeleted<SyncedDoc> | null,
89
50
  * ack: PushWriteAck | null }>}
90
51
  */
91
- async function pushRow({ port, newDocumentState, assumedMasterState }) {
52
+ async function pushRow({ port, newDocumentState, assumedMasterState, cache }) {
92
53
  const { id } = newDocumentState;
93
54
  const assumedVersion = assumedMasterState?.version;
94
55
  const isCreate = assumedMasterState === undefined;
95
56
  const ack = { id };
96
57
  const hasAck = () => ack.version !== undefined || ack.metaVersion !== undefined;
97
- // Builds the conflict outcome for a 412 at either half, PRESERVING any ack
98
- // already earned: a content write accepted before a `/meta` 412 must keep its
99
- // acked `version` (and feed it to the re-read fallback), or the local row
100
- // keeps the pre-write version and every later conditional write sends a
101
- // stale `If-Match`.
102
- const conflictResult = async () => {
103
- const conflict = await assembleConflict({
104
- port,
58
+ const fallbackVersion = () => ack.version ?? assumedVersion ?? newDocumentState.version;
59
+ // Re-reads this row's master. A row that has ALREADY written this batch
60
+ // (a content write accepted before a `/meta` rejection) bypasses the batch
61
+ // memo: a sibling's feed read may have paged past this resource before our
62
+ // own write landed, and the conflict entry must carry the revision that write
63
+ // produced. Rows that wrote nothing -- the ordinary content 412, the common
64
+ // case -- are exactly what the memo is for.
65
+ const readMaster = async () => hasAck()
66
+ ? port.get({ id })
67
+ : port.get({ id, ...(cache !== undefined && { cache }) });
68
+ // Builds the conflict outcome from a re-read master (or from its absence, a
69
+ // delete/delete race), PRESERVING any ack already earned: a content write
70
+ // accepted before a `/meta` 412 must keep its acked `version` (and feed it to
71
+ // the absent-master fallback), or the local row keeps the pre-write version
72
+ // and every later conditional write sends a stale `If-Match`.
73
+ const conflictOutcome = (master) => ({
74
+ conflict: masterOrTombstone({
105
75
  id,
76
+ master,
106
77
  fallbackUpdatedAt: newDocumentState.updatedAt,
107
- fallbackVersion: ack.version ?? assumedVersion ?? newDocumentState.version
108
- });
109
- return { conflict, ack: hasAck() ? ack : null };
110
- };
78
+ fallbackVersion: fallbackVersion()
79
+ }),
80
+ ack: hasAck() ? ack : null
81
+ });
82
+ // The 412 path: re-read the resource, then report its real master state.
83
+ const conflictResult = async () => conflictOutcome(await readMaster());
111
84
  try {
112
85
  if (newDocumentState._deleted) {
113
86
  // Delete supersedes any metadata write: drop the content, tombstone wins.
@@ -189,17 +162,9 @@ async function pushRow({ port, newDocumentState, assumedMasterState }) {
189
162
  // (the conflict handler reconciles it) instead of flipping the whole
190
163
  // session to "access expired" and wedging the batch in RxDB's retries.
191
164
  if (err instanceof WasSyncAuthError && err.status === 404) {
192
- const master = await port.get({ id });
165
+ const master = await readMaster();
193
166
  if (master === null || master.deleted) {
194
- const conflict = master
195
- ? masterToConflict(id, master)
196
- : {
197
- id,
198
- updatedAt: newDocumentState.updatedAt,
199
- version: ack.version ?? assumedVersion ?? newDocumentState.version,
200
- _deleted: true
201
- };
202
- return { conflict, ack: hasAck() ? ack : null };
167
+ return conflictOutcome(master);
203
168
  }
204
169
  // The resource is alive and readable while its `/meta` write 404s:
205
170
  // the write itself was rejected, so the auth signal stands.
@@ -221,6 +186,12 @@ async function pushRow({ port, newDocumentState, assumedMasterState }) {
221
186
  * caller can write the new `version` / `metaVersion` back into the local row
222
187
  * and keep subsequent conditional writes' `If-Match` in step with the server.
223
188
  *
189
+ * Each batch gets one short-lived master-read memo, shared by its rows and
190
+ * discarded with the batch (never held across batches, where it would go
191
+ * stale). A conflict re-read walks the changes feed from its origin and so pages
192
+ * past every other conflicting row's master on the way; without the memo a batch
193
+ * with k conflicts would run k concurrent full-feed walks.
194
+ *
224
195
  * @param port {WasSyncPort}
225
196
  * @param [onWriteAccepted] {(ack: PushWriteAck) => Promise<void>}
226
197
  * @returns {(rows: Array<{ newDocumentState: WithDeleted<SyncedDoc>,
@@ -229,11 +200,13 @@ async function pushRow({ port, newDocumentState, assumedMasterState }) {
229
200
  */
230
201
  export function createPushHandler(port, onWriteAccepted) {
231
202
  return async function push(rows) {
203
+ const cache = { byId: new Map(), inFlight: null };
232
204
  const results = await Promise.all(rows.map(async (row) => {
233
205
  const result = await pushRow({
234
206
  port,
235
207
  newDocumentState: row.newDocumentState,
236
- assumedMasterState: row.assumedMasterState
208
+ assumedMasterState: row.assumedMasterState,
209
+ cache
237
210
  });
238
211
  if (result.ack !== null && onWriteAccepted !== undefined) {
239
212
  await onWriteAccepted(result.ack);
@@ -1 +1 @@
1
- {"version":3,"file":"pushWrites.js","sourceRoot":"","sources":["../../src/sync/pushWrites.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pushWrites.js","sourceRoot":"","sources":["../../src/sync/pushWrites.ts"],"names":[],"mappings":"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"}
@@ -19,15 +19,63 @@
19
19
  * or the EDV envelope on an encrypted collection) and `custom` is the stored
20
20
  * metadata body (an opaque envelope on an encrypted collection); encrypt/decrypt
21
21
  * stays a read/write-time concern above this layer.
22
+ *
23
+ * The two small helpers that travel with these shapes live here as well: the
24
+ * opaque-body equality every routing decision is made on, and the optional-field
25
+ * copy every wire-to-local mapping runs.
22
26
  */
27
+ import type { Json } from '@interop/was-client';
23
28
  /**
24
29
  * A JSON value -- the opaque stored resource body the sync layer moves verbatim.
25
30
  * For a plaintext collection this is the user document; for an encrypted one it
26
- * is the EDV envelope. The adapter never inspects or transforms it.
31
+ * is the EDV envelope. The adapter never inspects or transforms it. Re-exported
32
+ * from `@interop/was-client`, which owns the wire model, so a body crosses the
33
+ * port boundary without a cast.
34
+ */
35
+ export type { Json };
36
+ /**
37
+ * The optional half of a synced document, shared verbatim by every shape it
38
+ * travels in ({@link WireDoc} on the feed, {@link SyncedDoc} locally,
39
+ * {@link MasterState} on the conflict re-read): the independently-versioned
40
+ * metadata revision and body, the content body, and the content body's
41
+ * key-epoch stamp. Each is genuinely absent rather than `undefined` when the
42
+ * server has nothing for it, so every mapping between the shapes copies them
43
+ * conditionally -- see {@link copyOptionalBodyFields}.
44
+ */
45
+ export interface OptionalBodyFields {
46
+ metaVersion?: number;
47
+ data?: Json;
48
+ custom?: Json;
49
+ epoch?: string;
50
+ }
51
+ /**
52
+ * Structural equality over two opaque bodies, by canonical-free JSON string.
53
+ * Decides whether the content or the metadata half changed -- which endpoint(s)
54
+ * a push writes, and whether two states compare equal for conflict resolution.
55
+ * Content-addressed collections never mutate `data` for a given id, and a real
56
+ * metadata edit re-encrypts to fresh bytes, so this coarse comparison suffices.
57
+ *
58
+ * @param left {Json | undefined}
59
+ * @param right {Json | undefined}
60
+ * @returns {boolean}
27
61
  */
28
- export type Json = null | boolean | number | string | Json[] | {
29
- [key: string]: Json;
30
- };
62
+ export declare function bodiesEqual(left: Json | undefined, right: Json | undefined): boolean;
63
+ /**
64
+ * Copies the {@link OptionalBodyFields} that are present on `source` onto
65
+ * `target`, leaving an absent field absent (never writing an explicit
66
+ * `undefined`, which would surface the key in a serialized body). The single
67
+ * place every wire-to-local, feed-to-master, and master-to-conflict mapping
68
+ * shares, so a new optional wire field is added once.
69
+ *
70
+ * @param options {object}
71
+ * @param options.source {OptionalBodyFields}
72
+ * @param options.target {OptionalBodyFields} mutated in place
73
+ * @returns {void}
74
+ */
75
+ export declare function copyOptionalBodyFields({ source, target }: {
76
+ source: OptionalBodyFields;
77
+ target: OptionalBodyFields;
78
+ }): void;
31
79
  /**
32
80
  * The keyset position in the change feed: the `{ id, updatedAt }` of the last
33
81
  * document a pull returned. Passed back verbatim to resume, and used as the
@@ -103,6 +151,26 @@ export interface MasterState {
103
151
  */
104
152
  epoch?: string;
105
153
  }
154
+ /**
155
+ * The short-lived master-read memo the rows of ONE push batch share. A
156
+ * resolving `get` implementation may answer from `byId` and MUST record every
157
+ * master it resolved on the way there; a feed-walking implementation pages past
158
+ * all of them anyway, so a batch of k conflicts costs one walk instead of k.
159
+ *
160
+ * `inFlight` is what makes that hold under concurrency: the batch's rows push in
161
+ * parallel, so without it every row would start its own walk before the first
162
+ * one finished and the memo would never be read. A `get` that must walk
163
+ * publishes its walk here; a `get` that finds a walk already running awaits it
164
+ * and re-checks `byId` first. It settles (never rejects) so one row's failed
165
+ * read is not another row's, and is `null` whenever no walk is running.
166
+ *
167
+ * The whole object is created per batch invocation and dropped with it: a memo
168
+ * held across batches would go stale.
169
+ */
170
+ export interface MasterReadCache {
171
+ byId: Map<string, MasterState | null>;
172
+ inFlight: Promise<void> | null;
173
+ }
106
174
  /**
107
175
  * The write/query half of the injected WAS-access seam. An adapter (the app-side
108
176
  * `createWasSyncPort`) implements this over `@interop/was-client`; the core
@@ -221,12 +289,20 @@ export interface WasSyncPort extends WasSyncBasePort {
221
289
  * cannot be resolved (e.g. a feed re-read that exhausts its scan budget), so
222
290
  * the replication cycle retries rather than fabricating a false tombstone.
223
291
  *
292
+ * `cache` is an OPTIONAL {@link MasterReadCache}, shared by the rows of one
293
+ * push batch (see its own docs for the contract). A hit is no staler than a
294
+ * read issued at the same moment, since the memo lives only for that batch. An
295
+ * implementation that ignores it is fully conformant, and a caller that omits
296
+ * it gets an uncached read.
297
+ *
224
298
  * @param options {object}
225
299
  * @param options.id {string}
300
+ * @param [options.cache] {MasterReadCache}
226
301
  * @returns {Promise<MasterState | null>}
227
302
  */
228
303
  get(options: {
229
304
  id: string;
305
+ cache?: MasterReadCache;
230
306
  }): Promise<MasterState | null>;
231
307
  }
232
308
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/sync/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/sync/types.ts"],"names":[],"mappings":"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"}
@@ -1,25 +1,43 @@
1
- /*!
2
- * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
- */
4
1
  /**
5
- * Shared types for the collection-agnostic WAS replication adapter.
2
+ * Structural equality over two opaque bodies, by canonical-free JSON string.
3
+ * Decides whether the content or the metadata half changed -- which endpoint(s)
4
+ * a push writes, and whether two states compare equal for conflict resolution.
5
+ * Content-addressed collections never mutate `data` for a given id, and a real
6
+ * metadata edit re-encrypts to fresh bytes, so this coarse comparison suffices.
6
7
  *
7
- * The adapter drives an RxDB `replicateRxCollection` state machine against a
8
- * remote WAS Collection's replication endpoints. It is deliberately free of any
9
- * React or app imports so it can stand alone; all WAS access is injected through
10
- * the {@link WasSyncPort} seam rather than importing `@interop/was-client`
11
- * directly.
8
+ * @param left {Json | undefined}
9
+ * @param right {Json | undefined}
10
+ * @returns {boolean}
11
+ */
12
+ export function bodiesEqual(left, right) {
13
+ return JSON.stringify(left ?? null) === JSON.stringify(right ?? null);
14
+ }
15
+ /**
16
+ * Copies the {@link OptionalBodyFields} that are present on `source` onto
17
+ * `target`, leaving an absent field absent (never writing an explicit
18
+ * `undefined`, which would surface the key in a serialized body). The single
19
+ * place every wire-to-local, feed-to-master, and master-to-conflict mapping
20
+ * shares, so a new optional wire field is added once.
12
21
  *
13
- * The wire contract follows the WAS `changes` feed and its V2 encrypted-metadata
14
- * profile: a synced document carries both a content revision (`version` /
15
- * `data`) and an independently-versioned metadata sub-resource (`metaVersion` /
16
- * `custom`). A metadata-only edit re-surfaces the resource with a bumped
17
- * `updatedAt` / `metaVersion` but unchanged `version` / `data`. The sync layer
18
- * moves both bodies opaquely: `data` is the stored content body (plaintext JSON,
19
- * or the EDV envelope on an encrypted collection) and `custom` is the stored
20
- * metadata body (an opaque envelope on an encrypted collection); encrypt/decrypt
21
- * stays a read/write-time concern above this layer.
22
+ * @param options {object}
23
+ * @param options.source {OptionalBodyFields}
24
+ * @param options.target {OptionalBodyFields} mutated in place
25
+ * @returns {void}
22
26
  */
27
+ export function copyOptionalBodyFields({ source, target }) {
28
+ if (source.data !== undefined) {
29
+ target.data = source.data;
30
+ }
31
+ if (source.metaVersion !== undefined) {
32
+ target.metaVersion = source.metaVersion;
33
+ }
34
+ if (source.custom !== undefined) {
35
+ target.custom = source.custom;
36
+ }
37
+ if (source.epoch !== undefined) {
38
+ target.epoch = source.epoch;
39
+ }
40
+ }
23
41
  /**
24
42
  * Thrown by a {@link WasSyncPort} implementation when a conditional write is
25
43
  * rejected with `412 precondition-failed` (a lost-update conflict, or a
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/sync/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;GAkBG;AAuNH;;;;;;GAMG;AACH,MAAM,OAAO,oBAAqB,SAAQ,KAAK;IAC7C,YAAY,OAAO,GAAG,4CAA4C;QAChE,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAA;IACpC,CAAC;CACF;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IAChC,MAAM,CAAQ;IACvB,YAAY,MAAc;QACxB,KAAK,CAAC,mCAAmC,MAAM,IAAI,CAAC,CAAA;QACpD,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAA;QAC9B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;IACtB,CAAC;CACF"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/sync/types.ts"],"names":[],"mappings":"AAqDA;;;;;;;;;;GAUG;AACH,MAAM,UAAU,WAAW,CACzB,IAAsB,EACtB,KAAuB;IAEvB,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,IAAI,IAAI,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,IAAI,CAAC,CAAA;AACvE,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,sBAAsB,CAAC,EACrC,MAAM,EACN,MAAM,EAIP;IACC,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC9B,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAA;IAC3B,CAAC;IACD,IAAI,MAAM,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;QACrC,MAAM,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,CAAA;IACzC,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAChC,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAA;IAC/B,CAAC;IACD,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QAC/B,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAA;IAC7B,CAAC;AACH,CAAC;AA8OD;;;;;;GAMG;AACH,MAAM,OAAO,oBAAqB,SAAQ,KAAK;IAC7C,YAAY,OAAO,GAAG,4CAA4C;QAChE,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAA;IACpC,CAAC;CACF;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IAChC,MAAM,CAAQ;IACvB,YAAY,MAAc;QACxB,KAAK,CAAC,mCAAmC,MAAM,IAAI,CAAC,CAAA;QACpD,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAA;QAC9B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;IACtB,CAAC;CACF"}
@@ -26,17 +26,6 @@
26
26
  import { type WasClient } from '@interop/was-client';
27
27
  import type { IZcap } from '@interop/data-integrity-core';
28
28
  import { type WasSyncBasePort } from './types.js';
29
- /**
30
- * Extracts an HTTP status from a raw ky/ezcap error. `was.request()` rejects on
31
- * any non-2xx with `err.status` set (see `@interop/http-client`'s error
32
- * normaliser); this reads it defensively from either location. Shared with the
33
- * remote store's descriptor PUT and the dev-grant provisioner, which need the raw
34
- * status for diagnostics rather than the mapped sync error.
35
- *
36
- * @param err {unknown}
37
- * @returns {number | undefined}
38
- */
39
- export declare function errorStatus(err: unknown): number | undefined;
40
29
  /**
41
30
  * Normalizes an unknown caught error into a display string: the `Error`'s
42
31
  * `message` when it is one, else its `String(...)` coercion. Shared by the
@@ -1 +1 @@
1
- {"version":3,"file":"wasSyncPort.d.ts","sourceRoot":"","sources":["../../src/sync/wasSyncPort.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EAA0B,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAC5E,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,8BAA8B,CAAA;AACzD,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,YAAY,CAAA;AAEnB;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAK5D;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAEjD;AAgDD;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,GAAG,EACH,OAAO,EACP,YAAY,EACZ,UAAU,EACX,EAAE;IACD,GAAG,EAAE,SAAS,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,CAAC,EAAE,KAAK,CAAA;CACnB,GAAG,eAAe,CAiHlB"}
1
+ {"version":3,"file":"wasSyncPort.d.ts","sourceRoot":"","sources":["../../src/sync/wasSyncPort.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EAA0B,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAE5E,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,8BAA8B,CAAA;AAEzD,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,YAAY,CAAA;AAEnB;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAEjD;AAgCD;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,GAAG,EACH,OAAO,EACP,YAAY,EACZ,UAAU,EACX,EAAE;IACD,GAAG,EAAE,SAAS,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,CAAC,EAAE,KAAK,CAAA;CACnB,GAAG,eAAe,CAkHlB"}
@@ -24,21 +24,9 @@
24
24
  * encrypted fork here.
25
25
  */
26
26
  import { readEtag, writeHeaders } from '@interop/was-client';
27
+ import { errorStatus, parseEtag } from '@interop/was-client/sync';
28
+ import { collectionPath } from '../grants.js';
27
29
  import { WasSyncAuthError, WasSyncConflictError } from './types.js';
28
- /**
29
- * Extracts an HTTP status from a raw ky/ezcap error. `was.request()` rejects on
30
- * any non-2xx with `err.status` set (see `@interop/http-client`'s error
31
- * normaliser); this reads it defensively from either location. Shared with the
32
- * remote store's descriptor PUT and the dev-grant provisioner, which need the raw
33
- * status for diagnostics rather than the mapped sync error.
34
- *
35
- * @param err {unknown}
36
- * @returns {number | undefined}
37
- */
38
- export function errorStatus(err) {
39
- return (err.status ??
40
- err.response?.status);
41
- }
42
30
  /**
43
31
  * Normalizes an unknown caught error into a display string: the `Error`'s
44
32
  * `message` when it is one, else its `String(...)` coercion. Shared by the
@@ -79,21 +67,6 @@ function toPortError(err) {
79
67
  }
80
68
  return err;
81
69
  }
82
- /**
83
- * Parses a quoted strong ETag (`"3"`, as read by `readEtag`) into its numeric
84
- * revision, or `undefined` when the header was absent (the resource has no such
85
- * revision yet).
86
- *
87
- * @param etag {string | undefined}
88
- * @returns {number | undefined}
89
- */
90
- function parseEtag(etag) {
91
- if (etag === undefined) {
92
- return undefined;
93
- }
94
- const revision = Number(etag.replace(/"/g, ''));
95
- return Number.isFinite(revision) ? revision : undefined;
96
- }
97
70
  /**
98
71
  * Builds a `WasSyncPort` bound to a single Space + Collection on the remote WAS
99
72
  * server, backed by the session's signed `WasClient`.
@@ -108,8 +81,8 @@ function parseEtag(etag) {
108
81
  * @returns {WasSyncBasePort}
109
82
  */
110
83
  export function createWasSyncPort({ was, spaceId, collectionId, capability }) {
111
- const collectionPath = `/space/${spaceId}/${collectionId}`;
112
- const resourcePath = (id) => `${collectionPath}/${encodeURIComponent(id)}`;
84
+ const basePath = collectionPath({ spaceId, collectionId });
85
+ const resourcePath = (id) => `${basePath}/${encodeURIComponent(id)}`;
113
86
  /**
114
87
  * Runs a conditional write, mapping the server's `412 precondition-failed`
115
88
  * into the core's `WasSyncConflictError` and a `401` / `403` into
@@ -125,7 +98,9 @@ export function createWasSyncPort({ was, spaceId, collectionId, capability }) {
125
98
  run) => {
126
99
  try {
127
100
  const response = await run();
128
- return parseEtag(readEtag(response));
101
+ // `readEtag` reports an absent header as `undefined`; `parseEtag` reads
102
+ // the `null` spelling of the same thing.
103
+ return parseEtag(readEtag(response) ?? null);
129
104
  }
130
105
  catch (err) {
131
106
  throw toPortError(err);
@@ -136,7 +111,7 @@ export function createWasSyncPort({ was, spaceId, collectionId, capability }) {
136
111
  try {
137
112
  const response = await was.request({
138
113
  capability,
139
- path: `${collectionPath}/query`,
114
+ path: `${basePath}/query`,
140
115
  method: 'POST',
141
116
  json: {
142
117
  profile: 'changes',
@@ -1 +1 @@
1
- {"version":3,"file":"wasSyncPort.js","sourceRoot":"","sources":["../../src/sync/wasSyncPort.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"wasSyncPort.js","sourceRoot":"","sources":["../../src/sync/wasSyncPort.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@interop/was-react",
3
3
  "description": "React library for building 'Bring Your Own Everything' (BYOE) apps on Wallet Attached Storage: DID Auth login via CHAPI wallet, local-first encrypted storage, and background sync to a WAS server.",
4
- "version": "0.8.3",
4
+ "version": "0.10.0",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "build": "pnpm run clear && tsc",
@@ -53,10 +53,9 @@
53
53
  "@interop/security-document-loader": "^9.5.1",
54
54
  "@interop/vc": "^11.0.6",
55
55
  "@interop/verifier-core": "^3.5.3",
56
- "@interop/wallet-core": "^0.16.0",
57
- "@interop/was-client": "^0.26.0",
56
+ "@interop/wallet-core": "^0.20.0",
57
+ "@interop/was-client": "^0.27.0",
58
58
  "@interop/webkms-client": "^14.7.1",
59
- "@interop/x25519-key-agreement-key": "^5.2.1",
60
59
  "@scure/base": "^2.2.0",
61
60
  "byoe-context": "^0.2.0",
62
61
  "credential-handler-polyfill": "^4.1.0",
@@ -85,6 +84,7 @@
85
84
  "@emotion/react": "^11.14.0",
86
85
  "@emotion/styled": "^11.14.1",
87
86
  "@eslint/js": "^10.0.1",
87
+ "@interop/x25519-key-agreement-key": "^5.2.1",
88
88
  "@mui/icons-material": "^9.0.0",
89
89
  "@mui/material": "^9.0.0",
90
90
  "@playwright/test": "^1.60.0",
@@ -111,7 +111,7 @@
111
111
  "typescript-eslint": "^8.59.4",
112
112
  "vite": "^8.0.14",
113
113
  "vitest": "^4.1.7",
114
- "was-teaching-server": "^0.16.0",
114
+ "was-teaching-server": "^0.17.1",
115
115
  "zustand": "^5.0.12"
116
116
  },
117
117
  "publishConfig": {
@@ -1,27 +0,0 @@
1
- /*!
2
- * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
- */
4
- /**
5
- * App-session identity bootstrap: everything derivable from the master seed in
6
- * one call. Thin composition over the pinned derivation in `agents.ts` (which
7
- * is the shared-key contract: `CapabilityAgent.fromSeed` on raw bytes, keyName
8
- * `app-key` -- never change it without a migration plan).
9
- *
10
- * The agents it returns carry the app's identity key-agreement key, which is
11
- * the ONE key every encrypted collection is read with: the local replica's
12
- * ciphers (`LocalStore.init`) and every key-epoch roster entry the wallet
13
- * writes for this app alike.
14
- */
15
- import { type IdentityAgents } from './agents.js';
16
- /**
17
- * Derives the app's identity agents (stable did:key controller, signer,
18
- * ZcapClient) from the master seed.
19
- *
20
- * @param options {object}
21
- * @param options.seed {Uint8Array} the 32-byte master seed
22
- * @returns {Promise<IdentityAgents>}
23
- */
24
- export declare function initAppSession({ seed }: {
25
- seed: Uint8Array;
26
- }): Promise<IdentityAgents>;
27
- //# sourceMappingURL=initAppSession.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"initAppSession.d.ts","sourceRoot":"","sources":["../../src/identity/initAppSession.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;GAUG;AACH,OAAO,EAAkB,KAAK,cAAc,EAAE,MAAM,aAAa,CAAA;AAEjE;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAAC,EACnC,IAAI,EACL,EAAE;IACD,IAAI,EAAE,UAAU,CAAA;CACjB,GAAG,OAAO,CAAC,cAAc,CAAC,CAK1B"}