@interop/was-react 0.1.0 → 0.1.3
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.
- package/README.md +9 -1
- package/dist/auth/chapi.d.ts +3 -1
- package/dist/auth/chapi.d.ts.map +1 -1
- package/dist/auth/chapi.js +6 -2
- package/dist/auth/chapi.js.map +1 -1
- package/dist/auth/loginFlow.d.ts +30 -10
- package/dist/auth/loginFlow.d.ts.map +1 -1
- package/dist/auth/loginFlow.js +3 -1
- package/dist/auth/loginFlow.js.map +1 -1
- package/dist/auth/loginRequest.d.ts +9 -3
- package/dist/auth/loginRequest.d.ts.map +1 -1
- package/dist/auth/loginRequest.js +9 -3
- package/dist/auth/loginRequest.js.map +1 -1
- package/dist/auth/verifyResponse.d.ts +9 -3
- package/dist/auth/verifyResponse.d.ts.map +1 -1
- package/dist/auth/verifyResponse.js +6 -2
- package/dist/auth/verifyResponse.js.map +1 -1
- package/dist/config.d.ts +60 -20
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +12 -4
- package/dist/config.js.map +1 -1
- package/dist/dev/cli.d.ts.map +1 -1
- package/dist/dev/cli.js +4 -7
- package/dist/dev/cli.js.map +1 -1
- package/dist/dev/provisionDevGrants.d.ts +15 -5
- package/dist/dev/provisionDevGrants.d.ts.map +1 -1
- package/dist/dev/provisionDevGrants.js +2 -2
- package/dist/dev/provisionDevGrants.js.map +1 -1
- package/dist/grants.d.ts +7 -19
- package/dist/grants.d.ts.map +1 -1
- package/dist/grants.js +28 -7
- package/dist/grants.js.map +1 -1
- package/dist/identity/appSession.d.ts +9 -3
- package/dist/identity/appSession.d.ts.map +1 -1
- package/dist/identity/appSession.js +3 -1
- package/dist/identity/appSession.js.map +1 -1
- package/dist/identity/documentLoader.d.ts +3 -1
- package/dist/identity/documentLoader.d.ts.map +1 -1
- package/dist/identity/documentLoader.js +6 -2
- package/dist/identity/documentLoader.js.map +1 -1
- package/dist/identity/seedCredential.d.ts +9 -3
- package/dist/identity/seedCredential.d.ts.map +1 -1
- package/dist/identity/seedCredential.js +9 -15
- package/dist/identity/seedCredential.js.map +1 -1
- package/dist/identity/seedStore.d.ts +18 -6
- package/dist/identity/seedStore.d.ts.map +1 -1
- package/dist/identity/seedStore.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/react/WasSessionProvider.d.ts.map +1 -1
- package/dist/react/WasSessionProvider.js +10 -1
- package/dist/react/WasSessionProvider.js.map +1 -1
- package/dist/react/hooks.d.ts +10 -5
- package/dist/react/hooks.d.ts.map +1 -1
- package/dist/react/hooks.js +6 -30
- package/dist/react/hooks.js.map +1 -1
- package/dist/session/authStore.d.ts +29 -7
- package/dist/session/authStore.d.ts.map +1 -1
- package/dist/session/authStore.js +123 -63
- package/dist/session/authStore.js.map +1 -1
- package/dist/storage/entityStore.d.ts +32 -9
- package/dist/storage/entityStore.d.ts.map +1 -1
- package/dist/storage/entityStore.js +88 -48
- package/dist/storage/entityStore.js.map +1 -1
- package/dist/storage/localStore.d.ts +13 -0
- package/dist/storage/localStore.d.ts.map +1 -1
- package/dist/storage/localStore.js +17 -0
- package/dist/storage/localStore.js.map +1 -1
- package/dist/storage/rehydrate.d.ts.map +1 -1
- package/dist/storage/rehydrate.js +3 -1
- package/dist/storage/rehydrate.js.map +1 -1
- package/dist/storage/syncController.d.ts +7 -3
- package/dist/storage/syncController.d.ts.map +1 -1
- package/dist/storage/syncController.js +28 -15
- package/dist/storage/syncController.js.map +1 -1
- package/dist/storage/syncStatusStore.d.ts +26 -1
- package/dist/storage/syncStatusStore.d.ts.map +1 -1
- package/dist/storage/syncStatusStore.js +40 -0
- package/dist/storage/syncStatusStore.js.map +1 -1
- package/dist/storage/wasRemoteStore.d.ts +3 -1
- package/dist/storage/wasRemoteStore.d.ts.map +1 -1
- package/dist/storage/wasRemoteStore.js +3 -3
- package/dist/storage/wasRemoteStore.js.map +1 -1
- package/dist/sync/feedMasterPort.d.ts +21 -19
- package/dist/sync/feedMasterPort.d.ts.map +1 -1
- package/dist/sync/feedMasterPort.js +18 -5
- package/dist/sync/feedMasterPort.js.map +1 -1
- package/dist/sync/index.d.ts +3 -3
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/index.js +2 -2
- package/dist/sync/index.js.map +1 -1
- package/dist/sync/lwwConflictHandler.d.ts +35 -2
- package/dist/sync/lwwConflictHandler.d.ts.map +1 -1
- package/dist/sync/lwwConflictHandler.js +47 -9
- package/dist/sync/lwwConflictHandler.js.map +1 -1
- package/dist/sync/pushWrites.d.ts +27 -8
- package/dist/sync/pushWrites.d.ts.map +1 -1
- package/dist/sync/pushWrites.js +43 -15
- package/dist/sync/pushWrites.js.map +1 -1
- package/dist/sync/types.d.ts +49 -16
- package/dist/sync/types.d.ts.map +1 -1
- package/dist/sync/types.js +17 -0
- package/dist/sync/types.js.map +1 -1
- package/dist/sync/wasReplication.d.ts.map +1 -1
- package/dist/sync/wasReplication.js +38 -1
- package/dist/sync/wasReplication.js.map +1 -1
- package/dist/sync/wasSyncPort.d.ts +29 -6
- package/dist/sync/wasSyncPort.d.ts.map +1 -1
- package/dist/sync/wasSyncPort.js +105 -101
- package/dist/sync/wasSyncPort.js.map +1 -1
- package/package.json +7 -5
- package/dist/auth/verifyResponse.test.d.ts +0 -2
- package/dist/auth/verifyResponse.test.d.ts.map +0 -1
- package/dist/auth/verifyResponse.test.js +0 -282
- package/dist/auth/verifyResponse.test.js.map +0 -1
- package/dist/dev/cli.test.d.ts +0 -2
- package/dist/dev/cli.test.d.ts.map +0 -1
- package/dist/dev/cli.test.js +0 -39
- package/dist/dev/cli.test.js.map +0 -1
- package/dist/grants.test.d.ts +0 -2
- package/dist/grants.test.d.ts.map +0 -1
- package/dist/grants.test.js +0 -79
- package/dist/grants.test.js.map +0 -1
- package/dist/identity/seedCredential.test.d.ts +0 -2
- package/dist/identity/seedCredential.test.d.ts.map +0 -1
- package/dist/identity/seedCredential.test.js +0 -205
- package/dist/identity/seedCredential.test.js.map +0 -1
- package/dist/storage/rehydrate.test.d.ts +0 -2
- package/dist/storage/rehydrate.test.d.ts.map +0 -1
- package/dist/storage/rehydrate.test.js +0 -172
- package/dist/storage/rehydrate.test.js.map +0 -1
- package/dist/sync/changesQuery.test.d.ts +0 -2
- package/dist/sync/changesQuery.test.d.ts.map +0 -1
- package/dist/sync/changesQuery.test.js +0 -145
- package/dist/sync/changesQuery.test.js.map +0 -1
- package/dist/sync/lww.test.d.ts +0 -2
- package/dist/sync/lww.test.d.ts.map +0 -1
- package/dist/sync/lww.test.js +0 -28
- package/dist/sync/lww.test.js.map +0 -1
- package/dist/sync/lwwConflictHandler.test.d.ts +0 -2
- package/dist/sync/lwwConflictHandler.test.d.ts.map +0 -1
- package/dist/sync/lwwConflictHandler.test.js +0 -78
- package/dist/sync/lwwConflictHandler.test.js.map +0 -1
- package/dist/sync/pushWrites.test.d.ts +0 -2
- package/dist/sync/pushWrites.test.d.ts.map +0 -1
- package/dist/sync/pushWrites.test.js +0 -287
- package/dist/sync/pushWrites.test.js.map +0 -1
|
@@ -19,10 +19,42 @@
|
|
|
19
19
|
* every replica compares that same master against its own local edit, and the
|
|
20
20
|
* `payloadWins` comparator is a total order over `(updatedAt, deviceId)`, so the
|
|
21
21
|
* globally-latest payload wins on every replica with no coordination.
|
|
22
|
+
*
|
|
23
|
+
* The resolution rules, in order:
|
|
24
|
+
*
|
|
25
|
+
* 1. Version-only conflict: when the real master's whole content (`data` +
|
|
26
|
+
* `custom` + `_deleted`) still equals the assumed master's, the server holds
|
|
27
|
+
* nothing newer than what this replica last synced -- the 412 came from a
|
|
28
|
+
* stale `If-Match` (typically our own earlier write racing its feed echo).
|
|
29
|
+
* The local state (edit or tombstone) is re-asserted and re-pushed against
|
|
30
|
+
* the corrected version. Without this rule a local delete would be dropped by
|
|
31
|
+
* rule 5 and the entity would silently resurrect. `custom` MUST be part of
|
|
32
|
+
* this comparison, or a concurrent metadata-only edit committed on the server
|
|
33
|
+
* would be misclassified here and clobbered (rule 2 is what settles it).
|
|
34
|
+
* 2. Metadata conflict: `data` and `_deleted` are unchanged from the assumed
|
|
35
|
+
* master but `custom` differs -- a metadata-only edit committed on the server
|
|
36
|
+
* since this replica last synced (another device won the `/meta` race).
|
|
37
|
+
* Metadata carries no LWW timestamp of its own (the payload `updatedAt` lives
|
|
38
|
+
* in the encrypted `data`, which is equal on both sides here), so there is no
|
|
39
|
+
* payload to compare; the sound, replica-independent default is that the
|
|
40
|
+
* server-committed state wins -- the real master is adopted for `custom`.
|
|
41
|
+
* Without this rule the equal-`data` case would fall through to rule 3, where
|
|
42
|
+
* the two payloads compare equal and the tie keeps the local (stale) `custom`,
|
|
43
|
+
* silently clobbering the committed metadata.
|
|
44
|
+
* 3. Both sides carry an LWW payload: pure payload LWW via `payloadWins`.
|
|
45
|
+
* 4. A live local edit vs an incomparable remote (e.g. a remote tombstone):
|
|
46
|
+
* the edit wins and is re-pushed (resurrection).
|
|
47
|
+
* 5. Everything else -- a local tombstone vs a REAL remote content change, or
|
|
48
|
+
* both sides incomparable: the master wins. Together with rule 4 this makes
|
|
49
|
+
* the delete-vs-concurrent-edit rule "the edit wins" on every replica: a
|
|
50
|
+
* tombstone carries no LWW payload of its own, so a genuine racing edit
|
|
51
|
+
* deterministically survives, whichever write reached the server first.
|
|
22
52
|
*/
|
|
23
53
|
import type { WithDeleted } from 'rxdb/plugins/core';
|
|
24
54
|
import type { Json, SyncedDoc } from './types.js';
|
|
25
|
-
/**
|
|
55
|
+
/**
|
|
56
|
+
* The LWW fields read out of a decrypted entity payload.
|
|
57
|
+
*/
|
|
26
58
|
interface LwwPayload {
|
|
27
59
|
updatedAt: string;
|
|
28
60
|
deviceId: string;
|
|
@@ -40,9 +72,10 @@ interface LwwPayload {
|
|
|
40
72
|
*/
|
|
41
73
|
export declare function makeLwwConflictHandler(decrypt: (envelope: Json) => Promise<Json>, payloadWins?: (remote: LwwPayload, local: LwwPayload) => boolean): {
|
|
42
74
|
isEqual(a: WithDeleted<SyncedDoc>, b: WithDeleted<SyncedDoc>): boolean;
|
|
43
|
-
resolve({ realMasterState, newDocumentState }: {
|
|
75
|
+
resolve({ realMasterState, newDocumentState, assumedMasterState }: {
|
|
44
76
|
realMasterState: WithDeleted<SyncedDoc>;
|
|
45
77
|
newDocumentState: WithDeleted<SyncedDoc>;
|
|
78
|
+
assumedMasterState?: WithDeleted<SyncedDoc>;
|
|
46
79
|
}): Promise<WithDeleted<SyncedDoc>>;
|
|
47
80
|
};
|
|
48
81
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lwwConflictHandler.d.ts","sourceRoot":"","sources":["../../src/sync/lwwConflictHandler.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH
|
|
1
|
+
{"version":3,"file":"lwwConflictHandler.d.ts","sourceRoot":"","sources":["../../src/sync/lwwConflictHandler.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACpD,OAAO,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAGjD;;GAEG;AACH,UAAU,UAAU;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAmCD;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,CAAC,QAAQ,EAAE,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,EAC1C,WAAW,GAAE,CACX,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,UAAU,KACd,OAA2B;eAQnB,WAAW,CAAC,SAAS,CAAC,KAAK,WAAW,CAAC,SAAS,CAAC,GAAG,OAAO;uEAcnE;QACD,eAAe,EAAE,WAAW,CAAC,SAAS,CAAC,CAAA;QACvC,gBAAgB,EAAE,WAAW,CAAC,SAAS,CAAC,CAAA;QACxC,kBAAkB,CAAC,EAAE,WAAW,CAAC,SAAS,CAAC,CAAA;KAC5C,GAAG,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;EAoDtC"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { remotePayloadWins } from './lww.js';
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* Structural equality of two opaque bodies (used for the fast `isEqual`).
|
|
4
|
+
*/
|
|
3
5
|
function bodiesEqual(a, b) {
|
|
4
6
|
return JSON.stringify(a ?? null) === JSON.stringify(b ?? null);
|
|
5
7
|
}
|
|
@@ -37,27 +39,63 @@ async function lwwFieldsOf(doc, decrypt) {
|
|
|
37
39
|
*/
|
|
38
40
|
export function makeLwwConflictHandler(decrypt, payloadWins = remotePayloadWins) {
|
|
39
41
|
return {
|
|
40
|
-
// Non-async and fast, as RxDB requires
|
|
41
|
-
//
|
|
42
|
+
// Non-async and fast, as RxDB requires. The server revisions (`version` /
|
|
43
|
+
// `metaVersion`) participate deliberately: our own write's feed echo comes
|
|
44
|
+
// back byte-identical but one revision ahead, and it must NOT compare
|
|
45
|
+
// equal, or the higher version is never adopted and every later
|
|
46
|
+
// conditional write sends a stale `If-Match` (a guaranteed 412).
|
|
42
47
|
isEqual(a, b) {
|
|
43
|
-
return a._deleted === b._deleted &&
|
|
48
|
+
return (a._deleted === b._deleted &&
|
|
49
|
+
a.version === b.version &&
|
|
50
|
+
a.metaVersion === b.metaVersion &&
|
|
51
|
+
bodiesEqual(a.data, b.data) &&
|
|
52
|
+
bodiesEqual(a.custom, b.custom));
|
|
44
53
|
},
|
|
45
|
-
async resolve({ realMasterState, newDocumentState }) {
|
|
54
|
+
async resolve({ realMasterState, newDocumentState, assumedMasterState }) {
|
|
55
|
+
// Rule 1 -- version-only conflict: the master's whole content (`data` +
|
|
56
|
+
// `custom`) is exactly what this replica last synced (only the revision
|
|
57
|
+
// moved, e.g. our own write racing its feed echo), so nothing remote is
|
|
58
|
+
// actually newer. Re-assert the local state -- crucially including a local
|
|
59
|
+
// TOMBSTONE, which rule 5 would otherwise drop (the silent-resurrection
|
|
60
|
+
// bug). `custom` is part of this equality so a concurrent metadata-only
|
|
61
|
+
// edit is NOT misclassified here (rule 2 handles it).
|
|
62
|
+
if (assumedMasterState !== undefined &&
|
|
63
|
+
realMasterState._deleted === assumedMasterState._deleted &&
|
|
64
|
+
bodiesEqual(realMasterState.data, assumedMasterState.data) &&
|
|
65
|
+
bodiesEqual(realMasterState.custom, assumedMasterState.custom)) {
|
|
66
|
+
return newDocumentState;
|
|
67
|
+
}
|
|
68
|
+
// Rule 2 -- metadata conflict: `data` and `_deleted` are unchanged from
|
|
69
|
+
// the assumed master, but `custom` moved on the server (a metadata-only
|
|
70
|
+
// edit that won the `/meta` race on another device). Metadata has no LWW
|
|
71
|
+
// timestamp of its own -- the payload `updatedAt` lives in `data`, equal
|
|
72
|
+
// on both sides here -- so the sound, replica-independent default is that
|
|
73
|
+
// the server-committed state wins: adopt the real master for `custom`.
|
|
74
|
+
// Without this rule the equal-`data` case would reach rule 3 with two
|
|
75
|
+
// equal payloads and the tie would keep the local (stale) metadata.
|
|
76
|
+
if (assumedMasterState !== undefined &&
|
|
77
|
+
realMasterState._deleted === assumedMasterState._deleted &&
|
|
78
|
+
bodiesEqual(realMasterState.data, assumedMasterState.data) &&
|
|
79
|
+
!bodiesEqual(realMasterState.custom, assumedMasterState.custom)) {
|
|
80
|
+
return realMasterState;
|
|
81
|
+
}
|
|
46
82
|
const [remote, local] = await Promise.all([
|
|
47
83
|
lwwFieldsOf(realMasterState, decrypt),
|
|
48
84
|
lwwFieldsOf(newDocumentState, decrypt)
|
|
49
85
|
]);
|
|
50
|
-
//
|
|
86
|
+
// Rule 3 -- both sides comparable: pure payload LWW.
|
|
51
87
|
if (remote && local) {
|
|
52
88
|
return payloadWins(remote, local) ? realMasterState : newDocumentState;
|
|
53
89
|
}
|
|
54
|
-
//
|
|
90
|
+
// Rule 4 -- a live local edit against a remote tombstone (or otherwise
|
|
55
91
|
// incomparable remote): keep the edit (resurrect / re-assert the write).
|
|
56
92
|
if (local && !remote) {
|
|
57
93
|
return newDocumentState;
|
|
58
94
|
}
|
|
59
|
-
//
|
|
60
|
-
//
|
|
95
|
+
// Rule 5 -- everything else (a local tombstone racing a REAL remote
|
|
96
|
+
// content change, or both incomparable): the master wins, which is
|
|
97
|
+
// deterministic and convergent across replicas (the edit survives the
|
|
98
|
+
// delete on every device).
|
|
61
99
|
return realMasterState;
|
|
62
100
|
}
|
|
63
101
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lwwConflictHandler.js","sourceRoot":"","sources":["../../src/sync/lwwConflictHandler.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"lwwConflictHandler.js","sourceRoot":"","sources":["../../src/sync/lwwConflictHandler.ts"],"names":[],"mappings":"AAsDA,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AAU5C;;GAEG;AACH,SAAS,WAAW,CAAC,CAAmB,EAAE,CAAmB;IAC3D,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,IAAI,CAAC,CAAA;AAChE,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,WAAW,CACxB,GAA2B,EAC3B,OAA0C;IAE1C,IAAI,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC3C,OAAO,IAAI,CAAA;IACb,CAAC;IACD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,CAAC,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAwB,CAAA;QAChE,IACE,OAAO,OAAO,CAAC,SAAS,KAAK,QAAQ;YACrC,OAAO,OAAO,CAAC,QAAQ,KAAK,QAAQ,EACpC,CAAC;YACD,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAA;QACrE,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,sBAAsB,CACpC,OAA0C,EAC1C,cAGe,iBAAiB;IAEhC,OAAO;QACL,0EAA0E;QAC1E,2EAA2E;QAC3E,sEAAsE;QACtE,gEAAgE;QAChE,iEAAiE;QACjE,OAAO,CAAC,CAAyB,EAAE,CAAyB;YAC1D,OAAO,CACL,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,QAAQ;gBACzB,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO;gBACvB,CAAC,CAAC,WAAW,KAAK,CAAC,CAAC,WAAW;gBAC/B,WAAW,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;gBAC3B,WAAW,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAChC,CAAA;QACH,CAAC;QAED,KAAK,CAAC,OAAO,CAAC,EACZ,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAKnB;YACC,wEAAwE;YACxE,wEAAwE;YACxE,wEAAwE;YACxE,2EAA2E;YAC3E,wEAAwE;YACxE,wEAAwE;YACxE,sDAAsD;YACtD,IACE,kBAAkB,KAAK,SAAS;gBAChC,eAAe,CAAC,QAAQ,KAAK,kBAAkB,CAAC,QAAQ;gBACxD,WAAW,CAAC,eAAe,CAAC,IAAI,EAAE,kBAAkB,CAAC,IAAI,CAAC;gBAC1D,WAAW,CAAC,eAAe,CAAC,MAAM,EAAE,kBAAkB,CAAC,MAAM,CAAC,EAC9D,CAAC;gBACD,OAAO,gBAAgB,CAAA;YACzB,CAAC;YACD,wEAAwE;YACxE,wEAAwE;YACxE,yEAAyE;YACzE,yEAAyE;YACzE,0EAA0E;YAC1E,uEAAuE;YACvE,sEAAsE;YACtE,oEAAoE;YACpE,IACE,kBAAkB,KAAK,SAAS;gBAChC,eAAe,CAAC,QAAQ,KAAK,kBAAkB,CAAC,QAAQ;gBACxD,WAAW,CAAC,eAAe,CAAC,IAAI,EAAE,kBAAkB,CAAC,IAAI,CAAC;gBAC1D,CAAC,WAAW,CAAC,eAAe,CAAC,MAAM,EAAE,kBAAkB,CAAC,MAAM,CAAC,EAC/D,CAAC;gBACD,OAAO,eAAe,CAAA;YACxB,CAAC;YACD,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;gBACxC,WAAW,CAAC,eAAe,EAAE,OAAO,CAAC;gBACrC,WAAW,CAAC,gBAAgB,EAAE,OAAO,CAAC;aACvC,CAAC,CAAA;YACF,qDAAqD;YACrD,IAAI,MAAM,IAAI,KAAK,EAAE,CAAC;gBACpB,OAAO,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,gBAAgB,CAAA;YACxE,CAAC;YACD,uEAAuE;YACvE,yEAAyE;YACzE,IAAI,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC;gBACrB,OAAO,gBAAgB,CAAA;YACzB,CAAC;YACD,oEAAoE;YACpE,mEAAmE;YACnE,sEAAsE;YACtE,2BAA2B;YAC3B,OAAO,eAAe,CAAA;QACxB,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -20,16 +20,29 @@
|
|
|
20
20
|
* `/meta` write to a resource that does not yet exist.
|
|
21
21
|
*
|
|
22
22
|
* RxDB's push contract asks only for *conflicts* back (the current master state
|
|
23
|
-
* of each rejected row)
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
23
|
+
* of each rejected row), so a successful write's new `version` / `metaVersion`
|
|
24
|
+
* is reported out-of-band: the response ETag of each accepted write is captured
|
|
25
|
+
* and handed to the optional `onWriteAccepted` callback, which writes the acked
|
|
26
|
+
* revision back into the local row (see `createWasReplication`). Without that
|
|
27
|
+
* write-back the local `version` would stay one revision behind the server and
|
|
28
|
+
* every subsequent conditional write would send a stale `If-Match` and 412. The
|
|
29
|
+
* write-back only touches the revision fields (never `data` / `updatedAt`), so
|
|
30
|
+
* the follow-up push cycle it triggers finds nothing changed to write and
|
|
31
|
+
* settles -- no re-push loop.
|
|
30
32
|
*/
|
|
31
33
|
import type { WithDeleted } from 'rxdb/plugins/core';
|
|
32
34
|
import type { SyncedDoc, WasSyncPort } from './types.js';
|
|
35
|
+
/**
|
|
36
|
+
* The acked server revisions of one row's accepted writes: the new content
|
|
37
|
+
* `version` (from a `PUT /:id` or `DELETE /:id` response ETag) and/or the new
|
|
38
|
+
* `metaVersion` (from a `PUT /:id/meta` response ETag). Absent fields mean the
|
|
39
|
+
* corresponding write did not run or its response carried no ETag.
|
|
40
|
+
*/
|
|
41
|
+
export interface PushWriteAck {
|
|
42
|
+
id: string;
|
|
43
|
+
version?: number;
|
|
44
|
+
metaVersion?: number;
|
|
45
|
+
}
|
|
33
46
|
/**
|
|
34
47
|
* Formats a master revision (`version` or `metaVersion`) as the quoted strong
|
|
35
48
|
* ETag the server compares `If-Match` against (revision `3` becomes `"3"`).
|
|
@@ -45,12 +58,18 @@ export declare function formatEtag(revision: number): string;
|
|
|
45
58
|
* Rows are pushed concurrently; if any non-conflict error is thrown the whole
|
|
46
59
|
* batch rejects (RxDB re-sends it later), matching RxDB's all-or-nothing retry.
|
|
47
60
|
*
|
|
61
|
+
* Each accepted write's acked server revision(s) are handed to
|
|
62
|
+
* `onWriteAccepted` (when supplied) as soon as that row's writes settle, so the
|
|
63
|
+
* caller can write the new `version` / `metaVersion` back into the local row
|
|
64
|
+
* and keep subsequent conditional writes' `If-Match` in step with the server.
|
|
65
|
+
*
|
|
48
66
|
* @param port {WasSyncPort}
|
|
67
|
+
* @param [onWriteAccepted] {(ack: PushWriteAck) => Promise<void>}
|
|
49
68
|
* @returns {(rows: Array<{ newDocumentState: WithDeleted<SyncedDoc>,
|
|
50
69
|
* assumedMasterState?: WithDeleted<SyncedDoc> }>) =>
|
|
51
70
|
* Promise<WithDeleted<SyncedDoc>[]>}
|
|
52
71
|
*/
|
|
53
|
-
export declare function createPushHandler(port: WasSyncPort): (rows: Array<{
|
|
72
|
+
export declare function createPushHandler(port: WasSyncPort, onWriteAccepted?: (ack: PushWriteAck) => Promise<void>): (rows: Array<{
|
|
54
73
|
newDocumentState: WithDeleted<SyncedDoc>;
|
|
55
74
|
assumedMasterState?: WithDeleted<SyncedDoc>;
|
|
56
75
|
}>) => Promise<WithDeleted<SyncedDoc>[]>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pushWrites.d.ts","sourceRoot":"","sources":["../../src/sync/pushWrites.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH
|
|
1
|
+
{"version":3,"file":"pushWrites.d.ts","sourceRoot":"","sources":["../../src/sync/pushWrites.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACpD,OAAO,KAAK,EAAqB,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAG3E;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEnD;AA4KD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,WAAW,EACjB,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,IAGpD,MAAM,KAAK,CAAC;IACV,gBAAgB,EAAE,WAAW,CAAC,SAAS,CAAC,CAAA;IACxC,kBAAkB,CAAC,EAAE,WAAW,CAAC,SAAS,CAAC,CAAA;CAC5C,CAAC,KACD,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,EAAE,CAAC,CAoBrC"}
|
package/dist/sync/pushWrites.js
CHANGED
|
@@ -67,35 +67,41 @@ async function assembleConflict({ port, id, fallbackUpdatedAt, fallbackVersion }
|
|
|
67
67
|
/**
|
|
68
68
|
* Sends one local change to the remote Collection as up to two conditional
|
|
69
69
|
* writes (content, then metadata). Returns the master-state conflict entry on a
|
|
70
|
-
* `412` at either step
|
|
70
|
+
* `412` at either step (with `ack: null`), or the accepted writes' acked
|
|
71
|
+
* revisions on success (`ack: null` when no response carried a revision).
|
|
71
72
|
*
|
|
72
73
|
* @param options {object}
|
|
73
74
|
* @param options.port {WasSyncPort}
|
|
74
75
|
* @param options.newDocumentState {WithDeleted<SyncedDoc>}
|
|
75
76
|
* @param [options.assumedMasterState] {WithDeleted<SyncedDoc>}
|
|
76
|
-
* @returns {Promise<WithDeleted<SyncedDoc> | null
|
|
77
|
+
* @returns {Promise<{ conflict: WithDeleted<SyncedDoc> | null,
|
|
78
|
+
* ack: PushWriteAck | null }>}
|
|
77
79
|
*/
|
|
78
80
|
async function pushRow({ port, newDocumentState, assumedMasterState }) {
|
|
79
81
|
const { id } = newDocumentState;
|
|
80
82
|
const assumedVersion = assumedMasterState?.version;
|
|
81
83
|
const isCreate = assumedMasterState === undefined;
|
|
82
84
|
try {
|
|
85
|
+
const ack = { id };
|
|
83
86
|
if (newDocumentState._deleted) {
|
|
84
87
|
// Delete supersedes any metadata write: drop the content, tombstone wins.
|
|
85
|
-
await port.deleteContent({
|
|
88
|
+
const ackedVersion = await port.deleteContent({
|
|
86
89
|
id,
|
|
87
90
|
...(assumedVersion !== undefined && {
|
|
88
91
|
ifMatch: formatEtag(assumedVersion)
|
|
89
92
|
})
|
|
90
93
|
});
|
|
91
|
-
|
|
94
|
+
if (ackedVersion !== undefined) {
|
|
95
|
+
ack.version = ackedVersion;
|
|
96
|
+
}
|
|
97
|
+
return { conflict: null, ack: ack.version !== undefined ? ack : null };
|
|
92
98
|
}
|
|
93
99
|
// Content half: write on create, or when the content body changed. For a
|
|
94
100
|
// content-addressed collection the update case never fires (an immutable
|
|
95
101
|
// body for a stable id), but it is handled for generality.
|
|
96
102
|
const contentChanged = isCreate || !bodiesEqual(newDocumentState.data, assumedMasterState?.data);
|
|
97
103
|
if (contentChanged) {
|
|
98
|
-
await port.putContent({
|
|
104
|
+
const ackedVersion = await port.putContent({
|
|
99
105
|
id,
|
|
100
106
|
data: newDocumentState.data ?? null,
|
|
101
107
|
...(isCreate
|
|
@@ -104,30 +110,38 @@ async function pushRow({ port, newDocumentState, assumedMasterState }) {
|
|
|
104
110
|
ifMatch: formatEtag(assumedVersion)
|
|
105
111
|
})
|
|
106
112
|
});
|
|
113
|
+
if (ackedVersion !== undefined) {
|
|
114
|
+
ack.version = ackedVersion;
|
|
115
|
+
}
|
|
107
116
|
}
|
|
108
117
|
// Metadata half: write when the resource has metadata and it changed. On a
|
|
109
118
|
// create this runs after the content write (the resource must exist first).
|
|
110
119
|
const metadataChanged = !bodiesEqual(newDocumentState.custom, assumedMasterState?.custom);
|
|
111
120
|
if (newDocumentState.custom !== undefined && metadataChanged) {
|
|
112
121
|
const assumedMetaVersion = assumedMasterState?.metaVersion;
|
|
113
|
-
await port.putMeta({
|
|
122
|
+
const ackedMetaVersion = await port.putMeta({
|
|
114
123
|
id,
|
|
115
124
|
custom: newDocumentState.custom,
|
|
116
125
|
...(assumedMetaVersion !== undefined
|
|
117
126
|
? { ifMatch: formatEtag(assumedMetaVersion) }
|
|
118
127
|
: { ifNoneMatch: true })
|
|
119
128
|
});
|
|
129
|
+
if (ackedMetaVersion !== undefined) {
|
|
130
|
+
ack.metaVersion = ackedMetaVersion;
|
|
131
|
+
}
|
|
120
132
|
}
|
|
121
|
-
|
|
133
|
+
const hasAck = ack.version !== undefined || ack.metaVersion !== undefined;
|
|
134
|
+
return { conflict: null, ack: hasAck ? ack : null };
|
|
122
135
|
}
|
|
123
136
|
catch (err) {
|
|
124
137
|
if (err instanceof WasSyncConflictError) {
|
|
125
|
-
|
|
138
|
+
const conflict = await assembleConflict({
|
|
126
139
|
port,
|
|
127
140
|
id,
|
|
128
141
|
fallbackUpdatedAt: newDocumentState.updatedAt,
|
|
129
142
|
fallbackVersion: assumedVersion ?? newDocumentState.version
|
|
130
143
|
});
|
|
144
|
+
return { conflict, ack: null };
|
|
131
145
|
}
|
|
132
146
|
// Any non-conflict error (network, 5xx, auth) propagates so RxDB retries
|
|
133
147
|
// the whole batch with backoff.
|
|
@@ -141,19 +155,33 @@ async function pushRow({ port, newDocumentState, assumedMasterState }) {
|
|
|
141
155
|
* Rows are pushed concurrently; if any non-conflict error is thrown the whole
|
|
142
156
|
* batch rejects (RxDB re-sends it later), matching RxDB's all-or-nothing retry.
|
|
143
157
|
*
|
|
158
|
+
* Each accepted write's acked server revision(s) are handed to
|
|
159
|
+
* `onWriteAccepted` (when supplied) as soon as that row's writes settle, so the
|
|
160
|
+
* caller can write the new `version` / `metaVersion` back into the local row
|
|
161
|
+
* and keep subsequent conditional writes' `If-Match` in step with the server.
|
|
162
|
+
*
|
|
144
163
|
* @param port {WasSyncPort}
|
|
164
|
+
* @param [onWriteAccepted] {(ack: PushWriteAck) => Promise<void>}
|
|
145
165
|
* @returns {(rows: Array<{ newDocumentState: WithDeleted<SyncedDoc>,
|
|
146
166
|
* assumedMasterState?: WithDeleted<SyncedDoc> }>) =>
|
|
147
167
|
* Promise<WithDeleted<SyncedDoc>[]>}
|
|
148
168
|
*/
|
|
149
|
-
export function createPushHandler(port) {
|
|
169
|
+
export function createPushHandler(port, onWriteAccepted) {
|
|
150
170
|
return async function push(rows) {
|
|
151
|
-
const results = await Promise.all(rows.map(row =>
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
171
|
+
const results = await Promise.all(rows.map(async (row) => {
|
|
172
|
+
const result = await pushRow({
|
|
173
|
+
port,
|
|
174
|
+
newDocumentState: row.newDocumentState,
|
|
175
|
+
assumedMasterState: row.assumedMasterState
|
|
176
|
+
});
|
|
177
|
+
if (result.ack !== null && onWriteAccepted !== undefined) {
|
|
178
|
+
await onWriteAccepted(result.ack);
|
|
179
|
+
}
|
|
180
|
+
return result;
|
|
181
|
+
}));
|
|
182
|
+
return results
|
|
183
|
+
.map(result => result.conflict)
|
|
184
|
+
.filter((conflict) => conflict !== null);
|
|
157
185
|
};
|
|
158
186
|
}
|
|
159
187
|
//# sourceMappingURL=pushWrites.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pushWrites.js","sourceRoot":"","sources":["../../src/sync/pushWrites.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"pushWrites.js","sourceRoot":"","sources":["../../src/sync/pushWrites.ts"],"names":[],"mappings":"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"}
|
package/dist/sync/types.d.ts
CHANGED
|
@@ -87,18 +87,23 @@ export interface MasterState {
|
|
|
87
87
|
custom?: Json;
|
|
88
88
|
}
|
|
89
89
|
/**
|
|
90
|
-
* The injected WAS-access seam. An adapter (the app-side
|
|
91
|
-
* implements this over `@interop/was-client`; the core
|
|
92
|
-
* this interface, never on `was-client` itself. Every
|
|
93
|
-
* body verbatim -- no codec, no key handling -- so the
|
|
94
|
-
* plaintext and encrypted collections alike.
|
|
90
|
+
* The write/query half of the injected WAS-access seam. An adapter (the app-side
|
|
91
|
+
* `createWasSyncPort`) implements this over `@interop/was-client`; the core
|
|
92
|
+
* module depends only on this interface, never on `was-client` itself. Every
|
|
93
|
+
* method moves the stored body verbatim -- no codec, no key handling -- so the
|
|
94
|
+
* same port works for plaintext and encrypted collections alike.
|
|
95
95
|
*
|
|
96
96
|
* `putContent` / `deleteContent` / `putMeta` MUST throw
|
|
97
97
|
* {@link WasSyncConflictError} when the server rejects a conditional write with
|
|
98
98
|
* `412 precondition-failed`, and let every other error propagate so RxDB's
|
|
99
99
|
* retry/backoff handles it.
|
|
100
|
+
*
|
|
101
|
+
* The 412 conflict re-read (`get`) is deliberately NOT part of this base:
|
|
102
|
+
* `createWasSyncPort` returns a `WasSyncBasePort`, and `withFeedMasterRead`
|
|
103
|
+
* supplies a `get` that resolves the master state from the changes-feed body
|
|
104
|
+
* (origin-independent) to produce a full {@link WasSyncPort}.
|
|
100
105
|
*/
|
|
101
|
-
export interface
|
|
106
|
+
export interface WasSyncBasePort {
|
|
102
107
|
/**
|
|
103
108
|
* Pulls one page of the `changes` feed. Omit `checkpoint` for the first page.
|
|
104
109
|
* Returns the page's `documents` and its resume `checkpoint`, or
|
|
@@ -119,59 +124,74 @@ export interface WasSyncPort {
|
|
|
119
124
|
/**
|
|
120
125
|
* Conditionally writes the content body verbatim (`PUT /:id`). Pass
|
|
121
126
|
* `ifNoneMatch: true` for a create-if-absent, or `ifMatch` (a quoted ETag over
|
|
122
|
-
* the content `version`) for an update-if-unchanged.
|
|
127
|
+
* the content `version`) for an update-if-unchanged. Returns the new content
|
|
128
|
+
* `version` parsed from the response ETag (the acked master revision), or
|
|
129
|
+
* `undefined` when the server does not supply one.
|
|
123
130
|
*
|
|
124
131
|
* @param options {object}
|
|
125
132
|
* @param options.id {string}
|
|
126
133
|
* @param options.data {Json}
|
|
127
134
|
* @param [options.ifMatch] {string}
|
|
128
135
|
* @param [options.ifNoneMatch] {boolean}
|
|
129
|
-
* @returns {Promise<
|
|
136
|
+
* @returns {Promise<number | undefined>}
|
|
130
137
|
*/
|
|
131
138
|
putContent(options: {
|
|
132
139
|
id: string;
|
|
133
140
|
data: Json;
|
|
134
141
|
ifMatch?: string;
|
|
135
142
|
ifNoneMatch?: boolean;
|
|
136
|
-
}): Promise<
|
|
143
|
+
}): Promise<number | undefined>;
|
|
137
144
|
/**
|
|
138
145
|
* Conditionally deletes a resource (writes a tombstone; `DELETE /:id`). Pass
|
|
139
146
|
* `ifMatch` (a quoted ETag over the content `version`) to delete only if
|
|
140
|
-
* unchanged.
|
|
147
|
+
* unchanged. Returns the tombstone `version` parsed from the response ETag
|
|
148
|
+
* when the server supplies one (the reference server does not).
|
|
141
149
|
*
|
|
142
150
|
* @param options {object}
|
|
143
151
|
* @param options.id {string}
|
|
144
152
|
* @param [options.ifMatch] {string}
|
|
145
|
-
* @returns {Promise<
|
|
153
|
+
* @returns {Promise<number | undefined>}
|
|
146
154
|
*/
|
|
147
155
|
deleteContent(options: {
|
|
148
156
|
id: string;
|
|
149
157
|
ifMatch?: string;
|
|
150
|
-
}): Promise<
|
|
158
|
+
}): Promise<number | undefined>;
|
|
151
159
|
/**
|
|
152
160
|
* Conditionally writes the metadata body verbatim (`PUT /:id/meta`, body
|
|
153
161
|
* `{ custom }`). Pass `ifNoneMatch: true` when the resource has no metadata
|
|
154
162
|
* yet, or `ifMatch` (a quoted ETag over `metaVersion`) for an
|
|
155
163
|
* update-if-unchanged. The resource must already exist (the server does not
|
|
156
|
-
* create a resource from a `/meta` write).
|
|
164
|
+
* create a resource from a `/meta` write). Returns the new `metaVersion`
|
|
165
|
+
* parsed from the response ETag, or `undefined` when the server does not
|
|
166
|
+
* supply one.
|
|
157
167
|
*
|
|
158
168
|
* @param options {object}
|
|
159
169
|
* @param options.id {string}
|
|
160
170
|
* @param options.custom {Json}
|
|
161
171
|
* @param [options.ifMatch] {string}
|
|
162
172
|
* @param [options.ifNoneMatch] {boolean}
|
|
163
|
-
* @returns {Promise<
|
|
173
|
+
* @returns {Promise<number | undefined>}
|
|
164
174
|
*/
|
|
165
175
|
putMeta(options: {
|
|
166
176
|
id: string;
|
|
167
177
|
custom: Json;
|
|
168
178
|
ifMatch?: string;
|
|
169
179
|
ifNoneMatch?: boolean;
|
|
170
|
-
}): Promise<
|
|
180
|
+
}): Promise<number | undefined>;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* The full sync port the core consumes: a {@link WasSyncBasePort}'s write/query
|
|
184
|
+
* methods plus the `get` used by the 412 conflict assembler. The push handler
|
|
185
|
+
* and `createWasReplication` require this full port; build one by wrapping a
|
|
186
|
+
* base port with `withFeedMasterRead`, which supplies `get`.
|
|
187
|
+
*/
|
|
188
|
+
export interface WasSyncPort extends WasSyncBasePort {
|
|
171
189
|
/**
|
|
172
190
|
* Re-reads a single resource's current master state (content + metadata) for
|
|
173
191
|
* the 412 conflict assembler. Returns `null` when the resource is genuinely
|
|
174
|
-
* absent (a delete/delete race)
|
|
192
|
+
* absent (a delete/delete race); throws a retryable error when the master
|
|
193
|
+
* cannot be resolved (e.g. a feed re-read that exhausts its scan budget), so
|
|
194
|
+
* the replication cycle retries rather than fabricating a false tombstone.
|
|
175
195
|
*
|
|
176
196
|
* @param options {object}
|
|
177
197
|
* @param options.id {string}
|
|
@@ -191,4 +211,17 @@ export interface WasSyncPort {
|
|
|
191
211
|
export declare class WasSyncConflictError extends Error {
|
|
192
212
|
constructor(message?: string);
|
|
193
213
|
}
|
|
214
|
+
/**
|
|
215
|
+
* Thrown by a {@link WasSyncPort} implementation when a request is rejected with
|
|
216
|
+
* `401 unauthorized` or `403 forbidden` -- the storage-access-expired/revoked
|
|
217
|
+
* signal. Mapping the raw HTTP status to a typed error at the port boundary (the
|
|
218
|
+
* same way `412` becomes {@link WasSyncConflictError}) lets the sync controller
|
|
219
|
+
* recognise expired access with an `instanceof` check rather than re-extracting
|
|
220
|
+
* status codes from a wrapped RxDB error graph. The offending HTTP status is
|
|
221
|
+
* kept on `status` for diagnostics.
|
|
222
|
+
*/
|
|
223
|
+
export declare class WasSyncAuthError extends Error {
|
|
224
|
+
readonly status: number;
|
|
225
|
+
constructor(status: number);
|
|
226
|
+
}
|
|
194
227
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/sync/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/sync/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;GAkBG;AAEH;;;;GAIG;AACH,MAAM,MAAM,IAAI,GACd,IAAI,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,EAAE,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAA;AAErE;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,SAAS,EAAE,MAAM,CAAA;CAClB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAA;IACV,QAAQ,EAAE,OAAO,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,IAAI,CAAC,EAAE,IAAI,CAAA;IACX,MAAM,CAAC,EAAE,IAAI,CAAA;CACd;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAA;IACV,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,IAAI,CAAC,EAAE,IAAI,CAAA;IACX,MAAM,CAAC,EAAE,IAAI,CAAA;CACd;AAED;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,OAAO,CAAA;IAChB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,IAAI,CAAC,EAAE,IAAI,CAAA;IACX,MAAM,CAAC,EAAE,IAAI,CAAA;CACd;AAED
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/sync/types.ts"],"names":[],"mappings":"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"}
|
package/dist/sync/types.js
CHANGED
|
@@ -33,4 +33,21 @@ export class WasSyncConflictError extends Error {
|
|
|
33
33
|
this.name = 'WasSyncConflictError';
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Thrown by a {@link WasSyncPort} implementation when a request is rejected with
|
|
38
|
+
* `401 unauthorized` or `403 forbidden` -- the storage-access-expired/revoked
|
|
39
|
+
* signal. Mapping the raw HTTP status to a typed error at the port boundary (the
|
|
40
|
+
* same way `412` becomes {@link WasSyncConflictError}) lets the sync controller
|
|
41
|
+
* recognise expired access with an `instanceof` check rather than re-extracting
|
|
42
|
+
* status codes from a wrapped RxDB error graph. The offending HTTP status is
|
|
43
|
+
* kept on `status` for diagnostics.
|
|
44
|
+
*/
|
|
45
|
+
export class WasSyncAuthError extends Error {
|
|
46
|
+
status;
|
|
47
|
+
constructor(status) {
|
|
48
|
+
super(`WAS storage access denied (HTTP ${status}).`);
|
|
49
|
+
this.name = 'WasSyncAuthError';
|
|
50
|
+
this.status = status;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
36
53
|
//# sourceMappingURL=types.js.map
|
package/dist/sync/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/sync/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;GAkBG;
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/sync/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;GAkBG;AA2LH;;;;;;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;;;;;;;;GAQG;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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wasReplication.d.ts","sourceRoot":"","sources":["../../src/sync/wasReplication.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;GAWG;AACH,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,0BAA0B,CAAA;AACjC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"wasReplication.d.ts","sourceRoot":"","sources":["../../src/sync/wasReplication.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;GAWG;AACH,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,0BAA0B,CAAA;AACjC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AA2CxE;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,YAAY,EACZ,OAAO,EACP,qBAAqB,EACrB,SAAe,EACf,SAAS,EACT,IAAW,EACX,SAAgB,EAChB,YAAyB,EAC1B,EAAE;IACD,YAAY,EAAE,YAAY,CAAC,SAAS,CAAC,CAAA;IACrC,OAAO,EAAE,WAAW,CAAA;IACpB,qBAAqB,EAAE,MAAM,CAAA;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,GAAG,kBAAkB,CAAC,SAAS,EAAE,cAAc,CAAC,CAiBhD"}
|
|
@@ -16,6 +16,43 @@
|
|
|
16
16
|
import { replicateRxCollection } from 'rxdb/plugins/replication';
|
|
17
17
|
import { createPullHandler } from './changesQuery.js';
|
|
18
18
|
import { createPushHandler } from './pushWrites.js';
|
|
19
|
+
/**
|
|
20
|
+
* Builds the push write-back: patches an accepted write's acked server
|
|
21
|
+
* revision(s) (`version` / `metaVersion`) into the local row so the next
|
|
22
|
+
* conditional write's `If-Match` matches the server. Skips rows that are gone
|
|
23
|
+
* or already current (a tombstoned row is invisible to `findOne` and needs no
|
|
24
|
+
* write-back -- nothing further is pushed for a deleted id). Failures are
|
|
25
|
+
* swallowed: the write itself succeeded, and a missed write-back only means
|
|
26
|
+
* the acked revision is adopted from the change feed's echo on a later pull.
|
|
27
|
+
*
|
|
28
|
+
* @param rxCollection {RxCollection<SyncedDoc>}
|
|
29
|
+
* @returns {(ack: PushWriteAck) => Promise<void>}
|
|
30
|
+
*/
|
|
31
|
+
function createAckWriteBack(rxCollection) {
|
|
32
|
+
return async function writeBack(ack) {
|
|
33
|
+
try {
|
|
34
|
+
const doc = await rxCollection.findOne(ack.id).exec();
|
|
35
|
+
if (doc === null) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const patch = {};
|
|
39
|
+
if (ack.version !== undefined && doc.get('version') !== ack.version) {
|
|
40
|
+
patch.version = ack.version;
|
|
41
|
+
}
|
|
42
|
+
if (ack.metaVersion !== undefined &&
|
|
43
|
+
doc.get('metaVersion') !== ack.metaVersion) {
|
|
44
|
+
patch.metaVersion = ack.metaVersion;
|
|
45
|
+
}
|
|
46
|
+
if (Object.keys(patch).length > 0) {
|
|
47
|
+
await doc.incrementalPatch(patch);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// Best-effort: the server write was accepted; the revision echo on the
|
|
52
|
+
// next pull corrects the row if this local patch could not be applied.
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
19
56
|
/**
|
|
20
57
|
* Starts (or configures) replication of one RxDB collection against a remote WAS
|
|
21
58
|
* Collection. Poll-based only -- no `pull.stream$` (live streaming is deferred
|
|
@@ -47,7 +84,7 @@ export function createWasReplication({ rxCollection, wasPort, replicationIdentif
|
|
|
47
84
|
batchSize
|
|
48
85
|
},
|
|
49
86
|
push: {
|
|
50
|
-
handler: createPushHandler(wasPort),
|
|
87
|
+
handler: createPushHandler(wasPort, createAckWriteBack(rxCollection)),
|
|
51
88
|
batchSize
|
|
52
89
|
}
|
|
53
90
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wasReplication.js","sourceRoot":"","sources":["../../src/sync/wasReplication.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;GAWG;AACH,OAAO,EACL,qBAAqB,EAEtB,MAAM,0BAA0B,CAAA;AAGjC,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,EAAE,iBAAiB,
|
|
1
|
+
{"version":3,"file":"wasReplication.js","sourceRoot":"","sources":["../../src/sync/wasReplication.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;GAWG;AACH,OAAO,EACL,qBAAqB,EAEtB,MAAM,0BAA0B,CAAA;AAGjC,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,EAAE,iBAAiB,EAAqB,MAAM,iBAAiB,CAAA;AAEtE;;;;;;;;;;;GAWG;AACH,SAAS,kBAAkB,CAAC,YAAqC;IAC/D,OAAO,KAAK,UAAU,SAAS,CAAC,GAAiB;QAC/C,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;YACrD,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;gBACjB,OAAM;YACR,CAAC;YACD,MAAM,KAAK,GAAuB,EAAE,CAAA;YACpC,IAAI,GAAG,CAAC,OAAO,KAAK,SAAS,IAAI,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC;gBACpE,KAAK,CAAC,OAAO,GAAG,GAAG,CAAC,OAAO,CAAA;YAC7B,CAAC;YACD,IACE,GAAG,CAAC,WAAW,KAAK,SAAS;gBAC7B,GAAG,CAAC,GAAG,CAAC,aAAa,CAAC,KAAK,GAAG,CAAC,WAAW,EAC1C,CAAC;gBACD,KAAK,CAAC,WAAW,GAAG,GAAG,CAAC,WAAW,CAAA;YACrC,CAAC;YACD,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAClC,MAAM,GAAG,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAA;YACnC,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,uEAAuE;YACvE,uEAAuE;QACzE,CAAC;IACH,CAAC,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,oBAAoB,CAAC,EACnC,YAAY,EACZ,OAAO,EACP,qBAAqB,EACrB,SAAS,GAAG,GAAG,EACf,SAAS,EACT,IAAI,GAAG,IAAI,EACX,SAAS,GAAG,IAAI,EAChB,YAAY,GAAG,UAAU,EAU1B;IACC,OAAO,qBAAqB,CAA4B;QACtD,qBAAqB;QACrB,UAAU,EAAE,YAAY;QACxB,YAAY;QACZ,IAAI;QACJ,SAAS;QACT,GAAG,CAAC,SAAS,KAAK,SAAS,IAAI,EAAE,SAAS,EAAE,CAAC;QAC7C,IAAI,EAAE;YACJ,OAAO,EAAE,iBAAiB,CAAC,OAAO,CAAC;YACnC,SAAS;SACV;QACD,IAAI,EAAE;YACJ,OAAO,EAAE,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,CAAC,YAAY,CAAC,CAAC;YACrE,SAAS;SACV;KACF,CAAC,CAAA;AACJ,CAAC"}
|