@interop/was-react 0.11.1 → 0.13.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 (73) hide show
  1. package/README.md +23 -25
  2. package/dist/auth/loginFlow.d.ts +8 -5
  3. package/dist/auth/loginFlow.d.ts.map +1 -1
  4. package/dist/auth/loginFlow.js +46 -16
  5. package/dist/auth/loginFlow.js.map +1 -1
  6. package/dist/auth/loginRequest.d.ts +27 -81
  7. package/dist/auth/loginRequest.d.ts.map +1 -1
  8. package/dist/auth/loginRequest.js +94 -41
  9. package/dist/auth/loginRequest.js.map +1 -1
  10. package/dist/auth/verifyResponse.d.ts +3 -3
  11. package/dist/auth/verifyResponse.d.ts.map +1 -1
  12. package/dist/auth/verifyResponse.js +16 -13
  13. package/dist/auth/verifyResponse.js.map +1 -1
  14. package/dist/auth/walletRequestTypes.d.ts +8 -6
  15. package/dist/auth/walletRequestTypes.d.ts.map +1 -1
  16. package/dist/config.d.ts +19 -13
  17. package/dist/config.d.ts.map +1 -1
  18. package/dist/config.js +0 -0
  19. package/dist/config.js.map +1 -1
  20. package/dist/dev/provisionDevGrants.d.ts.map +1 -1
  21. package/dist/dev/provisionDevGrants.js +7 -12
  22. package/dist/dev/provisionDevGrants.js.map +1 -1
  23. package/dist/grants.d.ts +0 -14
  24. package/dist/grants.d.ts.map +1 -1
  25. package/dist/grants.js +0 -13
  26. package/dist/grants.js.map +1 -1
  27. package/dist/identity/seedCredential.d.ts +39 -38
  28. package/dist/identity/seedCredential.d.ts.map +1 -1
  29. package/dist/identity/seedCredential.js +79 -77
  30. package/dist/identity/seedCredential.js.map +1 -1
  31. package/dist/index.d.ts +3 -3
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +3 -3
  34. package/dist/index.js.map +1 -1
  35. package/dist/react/documentApp.d.ts +6 -6
  36. package/dist/react/documentApp.d.ts.map +1 -1
  37. package/dist/react/documentApp.js +7 -6
  38. package/dist/react/documentApp.js.map +1 -1
  39. package/dist/react/hooks.d.ts +1 -1
  40. package/dist/react/hooks.js +1 -1
  41. package/dist/session/authStore.d.ts +1 -1
  42. package/dist/session/authStore.js +6 -6
  43. package/dist/session/authStore.js.map +1 -1
  44. package/dist/storage/adopt.d.ts +3 -3
  45. package/dist/storage/adopt.js +5 -5
  46. package/dist/storage/entityStore.d.ts +1 -1
  47. package/dist/storage/localStore.d.ts +2 -2
  48. package/dist/storage/localStore.js +2 -2
  49. package/dist/storage/storageManager.d.ts +8 -3
  50. package/dist/storage/storageManager.d.ts.map +1 -1
  51. package/dist/storage/storageManager.js +19 -12
  52. package/dist/storage/storageManager.js.map +1 -1
  53. package/dist/storage/wasRemoteStore.d.ts +1 -1
  54. package/dist/storage/wasRemoteStore.d.ts.map +1 -1
  55. package/dist/storage/wasRemoteStore.js +8 -8
  56. package/dist/storage/wasRemoteStore.js.map +1 -1
  57. package/dist/sync/index.d.ts +2 -2
  58. package/dist/sync/index.d.ts.map +1 -1
  59. package/dist/sync/index.js +2 -2
  60. package/dist/sync/index.js.map +1 -1
  61. package/dist/sync/lww.d.ts +4 -4
  62. package/dist/sync/lww.js +7 -7
  63. package/dist/sync/lwwConflictHandler.d.ts +4 -4
  64. package/dist/sync/lwwConflictHandler.js +1 -1
  65. package/dist/sync/types.d.ts +28 -45
  66. package/dist/sync/types.d.ts.map +1 -1
  67. package/dist/sync/types.js +17 -35
  68. package/dist/sync/types.js.map +1 -1
  69. package/dist/sync/wasSyncPort.d.ts +14 -31
  70. package/dist/sync/wasSyncPort.d.ts.map +1 -1
  71. package/dist/sync/wasSyncPort.js +27 -169
  72. package/dist/sync/wasSyncPort.js.map +1 -1
  73. package/package.json +3 -3
@@ -13,12 +13,12 @@ export { syncedDocSchema } from './syncedDocSchema.js';
13
13
  export { createPullHandler, wireDocToRxDoc } from './changesQuery.js';
14
14
  export { createPushHandler } from './pushWrites.js';
15
15
  export { WasSyncConflictError, WasSyncAuthError } from './types.js';
16
- export { createWasSyncPort, errorMessage } from './wasSyncPort.js';
16
+ export { createWasSyncPort } from './wasSyncPort.js';
17
17
  export { withFeedMasterRead } from './feedMasterPort.js';
18
18
  export { createDocCipher, createPlaintextDocCodec, createUnprovisionedDocCipher, hasKeyEpochs, isUnknownEpochError } from './docCipher.js';
19
19
  export { makeLwwConflictHandler } from './lwwConflictHandler.js';
20
20
  export { lwwFields, remotePayloadWins } from './lww.js';
21
21
  // Owned by the client's sync subpath and re-exported verbatim, so the names this
22
22
  // package's consumers already import stay put.
23
- export { errorStatus, formatEtag, isEncryptedEnvelope } from '@interop/was-client/sync';
23
+ export { errorStatus, errorMessage, formatEtag, isEncryptedEnvelope } from '@interop/was-client/sync';
24
24
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sync/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;GAMG;AACH,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AACrE,OAAO,EAAE,iBAAiB,EAAqB,MAAM,iBAAiB,CAAA;AACtE,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EASjB,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,EACL,eAAe,EACf,uBAAuB,EACvB,4BAA4B,EAC5B,YAAY,EACZ,mBAAmB,EAEpB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAA;AAChE,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAkB,MAAM,UAAU,CAAA;AACvE,iFAAiF;AACjF,+CAA+C;AAC/C,OAAO,EACL,WAAW,EACX,UAAU,EACV,mBAAmB,EACpB,MAAM,0BAA0B,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sync/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;GAMG;AACH,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AACrE,OAAO,EAAE,iBAAiB,EAAqB,MAAM,iBAAiB,CAAA;AACtE,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EASjB,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,EACL,eAAe,EACf,uBAAuB,EACvB,4BAA4B,EAC5B,YAAY,EACZ,mBAAmB,EAEpB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAA;AAChE,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAkB,MAAM,UAAU,CAAA;AACvE,iFAAiF;AACjF,+CAA+C;AAC/C,OAAO,EACL,WAAW,EACX,YAAY,EACZ,UAAU,EACV,mBAAmB,EACpB,MAAM,0BAA0B,CAAA"}
@@ -9,7 +9,7 @@
9
9
  * `updatedAt` (an ISO-8601 string) decides, compared CHRONOLOGICALLY (parsed to
10
10
  * epoch ms, never lexically -- `updatedAt` is app-owned, so mixed precision like
11
11
  * `...05Z` vs `...05.400Z`, or a `+00:00` offset, must still order by instant);
12
- * `clientId` breaks an exact-instant tie deterministically. Both fields live in
12
+ * `writerId` breaks an exact-instant tie deterministically. Both fields live in
13
13
  * the payload (never the envelope-level checkpoint `updatedAt`).
14
14
  */
15
15
  /**
@@ -20,12 +20,12 @@
20
20
  */
21
21
  export interface LwwFields {
22
22
  updatedAt: string;
23
- clientId: string;
23
+ writerId: string;
24
24
  }
25
25
  /**
26
26
  * Whether the remote payload wins over the local one under last-write-wins.
27
27
  * The chronologically later `updatedAt` wins; on an exact-instant tie the
28
- * lexically greater `clientId` wins (an arbitrary but deterministic,
28
+ * lexically greater `writerId` wins (an arbitrary but deterministic,
29
29
  * replica-independent choice).
30
30
  *
31
31
  * Unparseable `updatedAt` values are rejected as losers: a side whose stamp
@@ -40,7 +40,7 @@ export interface LwwFields {
40
40
  export declare function remotePayloadWins(remote: LwwFields, local: LwwFields): boolean;
41
41
  /**
42
42
  * Reads the LWW fields off a doc when it carries them. Storage payloads are
43
- * generic over `{ id: string }`, so docs without `updatedAt`/`clientId` are
43
+ * generic over `{ id: string }`, so docs without `updatedAt`/`writerId` are
44
44
  * legal; callers fall back to their own rule for those.
45
45
  *
46
46
  * @param doc {unknown}
package/dist/sync/lww.js CHANGED
@@ -9,7 +9,7 @@
9
9
  * `updatedAt` (an ISO-8601 string) decides, compared CHRONOLOGICALLY (parsed to
10
10
  * epoch ms, never lexically -- `updatedAt` is app-owned, so mixed precision like
11
11
  * `...05Z` vs `...05.400Z`, or a `+00:00` offset, must still order by instant);
12
- * `clientId` breaks an exact-instant tie deterministically. Both fields live in
12
+ * `writerId` breaks an exact-instant tie deterministically. Both fields live in
13
13
  * the payload (never the envelope-level checkpoint `updatedAt`).
14
14
  */
15
15
  /**
@@ -22,7 +22,7 @@ function parseInstant(updatedAt) {
22
22
  /**
23
23
  * Whether the remote payload wins over the local one under last-write-wins.
24
24
  * The chronologically later `updatedAt` wins; on an exact-instant tie the
25
- * lexically greater `clientId` wins (an arbitrary but deterministic,
25
+ * lexically greater `writerId` wins (an arbitrary but deterministic,
26
26
  * replica-independent choice).
27
27
  *
28
28
  * Unparseable `updatedAt` values are rejected as losers: a side whose stamp
@@ -50,20 +50,20 @@ export function remotePayloadWins(remote, local) {
50
50
  // Neither parses: fall back to the deterministic lexical compare.
51
51
  return remote.updatedAt > local.updatedAt;
52
52
  }
53
- return remote.clientId > local.clientId;
53
+ return remote.writerId > local.writerId;
54
54
  }
55
55
  /**
56
56
  * Reads the LWW fields off a doc when it carries them. Storage payloads are
57
- * generic over `{ id: string }`, so docs without `updatedAt`/`clientId` are
57
+ * generic over `{ id: string }`, so docs without `updatedAt`/`writerId` are
58
58
  * legal; callers fall back to their own rule for those.
59
59
  *
60
60
  * @param doc {unknown}
61
61
  * @returns {LwwFields | null}
62
62
  */
63
63
  export function lwwFields(doc) {
64
- const { updatedAt, clientId } = doc;
65
- return typeof updatedAt === 'string' && typeof clientId === 'string'
66
- ? { updatedAt, clientId }
64
+ const { updatedAt, writerId } = doc;
65
+ return typeof updatedAt === 'string' && typeof writerId === 'string'
66
+ ? { updatedAt, writerId }
67
67
  : null;
68
68
  }
69
69
  //# sourceMappingURL=lww.js.map
@@ -7,17 +7,17 @@
7
7
  * correct for content-addressed (immutable-per-id) collections but wrong here:
8
8
  * every entity is a mutable head that two devices can edit concurrently, so a
9
9
  * genuine content conflict must be settled by last-write-wins on the payload's
10
- * own `updatedAt` (clientId tiebreak) -- exactly the rule two offline replicas
10
+ * own `updatedAt` (writerId tiebreak) -- exactly the rule two offline replicas
11
11
  * apply independently to converge.
12
12
  *
13
13
  * The wrinkle is that the conflicting bodies are EDV envelopes (ciphertext), so
14
14
  * `resolve` must decrypt both sides through this collection's cipher before it
15
- * can compare the plaintext `updatedAt` / `clientId`. `isEqual` stays cheap and
15
+ * can compare the plaintext `updatedAt` / `writerId`. `isEqual` stays cheap and
16
16
  * synchronous (a structural compare of the opaque bodies), as RxDB requires.
17
17
  *
18
18
  * Convergence: the server holds ONE winner of the push race as `realMasterState`;
19
19
  * every replica compares that same master against its own local edit, and the
20
- * `payloadWins` comparator is a total order over `(updatedAt, clientId)`, so the
20
+ * `payloadWins` comparator is a total order over `(updatedAt, writerId)`, so the
21
21
  * globally-latest payload wins on every replica with no coordination.
22
22
  *
23
23
  * The resolution rules, in order:
@@ -69,7 +69,7 @@ import { type LwwFields } from './lww.js';
69
69
  * @param [payloadWins] {(remote: LwwFields, local: LwwFields) => boolean}
70
70
  * the total-order comparator deciding whether the remote payload replaces the
71
71
  * local one; defaults to {@link remotePayloadWins} (later `updatedAt` wins,
72
- * `clientId` breaks a tie)
72
+ * `writerId` breaks a tie)
73
73
  * @returns {import('rxdb/plugins/core').RxConflictHandler<SyncedDoc>}
74
74
  */
75
75
  export declare function makeLwwConflictHandler(decrypt: (envelope: Json) => Promise<Json>, payloadWins?: (remote: LwwFields, local: LwwFields) => boolean): {
@@ -25,7 +25,7 @@ async function lwwFieldsOf(doc, decrypt) {
25
25
  * @param [payloadWins] {(remote: LwwFields, local: LwwFields) => boolean}
26
26
  * the total-order comparator deciding whether the remote payload replaces the
27
27
  * local one; defaults to {@link remotePayloadWins} (later `updatedAt` wins,
28
- * `clientId` breaks a tie)
28
+ * `writerId` breaks a tie)
29
29
  * @returns {import('rxdb/plugins/core').RxConflictHandler<SyncedDoc>}
30
30
  */
31
31
  export function makeLwwConflictHandler(decrypt, payloadWins = remotePayloadWins) {
@@ -25,6 +25,24 @@
25
25
  * copy every wire-to-local mapping runs.
26
26
  */
27
27
  import type { Json } from '@interop/was-client';
28
+ import type { MasterState as ClientMasterState, SyncCheckpoint as ClientSyncCheckpoint } from '@interop/was-client/sync';
29
+ /**
30
+ * The typed port signals, owned by `@interop/was-client` (its sync port raises
31
+ * them) and re-exported here so the names this package's consumers already
32
+ * import stay put -- and so an `instanceof` check anywhere in this package
33
+ * matches the very class the port throws.
34
+ *
35
+ * `WasSyncConflictError` marks a conditional write rejected with `412
36
+ * precondition-failed` (a lost-update conflict, or a create-if-absent whose
37
+ * target already exists); the push handler catches exactly it to trigger the
38
+ * re-read-and-report-conflict path. `WasSyncAuthError` marks a request refused
39
+ * on authorization grounds -- `401`, `403`, or the `404` a WAS server masks a
40
+ * failed capability invocation as, which on the sync paths (where the invoked
41
+ * collection is known to exist) means the invocation was rejected. It keeps the
42
+ * offending HTTP status on `status`, which the `/meta` push path reads to tell
43
+ * a masked denial from an ordinary delete race.
44
+ */
45
+ export { WasSyncAuthError, WasSyncConflictError } from '@interop/was-client/sync';
28
46
  /**
29
47
  * A JSON value -- the opaque stored resource body the sync layer moves verbatim.
30
48
  * For a plaintext collection this is the user document; for an encrypted one it
@@ -80,12 +98,10 @@ export declare function copyOptionalBodyFields({ source, target }: {
80
98
  * The keyset position in the change feed: the `{ id, updatedAt }` of the last
81
99
  * document a pull returned. Passed back verbatim to resume, and used as the
82
100
  * RxDB replication checkpoint. `id` is the total-order tiebreaker within a
83
- * single `updatedAt`.
101
+ * single `updatedAt`. The client's own checkpoint type, aliased here so the
102
+ * replication layer and the port agree by construction.
84
103
  */
85
- export interface SyncCheckpoint {
86
- id: string;
87
- updatedAt: string;
88
- }
104
+ export type SyncCheckpoint = ClientSyncCheckpoint;
89
105
  /**
90
106
  * One document as it travels on the `changes` feed wire
91
107
  * (`POST /space/:s/:c/query`, profile `changes`). `id` is the WAS resourceId,
@@ -136,20 +152,15 @@ export interface SyncedDoc {
136
152
  }
137
153
  /**
138
154
  * The current master state of a single resource, as read back for the 412
139
- * conflict path. `deleted` distinguishes a tombstone from a live resource;
140
- * `metaVersion` / `custom` are present only once metadata has been written.
155
+ * conflict path: the client's own `MasterState` (content `version` /
156
+ * `updatedAt`, plus the optional `metaVersion` / `data` / `custom` /
157
+ * `createdBy` / `epoch`) plus the `deleted` flag that distinguishes a tombstone
158
+ * from a live resource. The flag is required here because this package resolves
159
+ * the master from the changes feed, where a tombstone travels as a document
160
+ * rather than as an absent read.
141
161
  */
142
- export interface MasterState {
143
- version: number;
144
- updatedAt: string;
162
+ export interface MasterState extends ClientMasterState {
145
163
  deleted: boolean;
146
- metaVersion?: number;
147
- data?: Json;
148
- custom?: Json;
149
- /**
150
- * The key-epoch stamp of the master's content body, when the feed carries one.
151
- */
152
- epoch?: string;
153
164
  }
154
165
  /**
155
166
  * The short-lived master-read memo the rows of ONE push batch share. A
@@ -305,32 +316,4 @@ export interface WasSyncPort extends WasSyncBasePort {
305
316
  cache?: MasterReadCache;
306
317
  }): Promise<MasterState | null>;
307
318
  }
308
- /**
309
- * Thrown by a {@link WasSyncPort} implementation when a conditional write is
310
- * rejected with `412 precondition-failed` (a lost-update conflict, or a
311
- * create-if-absent whose target already exists). The core push handler catches
312
- * exactly this type to trigger the re-read-and-report-conflict path; any other
313
- * error propagates to RxDB for retry.
314
- */
315
- export declare class WasSyncConflictError extends Error {
316
- constructor(message?: string);
317
- }
318
- /**
319
- * Thrown by a {@link WasSyncPort} implementation when a request is rejected with
320
- * `401 unauthorized`, `403 forbidden`, or `404 not found` -- the
321
- * storage-access-expired/revoked signal. `404` qualifies because a WAS server
322
- * masks a failed capability invocation as `404` (never confirming to an
323
- * unauthorized caller whether the resource exists); on the sync paths the
324
- * invoked collection is known to exist, so a `404` means the invocation was
325
- * rejected. Mapping the raw HTTP status to a typed error at the port boundary
326
- * (the same way `412` becomes {@link WasSyncConflictError}) lets the sync
327
- * controller recognise expired access with an `instanceof` check rather than
328
- * re-extracting status codes from a wrapped RxDB error graph. The offending
329
- * HTTP status is kept on `status` -- diagnostics, plus the delete path's
330
- * already-absent-404 check.
331
- */
332
- export declare class WasSyncAuthError extends Error {
333
- readonly status: number;
334
- constructor(status: number);
335
- }
336
319
  //# sourceMappingURL=types.d.ts.map
@@ -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,3 +1,20 @@
1
+ /**
2
+ * The typed port signals, owned by `@interop/was-client` (its sync port raises
3
+ * them) and re-exported here so the names this package's consumers already
4
+ * import stay put -- and so an `instanceof` check anywhere in this package
5
+ * matches the very class the port throws.
6
+ *
7
+ * `WasSyncConflictError` marks a conditional write rejected with `412
8
+ * precondition-failed` (a lost-update conflict, or a create-if-absent whose
9
+ * target already exists); the push handler catches exactly it to trigger the
10
+ * re-read-and-report-conflict path. `WasSyncAuthError` marks a request refused
11
+ * on authorization grounds -- `401`, `403`, or the `404` a WAS server masks a
12
+ * failed capability invocation as, which on the sync paths (where the invoked
13
+ * collection is known to exist) means the invocation was rejected. It keeps the
14
+ * offending HTTP status on `status`, which the `/meta` push path reads to tell
15
+ * a masked denial from an ordinary delete race.
16
+ */
17
+ export { WasSyncAuthError, WasSyncConflictError } from '@interop/was-client/sync';
1
18
  /**
2
19
  * Structural equality over two opaque bodies, by canonical-free JSON string.
3
20
  * Decides whether the content or the metadata half changed -- which endpoint(s)
@@ -38,39 +55,4 @@ export function copyOptionalBodyFields({ source, target }) {
38
55
  target.epoch = source.epoch;
39
56
  }
40
57
  }
41
- /**
42
- * Thrown by a {@link WasSyncPort} implementation when a conditional write is
43
- * rejected with `412 precondition-failed` (a lost-update conflict, or a
44
- * create-if-absent whose target already exists). The core push handler catches
45
- * exactly this type to trigger the re-read-and-report-conflict path; any other
46
- * error propagates to RxDB for retry.
47
- */
48
- export class WasSyncConflictError extends Error {
49
- constructor(message = 'WAS conditional write precondition failed.') {
50
- super(message);
51
- this.name = 'WasSyncConflictError';
52
- }
53
- }
54
- /**
55
- * Thrown by a {@link WasSyncPort} implementation when a request is rejected with
56
- * `401 unauthorized`, `403 forbidden`, or `404 not found` -- the
57
- * storage-access-expired/revoked signal. `404` qualifies because a WAS server
58
- * masks a failed capability invocation as `404` (never confirming to an
59
- * unauthorized caller whether the resource exists); on the sync paths the
60
- * invoked collection is known to exist, so a `404` means the invocation was
61
- * rejected. Mapping the raw HTTP status to a typed error at the port boundary
62
- * (the same way `412` becomes {@link WasSyncConflictError}) lets the sync
63
- * controller recognise expired access with an `instanceof` check rather than
64
- * re-extracting status codes from a wrapped RxDB error graph. The offending
65
- * HTTP status is kept on `status` -- diagnostics, plus the delete path's
66
- * already-absent-404 check.
67
- */
68
- export class WasSyncAuthError extends Error {
69
- status;
70
- constructor(status) {
71
- super(`WAS storage access denied (HTTP ${status}).`);
72
- this.name = 'WasSyncAuthError';
73
- this.status = status;
74
- }
75
- }
76
58
  //# sourceMappingURL=types.js.map
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/sync/types.ts"],"names":[],"mappings":"AAgCA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACrB,MAAM,0BAA0B,CAAA;AA2BjC;;;;;;;;;;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"}
@@ -3,41 +3,24 @@
3
3
  */
4
4
  /**
5
5
  * The app-side seam between the generic sync adapter and `@interop/was-client`.
6
- * Implements the `WasSyncBasePort` interface (query + conditional writes) for one
7
- * remote WAS Collection using the raw, signed `was.request()` escape hatch. The
8
- * 412 conflict re-read (`get`) is supplied separately by `withFeedMasterRead`,
9
- * which resolves the master state from the changes-feed body -- origin-
10
- * independent, unlike a cross-origin `GET` ETag header.
6
+ * Delegates to the client's own `createWasSyncPort` -- which owns the WAS URL
7
+ * grammar, the verbatim (codec-bypassing) `was.request()` writes, the `changes`
8
+ * feed pull, the key-epoch header, and the conditional-write ETag handling --
9
+ * and pins the two options this package needs: the collection's delegated
10
+ * capability, and `mapAuthErrors` so a `401` / `403` / the server's masked `404`
11
+ * arrive as a `WasSyncAuthError` the sync controller can `instanceof`-match.
11
12
  *
12
- * Using `request()` (rather than the `Resource` / `Collection` handles) is
13
- * deliberate: it moves the stored body VERBATIM, bypassing the encryption codec.
14
- * The `changes` feed already ships opaque stored bodies (plaintext for a
15
- * plaintext collection, the EDV envelope for an encrypted one), and the push
16
- * side must write those same bytes back unchanged -- running them through
17
- * `resource.put()` would re-encrypt an already-encrypted envelope. Encrypt /
18
- * decrypt stays a read/write-time concern above this layer; this port is
19
- * collection-agnostic and never touches keys.
20
- *
21
- * Conditional writes ride the server's monotonic `version` (content) and
22
- * `metaVersion` (metadata) ETags, which the reference server enforces uniformly
23
- * for plaintext and encrypted resources alike -- so there is no plaintext-vs-
24
- * encrypted fork here.
13
+ * What stays here is the narrowing to a {@link WasSyncBasePort}: the 412
14
+ * conflict re-read (`get`) is supplied separately by `withFeedMasterRead`, which
15
+ * resolves the master state from the changes-feed BODY -- origin-independent,
16
+ * unlike a cross-origin `GET` whose ETag header CORS hides.
25
17
  */
26
- import { type WasClient } from '@interop/was-client';
18
+ import type { WasClient } from '@interop/was-client';
27
19
  import type { IZcap } from '@interop/data-integrity-core';
28
- import { type WasSyncBasePort } from './types.js';
29
- /**
30
- * Normalizes an unknown caught error into a display string: the `Error`'s
31
- * `message` when it is one, else its `String(...)` coercion. Shared by the
32
- * remote store's descriptor PUT and the session activation error path.
33
- *
34
- * @param err {unknown}
35
- * @returns {string}
36
- */
37
- export declare function errorMessage(err: unknown): string;
20
+ import type { WasSyncBasePort } from './types.js';
38
21
  /**
39
- * Builds a `WasSyncPort` bound to a single Space + Collection on the remote WAS
40
- * server, backed by the session's signed `WasClient`.
22
+ * Builds a `WasSyncBasePort` bound to a single Space + Collection on the remote
23
+ * WAS server, backed by the session's signed `WasClient`.
41
24
  *
42
25
  * @param options {object}
43
26
  * @param options.was {WasClient} the session client (holds the signer)
@@ -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;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"}
1
+ {"version":3,"file":"wasSyncPort.d.ts","sourceRoot":"","sources":["../../src/sync/wasSyncPort.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAEpD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,8BAA8B,CAAA;AACzD,OAAO,KAAK,EAAkB,eAAe,EAAW,MAAM,YAAY,CAAA;AAE1E;;;;;;;;;;;;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,CAqClB"}
@@ -1,75 +1,7 @@
1
- /*!
2
- * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
- */
4
- /**
5
- * The app-side seam between the generic sync adapter and `@interop/was-client`.
6
- * Implements the `WasSyncBasePort` interface (query + conditional writes) for one
7
- * remote WAS Collection using the raw, signed `was.request()` escape hatch. The
8
- * 412 conflict re-read (`get`) is supplied separately by `withFeedMasterRead`,
9
- * which resolves the master state from the changes-feed body -- origin-
10
- * independent, unlike a cross-origin `GET` ETag header.
11
- *
12
- * Using `request()` (rather than the `Resource` / `Collection` handles) is
13
- * deliberate: it moves the stored body VERBATIM, bypassing the encryption codec.
14
- * The `changes` feed already ships opaque stored bodies (plaintext for a
15
- * plaintext collection, the EDV envelope for an encrypted one), and the push
16
- * side must write those same bytes back unchanged -- running them through
17
- * `resource.put()` would re-encrypt an already-encrypted envelope. Encrypt /
18
- * decrypt stays a read/write-time concern above this layer; this port is
19
- * collection-agnostic and never touches keys.
20
- *
21
- * Conditional writes ride the server's monotonic `version` (content) and
22
- * `metaVersion` (metadata) ETags, which the reference server enforces uniformly
23
- * for plaintext and encrypted resources alike -- so there is no plaintext-vs-
24
- * encrypted fork here.
25
- */
26
- import { readEtag, writeHeaders } from '@interop/was-client';
27
- import { errorStatus, parseEtag } from '@interop/was-client/sync';
28
- import { collectionPath } from '../grants.js';
29
- import { WasSyncAuthError, WasSyncConflictError } from './types.js';
30
- /**
31
- * Normalizes an unknown caught error into a display string: the `Error`'s
32
- * `message` when it is one, else its `String(...)` coercion. Shared by the
33
- * remote store's descriptor PUT and the session activation error path.
34
- *
35
- * @param err {unknown}
36
- * @returns {string}
37
- */
38
- export function errorMessage(err) {
39
- return err instanceof Error ? err.message : String(err);
40
- }
41
- /**
42
- * Maps a raw `was.request()` rejection to the sync layer's typed errors: `412`
43
- * to {@link WasSyncConflictError} (a lost-update conflict) and `401` / `403` /
44
- * `404` to {@link WasSyncAuthError} (expired/revoked storage access). Every
45
- * other error is returned unchanged so RxDB's retry/backoff handles it. Returns
46
- * the error to throw (the caller re-throws) rather than throwing itself.
47
- *
48
- * `404` counts as access denied because a WAS server masks a failed capability
49
- * invocation as `404` ("URL not found or invalid authorization") rather than
50
- * `403`, so an unauthorized caller cannot probe which resources exist. On the
51
- * sync paths the invoked space and collection are known to exist (the session
52
- * synced them), so a `404` means the invocation itself was rejected -- the
53
- * revoked/expired-grant signal. The one route where `404` is routine --
54
- * deleting an already-absent resource -- inspects the preserved `status` on the
55
- * mapped error and stays a success (see `deleteContent`).
56
- *
57
- * @param err {unknown}
58
- * @returns {unknown}
59
- */
60
- function toPortError(err) {
61
- const status = errorStatus(err);
62
- if (status === 412) {
63
- return new WasSyncConflictError();
64
- }
65
- if (status === 401 || status === 403 || status === 404) {
66
- return new WasSyncAuthError(status);
67
- }
68
- return err;
69
- }
1
+ import { createWasSyncPort as createClientSyncPort } from '@interop/was-client/sync';
70
2
  /**
71
- * Builds a `WasSyncPort` bound to a single Space + Collection on the remote WAS
72
- * server, backed by the session's signed `WasClient`.
3
+ * Builds a `WasSyncBasePort` bound to a single Space + Collection on the remote
4
+ * WAS server, backed by the session's signed `WasClient`.
73
5
  *
74
6
  * @param options {object}
75
7
  * @param options.was {WasClient} the session client (holds the signer)
@@ -81,106 +13,32 @@ function toPortError(err) {
81
13
  * @returns {WasSyncBasePort}
82
14
  */
83
15
  export function createWasSyncPort({ was, spaceId, collectionId, capability }) {
84
- const basePath = collectionPath({ spaceId, collectionId });
85
- const resourcePath = (id) => `${basePath}/${encodeURIComponent(id)}`;
86
- /**
87
- * Runs a conditional write, mapping the server's `412 precondition-failed`
88
- * into the core's `WasSyncConflictError` and a `401` / `403` into
89
- * `WasSyncAuthError` (see {@link toPortError}), and letting all else propagate.
90
- * Returns the accepted write's new revision parsed from the response ETag
91
- * (`version` for content writes, `metaVersion` for `/meta` writes), or
92
- * `undefined` when the response carries no ETag.
93
- */
94
- const conditionalWrite = async (
95
- // Typed off readEtag's parameter (was-client's HttpResponse) so the type
96
- // needn't be imported from @interop/http-client, which is not a direct
97
- // dependency here.
98
- run) => {
99
- try {
100
- const response = await run();
101
- // `readEtag` reports an absent header as `undefined`; `parseEtag` reads
102
- // the `null` spelling of the same thing.
103
- return parseEtag(readEtag(response) ?? null);
104
- }
105
- catch (err) {
106
- throw toPortError(err);
107
- }
108
- };
16
+ const port = createClientSyncPort({
17
+ was,
18
+ spaceId,
19
+ collectionId,
20
+ ...(capability !== undefined && { capability }),
21
+ mapAuthErrors: true
22
+ });
23
+ // The client types `putMeta` as optional (a port may replicate content only);
24
+ // its own implementation always supplies it, and this package's replication
25
+ // always writes the `/meta` half, so require it at the boundary rather than
26
+ // guarding at every call site.
27
+ const { putMeta } = port;
28
+ if (putMeta === undefined) {
29
+ throw new Error('The WAS sync port does not implement `putMeta`, which this ' +
30
+ "package's replication requires.");
31
+ }
109
32
  return {
110
- async query({ checkpoint, limit }) {
111
- try {
112
- const response = await was.request({
113
- capability,
114
- path: `${basePath}/query`,
115
- method: 'POST',
116
- json: {
117
- profile: 'changes',
118
- ...(checkpoint !== undefined && { checkpoint }),
119
- limit
120
- }
121
- });
122
- return response.data;
123
- }
124
- catch (err) {
125
- // Map a pull-path auth rejection (401/403, or the masked 404) to
126
- // WasSyncAuthError too, so expired/revoked access is recognised
127
- // whether it surfaces on the pull or the push side.
128
- throw toPortError(err);
129
- }
130
- },
131
- async putContent({ id, data, ifMatch, ifNoneMatch, epoch }) {
132
- return conditionalWrite(() => was.request({
133
- capability,
134
- path: resourcePath(id),
135
- method: 'PUT',
136
- json: data,
137
- // `epoch` rides as the `Key-Epoch` header so the server's stamp
138
- // stays in step with the envelope (an absent epoch clears any prior
139
- // stamp, per the `key-epochs` feature).
140
- headers: writeHeaders({
141
- precondition: { ifMatch, ifNoneMatch },
142
- epoch
143
- })
144
- }));
145
- },
146
- async deleteContent({ id, ifMatch }) {
147
- try {
148
- return await conditionalWrite(() => was.request({
149
- capability,
150
- path: resourcePath(id),
151
- method: 'DELETE',
152
- headers: writeHeaders({ precondition: { ifMatch } })
153
- }));
154
- }
155
- catch (err) {
156
- // A 404 means the resource is already absent -- the row was created
157
- // and deleted locally before ever being pushed, or another device
158
- // deleted it first. Either way the tombstone's goal state holds, so
159
- // report success (no acked revision) instead of rejecting the batch,
160
- // which RxDB would otherwise retry forever. (A masked auth-failure 404
161
- // is swallowed here too -- indistinguishable by design -- but revoked
162
- // access still surfaces within one pull-poll interval, where 404 maps
163
- // to WasSyncAuthError.) The mapped error keeps the raw `status`, so
164
- // this check works on the WasSyncAuthError conditionalWrite now throws
165
- // for a 404.
166
- if (errorStatus(err) === 404) {
167
- return undefined;
168
- }
169
- throw err;
170
- }
33
+ async query(options) {
34
+ // The feed page's bodies are `unknown` upstream (a stored body is any
35
+ // JSON, and a future encryption scheme's envelope need not be an EDV
36
+ // document); this layer moves them verbatim as `Json`.
37
+ return (await port.query(options));
171
38
  },
172
- async putMeta({ id, custom, ifMatch, ifNoneMatch }) {
173
- return conditionalWrite(() => was.request({
174
- capability,
175
- path: `${resourcePath(id)}/meta`,
176
- method: 'PUT',
177
- // The `/meta` PUT is a full replacement of `custom`: a body with no
178
- // `custom` member writes the CLEARED state (the server clears every
179
- // property the body omits), which is how a metadata clear replicates.
180
- json: custom === undefined ? {} : { custom },
181
- headers: writeHeaders({ precondition: { ifMatch, ifNoneMatch } })
182
- }));
183
- }
39
+ putContent: options => port.putContent(options),
40
+ deleteContent: options => port.deleteContent(options),
41
+ putMeta: options => putMeta(options)
184
42
  };
185
43
  }
186
44
  //# sourceMappingURL=wasSyncPort.js.map
@@ -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":"AAkBA,OAAO,EAAE,iBAAiB,IAAI,oBAAoB,EAAE,MAAM,0BAA0B,CAAA;AAIpF;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,iBAAiB,CAAC,EAChC,GAAG,EACH,OAAO,EACP,YAAY,EACZ,UAAU,EAMX;IACC,MAAM,IAAI,GAAG,oBAAoB,CAAC;QAChC,GAAG;QACH,OAAO;QACP,YAAY;QACZ,GAAG,CAAC,UAAU,KAAK,SAAS,IAAI,EAAE,UAAU,EAAE,CAAC;QAC/C,aAAa,EAAE,IAAI;KACpB,CAAC,CAAA;IACF,8EAA8E;IAC9E,4EAA4E;IAC5E,4EAA4E;IAC5E,+BAA+B;IAC/B,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;IACxB,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CACb,6DAA6D;YAC3D,iCAAiC,CACpC,CAAA;IACH,CAAC;IAED,OAAO;QACL,KAAK,CAAC,KAAK,CAAC,OAAO;YACjB,sEAAsE;YACtE,qEAAqE;YACrE,uDAAuD;YACvD,OAAO,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAGhC,CAAA;QACH,CAAC;QAED,UAAU,EAAE,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;QAE/C,aAAa,EAAE,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC;QAErD,OAAO,EAAE,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC;KACrC,CAAA;AACH,CAAC"}