velocious 1.0.487 → 1.0.489

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.
Files changed (37) hide show
  1. package/README.md +2 -0
  2. package/build/configuration-types.js +1 -0
  3. package/build/configuration.js +29 -0
  4. package/build/environment-handlers/node.js +22 -6
  5. package/build/src/configuration-types.d.ts +5 -0
  6. package/build/src/configuration-types.d.ts.map +1 -1
  7. package/build/src/configuration-types.js +2 -1
  8. package/build/src/configuration.d.ts +11 -0
  9. package/build/src/configuration.d.ts.map +1 -1
  10. package/build/src/configuration.js +27 -1
  11. package/build/src/environment-handlers/node.d.ts.map +1 -1
  12. package/build/src/environment-handlers/node.js +20 -8
  13. package/build/src/sync/sync-api-client-types.d.ts +182 -0
  14. package/build/src/sync/sync-api-client-types.d.ts.map +1 -0
  15. package/build/src/sync/sync-api-client-types.js +3 -0
  16. package/build/src/sync/sync-api-client.d.ts +188 -0
  17. package/build/src/sync/sync-api-client.d.ts.map +1 -0
  18. package/build/src/sync/sync-api-client.js +307 -0
  19. package/build/src/sync/sync-api-controller.d.ts +78 -0
  20. package/build/src/sync/sync-api-controller.d.ts.map +1 -0
  21. package/build/src/sync/sync-api-controller.js +142 -0
  22. package/build/src/sync/sync-model-change-feed-service.d.ts +131 -0
  23. package/build/src/sync/sync-model-change-feed-service.d.ts.map +1 -0
  24. package/build/src/sync/sync-model-change-feed-service.js +212 -0
  25. package/build/sync/sync-api-client-types.js +77 -0
  26. package/build/sync/sync-api-client.js +342 -0
  27. package/build/sync/sync-api-controller.js +161 -0
  28. package/build/sync/sync-model-change-feed-service.js +244 -0
  29. package/build/tsconfig.tsbuildinfo +1 -1
  30. package/package.json +4 -4
  31. package/src/configuration-types.js +1 -0
  32. package/src/configuration.js +29 -0
  33. package/src/environment-handlers/node.js +22 -6
  34. package/src/sync/sync-api-client-types.js +77 -0
  35. package/src/sync/sync-api-client.js +342 -0
  36. package/src/sync/sync-api-controller.js +161 -0
  37. 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=