@interop/was-react 0.20.1 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -2
- package/dist/dev/provisionDevGrants.js +1 -1
- package/dist/dev/provisionDevGrants.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -2
- package/dist/index.js.map +1 -1
- package/dist/session/authStore.js +1 -1
- package/dist/session/authStore.js.map +1 -1
- package/dist/storage/adopt.d.ts.map +1 -1
- package/dist/storage/adopt.js +2 -1
- package/dist/storage/adopt.js.map +1 -1
- package/dist/{sync → storage}/docCipher.d.ts +3 -29
- package/dist/storage/docCipher.d.ts.map +1 -0
- package/dist/{sync → storage}/docCipher.js +0 -20
- package/dist/storage/docCipher.js.map +1 -0
- package/dist/storage/entityStore.d.ts.map +1 -1
- package/dist/storage/entityStore.js +2 -1
- package/dist/storage/entityStore.js.map +1 -1
- package/dist/storage/localStore.d.ts +1 -1
- package/dist/storage/localStore.d.ts.map +1 -1
- package/dist/storage/localStore.js +14 -2
- package/dist/storage/localStore.js.map +1 -1
- package/dist/storage/sharedCollectionReader.d.ts +1 -1
- package/dist/storage/sharedCollectionReader.d.ts.map +1 -1
- package/dist/storage/sharedCollectionReader.js +1 -1
- package/dist/storage/sharedCollectionReader.js.map +1 -1
- package/dist/storage/storageContext.d.ts +1 -2
- package/dist/storage/storageContext.d.ts.map +1 -1
- package/dist/storage/storageContext.js.map +1 -1
- package/dist/storage/storageManager.d.ts +1 -1
- package/dist/storage/storageManager.d.ts.map +1 -1
- package/dist/storage/syncController.d.ts +30 -19
- package/dist/storage/syncController.d.ts.map +1 -1
- package/dist/storage/syncController.js +84 -157
- package/dist/storage/syncController.js.map +1 -1
- package/dist/storage/syncStatusStore.d.ts +8 -7
- package/dist/storage/syncStatusStore.d.ts.map +1 -1
- package/dist/storage/syncStatusStore.js.map +1 -1
- package/dist/storage/wasRemoteStore.js +1 -1
- package/dist/storage/wasRemoteStore.js.map +1 -1
- package/dist/storage/wasSync.d.ts +1 -1
- package/dist/storage/wasSync.d.ts.map +1 -1
- package/dist/storage/wasSync.js +1 -1
- package/dist/storage/wasSync.js.map +1 -1
- package/dist/{sync → storage}/wasSyncPort.d.ts +14 -8
- package/dist/storage/wasSyncPort.d.ts.map +1 -0
- package/dist/storage/wasSyncPort.js.map +1 -0
- package/dist/storage/writerId.d.ts +7 -13
- package/dist/storage/writerId.d.ts.map +1 -1
- package/dist/storage/writerId.js +30 -46
- package/dist/storage/writerId.js.map +1 -1
- package/package.json +13 -10
- package/dist/sync/changesQuery.d.ts +0 -44
- package/dist/sync/changesQuery.d.ts.map +0 -1
- package/dist/sync/changesQuery.js +0 -54
- package/dist/sync/changesQuery.js.map +0 -1
- package/dist/sync/docCipher.d.ts.map +0 -1
- package/dist/sync/docCipher.js.map +0 -1
- package/dist/sync/feedMasterPort.d.ts +0 -38
- package/dist/sync/feedMasterPort.d.ts.map +0 -1
- package/dist/sync/feedMasterPort.js +0 -116
- package/dist/sync/feedMasterPort.js.map +0 -1
- package/dist/sync/index.d.ts +0 -23
- package/dist/sync/index.d.ts.map +0 -1
- package/dist/sync/index.js +0 -27
- package/dist/sync/index.js.map +0 -1
- package/dist/sync/lww.d.ts +0 -50
- package/dist/sync/lww.d.ts.map +0 -1
- package/dist/sync/lww.js +0 -69
- package/dist/sync/lww.js.map +0 -1
- package/dist/sync/lwwConflictHandler.d.ts +0 -83
- package/dist/sync/lwwConflictHandler.d.ts.map +0 -1
- package/dist/sync/lwwConflictHandler.js +0 -114
- package/dist/sync/lwwConflictHandler.js.map +0 -1
- package/dist/sync/pushWrites.d.ts +0 -76
- package/dist/sync/pushWrites.d.ts.map +0 -1
- package/dist/sync/pushWrites.js +0 -221
- package/dist/sync/pushWrites.js.map +0 -1
- package/dist/sync/syncedDocSchema.d.ts +0 -22
- package/dist/sync/syncedDocSchema.d.ts.map +0 -1
- package/dist/sync/syncedDocSchema.js +0 -29
- package/dist/sync/syncedDocSchema.js.map +0 -1
- package/dist/sync/types.d.ts +0 -319
- package/dist/sync/types.d.ts.map +0 -1
- package/dist/sync/types.js +0 -58
- package/dist/sync/types.js.map +0 -1
- package/dist/sync/wasReplication.d.ts +0 -47
- package/dist/sync/wasReplication.d.ts.map +0 -1
- package/dist/sync/wasReplication.js +0 -92
- package/dist/sync/wasReplication.js.map +0 -1
- package/dist/sync/wasSyncPort.d.ts.map +0 -1
- package/dist/sync/wasSyncPort.js.map +0 -1
- /package/dist/{sync → storage}/wasSyncPort.js +0 -0
package/dist/sync/pushWrites.js
DELETED
|
@@ -1,221 +0,0 @@
|
|
|
1
|
-
import { formatEtag } from '@interop/was-client/sync';
|
|
2
|
-
import { bodiesEqual, copyOptionalBodyFields, WasSyncAuthError, WasSyncConflictError } from './types.js';
|
|
3
|
-
/**
|
|
4
|
-
* Maps a re-read master state into the RxDB conflict entry for one row, or --
|
|
5
|
-
* when the re-read found the resource genuinely absent (`master === null`, a
|
|
6
|
-
* delete/delete race) -- synthesizes the tombstone conflict entry from what we
|
|
7
|
-
* know locally. Shared by the `412` assembler and the `/meta` 404 recovery, so
|
|
8
|
-
* both report an absent master identically.
|
|
9
|
-
*
|
|
10
|
-
* @param options {object}
|
|
11
|
-
* @param options.id {string}
|
|
12
|
-
* @param options.master {MasterState | null}
|
|
13
|
-
* @param options.fallbackUpdatedAt {string} used if the resource is now absent
|
|
14
|
-
* @param options.fallbackVersion {number} used if the resource is now absent
|
|
15
|
-
* @returns {WithDeleted<SyncedDoc>}
|
|
16
|
-
*/
|
|
17
|
-
function masterOrTombstone({ id, master, fallbackUpdatedAt, fallbackVersion }) {
|
|
18
|
-
if (master === null) {
|
|
19
|
-
return {
|
|
20
|
-
id,
|
|
21
|
-
updatedAt: fallbackUpdatedAt,
|
|
22
|
-
version: fallbackVersion,
|
|
23
|
-
_deleted: true
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
const conflict = {
|
|
27
|
-
id,
|
|
28
|
-
updatedAt: master.updatedAt,
|
|
29
|
-
version: master.version,
|
|
30
|
-
_deleted: master.deleted
|
|
31
|
-
};
|
|
32
|
-
copyOptionalBodyFields({ source: master, target: conflict });
|
|
33
|
-
return conflict;
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Sends one local change to the remote Collection as up to two conditional
|
|
37
|
-
* writes (content, then metadata). Returns the master-state conflict entry on a
|
|
38
|
-
* `412` at either step, or the accepted writes' acked revisions on success
|
|
39
|
-
* (`ack: null` when no response carried a revision). A conflict on the
|
|
40
|
-
* metadata half still returns the content half's earned ack alongside the
|
|
41
|
-
* conflict entry, so an accepted content version is never discarded.
|
|
42
|
-
*
|
|
43
|
-
* @param options {object}
|
|
44
|
-
* @param options.port {WasSyncPort}
|
|
45
|
-
* @param options.newDocumentState {WithDeleted<SyncedDoc>}
|
|
46
|
-
* @param [options.assumedMasterState] {WithDeleted<SyncedDoc>}
|
|
47
|
-
* @param [options.cache] {MasterReadCache} the push batch's shared
|
|
48
|
-
* master-read memo
|
|
49
|
-
* @returns {Promise<{ conflict: WithDeleted<SyncedDoc> | null,
|
|
50
|
-
* ack: PushWriteAck | null }>}
|
|
51
|
-
*/
|
|
52
|
-
async function pushRow({ port, newDocumentState, assumedMasterState, cache }) {
|
|
53
|
-
const { id } = newDocumentState;
|
|
54
|
-
const assumedVersion = assumedMasterState?.version;
|
|
55
|
-
const isCreate = assumedMasterState === undefined;
|
|
56
|
-
const ack = { id };
|
|
57
|
-
const hasAck = () => ack.version !== undefined || ack.metaVersion !== undefined;
|
|
58
|
-
const fallbackVersion = () => ack.version ?? assumedVersion ?? newDocumentState.version;
|
|
59
|
-
// Re-reads this row's master. A row that has ALREADY written this batch
|
|
60
|
-
// (a content write accepted before a `/meta` rejection) bypasses the batch
|
|
61
|
-
// memo: a sibling's feed read may have paged past this resource before our
|
|
62
|
-
// own write landed, and the conflict entry must carry the revision that write
|
|
63
|
-
// produced. Rows that wrote nothing -- the ordinary content 412, the common
|
|
64
|
-
// case -- are exactly what the memo is for.
|
|
65
|
-
const readMaster = async () => hasAck()
|
|
66
|
-
? port.get({ id })
|
|
67
|
-
: port.get({ id, ...(cache !== undefined && { cache }) });
|
|
68
|
-
// Builds the conflict outcome from a re-read master (or from its absence, a
|
|
69
|
-
// delete/delete race), PRESERVING any ack already earned: a content write
|
|
70
|
-
// accepted before a `/meta` 412 must keep its acked `version` (and feed it to
|
|
71
|
-
// the absent-master fallback), or the local row keeps the pre-write version
|
|
72
|
-
// and every later conditional write sends a stale `If-Match`.
|
|
73
|
-
const conflictOutcome = (master) => ({
|
|
74
|
-
conflict: masterOrTombstone({
|
|
75
|
-
id,
|
|
76
|
-
master,
|
|
77
|
-
fallbackUpdatedAt: newDocumentState.updatedAt,
|
|
78
|
-
fallbackVersion: fallbackVersion()
|
|
79
|
-
}),
|
|
80
|
-
ack: hasAck() ? ack : null
|
|
81
|
-
});
|
|
82
|
-
// The 412 path: re-read the resource, then report its real master state.
|
|
83
|
-
const conflictResult = async () => conflictOutcome(await readMaster());
|
|
84
|
-
try {
|
|
85
|
-
if (newDocumentState._deleted) {
|
|
86
|
-
// Delete supersedes any metadata write: drop the content, tombstone wins.
|
|
87
|
-
const ackedVersion = await port.deleteContent({
|
|
88
|
-
id,
|
|
89
|
-
...(assumedVersion !== undefined && {
|
|
90
|
-
ifMatch: formatEtag(assumedVersion)
|
|
91
|
-
})
|
|
92
|
-
});
|
|
93
|
-
if (ackedVersion !== undefined) {
|
|
94
|
-
ack.version = ackedVersion;
|
|
95
|
-
}
|
|
96
|
-
return { conflict: null, ack: ack.version !== undefined ? ack : null };
|
|
97
|
-
}
|
|
98
|
-
// Content half: write on create, or when the content body changed. For a
|
|
99
|
-
// content-addressed collection the update case never fires (an immutable
|
|
100
|
-
// body for a stable id), but it is handled for generality.
|
|
101
|
-
const contentChanged = isCreate || !bodiesEqual(newDocumentState.data, assumedMasterState?.data);
|
|
102
|
-
if (contentChanged) {
|
|
103
|
-
const ackedVersion = await port.putContent({
|
|
104
|
-
id,
|
|
105
|
-
data: newDocumentState.data ?? null,
|
|
106
|
-
...(newDocumentState.epoch !== undefined && {
|
|
107
|
-
epoch: newDocumentState.epoch
|
|
108
|
-
}),
|
|
109
|
-
...(isCreate
|
|
110
|
-
? { ifNoneMatch: true }
|
|
111
|
-
: assumedVersion !== undefined && {
|
|
112
|
-
ifMatch: formatEtag(assumedVersion)
|
|
113
|
-
})
|
|
114
|
-
});
|
|
115
|
-
if (ackedVersion !== undefined) {
|
|
116
|
-
ack.version = ackedVersion;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
catch (err) {
|
|
121
|
-
if (err instanceof WasSyncConflictError) {
|
|
122
|
-
return await conflictResult();
|
|
123
|
-
}
|
|
124
|
-
// Any non-conflict error (network, 5xx, auth) propagates so RxDB retries
|
|
125
|
-
// the whole batch with backoff.
|
|
126
|
-
throw err;
|
|
127
|
-
}
|
|
128
|
-
// Metadata half: write when the metadata changed -- including a CLEAR (the
|
|
129
|
-
// new state carries no `custom` while the assumed master does; `putMeta`
|
|
130
|
-
// with no `custom` writes the cleared state). On a create this runs after
|
|
131
|
-
// the content write (the resource must exist first). Its own try/catch so a
|
|
132
|
-
// rejection here can never discard an ack the content half already earned.
|
|
133
|
-
const metadataChanged = !bodiesEqual(newDocumentState.custom, assumedMasterState?.custom);
|
|
134
|
-
if (metadataChanged) {
|
|
135
|
-
try {
|
|
136
|
-
const assumedMetaVersion = assumedMasterState?.metaVersion;
|
|
137
|
-
const ackedMetaVersion = await port.putMeta({
|
|
138
|
-
id,
|
|
139
|
-
...(newDocumentState.custom !== undefined && {
|
|
140
|
-
custom: newDocumentState.custom
|
|
141
|
-
}),
|
|
142
|
-
...(assumedMetaVersion !== undefined
|
|
143
|
-
? { ifMatch: formatEtag(assumedMetaVersion) }
|
|
144
|
-
: { ifNoneMatch: true })
|
|
145
|
-
});
|
|
146
|
-
if (ackedMetaVersion !== undefined) {
|
|
147
|
-
ack.metaVersion = ackedMetaVersion;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
catch (err) {
|
|
151
|
-
if (err instanceof WasSyncConflictError) {
|
|
152
|
-
return await conflictResult();
|
|
153
|
-
}
|
|
154
|
-
// Corroborate before condemnation: under WAS 404-masking a `/meta` 404
|
|
155
|
-
// is ambiguous -- expired access, or an ordinary race with a remote
|
|
156
|
-
// delete (a PUT to the `/meta` of a nonexistent resource legitimately
|
|
157
|
-
// 404s). An independent request decides: re-read the master off the
|
|
158
|
-
// changes feed. A feed read that is itself denied rethrows its own auth
|
|
159
|
-
// error (access genuinely expired, so the controller escalates); a feed
|
|
160
|
-
// that answers with an absent/deleted master confirms the delete race,
|
|
161
|
-
// and the row is resolved with that tombstone as the conflict entry
|
|
162
|
-
// (the conflict handler reconciles it) instead of flipping the whole
|
|
163
|
-
// session to "access expired" and wedging the batch in RxDB's retries.
|
|
164
|
-
if (err instanceof WasSyncAuthError && err.status === 404) {
|
|
165
|
-
const master = await readMaster();
|
|
166
|
-
if (master === null || master.deleted) {
|
|
167
|
-
return conflictOutcome(master);
|
|
168
|
-
}
|
|
169
|
-
// The resource is alive and readable while its `/meta` write 404s:
|
|
170
|
-
// the write itself was rejected, so the auth signal stands.
|
|
171
|
-
}
|
|
172
|
-
throw err;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
return { conflict: null, ack: hasAck() ? ack : null };
|
|
176
|
-
}
|
|
177
|
-
/**
|
|
178
|
-
* Builds the RxDB push handler that fans a batch of local changes out to
|
|
179
|
-
* conditional WAS writes and returns the conflicting rows' master states.
|
|
180
|
-
*
|
|
181
|
-
* Rows are pushed concurrently; if any non-conflict error is thrown the whole
|
|
182
|
-
* batch rejects (RxDB re-sends it later), matching RxDB's all-or-nothing retry.
|
|
183
|
-
*
|
|
184
|
-
* Each accepted write's acked server revision(s) are handed to
|
|
185
|
-
* `onWriteAccepted` (when supplied) as soon as that row's writes settle, so the
|
|
186
|
-
* caller can write the new `version` / `metaVersion` back into the local row
|
|
187
|
-
* and keep subsequent conditional writes' `If-Match` in step with the server.
|
|
188
|
-
*
|
|
189
|
-
* Each batch gets one short-lived master-read memo, shared by its rows and
|
|
190
|
-
* discarded with the batch (never held across batches, where it would go
|
|
191
|
-
* stale). A conflict re-read walks the changes feed from its origin and so pages
|
|
192
|
-
* past every other conflicting row's master on the way; without the memo a batch
|
|
193
|
-
* with k conflicts would run k concurrent full-feed walks.
|
|
194
|
-
*
|
|
195
|
-
* @param port {WasSyncPort}
|
|
196
|
-
* @param [onWriteAccepted] {(ack: PushWriteAck) => Promise<void>}
|
|
197
|
-
* @returns {(rows: Array<{ newDocumentState: WithDeleted<SyncedDoc>,
|
|
198
|
-
* assumedMasterState?: WithDeleted<SyncedDoc> }>) =>
|
|
199
|
-
* Promise<WithDeleted<SyncedDoc>[]>}
|
|
200
|
-
*/
|
|
201
|
-
export function createPushHandler(port, onWriteAccepted) {
|
|
202
|
-
return async function push(rows) {
|
|
203
|
-
const cache = { byId: new Map(), inFlight: null };
|
|
204
|
-
const results = await Promise.all(rows.map(async (row) => {
|
|
205
|
-
const result = await pushRow({
|
|
206
|
-
port,
|
|
207
|
-
newDocumentState: row.newDocumentState,
|
|
208
|
-
assumedMasterState: row.assumedMasterState,
|
|
209
|
-
cache
|
|
210
|
-
});
|
|
211
|
-
if (result.ack !== null && onWriteAccepted !== undefined) {
|
|
212
|
-
await onWriteAccepted(result.ack);
|
|
213
|
-
}
|
|
214
|
-
return result;
|
|
215
|
-
}));
|
|
216
|
-
return results
|
|
217
|
-
.map(result => result.conflict)
|
|
218
|
-
.filter((conflict) => conflict !== null);
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
//# sourceMappingURL=pushWrites.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"pushWrites.js","sourceRoot":"","sources":["../../src/sync/pushWrites.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* Copyright (c) 2026 Interop Alliance. All rights reserved.
|
|
3
|
-
*/
|
|
4
|
-
/**
|
|
5
|
-
* The single generic RxDB JSON schema reused across every synced collection.
|
|
6
|
-
* One shape (`{ id, updatedAt, version, metaVersion?, data?, custom? }`) carries
|
|
7
|
-
* both a content revision and an independently-versioned metadata sub-resource;
|
|
8
|
-
* `_deleted` is added by RxDB via `deletedField`. `data` / `custom` are opaque
|
|
9
|
-
* bodies (plaintext JSON, or an EDV envelope on an encrypted collection), so
|
|
10
|
-
* they are typed as free-form objects.
|
|
11
|
-
*/
|
|
12
|
-
import type { RxJsonSchema } from 'rxdb/plugins/core';
|
|
13
|
-
import type { SyncedDoc } from './types.js';
|
|
14
|
-
/**
|
|
15
|
-
* Returns the synced-doc schema. `id` is the primary key (the WAS resourceId);
|
|
16
|
-
* `updatedAt` is indexed because it is the change-feed sort field / checkpoint
|
|
17
|
-
* component.
|
|
18
|
-
*
|
|
19
|
-
* @returns {RxJsonSchema<SyncedDoc>}
|
|
20
|
-
*/
|
|
21
|
-
export declare function syncedDocSchema(): RxJsonSchema<SyncedDoc>;
|
|
22
|
-
//# sourceMappingURL=syncedDocSchema.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"syncedDocSchema.d.ts","sourceRoot":"","sources":["../../src/sync/syncedDocSchema.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAE3C;;;;;;GAMG;AACH,wBAAgB,eAAe,IAAI,YAAY,CAAC,SAAS,CAAC,CAoBzD"}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Returns the synced-doc schema. `id` is the primary key (the WAS resourceId);
|
|
3
|
-
* `updatedAt` is indexed because it is the change-feed sort field / checkpoint
|
|
4
|
-
* component.
|
|
5
|
-
*
|
|
6
|
-
* @returns {RxJsonSchema<SyncedDoc>}
|
|
7
|
-
*/
|
|
8
|
-
export function syncedDocSchema() {
|
|
9
|
-
return {
|
|
10
|
-
version: 0,
|
|
11
|
-
primaryKey: 'id',
|
|
12
|
-
type: 'object',
|
|
13
|
-
properties: {
|
|
14
|
-
id: { type: 'string', maxLength: 256 },
|
|
15
|
-
updatedAt: { type: 'string', maxLength: 64 },
|
|
16
|
-
version: { type: 'number' },
|
|
17
|
-
metaVersion: { type: 'number' },
|
|
18
|
-
// The opaque key-epoch id `data` was encrypted under (the `Key-Epoch`
|
|
19
|
-
// content-push header), when known.
|
|
20
|
-
epoch: { type: 'string', maxLength: 256 },
|
|
21
|
-
// Opaque stored bodies -- content and metadata envelopes -- moved verbatim.
|
|
22
|
-
data: { type: 'object', additionalProperties: true },
|
|
23
|
-
custom: { type: 'object', additionalProperties: true }
|
|
24
|
-
},
|
|
25
|
-
required: ['id', 'updatedAt', 'version'],
|
|
26
|
-
indexes: ['updatedAt']
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
//# sourceMappingURL=syncedDocSchema.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"syncedDocSchema.js","sourceRoot":"","sources":["../../src/sync/syncedDocSchema.ts"],"names":[],"mappings":"AAcA;;;;;;GAMG;AACH,MAAM,UAAU,eAAe;IAC7B,OAAO;QACL,OAAO,EAAE,CAAC;QACV,UAAU,EAAE,IAAI;QAChB,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,EAAE;YACtC,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,EAAE;YAC5C,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YAC3B,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YAC/B,sEAAsE;YACtE,oCAAoC;YACpC,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,EAAE;YACzC,4EAA4E;YAC5E,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,oBAAoB,EAAE,IAAI,EAAE;YACpD,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,oBAAoB,EAAE,IAAI,EAAE;SACvD;QACD,QAAQ,EAAE,CAAC,IAAI,EAAE,WAAW,EAAE,SAAS,CAAC;QACxC,OAAO,EAAE,CAAC,WAAW,CAAC;KACI,CAAA;AAC9B,CAAC"}
|
package/dist/sync/types.d.ts
DELETED
|
@@ -1,319 +0,0 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* Copyright (c) 2026 Interop Alliance. All rights reserved.
|
|
3
|
-
*/
|
|
4
|
-
/**
|
|
5
|
-
* Shared types for the collection-agnostic WAS replication adapter.
|
|
6
|
-
*
|
|
7
|
-
* The adapter drives an RxDB `replicateRxCollection` state machine against a
|
|
8
|
-
* remote WAS Collection's replication endpoints. It is deliberately free of any
|
|
9
|
-
* React or app imports so it can stand alone; all WAS access is injected through
|
|
10
|
-
* the {@link WasSyncPort} seam rather than importing `@interop/was-client`
|
|
11
|
-
* directly.
|
|
12
|
-
*
|
|
13
|
-
* The wire contract follows the WAS `changes` feed and its V2 encrypted-metadata
|
|
14
|
-
* profile: a synced document carries both a content revision (`version` /
|
|
15
|
-
* `data`) and an independently-versioned metadata sub-resource (`metaVersion` /
|
|
16
|
-
* `custom`). A metadata-only edit re-surfaces the resource with a bumped
|
|
17
|
-
* `updatedAt` / `metaVersion` but unchanged `version` / `data`. The sync layer
|
|
18
|
-
* moves both bodies opaquely: `data` is the stored content body (plaintext JSON,
|
|
19
|
-
* or the EDV envelope on an encrypted collection) and `custom` is the stored
|
|
20
|
-
* metadata body (an opaque envelope on an encrypted collection); encrypt/decrypt
|
|
21
|
-
* stays a read/write-time concern above this layer.
|
|
22
|
-
*
|
|
23
|
-
* The two small helpers that travel with these shapes live here as well: the
|
|
24
|
-
* opaque-body equality every routing decision is made on, and the optional-field
|
|
25
|
-
* copy every wire-to-local mapping runs.
|
|
26
|
-
*/
|
|
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';
|
|
46
|
-
/**
|
|
47
|
-
* A JSON value -- the opaque stored resource body the sync layer moves verbatim.
|
|
48
|
-
* For a plaintext collection this is the user document; for an encrypted one it
|
|
49
|
-
* is the EDV envelope. The adapter never inspects or transforms it. Re-exported
|
|
50
|
-
* from `@interop/was-client`, which owns the wire model, so a body crosses the
|
|
51
|
-
* port boundary without a cast.
|
|
52
|
-
*/
|
|
53
|
-
export type { Json };
|
|
54
|
-
/**
|
|
55
|
-
* The optional half of a synced document, shared verbatim by every shape it
|
|
56
|
-
* travels in ({@link WireDoc} on the feed, {@link SyncedDoc} locally,
|
|
57
|
-
* {@link MasterState} on the conflict re-read): the independently-versioned
|
|
58
|
-
* metadata revision and body, the content body, and the content body's
|
|
59
|
-
* key-epoch stamp. Each is genuinely absent rather than `undefined` when the
|
|
60
|
-
* server has nothing for it, so every mapping between the shapes copies them
|
|
61
|
-
* conditionally -- see {@link copyOptionalBodyFields}.
|
|
62
|
-
*/
|
|
63
|
-
export interface OptionalBodyFields {
|
|
64
|
-
metaVersion?: number;
|
|
65
|
-
data?: Json;
|
|
66
|
-
custom?: Json;
|
|
67
|
-
epoch?: string;
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Structural equality over two opaque bodies, by canonical-free JSON string.
|
|
71
|
-
* Decides whether the content or the metadata half changed -- which endpoint(s)
|
|
72
|
-
* a push writes, and whether two states compare equal for conflict resolution.
|
|
73
|
-
* Content-addressed collections never mutate `data` for a given id, and a real
|
|
74
|
-
* metadata edit re-encrypts to fresh bytes, so this coarse comparison suffices.
|
|
75
|
-
*
|
|
76
|
-
* @param left {Json | undefined}
|
|
77
|
-
* @param right {Json | undefined}
|
|
78
|
-
* @returns {boolean}
|
|
79
|
-
*/
|
|
80
|
-
export declare function bodiesEqual(left: Json | undefined, right: Json | undefined): boolean;
|
|
81
|
-
/**
|
|
82
|
-
* Copies the {@link OptionalBodyFields} that are present on `source` onto
|
|
83
|
-
* `target`, leaving an absent field absent (never writing an explicit
|
|
84
|
-
* `undefined`, which would surface the key in a serialized body). The single
|
|
85
|
-
* place every wire-to-local, feed-to-master, and master-to-conflict mapping
|
|
86
|
-
* shares, so a new optional wire field is added once.
|
|
87
|
-
*
|
|
88
|
-
* @param options {object}
|
|
89
|
-
* @param options.source {OptionalBodyFields}
|
|
90
|
-
* @param options.target {OptionalBodyFields} mutated in place
|
|
91
|
-
* @returns {void}
|
|
92
|
-
*/
|
|
93
|
-
export declare function copyOptionalBodyFields({ source, target }: {
|
|
94
|
-
source: OptionalBodyFields;
|
|
95
|
-
target: OptionalBodyFields;
|
|
96
|
-
}): void;
|
|
97
|
-
/**
|
|
98
|
-
* The keyset position in the change feed: the `{ id, updatedAt }` of the last
|
|
99
|
-
* document a pull returned. Passed back verbatim to resume, and used as the
|
|
100
|
-
* RxDB replication checkpoint. `id` is the total-order tiebreaker within a
|
|
101
|
-
* single `updatedAt`. The client's own checkpoint type, aliased here so the
|
|
102
|
-
* replication layer and the port agree by construction.
|
|
103
|
-
*/
|
|
104
|
-
export type SyncCheckpoint = ClientSyncCheckpoint;
|
|
105
|
-
/**
|
|
106
|
-
* One document as it travels on the `changes` feed wire
|
|
107
|
-
* (`POST /space/:s/:c/query`, profile `changes`). `id` is the WAS resourceId,
|
|
108
|
-
* `version` is the content master revision (feeds the content push `If-Match`
|
|
109
|
-
* ETag) and the user content body is nested under `data`; `metaVersion` is the
|
|
110
|
-
* independent metadata revision (feeds the `/meta` push `If-Match` ETag) and the
|
|
111
|
-
* user-writable metadata body is under `custom`. A tombstone carries
|
|
112
|
-
* `_deleted: true` with no `data`. `metaVersion` / `custom` are present only
|
|
113
|
-
* once metadata has been written for the resource.
|
|
114
|
-
*/
|
|
115
|
-
export interface WireDoc {
|
|
116
|
-
id: string;
|
|
117
|
-
_deleted: boolean;
|
|
118
|
-
updatedAt: string;
|
|
119
|
-
version: number;
|
|
120
|
-
metaVersion?: number;
|
|
121
|
-
data?: Json;
|
|
122
|
-
custom?: Json;
|
|
123
|
-
/**
|
|
124
|
-
* The opaque key-epoch id the content body was encrypted under (the
|
|
125
|
-
* `key-epochs` feature), present when the server holds a stamp for the
|
|
126
|
-
* resource. Moved verbatim; the sync layer never interprets it.
|
|
127
|
-
*/
|
|
128
|
-
epoch?: string;
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* The local RxDB document shape, shared across every synced collection. The
|
|
132
|
-
* envelope fields are top-level (`id` primary key, `updatedAt` the checkpoint
|
|
133
|
-
* sort field, `version` / `metaVersion` the server master revisions); the user
|
|
134
|
-
* bodies stay nested (`data` for content, `custom` for metadata) to avoid field
|
|
135
|
-
* collisions. `_deleted` is managed by RxDB via `deletedField` and so is not
|
|
136
|
-
* part of this "clean" shape (handlers work with RxDB's `WithDeleted<SyncedDoc>`).
|
|
137
|
-
*/
|
|
138
|
-
export interface SyncedDoc {
|
|
139
|
-
id: string;
|
|
140
|
-
updatedAt: string;
|
|
141
|
-
version: number;
|
|
142
|
-
metaVersion?: number;
|
|
143
|
-
data?: Json;
|
|
144
|
-
custom?: Json;
|
|
145
|
-
/**
|
|
146
|
-
* The opaque key-epoch id `data` was encrypted under, when known (stamped by
|
|
147
|
-
* the encrypting cipher on a local write, or pulled off the feed). Sent as
|
|
148
|
-
* the `Key-Epoch` header on the content push so the server's stamp stays
|
|
149
|
-
* in step with the envelope.
|
|
150
|
-
*/
|
|
151
|
-
epoch?: string;
|
|
152
|
-
}
|
|
153
|
-
/**
|
|
154
|
-
* The current master state of a single resource, as read back for the 412
|
|
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.
|
|
161
|
-
*/
|
|
162
|
-
export interface MasterState extends ClientMasterState {
|
|
163
|
-
deleted: boolean;
|
|
164
|
-
}
|
|
165
|
-
/**
|
|
166
|
-
* The short-lived master-read memo the rows of ONE push batch share. A
|
|
167
|
-
* resolving `get` implementation may answer from `byId` and MUST record every
|
|
168
|
-
* master it resolved on the way there; a feed-walking implementation pages past
|
|
169
|
-
* all of them anyway, so a batch of k conflicts costs one walk instead of k.
|
|
170
|
-
*
|
|
171
|
-
* `inFlight` is what makes that hold under concurrency: the batch's rows push in
|
|
172
|
-
* parallel, so without it every row would start its own walk before the first
|
|
173
|
-
* one finished and the memo would never be read. A `get` that must walk
|
|
174
|
-
* publishes its walk here; a `get` that finds a walk already running awaits it
|
|
175
|
-
* and re-checks `byId` first. It settles (never rejects) so one row's failed
|
|
176
|
-
* read is not another row's, and is `null` whenever no walk is running.
|
|
177
|
-
*
|
|
178
|
-
* The whole object is created per batch invocation and dropped with it: a memo
|
|
179
|
-
* held across batches would go stale.
|
|
180
|
-
*/
|
|
181
|
-
export interface MasterReadCache {
|
|
182
|
-
byId: Map<string, MasterState | null>;
|
|
183
|
-
inFlight: Promise<void> | null;
|
|
184
|
-
}
|
|
185
|
-
/**
|
|
186
|
-
* The write/query half of the injected WAS-access seam. An adapter (the app-side
|
|
187
|
-
* `createWasSyncPort`) implements this over `@interop/was-client`; the core
|
|
188
|
-
* module depends only on this interface, never on `was-client` itself. Every
|
|
189
|
-
* method moves the stored body verbatim -- no codec, no key handling -- so the
|
|
190
|
-
* same port works for plaintext and encrypted collections alike.
|
|
191
|
-
*
|
|
192
|
-
* `putContent` / `deleteContent` / `putMeta` MUST throw
|
|
193
|
-
* {@link WasSyncConflictError} when the server rejects a conditional write with
|
|
194
|
-
* `412 precondition-failed`, and let every other error propagate so RxDB's
|
|
195
|
-
* retry/backoff handles it.
|
|
196
|
-
*
|
|
197
|
-
* The 412 conflict re-read (`get`) is deliberately NOT part of this base:
|
|
198
|
-
* `createWasSyncPort` returns a `WasSyncBasePort`, and `withFeedMasterRead`
|
|
199
|
-
* supplies a `get` that resolves the master state from the changes-feed body
|
|
200
|
-
* (origin-independent) to produce a full {@link WasSyncPort}.
|
|
201
|
-
*/
|
|
202
|
-
export interface WasSyncBasePort {
|
|
203
|
-
/**
|
|
204
|
-
* Pulls one page of the `changes` feed. Omit `checkpoint` for the first page.
|
|
205
|
-
* Returns the page's `documents` and its resume `checkpoint`, or
|
|
206
|
-
* `checkpoint: null` for an empty (no-change) page.
|
|
207
|
-
*
|
|
208
|
-
* @param options {object}
|
|
209
|
-
* @param [options.checkpoint] {SyncCheckpoint} resume position
|
|
210
|
-
* @param options.limit {number} requested batch size
|
|
211
|
-
* @returns {Promise<{ documents: WireDoc[], checkpoint: SyncCheckpoint | null }>}
|
|
212
|
-
*/
|
|
213
|
-
query(options: {
|
|
214
|
-
checkpoint?: SyncCheckpoint;
|
|
215
|
-
limit: number;
|
|
216
|
-
}): Promise<{
|
|
217
|
-
documents: WireDoc[];
|
|
218
|
-
checkpoint: SyncCheckpoint | null;
|
|
219
|
-
}>;
|
|
220
|
-
/**
|
|
221
|
-
* Conditionally writes the content body verbatim (`PUT /:id`). Pass
|
|
222
|
-
* `ifNoneMatch: true` for a create-if-absent, or `ifMatch` (a quoted ETag over
|
|
223
|
-
* the content `version`) for an update-if-unchanged. `epoch` is the opaque
|
|
224
|
-
* key-epoch id the body was encrypted under, sent as the `Key-Epoch`
|
|
225
|
-
* header (an absent epoch clears any prior stamp on the server, per the
|
|
226
|
-
* `key-epochs` feature). Returns the new content `version` parsed from the
|
|
227
|
-
* response ETag (the acked master revision), or `undefined` when the server
|
|
228
|
-
* does not supply one.
|
|
229
|
-
*
|
|
230
|
-
* @param options {object}
|
|
231
|
-
* @param options.id {string}
|
|
232
|
-
* @param options.data {Json}
|
|
233
|
-
* @param [options.ifMatch] {string}
|
|
234
|
-
* @param [options.ifNoneMatch] {boolean}
|
|
235
|
-
* @param [options.epoch] {string}
|
|
236
|
-
* @returns {Promise<number | undefined>}
|
|
237
|
-
*/
|
|
238
|
-
putContent(options: {
|
|
239
|
-
id: string;
|
|
240
|
-
data: Json;
|
|
241
|
-
ifMatch?: string;
|
|
242
|
-
ifNoneMatch?: boolean;
|
|
243
|
-
epoch?: string;
|
|
244
|
-
}): Promise<number | undefined>;
|
|
245
|
-
/**
|
|
246
|
-
* Conditionally deletes a resource (writes a tombstone; `DELETE /:id`). Pass
|
|
247
|
-
* `ifMatch` (a quoted ETag over the content `version`) to delete only if
|
|
248
|
-
* unchanged. Returns the tombstone `version` parsed from the response ETag
|
|
249
|
-
* when the server supplies one (the reference server does not). MUST treat a
|
|
250
|
-
* `404` as success (resolve `undefined`): the resource is already absent --
|
|
251
|
-
* a row deleted locally before its first push, or deleted remotely first --
|
|
252
|
-
* so the tombstone's goal state holds; rejecting would wedge the push batch
|
|
253
|
-
* in RxDB's retry loop.
|
|
254
|
-
*
|
|
255
|
-
* @param options {object}
|
|
256
|
-
* @param options.id {string}
|
|
257
|
-
* @param [options.ifMatch] {string}
|
|
258
|
-
* @returns {Promise<number | undefined>}
|
|
259
|
-
*/
|
|
260
|
-
deleteContent(options: {
|
|
261
|
-
id: string;
|
|
262
|
-
ifMatch?: string;
|
|
263
|
-
}): Promise<number | undefined>;
|
|
264
|
-
/**
|
|
265
|
-
* Conditionally writes the metadata body verbatim (`PUT /:id/meta`, body
|
|
266
|
-
* `{ custom }`). An ABSENT `custom` writes the CLEARED state (a body with no
|
|
267
|
-
* `custom` member -- the server's metadata replace clears every property the
|
|
268
|
-
* body omits), so removing a resource's metadata replicates rather than being
|
|
269
|
-
* skipped. Pass `ifNoneMatch: true` when the resource has no metadata yet, or
|
|
270
|
-
* `ifMatch` (a quoted ETag over `metaVersion`) for an update-if-unchanged.
|
|
271
|
-
* The resource must already exist (the server does not create a resource from
|
|
272
|
-
* a `/meta` write). Returns the new `metaVersion` parsed from the response
|
|
273
|
-
* ETag, or `undefined` when the server does not supply one.
|
|
274
|
-
*
|
|
275
|
-
* @param options {object}
|
|
276
|
-
* @param [options.custom] {Json} absent = write the cleared state
|
|
277
|
-
* @param options.id {string}
|
|
278
|
-
* @param [options.ifMatch] {string}
|
|
279
|
-
* @param [options.ifNoneMatch] {boolean}
|
|
280
|
-
* @returns {Promise<number | undefined>}
|
|
281
|
-
*/
|
|
282
|
-
putMeta(options: {
|
|
283
|
-
id: string;
|
|
284
|
-
custom?: Json;
|
|
285
|
-
ifMatch?: string;
|
|
286
|
-
ifNoneMatch?: boolean;
|
|
287
|
-
}): Promise<number | undefined>;
|
|
288
|
-
}
|
|
289
|
-
/**
|
|
290
|
-
* The full sync port the core consumes: a {@link WasSyncBasePort}'s write/query
|
|
291
|
-
* methods plus the `get` used by the 412 conflict assembler. The push handler
|
|
292
|
-
* and `createWasReplication` require this full port; build one by wrapping a
|
|
293
|
-
* base port with `withFeedMasterRead`, which supplies `get`.
|
|
294
|
-
*/
|
|
295
|
-
export interface WasSyncPort extends WasSyncBasePort {
|
|
296
|
-
/**
|
|
297
|
-
* Re-reads a single resource's current master state (content + metadata) for
|
|
298
|
-
* the 412 conflict assembler. Returns `null` when the resource is genuinely
|
|
299
|
-
* absent (a delete/delete race); throws a retryable error when the master
|
|
300
|
-
* cannot be resolved (e.g. a feed re-read that exhausts its scan budget), so
|
|
301
|
-
* the replication cycle retries rather than fabricating a false tombstone.
|
|
302
|
-
*
|
|
303
|
-
* `cache` is an OPTIONAL {@link MasterReadCache}, shared by the rows of one
|
|
304
|
-
* push batch (see its own docs for the contract). A hit is no staler than a
|
|
305
|
-
* read issued at the same moment, since the memo lives only for that batch. An
|
|
306
|
-
* implementation that ignores it is fully conformant, and a caller that omits
|
|
307
|
-
* it gets an uncached read.
|
|
308
|
-
*
|
|
309
|
-
* @param options {object}
|
|
310
|
-
* @param options.id {string}
|
|
311
|
-
* @param [options.cache] {MasterReadCache}
|
|
312
|
-
* @returns {Promise<MasterState | null>}
|
|
313
|
-
*/
|
|
314
|
-
get(options: {
|
|
315
|
-
id: string;
|
|
316
|
-
cache?: MasterReadCache;
|
|
317
|
-
}): Promise<MasterState | null>;
|
|
318
|
-
}
|
|
319
|
-
//# sourceMappingURL=types.d.ts.map
|
package/dist/sync/types.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/sync/types.ts"],"names":[],"mappings":"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"}
|