velocious 1.0.487 → 1.0.488
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/build/src/sync/sync-api-client-types.d.ts +182 -0
- package/build/src/sync/sync-api-client-types.d.ts.map +1 -0
- package/build/src/sync/sync-api-client-types.js +3 -0
- package/build/src/sync/sync-api-client.d.ts +188 -0
- package/build/src/sync/sync-api-client.d.ts.map +1 -0
- package/build/src/sync/sync-api-client.js +307 -0
- package/build/src/sync/sync-api-controller.d.ts +78 -0
- package/build/src/sync/sync-api-controller.d.ts.map +1 -0
- package/build/src/sync/sync-api-controller.js +142 -0
- package/build/src/sync/sync-model-change-feed-service.d.ts +131 -0
- package/build/src/sync/sync-model-change-feed-service.d.ts.map +1 -0
- package/build/src/sync/sync-model-change-feed-service.js +212 -0
- package/build/sync/sync-api-client-types.js +77 -0
- package/build/sync/sync-api-client.js +342 -0
- package/build/sync/sync-api-controller.js +161 -0
- package/build/sync/sync-model-change-feed-service.js +244 -0
- package/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/sync/sync-api-client-types.js +77 -0
- package/src/sync/sync-api-client.js +342 -0
- package/src/sync/sync-api-controller.js +161 -0
- package/src/sync/sync-model-change-feed-service.js +244 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import { optionalInteger } from "typanic";
|
|
3
|
+
/** @typedef {import("./sync-api-client-types.js").SyncChangeApplyResult} SyncChangeApplyResult */
|
|
4
|
+
/** @typedef {import("./sync-api-client-types.js").SyncChangeEnvelope} SyncChangeEnvelope */
|
|
5
|
+
/** @typedef {import("./sync-api-client-types.js").SyncChangesRequest} SyncChangesRequest */
|
|
6
|
+
/** @typedef {import("./sync-api-client-types.js").SyncChangesResponse} SyncChangesResponse */
|
|
7
|
+
/** @typedef {import("./sync-api-client-types.js").SyncChangesResult} SyncChangesResult */
|
|
8
|
+
/** @typedef {import("./sync-api-client-types.js").SyncCursor} SyncCursor */
|
|
9
|
+
/** @typedef {import("./sync-api-client-types.js").SyncReplayItem} SyncReplayItem */
|
|
10
|
+
/** @typedef {import("./sync-api-client-types.js").SyncReplayResponse} SyncReplayResponse */
|
|
11
|
+
/** @typedef {import("./sync-api-client-types.js").SyncResourceConfig} SyncResourceConfig */
|
|
12
|
+
/**
|
|
13
|
+
* Generic client-side helper for replaying pending sync envelopes through the
|
|
14
|
+
* framework-owned `/velocious/sync/replay` endpoint. Apps provide only local
|
|
15
|
+
* persistence/auth hooks.
|
|
16
|
+
*/
|
|
17
|
+
export default class SyncApiClient {
|
|
18
|
+
/**
|
|
19
|
+
* Pulls backend sync changes in stable pages, applies them locally, and stores
|
|
20
|
+
* the acknowledged cursor. Apps provide only auth, persistence, transport, and
|
|
21
|
+
* resource policy hooks.
|
|
22
|
+
* @param {object} args - Pull args.
|
|
23
|
+
* @param {string} args.authenticationToken - Auth token to send with change requests.
|
|
24
|
+
* @param {number} [args.batchSize] - Max syncs per request. Defaults to 100.
|
|
25
|
+
* @param {() => Promise<SyncCursor | string | null | undefined>} args.loadCursor - Loads the persisted local cursor.
|
|
26
|
+
* @param {(cursor: SyncCursor) => Promise<void>} args.saveCursor - Persists the final acknowledged cursor.
|
|
27
|
+
* @param {(payload: SyncChangesRequest) => Promise<SyncChangesResponse>} args.postChanges - Posts one changes request.
|
|
28
|
+
* @param {(sync: SyncChangeEnvelope) => Promise<SyncChangeApplyResult>} args.applySync - Applies one normalized sync row locally.
|
|
29
|
+
* @param {(progress: {pages: number, syncedCount: number}) => void} [args.onProgress] - Progress callback.
|
|
30
|
+
* @returns {Promise<SyncChangesResult>} Pull result.
|
|
31
|
+
*/
|
|
32
|
+
static async pullChanges(args) {
|
|
33
|
+
let afterCursor = this.syncCursorFromPayload(await args.loadCursor());
|
|
34
|
+
let upToCursor = null;
|
|
35
|
+
let pages = 0;
|
|
36
|
+
let syncedCount = 0;
|
|
37
|
+
let changed = false;
|
|
38
|
+
const resourceCounts = /** @type {Record<string, number>} */ ({});
|
|
39
|
+
const resourceChanged = /** @type {Record<string, boolean>} */ ({});
|
|
40
|
+
const batchSize = this.normalizedBatchSize(args.batchSize);
|
|
41
|
+
while (true) {
|
|
42
|
+
const changesResponse = await this.changesPage({ ...args, afterCursor, batchSize, upToCursor });
|
|
43
|
+
const syncs = changesResponse.syncs;
|
|
44
|
+
if (!upToCursor)
|
|
45
|
+
upToCursor = changesResponse.upToCursor;
|
|
46
|
+
if (syncs.length === 0)
|
|
47
|
+
break;
|
|
48
|
+
pages += 1;
|
|
49
|
+
for (const sync of syncs) {
|
|
50
|
+
const applyResult = await args.applySync(sync);
|
|
51
|
+
const resourceType = applyResult.resourceType ?? sync.resourceType();
|
|
52
|
+
changed ||= applyResult.changed === true;
|
|
53
|
+
syncedCount += 1;
|
|
54
|
+
if (resourceType) {
|
|
55
|
+
resourceCounts[resourceType] = (resourceCounts[resourceType] || 0) + 1;
|
|
56
|
+
resourceChanged[resourceType] ||= applyResult.changed === true;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
afterCursor = changesResponse.nextCursor;
|
|
60
|
+
if (args.onProgress)
|
|
61
|
+
args.onProgress({ pages, syncedCount });
|
|
62
|
+
if (syncs.length < batchSize)
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
if (afterCursor)
|
|
66
|
+
await args.saveCursor(afterCursor);
|
|
67
|
+
return { changed, pages, resourceChanged, resourceCounts, syncedCount };
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Fetches and validates one backend sync changes page.
|
|
71
|
+
* @param {object} args - Page args.
|
|
72
|
+
* @param {SyncCursor} args.afterCursor - Last acknowledged cursor.
|
|
73
|
+
* @param {string} args.authenticationToken - Auth token.
|
|
74
|
+
* @param {number} args.batchSize - Page size.
|
|
75
|
+
* @param {(payload: SyncChangesRequest) => Promise<SyncChangesResponse>} args.postChanges - Changes poster.
|
|
76
|
+
* @param {SyncCursor} args.upToCursor - Snapshot upper-bound cursor.
|
|
77
|
+
* @returns {Promise<{nextCursor: SyncCursor, syncs: SyncChangeEnvelope[], upToCursor: SyncCursor}>} Normalized changes page.
|
|
78
|
+
*/
|
|
79
|
+
static async changesPage({ afterCursor, authenticationToken, batchSize, postChanges, upToCursor }) {
|
|
80
|
+
const response = await postChanges({
|
|
81
|
+
authenticationToken,
|
|
82
|
+
limit: batchSize,
|
|
83
|
+
...this.cursorPayload("after", afterCursor),
|
|
84
|
+
...this.cursorPayload("upTo", upToCursor)
|
|
85
|
+
});
|
|
86
|
+
this.ensureSuccessfulChangesResponse(response);
|
|
87
|
+
const syncs = /** @type {unknown[]} */ (response.syncs);
|
|
88
|
+
return {
|
|
89
|
+
nextCursor: this.syncCursorFromPayload(response.nextCursor ?? null),
|
|
90
|
+
syncs: syncs.map((syncPayload) => this.syncEnvelopeFromPayload(syncPayload)),
|
|
91
|
+
upToCursor: this.syncCursorFromPayload(response.upToCursor ?? null)
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Checks API response status and shape for change-feed pulls.
|
|
96
|
+
* @param {SyncChangesResponse} response - Changes response.
|
|
97
|
+
* @returns {void}
|
|
98
|
+
*/
|
|
99
|
+
static ensureSuccessfulChangesResponse(response) {
|
|
100
|
+
if (response.status === "error")
|
|
101
|
+
throw new Error(response.errorMessage || "Sync changes failed");
|
|
102
|
+
if (!Array.isArray(response.syncs))
|
|
103
|
+
throw new Error("Sync changes response missing syncs");
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Converts a cursor into request params with the given prefix.
|
|
107
|
+
* @param {"after" | "upTo"} prefix - Request field prefix.
|
|
108
|
+
* @param {SyncCursor} cursor - Cursor to serialize.
|
|
109
|
+
* @returns {Record<string, string | number | null>} Request params.
|
|
110
|
+
*/
|
|
111
|
+
static cursorPayload(prefix, cursor) {
|
|
112
|
+
if (!cursor)
|
|
113
|
+
return {};
|
|
114
|
+
return {
|
|
115
|
+
[`${prefix}Id`]: cursor.id,
|
|
116
|
+
...(cursor.serverSequence ? { [`${prefix}ServerSequence`]: cursor.serverSequence } : {}),
|
|
117
|
+
[`${prefix}UpdatedAt`]: cursor.updatedAt
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Parses a persisted or response cursor payload.
|
|
122
|
+
* @param {SyncCursor | string | Record<string, ?> | null | undefined} payload - Cursor payload.
|
|
123
|
+
* @returns {SyncCursor} Parsed cursor.
|
|
124
|
+
*/
|
|
125
|
+
static syncCursorFromPayload(payload) {
|
|
126
|
+
if (!payload)
|
|
127
|
+
return null;
|
|
128
|
+
if (typeof payload === "string") {
|
|
129
|
+
try {
|
|
130
|
+
return this.syncCursorFromPayload(JSON.parse(payload));
|
|
131
|
+
}
|
|
132
|
+
catch (_error) {
|
|
133
|
+
return { id: null, serverSequence: null, updatedAt: payload };
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (typeof payload !== "object" || Array.isArray(payload))
|
|
137
|
+
return null;
|
|
138
|
+
const updatedAt = typeof payload.updatedAt === "string" ? payload.updatedAt : null;
|
|
139
|
+
if (!updatedAt)
|
|
140
|
+
return null;
|
|
141
|
+
return {
|
|
142
|
+
id: payload.id === null || payload.id === undefined ? null : String(payload.id),
|
|
143
|
+
serverSequence: optionalInteger(payload.serverSequence === "" ? null : payload.serverSequence),
|
|
144
|
+
updatedAt
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Builds a normalized sync row adapter.
|
|
149
|
+
* @param {?} payload - Raw sync payload.
|
|
150
|
+
* @returns {SyncChangeEnvelope} Sync row adapter.
|
|
151
|
+
*/
|
|
152
|
+
static syncEnvelopeFromPayload(payload) {
|
|
153
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
154
|
+
throw new Error("Sync changes entry must be an object");
|
|
155
|
+
const syncPayload = /** @type {Record<string, ?>} */ (payload);
|
|
156
|
+
return {
|
|
157
|
+
data: () => syncPayload.data,
|
|
158
|
+
id: () => syncPayload.id,
|
|
159
|
+
resourceId: () => syncPayload.resourceId,
|
|
160
|
+
resourceType: () => syncPayload.resourceType === null || syncPayload.resourceType === undefined ? null : String(syncPayload.resourceType),
|
|
161
|
+
syncType: () => syncPayload.syncType === null || syncPayload.syncType === undefined ? "" : String(syncPayload.syncType)
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Builds an app-configured resource applier for pulled sync rows. The sync
|
|
166
|
+
* mechanics stay here; apps only declare which models/attributes/hooks are
|
|
167
|
+
* allowed for each resource type.
|
|
168
|
+
* @param {Record<string, SyncResourceConfig>} resources - Resource policy map.
|
|
169
|
+
* @returns {(sync: SyncChangeEnvelope) => Promise<SyncChangeApplyResult>} Sync apply callback.
|
|
170
|
+
*/
|
|
171
|
+
static resourceApplier(resources) {
|
|
172
|
+
return async (sync) => await this.applyResourceSync({ resources, sync });
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Applies one sync row using declarative resource policy.
|
|
176
|
+
* @param {{resources: Record<string, SyncResourceConfig>, sync: SyncChangeEnvelope}} args - Apply args.
|
|
177
|
+
* @returns {Promise<SyncChangeApplyResult>} Apply result.
|
|
178
|
+
*/
|
|
179
|
+
static async applyResourceSync({ resources, sync }) {
|
|
180
|
+
const resourceType = sync.resourceType();
|
|
181
|
+
const resource = resourceType ? resources[resourceType] : undefined;
|
|
182
|
+
if (!resource || !resource.enabled)
|
|
183
|
+
return { changed: false, resourceType };
|
|
184
|
+
if (sync.syncType() === "delete") {
|
|
185
|
+
return { changed: await this.destroySyncedResource({ resource, sync }), resourceType };
|
|
186
|
+
}
|
|
187
|
+
const data = this.syncData(sync);
|
|
188
|
+
const record = resource.findRecord ? await resource.findRecord({ data, resourceId: sync.resourceId(), sync }) : await resource.modelClass.findOrInitializeBy({ id: data.id ?? sync.resourceId() });
|
|
189
|
+
const attributes = await resource.attributes({ data, record, sync });
|
|
190
|
+
let changed = false;
|
|
191
|
+
record.assign(attributes);
|
|
192
|
+
if (record.isChanged()) {
|
|
193
|
+
await record.save();
|
|
194
|
+
changed = true;
|
|
195
|
+
}
|
|
196
|
+
if (resource.afterApply) {
|
|
197
|
+
const hookChanged = await resource.afterApply({ attributes, data, record, sync });
|
|
198
|
+
changed ||= hookChanged === true;
|
|
199
|
+
}
|
|
200
|
+
return { changed, resourceType };
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Destroys a synced resource via its declared model policy.
|
|
204
|
+
* @param {{resource: SyncResourceConfig, sync: SyncChangeEnvelope}} args - Destroy args.
|
|
205
|
+
* @returns {Promise<boolean>} Whether a local row was destroyed.
|
|
206
|
+
*/
|
|
207
|
+
static async destroySyncedResource({ resource, sync }) {
|
|
208
|
+
const id = sync.resourceId();
|
|
209
|
+
const record = resource.findRecordForDelete ? await resource.findRecordForDelete({ resourceId: id, sync }) : await resource.modelClass.findBy({ id });
|
|
210
|
+
if (!record)
|
|
211
|
+
return false;
|
|
212
|
+
await record.destroy();
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Parses the embedded sync data JSON/object.
|
|
217
|
+
* @param {SyncChangeEnvelope} sync - Sync row.
|
|
218
|
+
* @returns {Record<string, unknown>} Sync data object.
|
|
219
|
+
*/
|
|
220
|
+
static syncData(sync) {
|
|
221
|
+
const data = sync.data();
|
|
222
|
+
if (!data)
|
|
223
|
+
throw new Error(`Sync ${sync.id()} is missing data`);
|
|
224
|
+
if (typeof data === "string")
|
|
225
|
+
return /** @type {Record<string, unknown>} */ (JSON.parse(data));
|
|
226
|
+
if (typeof data === "object" && !Array.isArray(data))
|
|
227
|
+
return /** @type {Record<string, unknown>} */ (data);
|
|
228
|
+
throw new Error(`Sync ${sync.id()} has invalid data`);
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Drains pending sync records in stable order and marks acknowledged rows.
|
|
232
|
+
* @param {object} args - Replay args.
|
|
233
|
+
* @param {string} args.authenticationToken - Auth token to send with replay requests.
|
|
234
|
+
* @param {number} [args.batchSize] - Max syncs per request. Defaults to 100.
|
|
235
|
+
* @param {() => Promise<Array<unknown>>} args.pendingSyncs - Loads pending local sync rows in replay order.
|
|
236
|
+
* @param {(sync: unknown) => string | number | null | undefined} args.syncId - Returns the local sync id.
|
|
237
|
+
* @param {(sync: unknown) => Record<string, ?>} args.syncPayload - Builds the API sync envelope.
|
|
238
|
+
* @param {(payload: {authenticationToken: string, syncs: Array<Record<string, ?>>}) => Promise<SyncReplayResponse>} args.postReplay - Posts one replay request.
|
|
239
|
+
* @param {(sync: unknown, response: SyncReplayItem) => Promise<void>} args.markSuccessful - Marks one sync as successful locally.
|
|
240
|
+
* @returns {Promise<void>} Resolves after all batches are replayed.
|
|
241
|
+
*/
|
|
242
|
+
static async replayPending(args) {
|
|
243
|
+
const pendingSyncs = await args.pendingSyncs();
|
|
244
|
+
const batchSize = this.normalizedBatchSize(args.batchSize);
|
|
245
|
+
for (let offset = 0; offset < pendingSyncs.length; offset += batchSize) {
|
|
246
|
+
await this.replayBatch({ ...args, pendingSyncs: pendingSyncs.slice(offset, offset + batchSize) });
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Replays one batch of syncs.
|
|
251
|
+
* @param {object} args - Replay args.
|
|
252
|
+
* @param {string} args.authenticationToken - Auth token.
|
|
253
|
+
* @param {Array<unknown>} args.pendingSyncs - Batch syncs.
|
|
254
|
+
* @param {(sync: unknown) => string | number | null | undefined} args.syncId - Sync id getter.
|
|
255
|
+
* @param {(sync: unknown) => Record<string, ?>} args.syncPayload - Payload builder.
|
|
256
|
+
* @param {(payload: {authenticationToken: string, syncs: Array<Record<string, ?>>}) => Promise<SyncReplayResponse>} args.postReplay - Replay poster.
|
|
257
|
+
* @param {(sync: unknown, response: SyncReplayItem) => Promise<void>} args.markSuccessful - Success hook.
|
|
258
|
+
* @returns {Promise<void>} Resolves after the batch is acknowledged.
|
|
259
|
+
*/
|
|
260
|
+
static async replayBatch(args) {
|
|
261
|
+
const { authenticationToken, markSuccessful, pendingSyncs, postReplay, syncId, syncPayload } = args;
|
|
262
|
+
if (pendingSyncs.length === 0)
|
|
263
|
+
return;
|
|
264
|
+
const syncsById = new Map();
|
|
265
|
+
for (const sync of pendingSyncs) {
|
|
266
|
+
const id = syncId(sync);
|
|
267
|
+
if (id !== undefined && id !== null)
|
|
268
|
+
syncsById.set(String(id), sync);
|
|
269
|
+
}
|
|
270
|
+
const response = await postReplay({
|
|
271
|
+
authenticationToken,
|
|
272
|
+
syncs: pendingSyncs.map((sync) => syncPayload(sync))
|
|
273
|
+
});
|
|
274
|
+
this.ensureSuccessfulResponse(response);
|
|
275
|
+
for (const syncResponse of response.syncs || []) {
|
|
276
|
+
const sync = syncsById.get(String(syncResponse.id));
|
|
277
|
+
if (!sync)
|
|
278
|
+
continue;
|
|
279
|
+
if (syncResponse.syncState !== "successful") {
|
|
280
|
+
throw new Error(`Invalid sync state returned for sync ${String(syncResponse.id)}: ${String(syncResponse.syncState)}`);
|
|
281
|
+
}
|
|
282
|
+
await markSuccessful(sync, syncResponse);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Checks API response status and shape.
|
|
287
|
+
* @param {SyncReplayResponse} response - Replay response.
|
|
288
|
+
* @returns {void}
|
|
289
|
+
*/
|
|
290
|
+
static ensureSuccessfulResponse(response) {
|
|
291
|
+
if (response.status === "error")
|
|
292
|
+
throw new Error(response.errorMessage || "Sync failed");
|
|
293
|
+
if (!Array.isArray(response.syncs))
|
|
294
|
+
throw new Error("Sync response missing syncs");
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Normalizes a positive batch size.
|
|
298
|
+
* @param {number | undefined} batchSize - Batch size.
|
|
299
|
+
* @returns {number} Positive batch size.
|
|
300
|
+
*/
|
|
301
|
+
static normalizedBatchSize(batchSize) {
|
|
302
|
+
if (typeof batchSize !== "number" || !Number.isFinite(batchSize) || batchSize < 1)
|
|
303
|
+
return 100;
|
|
304
|
+
return Math.floor(batchSize);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3luYy1hcGktY2xpZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3N5bmMvc3luYy1hcGktY2xpZW50LmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLFlBQVk7QUFFWixPQUFPLEVBQUMsZUFBZSxFQUFDLE1BQU0sU0FBUyxDQUFBO0FBRXZDLGtHQUFrRztBQUNsRyw0RkFBNEY7QUFDNUYsNEZBQTRGO0FBQzVGLDhGQUE4RjtBQUM5RiwwRkFBMEY7QUFDMUYsNEVBQTRFO0FBQzVFLG9GQUFvRjtBQUNwRiw0RkFBNEY7QUFDNUYsNEZBQTRGO0FBRTVGOzs7O0dBSUc7QUFDSCxNQUFNLENBQUMsT0FBTyxPQUFPLGFBQWE7SUFDaEM7Ozs7Ozs7Ozs7Ozs7T0FhRztJQUNILE1BQU0sQ0FBQyxLQUFLLENBQUMsV0FBVyxDQUFDLElBQUk7UUFDM0IsSUFBSSxXQUFXLEdBQUcsSUFBSSxDQUFDLHFCQUFxQixDQUFDLE1BQU0sSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDLENBQUE7UUFDckUsSUFBSSxVQUFVLEdBQUcsSUFBSSxDQUFBO1FBQ3JCLElBQUksS0FBSyxHQUFHLENBQUMsQ0FBQTtRQUNiLElBQUksV0FBVyxHQUFHLENBQUMsQ0FBQTtRQUNuQixJQUFJLE9BQU8sR0FBRyxLQUFLLENBQUE7UUFDbkIsTUFBTSxjQUFjLEdBQUcscUNBQXFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQTtRQUNqRSxNQUFNLGVBQWUsR0FBRyxzQ0FBc0MsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFBO1FBQ25FLE1BQU0sU0FBUyxHQUFHLElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUE7UUFFMUQsT0FBTyxJQUFJLEVBQUUsQ0FBQztZQUNaLE1BQU0sZUFBZSxHQUFHLE1BQU0sSUFBSSxDQUFDLFdBQVcsQ0FBQyxFQUFDLEdBQUcsSUFBSSxFQUFFLFdBQVcsRUFBRSxTQUFTLEVBQUUsVUFBVSxFQUFD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blxuaW1wb3J0IHtvcHRpb25hbEludGVnZXJ9IGZyb20gXCJ0eXBhbmljXCJcblxuLyoqIEB0eXBlZGVmIHtpbXBvcnQoXCIuL3N5bmMtYXBpLWNsaWVudC10eXBlcy5qc1wiKS5TeW5jQ2hhbmdlQXBwbHlSZXN1bHR9IFN5bmNDaGFuZ2VBcHBseVJlc3VsdCAqL1xuLyoqIEB0eXBlZGVmIHtpbXBvcnQoXCIuL3N5bmMtYXBpLWNsaWVudC10eXBlcy5qc1wiKS5TeW5jQ2hhbmdlRW52ZWxvcGV9IFN5bmNDaGFuZ2VFbnZlbG9wZSAqL1xuLyoqIEB0eXBlZGVmIHtpbXBvcnQoXCIuL3N5bmMtYXBpLWNsaWVudC10eXBlcy5qc1wiKS5TeW5jQ2hhbmdlc1JlcXVlc3R9IFN5bmNDaGFuZ2VzUmVxdWVzdCAqL1xuLyoqIEB0eXBlZGVmIHtpbXBvcnQoXCIuL3N5bmMtYXBpLWNsaWVudC10eXBlcy5qc1wiKS5TeW5jQ2hhbmdlc1Jlc3BvbnNlfSBTeW5jQ2hhbmdlc1Jlc3BvbnNlICovXG4vKiogQHR5cGVkZWYge2ltcG9ydChcIi4vc3luYy1hcGktY2xpZW50LXR5cGVzLmpzXCIpLlN5bmNDaGFuZ2VzUmVzdWx0fSBTeW5jQ2hhbmdlc1Jlc3VsdCAqL1xuLyoqIEB0eXBlZGVmIHtpbXBvcnQoXCIuL3N5bmMtYXBpLWNsaWVudC10eXBlcy5qc1wiKS5TeW5jQ3Vyc29yfSBTeW5jQ3Vyc29yICovXG4vKiogQHR5cGVkZWYge2ltcG9ydChcIi4vc3luYy1hcGktY2xpZW50LXR5cGVzLmpzXCIpLlN5bmNSZXBsYXlJdGVtfSBTeW5jUmVwbGF5SXRlbSAqL1xuLyoqIEB0eXBlZGVmIHtpbXBvcnQoXCIuL3N5bmMtYXBpLWNsaWVudC10eXBlcy5qc1wiKS5TeW5jUmVwbGF5UmVzcG9uc2V9IFN5bmNSZXBsYXlSZXNwb25zZSAqL1xuLyoqIEB0eXBlZGVmIHtpbXBvcnQoXCIuL3N5bmMtYXBpLWNsaWVudC10eXBlcy5qc1wiKS5TeW5jUmVzb3VyY2VDb25maWd9IFN5bmNSZXNvdXJjZUNvbmZpZyAqL1xuXG4vKipcbiAqIEdlbmVyaWMgY2xpZW50LXNpZGUgaGVscGVyIGZvciByZXBsYXlpbmcgcGVuZGluZyBzeW5jIGVudmVsb3BlcyB0aHJvdWdoIHRoZVxuICogZnJhbWV3b3JrLW93bmVkIGAvdmVsb2Npb3VzL3N5bmMvcmVwbGF5YCBlbmRwb2ludC4gQXBwcyBwcm92aWRlIG9ubHkgbG9jYWxcbiAqIHBlcnNpc3RlbmNlL2F1dGggaG9va3MuXG4gKi9cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIFN5bmNBcGlDbGllbnQge1xuICAvKipcbiAgICogUHVsbHMgYmFja2VuZCBzeW5jIGNoYW5nZXMgaW4gc3RhYmxlIHBhZ2VzLCBhcHBsaWVzIHRoZW0gbG9jYWxseSwgYW5kIHN0b3Jlc1xuICAgKiB0aGUgYWNrbm93bGVkZ2VkIGN1cnNvci4gQXBwcyBwcm92aWRlIG9ubHkgYXV0aCwgcGVyc2lzdGVuY2UsIHRyYW5zcG9ydCwgYW5kXG4gICAqIHJlc291cmNlIHBvbGljeSBob29rcy5cbiAgICogQHBhcmFtIHtvYmplY3R9IGFyZ3MgLSBQdWxsIGFyZ3MuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhcmdzLmF1dGhlbnRpY2F0aW9uVG9rZW4gLSBBdXRoIHRva2VuIHRvIHNlbmQgd2l0aCBjaGFuZ2UgcmVxdWVzdHMuXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbYXJncy5iYXRjaFNpemVdIC0gTWF4IHN5bmNzIHBlciByZXF1ZXN0LiBEZWZhdWx0cyB0byAxMDAuXG4gICAqIEBwYXJhbSB7KCkgPT4gUHJvbWlzZTxTeW5jQ3Vyc29yIHwgc3RyaW5nIHwgbnVsbCB8IHVuZGVmaW5lZD59IGFyZ3MubG9hZEN1cnNvciAtIExvYWRzIHRoZSBwZXJzaXN0ZWQgbG9jYWwgY3Vyc29yLlxuICAgKiBAcGFyYW0geyhjdXJzb3I6IFN5bmNDdXJzb3IpID0+IFByb21pc2U8dm9pZD59IGFyZ3Muc2F2ZUN1cnNvciAtIFBlcnNpc3RzIHRoZSBmaW5hbCBhY2tub3dsZWRnZWQgY3Vyc29yLlxuICAgKiBAcGFyYW0geyhwYXlsb2FkOiBTeW5jQ2hhbmdlc1JlcXVlc3QpID0+IFByb21pc2U8U3luY0NoYW5nZXNSZXNwb25zZT59IGFyZ3MucG9zdENoYW5nZXMgLSBQb3N0cyBvbmUgY2hhbmdlcyByZXF1ZXN0LlxuICAgKiBAcGFyYW0geyhzeW5jOiBTeW5jQ2hhbmdlRW52ZWxvcGUpID0+IFByb21pc2U8U3luY0NoYW5nZUFwcGx5UmVzdWx0Pn0gYXJncy5hcHBseVN5bmMgLSBBcHBsaWVzIG9uZSBub3JtYWxpemVkIHN5bmMgcm93IGxvY2FsbHkuXG4gICAqIEBwYXJhbSB7KHByb2dyZXNzOiB7cGFnZXM6IG51bWJlciwgc3luY2VkQ291bnQ6IG51bWJlcn0pID0+IHZvaWR9IFthcmdzLm9uUHJvZ3Jlc3NdIC0gUHJvZ3Jlc3MgY2FsbGJhY2suXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPFN5bmNDaGFuZ2VzUmVzdWx0Pn0gUHVsbCByZXN1bHQuXG4gICAqL1xuICBzdGF0aWMgYXN5bmMgcHVsbENoYW5nZXMoYXJncykge1xuICAgIGxldCBhZnRlckN1cnNvciA9IHRoaXMuc3luY0N1cnNvckZyb21QYXlsb2FkKGF3YWl0IGFyZ3MubG9hZEN1cnNvcigpKVxuICAgIGxldCB1cFRvQ3Vyc29yID0gbnVsbFxuICAgIGxldCBwYWdlcyA9IDBcbiAgICBsZXQgc3luY2VkQ291bnQgPSAwXG4gICAgbGV0IGNoYW5nZWQgPSBmYWxzZVxuICAgIGNvbnN0IHJlc291cmNlQ291bnRzID0gLyoqIEB0eXBlIHtSZWNvcmQ8c3RyaW5nLCBudW1iZXI+fSAqLyAoe30pXG4gICAgY29uc3QgcmVzb3VyY2VDaGFuZ2VkID0gLyoqIEB0eXBlIHtSZWNvcmQ8c3RyaW5nLCBib29sZWFuPn0gKi8gKHt9KVxuICAgIGNvbnN0IGJhdGNoU2l6ZSA9IHRoaXMubm9ybWFsaXplZEJhdGNoU2l6ZShhcmdzLmJhdGNoU2l6ZSlcblxuICAgIHdoaWxlICh0cnVlKSB7XG4gICAgICBjb25zdCBjaGFuZ2VzUmVzcG9uc2UgPSBhd2FpdCB0aGlzLmNoYW5nZXNQYWdlKHsuLi5hcmdzLCBhZnRlckN1cnNvciwgYmF0Y2hTaXplLCB1cFRvQ3Vyc29yfSlcbiAgICAgIGNvbnN0IHN5bmNzID0gY2hhbmdlc1Jlc3BvbnNlLnN5bmNzXG5cbiAgICAgIGlmICghdXBUb0N1cnNvcikgdXBUb0N1cnNvciA9IGNoYW5nZXNSZXNwb25zZS51cFRvQ3Vyc29yXG4gICAgICBpZiAoc3luY3MubGVuZ3RoID09PSAwKSBicmVha1xuXG4gICAgICBwYWdlcyArPSAxXG5cbiAgICAgIGZvciAoY29uc3Qgc3luYyBvZiBzeW5jcykge1xuICAgICAgICBjb25zdCBhcHBseVJlc3VsdCA9IGF3YWl0IGFyZ3MuYXBwbHlTeW5jKHN5bmMpXG4gICAgICAgIGNvbnN0IHJlc291cmNlVHlwZSA9IGFwcGx5UmVzdWx0LnJlc291cmNlVHlwZSA/PyBzeW5jLnJlc291cmNlVHlwZSgpXG5cbiAgICAgICAgY2hhbmdlZCB8fD0gYXBwbHlSZXN1bHQuY2hhbmdlZCA9PT0gdHJ1ZVxuICAgICAgICBzeW5jZWRDb3VudCArPSAxXG5cbiAgICAgICAgaWYgKHJlc291cmNlVHlwZSkge1xuICAgICAgICAgIHJlc291cmNlQ291bnRzW3Jlc291cmNlVHlwZV0gPSAocmVzb3VyY2VDb3VudHNbcmVzb3VyY2VUeXBlXSB8fCAwKSArIDFcbiAgICAgICAgICByZXNvdXJjZUNoYW5nZWRbcmVzb3VyY2VUeXBlXSB8fD0gYXBwbHlSZXN1bHQuY2hhbmdlZCA9PT0gdHJ1ZVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGFmdGVyQ3Vyc29yID0gY2hhbmdlc1Jlc3BvbnNlLm5leHRDdXJzb3JcblxuICAgICAgaWYgKGFyZ3Mub25Qcm9ncmVzcykgYXJncy5vblByb2dyZXNzKHtwYWdlcywgc3luY2VkQ291bnR9KVxuICAgICAgaWYgKHN5bmNzLmxlbmd0aCA8IGJhdGNoU2l6ZSkgYnJlYWtcbiAgICB9XG5cbiAgICBpZiAoYWZ0ZXJDdXJzb3IpIGF3YWl0IGFyZ3Muc2F2ZUN1cnNvcihhZnRlckN1cnNvcilcblxuICAgIHJldHVybiB7Y2hhbmdlZCwgcGFnZXMsIHJlc291cmNlQ2hhbmdlZCwgcmVzb3VyY2VDb3VudHMsIHN5bmNlZENvdW50fVxuICB9XG5cbiAgLyoqXG4gICAqIEZldGNoZXMgYW5kIHZhbGlkYXRlcyBvbmUgYmFja2VuZCBzeW5jIGNoYW5nZXMgcGFnZS5cbiAgICogQHBhcmFtIHtvYmplY3R9IGFyZ3MgLSBQYWdlIGFyZ3MuXG4gICAqIEBwYXJhbSB7U3luY0N1cnNvcn0gYXJncy5hZnRlckN1cnNvciAtIExhc3QgYWNrbm93bGVkZ2VkIGN1cnNvci5cbiAgICogQHBhcmFtIHtzdHJpbmd9IGFyZ3MuYXV0aGVudGljYXRpb25Ub2tlbiAtIEF1dGggdG9rZW4uXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBhcmdzLmJhdGNoU2l6ZSAtIFBhZ2Ugc2l6ZS5cbiAgICogQHBhcmFtIHsocGF5bG9hZDogU3luY0NoYW5nZXNSZXF1ZXN0KSA9PiBQcm9taXNlPFN5bmNDaGFuZ2VzUmVzcG9uc2U+fSBhcmdzLnBvc3RDaGFuZ2VzIC0gQ2hhbmdlcyBwb3N0ZXIuXG4gICAqIEBwYXJhbSB7U3luY0N1cnNvcn0gYXJncy51cFRvQ3Vyc29yIC0gU25hcHNob3QgdXBwZXItYm91bmQgY3Vyc29yLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTx7bmV4dEN1cnNvcjogU3luY0N1cnNvciwgc3luY3M6IFN5bmNDaGFuZ2VFbnZlbG9wZVtdLCB1cFRvQ3Vyc29yOiBTeW5jQ3Vyc29yfT59IE5vcm1hbGl6ZWQgY2hhbmdlcyBwYWdlLlxuICAgKi9cbiAgc3RhdGljIGFzeW5jIGNoYW5nZXNQYWdlKHthZnRlckN1cnNvciwgYXV0aGVudGljYXRpb25Ub2tlbiwgYmF0Y2hTaXplLCBwb3N0Q2hhbmdlcywgdXBUb0N1cnNvcn0pIHtcbiAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IHBvc3RDaGFuZ2VzKHtcbiAgICAgIGF1dGhlbnRpY2F0aW9uVG9rZW4sXG4gICAgICBsaW1pdDogYmF0Y2hTaXplLFxuICAgICAgLi4udGhpcy5jdXJzb3JQYXlsb2FkKFwiYWZ0ZXJcIiwgYWZ0ZXJDdXJzb3IpLFxuICAgICAgLi4udGhpcy5jdXJzb3JQYXlsb2FkKFwidXBUb1wiLCB1cFRvQ3Vyc29yKVxuICAgIH0pXG5cbiAgICB0aGlzLmVuc3VyZVN1Y2Nlc3NmdWxDaGFuZ2VzUmVzcG9uc2UocmVzcG9uc2UpXG5cbiAgICBjb25zdCBzeW5jcyA9IC8qKiBAdHlwZSB7dW5rbm93bltdfSAqLyAocmVzcG9uc2Uuc3luY3MpXG5cbiAgICByZXR1cm4ge1xuICAgICAgbmV4dEN1cnNvcjogdGhpcy5zeW5jQ3Vyc29yRnJvbVBheWxvYWQocmVzcG9uc2UubmV4dEN1cnNvciA/PyBudWxsKSxcbiAgICAgIHN5bmNzOiBzeW5jcy5tYXAoKHN5bmNQYXlsb2FkKSA9PiB0aGlzLnN5bmNFbnZlbG9wZUZyb21QYXlsb2FkKHN5bmNQYXlsb2FkKSksXG4gICAgICB1cFRvQ3Vyc29yOiB0aGlzLnN5bmNDdXJzb3JGcm9tUGF5bG9hZChyZXNwb25zZS51cFRvQ3Vyc29yID8/IG51bGwpXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIENoZWNrcyBBUEkgcmVzcG9uc2Ugc3RhdHVzIGFuZCBzaGFwZSBmb3IgY2hhbmdlLWZlZWQgcHVsbHMuXG4gICAqIEBwYXJhbSB7U3luY0NoYW5nZXNSZXNwb25zZX0gcmVzcG9uc2UgLSBDaGFuZ2VzIHJlc3BvbnNlLlxuICAgKiBAcmV0dXJucyB7dm9pZH1cbiAgICovXG4gIHN0YXRpYyBlbnN1cmVTdWNjZXNzZnVsQ2hhbmdlc1Jlc3BvbnNlKHJlc3BvbnNlKSB7XG4gICAgaWYgKHJlc3BvbnNlLnN0YXR1cyA9PT0gXCJlcnJvclwiKSB0aHJvdyBuZXcgRXJyb3IocmVzcG9uc2UuZXJyb3JNZXNzYWdlIHx8IFwiU3luYyBjaGFuZ2VzIGZhaWxlZFwiKVxuICAgIGlmICghQXJyYXkuaXNBcnJheShyZXNwb25zZS5zeW5jcykpIHRocm93IG5ldyBFcnJvcihcIlN5bmMgY2hhbmdlcyByZXNwb25zZSBtaXNzaW5nIHN5bmNzXCIpXG4gIH1cblxuICAvKipcbiAgICogQ29udmVydHMgYSBjdXJzb3IgaW50byByZXF1ZXN0IHBhcmFtcyB3aXRoIHRoZSBnaXZlbiBwcmVmaXguXG4gICAqIEBwYXJhbSB7XCJhZnRlclwiIHwgXCJ1cFRvXCJ9IHByZWZpeCAtIFJlcXVlc3QgZmllbGQgcHJlZml4LlxuICAgKiBAcGFyYW0ge1N5bmNDdXJzb3J9IGN1cnNvciAtIEN1cnNvciB0byBzZXJpYWxpemUuXG4gICAqIEByZXR1cm5zIHtSZWNvcmQ8c3RyaW5nLCBzdHJpbmcgfCBudW1iZXIgfCBudWxsPn0gUmVxdWVzdCBwYXJhbXMuXG4gICAqL1xuICBzdGF0aWMgY3Vyc29yUGF5bG9hZChwcmVmaXgsIGN1cnNvcikge1xuICAgIGlmICghY3Vyc29yKSByZXR1cm4ge31cblxuICAgIHJldHVybiB7XG4gICAgICBbYCR7cHJlZml4fUlkYF06IGN1cnNvci5pZCxcbiAgICAgIC4uLihjdXJzb3Iuc2VydmVyU2VxdWVuY2UgPyB7W2Ake3ByZWZpeH1TZXJ2ZXJTZXF1ZW5jZWBdOiBjdXJzb3Iuc2VydmVyU2VxdWVuY2V9IDoge30pLFxuICAgICAgW2Ake3ByZWZpeH1VcGRhdGVkQXRgXTogY3Vyc29yLnVwZGF0ZWRBdFxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBQYXJzZXMgYSBwZXJzaXN0ZWQgb3IgcmVzcG9uc2UgY3Vyc29yIHBheWxvYWQuXG4gICAqIEBwYXJhbSB7U3luY0N1cnNvciB8IHN0cmluZyB8IFJlY29yZDxzdHJpbmcsID8+IHwgbnVsbCB8IHVuZGVmaW5lZH0gcGF5bG9hZCAtIEN1cnNvciBwYXlsb2FkLlxuICAgKiBAcmV0dXJucyB7U3luY0N1cnNvcn0gUGFyc2VkIGN1cnNvci5cbiAgICovXG4gIHN0YXRpYyBzeW5jQ3Vyc29yRnJvbVBheWxvYWQocGF5bG9hZCkge1xuICAgIGlmICghcGF5bG9hZCkgcmV0dXJuIG51bGxcblxuICAgIGlmICh0eXBlb2YgcGF5bG9hZCA9PT0gXCJzdHJpbmdcIikge1xuICAgICAgdHJ5IHtcbiAgICAgICAgcmV0dXJuIHRoaXMuc3luY0N1cnNvckZyb21QYXlsb2FkKEpTT04ucGFyc2UocGF5bG9hZCkpXG4gICAgICB9IGNhdGNoIChfZXJyb3IpIHtcbiAgICAgICAgcmV0dXJuIHtpZDogbnVsbCwgc2VydmVyU2VxdWVuY2U6IG51bGwsIHVwZGF0ZWRBdDogcGF5bG9hZH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAodHlwZW9mIHBheWxvYWQgIT09IFwib2JqZWN0XCIgfHwgQXJyYXkuaXNBcnJheShwYXlsb2FkKSkgcmV0dXJuIG51bGxcblxuICAgIGNvbnN0IHVwZGF0ZWRBdCA9IHR5cGVvZiBwYXlsb2FkLnVwZGF0ZWRBdCA9PT0gXCJzdHJpbmdcIiA/IHBheWxvYWQudXBkYXRlZEF0IDogbnVsbFxuXG4gICAgaWYgKCF1cGRhdGVkQXQpIHJldHVybiBudWxsXG5cbiAgICByZXR1cm4ge1xuICAgICAgaWQ6IHBheWxvYWQuaWQgPT09IG51bGwgfHwgcGF5bG9hZC5pZCA9PT0gdW5kZWZpbmVkID8gbnVsbCA6IFN0cmluZyhwYXlsb2FkLmlkKSxcbiAgICAgIHNlcnZlclNlcXVlbmNlOiBvcHRpb25hbEludGVnZXIocGF5bG9hZC5zZXJ2ZXJTZXF1ZW5jZSA9PT0gXCJcIiA/IG51bGwgOiBwYXlsb2FkLnNlcnZlclNlcXVlbmNlKSxcbiAgICAgIHVwZGF0ZWRBdFxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBCdWlsZHMgYSBub3JtYWxpemVkIHN5bmMgcm93IGFkYXB0ZXIuXG4gICAqIEBwYXJhbSB7P30gcGF5bG9hZCAtIFJhdyBzeW5jIHBheWxvYWQuXG4gICAqIEByZXR1cm5zIHtTeW5jQ2hhbmdlRW52ZWxvcGV9IFN5bmMgcm93IGFkYXB0ZXIuXG4gICAqL1xuICBzdGF0aWMgc3luY0VudmVsb3BlRnJvbVBheWxvYWQocGF5bG9hZCkge1xuICAgIGlmICghcGF5bG9hZCB8fCB0eXBlb2YgcGF5bG9hZCAhPT0gXCJvYmplY3RcIiB8fCBBcnJheS5pc0FycmF5KHBheWxvYWQpKSB0aHJvdyBuZXcgRXJyb3IoXCJTeW5jIGNoYW5nZXMgZW50cnkgbXVzdCBiZSBhbiBvYmplY3RcIilcblxuICAgIGNvbnN0IHN5bmNQYXlsb2FkID0gLyoqIEB0eXBlIHtSZWNvcmQ8c3RyaW5nLCA/Pn0gKi8gKHBheWxvYWQpXG5cbiAgICByZXR1cm4ge1xuICAgICAgZGF0YTogKCkgPT4gc3luY1BheWxvYWQuZGF0YSxcbiAgICAgIGlkOiAoKSA9PiBzeW5jUGF5bG9hZC5pZCxcbiAgICAgIHJlc291cmNlSWQ6ICgpID0+IHN5bmNQYXlsb2FkLnJlc291cmNlSWQsXG4gICAgICByZXNvdXJjZVR5cGU6ICgpID0+IHN5bmNQYXlsb2FkLnJlc291cmNlVHlwZSA9PT0gbnVsbCB8fCBzeW5jUGF5bG9hZC5yZXNvdXJjZVR5cGUgPT09IHVuZGVmaW5lZCA/IG51bGwgOiBTdHJpbmcoc3luY1BheWxvYWQucmVzb3VyY2VUeXBlKSxcbiAgICAgIHN5bmNUeXBlOiAoKSA9PiBzeW5jUGF5bG9hZC5zeW5jVHlwZSA9PT0gbnVsbCB8fCBzeW5jUGF5bG9hZC5zeW5jVHlwZSA9PT0gdW5kZWZpbmVkID8gXCJcIiA6IFN0cmluZyhzeW5jUGF5bG9hZC5zeW5jVHlwZSlcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQnVpbGRzIGFuIGFwcC1jb25maWd1cmVkIHJlc291cmNlIGFwcGxpZXIgZm9yIHB1bGxlZCBzeW5jIHJvd3MuIFRoZSBzeW5jXG4gICAqIG1lY2hhbmljcyBzdGF5IGhlcmU7IGFwcHMgb25seSBkZWNsYXJlIHdoaWNoIG1vZGVscy9hdHRyaWJ1dGVzL2hvb2tzIGFyZVxuICAgKiBhbGxvd2VkIGZvciBlYWNoIHJlc291cmNlIHR5cGUuXG4gICAqIEBwYXJhbSB7UmVjb3JkPHN0cmluZywgU3luY1Jlc291cmNlQ29uZmlnPn0gcmVzb3VyY2VzIC0gUmVzb3VyY2UgcG9saWN5IG1hcC5cbiAgICogQHJldHVybnMgeyhzeW5jOiBTeW5jQ2hhbmdlRW52ZWxvcGUpID0+IFByb21pc2U8U3luY0NoYW5nZUFwcGx5UmVzdWx0Pn0gU3luYyBhcHBseSBjYWxsYmFjay5cbiAgICovXG4gIHN0YXRpYyByZXNvdXJjZUFwcGxpZXIocmVzb3VyY2VzKSB7XG4gICAgcmV0dXJuIGFzeW5jIChzeW5jKSA9PiBhd2FpdCB0aGlzLmFwcGx5UmVzb3VyY2VTeW5jKHtyZXNvdXJjZXMsIHN5bmN9KVxuICB9XG5cbiAgLyoqXG4gICAqIEFwcGxpZXMgb25lIHN5bmMgcm93IHVzaW5nIGRlY2xhcmF0aXZlIHJlc291cmNlIHBvbGljeS5cbiAgICogQHBhcmFtIHt7cmVzb3VyY2VzOiBSZWNvcmQ8c3RyaW5nLCBTeW5jUmVzb3VyY2VDb25maWc+LCBzeW5jOiBTeW5jQ2hhbmdlRW52ZWxvcGV9fSBhcmdzIC0gQXBwbHkgYXJncy5cbiAgICogQHJldHVybnMge1Byb21pc2U8U3luY0NoYW5nZUFwcGx5UmVzdWx0Pn0gQXBwbHkgcmVzdWx0LlxuICAgKi9cbiAgc3RhdGljIGFzeW5jIGFwcGx5UmVzb3VyY2VTeW5jKHtyZXNvdXJjZXMsIHN5bmN9KSB7XG4gICAgY29uc3QgcmVzb3VyY2VUeXBlID0gc3luYy5yZXNvdXJjZVR5cGUoKVxuICAgIGNvbnN0IHJlc291cmNlID0gcmVzb3VyY2VUeXBlID8gcmVzb3VyY2VzW3Jlc291cmNlVHlwZV0gOiB1bmRlZmluZWRcblxuICAgIGlmICghcmVzb3VyY2UgfHwgIXJlc291cmNlLmVuYWJsZWQpIHJldHVybiB7Y2hhbmdlZDogZmFsc2UsIHJlc291cmNlVHlwZX1cblxuICAgIGlmIChzeW5jLnN5bmNUeXBlKCkgPT09IFwiZGVsZXRlXCIpIHtcbiAgICAgIHJldHVybiB7Y2hhbmdlZDogYXdhaXQgdGhpcy5kZXN0cm95U3luY2VkUmVzb3VyY2Uoe3Jlc291cmNlLCBzeW5jfSksIHJlc291cmNlVHlwZX1cbiAgICB9XG5cbiAgICBjb25zdCBkYXRhID0gdGhpcy5zeW5jRGF0YShzeW5jKVxuICAgIGNvbnN0IHJlY29yZCA9IHJlc291cmNlLmZpbmRSZWNvcmQgPyBhd2FpdCByZXNvdXJjZS5maW5kUmVjb3JkKHtkYXRhLCByZXNvdXJjZUlkOiBzeW5jLnJlc291cmNlSWQoKSwgc3luY30pIDogYXdhaXQgcmVzb3VyY2UubW9kZWxDbGFzcy5maW5kT3JJbml0aWFsaXplQnkoe2lkOiBkYXRhLmlkID8/IHN5bmMucmVzb3VyY2VJZCgpfSlcbiAgICBjb25zdCBhdHRyaWJ1dGVzID0gYXdhaXQgcmVzb3VyY2UuYXR0cmlidXRlcyh7ZGF0YSwgcmVjb3JkLCBzeW5jfSlcbiAgICBsZXQgY2hhbmdlZCA9IGZhbHNlXG5cbiAgICByZWNvcmQuYXNzaWduKGF0dHJpYnV0ZXMpXG5cbiAgICBpZiAocmVjb3JkLmlzQ2hhbmdlZCgpKSB7XG4gICAgICBhd2FpdCByZWNvcmQuc2F2ZSgpXG4gICAgICBjaGFuZ2VkID0gdHJ1ZVxuICAgIH1cblxuICAgIGlmIChyZXNvdXJjZS5hZnRlckFwcGx5KSB7XG4gICAgICBjb25zdCBob29rQ2hhbmdlZCA9IGF3YWl0IHJlc291cmNlLmFmdGVyQXBwbHkoe2F0dHJpYnV0ZXMsIGRhdGEsIHJlY29yZCwgc3luY30pXG5cbiAgICAgIGNoYW5nZWQgfHw9IGhvb2tDaGFuZ2VkID09PSB0cnVlXG4gICAgfVxuXG4gICAgcmV0dXJuIHtjaGFuZ2VkLCByZXNvdXJjZVR5cGV9XG4gIH1cblxuICAvKipcbiAgICogRGVzdHJveXMgYSBzeW5jZWQgcmVzb3VyY2UgdmlhIGl0cyBkZWNsYXJlZCBtb2RlbCBwb2xpY3kuXG4gICAqIEBwYXJhbSB7e3Jlc291cmNlOiBTeW5jUmVzb3VyY2VDb25maWcsIHN5bmM6IFN5bmNDaGFuZ2VFbnZlbG9wZX19IGFyZ3MgLSBEZXN0cm95IGFyZ3MuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPGJvb2xlYW4+fSBXaGV0aGVyIGEgbG9jYWwgcm93IHdhcyBkZXN0cm95ZWQuXG4gICAqL1xuICBzdGF0aWMgYXN5bmMgZGVzdHJveVN5bmNlZFJlc291cmNlKHtyZXNvdXJjZSwgc3luY30pIHtcbiAgICBjb25zdCBpZCA9IHN5bmMucmVzb3VyY2VJZCgpXG4gICAgY29uc3QgcmVjb3JkID0gcmVzb3VyY2UuZmluZFJlY29yZEZvckRlbGV0ZSA/IGF3YWl0IHJlc291cmNlLmZpbmRSZWNvcmRGb3JEZWxldGUoe3Jlc291cmNlSWQ6IGlkLCBzeW5jfSkgOiBhd2FpdCByZXNvdXJjZS5tb2RlbENsYXNzLmZpbmRCeSh7aWR9KVxuXG4gICAgaWYgKCFyZWNvcmQpIHJldHVybiBmYWxzZVxuXG4gICAgYXdhaXQgcmVjb3JkLmRlc3Ryb3koKVxuICAgIHJldHVybiB0cnVlXG4gIH1cblxuICAvKipcbiAgICogUGFyc2VzIHRoZSBlbWJlZGRlZCBzeW5jIGRhdGEgSlNPTi9vYmplY3QuXG4gICAqIEBwYXJhbSB7U3luY0NoYW5nZUVudmVsb3BlfSBzeW5jIC0gU3luYyByb3cuXG4gICAqIEByZXR1cm5zIHtSZWNvcmQ8c3RyaW5nLCB1bmtub3duPn0gU3luYyBkYXRhIG9iamVjdC5cbiAgICovXG4gIHN0YXRpYyBzeW5jRGF0YShzeW5jKSB7XG4gICAgY29uc3QgZGF0YSA9IHN5bmMuZGF0YSgpXG5cbiAgICBpZiAoIWRhdGEpIHRocm93IG5ldyBFcnJvcihgU3luYyAke3N5bmMuaWQoKX0gaXMgbWlzc2luZyBkYXRhYClcbiAgICBpZiAodHlwZW9mIGRhdGEgPT09IFwic3RyaW5nXCIpIHJldHVybiAvKiogQHR5cGUge1JlY29yZDxzdHJpbmcsIHVua25vd24+fSAqLyAoSlNPTi5wYXJzZShkYXRhKSlcbiAgICBpZiAodHlwZW9mIGRhdGEgPT09IFwib2JqZWN0XCIgJiYgIUFycmF5LmlzQXJyYXkoZGF0YSkpIHJldHVybiAvKiogQHR5cGUge1JlY29yZDxzdHJpbmcsIHVua25vd24+fSAqLyAoZGF0YSlcblxuICAgIHRocm93IG5ldyBFcnJvcihgU3luYyAke3N5bmMuaWQoKX0gaGFzIGludmFsaWQgZGF0YWApXG4gIH1cblxuICAvKipcbiAgICogRHJhaW5zIHBlbmRpbmcgc3luYyByZWNvcmRzIGluIHN0YWJsZSBvcmRlciBhbmQgbWFya3MgYWNrbm93bGVkZ2VkIHJvd3MuXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gUmVwbGF5IGFyZ3MuXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhcmdzLmF1dGhlbnRpY2F0aW9uVG9rZW4gLSBBdXRoIHRva2VuIHRvIHNlbmQgd2l0aCByZXBsYXkgcmVxdWVzdHMuXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBbYXJncy5iYXRjaFNpemVdIC0gTWF4IHN5bmNzIHBlciByZXF1ZXN0LiBEZWZhdWx0cyB0byAxMDAuXG4gICAqIEBwYXJhbSB7KCkgPT4gUHJvbWlzZTxBcnJheTx1bmtub3duPj59IGFyZ3MucGVuZGluZ1N5bmNzIC0gTG9hZHMgcGVuZGluZyBsb2NhbCBzeW5jIHJvd3MgaW4gcmVwbGF5IG9yZGVyLlxuICAgKiBAcGFyYW0geyhzeW5jOiB1bmtub3duKSA9PiBzdHJpbmcgfCBudW1iZXIgfCBudWxsIHwgdW5kZWZpbmVkfSBhcmdzLnN5bmNJZCAtIFJldHVybnMgdGhlIGxvY2FsIHN5bmMgaWQuXG4gICAqIEBwYXJhbSB7KHN5bmM6IHVua25vd24pID0+IFJlY29yZDxzdHJpbmcsID8+fSBhcmdzLnN5bmNQYXlsb2FkIC0gQnVpbGRzIHRoZSBBUEkgc3luYyBlbnZlbG9wZS5cbiAgICogQHBhcmFtIHsocGF5bG9hZDoge2F1dGhlbnRpY2F0aW9uVG9rZW46IHN0cmluZywgc3luY3M6IEFycmF5PFJlY29yZDxzdHJpbmcsID8+Pn0pID0+IFByb21pc2U8U3luY1JlcGxheVJlc3BvbnNlPn0gYXJncy5wb3N0UmVwbGF5IC0gUG9zdHMgb25lIHJlcGxheSByZXF1ZXN0LlxuICAgKiBAcGFyYW0geyhzeW5jOiB1bmtub3duLCByZXNwb25zZTogU3luY1JlcGxheUl0ZW0pID0+IFByb21pc2U8dm9pZD59IGFyZ3MubWFya1N1Y2Nlc3NmdWwgLSBNYXJrcyBvbmUgc3luYyBhcyBzdWNjZXNzZnVsIGxvY2FsbHkuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSBSZXNvbHZlcyBhZnRlciBhbGwgYmF0Y2hlcyBhcmUgcmVwbGF5ZWQuXG4gICAqL1xuICBzdGF0aWMgYXN5bmMgcmVwbGF5UGVuZGluZyhhcmdzKSB7XG4gICAgY29uc3QgcGVuZGluZ1N5bmNzID0gYXdhaXQgYXJncy5wZW5kaW5nU3luY3MoKVxuICAgIGNvbnN0IGJhdGNoU2l6ZSA9IHRoaXMubm9ybWFsaXplZEJhdGNoU2l6ZShhcmdzLmJhdGNoU2l6ZSlcblxuICAgIGZvciAobGV0IG9mZnNldCA9IDA7IG9mZnNldCA8IHBlbmRpbmdTeW5jcy5sZW5ndGg7IG9mZnNldCArPSBiYXRjaFNpemUpIHtcbiAgICAgIGF3YWl0IHRoaXMucmVwbGF5QmF0Y2goey4uLmFyZ3MsIHBlbmRpbmdTeW5jczogcGVuZGluZ1N5bmNzLnNsaWNlKG9mZnNldCwgb2Zmc2V0ICsgYmF0Y2hTaXplKX0pXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFJlcGxheXMgb25lIGJhdGNoIG9mIHN5bmNzLlxuICAgKiBAcGFyYW0ge29iamVjdH0gYXJncyAtIFJlcGxheSBhcmdzLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gYXJncy5hdXRoZW50aWNhdGlvblRva2VuIC0gQXV0aCB0b2tlbi5cbiAgICogQHBhcmFtIHtBcnJheTx1bmtub3duPn0gYXJncy5wZW5kaW5nU3luY3MgLSBCYXRjaCBzeW5jcy5cbiAgICogQHBhcmFtIHsoc3luYzogdW5rbm93bikgPT4gc3RyaW5nIHwgbnVtYmVyIHwgbnVsbCB8IHVuZGVmaW5lZH0gYXJncy5zeW5jSWQgLSBTeW5jIGlkIGdldHRlci5cbiAgICogQHBhcmFtIHsoc3luYzogdW5rbm93bikgPT4gUmVjb3JkPHN0cmluZywgPz59IGFyZ3Muc3luY1BheWxvYWQgLSBQYXlsb2FkIGJ1aWxkZXIuXG4gICAqIEBwYXJhbSB7KHBheWxvYWQ6IHthdXRoZW50aWNhdGlvblRva2VuOiBzdHJpbmcsIHN5bmNzOiBBcnJheTxSZWNvcmQ8c3RyaW5nLCA/Pj59KSA9PiBQcm9taXNlPFN5bmNSZXBsYXlSZXNwb25zZT59IGFyZ3MucG9zdFJlcGxheSAtIFJlcGxheSBwb3N0ZXIuXG4gICAqIEBwYXJhbSB7KHN5bmM6IHVua25vd24sIHJlc3BvbnNlOiBTeW5jUmVwbGF5SXRlbSkgPT4gUHJvbWlzZTx2b2lkPn0gYXJncy5tYXJrU3VjY2Vzc2Z1bCAtIFN1Y2Nlc3MgaG9vay5cbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59IFJlc29sdmVzIGFmdGVyIHRoZSBiYXRjaCBpcyBhY2tub3dsZWRnZWQuXG4gICAqL1xuICBzdGF0aWMgYXN5bmMgcmVwbGF5QmF0Y2goYXJncykge1xuICAgIGNvbnN0IHthdXRoZW50aWNhdGlvblRva2VuLCBtYXJrU3VjY2Vzc2Z1bCwgcGVuZGluZ1N5bmNzLCBwb3N0UmVwbGF5LCBzeW5jSWQsIHN5bmNQYXlsb2FkfSA9IGFyZ3NcblxuICAgIGlmIChwZW5kaW5nU3luY3MubGVuZ3RoID09PSAwKSByZXR1cm5cblxuICAgIGNvbnN0IHN5bmNzQnlJZCA9IG5ldyBNYXAoKVxuXG4gICAgZm9yIChjb25zdCBzeW5jIG9mIHBlbmRpbmdTeW5jcykge1xuICAgICAgY29uc3QgaWQgPSBzeW5jSWQoc3luYylcblxuICAgICAgaWYgKGlkICE9PSB1bmRlZmluZWQgJiYgaWQgIT09IG51bGwpIHN5bmNzQnlJZC5zZXQoU3RyaW5nKGlkKSwgc3luYylcbiAgICB9XG5cbiAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IHBvc3RSZXBsYXkoe1xuICAgICAgYXV0aGVudGljYXRpb25Ub2tlbixcbiAgICAgIHN5bmNzOiBwZW5kaW5nU3luY3MubWFwKChzeW5jKSA9PiBzeW5jUGF5bG9hZChzeW5jKSlcbiAgICB9KVxuXG4gICAgdGhpcy5lbnN1cmVTdWNjZXNzZnVsUmVzcG9uc2UocmVzcG9uc2UpXG5cbiAgICBmb3IgKGNvbnN0IHN5bmNSZXNwb25zZSBvZiByZXNwb25zZS5zeW5jcyB8fCBbXSkge1xuICAgICAgY29uc3Qgc3luYyA9IHN5bmNzQnlJZC5nZXQoU3RyaW5nKHN5bmNSZXNwb25zZS5pZCkpXG5cbiAgICAgIGlmICghc3luYykgY29udGludWVcbiAgICAgIGlmIChzeW5jUmVzcG9uc2Uuc3luY1N0YXRlICE9PSBcInN1Y2Nlc3NmdWxcIikge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYEludmFsaWQgc3luYyBzdGF0ZSByZXR1cm5lZCBmb3Igc3luYyAke1N0cmluZyhzeW5jUmVzcG9uc2UuaWQpfTogJHtTdHJpbmcoc3luY1Jlc3BvbnNlLnN5bmNTdGF0ZSl9YClcbiAgICAgIH1cblxuICAgICAgYXdhaXQgbWFya1N1Y2Nlc3NmdWwoc3luYywgc3luY1Jlc3BvbnNlKVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBDaGVja3MgQVBJIHJlc3BvbnNlIHN0YXR1cyBhbmQgc2hhcGUuXG4gICAqIEBwYXJhbSB7U3luY1JlcGxheVJlc3BvbnNlfSByZXNwb25zZSAtIFJlcGxheSByZXNwb25zZS5cbiAgICogQHJldHVybnMge3ZvaWR9XG4gICAqL1xuICBzdGF0aWMgZW5zdXJlU3VjY2Vzc2Z1bFJlc3BvbnNlKHJlc3BvbnNlKSB7XG4gICAgaWYgKHJlc3BvbnNlLnN0YXR1cyA9PT0gXCJlcnJvclwiKSB0aHJvdyBuZXcgRXJyb3IocmVzcG9uc2UuZXJyb3JNZXNzYWdlIHx8IFwiU3luYyBmYWlsZWRcIilcbiAgICBpZiAoIUFycmF5LmlzQXJyYXkocmVzcG9uc2Uuc3luY3MpKSB0aHJvdyBuZXcgRXJyb3IoXCJTeW5jIHJlc3BvbnNlIG1pc3Npbmcgc3luY3NcIilcbiAgfVxuXG4gIC8qKlxuICAgKiBOb3JtYWxpemVzIGEgcG9zaXRpdmUgYmF0Y2ggc2l6ZS5cbiAgICogQHBhcmFtIHtudW1iZXIgfCB1bmRlZmluZWR9IGJhdGNoU2l6ZSAtIEJhdGNoIHNpemUuXG4gICAqIEByZXR1cm5zIHtudW1iZXJ9IFBvc2l0aXZlIGJhdGNoIHNpemUuXG4gICAqL1xuICBzdGF0aWMgbm9ybWFsaXplZEJhdGNoU2l6ZShiYXRjaFNpemUpIHtcbiAgICBpZiAodHlwZW9mIGJhdGNoU2l6ZSAhPT0gXCJudW1iZXJcIiB8fCAhTnVtYmVyLmlzRmluaXRlKGJhdGNoU2l6ZSkgfHwgYmF0Y2hTaXplIDwgMSkgcmV0dXJuIDEwMFxuXG4gICAgcmV0dXJuIE1hdGguZmxvb3IoYmF0Y2hTaXplKVxuICB9XG59XG4iXX0=
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic `/velocious/sync` transport controller.
|
|
3
|
+
*
|
|
4
|
+
* Apps provide a sync resource class; Velocious owns endpoint shape and
|
|
5
|
+
* rendering. The app resource owns auth, scoping, and domain-specific
|
|
6
|
+
* replay/change hooks.
|
|
7
|
+
*/
|
|
8
|
+
export default class SyncApiController extends Controller {
|
|
9
|
+
/**
|
|
10
|
+
* Builds a sync API controller class bound to the given resource.
|
|
11
|
+
* @param {import("../configuration-types.js").FrontendModelResourceClassType} ResourceClass - Sync resource class.
|
|
12
|
+
* @returns {typeof SyncApiController} Controller class for the resource.
|
|
13
|
+
*/
|
|
14
|
+
static withSyncResourceClass(ResourceClass: import("../configuration-types.js").FrontendModelResourceClassType): typeof SyncApiController;
|
|
15
|
+
/**
|
|
16
|
+
* Mounts the standard Velocious sync endpoints into a route configuration.
|
|
17
|
+
* @param {{configuration?: import("../configuration.js").default, at?: string, syncResourceClass?: import("../configuration-types.js").FrontendModelResourceClassType}} args - Mount args.
|
|
18
|
+
* @returns {void}
|
|
19
|
+
*/
|
|
20
|
+
static mountInto(args: {
|
|
21
|
+
configuration?: import("../configuration.js").default;
|
|
22
|
+
at?: string;
|
|
23
|
+
syncResourceClass?: import("../configuration-types.js").FrontendModelResourceClassType;
|
|
24
|
+
}): void;
|
|
25
|
+
/**
|
|
26
|
+
* Normalizes a sync mount path.
|
|
27
|
+
* @param {string} at - Mount path.
|
|
28
|
+
* @returns {string} Normalized mount path without trailing slash.
|
|
29
|
+
*/
|
|
30
|
+
static normalizedMountPath(at: string): string;
|
|
31
|
+
/**
|
|
32
|
+
* Renders a replay response from the configured sync resource.
|
|
33
|
+
* @returns {Promise<void>}
|
|
34
|
+
*/
|
|
35
|
+
replay(): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Renders a change-feed response from the configured sync resource.
|
|
38
|
+
* @returns {Promise<void>}
|
|
39
|
+
*/
|
|
40
|
+
changes(): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Builds the sync resource that backs the transport endpoint.
|
|
43
|
+
* @param {Record<string, unknown>} params - Request params/body.
|
|
44
|
+
* @returns {FrontendModelBaseResource} Sync resource instance.
|
|
45
|
+
*/
|
|
46
|
+
syncResource(params: Record<string, unknown>): FrontendModelBaseResource;
|
|
47
|
+
/**
|
|
48
|
+
* Returns the app-provided sync resource class.
|
|
49
|
+
* @returns {import("../configuration-types.js").FrontendModelResourceClassType} Sync resource class.
|
|
50
|
+
*/
|
|
51
|
+
syncResourceClass(): import("../configuration-types.js").FrontendModelResourceClassType;
|
|
52
|
+
/**
|
|
53
|
+
* Raises a configuration error for subclasses that do not provide a resource.
|
|
54
|
+
* @returns {import("../configuration-types.js").FrontendModelResourceClassType} Sync resource class.
|
|
55
|
+
*/
|
|
56
|
+
missingSyncResourceClass(): import("../configuration-types.js").FrontendModelResourceClassType;
|
|
57
|
+
/** Raises a configuration error for subclasses that do not provide a resource. */
|
|
58
|
+
raiseMissingSyncResourceClass(): void;
|
|
59
|
+
/**
|
|
60
|
+
* Returns the model class exposed by the sync resource.
|
|
61
|
+
* @returns {typeof import("../database/record/index.js").default} Sync model class.
|
|
62
|
+
*/
|
|
63
|
+
syncModelClass(): typeof import("../database/record/index.js").default;
|
|
64
|
+
/**
|
|
65
|
+
* Returns the model name used to initialize the resource.
|
|
66
|
+
* @returns {string} Sync model name.
|
|
67
|
+
*/
|
|
68
|
+
syncModelName(): string;
|
|
69
|
+
/**
|
|
70
|
+
* Builds the minimal resource configuration needed by the sync resource.
|
|
71
|
+
* @param {import("../configuration-types.js").FrontendModelResourceClassType} ResourceClass - Sync resource class.
|
|
72
|
+
* @returns {import("../configuration-types.js").FrontendModelResourceConfiguration} Resource configuration.
|
|
73
|
+
*/
|
|
74
|
+
syncResourceConfiguration(ResourceClass: import("../configuration-types.js").FrontendModelResourceClassType): import("../configuration-types.js").FrontendModelResourceConfiguration;
|
|
75
|
+
}
|
|
76
|
+
import Controller from "../controller.js";
|
|
77
|
+
import FrontendModelBaseResource from "../frontend-model-resource/base-resource.js";
|
|
78
|
+
//# sourceMappingURL=sync-api-controller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-api-controller.d.ts","sourceRoot":"","sources":["../../../src/sync/sync-api-controller.js"],"names":[],"mappings":"AAKA;;;;;;GAMG;AACH;IAsDE;;;;OAIG;IACH,4CAHW,OAAO,2BAA2B,EAAE,8BAA8B,GAChE,OAAO,iBAAiB,CAYpC;IAED;;;;OAIG;IACH,uBAHW;QAAC,aAAa,CAAC,EAAE,OAAO,qBAAqB,EAAE,OAAO,CAAC;QAAC,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,iBAAiB,CAAC,EAAE,OAAO,2BAA2B,EAAE,8BAA8B,CAAA;KAAC,GAC1J,IAAI,CAahB;IAED;;;;OAIG;IACH,+BAHW,MAAM,GACJ,MAAM,CAQlB;IAnGD;;;OAGG;IACH,UAFa,OAAO,CAAC,IAAI,CAAC,CAMzB;IAED;;;OAGG;IACH,WAFa,OAAO,CAAC,IAAI,CAAC,CAMzB;IAED;;;;OAIG;IACH,qBAHW,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACrB,yBAAyB,CAoBrC;IAED;;;OAGG;IACH,qBAFa,OAAO,2BAA2B,EAAE,8BAA8B,CAI9E;IAkDD;;;OAGG;IACH,4BAFa,OAAO,2BAA2B,EAAE,8BAA8B,CAI9E;IAED,kFAAkF;IAClF,sCAEC;IAED;;;OAGG;IACH,kBAFa,cAAc,6BAA6B,EAAE,OAAO,CAShE;IAED;;;OAGG;IACH,iBAFa,MAAM,CAIlB;IAED;;;;OAIG;IACH,yCAHW,OAAO,2BAA2B,EAAE,8BAA8B,GAChE,OAAO,2BAA2B,EAAE,kCAAkC,CAOlF;CACF;uBA7JsB,kBAAkB;sCACH,6CAA6C"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import Controller from "../controller.js";
|
|
3
|
+
import FrontendModelBaseResource from "../frontend-model-resource/base-resource.js";
|
|
4
|
+
/**
|
|
5
|
+
* Generic `/velocious/sync` transport controller.
|
|
6
|
+
*
|
|
7
|
+
* Apps provide a sync resource class; Velocious owns endpoint shape and
|
|
8
|
+
* rendering. The app resource owns auth, scoping, and domain-specific
|
|
9
|
+
* replay/change hooks.
|
|
10
|
+
*/
|
|
11
|
+
export default class SyncApiController extends Controller {
|
|
12
|
+
/**
|
|
13
|
+
* Renders a replay response from the configured sync resource.
|
|
14
|
+
* @returns {Promise<void>}
|
|
15
|
+
*/
|
|
16
|
+
async replay() {
|
|
17
|
+
const resource = /** @type {FrontendModelBaseResource & {replay: () => Promise<unknown>}} */ (this.syncResource(this.params()));
|
|
18
|
+
await this.render({ json: /** @type {object} */ (await resource.replay()) });
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Renders a change-feed response from the configured sync resource.
|
|
22
|
+
* @returns {Promise<void>}
|
|
23
|
+
*/
|
|
24
|
+
async changes() {
|
|
25
|
+
const resource = /** @type {FrontendModelBaseResource & {changes: () => Promise<unknown>}} */ (this.syncResource(this.params()));
|
|
26
|
+
await this.render({ json: /** @type {object} */ (await resource.changes()) });
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Builds the sync resource that backs the transport endpoint.
|
|
30
|
+
* @param {Record<string, unknown>} params - Request params/body.
|
|
31
|
+
* @returns {FrontendModelBaseResource} Sync resource instance.
|
|
32
|
+
*/
|
|
33
|
+
syncResource(params) {
|
|
34
|
+
const ResourceClass = this.syncResourceClass();
|
|
35
|
+
const ability = this.currentAbility();
|
|
36
|
+
return new ResourceClass({
|
|
37
|
+
ability,
|
|
38
|
+
controller: /** @type {import("../frontend-model-resource/base-resource.js").FrontendModelResourceController} */ ( /** @type {unknown} */(this)),
|
|
39
|
+
context: {
|
|
40
|
+
...(ability?.getContext() || {}),
|
|
41
|
+
params: this.params(),
|
|
42
|
+
request: this.request()
|
|
43
|
+
},
|
|
44
|
+
locals: ability?.getLocals() || {},
|
|
45
|
+
modelClass: this.syncModelClass(),
|
|
46
|
+
modelName: this.syncModelName(),
|
|
47
|
+
params: /** @type {import("../configuration-types.js").VelociousParams} */ ( /** @type {unknown} */(params)),
|
|
48
|
+
resourceConfiguration: this.syncResourceConfiguration(ResourceClass)
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Returns the app-provided sync resource class.
|
|
53
|
+
* @returns {import("../configuration-types.js").FrontendModelResourceClassType} Sync resource class.
|
|
54
|
+
*/
|
|
55
|
+
syncResourceClass() {
|
|
56
|
+
return this.missingSyncResourceClass();
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Builds a sync API controller class bound to the given resource.
|
|
60
|
+
* @param {import("../configuration-types.js").FrontendModelResourceClassType} ResourceClass - Sync resource class.
|
|
61
|
+
* @returns {typeof SyncApiController} Controller class for the resource.
|
|
62
|
+
*/
|
|
63
|
+
static withSyncResourceClass(ResourceClass) {
|
|
64
|
+
return class ConfiguredSyncApiController extends this {
|
|
65
|
+
/**
|
|
66
|
+
* Returns the configured sync resource class.
|
|
67
|
+
* @returns {import("../configuration-types.js").FrontendModelResourceClassType} Sync resource class.
|
|
68
|
+
*/
|
|
69
|
+
syncResourceClass() {
|
|
70
|
+
return ResourceClass;
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Mounts the standard Velocious sync endpoints into a route configuration.
|
|
76
|
+
* @param {{configuration?: import("../configuration.js").default, at?: string, syncResourceClass?: import("../configuration-types.js").FrontendModelResourceClassType}} args - Mount args.
|
|
77
|
+
* @returns {void}
|
|
78
|
+
*/
|
|
79
|
+
static mountInto(args) {
|
|
80
|
+
const { configuration, syncResourceClass } = args;
|
|
81
|
+
const ControllerClass = syncResourceClass ? this.withSyncResourceClass(syncResourceClass) : this;
|
|
82
|
+
const at = this.normalizedMountPath(args.at || "/velocious/sync");
|
|
83
|
+
if (!configuration)
|
|
84
|
+
throw new Error("SyncApiController.mountInto requires configuration");
|
|
85
|
+
configuration.routes((routes) => {
|
|
86
|
+
routes.post(`${at}/changes`, { to: [ControllerClass, "changes"] });
|
|
87
|
+
routes.post(`${at}/replay`, { to: [ControllerClass, "replay"] });
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Normalizes a sync mount path.
|
|
92
|
+
* @param {string} at - Mount path.
|
|
93
|
+
* @returns {string} Normalized mount path without trailing slash.
|
|
94
|
+
*/
|
|
95
|
+
static normalizedMountPath(at) {
|
|
96
|
+
if (typeof at !== "string" || !at.startsWith("/")) {
|
|
97
|
+
throw new Error(`SyncApiController mount path must start with '/', got: ${String(at)}`);
|
|
98
|
+
}
|
|
99
|
+
return at.replace(/\/+$/u, "") || "/";
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Raises a configuration error for subclasses that do not provide a resource.
|
|
103
|
+
* @returns {import("../configuration-types.js").FrontendModelResourceClassType} Sync resource class.
|
|
104
|
+
*/
|
|
105
|
+
missingSyncResourceClass() {
|
|
106
|
+
return /** @type {typeof FrontendModelBaseResource} */ ( /** @type {unknown} */(this.raiseMissingSyncResourceClass()));
|
|
107
|
+
}
|
|
108
|
+
/** Raises a configuration error for subclasses that do not provide a resource. */
|
|
109
|
+
raiseMissingSyncResourceClass() {
|
|
110
|
+
throw new Error("SyncApiController.syncResourceClass must be implemented");
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Returns the model class exposed by the sync resource.
|
|
114
|
+
* @returns {typeof import("../database/record/index.js").default} Sync model class.
|
|
115
|
+
*/
|
|
116
|
+
syncModelClass() {
|
|
117
|
+
const ResourceClass = this.syncResourceClass();
|
|
118
|
+
const modelClass = ResourceClass.ModelClass;
|
|
119
|
+
if (!modelClass)
|
|
120
|
+
throw new Error("Sync resource class must define static ModelClass");
|
|
121
|
+
return modelClass;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Returns the model name used to initialize the resource.
|
|
125
|
+
* @returns {string} Sync model name.
|
|
126
|
+
*/
|
|
127
|
+
syncModelName() {
|
|
128
|
+
return this.syncModelClass().name;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Builds the minimal resource configuration needed by the sync resource.
|
|
132
|
+
* @param {import("../configuration-types.js").FrontendModelResourceClassType} ResourceClass - Sync resource class.
|
|
133
|
+
* @returns {import("../configuration-types.js").FrontendModelResourceConfiguration} Resource configuration.
|
|
134
|
+
*/
|
|
135
|
+
syncResourceConfiguration(ResourceClass) {
|
|
136
|
+
return /** @type {import("../configuration-types.js").FrontendModelResourceConfiguration} */ ({
|
|
137
|
+
attributes: ResourceClass.attributes || {},
|
|
138
|
+
sync: { enabled: true }
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3luYy1hcGktY29udHJvbGxlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9zeW5jL3N5bmMtYXBpLWNvbnRyb2xsZXIuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsWUFBWTtBQUVaLE9BQU8sVUFBVSxNQUFNLGtCQUFrQixDQUFBO0FBQ3pDLE9BQU8seUJBQXlCLE1BQU0sNkNBQTZDLENBQUE7QUFFbkY7Ozs7OztHQU1HO0FBQ0gsTUFBTSxDQUFDLE9BQU8sT0FBTyxpQkFBa0IsU0FBUSxVQUFVO0lBQ3ZEOzs7T0FHRztJQUNILEtBQUssQ0FBQyxNQUFNO1FBQ1YsTUFBTSxRQUFRLEdBQUcsMkVBQTJFLENBQUMsQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDLENBQUE7UUFFL0gsTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDLEVBQUMsSUFBSSxFQUFFLHFCQUFxQixDQUFDLENBQUMsTUFBTSxRQUFRLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBQyxDQUFDLENBQUE7SUFDNUUsQ0FBQztJQUVEOzs7T0FHRztJQUNILEtBQUssQ0FBQyxPQUFPO1FBQ1gsTUFBTSxRQUFRLEdBQUcsNEVBQTRFLENBQUMsQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDLENBQUE7UUFFaEksTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDLEVBQUMsSUFBSSxFQUFFLHFCQUFxQixDQUFDLENBQUMsTUFBTSxRQUFRLENBQUMsT0FBTyxFQUFFLENBQUMsRUFBQyxDQUFDLENBQUE7SUFDN0UsQ0FBQztJQUVEOzs7O09BSUc7SUFDSCxZQUFZLENBQUMsTUFBTTtRQUNqQixNQUFNLGFBQWEsR0FBRyxJQUFJLENBQUMsaUJBQWlCLEVBQUUsQ0FBQTtRQUM5QyxNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsY0FBYyxFQUFFLENBQUE7UUFFckMsT0FBTyxJ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QUM7SUFFRDs7O09BR0c7SUFDSCxjQUFjO1FBQ1osTUFBTSxhQUFhLEdBQUcsSUFBSSxDQUFDLGlCQUFpQixFQUFFLENBQUE7UUFDOUMsTUFBTSxVQUFVLEdBQUcsYUFBYSxDQUFDLFVBQVUsQ0FBQTtRQUUzQyxJQUFJLENBQUMsVUFBVTtZQUFFLE1BQU0sSUFBSSxLQUFLLENBQUMsbURBQW1ELENBQUMsQ0FBQTtRQUVyRixPQUFPLFVBQVUsQ0FBQTtJQUNuQixDQUFDO0lBRUQ7OztPQUdHO0lBQ0gsYUFBYTtRQUNYLE9BQU8sSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDLElBQUksQ0FBQTtJQUNuQyxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNILHlCQUF5QixDQUFDLGFBQWE7UUFDckMsT0FBTyxxRkFBcUYsQ0FBQyxDQUFDO1lBQzVGLFVBQVUsRUFBRSxhQUFhLENBQUMsVUFBVSxJQUFJLEVBQUU7WUFDMUMsSUFBSSxFQUFFLEVBQUMsT0FBTyxFQUFFLElBQUksRUFBQztTQUN0QixDQUFDLENBQUE7SUFDSixDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBAdHMtY2hlY2tcblxuaW1wb3J0IENvbnRyb2xsZXIgZnJvbSBcIi4uL2NvbnRyb2xsZXIuanNcIlxuaW1wb3J0IEZyb250ZW5kTW9kZWxCYXNlUmVzb3VyY2UgZnJvbSBcIi4uL2Zyb250ZW5kLW1vZGVsLXJlc291cmNlL2Jhc2UtcmVzb3VyY2UuanNcIlxuXG4vKipcbiAqIEdlbmVyaWMgYC92ZWxvY2lvdXMvc3luY2AgdHJhbnNwb3J0IGNvbnRyb2xsZXIuXG4gKlxuICogQXBwcyBwcm92aWRlIGEgc3luYyByZXNvdXJjZSBjbGFzczsgVmVsb2Npb3VzIG93bnMgZW5kcG9pbnQgc2hhcGUgYW5kXG4gKiByZW5kZXJpbmcuIFRoZSBhcHAgcmVzb3VyY2Ugb3ducyBhdXRoLCBzY29waW5nLCBhbmQgZG9tYWluLXNwZWNpZmljXG4gKiByZXBsYXkvY2hhbmdlIGhvb2tzLlxuICovXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBTeW5jQXBpQ29udHJvbGxlciBleHRlbmRzIENvbnRyb2xsZXIge1xuICAvKipcbiAgICogUmVuZGVycyBhIHJlcGxheSByZXNwb25zZSBmcm9tIHRoZSBjb25maWd1cmVkIHN5bmMgcmVzb3VyY2UuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fVxuICAgKi9cbiAgYXN5bmMgcmVwbGF5KCkge1xuICAgIGNvbnN0IHJlc291cmNlID0gLyoqIEB0eXBlIHtGcm9udGVuZE1vZGVsQmFzZVJlc291cmNlICYge3JlcGxheTogKCkgPT4gUHJvbWlzZTx1bmtub3duPn19ICovICh0aGlzLnN5bmNSZXNvdXJjZSh0aGlzLnBhcmFtcygpKSlcblxuICAgIGF3YWl0IHRoaXMucmVuZGVyKHtqc29uOiAvKiogQHR5cGUge29iamVjdH0gKi8gKGF3YWl0IHJlc291cmNlLnJlcGxheSgpKX0pXG4gIH1cblxuICAvKipcbiAgICogUmVuZGVycyBhIGNoYW5nZS1mZWVkIHJlc3BvbnNlIGZyb20gdGhlIGNvbmZpZ3VyZWQgc3luYyByZXNvdXJjZS5cbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59XG4gICAqL1xuICBhc3luYyBjaGFuZ2VzKCkge1xuICAgIGNvbnN0IHJlc291cmNlID0gLyoqIEB0eXBlIHtGcm9udGVuZE1vZGVsQmFzZVJlc291cmNlICYge2NoYW5nZXM6ICgpID0+IFByb21pc2U8dW5rbm93bj59fSAqLyAodGhpcy5zeW5jUmVzb3VyY2UodGhpcy5wYXJhbXMoKSkpXG5cbiAgICBhd2FpdCB0aGlzLnJlbmRlcih7anNvbjogLyoqIEB0eXBlIHtvYmplY3R9ICovIChhd2FpdCByZXNvdXJjZS5jaGFuZ2VzKCkpfSlcbiAgfVxuXG4gIC8qKlxuICAgKiBCdWlsZHMgdGhlIHN5bmMgcmVzb3VyY2UgdGhhdCBiYWNrcyB0aGUgdHJhbnNwb3J0IGVuZHBvaW50LlxuICAgKiBAcGFyYW0ge1JlY29yZDxzdHJpbmcsIHVua25vd24+fSBwYXJhbXMgLSBSZXF1ZXN0IHBhcmFtcy9ib2R5LlxuICAgKiBAcmV0dXJucyB7RnJvbnRlbmRNb2RlbEJhc2VSZXNvdXJjZX0gU3luYyByZXNvdXJjZSBpbnN0YW5jZS5cbiAgICovXG4gIHN5bmNSZXNvdXJjZShwYXJhbXMpIHtcbiAgICBjb25zdCBSZXNvdXJjZUNsYXNzID0gdGhpcy5zeW5jUmVzb3VyY2VDbGFzcygpXG4gICAgY29uc3QgYWJpbGl0eSA9IHRoaXMuY3VycmVudEFiaWxpdHkoKVxuXG4gICAgcmV0dXJuIG5ldyBSZXNvdXJjZUNsYXNzKHtcbiAgICAgIGFiaWxpdHksXG4gICAgICBjb250cm9sbGVyOiAvKiogQHR5cGUge2ltcG9ydChcIi4uL2Zyb250ZW5kLW1vZGVsLXJlc291cmNlL2Jhc2UtcmVzb3VyY2UuanNcIikuRnJvbnRlbmRNb2RlbFJlc291cmNlQ29udHJvbGxlcn0gKi8gKC8qKiBAdHlwZSB7dW5rbm93bn0gKi8gKHRoaXMpKSxcbiAgICAgIGNvbnRleHQ6IHtcbiAgICAgICAgLi4uKGFiaWxpdHk/LmdldENvbnRleHQoKSB8fCB7fSksXG4gICAgICAgIHBhcmFtczogdGhpcy5wYXJhbXMoKSxcbiAgICAgICAgcmVxdWVzdDogdGhpcy5yZXF1ZXN0KClcbiAgICAgIH0sXG4gICAgICBsb2NhbHM6IGFiaWxpdHk/LmdldExvY2FscygpIHx8IHt9LFxuICAgICAgbW9kZWxDbGFzczogdGhpcy5zeW5jTW9kZWxDbGFzcygpLFxuICAgICAgbW9kZWxOYW1lOiB0aGlzLnN5bmNNb2RlbE5hbWUoKSxcbiAgICAgIHBhcmFtczogLyoqIEB0eXBlIHtpbXBvcnQoXCIuLi9jb25maWd1cmF0aW9uLXR5cGVzLmpzXCIpLlZlbG9jaW91c1BhcmFtc30gKi8gKC8qKiBAdHlwZSB7dW5rbm93bn0gKi8gKHBhcmFtcykpLFxuICAgICAgcmVzb3VyY2VDb25maWd1cmF0aW9uOiB0aGlzLnN5bmNSZXNvdXJjZUNvbmZpZ3VyYXRpb24oUmVzb3VyY2VDbGFzcylcbiAgICB9KVxuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgdGhlIGFwcC1wcm92aWRlZCBzeW5jIHJlc291cmNlIGNsYXNzLlxuICAgKiBAcmV0dXJucyB7aW1wb3J0KFwiLi4vY29uZmlndXJhdGlvbi10eXBlcy5qc1wiKS5Gcm9udGVuZE1vZGVsUmVzb3VyY2VDbGFzc1R5cGV9IFN5bmMgcmVzb3VyY2UgY2xhc3MuXG4gICAqL1xuICBzeW5jUmVzb3VyY2VDbGFzcygpIHtcbiAgICByZXR1cm4gdGhpcy5taXNzaW5nU3luY1Jlc291cmNlQ2xhc3MoKVxuICB9XG5cbiAgLyoqXG4gICAqIEJ1aWxkcyBhIHN5bmMgQVBJIGNvbnRyb2xsZXIgY2xhc3MgYm91bmQgdG8gdGhlIGdpdmVuIHJlc291cmNlLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4uL2NvbmZpZ3VyYXRpb24tdHlwZXMuanNcIikuRnJvbnRlbmRNb2RlbFJlc291cmNlQ2xhc3NUeXBlfSBSZXNvdXJjZUNsYXNzIC0gU3luYyByZXNvdXJjZSBjbGFzcy5cbiAgICogQHJldHVybnMge3R5cGVvZiBTeW5jQXBpQ29udHJvbGxlcn0gQ29udHJvbGxlciBjbGFzcyBmb3IgdGhlIHJlc291cmNlLlxuICAgKi9cbiAgc3RhdGljIHdpdGhTeW5jUmVzb3VyY2VDbGFzcyhSZXNvdXJjZUNsYXNzKSB7XG4gICAgcmV0dXJuIGNsYXNzIENvbmZpZ3VyZWRTeW5jQXBpQ29udHJvbGxlciBleHRlbmRzIHRoaXMge1xuICAgICAgLyoqXG4gICAgICAgKiBSZXR1cm5zIHRoZSBjb25maWd1cmVkIHN5bmMgcmVzb3VyY2UgY2xhc3MuXG4gICAgICAgKiBAcmV0dXJucyB7aW1wb3J0KFwiLi4vY29uZmlndXJhdGlvbi10eXBlcy5qc1wiKS5Gcm9udGVuZE1vZGVsUmVzb3VyY2VDbGFzc1R5cGV9IFN5bmMgcmVzb3VyY2UgY2xhc3MuXG4gICAgICAgKi9cbiAgICAgIHN5bmNSZXNvdXJjZUNsYXNzKCkge1xuICAgICAgICByZXR1cm4gUmVzb3VyY2VDbGFzc1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBNb3VudHMgdGhlIHN0YW5kYXJkIFZlbG9jaW91cyBzeW5jIGVuZHBvaW50cyBpbnRvIGEgcm91dGUgY29uZmlndXJhdGlvbi5cbiAgICogQHBhcmFtIHt7Y29uZmlndXJhdGlvbj86IGltcG9ydChcIi4uL2NvbmZpZ3VyYXRpb24uanNcIikuZGVmYXVsdCwgYXQ/OiBzdHJpbmcsIHN5bmNSZXNvdXJjZUNsYXNzPzogaW1wb3J0KFwiLi4vY29uZmlndXJhdGlvbi10eXBlcy5qc1wiKS5Gcm9udGVuZE1vZGVsUmVzb3VyY2VDbGFzc1R5cGV9fSBhcmdzIC0gTW91bnQgYXJncy5cbiAgICogQHJldHVybnMge3ZvaWR9XG4gICAqL1xuICBzdGF0aWMgbW91bnRJbnRvKGFyZ3MpIHtcbiAgICBjb25zdCB7Y29uZmlndXJhdGlvbiwgc3luY1Jlc291cmNlQ2xhc3N9ID0gYXJnc1xuICAgIGNvbnN0IENvbnRyb2xsZXJDbGFzcyA9IHN5bmNSZXNvdXJjZUNsYXNzID8gdGhpcy53aXRoU3luY1Jlc291cmNlQ2xhc3Moc3luY1Jlc291cmNlQ2xhc3MpIDogdGhpc1xuICAgIGNvbnN0IGF0ID0gdGhpcy5ub3JtYWxpemVkTW91bnRQYXRoKGFyZ3MuYXQgfHwgXCIvdmVsb2Npb3VzL3N5bmNcIilcblxuICAgIGlmICghY29uZmlndXJhdGlvbikgdGhyb3cgbmV3IEVycm9yKFwiU3luY0FwaUNvbnRyb2xsZXIubW91bnRJbnRvIHJlcXVpcmVzIGNvbmZpZ3VyYXRpb25cIilcblxuICAgIGNvbmZpZ3VyYXRpb24ucm91dGVzKChyb3V0ZXMpID0+IHtcbiAgICAgIHJvdXRlcy5wb3N0KGAke2F0fS9jaGFuZ2VzYCwge3RvOiBbQ29udHJvbGxlckNsYXNzLCBcImNoYW5nZXNcIl19KVxuICAgICAgcm91dGVzLnBvc3QoYCR7YXR9L3JlcGxheWAsIHt0bzogW0NvbnRyb2xsZXJDbGFzcywgXCJyZXBsYXlcIl19KVxuICAgIH0pXG4gIH1cblxuICAvKipcbiAgICogTm9ybWFsaXplcyBhIHN5bmMgbW91bnQgcGF0aC5cbiAgICogQHBhcmFtIHtzdHJpbmd9IGF0IC0gTW91bnQgcGF0aC5cbiAgICogQHJldHVybnMge3N0cmluZ30gTm9ybWFsaXplZCBtb3VudCBwYXRoIHdpdGhvdXQgdHJhaWxpbmcgc2xhc2guXG4gICAqL1xuICBzdGF0aWMgbm9ybWFsaXplZE1vdW50UGF0aChhdCkge1xuICAgIGlmICh0eXBlb2YgYXQgIT09IFwic3RyaW5nXCIgfHwgIWF0LnN0YXJ0c1dpdGgoXCIvXCIpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYFN5bmNBcGlDb250cm9sbGVyIG1vdW50IHBhdGggbXVzdCBzdGFydCB3aXRoICcvJywgZ290OiAke1N0cmluZyhhdCl9YClcbiAgICB9XG5cbiAgICByZXR1cm4gYXQucmVwbGFjZSgvXFwvKyQvdSwgXCJcIikgfHwgXCIvXCJcbiAgfVxuXG4gIC8qKlxuICAgKiBSYWlzZXMgYSBjb25maWd1cmF0aW9uIGVycm9yIGZvciBzdWJjbGFzc2VzIHRoYXQgZG8gbm90IHByb3ZpZGUgYSByZXNvdXJjZS5cbiAgICogQHJldHVybnMge2ltcG9ydChcIi4uL2NvbmZpZ3VyYXRpb24tdHlwZXMuanNcIikuRnJvbnRlbmRNb2RlbFJlc291cmNlQ2xhc3NUeXBlfSBTeW5jIHJlc291cmNlIGNsYXNzLlxuICAgKi9cbiAgbWlzc2luZ1N5bmNSZXNvdXJjZUNsYXNzKCkge1xuICAgIHJldHVybiAvKiogQHR5cGUge3R5cGVvZiBGcm9udGVuZE1vZGVsQmFzZVJlc291cmNlfSAqLyAoLyoqIEB0eXBlIHt1bmtub3dufSAqLyAodGhpcy5yYWlzZU1pc3NpbmdTeW5jUmVzb3VyY2VDbGFzcygpKSlcbiAgfVxuXG4gIC8qKiBSYWlzZXMgYSBjb25maWd1cmF0aW9uIGVycm9yIGZvciBzdWJjbGFzc2VzIHRoYXQgZG8gbm90IHByb3ZpZGUgYSByZXNvdXJjZS4gKi9cbiAgcmFpc2VNaXNzaW5nU3luY1Jlc291cmNlQ2xhc3MoKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFwiU3luY0FwaUNvbnRyb2xsZXIuc3luY1Jlc291cmNlQ2xhc3MgbXVzdCBiZSBpbXBsZW1lbnRlZFwiKVxuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgdGhlIG1vZGVsIGNsYXNzIGV4cG9zZWQgYnkgdGhlIHN5bmMgcmVzb3VyY2UuXG4gICAqIEByZXR1cm5zIHt0eXBlb2YgaW1wb3J0KFwiLi4vZGF0YWJhc2UvcmVjb3JkL2luZGV4LmpzXCIpLmRlZmF1bHR9IFN5bmMgbW9kZWwgY2xhc3MuXG4gICAqL1xuICBzeW5jTW9kZWxDbGFzcygpIHtcbiAgICBjb25zdCBSZXNvdXJjZUNsYXNzID0gdGhpcy5zeW5jUmVzb3VyY2VDbGFzcygpXG4gICAgY29uc3QgbW9kZWxDbGFzcyA9IFJlc291cmNlQ2xhc3MuTW9kZWxDbGFzc1xuXG4gICAgaWYgKCFtb2RlbENsYXNzKSB0aHJvdyBuZXcgRXJyb3IoXCJTeW5jIHJlc291cmNlIGNsYXNzIG11c3QgZGVmaW5lIHN0YXRpYyBNb2RlbENsYXNzXCIpXG5cbiAgICByZXR1cm4gbW9kZWxDbGFzc1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgdGhlIG1vZGVsIG5hbWUgdXNlZCB0byBpbml0aWFsaXplIHRoZSByZXNvdXJjZS5cbiAgICogQHJldHVybnMge3N0cmluZ30gU3luYyBtb2RlbCBuYW1lLlxuICAgKi9cbiAgc3luY01vZGVsTmFtZSgpIHtcbiAgICByZXR1cm4gdGhpcy5zeW5jTW9kZWxDbGFzcygpLm5hbWVcbiAgfVxuXG4gIC8qKlxuICAgKiBCdWlsZHMgdGhlIG1pbmltYWwgcmVzb3VyY2UgY29uZmlndXJhdGlvbiBuZWVkZWQgYnkgdGhlIHN5bmMgcmVzb3VyY2UuXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi4vY29uZmlndXJhdGlvbi10eXBlcy5qc1wiKS5Gcm9udGVuZE1vZGVsUmVzb3VyY2VDbGFzc1R5cGV9IFJlc291cmNlQ2xhc3MgLSBTeW5jIHJlc291cmNlIGNsYXNzLlxuICAgKiBAcmV0dXJucyB7aW1wb3J0KFwiLi4vY29uZmlndXJhdGlvbi10eXBlcy5qc1wiKS5Gcm9udGVuZE1vZGVsUmVzb3VyY2VDb25maWd1cmF0aW9ufSBSZXNvdXJjZSBjb25maWd1cmF0aW9uLlxuICAgKi9cbiAgc3luY1Jlc291cmNlQ29uZmlndXJhdGlvbihSZXNvdXJjZUNsYXNzKSB7XG4gICAgcmV0dXJuIC8qKiBAdHlwZSB7aW1wb3J0KFwiLi4vY29uZmlndXJhdGlvbi10eXBlcy5qc1wiKS5Gcm9udGVuZE1vZGVsUmVzb3VyY2VDb25maWd1cmF0aW9ufSAqLyAoe1xuICAgICAgYXR0cmlidXRlczogUmVzb3VyY2VDbGFzcy5hdHRyaWJ1dGVzIHx8IHt9LFxuICAgICAgc3luYzoge2VuYWJsZWQ6IHRydWV9XG4gICAgfSlcbiAgfVxufVxuXG4iXX0=
|