@interop/wallet-core 0.90.0 → 0.91.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.
- package/dist/clientAnnex/grantRevocation.d.ts +9 -4
- package/dist/clientAnnex/grantRevocation.d.ts.map +1 -1
- package/dist/clientAnnex/grantRevocation.js +8 -4
- package/dist/clientAnnex/grantRevocation.js.map +1 -1
- package/dist/clientAnnex/index.d.ts +1 -1
- package/dist/clientAnnex/index.d.ts.map +1 -1
- package/dist/clientAnnex/index.js +1 -1
- package/dist/clientAnnex/index.js.map +1 -1
- package/dist/clientAnnex/ladder.d.ts +11 -13
- package/dist/clientAnnex/ladder.d.ts.map +1 -1
- package/dist/clientAnnex/ladder.js +71 -75
- package/dist/clientAnnex/ladder.js.map +1 -1
- package/dist/clientAnnex/ladderAnchored.d.ts +7 -58
- package/dist/clientAnnex/ladderAnchored.d.ts.map +1 -1
- package/dist/clientAnnex/ladderAnchored.js +9 -72
- package/dist/clientAnnex/ladderAnchored.js.map +1 -1
- package/dist/clientAnnex/log.d.ts +4 -2
- package/dist/clientAnnex/log.d.ts.map +1 -1
- package/dist/clientAnnex/log.js +12 -2
- package/dist/clientAnnex/log.js.map +1 -1
- package/dist/clientAnnex/recoveryLadderAnchored.d.ts +53 -17
- package/dist/clientAnnex/recoveryLadderAnchored.d.ts.map +1 -1
- package/dist/clientAnnex/recoveryLadderAnchored.js +52 -24
- package/dist/clientAnnex/recoveryLadderAnchored.js.map +1 -1
- package/dist/clientAnnex/selfEnroll.d.ts +3 -8
- package/dist/clientAnnex/selfEnroll.d.ts.map +1 -1
- package/dist/clientAnnex/selfEnroll.js +2 -1
- package/dist/clientAnnex/selfEnroll.js.map +1 -1
- package/dist/recovery/continuation.d.ts +13 -6
- package/dist/recovery/continuation.d.ts.map +1 -1
- package/dist/recovery/continuation.js +14 -2
- package/dist/recovery/continuation.js.map +1 -1
- package/dist/recovery/recoveryWebvh.d.ts +17 -4
- package/dist/recovery/recoveryWebvh.d.ts.map +1 -1
- package/dist/recovery/recoveryWebvh.js +14 -1
- package/dist/recovery/recoveryWebvh.js.map +1 -1
- package/dist/sync/contactsConflict.d.ts +9 -9
- package/dist/sync/contactsConflict.js +9 -9
- package/dist/sync/engine.d.ts +3 -3
- package/dist/sync/engine.js +3 -3
- package/dist/sync/engine.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/pull.d.ts +5 -5
- package/dist/sync/pull.js +5 -5
- package/dist/sync/push.d.ts +7 -6
- package/dist/sync/push.d.ts.map +1 -1
- package/dist/sync/push.js +65 -59
- package/dist/sync/push.js.map +1 -1
- package/dist/sync/remint.d.ts +37 -33
- package/dist/sync/remint.d.ts.map +1 -1
- package/dist/sync/remint.js +70 -64
- package/dist/sync/remint.js.map +1 -1
- package/dist/sync/types.d.ts +88 -83
- package/dist/sync/types.d.ts.map +1 -1
- package/dist/sync/types.js +12 -12
- package/dist/unlock/standingWebvh.d.ts.map +1 -1
- package/dist/unlock/standingWebvh.js +3 -1
- package/dist/unlock/standingWebvh.js.map +1 -1
- package/dist/webvh/accountEntry.d.ts +10 -1
- package/dist/webvh/accountEntry.d.ts.map +1 -1
- package/dist/webvh/accountEntry.js +23 -1
- package/dist/webvh/accountEntry.js.map +1 -1
- package/dist/webvh/builtOnHead.d.ts +79 -0
- package/dist/webvh/builtOnHead.d.ts.map +1 -0
- package/dist/webvh/builtOnHead.js +77 -0
- package/dist/webvh/builtOnHead.js.map +1 -0
- package/dist/webvh/didWebvh.d.ts +14 -6
- package/dist/webvh/didWebvh.d.ts.map +1 -1
- package/dist/webvh/didWebvh.js +5 -2
- package/dist/webvh/didWebvh.js.map +1 -1
- package/dist/webvh/enrollClient.d.ts +4 -1
- package/dist/webvh/enrollClient.d.ts.map +1 -1
- package/dist/webvh/enrollClient.js +38 -8
- package/dist/webvh/enrollClient.js.map +1 -1
- package/dist/webvh/index.d.ts +2 -0
- package/dist/webvh/index.d.ts.map +1 -1
- package/dist/webvh/index.js +1 -0
- package/dist/webvh/index.js.map +1 -1
- package/dist/webvh/revokeClient.d.ts.map +1 -1
- package/dist/webvh/revokeClient.js +4 -6
- package/dist/webvh/revokeClient.js.map +1 -1
- package/dist/webvh/verifyLog.d.ts +5 -1
- package/dist/webvh/verifyLog.d.ts.map +1 -1
- package/dist/webvh/verifyLog.js +4 -2
- package/dist/webvh/verifyLog.js.map +1 -1
- package/package.json +1 -1
|
@@ -210,6 +210,18 @@ export async function removeRecoveryKey({ idStore, signer, recovery, ladderSeed,
|
|
|
210
210
|
* never-published CLIENT's committed hashes, inert orphans in
|
|
211
211
|
* `nextKeyHashes` exactly as on the self-enrollment seam (keys of a lost
|
|
212
212
|
* random seed; nothing can reveal them)
|
|
213
|
+
* @param [options.resume] {object} `{ builtOnHead: { scid, versionId } }`
|
|
214
|
+
* -- the resume marker the torn run's pending record recorded, the head
|
|
215
|
+
* the seam handed it. Supplied, each attempt's first read is refused with
|
|
216
|
+
* `BuiltOnHeadNotReachedError` unless the served log carries that SCID and
|
|
217
|
+
* an entry with that `versionId`, as the transient continuation's resume
|
|
218
|
+
* is. The chain-head pin covers the same fork only once it has advanced
|
|
219
|
+
* past the reveal entry, and a tab death between the publish and the
|
|
220
|
+
* pin's advance leaves the pin one entry short, so the marker is what
|
|
221
|
+
* refuses a served log truncated to the pre-reveal head, where the
|
|
222
|
+
* completion check would otherwise build a second reveal entry. Both
|
|
223
|
+
* members must be non-empty strings; a malformed marker is refused with a
|
|
224
|
+
* `TypeError` before any read
|
|
213
225
|
* @param [options.expectedDid] {string} the account DID the log must resolve
|
|
214
226
|
* to, where the recovering flow already knows it. Every read both entries
|
|
215
227
|
* are built on is checked against the store's own chain-head pin (a served
|
|
@@ -247,7 +259,7 @@ export async function recoverWebvhClient(options) {
|
|
|
247
259
|
signingKeyMultibase: options.newClientKeys.signingKeyMultibase,
|
|
248
260
|
keyAgreementKeyMultibase: options.newClientKeys.keyAgreementKeyMultibase
|
|
249
261
|
});
|
|
250
|
-
const { newClientKeys, newClientUpdateSeeds, onCommitted, expectedDid, ...shared } = options;
|
|
262
|
+
const { newClientKeys, newClientUpdateSeeds, onCommitted, resume, expectedDid, ...shared } = options;
|
|
251
263
|
// One run outside the conflict retry: a lost compare-and-swap re-enters
|
|
252
264
|
// the body under the same run, and the emitter reports each stage once.
|
|
253
265
|
const events = ceremonyEvents({
|
|
@@ -281,6 +293,7 @@ export async function recoverWebvhClient(options) {
|
|
|
281
293
|
});
|
|
282
294
|
return { methods, ...relations };
|
|
283
295
|
},
|
|
296
|
+
...(resume !== undefined ? { builtOnHead: resume.builtOnHead } : {}),
|
|
284
297
|
...(expectedDid !== undefined ? { expectedDid } : {}),
|
|
285
298
|
events
|
|
286
299
|
})), spendOutcome);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recoveryWebvh.js","sourceRoot":"","sources":["../../src/recovery/recoveryWebvh.ts"],"names":[],"mappings":"AAwCA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACrD,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAC/B,OAAO,EACL,yBAAyB,EACzB,oBAAoB,EACpB,oBAAoB,EACrB,MAAM,sBAAsB,CAAA;AAM7B,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAA;
|
|
1
|
+
{"version":3,"file":"recoveryWebvh.js","sourceRoot":"","sources":["../../src/recovery/recoveryWebvh.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
* The last-write-wins conflict rule for the mutable `contacts` head
|
|
6
6
|
* collection. Every other synced collection is immutable and content-addressed,
|
|
7
7
|
* so a write-write conflict is impossible there. A contact head document is
|
|
8
|
-
* genuinely overwritten in place under a stable
|
|
9
|
-
* race on the same
|
|
10
|
-
* drop one side's edit.
|
|
8
|
+
* genuinely overwritten in place under a stable resource id, so two replicas
|
|
9
|
+
* CAN race on the same resource -- and a remote-master-always default would
|
|
10
|
+
* silently drop one side's edit.
|
|
11
11
|
*
|
|
12
12
|
* The rule lives here rather than in `@interop/social-core` because deciding
|
|
13
13
|
* it means DECRYPTING both sides: a head payload rides inside an encrypted
|
|
@@ -59,7 +59,7 @@ import { type ContactHeadPayload } from '@interop/social-core';
|
|
|
59
59
|
*/
|
|
60
60
|
export type ContactConflictWinner = 'remote' | 'local';
|
|
61
61
|
/**
|
|
62
|
-
* Recovers a validated head payload from a stored
|
|
62
|
+
* Recovers a validated head payload from a stored body: decrypts an
|
|
63
63
|
* envelope, passes plaintext through, and resolves `undefined` when the
|
|
64
64
|
* payload (and so the fields the rule compares) cannot be reached.
|
|
65
65
|
*
|
|
@@ -69,13 +69,13 @@ export type ContactConflictWinner = 'remote' | 'local';
|
|
|
69
69
|
* side leaves the reachable local body to win. Neither side is ever compared on
|
|
70
70
|
* a body it could not open.
|
|
71
71
|
*
|
|
72
|
-
* The decrypt is addressed with the
|
|
72
|
+
* The decrypt is addressed with the resource's own id, and an envelope sealed for
|
|
73
73
|
* another resource is refused with an `IntegrityError` rather than read as one
|
|
74
74
|
* more unreachable side. That refusal is rethrown, out of the resolver and into
|
|
75
75
|
* the replication cycle. See the module doc.
|
|
76
76
|
*
|
|
77
77
|
* @param options {object}
|
|
78
|
-
* @param options.id {string} the contact head
|
|
78
|
+
* @param options.id {string} the contact head's resource id, the id the
|
|
79
79
|
* stored envelope must be sealed for
|
|
80
80
|
* @param options.data {Json} the stored body: an encrypted envelope or a
|
|
81
81
|
* plaintext payload
|
|
@@ -94,10 +94,10 @@ export declare function contactHeadPayloadOf({ id, data, cipher }: {
|
|
|
94
94
|
* Decides a contact-head conflict. See the module doc for the fail-safe rule.
|
|
95
95
|
*
|
|
96
96
|
* @param options {object}
|
|
97
|
-
* @param options.id {string} the contested
|
|
97
|
+
* @param options.id {string} the contested resource's id, which both
|
|
98
98
|
* sides' envelopes must be sealed for
|
|
99
|
-
* @param options.remote {Json} the remote (master)
|
|
100
|
-
* @param options.local {Json} the local
|
|
99
|
+
* @param options.remote {Json} the remote (master) body
|
|
100
|
+
* @param options.local {Json} the local resource replica's body
|
|
101
101
|
* @param [options.cipher] {DocCipher} the collection's document cipher
|
|
102
102
|
* @param [options.remoteDeleted] {boolean} the remote side is a tombstone
|
|
103
103
|
* @param [options.localDeleted] {boolean} the local side is a tombstone
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
* The last-write-wins conflict rule for the mutable `contacts` head
|
|
6
6
|
* collection. Every other synced collection is immutable and content-addressed,
|
|
7
7
|
* so a write-write conflict is impossible there. A contact head document is
|
|
8
|
-
* genuinely overwritten in place under a stable
|
|
9
|
-
* race on the same
|
|
10
|
-
* drop one side's edit.
|
|
8
|
+
* genuinely overwritten in place under a stable resource id, so two replicas
|
|
9
|
+
* CAN race on the same resource -- and a remote-master-always default would
|
|
10
|
+
* silently drop one side's edit.
|
|
11
11
|
*
|
|
12
12
|
* The rule lives here rather than in `@interop/social-core` because deciding
|
|
13
13
|
* it means DECRYPTING both sides: a head payload rides inside an encrypted
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
import { isEncryptedEnvelope, isIntegrityError } from '@interop/was-client/sync';
|
|
56
56
|
import { isContactHeadPayload, remotePayloadWins } from '@interop/social-core';
|
|
57
57
|
/**
|
|
58
|
-
* Recovers a validated head payload from a stored
|
|
58
|
+
* Recovers a validated head payload from a stored body: decrypts an
|
|
59
59
|
* envelope, passes plaintext through, and resolves `undefined` when the
|
|
60
60
|
* payload (and so the fields the rule compares) cannot be reached.
|
|
61
61
|
*
|
|
@@ -65,13 +65,13 @@ import { isContactHeadPayload, remotePayloadWins } from '@interop/social-core';
|
|
|
65
65
|
* side leaves the reachable local body to win. Neither side is ever compared on
|
|
66
66
|
* a body it could not open.
|
|
67
67
|
*
|
|
68
|
-
* The decrypt is addressed with the
|
|
68
|
+
* The decrypt is addressed with the resource's own id, and an envelope sealed for
|
|
69
69
|
* another resource is refused with an `IntegrityError` rather than read as one
|
|
70
70
|
* more unreachable side. That refusal is rethrown, out of the resolver and into
|
|
71
71
|
* the replication cycle. See the module doc.
|
|
72
72
|
*
|
|
73
73
|
* @param options {object}
|
|
74
|
-
* @param options.id {string} the contact head
|
|
74
|
+
* @param options.id {string} the contact head's resource id, the id the
|
|
75
75
|
* stored envelope must be sealed for
|
|
76
76
|
* @param options.data {Json} the stored body: an encrypted envelope or a
|
|
77
77
|
* plaintext payload
|
|
@@ -106,10 +106,10 @@ export async function contactHeadPayloadOf({ id, data, cipher }) {
|
|
|
106
106
|
* Decides a contact-head conflict. See the module doc for the fail-safe rule.
|
|
107
107
|
*
|
|
108
108
|
* @param options {object}
|
|
109
|
-
* @param options.id {string} the contested
|
|
109
|
+
* @param options.id {string} the contested resource's id, which both
|
|
110
110
|
* sides' envelopes must be sealed for
|
|
111
|
-
* @param options.remote {Json} the remote (master)
|
|
112
|
-
* @param options.local {Json} the local
|
|
111
|
+
* @param options.remote {Json} the remote (master) body
|
|
112
|
+
* @param options.local {Json} the local resource replica's body
|
|
113
113
|
* @param [options.cipher] {DocCipher} the collection's document cipher
|
|
114
114
|
* @param [options.remoteDeleted] {boolean} the remote side is a tombstone
|
|
115
115
|
* @param [options.localDeleted] {boolean} the local side is a tombstone
|
package/dist/sync/engine.d.ts
CHANGED
|
@@ -84,8 +84,8 @@ export interface SyncEngineDeps {
|
|
|
84
84
|
* provisioning and the migration sweep, so no pending envelope sealed under
|
|
85
85
|
* a losing epoch reaches the push: the consumer builds its cipher from the
|
|
86
86
|
* descriptor provisioning settled on and runs `remintPendingEnvelopes`
|
|
87
|
-
* (`remint.ts`) with it. The re-mint decides per
|
|
88
|
-
* itself, so in the settled case the call is free. A lazy minter, whose
|
|
87
|
+
* (`remint.ts`) with it. The re-mint decides per resource replica from the
|
|
88
|
+
* envelope itself, so in the settled case the call is free. A lazy minter, whose
|
|
89
89
|
* envelopes are always minted under the settled descriptor, leaves it
|
|
90
90
|
* absent.
|
|
91
91
|
*/
|
|
@@ -168,7 +168,7 @@ export declare class SyncEngine {
|
|
|
168
168
|
invalidateProvisioning(): void;
|
|
169
169
|
/**
|
|
170
170
|
* Stops the engine: aborts any in-flight cycle (the injected signal unwinds
|
|
171
|
-
* pull/push between pages
|
|
171
|
+
* pull/push between pages and resource replicas), cancels a pending retry, and resets to idle.
|
|
172
172
|
* The caller drops the cached agents/ciphers so key material does not outlive
|
|
173
173
|
* the unlocked session.
|
|
174
174
|
*/
|
package/dist/sync/engine.js
CHANGED
|
@@ -68,7 +68,7 @@ export class SyncEngine {
|
|
|
68
68
|
}
|
|
69
69
|
/**
|
|
70
70
|
* Stops the engine: aborts any in-flight cycle (the injected signal unwinds
|
|
71
|
-
* pull/push between pages
|
|
71
|
+
* pull/push between pages and resource replicas), cancels a pending retry, and resets to idle.
|
|
72
72
|
* The caller drops the cached agents/ciphers so key material does not outlive
|
|
73
73
|
* the unlocked session.
|
|
74
74
|
*/
|
|
@@ -177,8 +177,8 @@ export class SyncEngine {
|
|
|
177
177
|
if (signal.aborted) {
|
|
178
178
|
return;
|
|
179
179
|
}
|
|
180
|
-
// A resolved last-write-wins conflict may leave a
|
|
181
|
-
// re-encrypt); rerun this run so the re-push settles instead of waiting for
|
|
180
|
+
// A resolved last-write-wins conflict may leave a resource replica dirty
|
|
181
|
+
// (local-wins re-encrypt); rerun this run so the re-push settles instead of waiting for
|
|
182
182
|
// the next external trigger. Idempotent once the conflict clears.
|
|
183
183
|
if (conflictsResolved > 0) {
|
|
184
184
|
this.rerunRequested = true;
|
package/dist/sync/engine.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/sync/engine.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/sync/engine.ts"],"names":[],"mappings":"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"}
|
package/dist/sync/index.d.ts
CHANGED
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
* `isSyncNotFoundError` / `isUnknownEpochError`) and the `SyncStatus`
|
|
19
19
|
* vocabulary ship from `@interop/was-client/sync` too. They are imported
|
|
20
20
|
* from there rather than re-exported here: one owner per name.
|
|
21
|
-
* - `SyncStore` / `
|
|
22
|
-
* replica-side persistence seam.
|
|
21
|
+
* - `SyncStore` / `SyncedResourceReplica` / `ProjectionAction` /
|
|
22
|
+
* `ResolveConflict` are the replica-side persistence seam.
|
|
23
23
|
* - `runPull` / `projectionForDoc`, `runPush`, and `SyncEngine` are the pull,
|
|
24
24
|
* push, and orchestration algorithms.
|
|
25
25
|
* - `remintPendingEnvelopes` is the create-loss re-mint for an eager-minting
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
*/
|
|
39
39
|
export { UnknownEpochError, WasSyncConflictError, WasSyncNotFoundError } from '@interop/was-client/sync';
|
|
40
40
|
export type { Json, SyncCheckpoint, WireDoc, MasterState, WriteAck, WasSyncPort, DocCipher } from '@interop/was-client/sync';
|
|
41
|
-
export type { SyncStore,
|
|
41
|
+
export type { SyncStore, SyncedResourceReplica, ProjectionAction, ResolveConflict } from './types.js';
|
|
42
42
|
export { runPull, projectionForDoc } from './pull.js';
|
|
43
43
|
export { runPush } from './push.js';
|
|
44
44
|
export { remintPendingEnvelopes } from './remint.js';
|
package/dist/sync/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sync/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,oBAAoB,EACrB,MAAM,0BAA0B,CAAA;AACjC,YAAY,EACV,IAAI,EACJ,cAAc,EACd,OAAO,EACP,WAAW,EACX,QAAQ,EACR,WAAW,EACX,SAAS,EACV,MAAM,0BAA0B,CAAA;AAEjC,YAAY,EACV,SAAS,EACT,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sync/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,oBAAoB,EACrB,MAAM,0BAA0B,CAAA;AACjC,YAAY,EACV,IAAI,EACJ,cAAc,EACd,OAAO,EACP,WAAW,EACX,QAAQ,EACR,WAAW,EACX,SAAS,EACV,MAAM,0BAA0B,CAAA;AAEjC,YAAY,EACV,SAAS,EACT,qBAAqB,EACrB,gBAAgB,EAChB,eAAe,EAChB,MAAM,YAAY,CAAA;AAEnB,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAA;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AACjD,YAAY,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAA;AAE5D,OAAO,EACL,oBAAoB,EACpB,0BAA0B,EAC3B,MAAM,uBAAuB,CAAA;AAC9B,YAAY,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAA"}
|
package/dist/sync/index.js
CHANGED
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
* `isSyncNotFoundError` / `isUnknownEpochError`) and the `SyncStatus`
|
|
19
19
|
* vocabulary ship from `@interop/was-client/sync` too. They are imported
|
|
20
20
|
* from there rather than re-exported here: one owner per name.
|
|
21
|
-
* - `SyncStore` / `
|
|
22
|
-
* replica-side persistence seam.
|
|
21
|
+
* - `SyncStore` / `SyncedResourceReplica` / `ProjectionAction` /
|
|
22
|
+
* `ResolveConflict` are the replica-side persistence seam.
|
|
23
23
|
* - `runPull` / `projectionForDoc`, `runPush`, and `SyncEngine` are the pull,
|
|
24
24
|
* push, and orchestration algorithms.
|
|
25
25
|
* - `remintPendingEnvelopes` is the create-loss re-mint for an eager-minting
|
package/dist/sync/pull.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ import type { Json, ProjectionAction, SyncStore, WasSyncPort, WireDoc } from './
|
|
|
7
7
|
*
|
|
8
8
|
* Decryption runs here, outside the store transaction, so a slow/failing decrypt
|
|
9
9
|
* never holds the store's write lock. A document whose body cannot be decrypted
|
|
10
|
-
* (legacy plaintext
|
|
10
|
+
* (legacy plaintext body, corrupt/foreign envelope, key mismatch) is skipped with
|
|
11
11
|
* a `none` projection rather than throwing: the body is still stored and the
|
|
12
12
|
* checkpoint advances past it, so one poison document can never permanently
|
|
13
13
|
* wedge the feed for the whole replica. A document that decrypts but fails the
|
|
@@ -15,17 +15,17 @@ import type { Json, ProjectionAction, SyncStore, WasSyncPort, WireDoc } from './
|
|
|
15
15
|
* possibly a buggy or schema-incompatible writer) is skipped the same way:
|
|
16
16
|
* stored, checkpoint advanced, never projected.
|
|
17
17
|
*
|
|
18
|
-
* The decrypt is addressed: the feed
|
|
18
|
+
* The decrypt is addressed: the feed document's own `id` goes to `decryptDoc`, and
|
|
19
19
|
* the cipher refuses an envelope sealed for some other resource with was-
|
|
20
20
|
* client's `IntegrityError`. That refusal has two causes and the reader
|
|
21
21
|
* cannot tell them apart: a host that moved one resource's stored envelope
|
|
22
|
-
* under another resource's id, and a
|
|
23
|
-
* addressed sealing (the legacy contacts
|
|
22
|
+
* under another resource's id, and a document minted by a writer that
|
|
23
|
+
* predates addressed sealing (the legacy contacts documents
|
|
24
24
|
* `docs/cross-replica-sync-compatibility.md` records as a stated loss, which
|
|
25
25
|
* every fresh replica bootstrap re-reads). It is logged apart from the
|
|
26
26
|
* ordinary undecryptable skip so a caller can count it, but the benign cause
|
|
27
27
|
* is the common one and an alert on it needs a rate rather than a single
|
|
28
|
-
* event. The projection outcome is the same `none`: the
|
|
28
|
+
* event. The projection outcome is the same `none`: the document is not applied,
|
|
29
29
|
* the body is still stored, and the checkpoint advances past it.
|
|
30
30
|
*
|
|
31
31
|
* @param doc {WireDoc}
|
package/dist/sync/pull.js
CHANGED
|
@@ -41,7 +41,7 @@ import { log } from '../log.js';
|
|
|
41
41
|
*
|
|
42
42
|
* Decryption runs here, outside the store transaction, so a slow/failing decrypt
|
|
43
43
|
* never holds the store's write lock. A document whose body cannot be decrypted
|
|
44
|
-
* (legacy plaintext
|
|
44
|
+
* (legacy plaintext body, corrupt/foreign envelope, key mismatch) is skipped with
|
|
45
45
|
* a `none` projection rather than throwing: the body is still stored and the
|
|
46
46
|
* checkpoint advances past it, so one poison document can never permanently
|
|
47
47
|
* wedge the feed for the whole replica. A document that decrypts but fails the
|
|
@@ -49,17 +49,17 @@ import { log } from '../log.js';
|
|
|
49
49
|
* possibly a buggy or schema-incompatible writer) is skipped the same way:
|
|
50
50
|
* stored, checkpoint advanced, never projected.
|
|
51
51
|
*
|
|
52
|
-
* The decrypt is addressed: the feed
|
|
52
|
+
* The decrypt is addressed: the feed document's own `id` goes to `decryptDoc`, and
|
|
53
53
|
* the cipher refuses an envelope sealed for some other resource with was-
|
|
54
54
|
* client's `IntegrityError`. That refusal has two causes and the reader
|
|
55
55
|
* cannot tell them apart: a host that moved one resource's stored envelope
|
|
56
|
-
* under another resource's id, and a
|
|
57
|
-
* addressed sealing (the legacy contacts
|
|
56
|
+
* under another resource's id, and a document minted by a writer that
|
|
57
|
+
* predates addressed sealing (the legacy contacts documents
|
|
58
58
|
* `docs/cross-replica-sync-compatibility.md` records as a stated loss, which
|
|
59
59
|
* every fresh replica bootstrap re-reads). It is logged apart from the
|
|
60
60
|
* ordinary undecryptable skip so a caller can count it, but the benign cause
|
|
61
61
|
* is the common one and an alert on it needs a rate rather than a single
|
|
62
|
-
* event. The projection outcome is the same `none`: the
|
|
62
|
+
* event. The projection outcome is the same `none`: the document is not applied,
|
|
63
63
|
* the body is still stored, and the checkpoint advances past it.
|
|
64
64
|
*
|
|
65
65
|
* @param doc {WireDoc}
|
package/dist/sync/push.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { ResolveConflict, SyncStore, WasSyncPort } from './types.js';
|
|
2
2
|
/**
|
|
3
|
-
* Pushes every dirty
|
|
4
|
-
* keeps conflict reconciliation deterministic). Honors
|
|
5
|
-
* A non-conflict error from any
|
|
6
|
-
*
|
|
3
|
+
* Pushes every dirty resource replica for one feed, sequentially (bounds
|
|
4
|
+
* sockets/CPU, and keeps conflict reconciliation deterministic). Honors
|
|
5
|
+
* `signal` between resource replicas. A non-conflict error from any resource
|
|
6
|
+
* replica propagates so the engine aborts the cycle and backs off;
|
|
7
|
+
* already-pushed resource replicas in the batch stay settled.
|
|
7
8
|
*
|
|
8
9
|
* @param options {object}
|
|
9
10
|
* @param options.port {WasSyncPort}
|
|
@@ -12,8 +13,8 @@ import type { ResolveConflict, SyncStore, WasSyncPort } from './types.js';
|
|
|
12
13
|
* @param [options.writerId] {string} this writer's attribution label, sent
|
|
13
14
|
* as `Writer-Id` on every write and delete; absent sends none
|
|
14
15
|
* @param [options.signal] {AbortSignal}
|
|
15
|
-
* @returns {Promise<{ pushed: number; conflictsResolved: number }>} dirty
|
|
16
|
-
* processed this cycle, and how many invoked the LWW resolver (a positive
|
|
16
|
+
* @returns {Promise<{ pushed: number; conflictsResolved: number }>} dirty
|
|
17
|
+
* resource replicas processed this cycle, and how many invoked the LWW resolver (a positive
|
|
17
18
|
* count means the caller should rerun so a local-wins re-push settles)
|
|
18
19
|
*/
|
|
19
20
|
export declare function runPush({ port, store, resolveConflict, writerId, signal }: {
|
package/dist/sync/push.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../../src/sync/push.ts"],"names":[],"mappings":"AAsCA,OAAO,KAAK,EAAQ,eAAe,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../../src/sync/push.ts"],"names":[],"mappings":"AAsCA,OAAO,KAAK,EAAQ,eAAe,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AA0M/E;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,OAAO,CAAC,EAC5B,IAAI,EACJ,KAAK,EACL,eAAe,EACf,QAAQ,EACR,MAAM,EACP,EAAE;IACD,IAAI,EAAE,WAAW,CAAA;IACjB,KAAK,EAAE,SAAS,CAAA;IAChB,eAAe,CAAC,EAAE,eAAe,CAAA;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAA;CAAE,CAAC,CAyBzD"}
|
package/dist/sync/push.js
CHANGED
|
@@ -2,23 +2,23 @@
|
|
|
2
2
|
* Copyright (c) 2026 Interop Alliance. All rights reserved.
|
|
3
3
|
*/
|
|
4
4
|
/**
|
|
5
|
-
* The push side of the WAS replication engine core: fan each dirty local
|
|
6
|
-
* to a conditional WAS write, then reconcile per the
|
|
7
|
-
* table.
|
|
5
|
+
* The push side of the WAS replication engine core: fan each dirty local
|
|
6
|
+
* resource replica out to a conditional WAS write, then reconcile per the
|
|
7
|
+
* content-addressed conflict table.
|
|
8
8
|
*
|
|
9
|
-
* On a content-addressed collection an id's `data` never mutates, so a live
|
|
10
|
-
* only ever pushes as a create (`If-None-Match: *`) and a
|
|
11
|
-
* there is no update path. A mutable (last-write-wins)
|
|
12
|
-
*
|
|
13
|
-
* (`If-Match`) once acked, and settles
|
|
14
|
-
* {@link ResolveConflict} policy.
|
|
9
|
+
* On a content-addressed collection an id's `data` never mutates, so a live
|
|
10
|
+
* resource replica only ever pushes as a create (`If-None-Match: *`) and a
|
|
11
|
+
* tombstone as a delete; there is no update path. A mutable (last-write-wins)
|
|
12
|
+
* collection pushes a live resource replica as a create while never-acked
|
|
13
|
+
* (`version 0`) and as an in-place update (`If-Match`) once acked, and settles
|
|
14
|
+
* a `412` through its injected {@link ResolveConflict} policy.
|
|
15
15
|
*
|
|
16
16
|
* This loop covers the CONTENT sub-resource only (`data` / `version`, at
|
|
17
17
|
* `PUT/DELETE /:id`). It does not drive the independently-versioned METADATA
|
|
18
18
|
* sub-resource (`custom` / `metaVersion`, at `PUT /:id/meta`): a replica that
|
|
19
19
|
* syncs user-writable metadata (the web wallet's RxDB driver, via
|
|
20
|
-
* `WasSyncPort.putMeta`) keeps that half in its own push handler. It is left
|
|
21
|
-
* of this core deliberately -- none of the wallet Space collections
|
|
20
|
+
* `WasSyncPort.putMeta`) keeps that half in its own push handler. It is left
|
|
21
|
+
* out of this core deliberately -- none of the wallet Space collections
|
|
22
22
|
* (`private-credentials`, `public-credentials`, `wallet-activity`, `contacts`,
|
|
23
23
|
* `contacts-history`) versions its metadata independently of its content, so
|
|
24
24
|
* folding a `putMeta` diff into this loop would add an untested code path with
|
|
@@ -34,14 +34,15 @@
|
|
|
34
34
|
*/
|
|
35
35
|
import { isSyncConflictError, isSyncNotFoundError } from '@interop/was-client/sync';
|
|
36
36
|
/**
|
|
37
|
-
* Pushes a dirty live
|
|
38
|
-
* (`PUT /:id` with `If-None-Match: *`); an acked
|
|
39
|
-
* in-place update (`If-Match` over its
|
|
40
|
-
* absent only against a backend that
|
|
41
|
-
* a mutable collection, since a
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
37
|
+
* Pushes a dirty live resource replica. A never-acked resource replica
|
|
38
|
+
* (`version 0`) is a create (`PUT /:id` with `If-None-Match: *`); an acked
|
|
39
|
+
* resource replica (`version > 0`) is an in-place update (`If-Match` over its
|
|
40
|
+
* stored `etag`, echoed back verbatim -- absent only against a backend that
|
|
41
|
+
* never returned one) -- reachable only on a mutable collection, since a
|
|
42
|
+
* content-addressed resource replica never mutates in place. On success the
|
|
43
|
+
* acked `version` / `etag` are recorded and the resource replica goes clean --
|
|
44
|
+
* unless a local write landed while the write was in flight, which the resource
|
|
45
|
+
* replica's `revision` token detects (see {@link SyncStore.markPushed}) so the
|
|
45
46
|
* newer write stays dirty for the rerun cycle.
|
|
46
47
|
*
|
|
47
48
|
* A `412` is settled by the collection's policy:
|
|
@@ -53,19 +54,21 @@ import { isSyncConflictError, isSyncNotFoundError } from '@interop/was-client/sy
|
|
|
53
54
|
* absent/tombstone -> deletion wins, adopt the tombstone and delete the
|
|
54
55
|
* projection (a later re-add re-encrypts to a fresh id, so nothing is blocked).
|
|
55
56
|
*/
|
|
56
|
-
async function pushUpsert({ port, store,
|
|
57
|
+
async function pushUpsert({ port, store, replica, resolveConflict, writerId }) {
|
|
57
58
|
try {
|
|
58
59
|
const ack = await port.putContent({
|
|
59
|
-
id:
|
|
60
|
-
data:
|
|
61
|
-
...(
|
|
60
|
+
id: replica.id,
|
|
61
|
+
data: replica.data ?? null,
|
|
62
|
+
...(replica.version > 0
|
|
63
|
+
? { ifMatch: replica.etag }
|
|
64
|
+
: { ifNoneMatch: true }),
|
|
62
65
|
...(writerId !== undefined && { writerId })
|
|
63
66
|
});
|
|
64
67
|
await store.markPushed({
|
|
65
|
-
id:
|
|
68
|
+
id: replica.id,
|
|
66
69
|
version: ack.version,
|
|
67
70
|
etag: ack.etag,
|
|
68
|
-
...(
|
|
71
|
+
...(replica.revision !== undefined && { revision: replica.revision })
|
|
69
72
|
});
|
|
70
73
|
return { conflictResolved: false };
|
|
71
74
|
}
|
|
@@ -75,26 +78,27 @@ async function pushUpsert({ port, store, row, resolveConflict, writerId }) {
|
|
|
75
78
|
}
|
|
76
79
|
if (resolveConflict) {
|
|
77
80
|
await resolveConflict({
|
|
78
|
-
id:
|
|
79
|
-
version:
|
|
80
|
-
data:
|
|
81
|
+
id: replica.id,
|
|
82
|
+
version: replica.version,
|
|
83
|
+
data: replica.data
|
|
81
84
|
});
|
|
82
|
-
// The resolver may have left the
|
|
83
|
-
// caller reruns so the re-push settles within the same
|
|
85
|
+
// The resolver may have left the resource replica dirty (local-wins
|
|
86
|
+
// re-encrypt); the caller reruns so the re-push settles within the same
|
|
87
|
+
// sync run.
|
|
84
88
|
return { conflictResolved: true };
|
|
85
89
|
}
|
|
86
|
-
const master = await port.get({ id:
|
|
90
|
+
const master = await port.get({ id: replica.id });
|
|
87
91
|
// An absent or tombstoned resource surfaces as `get` resolving null.
|
|
88
92
|
if (master === null) {
|
|
89
93
|
await store.adoptLatest({
|
|
90
|
-
id:
|
|
94
|
+
id: replica.id,
|
|
91
95
|
latest: null,
|
|
92
96
|
projection: { kind: 'delete' }
|
|
93
97
|
});
|
|
94
98
|
}
|
|
95
99
|
else {
|
|
96
100
|
await store.adoptLatest({
|
|
97
|
-
id:
|
|
101
|
+
id: replica.id,
|
|
98
102
|
latest: master,
|
|
99
103
|
projection: { kind: 'none' }
|
|
100
104
|
});
|
|
@@ -136,51 +140,53 @@ async function tryDelete({ port, store, id, ifMatch, revision, writerId }) {
|
|
|
136
140
|
}
|
|
137
141
|
}
|
|
138
142
|
/**
|
|
139
|
-
* Pushes a dirty tombstone. `DELETE /:id` with `If-Match` over the
|
|
140
|
-
* `etag` when the
|
|
143
|
+
* Pushes a dirty tombstone. `DELETE /:id` with `If-Match` over the resource
|
|
144
|
+
* replica's stored `etag` when the resource replica was ever acked (`version >
|
|
145
|
+
* 0`), unconditional otherwise:
|
|
141
146
|
* - `204` / `404` -> settled (clean).
|
|
142
147
|
* - `412` then master absent/tombstone -> delete/delete race, settled.
|
|
143
148
|
* - `412` then master live -> retry once with the master's fresh `etag`; a
|
|
144
|
-
* second `412` leaves the
|
|
145
|
-
* refreshes its `version` / `etag` via the dirty-deleted-vs-live rule,
|
|
146
|
-
* retry's `If-Match` becomes current).
|
|
149
|
+
* second `412` leaves the resource replica dirty for the next cycle (the next
|
|
150
|
+
* pull refreshes its `version` / `etag` via the dirty-deleted-vs-live rule,
|
|
151
|
+
* so the retry's `If-Match` becomes current).
|
|
147
152
|
*/
|
|
148
|
-
async function pushDelete({ port, store,
|
|
149
|
-
const revisionAck =
|
|
150
|
-
const firstIfMatch =
|
|
153
|
+
async function pushDelete({ port, store, replica, writerId }) {
|
|
154
|
+
const revisionAck = replica.revision !== undefined ? { revision: replica.revision } : {};
|
|
155
|
+
const firstIfMatch = replica.version > 0 ? replica.etag : undefined;
|
|
151
156
|
if (await tryDelete({
|
|
152
157
|
port,
|
|
153
158
|
store,
|
|
154
|
-
id:
|
|
159
|
+
id: replica.id,
|
|
155
160
|
ifMatch: firstIfMatch,
|
|
156
161
|
writerId,
|
|
157
162
|
...revisionAck
|
|
158
163
|
})) {
|
|
159
164
|
return;
|
|
160
165
|
}
|
|
161
|
-
const master = await port.get({ id:
|
|
166
|
+
const master = await port.get({ id: replica.id });
|
|
162
167
|
if (master === null) {
|
|
163
168
|
// delete/delete race -- the resource is already a tombstone / absent.
|
|
164
|
-
await store.markDeletedPushed({ id:
|
|
169
|
+
await store.markDeletedPushed({ id: replica.id, ...revisionAck });
|
|
165
170
|
return;
|
|
166
171
|
}
|
|
167
|
-
// Second attempt with the master's current etag. If it too hits 412 we
|
|
168
|
-
//
|
|
169
|
-
// write).
|
|
172
|
+
// Second attempt with the master's current etag. If it too hits 412 we simply
|
|
173
|
+
// leave the resource replica dirty (tryDelete returned false and made no
|
|
174
|
+
// store write).
|
|
170
175
|
await tryDelete({
|
|
171
176
|
port,
|
|
172
177
|
store,
|
|
173
|
-
id:
|
|
178
|
+
id: replica.id,
|
|
174
179
|
ifMatch: master.etag,
|
|
175
180
|
writerId,
|
|
176
181
|
...revisionAck
|
|
177
182
|
});
|
|
178
183
|
}
|
|
179
184
|
/**
|
|
180
|
-
* Pushes every dirty
|
|
181
|
-
* keeps conflict reconciliation deterministic). Honors
|
|
182
|
-
* A non-conflict error from any
|
|
183
|
-
*
|
|
185
|
+
* Pushes every dirty resource replica for one feed, sequentially (bounds
|
|
186
|
+
* sockets/CPU, and keeps conflict reconciliation deterministic). Honors
|
|
187
|
+
* `signal` between resource replicas. A non-conflict error from any resource
|
|
188
|
+
* replica propagates so the engine aborts the cycle and backs off;
|
|
189
|
+
* already-pushed resource replicas in the batch stay settled.
|
|
184
190
|
*
|
|
185
191
|
* @param options {object}
|
|
186
192
|
* @param options.port {WasSyncPort}
|
|
@@ -189,26 +195,26 @@ async function pushDelete({ port, store, row, writerId }) {
|
|
|
189
195
|
* @param [options.writerId] {string} this writer's attribution label, sent
|
|
190
196
|
* as `Writer-Id` on every write and delete; absent sends none
|
|
191
197
|
* @param [options.signal] {AbortSignal}
|
|
192
|
-
* @returns {Promise<{ pushed: number; conflictsResolved: number }>} dirty
|
|
193
|
-
* processed this cycle, and how many invoked the LWW resolver (a positive
|
|
198
|
+
* @returns {Promise<{ pushed: number; conflictsResolved: number }>} dirty
|
|
199
|
+
* resource replicas processed this cycle, and how many invoked the LWW resolver (a positive
|
|
194
200
|
* count means the caller should rerun so a local-wins re-push settles)
|
|
195
201
|
*/
|
|
196
202
|
export async function runPush({ port, store, resolveConflict, writerId, signal }) {
|
|
197
|
-
const
|
|
203
|
+
const replicas = await store.getDirtyResourceReplicas();
|
|
198
204
|
let pushed = 0;
|
|
199
205
|
let conflictsResolved = 0;
|
|
200
|
-
for (const
|
|
206
|
+
for (const replica of replicas) {
|
|
201
207
|
if (signal?.aborted) {
|
|
202
208
|
break;
|
|
203
209
|
}
|
|
204
|
-
if (
|
|
205
|
-
await pushDelete({ port, store,
|
|
210
|
+
if (replica.deleted) {
|
|
211
|
+
await pushDelete({ port, store, replica, writerId });
|
|
206
212
|
}
|
|
207
213
|
else {
|
|
208
214
|
const { conflictResolved } = await pushUpsert({
|
|
209
215
|
port,
|
|
210
216
|
store,
|
|
211
|
-
|
|
217
|
+
replica,
|
|
212
218
|
resolveConflict,
|
|
213
219
|
writerId
|
|
214
220
|
});
|