velocious 1.0.476 → 1.0.478
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 +1 -0
- package/build/configuration-types.js +3 -0
- package/build/configuration.js +24 -0
- package/build/frontend-model-controller.js +368 -11
- package/build/frontend-models/base.js +5 -1
- package/build/frontend-models/resource-definition.js +20 -3
- package/build/http-server/client/websocket-session.js +93 -0
- package/build/routes/hooks/frontend-model-command-route-hook.js +16 -0
- package/build/src/configuration-types.d.ts +15 -0
- package/build/src/configuration-types.d.ts.map +1 -1
- package/build/src/configuration-types.js +4 -1
- package/build/src/configuration.d.ts +13 -0
- package/build/src/configuration.d.ts.map +1 -1
- package/build/src/configuration.js +23 -1
- package/build/src/frontend-model-controller.d.ts +96 -2
- package/build/src/frontend-model-controller.d.ts.map +1 -1
- package/build/src/frontend-model-controller.js +319 -12
- package/build/src/frontend-models/base.d.ts +5 -0
- package/build/src/frontend-models/base.d.ts.map +1 -1
- package/build/src/frontend-models/base.js +6 -2
- package/build/src/frontend-models/resource-definition.d.ts.map +1 -1
- package/build/src/frontend-models/resource-definition.js +20 -4
- package/build/src/http-server/client/websocket-session.d.ts +39 -0
- package/build/src/http-server/client/websocket-session.d.ts.map +1 -1
- package/build/src/http-server/client/websocket-session.js +85 -1
- package/build/src/routes/hooks/frontend-model-command-route-hook.d.ts.map +1 -1
- package/build/src/routes/hooks/frontend-model-command-route-hook.js +15 -1
- package/build/src/sync/conflict-strategy.d.ts +83 -0
- package/build/src/sync/conflict-strategy.d.ts.map +1 -0
- package/build/src/sync/conflict-strategy.js +215 -0
- package/build/src/sync/peer-mutation-bundle.d.ts +97 -0
- package/build/src/sync/peer-mutation-bundle.d.ts.map +1 -0
- package/build/src/sync/peer-mutation-bundle.js +215 -0
- package/build/src/sync/server-change-feed.d.ts +265 -0
- package/build/src/sync/server-change-feed.d.ts.map +1 -0
- package/build/src/sync/server-change-feed.js +475 -0
- package/build/src/sync/sync-envelope-replay-service.d.ts +213 -0
- package/build/src/sync/sync-envelope-replay-service.d.ts.map +1 -0
- package/build/src/sync/sync-envelope-replay-service.js +229 -0
- package/build/sync/conflict-strategy.js +225 -0
- package/build/sync/peer-mutation-bundle.js +232 -0
- package/build/sync/server-change-feed.js +524 -0
- package/build/sync/sync-envelope-replay-service.js +263 -0
- package/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/configuration-types.js +3 -0
- package/src/configuration.js +24 -0
- package/src/frontend-model-controller.js +368 -11
- package/src/frontend-models/base.js +5 -1
- package/src/frontend-models/resource-definition.js +20 -3
- package/src/http-server/client/websocket-session.js +93 -0
- package/src/routes/hooks/frontend-model-command-route-hook.js +16 -0
- package/src/sync/conflict-strategy.js +225 -0
- package/src/sync/peer-mutation-bundle.js +232 -0
- package/src/sync/server-change-feed.js +524 -0
- package/src/sync/sync-envelope-replay-service.js +263 -0
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import { createSignedMutation, mutationIdempotencyKey, verifySignedMutation } from "./device-identity.js";
|
|
3
|
+
const PEER_MUTATION_BUNDLE_FORMAT = "velocious.sync.peer-mutation-bundle.v1";
|
|
4
|
+
const EXPORTABLE_STATUSES = new Set(["pending", "applied-locally", "conflict"]);
|
|
5
|
+
/**
|
|
6
|
+
* Peer mutation bundle exported by a device for offline/P2P transfer.
|
|
7
|
+
* @typedef {object} PeerMutationBundle
|
|
8
|
+
* @property {string} exportedAt - ISO timestamp when the bundle was exported.
|
|
9
|
+
* @property {"velocious.sync.peer-mutation-bundle.v1"} format - Bundle format identifier.
|
|
10
|
+
* @property {PeerMutationBundleEntry[]} mutations - Signed mutations in local sequence order.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* One peer mutation bundle entry.
|
|
14
|
+
* @typedef {object} PeerMutationBundleEntry
|
|
15
|
+
* @property {string} [localRecordId] - Exporting device's local mutation record id.
|
|
16
|
+
* @property {number} [localSequence] - Exporting device's local mutation sequence.
|
|
17
|
+
* @property {import("./device-identity.js").SignedSyncMutation} signedMutation - Device-signed mutation envelope.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Exports local non-terminal mutations as a signed peer-transfer bundle.
|
|
21
|
+
* @param {object} args - Arguments.
|
|
22
|
+
* @param {import("./device-identity.js").DeviceCertificate} args.deviceCertificate - Device certificate for signing records.
|
|
23
|
+
* @param {import("./device-identity.js").SyncJsonWebKey} args.devicePrivateKey - Device private key for signing records.
|
|
24
|
+
* @param {import("./local-mutation-log.js").default} args.mutationLog - Local mutation log.
|
|
25
|
+
* @param {() => Date} [args.now] - Export clock.
|
|
26
|
+
* @param {import("./local-mutation-log.js").LocalMutationStatus[]} [args.statuses] - Statuses to export.
|
|
27
|
+
* @returns {Promise<PeerMutationBundle>} - Signed peer mutation bundle.
|
|
28
|
+
*/
|
|
29
|
+
export async function exportPeerMutationBundle({ deviceCertificate, devicePrivateKey, mutationLog, now = () => new Date(), statuses }) {
|
|
30
|
+
const selectedStatuses = normalizeExportStatuses(statuses);
|
|
31
|
+
const timestamp = isoTimestamp(now(), "exportedAt");
|
|
32
|
+
const records = (await mutationLog.records())
|
|
33
|
+
.filter((record) => selectedStatuses.has(record.status))
|
|
34
|
+
.sort((left, right) => left.sequence - right.sequence);
|
|
35
|
+
const mutations = [];
|
|
36
|
+
for (const record of records) {
|
|
37
|
+
mutations.push({
|
|
38
|
+
localRecordId: record.id,
|
|
39
|
+
localSequence: record.sequence,
|
|
40
|
+
signedMutation: await createSignedMutation({
|
|
41
|
+
deviceCertificate,
|
|
42
|
+
devicePrivateKey,
|
|
43
|
+
mutation: record.mutation
|
|
44
|
+
})
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
exportedAt: timestamp,
|
|
49
|
+
format: PEER_MUTATION_BUNDLE_FORMAT,
|
|
50
|
+
mutations
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Imports verified peer mutations into the local mutation log.
|
|
55
|
+
* @param {object} args - Arguments.
|
|
56
|
+
* @param {import("./device-identity.js").SyncJsonWebKey} args.backendPublicKey - Backend public key for verifying peer certificates.
|
|
57
|
+
* @param {PeerMutationBundle} args.bundle - Peer bundle to import.
|
|
58
|
+
* @param {import("./local-mutation-log.js").default} args.mutationLog - Local mutation log.
|
|
59
|
+
* @param {Date} [args.now] - Verification time.
|
|
60
|
+
* @returns {Promise<{imported: {clientMutationId: string, idempotencyKey: string, localRecordId: string}[], rejected: {errorMessage: string, index: number}[], skipped: {clientMutationId: string, idempotencyKey: string, localRecordId: string, reason: "duplicate"}[]}>} - Import result.
|
|
61
|
+
*/
|
|
62
|
+
export async function importPeerMutationBundle({ backendPublicKey, bundle, mutationLog, now = new Date() }) {
|
|
63
|
+
const normalizedBundle = normalizePeerMutationBundle(bundle);
|
|
64
|
+
const existingRecords = await mutationLog.records();
|
|
65
|
+
const existingByIdempotencyKey = new Map(existingRecords.map((record) => [mutationIdempotencyKey({ mutation: record.mutation }), record]));
|
|
66
|
+
/** @type {{clientMutationId: string, idempotencyKey: string, localRecordId: string}[]} */
|
|
67
|
+
const imported = [];
|
|
68
|
+
/** @type {{errorMessage: string, index: number}[]} */
|
|
69
|
+
const rejected = [];
|
|
70
|
+
/** @type {{clientMutationId: string, idempotencyKey: string, localRecordId: string, reason: "duplicate"}[]} */
|
|
71
|
+
const skipped = [];
|
|
72
|
+
for (const [index, entry] of normalizedBundle.mutations.entries()) {
|
|
73
|
+
const mutation = await verifiedBundleMutation({ backendPublicKey, entry, index, now, rejected });
|
|
74
|
+
if (!mutation)
|
|
75
|
+
continue;
|
|
76
|
+
const idempotencyKey = mutationIdempotencyKey(entry.signedMutation);
|
|
77
|
+
const duplicateRecord = existingByIdempotencyKey.get(idempotencyKey);
|
|
78
|
+
if (duplicateRecord) {
|
|
79
|
+
skipped.push({
|
|
80
|
+
clientMutationId: mutation.clientMutationId,
|
|
81
|
+
idempotencyKey,
|
|
82
|
+
localRecordId: duplicateRecord.id,
|
|
83
|
+
reason: /** @type {"duplicate"} */ ("duplicate")
|
|
84
|
+
});
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const record = await mutationLog.append({ mutation });
|
|
88
|
+
const updated = await mutationLog.updateStatus({ id: record.id, status: "peer-applied" });
|
|
89
|
+
existingByIdempotencyKey.set(idempotencyKey, updated);
|
|
90
|
+
imported.push({ clientMutationId: mutation.clientMutationId, idempotencyKey, localRecordId: updated.id });
|
|
91
|
+
}
|
|
92
|
+
return { imported, rejected, skipped };
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Verifies a bundle mutation and records normal per-entry verification rejections.
|
|
96
|
+
* Storage writes intentionally happen outside this helper so storage failures escape import.
|
|
97
|
+
* @param {object} args - Arguments.
|
|
98
|
+
* @param {import("./device-identity.js").SyncJsonWebKey} args.backendPublicKey - Backend public key.
|
|
99
|
+
* @param {PeerMutationBundleEntry} args.entry - Bundle entry.
|
|
100
|
+
* @param {number} args.index - Entry index.
|
|
101
|
+
* @param {Date} args.now - Verification time.
|
|
102
|
+
* @param {{errorMessage: string, index: number}[]} args.rejected - Rejection accumulator.
|
|
103
|
+
* @returns {Promise<import("./device-identity.js").SyncMutation | null>} - Verified mutation, or null when rejected.
|
|
104
|
+
*/
|
|
105
|
+
async function verifiedBundleMutation({ backendPublicKey, entry, index, now, rejected }) {
|
|
106
|
+
try {
|
|
107
|
+
return await verifySignedMutation({ backendPublicKey, now, signedMutation: entry.signedMutation });
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
rejected.push({ errorMessage: errorMessage(error), index });
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Normalizes export status filters.
|
|
116
|
+
* @param {import("./local-mutation-log.js").LocalMutationStatus[] | undefined} statuses - Optional statuses.
|
|
117
|
+
* @returns {Set<import("./local-mutation-log.js").LocalMutationStatus>} - Status set.
|
|
118
|
+
*/
|
|
119
|
+
function normalizeExportStatuses(statuses) {
|
|
120
|
+
if (statuses === undefined)
|
|
121
|
+
return /** @type {Set<import("./local-mutation-log.js").LocalMutationStatus>} */ (new Set(EXPORTABLE_STATUSES));
|
|
122
|
+
if (!Array.isArray(statuses))
|
|
123
|
+
throw new Error("Expected peer mutation export statuses array");
|
|
124
|
+
const normalized = new Set();
|
|
125
|
+
for (const status of statuses) {
|
|
126
|
+
if (!EXPORTABLE_STATUSES.has(status))
|
|
127
|
+
throw new Error(`Unsupported peer mutation export status '${String(status)}'`);
|
|
128
|
+
normalized.add(status);
|
|
129
|
+
}
|
|
130
|
+
return /** @type {Set<import("./local-mutation-log.js").LocalMutationStatus>} */ (normalized);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Normalizes a peer mutation bundle.
|
|
134
|
+
* @param {PeerMutationBundle} bundle - Bundle value.
|
|
135
|
+
* @returns {PeerMutationBundle} - Normalized bundle.
|
|
136
|
+
*/
|
|
137
|
+
function normalizePeerMutationBundle(bundle) {
|
|
138
|
+
if (!bundle || typeof bundle !== "object" || Array.isArray(bundle))
|
|
139
|
+
throw new Error("Expected peer mutation bundle object");
|
|
140
|
+
if (bundle.format !== PEER_MUTATION_BUNDLE_FORMAT)
|
|
141
|
+
throw new Error(`Unsupported peer mutation bundle format '${String(bundle.format)}'`);
|
|
142
|
+
isoTimestamp(new Date(requiredString(bundle.exportedAt, "exportedAt")), "exportedAt");
|
|
143
|
+
if (!Array.isArray(bundle.mutations))
|
|
144
|
+
throw new Error("Expected peer mutation bundle mutations array");
|
|
145
|
+
return {
|
|
146
|
+
exportedAt: bundle.exportedAt,
|
|
147
|
+
format: bundle.format,
|
|
148
|
+
mutations: bundle.mutations.map((entry, index) => normalizePeerMutationBundleEntry(entry, index))
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Normalizes one peer mutation bundle entry.
|
|
153
|
+
* @param {PeerMutationBundleEntry} entry - Bundle entry.
|
|
154
|
+
* @param {number} index - Entry index.
|
|
155
|
+
* @returns {PeerMutationBundleEntry} - Normalized entry.
|
|
156
|
+
*/
|
|
157
|
+
function normalizePeerMutationBundleEntry(entry, index) {
|
|
158
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
159
|
+
throw new Error(`Expected peer mutation bundle entry ${index} object`);
|
|
160
|
+
if (!entry.signedMutation || typeof entry.signedMutation !== "object" || Array.isArray(entry.signedMutation))
|
|
161
|
+
throw new Error(`Expected peer mutation bundle entry ${index} signedMutation object`);
|
|
162
|
+
/** @type {PeerMutationBundleEntry} */
|
|
163
|
+
const normalized = {
|
|
164
|
+
signedMutation: entry.signedMutation
|
|
165
|
+
};
|
|
166
|
+
if (entry.localRecordId !== undefined)
|
|
167
|
+
normalized.localRecordId = requiredString(entry.localRecordId, `entry ${index} localRecordId`);
|
|
168
|
+
if (entry.localSequence !== undefined)
|
|
169
|
+
normalized.localSequence = positiveInteger(entry.localSequence, `entry ${index} localSequence`);
|
|
170
|
+
return normalized;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Requires a non-empty string.
|
|
174
|
+
* @param {unknown} value - Value.
|
|
175
|
+
* @param {string} label - Label.
|
|
176
|
+
* @returns {string} - String.
|
|
177
|
+
*/
|
|
178
|
+
function requiredString(value, label) {
|
|
179
|
+
if (typeof value !== "string" || value.length < 1)
|
|
180
|
+
throw new Error(`Expected peer mutation bundle ${label}`);
|
|
181
|
+
return value;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Requires a positive integer.
|
|
185
|
+
* @param {unknown} value - Value.
|
|
186
|
+
* @param {string} label - Label.
|
|
187
|
+
* @returns {number} - Integer.
|
|
188
|
+
*/
|
|
189
|
+
function positiveInteger(value, label) {
|
|
190
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 1)
|
|
191
|
+
throw new Error(`Expected peer mutation bundle ${label} positive integer`);
|
|
192
|
+
return value;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Returns an ISO timestamp from a valid Date.
|
|
196
|
+
* @param {Date} date - Date.
|
|
197
|
+
* @param {string} label - Label.
|
|
198
|
+
* @returns {string} - ISO timestamp.
|
|
199
|
+
*/
|
|
200
|
+
function isoTimestamp(date, label) {
|
|
201
|
+
if (!(date instanceof Date) || Number.isNaN(date.getTime()))
|
|
202
|
+
throw new Error(`Invalid peer mutation bundle ${label}`);
|
|
203
|
+
return date.toISOString();
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Returns a safe error message.
|
|
207
|
+
* @param {unknown} error - Error.
|
|
208
|
+
* @returns {string} - Message.
|
|
209
|
+
*/
|
|
210
|
+
function errorMessage(error) {
|
|
211
|
+
if (error instanceof Error)
|
|
212
|
+
return error.message;
|
|
213
|
+
return String(error);
|
|
214
|
+
}
|
|
215
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGVlci1tdXRhdGlvbi1idW5kbGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvc3luYy9wZWVyLW11dGF0aW9uLWJ1bmRsZS5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxZQUFZO0FBRVosT0FBTyxFQUFDLG9CQUFvQixFQUFFLHNCQUFzQixFQUFFLG9CQUFvQixFQUFDLE1BQU0sc0JBQXNCLENBQUE7QUFFdkcsTUFBTSwyQkFBMkIsR0FBRyx3Q0FBd0MsQ0FBQTtBQUM1RSxNQUFNLG1CQUFtQixHQUFHLElBQUksR0FBRyxDQUFDLENBQUMsU0FBUyxFQUFFLGlCQUFpQixFQUFFLFVBQVUsQ0FBQyxDQUFDLENBQUE7QUFFL0U7Ozs7OztHQU1HO0FBRUg7Ozs7OztHQU1HO0FBRUg7Ozs7Ozs7OztHQVNHO0FBQ0gsTUFBTSxDQUFDLEtBQUssVUFBVSx3QkFBd0IsQ0FBQyxFQUFDLGlCQUFpQixFQUFFLGdCQUFnQixFQUFFLFdBQVcsRUFBRSxHQUFHLEdBQUcsR0FBRyxFQUFFLENBQUMsSUFBSSxJQUFJLEVBQUUsRUFBRSxRQUFRLEVBQUM7SUFDakksTUFBTSxnQkFBZ0IsR0FBRyx1QkFBdUIsQ0FBQyxRQUFRLENBQUMsQ0FBQTtJQUMxRCxNQUFNLFNBQVMsR0FBRyxZQUFZLENBQUMsR0FBRyxFQUFFLEVBQUUsWUFBWSxDQUFDLENBQUE7SUFDbkQsTUFBTSxPQUFPLEdBQUcsQ0FBQyxNQUFNLFdBQVcsQ0FBQyxPQUFPLEVBQUUsQ0FBQztTQUMxQyxNQUFNLENBQUMsQ0FBQyxNQUFNLEVBQUUsRUFBRSxDQUFDLGdCQUFnQixDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUM7U0FDdkQsSUFBSSxDQUFDLENBQUMsSUFBSSxFQUFFLEtBQUssRUFBRSxFQUFFLENBQUMsSUFBSSxDQUFDLFFBQVEsR0FBRyxLQUFLLENBQUMsUUFBUSxDQUFDLENBQUE7SUFDeEQsTUFBTSxTQUFTLEdBQUcsRUFBRSxDQUFB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T0FBTyxLQUFLLENBQUE7QUFDZCxDQUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxTQUFTLFlBQVksQ0FBQyxJQUFJLEVBQUUsS0FBSztJQUMvQixJQUFJLENBQUMsQ0FBQyxJQUFJLFlBQVksSUFBSSxDQUFDLElBQUksTUFBTSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7UUFBRSxNQUFNLElBQUksS0FBSyxDQUFDLGdDQUFnQyxLQUFLLEVBQUUsQ0FBQyxDQUFBO0lBRXJILE9BQU8sSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFBO0FBQzNCLENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsU0FBUyxZQUFZLENBQUMsS0FBSztJQUN6QixJQUFJLEtBQUssWUFBWSxLQUFLO1FBQUUsT0FBTyxLQUFLLENBQUMsT0FBTyxDQUFBO0lBRWhELE9BQU8sTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFBO0FBQ3RCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBAdHMtY2hlY2tcblxuaW1wb3J0IHtjcmVhdGVTaWduZWRNdXRhdGlvbiwgbXV0YXRpb25JZGVtcG90ZW5jeUtleSwgdmVyaWZ5U2lnbmVkTXV0YXRpb259IGZyb20gXCIuL2RldmljZS1pZGVudGl0eS5qc1wiXG5cbmNvbnN0IFBFRVJfTVVUQVRJT05fQlVORExFX0ZPUk1BVCA9IFwidmVsb2Npb3VzLnN5bmMucGVlci1tdXRhdGlvbi1idW5kbGUudjFcIlxuY29uc3QgRVhQT1JUQUJMRV9TVEFUVVNFUyA9IG5ldyBTZXQoW1wicGVuZGluZ1wiLCBcImFwcGxpZWQtbG9jYWxseVwiLCBcImNvbmZsaWN0XCJdKVxuXG4vKipcbiAqIFBlZXIgbXV0YXRpb24gYnVuZGxlIGV4cG9ydGVkIGJ5IGEgZGV2aWNlIGZvciBvZmZsaW5lL1AyUCB0cmFuc2Zlci5cbiAqIEB0eXBlZGVmIHtvYmplY3R9IFBlZXJNdXRhdGlvbkJ1bmRsZVxuICogQHByb3BlcnR5IHtzdHJpbmd9IGV4cG9ydGVkQXQgLSBJU08gdGltZXN0YW1wIHdoZW4gdGhlIGJ1bmRsZSB3YXMgZXhwb3J0ZWQuXG4gKiBAcHJvcGVydHkge1widmVsb2Npb3VzLnN5bmMucGVlci1tdXRhdGlvbi1idW5kbGUudjFcIn0gZm9ybWF0IC0gQnVuZGxlIGZvcm1hdCBpZGVudGlmaWVyLlxuICogQHByb3BlcnR5IHtQZWVyTXV0YXRpb25CdW5kbGVFbnRyeVtdfSBtdXRhdGlvbnMgLSBTaWduZWQgbXV0YXRpb25zIGluIGxvY2FsIHNlcXVlbmNlIG9yZGVyLlxuICovXG5cbi8qKlxuICogT25lIHBlZXIgbXV0YXRpb24gYnVuZGxlIGVudHJ5LlxuICogQHR5cGVkZWYge29iamVjdH0gUGVlck11dGF0aW9uQnVuZGxlRW50cnlcbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBbbG9jYWxSZWNvcmRJZF0gLSBFeHBvcnRpbmcgZGV2aWNlJ3MgbG9jYWwgbXV0YXRpb24gcmVjb3JkIGlkLlxuICogQHByb3BlcnR5IHtudW1iZXJ9IFtsb2NhbFNlcXVlbmNlXSAtIEV4cG9ydGluZyBkZXZpY2UncyBsb2NhbCBtdXRhdGlvbiBzZXF1ZW5jZS5cbiAqIEBwcm9wZXJ0eSB7aW1wb3J0KFwiLi9kZXZpY2UtaWRlbnRpdHkuanNcIikuU2lnbmVkU3luY011dGF0aW9ufSBzaWduZWRNdXRhdGlvbiAtIERldmljZS1zaWduZWQgbXV0YXRpb24gZW52ZWxvcGUuXG4gKi9cblxuLyoqXG4gKiBFeHBvcnRzIGxvY2FsIG5vbi10ZXJtaW5hbCBtdXRhdGlvbnMgYXMgYSBzaWduZWQgcGVlci10cmFuc2ZlciBidW5kbGUuXG4gKiBAcGFyYW0ge29iamVjdH0gYXJncyAtIEFyZ3VtZW50cy5cbiAqIEBwYXJhbSB7aW1wb3J0KFwiLi9kZXZpY2UtaWRlbnRpdHkuanNcIikuRGV2aWNlQ2VydGlmaWNhdGV9IGFyZ3MuZGV2aWNlQ2VydGlmaWNhdGUgLSBEZXZpY2UgY2VydGlmaWNhdGUgZm9yIHNpZ25pbmcgcmVjb3Jkcy5cbiAqIEBwYXJhbSB7aW1wb3J0KFwiLi9kZXZpY2UtaWRlbnRpdHkuanNcIikuU3luY0pzb25XZWJLZXl9IGFyZ3MuZGV2aWNlUHJpdmF0ZUtleSAtIERldmljZSBwcml2YXRlIGtleSBmb3Igc2lnbmluZyByZWNvcmRzLlxuICogQHBhcmFtIHtpbXBvcnQoXCIuL2xvY2FsLW11dGF0aW9uLWxvZy5qc1wiKS5kZWZhdWx0fSBhcmdzLm11dGF0aW9uTG9nIC0gTG9jYWwgbXV0YXRpb24gbG9nLlxuICogQHBhcmFtIHsoKSA9PiBEYXRlfSBbYXJncy5ub3ddIC0gRXhwb3J0IGNsb2NrLlxuICogQHBhcmFtIHtpbXBvcnQoXCIuL2xvY2FsLW11dGF0aW9uLWxvZy5qc1wiKS5Mb2NhbE11dGF0aW9uU3RhdHVzW119IFthcmdzLnN0YXR1c2VzXSAtIFN0YXR1c2VzIHRvIGV4cG9ydC5cbiAqIEByZXR1cm5zIHtQcm9taXNlPFBlZXJNdXRhdGlvbkJ1bmRsZT59IC0gU2lnbmVkIHBlZXIgbXV0YXRpb24gYnVuZGxlLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZXhwb3J0UGVlck11dGF0aW9uQnVuZGxlKHtkZXZpY2VDZXJ0aWZpY2F0ZSwgZGV2aWNlUHJpdmF0ZUtleSwgbXV0YXRpb25Mb2csIG5vdyA9ICgpID0+IG5ldyBEYXRlKCksIHN0YXR1c2VzfSkge1xuICBjb25zdCBzZWxlY3RlZFN0YXR1c2VzID0gbm9ybWFsaXplRXhwb3J0U3RhdHVzZXMoc3RhdHVzZXMpXG4gIGNvbnN0IHRpbWVzdGFtcCA9IGlzb1RpbWVzdGFtcChub3coKSwgXCJleHBvcnRlZEF0XCIpXG4gIGNvbnN0IHJlY29yZHMgPSAoYXdhaXQgbXV0YXRpb25Mb2cucmVjb3JkcygpKVxuICAgIC5maWx0ZXIoKHJlY29yZCkgPT4gc2VsZWN0ZWRTdGF0dXNlcy5oYXMocmVjb3JkLnN0YXR1cykpXG4gICAgLnNvcnQoKGxlZnQsIHJpZ2h0KSA9PiBsZWZ0LnNlcXVlbmNlIC0gcmlnaHQuc2VxdWVuY2UpXG4gIGNvbnN0IG11dGF0aW9ucyA9IFtdXG5cbiAgZm9yIChjb25zdCByZWNvcmQgb2YgcmVjb3Jkcykge1xuICAgIG11dGF0aW9ucy5wdXNoKHtcbiAgICAgIGxvY2FsUmVjb3JkSWQ6IHJlY29yZC5pZCxcbiAgICAgIGxvY2FsU2VxdWVuY2U6IHJlY29yZC5zZXF1ZW5jZSxcbiAgICAgIHNpZ25lZE11dGF0aW9uOiBhd2FpdCBjcmVhdGVTaWduZWRNdXRhdGlvbih7XG4gICAgICAgIGRldmljZUNlcnRpZmljYXRlLFxuICAgICAgICBkZXZpY2VQcml2YXRlS2V5LFxuICAgICAgICBtdXRhdGlvbjogcmVjb3JkLm11dGF0aW9uXG4gICAgICB9KVxuICAgIH0pXG4gIH1cblxuICByZXR1cm4ge1xuICAgIGV4cG9ydGVkQXQ6IHRpbWVzdGFtcCxcbiAgICBmb3JtYXQ6IFBFRVJfTVVUQVRJT05fQlVORExFX0ZPUk1BVCxcbiAgICBtdXRhdGlvbnNcbiAgfVxufVxuXG4vKipcbiAqIEltcG9ydHMgdmVyaWZpZWQgcGVlciBtdXRhdGlvbnMgaW50byB0aGUgbG9jYWwgbXV0YXRpb24gbG9nLlxuICogQHBhcmFtIHtvYmplY3R9IGFyZ3MgLSBBcmd1bWVudHMuXG4gKiBAcGFyYW0ge2ltcG9ydChcIi4vZGV2aWNlLWlkZW50aXR5LmpzXCIpLlN5bmNKc29uV2ViS2V5fSBhcmdzLmJhY2tlbmRQdWJsaWNLZXkgLSBCYWNrZW5kIHB1YmxpYyBrZXkgZm9yIHZlcmlmeWluZyBwZWVyIGNlcnRpZmljYXRlcy5cbiAqIEBwYXJhbSB7UGVlck11dGF0aW9uQnVuZGxlfSBhcmdzLmJ1bmRsZSAtIFBlZXIgYnVuZGxlIHRvIGltcG9ydC5cbiAqIEBwYXJhbSB7aW1wb3J0KFwiLi9sb2NhbC1tdXRhdGlvbi1sb2cuanNcIikuZGVmYXVsdH0gYXJncy5tdXRhdGlvbkxvZyAtIExvY2FsIG11dGF0aW9uIGxvZy5cbiAqIEBwYXJhbSB7RGF0ZX0gW2FyZ3Mubm93XSAtIFZlcmlmaWNhdGlvbiB0aW1lLlxuICogQHJldHVybnMge1Byb21pc2U8e2ltcG9ydGVkOiB7Y2xpZW50TXV0YXRpb25JZDogc3RyaW5nLCBpZGVtcG90ZW5jeUtleTogc3RyaW5nLCBsb2NhbFJlY29yZElkOiBzdHJpbmd9W10sIHJlamVjdGVkOiB7ZXJyb3JNZXNzYWdlOiBzdHJpbmcsIGluZGV4OiBudW1iZXJ9W10sIHNraXBwZWQ6IHtjbGllbnRNdXRhdGlvbklkOiBzdHJpbmcsIGlkZW1wb3RlbmN5S2V5OiBzdHJpbmcsIGxvY2FsUmVjb3JkSWQ6IHN0cmluZywgcmVhc29uOiBcImR1cGxpY2F0ZVwifVtdfT59IC0gSW1wb3J0IHJlc3VsdC5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGltcG9ydFBlZXJNdXRhdGlvbkJ1bmRsZSh7YmFja2VuZFB1YmxpY0tleSwgYnVuZGxlLCBtdXRhdGlvbkxvZywgbm93ID0gbmV3IERhdGUoKX0pIHtcbiAgY29uc3Qgbm9ybWFsaXplZEJ1bmRsZSA9IG5vcm1hbGl6ZVBlZXJNdXRhdGlvbkJ1bmRsZShidW5kbGUpXG4gIGNvbnN0IGV4aXN0aW5nUmVjb3JkcyA9IGF3YWl0IG11dGF0aW9uTG9nLnJlY29yZHMoKVxuICBjb25zdCBleGlzdGluZ0J5SWRlbXBvdGVuY3lLZXkgPSBuZXcgTWFwKGV4aXN0aW5nUmVjb3Jkcy5tYXAoKHJlY29yZCkgPT4gW211dGF0aW9uSWRlbXBvdGVuY3lLZXkoe211dGF0aW9uOiByZWNvcmQubXV0YXRpb259KSwgcmVjb3JkXSkpXG4gIC8qKiBAdHlwZSB7e2NsaWVudE11dGF0aW9uSWQ6IHN0cmluZywgaWRlbXBvdGVuY3lLZXk6IHN0cmluZywgbG9jYWxSZWNvcmRJZDogc3RyaW5nfVtdfSAqL1xuICBjb25zdCBpbXBvcnRlZCA9IFtdXG4gIC8qKiBAdHlwZSB7e2Vycm9yTWVzc2FnZTogc3RyaW5nLCBpbmRleDogbnVtYmVyfVtdfSAqL1xuICBjb25zdCByZWplY3RlZCA9IFtdXG4gIC8qKiBAdHlwZSB7e2NsaWVudE11dGF0aW9uSWQ6IHN0cmluZywgaWRlbXBvdGVuY3lLZXk6IHN0cmluZywgbG9jYWxSZWNvcmRJZDogc3RyaW5nLCByZWFzb246IFwiZHVwbGljYXRlXCJ9W119ICovXG4gIGNvbnN0IHNraXBwZWQgPSBbXVxuXG4gIGZvciAoY29uc3QgW2luZGV4LCBlbnRyeV0gb2Ygbm9ybWFsaXplZEJ1bmRsZS5tdXRhdGlvbnMuZW50cmllcygpKSB7XG4gICAgY29uc3QgbXV0YXRpb24gPSBhd2FpdCB2ZXJpZmllZEJ1bmRsZU11dGF0aW9uKHtiYWNrZW5kUHVibGljS2V5LCBlbnRyeSwgaW5kZXgsIG5vdywgcmVqZWN0ZWR9KVxuXG4gICAgaWYgKCFtdXRhdGlvbikgY29udGludWVcblxuICAgIGNvbnN0IGlkZW1wb3RlbmN5S2V5ID0gbXV0YXRpb25JZGVtcG90ZW5jeUtleShlbnRyeS5zaWduZWRNdXRhdGlvbilcbiAgICBjb25zdCBkdXBsaWNhdGVSZWNvcmQgPSBleGlzdGluZ0J5SWRlbXBvdGVuY3lLZXkuZ2V0KGlkZW1wb3RlbmN5S2V5KVxuXG4gICAgaWYgKGR1cGxpY2F0ZVJlY29yZCkge1xuICAgICAgc2tpcHBlZC5wdXNoKHtcbiAgICAgICAgY2xpZW50TXV0YXRpb25JZDogbXV0YXRpb24uY2xpZW50TXV0YXRpb25JZCxcbiAgICAgICAgaWRlbXBvdGVuY3lLZXksXG4gICAgICAgIGxvY2FsUmVjb3JkSWQ6IGR1cGxpY2F0ZVJlY29yZC5pZCxcbiAgICAgICAgcmVhc29uOiAvKiogQHR5cGUge1wiZHVwbGljYXRlXCJ9ICovIChcImR1cGxpY2F0ZVwiKVxuICAgICAgfSlcbiAgICAgIGNvbnRpbnVlXG4gICAgfVxuXG4gICAgY29uc3QgcmVjb3JkID0gYXdhaXQgbXV0YXRpb25Mb2cuYXBwZW5kKHttdXRhdGlvbn0pXG4gICAgY29uc3QgdXBkYXRlZCA9IGF3YWl0IG11dGF0aW9uTG9nLnVwZGF0ZVN0YXR1cyh7aWQ6IHJlY29yZC5pZCwgc3RhdHVzOiBcInBlZXItYXBwbGllZFwifSlcbiAgICBleGlzdGluZ0J5SWRlbXBvdGVuY3lLZXkuc2V0KGlkZW1wb3RlbmN5S2V5LCB1cGRhdGVkKVxuICAgIGltcG9ydGVkLnB1c2goe2NsaWVudE11dGF0aW9uSWQ6IG11dGF0aW9uLmNsaWVudE11dGF0aW9uSWQsIGlkZW1wb3RlbmN5S2V5LCBsb2NhbFJlY29yZElkOiB1cGRhdGVkLmlkfSlcbiAgfVxuXG4gIHJldHVybiB7aW1wb3J0ZWQsIHJlamVjdGVkLCBza2lwcGVkfVxufVxuXG4vKipcbiAqIFZlcmlmaWVzIGEgYnVuZGxlIG11dGF0aW9uIGFuZCByZWNvcmRzIG5vcm1hbCBwZXItZW50cnkgdmVyaWZpY2F0aW9uIHJlamVjdGlvbnMuXG4gKiBTdG9yYWdlIHdyaXRlcyBpbnRlbnRpb25hbGx5IGhhcHBlbiBvdXRzaWRlIHRoaXMgaGVscGVyIHNvIHN0b3JhZ2UgZmFpbHVyZXMgZXNjYXBlIGltcG9ydC5cbiAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gQXJndW1lbnRzLlxuICogQHBhcmFtIHtpbXBvcnQoXCIuL2RldmljZS1pZGVudGl0eS5qc1wiKS5TeW5jSnNvbldlYktleX0gYXJncy5iYWNrZW5kUHVibGljS2V5IC0gQmFja2VuZCBwdWJsaWMga2V5LlxuICogQHBhcmFtIHtQZWVyTXV0YXRpb25CdW5kbGVFbnRyeX0gYXJncy5lbnRyeSAtIEJ1bmRsZSBlbnRyeS5cbiAqIEBwYXJhbSB7bnVtYmVyfSBhcmdzLmluZGV4IC0gRW50cnkgaW5kZXguXG4gKiBAcGFyYW0ge0RhdGV9IGFyZ3Mubm93IC0gVmVyaWZpY2F0aW9uIHRpbWUuXG4gKiBAcGFyYW0ge3tlcnJvck1lc3NhZ2U6IHN0cmluZywgaW5kZXg6IG51bWJlcn1bXX0gYXJncy5yZWplY3RlZCAtIFJlamVjdGlvbiBhY2N1bXVsYXRvci5cbiAqIEByZXR1cm5zIHtQcm9taXNlPGltcG9ydChcIi4vZGV2aWNlLWlkZW50aXR5LmpzXCIpLlN5bmNNdXRhdGlvbiB8IG51bGw+fSAtIFZlcmlmaWVkIG11dGF0aW9uLCBvciBudWxsIHdoZW4gcmVqZWN0ZWQuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIHZlcmlmaWVkQnVuZGxlTXV0YXRpb24oe2JhY2tlbmRQdWJsaWNLZXksIGVudHJ5LCBpbmRleCwgbm93LCByZWplY3RlZH0pIHtcbiAgdHJ5IHtcbiAgICByZXR1cm4gYXdhaXQgdmVyaWZ5U2lnbmVkTXV0YXRpb24oe2JhY2tlbmRQdWJsaWNLZXksIG5vdywgc2lnbmVkTXV0YXRpb246IGVudHJ5LnNpZ25lZE11dGF0aW9ufSlcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICByZWplY3RlZC5wdXNoKHtlcnJvck1lc3NhZ2U6IGVycm9yTWVzc2FnZShlcnJvciksIGluZGV4fSlcblxuICAgIHJldHVybiBudWxsXG4gIH1cbn1cblxuLyoqXG4gKiBOb3JtYWxpemVzIGV4cG9ydCBzdGF0dXMgZmlsdGVycy5cbiAqIEBwYXJhbSB7aW1wb3J0KFwiLi9sb2NhbC1tdXRhdGlvbi1sb2cuanNcIikuTG9jYWxNdXRhdGlvblN0YXR1c1tdIHwgdW5kZWZpbmVkfSBzdGF0dXNlcyAtIE9wdGlvbmFsIHN0YXR1c2VzLlxuICogQHJldHVybnMge1NldDxpbXBvcnQoXCIuL2xvY2FsLW11dGF0aW9uLWxvZy5qc1wiKS5Mb2NhbE11dGF0aW9uU3RhdHVzPn0gLSBTdGF0dXMgc2V0LlxuICovXG5mdW5jdGlvbiBub3JtYWxpemVFeHBvcnRTdGF0dXNlcyhzdGF0dXNlcykge1xuICBpZiAoc3RhdHVzZXMgPT09IHVuZGVmaW5lZCkgcmV0dXJuIC8qKiBAdHlwZSB7U2V0PGltcG9ydChcIi4vbG9jYWwtbXV0YXRpb24tbG9nLmpzXCIpLkxvY2FsTXV0YXRpb25TdGF0dXM+fSAqLyAobmV3IFNldChFWFBPUlRBQkxFX1NUQVRVU0VTKSlcbiAgaWYgKCFBcnJheS5pc0FycmF5KHN0YXR1c2VzKSkgdGhyb3cgbmV3IEVycm9yKFwiRXhwZWN0ZWQgcGVlciBtdXRhdGlvbiBleHBvcnQgc3RhdHVzZXMgYXJyYXlcIilcblxuICBjb25zdCBub3JtYWxpemVkID0gbmV3IFNldCgpXG5cbiAgZm9yIChjb25zdCBzdGF0dXMgb2Ygc3RhdHVzZXMpIHtcbiAgICBpZiAoIUVYUE9SVEFCTEVfU1RBVFVTRVMuaGFzKHN0YXR1cykpIHRocm93IG5ldyBFcnJvcihgVW5zdXBwb3J0ZWQgcGVlciBtdXRhdGlvbiBleHBvcnQgc3RhdHVzICcke1N0cmluZyhzdGF0dXMpfSdgKVxuICAgIG5vcm1hbGl6ZWQuYWRkKHN0YXR1cylcbiAgfVxuXG4gIHJldHVybiAvKiogQHR5cGUge1NldDxpbXBvcnQoXCIuL2xvY2FsLW11dGF0aW9uLWxvZy5qc1wiKS5Mb2NhbE11dGF0aW9uU3RhdHVzPn0gKi8gKG5vcm1hbGl6ZWQpXG59XG5cbi8qKlxuICogTm9ybWFsaXplcyBhIHBlZXIgbXV0YXRpb24gYnVuZGxlLlxuICogQHBhcmFtIHtQZWVyTXV0YXRpb25CdW5kbGV9IGJ1bmRsZSAtIEJ1bmRsZSB2YWx1ZS5cbiAqIEByZXR1cm5zIHtQZWVyTXV0YXRpb25CdW5kbGV9IC0gTm9ybWFsaXplZCBidW5kbGUuXG4gKi9cbmZ1bmN0aW9uIG5vcm1hbGl6ZVBlZXJNdXRhdGlvbkJ1bmRsZShidW5kbGUpIHtcbiAgaWYgKCFidW5kbGUgfHwgdHlwZW9mIGJ1bmRsZSAhPT0gXCJvYmplY3RcIiB8fCBBcnJheS5pc0FycmF5KGJ1bmRsZSkpIHRocm93IG5ldyBFcnJvcihcIkV4cGVjdGVkIHBlZXIgbXV0YXRpb24gYnVuZGxlIG9iamVjdFwiKVxuICBpZiAoYnVuZGxlLmZvcm1hdCAhPT0gUEVFUl9NVVRBVElPTl9CVU5ETEVfRk9STUFUKSB0aHJvdyBuZXcgRXJyb3IoYFVuc3VwcG9ydGVkIHBlZXIgbXV0YXRpb24gYnVuZGxlIGZvcm1hdCAnJHtTdHJpbmcoYnVuZGxlLmZvcm1hdCl9J2ApXG4gIGlzb1RpbWVzdGFtcChuZXcgRGF0ZShyZXF1aXJlZFN0cmluZyhidW5kbGUuZXhwb3J0ZWRBdCwgXCJleHBvcnRlZEF0XCIpKSwgXCJleHBvcnRlZEF0XCIpXG4gIGlmICghQXJyYXkuaXNBcnJheShidW5kbGUubXV0YXRpb25zKSkgdGhyb3cgbmV3IEVycm9yKFwiRXhwZWN0ZWQgcGVlciBtdXRhdGlvbiBidW5kbGUgbXV0YXRpb25zIGFycmF5XCIpXG5cbiAgcmV0dXJuIHtcbiAgICBleHBvcnRlZEF0OiBidW5kbGUuZXhwb3J0ZWRBdCxcbiAgICBmb3JtYXQ6IGJ1bmRsZS5mb3JtYXQsXG4gICAgbXV0YXRpb25zOiBidW5kbGUubXV0YXRpb25zLm1hcCgoZW50cnksIGluZGV4KSA9PiBub3JtYWxpemVQZWVyTXV0YXRpb25CdW5kbGVFbnRyeShlbnRyeSwgaW5kZXgpKVxuICB9XG59XG5cbi8qKlxuICogTm9ybWFsaXplcyBvbmUgcGVlciBtdXRhdGlvbiBidW5kbGUgZW50cnkuXG4gKiBAcGFyYW0ge1BlZXJNdXRhdGlvbkJ1bmRsZUVudHJ5fSBlbnRyeSAtIEJ1bmRsZSBlbnRyeS5cbiAqIEBwYXJhbSB7bnVtYmVyfSBpbmRleCAtIEVudHJ5IGluZGV4LlxuICogQHJldHVybnMge1BlZXJNdXRhdGlvbkJ1bmRsZUVudHJ5fSAtIE5vcm1hbGl6ZWQgZW50cnkuXG4gKi9cbmZ1bmN0aW9uIG5vcm1hbGl6ZVBlZXJNdXRhdGlvbkJ1bmRsZUVudHJ5KGVudHJ5LCBpbmRleCkge1xuICBpZiAoIWVudHJ5IHx8IHR5cGVvZiBlbnRyeSAhPT0gXCJvYmplY3RcIiB8fCBBcnJheS5pc0FycmF5KGVudHJ5KSkgdGhyb3cgbmV3IEVycm9yKGBFeHBlY3RlZCBwZWVyIG11dGF0aW9uIGJ1bmRsZSBlbnRyeSAke2luZGV4fSBvYmplY3RgKVxuICBpZiAoIWVudHJ5LnNpZ25lZE11dGF0aW9uIHx8IHR5cGVvZiBlbnRyeS5zaWduZWRNdXRhdGlvbiAhPT0gXCJvYmplY3RcIiB8fCBBcnJheS5pc0FycmF5KGVudHJ5LnNpZ25lZE11dGF0aW9uKSkgdGhyb3cgbmV3IEVycm9yKGBFeHBlY3RlZCBwZWVyIG11dGF0aW9uIGJ1bmRsZSBlbnRyeSAke2luZGV4fSBzaWduZWRNdXRhdGlvbiBvYmplY3RgKVxuXG4gIC8qKiBAdHlwZSB7UGVlck11dGF0aW9uQnVuZGxlRW50cnl9ICovXG4gIGNvbnN0IG5vcm1hbGl6ZWQgPSB7XG4gICAgc2lnbmVkTXV0YXRpb246IGVudHJ5LnNpZ25lZE11dGF0aW9uXG4gIH1cblxuICBpZiAoZW50cnkubG9jYWxSZWNvcmRJZCAhPT0gdW5kZWZpbmVkKSBub3JtYWxpemVkLmxvY2FsUmVjb3JkSWQgPSByZXF1aXJlZFN0cmluZyhlbnRyeS5sb2NhbFJlY29yZElkLCBgZW50cnkgJHtpbmRleH0gbG9jYWxSZWNvcmRJZGApXG4gIGlmIChlbnRyeS5sb2NhbFNlcXVlbmNlICE9PSB1bmRlZmluZWQpIG5vcm1hbGl6ZWQubG9jYWxTZXF1ZW5jZSA9IHBvc2l0aXZlSW50ZWdlcihlbnRyeS5sb2NhbFNlcXVlbmNlLCBgZW50cnkgJHtpbmRleH0gbG9jYWxTZXF1ZW5jZWApXG5cbiAgcmV0dXJuIG5vcm1hbGl6ZWRcbn1cblxuLyoqXG4gKiBSZXF1aXJlcyBhIG5vbi1lbXB0eSBzdHJpbmcuXG4gKiBAcGFyYW0ge3Vua25vd259IHZhbHVlIC0gVmFsdWUuXG4gKiBAcGFyYW0ge3N0cmluZ30gbGFiZWwgLSBMYWJlbC5cbiAqIEByZXR1cm5zIHtzdHJpbmd9IC0gU3RyaW5nLlxuICovXG5mdW5jdGlvbiByZXF1aXJlZFN0cmluZyh2YWx1ZSwgbGFiZWwpIHtcbiAgaWYgKHR5cGVvZiB2YWx1ZSAhPT0gXCJzdHJpbmdcIiB8fCB2YWx1ZS5sZW5ndGggPCAxKSB0aHJvdyBuZXcgRXJyb3IoYEV4cGVjdGVkIHBlZXIgbXV0YXRpb24gYnVuZGxlICR7bGFiZWx9YClcblxuICByZXR1cm4gdmFsdWVcbn1cblxuLyoqXG4gKiBSZXF1aXJlcyBhIHBvc2l0aXZlIGludGVnZXIuXG4gKiBAcGFyYW0ge3Vua25vd259IHZhbHVlIC0gVmFsdWUuXG4gKiBAcGFyYW0ge3N0cmluZ30gbGFiZWwgLSBMYWJlbC5cbiAqIEByZXR1cm5zIHtudW1iZXJ9IC0gSW50ZWdlci5cbiAqL1xuZnVuY3Rpb24gcG9zaXRpdmVJbnRlZ2VyKHZhbHVlLCBsYWJlbCkge1xuICBpZiAodHlwZW9mIHZhbHVlICE9PSBcIm51bWJlclwiIHx8ICFOdW1iZXIuaXNJbnRlZ2VyKHZhbHVlKSB8fCB2YWx1ZSA8IDEpIHRocm93IG5ldyBFcnJvcihgRXhwZWN0ZWQgcGVlciBtdXRhdGlvbiBidW5kbGUgJHtsYWJlbH0gcG9zaXRpdmUgaW50ZWdlcmApXG5cbiAgcmV0dXJuIHZhbHVlXG59XG5cbi8qKlxuICogUmV0dXJucyBhbiBJU08gdGltZXN0YW1wIGZyb20gYSB2YWxpZCBEYXRlLlxuICogQHBhcmFtIHtEYXRlfSBkYXRlIC0gRGF0ZS5cbiAqIEBwYXJhbSB7c3RyaW5nfSBsYWJlbCAtIExhYmVsLlxuICogQHJldHVybnMge3N0cmluZ30gLSBJU08gdGltZXN0YW1wLlxuICovXG5mdW5jdGlvbiBpc29UaW1lc3RhbXAoZGF0ZSwgbGFiZWwpIHtcbiAgaWYgKCEoZGF0ZSBpbnN0YW5jZW9mIERhdGUpIHx8IE51bWJlci5pc05hTihkYXRlLmdldFRpbWUoKSkpIHRocm93IG5ldyBFcnJvcihgSW52YWxpZCBwZWVyIG11dGF0aW9uIGJ1bmRsZSAke2xhYmVsfWApXG5cbiAgcmV0dXJuIGRhdGUudG9JU09TdHJpbmcoKVxufVxuXG4vKipcbiAqIFJldHVybnMgYSBzYWZlIGVycm9yIG1lc3NhZ2UuXG4gKiBAcGFyYW0ge3Vua25vd259IGVycm9yIC0gRXJyb3IuXG4gKiBAcmV0dXJucyB7c3RyaW5nfSAtIE1lc3NhZ2UuXG4gKi9cbmZ1bmN0aW9uIGVycm9yTWVzc2FnZShlcnJvcikge1xuICBpZiAoZXJyb3IgaW5zdGFuY2VvZiBFcnJvcikgcmV0dXJuIGVycm9yLm1lc3NhZ2VcblxuICByZXR1cm4gU3RyaW5nKGVycm9yKVxufVxuIl19
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared server change-feed store for a configuration.
|
|
3
|
+
* @param {import("../configuration.js").default} configuration - Configuration.
|
|
4
|
+
* @returns {ServerChangeFeedStore} - Store.
|
|
5
|
+
*/
|
|
6
|
+
export function serverChangeFeedStoreForConfiguration(configuration: import("../configuration.js").default): ServerChangeFeedStore;
|
|
7
|
+
export default class ServerChangeFeedStore {
|
|
8
|
+
/**
|
|
9
|
+
* Runs constructor.
|
|
10
|
+
* @param {object} args - Options.
|
|
11
|
+
* @param {import("../configuration.js").default} args.configuration - Configuration.
|
|
12
|
+
* @param {string} [args.databaseIdentifier] - Database identifier.
|
|
13
|
+
* @param {number} [args.retentionSize] - Number of feed entries to retain.
|
|
14
|
+
*/
|
|
15
|
+
constructor({ configuration, databaseIdentifier, retentionSize }: {
|
|
16
|
+
configuration: import("../configuration.js").default;
|
|
17
|
+
databaseIdentifier?: string | undefined;
|
|
18
|
+
retentionSize?: number | undefined;
|
|
19
|
+
});
|
|
20
|
+
configuration: import("../configuration.js").default;
|
|
21
|
+
databaseIdentifier: string;
|
|
22
|
+
retentionSize: number;
|
|
23
|
+
/** @type {ServerChangeFeedEntry[]} */
|
|
24
|
+
_memoryChanges: ServerChangeFeedEntry[];
|
|
25
|
+
_memorySequence: number;
|
|
26
|
+
_isReady: boolean;
|
|
27
|
+
_readyPromise: Promise<void> | null;
|
|
28
|
+
/**
|
|
29
|
+
* Ensures the backing table exists.
|
|
30
|
+
* @returns {Promise<void>} - Resolves when ready.
|
|
31
|
+
*/
|
|
32
|
+
ensureReady(): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Appends a change and assigns the next server sequence.
|
|
35
|
+
* @param {Omit<ServerChangeFeedEntry, "createdAt" | "id" | "serverSequence"> & {createdAt?: string, id?: string}} change - Change payload.
|
|
36
|
+
* @returns {Promise<ServerChangeFeedEntry>} - Persisted change.
|
|
37
|
+
*/
|
|
38
|
+
append(change: Omit<ServerChangeFeedEntry, "createdAt" | "id" | "serverSequence"> & {
|
|
39
|
+
createdAt?: string;
|
|
40
|
+
id?: string;
|
|
41
|
+
}): Promise<ServerChangeFeedEntry>;
|
|
42
|
+
/**
|
|
43
|
+
* Returns current latest server sequence.
|
|
44
|
+
* @returns {Promise<number>} - Latest sequence.
|
|
45
|
+
*/
|
|
46
|
+
latestSequence(): Promise<number>;
|
|
47
|
+
/**
|
|
48
|
+
* Returns oldest retained server sequence.
|
|
49
|
+
* @returns {Promise<number | null>} - Oldest retained sequence.
|
|
50
|
+
*/
|
|
51
|
+
oldestSequence(): Promise<number | null>;
|
|
52
|
+
/**
|
|
53
|
+
* Returns ordered changes after a cursor.
|
|
54
|
+
* @param {object} args - Arguments.
|
|
55
|
+
* @param {number} args.afterSequence - Exclusive lower bound.
|
|
56
|
+
* @param {number} [args.limit] - Maximum number of changes.
|
|
57
|
+
* @param {number} [args.upToSequence] - Inclusive upper bound.
|
|
58
|
+
* @param {Record<string, ?>} [args.scope] - Caller sync scope.
|
|
59
|
+
* @returns {Promise<{changes: ServerChangeFeedEntry[], hasMore: boolean, nextSequence: number, oldestSequence: number | null, snapshotRequired: boolean, upToSequence: number}>} - Ordered page.
|
|
60
|
+
*/
|
|
61
|
+
changesAfter({ afterSequence, limit, scope, upToSequence }: {
|
|
62
|
+
afterSequence: number;
|
|
63
|
+
limit?: number | undefined;
|
|
64
|
+
upToSequence?: number | undefined;
|
|
65
|
+
scope?: Record<string, any> | undefined;
|
|
66
|
+
}): Promise<{
|
|
67
|
+
changes: ServerChangeFeedEntry[];
|
|
68
|
+
hasMore: boolean;
|
|
69
|
+
nextSequence: number;
|
|
70
|
+
oldestSequence: number | null;
|
|
71
|
+
snapshotRequired: boolean;
|
|
72
|
+
upToSequence: number;
|
|
73
|
+
}>;
|
|
74
|
+
/**
|
|
75
|
+
* Ensures schema is still present.
|
|
76
|
+
* @returns {Promise<boolean>} - Whether ready.
|
|
77
|
+
*/
|
|
78
|
+
_schemaReady(): Promise<boolean>;
|
|
79
|
+
/**
|
|
80
|
+
* Ensures changes table exists.
|
|
81
|
+
* @param {import("../database/drivers/base.js").default} db - Database connection.
|
|
82
|
+
* @returns {Promise<void>} - Resolves when complete.
|
|
83
|
+
*/
|
|
84
|
+
_ensureChangesTable(db: import("../database/drivers/base.js").default): Promise<void>;
|
|
85
|
+
/**
|
|
86
|
+
* Resolves a persisted change by id.
|
|
87
|
+
* @param {import("../database/drivers/base.js").default} db - Database connection.
|
|
88
|
+
* @param {string} id - Entry id.
|
|
89
|
+
* @returns {Promise<ServerChangeFeedEntry | null>} - Entry or null.
|
|
90
|
+
*/
|
|
91
|
+
_changeById(db: import("../database/drivers/base.js").default, id: string): Promise<ServerChangeFeedEntry | null>;
|
|
92
|
+
/**
|
|
93
|
+
* Resolves current latest sequence without readiness checks.
|
|
94
|
+
* @param {import("../database/drivers/base.js").default} db - Database connection.
|
|
95
|
+
* @returns {Promise<number>} - Latest sequence.
|
|
96
|
+
*/
|
|
97
|
+
_latestSequence(db: import("../database/drivers/base.js").default): Promise<number>;
|
|
98
|
+
/**
|
|
99
|
+
* Resolves current oldest sequence without readiness checks.
|
|
100
|
+
* @param {import("../database/drivers/base.js").default} db - Database connection.
|
|
101
|
+
* @returns {Promise<number | null>} - Oldest sequence.
|
|
102
|
+
*/
|
|
103
|
+
_oldestSequence(db: import("../database/drivers/base.js").default): Promise<number | null>;
|
|
104
|
+
/**
|
|
105
|
+
* Prunes old retained changes.
|
|
106
|
+
* @param {import("../database/drivers/base.js").default} db - Database connection.
|
|
107
|
+
* @param {number} latestSequence - Latest sequence after append.
|
|
108
|
+
* @returns {Promise<void>} - Resolves when complete.
|
|
109
|
+
*/
|
|
110
|
+
_pruneRetainedChanges(db: import("../database/drivers/base.js").default, latestSequence: number): Promise<void>;
|
|
111
|
+
/**
|
|
112
|
+
* Normalizes a change row.
|
|
113
|
+
* @param {ServerChangeFeedRow} row - Raw database row.
|
|
114
|
+
* @returns {ServerChangeFeedEntry} - Normalized change.
|
|
115
|
+
*/
|
|
116
|
+
_normalizeChangeRow(row: ServerChangeFeedRow): ServerChangeFeedEntry;
|
|
117
|
+
/**
|
|
118
|
+
* Whether this store should use process-local memory because no database identifier is configured.
|
|
119
|
+
* @returns {boolean} - Whether memory storage is active.
|
|
120
|
+
*/
|
|
121
|
+
_usesMemoryStorage(): boolean;
|
|
122
|
+
/**
|
|
123
|
+
* Appends a process-local memory entry when no database is configured.
|
|
124
|
+
* @param {Omit<ServerChangeFeedEntry, "serverSequence">} change - Change payload.
|
|
125
|
+
* @returns {ServerChangeFeedEntry} - Appended entry.
|
|
126
|
+
*/
|
|
127
|
+
_appendMemory(change: Omit<ServerChangeFeedEntry, "serverSequence">): ServerChangeFeedEntry;
|
|
128
|
+
/**
|
|
129
|
+
* Returns a process-local memory change page.
|
|
130
|
+
* @param {object} args - Arguments.
|
|
131
|
+
* @param {number} args.afterSequence - Exclusive lower bound.
|
|
132
|
+
* @param {number} args.limit - Page size.
|
|
133
|
+
* @param {number} [args.upToSequence] - Inclusive upper bound.
|
|
134
|
+
* @param {Record<string, ?>} [args.scope] - Caller sync scope.
|
|
135
|
+
* @returns {{changes: ServerChangeFeedEntry[], hasMore: boolean, nextSequence: number, oldestSequence: number | null, snapshotRequired: boolean, upToSequence: number}} - Ordered page.
|
|
136
|
+
*/
|
|
137
|
+
_memoryChangesAfter({ afterSequence, limit, scope, upToSequence }: {
|
|
138
|
+
afterSequence: number;
|
|
139
|
+
limit: number;
|
|
140
|
+
upToSequence?: number | undefined;
|
|
141
|
+
scope?: Record<string, any> | undefined;
|
|
142
|
+
}): {
|
|
143
|
+
changes: ServerChangeFeedEntry[];
|
|
144
|
+
hasMore: boolean;
|
|
145
|
+
nextSequence: number;
|
|
146
|
+
oldestSequence: number | null;
|
|
147
|
+
snapshotRequired: boolean;
|
|
148
|
+
upToSequence: number;
|
|
149
|
+
};
|
|
150
|
+
/**
|
|
151
|
+
* Runs with db.
|
|
152
|
+
* @param {(db: import("../database/drivers/base.js").default) => Promise<?>} callback - Callback.
|
|
153
|
+
* @returns {Promise<?>} - Callback result.
|
|
154
|
+
*/
|
|
155
|
+
_withDb(callback: (db: import("../database/drivers/base.js").default) => Promise<unknown>): Promise<unknown>;
|
|
156
|
+
}
|
|
157
|
+
export type ServerChangeFeedEntry = {
|
|
158
|
+
/**
|
|
159
|
+
* - Signed mutation actor device id when available.
|
|
160
|
+
*/
|
|
161
|
+
actorDeviceId: string | null;
|
|
162
|
+
/**
|
|
163
|
+
* - Signed mutation actor user id when available.
|
|
164
|
+
*/
|
|
165
|
+
actorUserId: string | null;
|
|
166
|
+
/**
|
|
167
|
+
* - Serialized mutation attributes.
|
|
168
|
+
*/
|
|
169
|
+
attributes: Record<string, unknown> | null;
|
|
170
|
+
/**
|
|
171
|
+
* - Server change creation timestamp.
|
|
172
|
+
*/
|
|
173
|
+
createdAt: string;
|
|
174
|
+
/**
|
|
175
|
+
* - Server change id.
|
|
176
|
+
*/
|
|
177
|
+
id: string;
|
|
178
|
+
/**
|
|
179
|
+
* - Mutation idempotency key when available.
|
|
180
|
+
*/
|
|
181
|
+
idempotencyKey: string | null;
|
|
182
|
+
/**
|
|
183
|
+
* - Frontend model name.
|
|
184
|
+
*/
|
|
185
|
+
model: string;
|
|
186
|
+
/**
|
|
187
|
+
* - Mutation operation.
|
|
188
|
+
*/
|
|
189
|
+
operation: string;
|
|
190
|
+
/**
|
|
191
|
+
* - Serialized mutation payload.
|
|
192
|
+
*/
|
|
193
|
+
payload: Record<string, unknown> | null;
|
|
194
|
+
/**
|
|
195
|
+
* - Changed record id when known.
|
|
196
|
+
*/
|
|
197
|
+
recordId: string | null;
|
|
198
|
+
/**
|
|
199
|
+
* - Command response payload.
|
|
200
|
+
*/
|
|
201
|
+
response: Record<string, unknown> | null;
|
|
202
|
+
/**
|
|
203
|
+
* - Offline grant scope.
|
|
204
|
+
*/
|
|
205
|
+
scope: Record<string, unknown> | null;
|
|
206
|
+
/**
|
|
207
|
+
* - Monotonic server sequence.
|
|
208
|
+
*/
|
|
209
|
+
serverSequence: number;
|
|
210
|
+
};
|
|
211
|
+
export type ServerChangeFeedRow = {
|
|
212
|
+
/**
|
|
213
|
+
* - Actor device id.
|
|
214
|
+
*/
|
|
215
|
+
actor_device_id: string | null;
|
|
216
|
+
/**
|
|
217
|
+
* - Actor user id.
|
|
218
|
+
*/
|
|
219
|
+
actor_user_id: string | null;
|
|
220
|
+
/**
|
|
221
|
+
* - Attributes JSON.
|
|
222
|
+
*/
|
|
223
|
+
attributes_json: string | null;
|
|
224
|
+
/**
|
|
225
|
+
* - Creation time.
|
|
226
|
+
*/
|
|
227
|
+
created_at: Date | string;
|
|
228
|
+
/**
|
|
229
|
+
* - Entry id.
|
|
230
|
+
*/
|
|
231
|
+
id: string;
|
|
232
|
+
/**
|
|
233
|
+
* - Mutation idempotency key.
|
|
234
|
+
*/
|
|
235
|
+
idempotency_key: string | null;
|
|
236
|
+
/**
|
|
237
|
+
* - Frontend model name.
|
|
238
|
+
*/
|
|
239
|
+
model: string;
|
|
240
|
+
/**
|
|
241
|
+
* - Mutation operation.
|
|
242
|
+
*/
|
|
243
|
+
operation: string;
|
|
244
|
+
/**
|
|
245
|
+
* - Mutation payload JSON.
|
|
246
|
+
*/
|
|
247
|
+
payload_json: string | null;
|
|
248
|
+
/**
|
|
249
|
+
* - Record id.
|
|
250
|
+
*/
|
|
251
|
+
record_id: string | null;
|
|
252
|
+
/**
|
|
253
|
+
* - Response JSON.
|
|
254
|
+
*/
|
|
255
|
+
response_json: string | null;
|
|
256
|
+
/**
|
|
257
|
+
* - Scope JSON.
|
|
258
|
+
*/
|
|
259
|
+
scope_json: string | null;
|
|
260
|
+
/**
|
|
261
|
+
* - Server sequence.
|
|
262
|
+
*/
|
|
263
|
+
server_sequence: number | string;
|
|
264
|
+
};
|
|
265
|
+
//# sourceMappingURL=server-change-feed.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-change-feed.d.ts","sourceRoot":"","sources":["../../../src/sync/server-change-feed.js"],"names":[],"mappings":"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"}
|