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,131 @@
1
+ /**
2
+ * Generic cursor-paginated change feed over an app-owned sync/change model.
3
+ *
4
+ * Apps provide the model and optional scoping/serialization hooks. Velocious owns
5
+ * cursor parsing, snapshot high-water resolution, stable ordering, page limits,
6
+ * and response shape for `/velocious/sync/changes` style endpoints.
7
+ */
8
+ export default class SyncModelChangeFeedService {
9
+ /**
10
+ * Creates a generic sync-model change-feed service.
11
+ * @param {object} args - Service arguments.
12
+ * @param {typeof import("../database/record/index.js").default} args.modelClass - Sync/change model class.
13
+ * @param {Record<string, unknown>} args.params - Request params.
14
+ * @param {number} [args.defaultLimit] - Default page size.
15
+ * @param {number} [args.maxLimit] - Maximum page size.
16
+ * @param {(record: ?) => Record<string, unknown>} [args.serializeRecord] - Record serializer.
17
+ * @param {function({query: import("../database/query/model-class-query.js").default}): void} [args.scopeQuery] - Applies app-owned visibility scope.
18
+ */
19
+ constructor({ defaultLimit, maxLimit, modelClass, params, scopeQuery, serializeRecord }: {
20
+ modelClass: typeof import("../database/record/index.js").default;
21
+ params: Record<string, unknown>;
22
+ defaultLimit?: number | undefined;
23
+ maxLimit?: number | undefined;
24
+ serializeRecord?: ((record: unknown) => Record<string, unknown>) | undefined;
25
+ scopeQuery?: ((arg0: {
26
+ query: import("../database/query/model-class-query.js").default;
27
+ }) => void) | undefined;
28
+ });
29
+ defaultLimit: number;
30
+ maxLimit: number;
31
+ modelClass: typeof import("../database/record/index.js").default;
32
+ params: Record<string, unknown>;
33
+ scopeQuery: ((arg0: {
34
+ query: import("../database/query/model-class-query.js").default;
35
+ }) => void) | null;
36
+ serializeRecord: (record: unknown) => Record<string, unknown>;
37
+ /**
38
+ * Builds a stable change-feed page.
39
+ * @returns {Promise<{status: string, nextCursor: {id: string, serverSequence: number, updatedAt: string} | null, syncs: Array<Record<string, unknown>>, upToCursor: {id: string, serverSequence: number, updatedAt: string} | null}>} Change-feed page result.
40
+ */
41
+ changes(): Promise<{
42
+ status: string;
43
+ nextCursor: {
44
+ id: string;
45
+ serverSequence: number;
46
+ updatedAt: string;
47
+ } | null;
48
+ syncs: Array<Record<string, unknown>>;
49
+ upToCursor: {
50
+ id: string;
51
+ serverSequence: number;
52
+ updatedAt: string;
53
+ } | null;
54
+ }>;
55
+ /**
56
+ * Normalizes the requested page limit.
57
+ * @param {unknown} value - Raw limit param.
58
+ * @returns {number} Normalized page limit.
59
+ */
60
+ normalizedLimit(value: unknown): number;
61
+ /**
62
+ * Parses an optional positive integer cursor field.
63
+ * @param {unknown} value - Raw integer param.
64
+ * @param {string} name - Param name for error messages.
65
+ * @returns {number | null} Positive integer value, or null when omitted.
66
+ */
67
+ optionalPositiveIntegerParam(value: unknown, name: string): number | null;
68
+ /**
69
+ * Resolves the high-water cursor that bounds the current feed page.
70
+ * @returns {Promise<{id: string, serverSequence: number, updatedAt: string} | null>} Snapshot upper-bound cursor.
71
+ */
72
+ resolveUpToCursor(): Promise<{
73
+ id: string;
74
+ serverSequence: number;
75
+ updatedAt: string;
76
+ } | null>;
77
+ /**
78
+ * Builds the ordered and cursor-filtered page query.
79
+ * @param {{limit: number, upToCursor: {id: string, serverSequence: number, updatedAt: string}}} args - Page query args.
80
+ * @returns {import("../database/query/model-class-query.js").default} Page query.
81
+ */
82
+ pageQuery({ limit, upToCursor }: {
83
+ limit: number;
84
+ upToCursor: {
85
+ id: string;
86
+ serverSequence: number;
87
+ updatedAt: string;
88
+ };
89
+ }): import("../database/query/model-class-query.js").default;
90
+ /**
91
+ * Builds a base query with app-owned scope applied.
92
+ * @returns {import("../database/query/model-class-query.js").default} Scoped base query.
93
+ */
94
+ scopedQuery(): import("../database/query/model-class-query.js").default;
95
+ /**
96
+ * Serializes a record into a transport cursor.
97
+ * @param {?} record - Sync/change record.
98
+ * @returns {{id: string, serverSequence: number, updatedAt: string}} Cursor for row.
99
+ */
100
+ cursorForRecord(record: unknown): {
101
+ id: string;
102
+ serverSequence: number;
103
+ updatedAt: string;
104
+ };
105
+ /**
106
+ * Serializes a record using the standard sync envelope shape.
107
+ * @param {?} record - Sync/change record.
108
+ * @returns {Record<string, unknown>} Default serialized row.
109
+ */
110
+ defaultSerializeRecord(record: unknown): Record<string, unknown>;
111
+ /**
112
+ * Reads and parses the record data payload.
113
+ * @param {?} record - Sync/change record.
114
+ * @returns {unknown} Parsed data value.
115
+ */
116
+ recordData(record: unknown): unknown;
117
+ /**
118
+ * Reads a value from either a record accessor method or plain property.
119
+ * @param {?} record - Sync/change record.
120
+ * @param {string} name - Camel-cased value/method name.
121
+ * @returns {unknown} Record value.
122
+ */
123
+ recordValue(record: unknown, name: string): unknown;
124
+ /**
125
+ * Converts a date-like value to an ISO string.
126
+ * @param {Date | string | null | undefined | unknown} value - Date value.
127
+ * @returns {string} ISO date.
128
+ */
129
+ isoDate(value: Date | string | null | undefined | unknown): string;
130
+ }
131
+ //# sourceMappingURL=sync-model-change-feed-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-model-change-feed-service.d.ts","sourceRoot":"","sources":["../../../src/sync/sync-model-change-feed-service.js"],"names":[],"mappings":"AAIA;;;;;;GAMG;AACH;IACE;;;;;;;;;OASG;IACH,yFAPG;QAAmE,UAAU,EAArE,cAAc,6BAA6B,EAAE,OAAO;QACtB,MAAM,EAApC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;QACT,YAAY;QACZ,QAAQ;QACwB,eAAe,aAApD,OAAC,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;QACmD,UAAU,WAA1F;YAAC,KAAK,EAAE,OAAO,wCAAwC,EAAE,OAAO,CAAA;SAAC,KAAG,IAAI;KAC3F,EAQA;IANC,qBAAgC;IAChC,iBAAwB;IACxB,iEAA4B;IAC5B,gCAAoB;IACpB,oBAPkB;QAAC,KAAK,EAAE,OAAO,wCAAwC,EAAE,OAAO,CAAA;KAAC,KAAG,IAAI,SAOtD;IACpC,0BATkB,OAAC,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAS4C;IAG7F;;;OAGG;IACH,WAFa,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE;YAAC,EAAE,EAAE,MAAM,CAAC;YAAC,cAAc,EAAE,MAAM,CAAC;YAAC,SAAS,EAAE,MAAM,CAAA;SAAC,GAAG,IAAI,CAAC;QAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;QAAC,UAAU,EAAE;YAAC,EAAE,EAAE,MAAM,CAAC;YAAC,cAAc,EAAE,MAAM,CAAC;YAAC,SAAS,EAAE,MAAM,CAAA;SAAC,GAAG,IAAI,CAAA;KAAC,CAAC,CAkBpO;IAED;;;;OAIG;IACH,uBAHW,OAAO,GACL,MAAM,CAYlB;IAED;;;;;OAKG;IACH,oCAJW,OAAO,QACP,MAAM,GACJ,MAAM,GAAG,IAAI,CAYzB;IAED;;;OAGG;IACH,qBAFa,OAAO,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAC,GAAG,IAAI,CAAC,CA0BnF;IAED;;;;OAIG;IACH,iCAHW;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE;YAAC,EAAE,EAAE,MAAM,CAAC;YAAC,cAAc,EAAE,MAAM,CAAC;YAAC,SAAS,EAAE,MAAM,CAAA;SAAC,CAAA;KAAC,GAClF,OAAO,wCAAwC,EAAE,OAAO,CA8BpE;IAED;;;OAGG;IACH,eAFa,OAAO,wCAAwC,EAAE,OAAO,CAQpE;IAED;;;;OAIG;IACH,wBAHW,OAAC,GACC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAC,CAInE;IAED;;;;OAIG;IACH,+BAHW,OAAC,GACC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAanC;IAED;;;;OAIG;IACH,mBAHW,OAAC,GACC,OAAO,CASnB;IAED;;;;;OAKG;IACH,oBAJW,OAAC,QACD,MAAM,GACJ,OAAO,CAgBnB;IAED;;;;OAIG;IACH,eAHW,IAAI,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,GACxC,MAAM,CAUlB;CACF"}
@@ -0,0 +1,212 @@
1
+ // @ts-check
2
+ import VelociousError from "../velocious-error.js";
3
+ /**
4
+ * Generic cursor-paginated change feed over an app-owned sync/change model.
5
+ *
6
+ * Apps provide the model and optional scoping/serialization hooks. Velocious owns
7
+ * cursor parsing, snapshot high-water resolution, stable ordering, page limits,
8
+ * and response shape for `/velocious/sync/changes` style endpoints.
9
+ */
10
+ export default class SyncModelChangeFeedService {
11
+ /**
12
+ * Creates a generic sync-model change-feed service.
13
+ * @param {object} args - Service arguments.
14
+ * @param {typeof import("../database/record/index.js").default} args.modelClass - Sync/change model class.
15
+ * @param {Record<string, unknown>} args.params - Request params.
16
+ * @param {number} [args.defaultLimit] - Default page size.
17
+ * @param {number} [args.maxLimit] - Maximum page size.
18
+ * @param {(record: ?) => Record<string, unknown>} [args.serializeRecord] - Record serializer.
19
+ * @param {function({query: import("../database/query/model-class-query.js").default}): void} [args.scopeQuery] - Applies app-owned visibility scope.
20
+ */
21
+ constructor({ defaultLimit = 1000, maxLimit = 1000, modelClass, params, scopeQuery, serializeRecord }) {
22
+ this.defaultLimit = defaultLimit;
23
+ this.maxLimit = maxLimit;
24
+ this.modelClass = modelClass;
25
+ this.params = params;
26
+ this.scopeQuery = scopeQuery || null;
27
+ this.serializeRecord = serializeRecord || ((record) => this.defaultSerializeRecord(record));
28
+ }
29
+ /**
30
+ * Builds a stable change-feed page.
31
+ * @returns {Promise<{status: string, nextCursor: {id: string, serverSequence: number, updatedAt: string} | null, syncs: Array<Record<string, unknown>>, upToCursor: {id: string, serverSequence: number, updatedAt: string} | null}>} Change-feed page result.
32
+ */
33
+ async changes() {
34
+ const limit = this.normalizedLimit(this.params.limit);
35
+ const upToCursor = await this.resolveUpToCursor();
36
+ if (!upToCursor)
37
+ return { status: "success", nextCursor: null, syncs: [], upToCursor: null };
38
+ const query = this.pageQuery({ limit, upToCursor });
39
+ const records = await query.toArray();
40
+ const nextCursor = records.length > 0 ? this.cursorForRecord(records[records.length - 1]) : upToCursor;
41
+ return {
42
+ status: "success",
43
+ nextCursor,
44
+ syncs: records.map((record) => this.serializeRecord(record)),
45
+ upToCursor
46
+ };
47
+ }
48
+ /**
49
+ * Normalizes the requested page limit.
50
+ * @param {unknown} value - Raw limit param.
51
+ * @returns {number} Normalized page limit.
52
+ */
53
+ normalizedLimit(value) {
54
+ if (value === undefined || value === null || value === "")
55
+ return this.defaultLimit;
56
+ const limit = Number(value);
57
+ if (!Number.isSafeInteger(limit) || limit <= 0) {
58
+ throw VelociousError.safe("Sync changes limit must be a positive integer.", { code: "sync-invalid-changes-limit" });
59
+ }
60
+ return Math.min(limit, this.maxLimit);
61
+ }
62
+ /**
63
+ * Parses an optional positive integer cursor field.
64
+ * @param {unknown} value - Raw integer param.
65
+ * @param {string} name - Param name for error messages.
66
+ * @returns {number | null} Positive integer value, or null when omitted.
67
+ */
68
+ optionalPositiveIntegerParam(value, name) {
69
+ if (value === undefined || value === null || value === "")
70
+ return null;
71
+ const integer = Number(value);
72
+ if (!Number.isSafeInteger(integer) || integer <= 0) {
73
+ throw VelociousError.safe(`${name} must be a positive integer.`, { code: "sync-invalid-changes-cursor" });
74
+ }
75
+ return integer;
76
+ }
77
+ /**
78
+ * Resolves the high-water cursor that bounds the current feed page.
79
+ * @returns {Promise<{id: string, serverSequence: number, updatedAt: string} | null>} Snapshot upper-bound cursor.
80
+ */
81
+ async resolveUpToCursor() {
82
+ const upToServerSequence = this.optionalPositiveIntegerParam(this.params.upToServerSequence, "upToServerSequence");
83
+ if (upToServerSequence !== null && typeof this.params.upToUpdatedAt === "string" && typeof this.params.upToId === "string") {
84
+ return { id: this.params.upToId, serverSequence: upToServerSequence, updatedAt: this.params.upToUpdatedAt };
85
+ }
86
+ if (typeof this.params.upToUpdatedAt === "string" && typeof this.params.upToId === "string") {
87
+ const upToRecord = await this.modelClass.findBy({ id: this.params.upToId });
88
+ if (upToRecord)
89
+ return this.cursorForRecord(upToRecord);
90
+ }
91
+ const query = this.scopedQuery();
92
+ const table = query.driver.quoteTable(this.modelClass.tableName());
93
+ const serverSequenceColumn = query.driver.quoteColumn("server_sequence");
94
+ const latestRecords = await query
95
+ .order(`${table}.${serverSequenceColumn} DESC`)
96
+ .limit(1)
97
+ .toArray();
98
+ if (latestRecords.length === 0)
99
+ return null;
100
+ return this.cursorForRecord(latestRecords[0]);
101
+ }
102
+ /**
103
+ * Builds the ordered and cursor-filtered page query.
104
+ * @param {{limit: number, upToCursor: {id: string, serverSequence: number, updatedAt: string}}} args - Page query args.
105
+ * @returns {import("../database/query/model-class-query.js").default} Page query.
106
+ */
107
+ pageQuery({ limit, upToCursor }) {
108
+ const query = this.scopedQuery();
109
+ const driver = query.driver;
110
+ const table = driver.quoteTable(this.modelClass.tableName());
111
+ const serverSequenceColumn = `${table}.${driver.quoteColumn("server_sequence")}`;
112
+ const updatedAtColumn = `${table}.${driver.quoteColumn("updated_at")}`;
113
+ const idColumn = `${table}.${driver.quoteColumn("id")}`;
114
+ query
115
+ .where(`${serverSequenceColumn} <= ${driver.quote(upToCursor.serverSequence)}`)
116
+ .order(`${serverSequenceColumn} ASC`)
117
+ .limit(limit);
118
+ const afterServerSequence = this.optionalPositiveIntegerParam(this.params.afterServerSequence, "afterServerSequence");
119
+ if (afterServerSequence !== null) {
120
+ query.where(`${serverSequenceColumn} > ${driver.quote(afterServerSequence)}`);
121
+ }
122
+ else if (typeof this.params.afterUpdatedAt === "string" && this.params.afterUpdatedAt !== "") {
123
+ const isPagingExistingSnapshot = typeof this.params.upToUpdatedAt === "string" && this.params.upToUpdatedAt !== "" && typeof this.params.upToId === "string" && this.params.upToId !== "";
124
+ if (isPagingExistingSnapshot && typeof this.params.afterId === "string" && this.params.afterId !== "") {
125
+ query.where(`(${updatedAtColumn} > ${driver.quote(this.params.afterUpdatedAt)} OR (${updatedAtColumn} = ${driver.quote(this.params.afterUpdatedAt)} AND ${idColumn} > ${driver.quote(this.params.afterId)}))`);
126
+ }
127
+ else {
128
+ query.where(`${updatedAtColumn} >= ${driver.quote(this.params.afterUpdatedAt)}`);
129
+ }
130
+ }
131
+ return query;
132
+ }
133
+ /**
134
+ * Builds a base query with app-owned scope applied.
135
+ * @returns {import("../database/query/model-class-query.js").default} Scoped base query.
136
+ */
137
+ scopedQuery() {
138
+ const query = this.modelClass.where({});
139
+ if (this.scopeQuery)
140
+ this.scopeQuery({ query });
141
+ return query;
142
+ }
143
+ /**
144
+ * Serializes a record into a transport cursor.
145
+ * @param {?} record - Sync/change record.
146
+ * @returns {{id: string, serverSequence: number, updatedAt: string}} Cursor for row.
147
+ */
148
+ cursorForRecord(record) {
149
+ return { id: String(this.recordValue(record, "id")), serverSequence: Number(this.recordValue(record, "serverSequence")), updatedAt: this.isoDate(this.recordValue(record, "updatedAt")) };
150
+ }
151
+ /**
152
+ * Serializes a record using the standard sync envelope shape.
153
+ * @param {?} record - Sync/change record.
154
+ * @returns {Record<string, unknown>} Default serialized row.
155
+ */
156
+ defaultSerializeRecord(record) {
157
+ return {
158
+ data: this.recordData(record),
159
+ eventId: this.recordValue(record, "eventId"),
160
+ id: this.recordValue(record, "id"),
161
+ resourceId: this.recordValue(record, "resourceId"),
162
+ resourceType: this.recordValue(record, "resourceType"),
163
+ serverSequence: this.recordValue(record, "serverSequence"),
164
+ syncType: this.recordValue(record, "syncType"),
165
+ updatedAt: this.isoDate(this.recordValue(record, "updatedAt"))
166
+ };
167
+ }
168
+ /**
169
+ * Reads and parses the record data payload.
170
+ * @param {?} record - Sync/change record.
171
+ * @returns {unknown} Parsed data value.
172
+ */
173
+ recordData(record) {
174
+ const data = this.recordValue(record, "data");
175
+ if (data === "" || data === null || data === undefined)
176
+ return null;
177
+ if (typeof data !== "string")
178
+ return data;
179
+ return JSON.parse(data);
180
+ }
181
+ /**
182
+ * Reads a value from either a record accessor method or plain property.
183
+ * @param {?} record - Sync/change record.
184
+ * @param {string} name - Camel-cased value/method name.
185
+ * @returns {unknown} Record value.
186
+ */
187
+ recordValue(record, name) {
188
+ if (!record || typeof record !== "object") {
189
+ throw new Error("Sync changes row must be an object.");
190
+ }
191
+ const recordObject = /** @type {Record<string, ?>} */ (record);
192
+ const method = recordObject[name];
193
+ const value = typeof method === "function" ? method.call(record) : method;
194
+ if (value === undefined) {
195
+ throw new Error(`Sync changes row is missing ${name}.`);
196
+ }
197
+ return value;
198
+ }
199
+ /**
200
+ * Converts a date-like value to an ISO string.
201
+ * @param {Date | string | null | undefined | unknown} value - Date value.
202
+ * @returns {string} ISO date.
203
+ */
204
+ isoDate(value) {
205
+ const date = value instanceof Date ? value : new Date(typeof value === "string" || typeof value === "number" ? value : 0);
206
+ if (Number.isNaN(date.getTime())) {
207
+ throw VelociousError.safe("Sync changes row has an invalid updated_at value.", { code: "sync-invalid-changes-row" });
208
+ }
209
+ return date.toISOString();
210
+ }
211
+ }
212
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3luYy1tb2RlbC1jaGFuZ2UtZmVlZC1zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3N5bmMvc3luYy1tb2RlbC1jaGFuZ2UtZmVlZC1zZXJ2aWNlLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLFlBQVk7QUFFWixPQUFPLGNBQWMsTUFBTSx1QkFBdUIsQ0FBQTtBQUVsRDs7Ozs7O0dBTUc7QUFDSCxNQUFNLENBQUMsT0FBTyxPQUFPLDBCQUEwQjtJQUM3Qzs7Ozs7Ozs7O09BU0c7SUFDSCxZQUFZLEVBQUMsWUFBWSxHQUFHLElBQUksRUFBRSxRQUFRLEdBQUcsSUFBSSxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUUsVUFBVSxFQUFFLGVBQWUsRUFBQztRQUNqRyxJQUFJLENBQUMsWUFBWSxHQUFHLFlBQVksQ0FBQTtRQUNoQyxJQUFJLENBQUMsUUFBUSxHQUFHLFFBQVEsQ0FBQTtRQUN4QixJQUFJLENBQUMsVUFBVSxHQUFHLFVBQVUsQ0FBQTtRQUM1QixJQUFJLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQTtRQUNwQixJQUFJLENBQUMsVUFBVSxHQUFHLFVBQVUsSUFBSSxJQUFJLENBQUE7UUFDcEMsSUFBSSxDQUFDLGVBQWUsR0FBRyxlQUFlLElBQUksQ0FBQyxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUMsSUFBSSxDQUFDLHNCQUFzQixDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUE7SUFDN0YsQ0FBQztJQUVEOzs7T0FHRztJQUNILEtBQUssQ0FBQyxPQUFPO1FBQ1gsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLGVBQWUsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFBO1FBQ3JELE1BQU0sVUFBVSxHQUFHLE1BQU0sSUFBSSxDQUFDLGlCQUFpQixFQUFFLENBQUE7UUFFakQsSUFBSSxDQUFDLFVBQVU7WUFBRSxPQUFPLEVBQUMsTUFBTSxFQUFFLFNBQVMsRUFBRSxVQUFVLEVBQUUs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QVcsRUFBRSxDQUFBO0lBQzNCLENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbIi8vIEB0cy1jaGVja1xuXG5pbXBvcnQgVmVsb2Npb3VzRXJyb3IgZnJvbSBcIi4uL3ZlbG9jaW91cy1lcnJvci5qc1wiXG5cbi8qKlxuICogR2VuZXJpYyBjdXJzb3ItcGFnaW5hdGVkIGNoYW5nZSBmZWVkIG92ZXIgYW4gYXBwLW93bmVkIHN5bmMvY2hhbmdlIG1vZGVsLlxuICpcbiAqIEFwcHMgcHJvdmlkZSB0aGUgbW9kZWwgYW5kIG9wdGlvbmFsIHNjb3Bpbmcvc2VyaWFsaXphdGlvbiBob29rcy4gVmVsb2Npb3VzIG93bnNcbiAqIGN1cnNvciBwYXJzaW5nLCBzbmFwc2hvdCBoaWdoLXdhdGVyIHJlc29sdXRpb24sIHN0YWJsZSBvcmRlcmluZywgcGFnZSBsaW1pdHMsXG4gKiBhbmQgcmVzcG9uc2Ugc2hhcGUgZm9yIGAvdmVsb2Npb3VzL3N5bmMvY2hhbmdlc2Agc3R5bGUgZW5kcG9pbnRzLlxuICovXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBTeW5jTW9kZWxDaGFuZ2VGZWVkU2VydmljZSB7XG4gIC8qKlxuICAgKiBDcmVhdGVzIGEgZ2VuZXJpYyBzeW5jLW1vZGVsIGNoYW5nZS1mZWVkIHNlcnZpY2UuXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gU2VydmljZSBhcmd1bWVudHMuXG4gICAqIEBwYXJhbSB7dHlwZW9mIGltcG9ydChcIi4uL2RhdGFiYXNlL3JlY29yZC9pbmRleC5qc1wiKS5kZWZhdWx0fSBhcmdzLm1vZGVsQ2xhc3MgLSBTeW5jL2NoYW5nZSBtb2RlbCBjbGFzcy5cbiAgICogQHBhcmFtIHtSZWNvcmQ8c3RyaW5nLCB1bmtub3duPn0gYXJncy5wYXJhbXMgLSBSZXF1ZXN0IHBhcmFtcy5cbiAgICogQHBhcmFtIHtudW1iZXJ9IFthcmdzLmRlZmF1bHRMaW1pdF0gLSBEZWZhdWx0IHBhZ2Ugc2l6ZS5cbiAgICogQHBhcmFtIHtudW1iZXJ9IFthcmdzLm1heExpbWl0XSAtIE1heGltdW0gcGFnZSBzaXplLlxuICAgKiBAcGFyYW0geyhyZWNvcmQ6ID8pID0+IFJlY29yZDxzdHJpbmcsIHVua25vd24+fSBbYXJncy5zZXJpYWxpemVSZWNvcmRdIC0gUmVjb3JkIHNlcmlhbGl6ZXIuXG4gICAqIEBwYXJhbSB7ZnVuY3Rpb24oe3F1ZXJ5OiBpbXBvcnQoXCIuLi9kYXRhYmFzZS9xdWVyeS9tb2RlbC1jbGFzcy1xdWVyeS5qc1wiKS5kZWZhdWx0fSk6IHZvaWR9IFthcmdzLnNjb3BlUXVlcnldIC0gQXBwbGllcyBhcHAtb3duZWQgdmlzaWJpbGl0eSBzY29wZS5cbiAgICovXG4gIGNvbnN0cnVjdG9yKHtkZWZhdWx0TGltaXQgPSAxMDAwLCBtYXhMaW1pdCA9IDEwMDAsIG1vZGVsQ2xhc3MsIHBhcmFtcywgc2NvcGVRdWVyeSwgc2VyaWFsaXplUmVjb3JkfSkge1xuICAgIHRoaXMuZGVmYXVsdExpbWl0ID0gZGVmYXVsdExpbWl0XG4gICAgdGhpcy5tYXhMaW1pdCA9IG1heExpbWl0XG4gICAgdGhpcy5tb2RlbENsYXNzID0gbW9kZWxDbGFzc1xuICAgIHRoaXMucGFyYW1zID0gcGFyYW1zXG4gICAgdGhpcy5zY29wZVF1ZXJ5ID0gc2NvcGVRdWVyeSB8fCBudWxsXG4gICAgdGhpcy5zZXJpYWxpemVSZWNvcmQgPSBzZXJpYWxpemVSZWNvcmQgfHwgKChyZWNvcmQpID0+IHRoaXMuZGVmYXVsdFNlcmlhbGl6ZVJlY29yZChyZWNvcmQpKVxuICB9XG5cbiAgLyoqXG4gICAqIEJ1aWxkcyBhIHN0YWJsZSBjaGFuZ2UtZmVlZCBwYWdlLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTx7c3RhdHVzOiBzdHJpbmcsIG5leHRDdXJzb3I6IHtpZDogc3RyaW5nLCBzZXJ2ZXJTZXF1ZW5jZTogbnVtYmVyLCB1cGRhdGVkQXQ6IHN0cmluZ30gfCBudWxsLCBzeW5jczogQXJyYXk8UmVjb3JkPHN0cmluZywgdW5rbm93bj4+LCB1cFRvQ3Vyc29yOiB7aWQ6IHN0cmluZywgc2VydmVyU2VxdWVuY2U6IG51bWJlciwgdXBkYXRlZEF0OiBzdHJpbmd9IHwgbnVsbH0+fSBDaGFuZ2UtZmVlZCBwYWdlIHJlc3VsdC5cbiAgICovXG4gIGFzeW5jIGNoYW5nZXMoKSB7XG4gICAgY29uc3QgbGltaXQgPSB0aGlzLm5vcm1hbGl6ZWRMaW1pdCh0aGlzLnBhcmFtcy5saW1pdClcbiAgICBjb25zdCB1cFRvQ3Vyc29yID0gYXdhaXQgdGhpcy5yZXNvbHZlVXBUb0N1cnNvcigpXG5cbiAgICBpZiAoIXVwVG9DdXJzb3IpIHJldHVybiB7c3RhdHVzOiBcInN1Y2Nlc3NcIiwgbmV4dEN1cnNvcjogbnVsbCwgc3luY3M6IFtdLCB1cFRvQ3Vyc29yOiBudWxsfVxuXG4gICAgY29uc3QgcXVlcnkgPSB0aGlzLnBhZ2VRdWVyeSh7bGltaXQsIHVwVG9DdXJzb3J9KVxuICAgIGNvbnN0IHJlY29yZHMgPSBhd2FpdCBxdWVyeS50b0FycmF5KClcbiAgICBjb25zdCBuZXh0Q3Vyc29yID0gcmVjb3Jkcy5sZW5ndGggPiAwID8gdGhpcy5jdXJzb3JGb3JSZWNvcmQocmVjb3Jkc1tyZWNvcmRzLmxlbmd0aCAtIDFdKSA6IHVwVG9DdXJzb3JcblxuICAgIHJldHVybiB7XG4gICAgICBzdGF0dXM6IFwic3VjY2Vzc1wiLFxuICAgICAgbmV4dEN1cnNvcixcbiAgICAgIHN5bmNzOiByZWNvcmRzLm1hcCgocmVjb3JkKSA9PiB0aGlzLnNlcmlhbGl6ZVJlY29yZChyZWNvcmQpKSxcbiAgICAgIHVwVG9DdXJzb3JcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogTm9ybWFsaXplcyB0aGUgcmVxdWVzdGVkIHBhZ2UgbGltaXQuXG4gICAqIEBwYXJhbSB7dW5rbm93bn0gdmFsdWUgLSBSYXcgbGltaXQgcGFyYW0uXG4gICAqIEByZXR1cm5zIHtudW1iZXJ9IE5vcm1hbGl6ZWQgcGFnZSBsaW1pdC5cbiAgICovXG4gIG5vcm1hbGl6ZWRMaW1pdCh2YWx1ZSkge1xuICAgIGlmICh2YWx1ZSA9PT0gdW5kZWZpbmVkIHx8IHZhbHVlID09PSBudWxsIHx8IHZhbHVlID09PSBcIlwiKSByZXR1cm4gdGhpcy5kZWZhdWx0TGltaXRcblxuICAgIGNvbnN0IGxpbWl0ID0gTnVtYmVyKHZhbHVlKVxuXG4gICAgaWYgKCFOdW1iZXIuaXNTYWZlSW50ZWdlcihsaW1pdCkgfHwgbGltaXQgPD0gMCkge1xuICAgICAgdGhyb3cgVmVsb2Npb3VzRXJyb3Iuc2FmZShcIlN5bmMgY2hhbmdlcyBsaW1pdCBtdXN0IGJlIGEgcG9zaXRpdmUgaW50ZWdlci5cIiwge2NvZGU6IFwic3luYy1pbnZhbGlkLWNoYW5nZXMtbGltaXRcIn0pXG4gICAgfVxuXG4gICAgcmV0dXJuIE1hdGgubWluKGxpbWl0LCB0aGlzLm1heExpbWl0KVxuICB9XG5cbiAgLyoqXG4gICAqIFBhcnNlcyBhbiBvcHRpb25hbCBwb3NpdGl2ZSBpbnRlZ2VyIGN1cnNvciBmaWVsZC5cbiAgICogQHBhcmFtIHt1bmtub3dufSB2YWx1ZSAtIFJhdyBpbnRlZ2VyIHBhcmFtLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gbmFtZSAtIFBhcmFtIG5hbWUgZm9yIGVycm9yIG1lc3NhZ2VzLlxuICAgKiBAcmV0dXJucyB7bnVtYmVyIHwgbnVsbH0gUG9zaXRpdmUgaW50ZWdlciB2YWx1ZSwgb3IgbnVsbCB3aGVuIG9taXR0ZWQuXG4gICAqL1xuICBvcHRpb25hbFBvc2l0aXZlSW50ZWdlclBhcmFtKHZhbHVlLCBuYW1lKSB7XG4gICAgaWYgKHZhbHVlID09PSB1bmRlZmluZWQgfHwgdmFsdWUgPT09IG51bGwgfHwgdmFsdWUgPT09IFwiXCIpIHJldHVybiBudWxsXG5cbiAgICBjb25zdCBpbnRlZ2VyID0gTnVtYmVyKHZhbHVlKVxuXG4gICAgaWYgKCFOdW1iZXIuaXNTYWZlSW50ZWdlcihpbnRlZ2VyKSB8fCBpbnRlZ2VyIDw9IDApIHtcbiAgICAgIHRocm93IFZlbG9jaW91c0Vycm9yLnNhZmUoYCR7bmFtZX0gbXVzdCBiZSBhIHBvc2l0aXZlIGludGVnZXIuYCwge2NvZGU6IFwic3luYy1pbnZhbGlkLWNoYW5nZXMtY3Vyc29yXCJ9KVxuICAgIH1cblxuICAgIHJldHVybiBpbnRlZ2VyXG4gIH1cblxuICAvKipcbiAgICogUmVzb2x2ZXMgdGhlIGhpZ2gtd2F0ZXIgY3Vyc29yIHRoYXQgYm91bmRzIHRoZSBjdXJyZW50IGZlZWQgcGFnZS5cbiAgICogQHJldHVybnMge1Byb21pc2U8e2lkOiBzdHJpbmcsIHNlcnZlclNlcXVlbmNlOiBudW1iZXIsIHVwZGF0ZWRBdDogc3RyaW5nfSB8IG51bGw+fSBTbmFwc2hvdCB1cHBlci1ib3VuZCBjdXJzb3IuXG4gICAqL1xuICBhc3luYyByZXNvbHZlVXBUb0N1cnNvcigpIHtcbiAgICBjb25zdCB1cFRvU2VydmVyU2VxdWVuY2UgPSB0aGlzLm9wdGlvbmFsUG9zaXRpdmVJbnRlZ2VyUGFyYW0odGhpcy5wYXJhbXMudXBUb1NlcnZlclNlcXVlbmNlLCBcInVwVG9TZXJ2ZXJTZXF1ZW5jZVwiKVxuXG4gICAgaWYgKHVwVG9TZXJ2ZXJTZXF1ZW5jZSAhPT0gbnVsbCAmJiB0eXBlb2YgdGhpcy5wYXJhbXMudXBUb1VwZGF0ZWRBdCA9PT0gXCJzdHJpbmdcIiAmJiB0eXBlb2YgdGhpcy5wYXJhbXMudXBUb0lkID09PSBcInN0cmluZ1wiKSB7XG4gICAgICByZXR1cm4ge2lkOiB0aGlzLnBhcmFtcy51cFRvSWQsIHNlcnZlclNlcXVlbmNlOiB1cFRvU2VydmVyU2VxdWVuY2UsIHVwZGF0ZWRBdDogdGhpcy5wYXJhbXMudXBUb1VwZGF0ZWRBdH1cbiAgICB9XG5cbiAgICBpZiAodHlwZW9mIHRoaXMucGFyYW1zLnVwVG9VcGRhdGVkQXQgPT09IFwic3RyaW5nXCIgJiYgdHlwZW9mIHRoaXMucGFyYW1zLnVwVG9JZCA9PT0gXCJzdHJpbmdcIikge1xuICAgICAgY29uc3QgdXBUb1JlY29yZCA9IGF3YWl0IHRoaXMubW9kZWxDbGFzcy5maW5kQnkoe2lkOiB0aGlzLnBhcmFtcy51cFRvSWR9KVxuXG4gICAgICBpZiAodXBUb1JlY29yZCkgcmV0dXJuIHRoaXMuY3Vyc29yRm9yUmVjb3JkKHVwVG9SZWNvcmQpXG4gICAgfVxuXG4gICAgY29uc3QgcXVlcnkgPSB0aGlzLnNjb3BlZFF1ZXJ5KClcbiAgICBjb25zdCB0YWJsZSA9IHF1ZXJ5LmRyaXZlci5xdW90ZVRhYmxlKHRoaXMubW9kZWxDbGFzcy50YWJsZU5hbWUoKSlcbiAgICBjb25zdCBzZXJ2ZXJTZXF1ZW5jZUNvbHVtbiA9IHF1ZXJ5LmRyaXZlci5xdW90ZUNvbHVtbihcInNlcnZlcl9zZXF1ZW5jZVwiKVxuICAgIGNvbnN0IGxhdGVzdFJlY29yZHMgPSBhd2FpdCBxdWVyeVxuICAgICAgLm9yZGVyKGAke3RhYmxlfS4ke3NlcnZlclNlcXVlbmNlQ29sdW1ufSBERVNDYClcbiAgICAgIC5saW1pdCgxKVxuICAgICAgLnRvQXJyYXkoKVxuXG4gICAgaWYgKGxhdGVzdFJlY29yZHMubGVuZ3RoID09PSAwKSByZXR1cm4gbnVsbFxuXG4gICAgcmV0dXJuIHRoaXMuY3Vyc29yRm9yUmVjb3JkKGxhdGVzdFJlY29yZHNbMF0pXG4gIH1cblxuICAvKipcbiAgICogQnVpbGRzIHRoZSBvcmRlcmVkIGFuZCBjdXJzb3ItZmlsdGVyZWQgcGFnZSBxdWVyeS5cbiAgICogQHBhcmFtIHt7bGltaXQ6IG51bWJlciwgdXBUb0N1cnNvcjoge2lkOiBzdHJpbmcsIHNlcnZlclNlcXVlbmNlOiBudW1iZXIsIHVwZGF0ZWRBdDogc3RyaW5nfX19IGFyZ3MgLSBQYWdlIHF1ZXJ5IGFyZ3MuXG4gICAqIEByZXR1cm5zIHtpbXBvcnQoXCIuLi9kYXRhYmFzZS9xdWVyeS9tb2RlbC1jbGFzcy1xdWVyeS5qc1wiKS5kZWZhdWx0fSBQYWdlIHF1ZXJ5LlxuICAgKi9cbiAgcGFnZVF1ZXJ5KHtsaW1pdCwgdXBUb0N1cnNvcn0pIHtcbiAgICBjb25zdCBxdWVyeSA9IHRoaXMuc2NvcGVkUXVlcnkoKVxuICAgIGNvbnN0IGRyaXZlciA9IHF1ZXJ5LmRyaXZlclxuICAgIGNvbnN0IHRhYmxlID0gZHJpdmVyLnF1b3RlVGFibGUodGhpcy5tb2RlbENsYXNzLnRhYmxlTmFtZSgpKVxuICAgIGNvbnN0IHNlcnZlclNlcXVlbmNlQ29sdW1uID0gYCR7dGFibGV9LiR7ZHJpdmVyLnF1b3RlQ29sdW1uKFwic2VydmVyX3NlcXVlbmNlXCIpfWBcbiAgICBjb25zdCB1cGRhdGVkQXRDb2x1bW4gPSBgJHt0YWJsZX0uJHtkcml2ZXIucXVvdGVDb2x1bW4oXCJ1cGRhdGVkX2F0XCIpfWBcbiAgICBjb25zdCBpZENvbHVtbiA9IGAke3RhYmxlfS4ke2RyaXZlci5xdW90ZUNvbHVtbihcImlkXCIpfWBcblxuICAgIHF1ZXJ5XG4gICAgICAud2hlcmUoYCR7c2VydmVyU2VxdWVuY2VDb2x1bW59IDw9ICR7ZHJpdmVyLnF1b3RlKHVwVG9DdXJzb3Iuc2VydmVyU2VxdWVuY2UpfWApXG4gICAgICAub3JkZXIoYCR7c2VydmVyU2VxdWVuY2VDb2x1bW59IEFTQ2ApXG4gICAgICAubGltaXQobGltaXQpXG5cbiAgICBjb25zdCBhZnRlclNlcnZlclNlcXVlbmNlID0gdGhpcy5vcHRpb25hbFBvc2l0aXZlSW50ZWdlclBhcmFtKHRoaXMucGFyYW1zLmFmdGVyU2VydmVyU2VxdWVuY2UsIFwiYWZ0ZXJTZXJ2ZXJTZXF1ZW5jZVwiKVxuXG4gICAgaWYgKGFmdGVyU2VydmVyU2VxdWVuY2UgIT09IG51bGwpIHtcbiAgICAgIHF1ZXJ5LndoZXJlKGAke3NlcnZlclNlcXVlbmNlQ29sdW1ufSA+ICR7ZHJpdmVyLnF1b3RlKGFmdGVyU2VydmVyU2VxdWVuY2UpfWApXG4gICAgfSBlbHNlIGlmICh0eXBlb2YgdGhpcy5wYXJhbXMuYWZ0ZXJVcGRhdGVkQXQgPT09IFwic3RyaW5nXCIgJiYgdGhpcy5wYXJhbXMuYWZ0ZXJVcGRhdGVkQXQgIT09IFwiXCIpIHtcbiAgICAgIGNvbnN0IGlzUGFnaW5nRXhpc3RpbmdTbmFwc2hvdCA9IHR5cGVvZiB0aGlzLnBhcmFtcy51cFRvVXBkYXRlZEF0ID09PSBcInN0cmluZ1wiICYmIHRoaXMucGFyYW1zLnVwVG9VcGRhdGVkQXQgIT09IFwiXCIgJiYgdHlwZW9mIHRoaXMucGFyYW1zLnVwVG9JZCA9PT0gXCJzdHJpbmdcIiAmJiB0aGlzLnBhcmFtcy51cFRvSWQgIT09IFwiXCJcblxuICAgICAgaWYgKGlzUGFnaW5nRXhpc3RpbmdTbmFwc2hvdCAmJiB0eXBlb2YgdGhpcy5wYXJhbXMuYWZ0ZXJJZCA9PT0gXCJzdHJpbmdcIiAmJiB0aGlzLnBhcmFtcy5hZnRlcklkICE9PSBcIlwiKSB7XG4gICAgICAgIHF1ZXJ5LndoZXJlKGAoJHt1cGRhdGVkQXRDb2x1bW59ID4gJHtkcml2ZXIucXVvdGUodGhpcy5wYXJhbXMuYWZ0ZXJVcGRhdGVkQXQpfSBPUiAoJHt1cGRhdGVkQXRDb2x1bW59ID0gJHtkcml2ZXIucXVvdGUodGhpcy5wYXJhbXMuYWZ0ZXJVcGRhdGVkQXQpfSBBTkQgJHtpZENvbHVtbn0gPiAke2RyaXZlci5xdW90ZSh0aGlzLnBhcmFtcy5hZnRlcklkKX0pKWApXG4gICAgICB9IGVsc2Uge1xuICAgICAgICBxdWVyeS53aGVyZShgJHt1cGRhdGVkQXRDb2x1bW59ID49ICR7ZHJpdmVyLnF1b3RlKHRoaXMucGFyYW1zLmFmdGVyVXBkYXRlZEF0KX1gKVxuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiBxdWVyeVxuICB9XG5cbiAgLyoqXG4gICAqIEJ1aWxkcyBhIGJhc2UgcXVlcnkgd2l0aCBhcHAtb3duZWQgc2NvcGUgYXBwbGllZC5cbiAgICogQHJldHVybnMge2ltcG9ydChcIi4uL2RhdGFiYXNlL3F1ZXJ5L21vZGVsLWNsYXNzLXF1ZXJ5LmpzXCIpLmRlZmF1bHR9IFNjb3BlZCBiYXNlIHF1ZXJ5LlxuICAgKi9cbiAgc2NvcGVkUXVlcnkoKSB7XG4gICAgY29uc3QgcXVlcnkgPSB0aGlzLm1vZGVsQ2xhc3Mud2hlcmUoe30pXG5cbiAgICBpZiAodGhpcy5zY29wZVF1ZXJ5KSB0aGlzLnNjb3BlUXVlcnkoe3F1ZXJ5fSlcblxuICAgIHJldHVybiBxdWVyeVxuICB9XG5cbiAgLyoqXG4gICAqIFNlcmlhbGl6ZXMgYSByZWNvcmQgaW50byBhIHRyYW5zcG9ydCBjdXJzb3IuXG4gICAqIEBwYXJhbSB7P30gcmVjb3JkIC0gU3luYy9jaGFuZ2UgcmVjb3JkLlxuICAgKiBAcmV0dXJucyB7e2lkOiBzdHJpbmcsIHNlcnZlclNlcXVlbmNlOiBudW1iZXIsIHVwZGF0ZWRBdDogc3RyaW5nfX0gQ3Vyc29yIGZvciByb3cuXG4gICAqL1xuICBjdXJzb3JGb3JSZWNvcmQocmVjb3JkKSB7XG4gICAgcmV0dXJuIHtpZDogU3RyaW5nKHRoaXMucmVjb3JkVmFsdWUocmVjb3JkLCBcImlkXCIpKSwgc2VydmVyU2VxdWVuY2U6IE51bWJlcih0aGlzLnJlY29yZFZhbHVlKHJlY29yZCwgXCJzZXJ2ZXJTZXF1ZW5jZVwiKSksIHVwZGF0ZWRBdDogdGhpcy5pc29EYXRlKHRoaXMucmVjb3JkVmFsdWUocmVjb3JkLCBcInVwZGF0ZWRBdFwiKSl9XG4gIH1cblxuICAvKipcbiAgICogU2VyaWFsaXplcyBhIHJlY29yZCB1c2luZyB0aGUgc3RhbmRhcmQgc3luYyBlbnZlbG9wZSBzaGFwZS5cbiAgICogQHBhcmFtIHs/fSByZWNvcmQgLSBTeW5jL2NoYW5nZSByZWNvcmQuXG4gICAqIEByZXR1cm5zIHtSZWNvcmQ8c3RyaW5nLCB1bmtub3duPn0gRGVmYXVsdCBzZXJpYWxpemVkIHJvdy5cbiAgICovXG4gIGRlZmF1bHRTZXJpYWxpemVSZWNvcmQocmVjb3JkKSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIGRhdGE6IHRoaXMucmVjb3JkRGF0YShyZWNvcmQpLFxuICAgICAgZXZlbnRJZDogdGhpcy5yZWNvcmRWYWx1ZShyZWNvcmQsIFwiZXZlbnRJZFwiKSxcbiAgICAgIGlkOiB0aGlzLnJlY29yZFZhbHVlKHJlY29yZCwgXCJpZFwiKSxcbiAgICAgIHJlc291cmNlSWQ6IHRoaXMucmVjb3JkVmFsdWUocmVjb3JkLCBcInJlc291cmNlSWRcIiksXG4gICAgICByZXNvdXJjZVR5cGU6IHRoaXMucmVjb3JkVmFsdWUocmVjb3JkLCBcInJlc291cmNlVHlwZVwiKSxcbiAgICAgIHNlcnZlclNlcXVlbmNlOiB0aGlzLnJlY29yZFZhbHVlKHJlY29yZCwgXCJzZXJ2ZXJTZXF1ZW5jZVwiKSxcbiAgICAgIHN5bmNUeXBlOiB0aGlzLnJlY29yZFZhbHVlKHJlY29yZCwgXCJzeW5jVHlwZVwiKSxcbiAgICAgIHVwZGF0ZWRBdDogdGhpcy5pc29EYXRlKHRoaXMucmVjb3JkVmFsdWUocmVjb3JkLCBcInVwZGF0ZWRBdFwiKSlcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUmVhZHMgYW5kIHBhcnNlcyB0aGUgcmVjb3JkIGRhdGEgcGF5bG9hZC5cbiAgICogQHBhcmFtIHs/fSByZWNvcmQgLSBTeW5jL2NoYW5nZSByZWNvcmQuXG4gICAqIEByZXR1cm5zIHt1bmtub3dufSBQYXJzZWQgZGF0YSB2YWx1ZS5cbiAgICovXG4gIHJlY29yZERhdGEocmVjb3JkKSB7XG4gICAgY29uc3QgZGF0YSA9IHRoaXMucmVjb3JkVmFsdWUocmVjb3JkLCBcImRhdGFcIilcblxuICAgIGlmIChkYXRhID09PSBcIlwiIHx8IGRhdGEgPT09IG51bGwgfHwgZGF0YSA9PT0gdW5kZWZpbmVkKSByZXR1cm4gbnVsbFxuICAgIGlmICh0eXBlb2YgZGF0YSAhPT0gXCJzdHJpbmdcIikgcmV0dXJuIGRhdGFcblxuICAgIHJldHVybiBKU09OLnBhcnNlKGRhdGEpXG4gIH1cblxuICAvKipcbiAgICogUmVhZHMgYSB2YWx1ZSBmcm9tIGVpdGhlciBhIHJlY29yZCBhY2Nlc3NvciBtZXRob2Qgb3IgcGxhaW4gcHJvcGVydHkuXG4gICAqIEBwYXJhbSB7P30gcmVjb3JkIC0gU3luYy9jaGFuZ2UgcmVjb3JkLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gbmFtZSAtIENhbWVsLWNhc2VkIHZhbHVlL21ldGhvZCBuYW1lLlxuICAgKiBAcmV0dXJucyB7dW5rbm93bn0gUmVjb3JkIHZhbHVlLlxuICAgKi9cbiAgcmVjb3JkVmFsdWUocmVjb3JkLCBuYW1lKSB7XG4gICAgaWYgKCFyZWNvcmQgfHwgdHlwZW9mIHJlY29yZCAhPT0gXCJvYmplY3RcIikge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFwiU3luYyBjaGFuZ2VzIHJvdyBtdXN0IGJlIGFuIG9iamVjdC5cIilcbiAgICB9XG5cbiAgICBjb25zdCByZWNvcmRPYmplY3QgPSAvKiogQHR5cGUge1JlY29yZDxzdHJpbmcsID8+fSAqLyAocmVjb3JkKVxuICAgIGNvbnN0IG1ldGhvZCA9IHJlY29yZE9iamVjdFtuYW1lXVxuICAgIGNvbnN0IHZhbHVlID0gdHlwZW9mIG1ldGhvZCA9PT0gXCJmdW5jdGlvblwiID8gbWV0aG9kLmNhbGwocmVjb3JkKSA6IG1ldGhvZFxuXG4gICAgaWYgKHZhbHVlID09PSB1bmRlZmluZWQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgU3luYyBjaGFuZ2VzIHJvdyBpcyBtaXNzaW5nICR7bmFtZX0uYClcbiAgICB9XG5cbiAgICByZXR1cm4gdmFsdWVcbiAgfVxuXG4gIC8qKlxuICAgKiBDb252ZXJ0cyBhIGRhdGUtbGlrZSB2YWx1ZSB0byBhbiBJU08gc3RyaW5nLlxuICAgKiBAcGFyYW0ge0RhdGUgfCBzdHJpbmcgfCBudWxsIHwgdW5kZWZpbmVkIHwgdW5rbm93bn0gdmFsdWUgLSBEYXRlIHZhbHVlLlxuICAgKiBAcmV0dXJucyB7c3RyaW5nfSBJU08gZGF0ZS5cbiAgICovXG4gIGlzb0RhdGUodmFsdWUpIHtcbiAgICBjb25zdCBkYXRlID0gdmFsdWUgaW5zdGFuY2VvZiBEYXRlID8gdmFsdWUgOiBuZXcgRGF0ZSh0eXBlb2YgdmFsdWUgPT09IFwic3RyaW5nXCIgfHwgdHlwZW9mIHZhbHVlID09PSBcIm51bWJlclwiID8gdmFsdWUgOiAwKVxuXG4gICAgaWYgKE51bWJlci5pc05hTihkYXRlLmdldFRpbWUoKSkpIHtcbiAgICAgIHRocm93IFZlbG9jaW91c0Vycm9yLnNhZmUoXCJTeW5jIGNoYW5nZXMgcm93IGhhcyBhbiBpbnZhbGlkIHVwZGF0ZWRfYXQgdmFsdWUuXCIsIHtjb2RlOiBcInN5bmMtaW52YWxpZC1jaGFuZ2VzLXJvd1wifSlcbiAgICB9XG5cbiAgICByZXR1cm4gZGF0ZS50b0lTT1N0cmluZygpXG4gIH1cbn1cblxuIl19
@@ -0,0 +1,77 @@
1
+ // @ts-check
2
+
3
+ /**
4
+ * @module sync-api-client-types
5
+ */
6
+
7
+ /** @typedef {{id: string | null, serverSequence: number | null, updatedAt: string} | null} SyncCursor */
8
+
9
+ /**
10
+ * @typedef {object} SyncChangeEnvelope
11
+ * @property {() => unknown} data - Sync data payload.
12
+ * @property {() => unknown} id - Sync row identifier.
13
+ * @property {() => unknown} resourceId - Resource identifier.
14
+ * @property {() => string | null} resourceType - Resource type name.
15
+ * @property {() => string} syncType - Sync operation type.
16
+ */
17
+
18
+ /**
19
+ * @typedef {object} SyncChangeApplyResult
20
+ * @property {boolean} changed - Whether the local store changed.
21
+ * @property {string | null} [resourceType] - Applied resource type override.
22
+ */
23
+
24
+ /**
25
+ * @typedef {object} SyncResourceConfig
26
+ * @property {(args: {attributes: Record<string, unknown>, data: Record<string, unknown>, record: ?, sync: SyncChangeEnvelope}) => Promise<boolean | void> | boolean | void} [afterApply] - Optional post-save hook.
27
+ * @property {(args: {data: Record<string, unknown>, record: ?, sync: SyncChangeEnvelope}) => Promise<Record<string, unknown>> | Record<string, unknown>} attributes - Allowed attributes builder.
28
+ * @property {boolean} enabled - Whether this resource is sync-enabled.
29
+ * @property {(args: {data: Record<string, unknown>, resourceId: unknown, sync: SyncChangeEnvelope}) => Promise<?> | ?} [findRecord] - Optional upsert finder.
30
+ * @property {(args: {resourceId: unknown, sync: SyncChangeEnvelope}) => Promise<?> | ?} [findRecordForDelete] - Optional destroy finder.
31
+ * @property {?} modelClass - Velocious model class.
32
+ */
33
+
34
+ /**
35
+ * @typedef {object} SyncChangesRequest
36
+ * @property {string} authenticationToken - Auth token.
37
+ * @property {string | null} [afterId] - Last seen row id.
38
+ * @property {number} [afterServerSequence] - Last seen server sequence.
39
+ * @property {string} [afterUpdatedAt] - Last seen timestamp.
40
+ * @property {number} limit - Page size.
41
+ * @property {string | null} [upToId] - Snapshot upper-bound row id.
42
+ * @property {number} [upToServerSequence] - Snapshot upper-bound server sequence.
43
+ * @property {string} [upToUpdatedAt] - Snapshot upper-bound timestamp.
44
+ */
45
+
46
+ /**
47
+ * @typedef {object} SyncChangesResponse
48
+ * @property {string} [errorMessage] - Error message.
49
+ * @property {SyncCursor | Record<string, ?>} [nextCursor] - Next cursor.
50
+ * @property {string} [status] - Response status.
51
+ * @property {Array<unknown>} [syncs] - Sync rows.
52
+ * @property {SyncCursor | Record<string, ?>} [upToCursor] - Snapshot upper-bound cursor.
53
+ */
54
+
55
+ /**
56
+ * @typedef {object} SyncChangesResult
57
+ * @property {boolean} changed - Whether any local record changed.
58
+ * @property {number} pages - Applied page count.
59
+ * @property {Record<string, boolean>} resourceChanged - Changed flags by resource type.
60
+ * @property {Record<string, number>} resourceCounts - Applied counts by resource type.
61
+ * @property {number} syncedCount - Applied row count.
62
+ */
63
+
64
+ /**
65
+ * @typedef {object} SyncReplayItem
66
+ * @property {string | number} id - Sync id.
67
+ * @property {string} syncState - Replay state.
68
+ */
69
+
70
+ /**
71
+ * @typedef {object} SyncReplayResponse
72
+ * @property {string} [errorMessage] - Error message.
73
+ * @property {string} [status] - Response status.
74
+ * @property {Array<SyncReplayItem>} [syncs] - Replay results.
75
+ */
76
+
77
+ export {}