velocious 1.0.317 → 1.0.319

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1630,6 +1630,37 @@ Supported schedule syntax:
1630
1630
  - `every: ["1h", {first_in: "30s"}]`
1631
1631
  - `every: ["1 day", {firstIn: "5 minutes"}]`
1632
1632
 
1633
+ Or a 5-field POSIX crontab expression via `cron`:
1634
+
1635
+ ```js
1636
+ scheduledBackgroundJobs: {
1637
+ jobs: {
1638
+ nightlyDigest: {
1639
+ class: NightlyDigestJob,
1640
+ cron: "0 3 * * *" // every day at 03:00 server-local time
1641
+ },
1642
+ weekdayMornings: {
1643
+ class: WeekdayMorningJob,
1644
+ cron: "0 9 * * 1-5" // 09:00 Mon–Fri
1645
+ },
1646
+ everyHour: {
1647
+ class: HourlyCleanupJob,
1648
+ cron: "@hourly"
1649
+ }
1650
+ }
1651
+ }
1652
+ ```
1653
+
1654
+ Cron fields are: `minute hour day-of-month month day-of-week`. Supported syntax:
1655
+
1656
+ - `*` (any), single values (`5`), ranges (`1-5`), lists (`1,3,5`).
1657
+ - Step expressions: `*/15` (every 15 minutes), `0-30/5` (every 5 between 0 and 30).
1658
+ - Month and weekday names: `jan`-`dec`, `sun`-`sat` (case-insensitive). Both `0` and `7` mean Sunday.
1659
+ - POSIX shortcuts: `@hourly`, `@daily` / `@midnight`, `@weekly`, `@monthly`, `@yearly` / `@annually`.
1660
+ - Day-of-month and day-of-week interaction follows POSIX/Vixie cron: when both are restricted (neither `*`), the job fires when **either** matches.
1661
+
1662
+ Each job must define exactly one of `every` or `cron`. Cron times are evaluated in the **server's local timezone**, at minute granularity.
1663
+
1633
1664
  `background-jobs-main` owns the schedule and enqueues the configured jobs into the normal Velocious background-jobs queue. The HTTP server does not run scheduled jobs itself.
1634
1665
 
1635
1666
  ## Persistence and retries
@@ -0,0 +1,62 @@
1
+ /**
2
+ * @typedef {object} ParsedCron
3
+ * @property {Set<number>} minute - Allowed minute values (0-59).
4
+ * @property {Set<number>} hour - Allowed hour values (0-23).
5
+ * @property {Set<number>} dayOfMonth - Allowed day-of-month values (1-31).
6
+ * @property {Set<number>} month - Allowed month values (1-12).
7
+ * @property {Set<number>} dayOfWeek - Allowed day-of-week values (0-6, 0=Sun).
8
+ * @property {boolean} dayOfMonthRestricted - True when the dayOfMonth field is not `*`.
9
+ * @property {boolean} dayOfWeekRestricted - True when the dayOfWeek field is not `*`.
10
+ * @property {string} expression - Original expression for diagnostics.
11
+ */
12
+ /**
13
+ * @param {string} expression - Cron expression or shortcut.
14
+ * @returns {ParsedCron}
15
+ */
16
+ export function parseCronExpression(expression: string): ParsedCron;
17
+ /**
18
+ * Returns the next Date strictly after `from` that satisfies `parsed`.
19
+ * Operates at minute granularity. Bails out with an error after five
20
+ * years of search, which only happens if the expression matches no
21
+ * real time (e.g., `0 0 31 2 *` — Feb 31st).
22
+ *
23
+ * @param {ParsedCron} parsed - Parsed cron expression.
24
+ * @param {Date} from - Reference Date — the next match is strictly after this.
25
+ * @returns {Date}
26
+ */
27
+ export function nextCronFireDate(parsed: ParsedCron, from: Date): Date;
28
+ export type ParsedCron = {
29
+ /**
30
+ * - Allowed minute values (0-59).
31
+ */
32
+ minute: Set<number>;
33
+ /**
34
+ * - Allowed hour values (0-23).
35
+ */
36
+ hour: Set<number>;
37
+ /**
38
+ * - Allowed day-of-month values (1-31).
39
+ */
40
+ dayOfMonth: Set<number>;
41
+ /**
42
+ * - Allowed month values (1-12).
43
+ */
44
+ month: Set<number>;
45
+ /**
46
+ * - Allowed day-of-week values (0-6, 0=Sun).
47
+ */
48
+ dayOfWeek: Set<number>;
49
+ /**
50
+ * - True when the dayOfMonth field is not `*`.
51
+ */
52
+ dayOfMonthRestricted: boolean;
53
+ /**
54
+ * - True when the dayOfWeek field is not `*`.
55
+ */
56
+ dayOfWeekRestricted: boolean;
57
+ /**
58
+ * - Original expression for diagnostics.
59
+ */
60
+ expression: string;
61
+ };
62
+ //# sourceMappingURL=cron-expression.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cron-expression.d.ts","sourceRoot":"","sources":["../../../src/background-jobs/cron-expression.js"],"names":[],"mappings":"AAsCA;;;;;;;;;;GAUG;AAEH;;;GAGG;AACH,gDAHW,MAAM,GACJ,UAAU,CA+BtB;AAsID;;;;;;;;;GASG;AACH,yCAJW,UAAU,QACV,IAAI,GACF,IAAI,CAehB;;;;;YAxMa,GAAG,CAAC,MAAM,CAAC;;;;UACX,GAAG,CAAC,MAAM,CAAC;;;;gBACX,GAAG,CAAC,MAAM,CAAC;;;;WACX,GAAG,CAAC,MAAM,CAAC;;;;eACX,GAAG,CAAC,MAAM,CAAC;;;;0BACX,OAAO;;;;yBACP,OAAO;;;;gBACP,MAAM"}
@@ -0,0 +1,228 @@
1
+ // @ts-check
2
+ /**
3
+ * Minimal POSIX-style 5-field cron parser used by the background-job
4
+ * scheduler. Supports `*`, single values, ranges (`N-M`), steps
5
+ * (`*\/N` or `N-M/N`), comma-separated lists, and the common
6
+ * `@hourly`/`@daily`/`@weekly`/`@monthly`/`@yearly`/`@midnight`
7
+ * shortcuts. Month and day-of-week names (`jan`-`dec`, `sun`-`sat`,
8
+ * case-insensitive) are also accepted.
9
+ *
10
+ * For day-of-month + day-of-week interaction, follows POSIX/Vixie
11
+ * cron semantics: when both fields are restricted (neither `*`), the
12
+ * job fires when EITHER matches. When one is `*` it has no effect.
13
+ */
14
+ const MONTH_NAMES = ["jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"];
15
+ const DAY_NAMES = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"];
16
+ const SHORTCUTS = {
17
+ "@hourly": "0 * * * *",
18
+ "@daily": "0 0 * * *",
19
+ "@midnight": "0 0 * * *",
20
+ "@weekly": "0 0 * * 0",
21
+ "@monthly": "0 0 1 * *",
22
+ "@yearly": "0 0 1 1 *",
23
+ "@annually": "0 0 1 1 *"
24
+ };
25
+ const FIELDS = [
26
+ { name: "minute", min: 0, max: 59 },
27
+ { name: "hour", min: 0, max: 23 },
28
+ { name: "dayOfMonth", min: 1, max: 31 },
29
+ { name: "month", min: 1, max: 12, names: MONTH_NAMES },
30
+ // Accept 0-7 so ranges like `5-7` (Fri-Sun) work; we normalize 7
31
+ // down to 0 after parsing in `normalizeDayOfWeek` below.
32
+ { name: "dayOfWeek", min: 0, max: 7, names: DAY_NAMES }
33
+ ];
34
+ /**
35
+ * @typedef {object} ParsedCron
36
+ * @property {Set<number>} minute - Allowed minute values (0-59).
37
+ * @property {Set<number>} hour - Allowed hour values (0-23).
38
+ * @property {Set<number>} dayOfMonth - Allowed day-of-month values (1-31).
39
+ * @property {Set<number>} month - Allowed month values (1-12).
40
+ * @property {Set<number>} dayOfWeek - Allowed day-of-week values (0-6, 0=Sun).
41
+ * @property {boolean} dayOfMonthRestricted - True when the dayOfMonth field is not `*`.
42
+ * @property {boolean} dayOfWeekRestricted - True when the dayOfWeek field is not `*`.
43
+ * @property {string} expression - Original expression for diagnostics.
44
+ */
45
+ /**
46
+ * @param {string} expression - Cron expression or shortcut.
47
+ * @returns {ParsedCron}
48
+ */
49
+ export function parseCronExpression(expression) {
50
+ if (typeof expression !== "string" || !expression.trim()) {
51
+ throw new Error(`Invalid cron expression: ${expression}`);
52
+ }
53
+ const trimmed = expression.trim().toLowerCase();
54
+ const expanded = SHORTCUTS[ /** @type {keyof typeof SHORTCUTS} */(trimmed)] || trimmed;
55
+ const fields = expanded.split(/\s+/);
56
+ if (fields.length !== 5) {
57
+ throw new Error(`Invalid cron expression "${expression}": expected 5 fields, got ${fields.length}`);
58
+ }
59
+ const [minuteField, hourField, dayOfMonthField, monthField, dayOfWeekField] = fields;
60
+ const parsed = {
61
+ minute: parseField(minuteField, FIELDS[0], expression),
62
+ hour: parseField(hourField, FIELDS[1], expression),
63
+ dayOfMonth: parseField(dayOfMonthField, FIELDS[2], expression),
64
+ month: parseField(monthField, FIELDS[3], expression),
65
+ // Cron treats both 0 and 7 as Sunday. We accept 7 throughout the
66
+ // parse pass (so `5-7` for Fri-Sun works) and then normalize any
67
+ // 7s down to 0 so the matcher only deals with 0-6.
68
+ dayOfWeek: normalizeDayOfWeek(parseField(dayOfWeekField, FIELDS[4], expression)),
69
+ dayOfMonthRestricted: dayOfMonthField !== "*",
70
+ dayOfWeekRestricted: dayOfWeekField !== "*",
71
+ expression
72
+ };
73
+ return parsed;
74
+ }
75
+ /**
76
+ * @param {Set<number>} dayOfWeek
77
+ * @returns {Set<number>}
78
+ */
79
+ function normalizeDayOfWeek(dayOfWeek) {
80
+ if (dayOfWeek.has(7)) {
81
+ dayOfWeek.delete(7);
82
+ dayOfWeek.add(0);
83
+ }
84
+ return dayOfWeek;
85
+ }
86
+ /**
87
+ * @param {string} field - Field expression.
88
+ * @param {{name: string, min: number, max: number, names?: string[]}} fieldSpec - Field spec.
89
+ * @param {string} expression - Whole cron expression for error messages.
90
+ * @returns {Set<number>}
91
+ */
92
+ function parseField(field, fieldSpec, expression) {
93
+ const result = new Set();
94
+ for (const part of field.split(",")) {
95
+ addPartValues(part, fieldSpec, expression, result);
96
+ }
97
+ return result;
98
+ }
99
+ /**
100
+ * @param {string} part - Single comma-separated chunk.
101
+ * @param {{name: string, min: number, max: number, names?: string[]}} fieldSpec - Field spec.
102
+ * @param {string} expression - Original expression for errors.
103
+ * @param {Set<number>} result - Accumulator.
104
+ * @returns {void}
105
+ */
106
+ function addPartValues(part, fieldSpec, expression, result) {
107
+ if (!part) {
108
+ throw new Error(`Invalid ${fieldSpec.name} field in cron expression "${expression}"`);
109
+ }
110
+ const [rangePart, stepPart] = part.split("/");
111
+ const step = stepPart === undefined ? 1 : parseStep(stepPart, fieldSpec, expression);
112
+ const [start, end] = parseRange(rangePart, fieldSpec, expression, stepPart !== undefined);
113
+ for (let value = start; value <= end; value += step) {
114
+ if (value < fieldSpec.min || value > fieldSpec.max) {
115
+ throw new Error(`Value ${value} out of range for ${fieldSpec.name} in cron expression "${expression}"`);
116
+ }
117
+ result.add(value);
118
+ }
119
+ }
120
+ /**
121
+ * @param {string} value - Step value.
122
+ * @param {{name: string, min: number, max: number}} fieldSpec - Field spec.
123
+ * @param {string} expression - Original expression for errors.
124
+ * @returns {number}
125
+ */
126
+ function parseStep(value, fieldSpec, expression) {
127
+ const step = Number(value);
128
+ if (!Number.isInteger(step) || step <= 0) {
129
+ throw new Error(`Invalid step "${value}" for ${fieldSpec.name} in cron expression "${expression}"`);
130
+ }
131
+ return step;
132
+ }
133
+ /**
134
+ * @param {string} rangePart - Range portion (before any `/`).
135
+ * @param {{name: string, min: number, max: number, names?: string[]}} fieldSpec - Field spec.
136
+ * @param {string} expression - Original expression for errors.
137
+ * @param {boolean} hasStep - Whether the part had a `/step` suffix.
138
+ * @returns {[number, number]}
139
+ */
140
+ function parseRange(rangePart, fieldSpec, expression, hasStep) {
141
+ if (rangePart === "*") {
142
+ return [fieldSpec.min, fieldSpec.max];
143
+ }
144
+ const dashIndex = rangePart.indexOf("-");
145
+ if (dashIndex === -1) {
146
+ const value = parseValue(rangePart, fieldSpec, expression);
147
+ // `N/step` is shorthand for `N-max/step` (Vixie cron).
148
+ return [value, hasStep ? fieldSpec.max : value];
149
+ }
150
+ const start = parseValue(rangePart.slice(0, dashIndex), fieldSpec, expression);
151
+ const end = parseValue(rangePart.slice(dashIndex + 1), fieldSpec, expression);
152
+ if (start > end) {
153
+ throw new Error(`Range start ${start} > end ${end} for ${fieldSpec.name} in cron expression "${expression}"`);
154
+ }
155
+ return [start, end];
156
+ }
157
+ /**
158
+ * @param {string} rawValue - Raw value (may be a name).
159
+ * @param {{name: string, min: number, max: number, names?: string[]}} fieldSpec - Field spec.
160
+ * @param {string} expression - Original expression for errors.
161
+ * @returns {number}
162
+ */
163
+ function parseValue(rawValue, fieldSpec, expression) {
164
+ if (!rawValue) {
165
+ throw new Error(`Invalid ${fieldSpec.name} value in cron expression "${expression}"`);
166
+ }
167
+ const namedIndex = fieldSpec.names?.indexOf(rawValue);
168
+ if (typeof namedIndex === "number" && namedIndex !== -1) {
169
+ return namedIndex + fieldSpec.min;
170
+ }
171
+ const value = Number(rawValue);
172
+ if (!Number.isInteger(value)) {
173
+ throw new Error(`Invalid ${fieldSpec.name} value "${rawValue}" in cron expression "${expression}"`);
174
+ }
175
+ return value;
176
+ }
177
+ // 5 years of minutes — covers the worst-case legitimate gap, the
178
+ // `0 0 29 2 *` (Feb 29) leap-year-only schedule, with a one-year
179
+ // buffer so we never report a real cron pattern as "never matches".
180
+ const MAX_NEXT_FIRE_ITERATIONS = 5 * 366 * 24 * 60;
181
+ /**
182
+ * Returns the next Date strictly after `from` that satisfies `parsed`.
183
+ * Operates at minute granularity. Bails out with an error after five
184
+ * years of search, which only happens if the expression matches no
185
+ * real time (e.g., `0 0 31 2 *` — Feb 31st).
186
+ *
187
+ * @param {ParsedCron} parsed - Parsed cron expression.
188
+ * @param {Date} from - Reference Date — the next match is strictly after this.
189
+ * @returns {Date}
190
+ */
191
+ export function nextCronFireDate(parsed, from) {
192
+ const candidate = new Date(from.getTime());
193
+ candidate.setSeconds(0, 0);
194
+ candidate.setMinutes(candidate.getMinutes() + 1);
195
+ for (let iterations = 0; iterations < MAX_NEXT_FIRE_ITERATIONS; iterations += 1) {
196
+ if (candidateMatches(candidate, parsed))
197
+ return candidate;
198
+ candidate.setMinutes(candidate.getMinutes() + 1);
199
+ }
200
+ throw new Error(`Cron expression "${parsed.expression}" never matches`);
201
+ }
202
+ /**
203
+ * @param {Date} candidate - Candidate Date (in local time).
204
+ * @param {ParsedCron} parsed - Parsed expression.
205
+ * @returns {boolean}
206
+ */
207
+ function candidateMatches(candidate, parsed) {
208
+ if (!parsed.minute.has(candidate.getMinutes()))
209
+ return false;
210
+ if (!parsed.hour.has(candidate.getHours()))
211
+ return false;
212
+ if (!parsed.month.has(candidate.getMonth() + 1))
213
+ return false;
214
+ const dayOfMonthMatch = parsed.dayOfMonth.has(candidate.getDate());
215
+ const dayOfWeekMatch = parsed.dayOfWeek.has(candidate.getDay());
216
+ // POSIX/Vixie cron OR semantics: when both day fields are
217
+ // restricted, fire when EITHER matches. When only one is
218
+ // restricted, only that one applies.
219
+ if (parsed.dayOfMonthRestricted && parsed.dayOfWeekRestricted) {
220
+ return dayOfMonthMatch || dayOfWeekMatch;
221
+ }
222
+ if (parsed.dayOfMonthRestricted)
223
+ return dayOfMonthMatch;
224
+ if (parsed.dayOfWeekRestricted)
225
+ return dayOfWeekMatch;
226
+ return true;
227
+ }
228
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3Jvbi1leHByZXNzaW9uLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2JhY2tncm91bmQtam9icy9jcm9uLWV4cHJlc3Npb24uanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsWUFBWTtBQUVaOzs7Ozs7Ozs7OztHQVdHO0FBRUgsTUFBTSxXQUFXLEdBQUcsQ0FBQyxLQUFLLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLEtBQUssQ0FBQyxDQUFBO0FBQ3hHLE1BQU0sU0FBUyxHQUFHLENBQUMsS0FBSyxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsS0FBSyxDQUFDLENBQUE7QUFFbkUsTUFBTSxTQUFTLEdBQUc7SUFDaEIsU0FBUyxFQUFFLFdBQVc7SUFDdEIsUUFBUSxFQUFFLFdBQVc7SUFDckIsV0FBVyxFQUFFLFdBQVc7SUFDeEIsU0FBUyxFQUFFLFdBQVc7SUFDdEIsVUFBVSxFQUFFLFdBQVc7SUFDdkIsU0FBUyxFQUFFLFdBQVc7SUFDdEIsV0FBVyxFQUFFLFdBQVc7Q0FDekIsQ0FBQTtBQUVELE1BQU0sTUFBTSxHQUFHO0lBQ2IsRUFBQyxJQUFJLEVBQUUsUUFBUSxFQUFFLEdBQUcsRUFBRSxDQUFDLEVBQUUsR0FBRyxFQUFFLEVBQUUsRUFBQztJQUNqQyxFQUFDLElBQUksRUFBRSxNQUFNLEVBQUUsR0FBRyxFQUFFLENBQUMsRUFBRSxHQUFHLEVBQUUsRUFBRSxFQUFDO0lBQy9CLEVBQUMsSUFBSSxFQUFFLFlBQVksRUFBRSxHQUFHLEVBQUUsQ0FBQyxFQUFFLEdBQUcsRUFBRSxFQUFFLEVBQUM7SUFDckMsRUFBQyxJQUFJLEVBQUUsT0FBTyxFQUFFLEdBQUcsRUFBRSxDQUFDLEVBQUUsR0FBRyxFQUFFLEVBQUUs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U0FBUyxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQTtJQUUvRCwwREFBMEQ7SUFDMUQseURBQXlEO0lBQ3pELHFDQUFxQztJQUNyQyxJQUFJLE1BQU0sQ0FBQyxvQkFBb0IsSUFBSSxNQUFNLENBQUMsbUJBQW1CLEVBQUUsQ0FBQztRQUM5RCxPQUFPLGVBQWUsSUFBSSxjQUFjLENBQUE7SUFDMUMsQ0FBQztJQUVELElBQUksTUFBTSxDQUFDLG9CQUFvQjtRQUFFLE9BQU8sZUFBZSxDQUFBO0lBQ3ZELElBQUksTUFBTSxDQUFDLG1CQUFtQjtRQUFFLE9BQU8sY0FBYyxDQUFBO0lBRXJELE9BQU8sSUFBSSxDQUFBO0FBQ2IsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIEB0cy1jaGVja1xuXG4vKipcbiAqIE1pbmltYWwgUE9TSVgtc3R5bGUgNS1maWVsZCBjcm9uIHBhcnNlciB1c2VkIGJ5IHRoZSBiYWNrZ3JvdW5kLWpvYlxuICogc2NoZWR1bGVyLiBTdXBwb3J0cyBgKmAsIHNpbmdsZSB2YWx1ZXMsIHJhbmdlcyAoYE4tTWApLCBzdGVwc1xuICogKGAqXFwvTmAgb3IgYE4tTS9OYCksIGNvbW1hLXNlcGFyYXRlZCBsaXN0cywgYW5kIHRoZSBjb21tb25cbiAqIGBAaG91cmx5YC9gQGRhaWx5YC9gQHdlZWtseWAvYEBtb250aGx5YC9gQHllYXJseWAvYEBtaWRuaWdodGBcbiAqIHNob3J0Y3V0cy4gTW9udGggYW5kIGRheS1vZi13ZWVrIG5hbWVzIChgamFuYC1gZGVjYCwgYHN1bmAtYHNhdGAsXG4gKiBjYXNlLWluc2Vuc2l0aXZlKSBhcmUgYWxzbyBhY2NlcHRlZC5cbiAqXG4gKiBGb3IgZGF5LW9mLW1vbnRoICsgZGF5LW9mLXdlZWsgaW50ZXJhY3Rpb24sIGZvbGxvd3MgUE9TSVgvVml4aWVcbiAqIGNyb24gc2VtYW50aWNzOiB3aGVuIGJvdGggZmllbGRzIGFyZSByZXN0cmljdGVkIChuZWl0aGVyIGAqYCksIHRoZVxuICogam9iIGZpcmVzIHdoZW4gRUlUSEVSIG1hdGNoZXMuIFdoZW4gb25lIGlzIGAqYCBpdCBoYXMgbm8gZWZmZWN0LlxuICovXG5cbmNvbnN0IE1PTlRIX05BTUVTID0gW1wiamFuXCIsIFwiZmViXCIsIFwibWFyXCIsIFwiYXByXCIsIFwibWF5XCIsIFwianVuXCIsIFwianVsXCIsIFwiYXVnXCIsIFwic2VwXCIsIFwib2N0XCIsIFwibm92XCIsIFwiZGVjXCJdXG5jb25zdCBEQVlfTkFNRVMgPSBbXCJzdW5cIiwgXCJtb25cIiwgXCJ0dWVcIiwgXCJ3ZWRcIiwgXCJ0aHVcIiwgXCJmcmlcIiwgXCJzYXRcIl1cblxuY29uc3QgU0hPUlRDVVRTID0ge1xuICBcIkBob3VybHlcIjogXCIwICogKiAqICpcIixcbiAgXCJAZGFpbHlcIjogXCIwIDAgKiAqICpcIixcbiAgXCJAbWlkbmlnaHRcIjogXCIwIDAgKiAqICpcIixcbiAgXCJAd2Vla2x5XCI6IFwiMCAwICogKiAwXCIsXG4gIFwiQG1vbnRobHlcIjogXCIwIDAgMSAqICpcIixcbiAgXCJAeWVhcmx5XCI6IFwiMCAwIDEgMSAqXCIsXG4gIFwiQGFubnVhbGx5XCI6IFwiMCAwIDEgMSAqXCJcbn1cblxuY29uc3QgRklFTERTID0gW1xuICB7bmFtZTogXCJtaW51dGVcIiwgbWluOiAwLCBtYXg6IDU5fSxcbiAge25hbWU6IFwiaG91clwiLCBtaW46IDAsIG1heDogMjN9LFxuICB7bmFtZTogXCJkYXlPZk1vbnRoXCIsIG1pbjogMSwgbWF4OiAzMX0sXG4gIHtuYW1lOiBcIm1vbnRoXCIsIG1pbjogMSwgbWF4OiAxMiwgbmFtZXM6IE1PTlRIX05BTUVTfSxcbiAgLy8gQWNjZXB0IDAtNyBzbyByYW5nZXMgbGlrZSBgNS03YCAoRnJpLVN1bikgd29yazsgd2Ugbm9ybWFsaXplIDdcbiAgLy8gZG93biB0byAwIGFmdGVyIHBhcnNpbmcgaW4gYG5vcm1hbGl6ZURheU9mV2Vla2AgYmVsb3cuXG4gIHtuYW1lOiBcImRheU9mV2Vla1wiLCBtaW46IDAsIG1heDogNywgbmFtZXM6IERBWV9OQU1FU31cbl1cblxuLyoqXG4gKiBAdHlwZWRlZiB7b2JqZWN0fSBQYXJzZWRDcm9uXG4gKiBAcHJvcGVydHkge1NldDxudW1iZXI+fSBtaW51dGUgLSBBbGxvd2VkIG1pbnV0ZSB2YWx1ZXMgKDAtNTkpLlxuICogQHByb3BlcnR5IHtTZXQ8bnVtYmVyPn0gaG91ciAtIEFsbG93ZWQgaG91ciB2YWx1ZXMgKDAtMjMpLlxuICogQHByb3BlcnR5IHtTZXQ8bnVtYmVyPn0gZGF5T2ZNb250aCAtIEFsbG93ZWQgZGF5LW9mLW1vbnRoIHZhbHVlcyAoMS0zMSkuXG4gKiBAcHJvcGVydHkge1NldDxudW1iZXI+fSBtb250aCAtIEFsbG93ZWQgbW9udGggdmFsdWVzICgxLTEyKS5cbiAqIEBwcm9wZXJ0eSB7U2V0PG51bWJlcj59IGRheU9mV2VlayAtIEFsbG93ZWQgZGF5LW9mLXdlZWsgdmFsdWVzICgwLTYsIDA9U3VuKS5cbiAqIEBwcm9wZXJ0eSB7Ym9vbGVhbn0gZGF5T2ZNb250aFJlc3RyaWN0ZWQgLSBUcnVlIHdoZW4gdGhlIGRheU9mTW9udGggZmllbGQgaXMgbm90IGAqYC5cbiAqIEBwcm9wZXJ0eSB7Ym9vbGVhbn0gZGF5T2ZXZWVrUmVzdHJpY3RlZCAtIFRydWUgd2hlbiB0aGUgZGF5T2ZXZWVrIGZpZWxkIGlzIG5vdCBgKmAuXG4gKiBAcHJvcGVydHkge3N0cmluZ30gZXhwcmVzc2lvbiAtIE9yaWdpbmFsIGV4cHJlc3Npb24gZm9yIGRpYWdub3N0aWNzLlxuICovXG5cbi8qKlxuICogQHBhcmFtIHtzdHJpbmd9IGV4cHJlc3Npb24gLSBDcm9uIGV4cHJlc3Npb24gb3Igc2hvcnRjdXQuXG4gKiBAcmV0dXJucyB7UGFyc2VkQ3Jvbn1cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHBhcnNlQ3JvbkV4cHJlc3Npb24oZXhwcmVzc2lvbikge1xuICBpZiAodHlwZW9mIGV4cHJlc3Npb24gIT09IFwic3RyaW5nXCIgfHwgIWV4cHJlc3Npb24udHJpbSgpKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkIGNyb24gZXhwcmVzc2lvbjogJHtleHByZXNzaW9ufWApXG4gIH1cblxuICBjb25zdCB0cmltbWVkID0gZXhwcmVzc2lvbi50cmltKCkudG9Mb3dlckNhc2UoKVxuICBjb25zdCBleHBhbmRlZCA9IFNIT1JUQ1VUU1svKiogQHR5cGUge2tleW9mIHR5cGVvZiBTSE9SVENVVFN9ICovICh0cmltbWVkKV0gfHwgdHJpbW1lZFxuICBjb25zdCBmaWVsZHMgPSBleHBhbmRlZC5zcGxpdCgvXFxzKy8pXG5cbiAgaWYgKGZpZWxkcy5sZW5ndGggIT09IDUpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYEludmFsaWQgY3JvbiBleHByZXNzaW9uIFwiJHtleHByZXNzaW9ufVwiOiBleHBlY3RlZCA1IGZpZWxkcywgZ290ICR7ZmllbGRzLmxlbmd0aH1gKVxuICB9XG5cbiAgY29uc3QgW21pbnV0ZUZpZWxkLCBob3VyRmllbGQsIGRheU9mTW9udGhGaWVsZCwgbW9udGhGaWVsZCwgZGF5T2ZXZWVrRmllbGRdID0gZmllbGRzXG4gIGNvbnN0IHBhcnNlZCA9IHtcbiAgICBtaW51dGU6IHBhcnNlRmllbGQobWludXRlRmllbGQsIEZJRUxEU1swXSwgZXhwcmVzc2lvbiksXG4gICAgaG91cjogcGFyc2VGaWVsZChob3VyRmllbGQsIEZJRUxEU1sxXSwgZXhwcmVzc2lvbiksXG4gICAgZGF5T2ZNb250aDogcGFyc2VGaWVsZChkYXlPZk1vbnRoRmllbGQsIEZJRUxEU1syXSwgZXhwcmVzc2lvbiksXG4gICAgbW9udGg6IHBhcnNlRmllbGQobW9udGhGaWVsZCwgRklFTERTWzNdLCBleHByZXNzaW9uKSxcbiAgICAvLyBDcm9uIHRyZWF0cyBib3RoIDAgYW5kIDcgYXMgU3VuZGF5LiBXZSBhY2NlcHQgNyB0aHJvdWdob3V0IHRoZVxuICAgIC8vIHBhcnNlIHBhc3MgKHNvIGA1LTdgIGZvciBGcmktU3VuIHdvcmtzKSBhbmQgdGhlbiBub3JtYWxpemUgYW55XG4gICAgLy8gN3MgZG93biB0byAwIHNvIHRoZSBtYXRjaGVyIG9ubHkgZGVhbHMgd2l0aCAwLTYuXG4gICAgZGF5T2ZXZWVrOiBub3JtYWxpemVEYXlPZldlZWsocGFyc2VGaWVsZChkYXlPZldlZWtGaWVsZCwgRklFTERTWzRdLCBleHByZXNzaW9uKSksXG4gICAgZGF5T2ZNb250aFJlc3RyaWN0ZWQ6IGRheU9mTW9udGhGaWVsZCAhPT0gXCIqXCIsXG4gICAgZGF5T2ZXZWVrUmVzdHJpY3RlZDogZGF5T2ZXZWVrRmllbGQgIT09IFwiKlwiLFxuICAgIGV4cHJlc3Npb25cbiAgfVxuXG4gIHJldHVybiBwYXJzZWRcbn1cblxuLyoqXG4gKiBAcGFyYW0ge1NldDxudW1iZXI+fSBkYXlPZldlZWtcbiAqIEByZXR1cm5zIHtTZXQ8bnVtYmVyPn1cbiAqL1xuZnVuY3Rpb24gbm9ybWFsaXplRGF5T2ZXZWVrKGRheU9mV2Vlaykge1xuICBpZiAoZGF5T2ZXZWVrLmhhcyg3KSkge1xuICAgIGRheU9mV2Vlay5kZWxldGUoNylcbiAgICBkYXlPZldlZWsuYWRkKDApXG4gIH1cblxuICByZXR1cm4gZGF5T2ZXZWVrXG59XG5cbi8qKlxuICogQHBhcmFtIHtzdHJpbmd9IGZpZWxkIC0gRmllbGQgZXhwcmVzc2lvbi5cbiAqIEBwYXJhbSB7e25hbWU6IHN0cmluZywgbWluOiBudW1iZXIsIG1heDogbnVtYmVyLCBuYW1lcz86IHN0cmluZ1tdfX0gZmllbGRTcGVjIC0gRmllbGQgc3BlYy5cbiAqIEBwYXJhbSB7c3RyaW5nfSBleHByZXNzaW9uIC0gV2hvbGUgY3JvbiBleHByZXNzaW9uIGZvciBlcnJvciBtZXNzYWdlcy5cbiAqIEByZXR1cm5zIHtTZXQ8bnVtYmVyPn1cbiAqL1xuZnVuY3Rpb24gcGFyc2VGaWVsZChmaWVsZCwgZmllbGRTcGVjLCBleHByZXNzaW9uKSB7XG4gIGNvbnN0IHJlc3VsdCA9IG5ldyBTZXQoKVxuXG4gIGZvciAoY29uc3QgcGFydCBvZiBmaWVsZC5zcGxpdChcIixcIikpIHtcbiAgICBhZGRQYXJ0VmFsdWVzKHBhcnQsIGZpZWxkU3BlYywgZXhwcmVzc2lvbiwgcmVzdWx0KVxuICB9XG5cbiAgcmV0dXJuIHJlc3VsdFxufVxuXG4vKipcbiAqIEBwYXJhbSB7c3RyaW5nfSBwYXJ0IC0gU2luZ2xlIGNvbW1hLXNlcGFyYXRlZCBjaHVuay5cbiAqIEBwYXJhbSB7e25hbWU6IHN0cmluZywgbWluOiBudW1iZXIsIG1heDogbnVtYmVyLCBuYW1lcz86IHN0cmluZ1tdfX0gZmllbGRTcGVjIC0gRmllbGQgc3BlYy5cbiAqIEBwYXJhbSB7c3RyaW5nfSBleHByZXNzaW9uIC0gT3JpZ2luYWwgZXhwcmVzc2lvbiBmb3IgZXJyb3JzLlxuICogQHBhcmFtIHtTZXQ8bnVtYmVyPn0gcmVzdWx0IC0gQWNjdW11bGF0b3IuXG4gKiBAcmV0dXJucyB7dm9pZH1cbiAqL1xuZnVuY3Rpb24gYWRkUGFydFZhbHVlcyhwYXJ0LCBmaWVsZFNwZWMsIGV4cHJlc3Npb24sIHJlc3VsdCkge1xuICBpZiAoIXBhcnQpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYEludmFsaWQgJHtmaWVsZFNwZWMubmFtZX0gZmllbGQgaW4gY3JvbiBleHByZXNzaW9uIFwiJHtleHByZXNzaW9ufVwiYClcbiAgfVxuXG4gIGNvbnN0IFtyYW5nZVBhcnQsIHN0ZXBQYXJ0XSA9IHBhcnQuc3BsaXQoXCIvXCIpXG4gIGNvbnN0IHN0ZXAgPSBzdGVwUGFydCA9PT0gdW5kZWZpbmVkID8gMSA6IHBhcnNlU3RlcChzdGVwUGFydCwgZmllbGRTcGVjLCBleHByZXNzaW9uKVxuICBjb25zdCBbc3RhcnQsIGVuZF0gPSBwYXJzZVJhbmdlKHJhbmdlUGFydCwgZmllbGRTcGVjLCBleHByZXNzaW9uLCBzdGVwUGFydCAhPT0gdW5kZWZpbmVkKVxuXG4gIGZvciAobGV0IHZhbHVlID0gc3RhcnQ7IHZhbHVlIDw9IGVuZDsgdmFsdWUgKz0gc3RlcCkge1xuICAgIGlmICh2YWx1ZSA8IGZpZWxkU3BlYy5taW4gfHwgdmFsdWUgPiBmaWVsZFNwZWMubWF4KSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYFZhbHVlICR7dmFsdWV9IG91dCBvZiByYW5nZSBmb3IgJHtmaWVsZFNwZWMubmFtZX0gaW4gY3JvbiBleHByZXNzaW9uIFwiJHtleHByZXNzaW9ufVwiYClcbiAgICB9XG5cbiAgICByZXN1bHQuYWRkKHZhbHVlKVxuICB9XG59XG5cbi8qKlxuICogQHBhcmFtIHtzdHJpbmd9IHZhbHVlIC0gU3RlcCB2YWx1ZS5cbiAqIEBwYXJhbSB7e25hbWU6IHN0cmluZywgbWluOiBudW1iZXIsIG1heDogbnVtYmVyfX0gZmllbGRTcGVjIC0gRmllbGQgc3BlYy5cbiAqIEBwYXJhbSB7c3RyaW5nfSBleHByZXNzaW9uIC0gT3JpZ2luYWwgZXhwcmVzc2lvbiBmb3IgZXJyb3JzLlxuICogQHJldHVybnMge251bWJlcn1cbiAqL1xuZnVuY3Rpb24gcGFyc2VTdGVwKHZhbHVlLCBmaWVsZFNwZWMsIGV4cHJlc3Npb24pIHtcbiAgY29uc3Qgc3RlcCA9IE51bWJlcih2YWx1ZSlcblxuICBpZiAoIU51bWJlci5pc0ludGVnZXIoc3RlcCkgfHwgc3RlcCA8PSAwKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkIHN0ZXAgXCIke3ZhbHVlfVwiIGZvciAke2ZpZWxkU3BlYy5uYW1lfSBpbiBjcm9uIGV4cHJlc3Npb24gXCIke2V4cHJlc3Npb259XCJgKVxuICB9XG5cbiAgcmV0dXJuIHN0ZXBcbn1cblxuLyoqXG4gKiBAcGFyYW0ge3N0cmluZ30gcmFuZ2VQYXJ0IC0gUmFuZ2UgcG9ydGlvbiAoYmVmb3JlIGFueSBgL2ApLlxuICogQHBhcmFtIHt7bmFtZTogc3RyaW5nLCBtaW46IG51bWJlciwgbWF4OiBudW1iZXIsIG5hbWVzPzogc3RyaW5nW119fSBmaWVsZFNwZWMgLSBGaWVsZCBzcGVjLlxuICogQHBhcmFtIHtzdHJpbmd9IGV4cHJlc3Npb24gLSBPcmlnaW5hbCBleHByZXNzaW9uIGZvciBlcnJvcnMuXG4gKiBAcGFyYW0ge2Jvb2xlYW59IGhhc1N0ZXAgLSBXaGV0aGVyIHRoZSBwYXJ0IGhhZCBhIGAvc3RlcGAgc3VmZml4LlxuICogQHJldHVybnMge1tudW1iZXIsIG51bWJlcl19XG4gKi9cbmZ1bmN0aW9uIHBhcnNlUmFuZ2UocmFuZ2VQYXJ0LCBmaWVsZFNwZWMsIGV4cHJlc3Npb24sIGhhc1N0ZXApIHtcbiAgaWYgKHJhbmdlUGFydCA9PT0gXCIqXCIpIHtcbiAgICByZXR1cm4gW2ZpZWxkU3BlYy5taW4sIGZpZWxkU3BlYy5tYXhdXG4gIH1cblxuICBjb25zdCBkYXNoSW5kZXggPSByYW5nZVBhcnQuaW5kZXhPZihcIi1cIilcblxuICBpZiAoZGFzaEluZGV4ID09PSAtMSkge1xuICAgIGNvbnN0IHZhbHVlID0gcGFyc2VWYWx1ZShyYW5nZVBhcnQsIGZpZWxkU3BlYywgZXhwcmVzc2lvbilcblxuICAgIC8vIGBOL3N0ZXBgIGlzIHNob3J0aGFuZCBmb3IgYE4tbWF4L3N0ZXBgIChWaXhpZSBjcm9uKS5cbiAgICByZXR1cm4gW3ZhbHVlLCBoYXNTdGVwID8gZmllbGRTcGVjLm1heCA6IHZhbHVlXVxuICB9XG5cbiAgY29uc3Qgc3RhcnQgPSBwYXJzZVZhbHVlKHJhbmdlUGFydC5zbGljZSgwLCBkYXNoSW5kZXgpLCBmaWVsZFNwZWMsIGV4cHJlc3Npb24pXG4gIGNvbnN0IGVuZCA9IHBhcnNlVmFsdWUocmFuZ2VQYXJ0LnNsaWNlKGRhc2hJbmRleCArIDEpLCBmaWVsZFNwZWMsIGV4cHJlc3Npb24pXG5cbiAgaWYgKHN0YXJ0ID4gZW5kKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBSYW5nZSBzdGFydCAke3N0YXJ0fSA+IGVuZCAke2VuZH0gZm9yICR7ZmllbGRTcGVjLm5hbWV9IGluIGNyb24gZXhwcmVzc2lvbiBcIiR7ZXhwcmVzc2lvbn1cImApXG4gIH1cblxuICByZXR1cm4gW3N0YXJ0LCBlbmRdXG59XG5cbi8qKlxuICogQHBhcmFtIHtzdHJpbmd9IHJhd1ZhbHVlIC0gUmF3IHZhbHVlIChtYXkgYmUgYSBuYW1lKS5cbiAqIEBwYXJhbSB7e25hbWU6IHN0cmluZywgbWluOiBudW1iZXIsIG1heDogbnVtYmVyLCBuYW1lcz86IHN0cmluZ1tdfX0gZmllbGRTcGVjIC0gRmllbGQgc3BlYy5cbiAqIEBwYXJhbSB7c3RyaW5nfSBleHByZXNzaW9uIC0gT3JpZ2luYWwgZXhwcmVzc2lvbiBmb3IgZXJyb3JzLlxuICogQHJldHVybnMge251bWJlcn1cbiAqL1xuZnVuY3Rpb24gcGFyc2VWYWx1ZShyYXdWYWx1ZSwgZmllbGRTcGVjLCBleHByZXNzaW9uKSB7XG4gIGlmICghcmF3VmFsdWUpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYEludmFsaWQgJHtmaWVsZFNwZWMubmFtZX0gdmFsdWUgaW4gY3JvbiBleHByZXNzaW9uIFwiJHtleHByZXNzaW9ufVwiYClcbiAgfVxuXG4gIGNvbnN0IG5hbWVkSW5kZXggPSBmaWVsZFNwZWMubmFtZXM/LmluZGV4T2YocmF3VmFsdWUpXG5cbiAgaWYgKHR5cGVvZiBuYW1lZEluZGV4ID09PSBcIm51bWJlclwiICYmIG5hbWVkSW5kZXggIT09IC0xKSB7XG4gICAgcmV0dXJuIG5hbWVkSW5kZXggKyBmaWVsZFNwZWMubWluXG4gIH1cblxuICBjb25zdCB2YWx1ZSA9IE51bWJlcihyYXdWYWx1ZSlcblxuICBpZiAoIU51bWJlci5pc0ludGVnZXIodmFsdWUpKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkICR7ZmllbGRTcGVjLm5hbWV9IHZhbHVlIFwiJHtyYXdWYWx1ZX1cIiBpbiBjcm9uIGV4cHJlc3Npb24gXCIke2V4cHJlc3Npb259XCJgKVxuICB9XG5cbiAgcmV0dXJuIHZhbHVlXG59XG5cbi8vIDUgeWVhcnMgb2YgbWludXRlcyDigJQgY292ZXJzIHRoZSB3b3JzdC1jYXNlIGxlZ2l0aW1hdGUgZ2FwLCB0aGVcbi8vIGAwIDAgMjkgMiAqYCAoRmViIDI5KSBsZWFwLXllYXItb25seSBzY2hlZHVsZSwgd2l0aCBhIG9uZS15ZWFyXG4vLyBidWZmZXIgc28gd2UgbmV2ZXIgcmVwb3J0IGEgcmVhbCBjcm9uIHBhdHRlcm4gYXMgXCJuZXZlciBtYXRjaGVzXCIuXG5jb25zdCBNQVhfTkVYVF9GSVJFX0lURVJBVElPTlMgPSA1ICogMzY2ICogMjQgKiA2MFxuXG4vKipcbiAqIFJldHVybnMgdGhlIG5leHQgRGF0ZSBzdHJpY3RseSBhZnRlciBgZnJvbWAgdGhhdCBzYXRpc2ZpZXMgYHBhcnNlZGAuXG4gKiBPcGVyYXRlcyBhdCBtaW51dGUgZ3JhbnVsYXJpdHkuIEJhaWxzIG91dCB3aXRoIGFuIGVycm9yIGFmdGVyIGZpdmVcbiAqIHllYXJzIG9mIHNlYXJjaCwgd2hpY2ggb25seSBoYXBwZW5zIGlmIHRoZSBleHByZXNzaW9uIG1hdGNoZXMgbm9cbiAqIHJlYWwgdGltZSAoZS5nLiwgYDAgMCAzMSAyICpgIOKAlCBGZWIgMzFzdCkuXG4gKlxuICogQHBhcmFtIHtQYXJzZWRDcm9ufSBwYXJzZWQgLSBQYXJzZWQgY3JvbiBleHByZXNzaW9uLlxuICogQHBhcmFtIHtEYXRlfSBmcm9tIC0gUmVmZXJlbmNlIERhdGUg4oCUIHRoZSBuZXh0IG1hdGNoIGlzIHN0cmljdGx5IGFmdGVyIHRoaXMuXG4gKiBAcmV0dXJucyB7RGF0ZX1cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIG5leHRDcm9uRmlyZURhdGUocGFyc2VkLCBmcm9tKSB7XG4gIGNvbnN0IGNhbmRpZGF0ZSA9IG5ldyBEYXRlKGZyb20uZ2V0VGltZSgpKVxuXG4gIGNhbmRpZGF0ZS5zZXRTZWNvbmRzKDAsIDApXG4gIGNhbmRpZGF0ZS5zZXRNaW51dGVzKGNhbmRpZGF0ZS5nZXRNaW51dGVzKCkgKyAxKVxuXG4gIGZvciAobGV0IGl0ZXJhdGlvbnMgPSAwOyBpdGVyYXRpb25zIDwgTUFYX05FWFRfRklSRV9JVEVSQVRJT05TOyBpdGVyYXRpb25zICs9IDEpIHtcbiAgICBpZiAoY2FuZGlkYXRlTWF0Y2hlcyhjYW5kaWRhdGUsIHBhcnNlZCkpIHJldHVybiBjYW5kaWRhdGVcblxuICAgIGNhbmRpZGF0ZS5zZXRNaW51dGVzKGNhbmRpZGF0ZS5nZXRNaW51dGVzKCkgKyAxKVxuICB9XG5cbiAgdGhyb3cgbmV3IEVycm9yKGBDcm9uIGV4cHJlc3Npb24gXCIke3BhcnNlZC5leHByZXNzaW9ufVwiIG5ldmVyIG1hdGNoZXNgKVxufVxuXG4vKipcbiAqIEBwYXJhbSB7RGF0ZX0gY2FuZGlkYXRlIC0gQ2FuZGlkYXRlIERhdGUgKGluIGxvY2FsIHRpbWUpLlxuICogQHBhcmFtIHtQYXJzZWRDcm9ufSBwYXJzZWQgLSBQYXJzZWQgZXhwcmVzc2lvbi5cbiAqIEByZXR1cm5zIHtib29sZWFufVxuICovXG5mdW5jdGlvbiBjYW5kaWRhdGVNYXRjaGVzKGNhbmRpZGF0ZSwgcGFyc2VkKSB7XG4gIGlmICghcGFyc2VkLm1pbnV0ZS5oYXMoY2FuZGlkYXRlLmdldE1pbnV0ZXMoKSkpIHJldHVybiBmYWxzZVxuICBpZiAoIXBhcnNlZC5ob3VyLmhhcyhjYW5kaWRhdGUuZ2V0SG91cnMoKSkpIHJldHVybiBmYWxzZVxuICBpZiAoIXBhcnNlZC5tb250aC5oYXMoY2FuZGlkYXRlLmdldE1vbnRoKCkgKyAxKSkgcmV0dXJuIGZhbHNlXG5cbiAgY29uc3QgZGF5T2ZNb250aE1hdGNoID0gcGFyc2VkLmRheU9mTW9udGguaGFzKGNhbmRpZGF0ZS5nZXREYXRlKCkpXG4gIGNvbnN0IGRheU9mV2Vla01hdGNoID0gcGFyc2VkLmRheU9mV2Vlay5oYXMoY2FuZGlkYXRlLmdldERheSgpKVxuXG4gIC8vIFBPU0lYL1ZpeGllIGNyb24gT1Igc2VtYW50aWNzOiB3aGVuIGJvdGggZGF5IGZpZWxkcyBhcmVcbiAgLy8gcmVzdHJpY3RlZCwgZmlyZSB3aGVuIEVJVEhFUiBtYXRjaGVzLiBXaGVuIG9ubHkgb25lIGlzXG4gIC8vIHJlc3RyaWN0ZWQsIG9ubHkgdGhhdCBvbmUgYXBwbGllcy5cbiAgaWYgKHBhcnNlZC5kYXlPZk1vbnRoUmVzdHJpY3RlZCAmJiBwYXJzZWQuZGF5T2ZXZWVrUmVzdHJpY3RlZCkge1xuICAgIHJldHVybiBkYXlPZk1vbnRoTWF0Y2ggfHwgZGF5T2ZXZWVrTWF0Y2hcbiAgfVxuXG4gIGlmIChwYXJzZWQuZGF5T2ZNb250aFJlc3RyaWN0ZWQpIHJldHVybiBkYXlPZk1vbnRoTWF0Y2hcbiAgaWYgKHBhcnNlZC5kYXlPZldlZWtSZXN0cmljdGVkKSByZXR1cm4gZGF5T2ZXZWVrTWF0Y2hcblxuICByZXR1cm4gdHJ1ZVxufVxuIl19
@@ -33,6 +33,8 @@ export default class BackgroundJobsScheduler {
33
33
  intervalIds: Array<ReturnType<typeof setInterval>>;
34
34
  /** @type {Array<ReturnType<typeof setTimeout>>} */
35
35
  timeoutIds: Array<ReturnType<typeof setTimeout>>;
36
+ /** @type {boolean} - True between stop() and the next start(); cron self-rescheduler checks this so a stop() during an in-flight enqueue doesn't immediately re-arm. */
37
+ stopped: boolean;
36
38
  /** @returns {Promise<void>} */
37
39
  start(): Promise<void>;
38
40
  /** @returns {void} */
@@ -47,6 +49,31 @@ export default class BackgroundJobsScheduler {
47
49
  jobConfiguration: import("../configuration-types.js").ScheduledBackgroundJobConfiguration;
48
50
  jobKey: string;
49
51
  }): void;
52
+ /**
53
+ * @param {object} args - Options.
54
+ * @param {import("../configuration-types.js").ScheduledBackgroundJobConfiguration} args.jobConfiguration - Job configuration.
55
+ * @param {string} args.jobKey - Job key.
56
+ * @returns {void}
57
+ */
58
+ scheduleEveryJob({ jobConfiguration, jobKey }: {
59
+ jobConfiguration: import("../configuration-types.js").ScheduledBackgroundJobConfiguration;
60
+ jobKey: string;
61
+ }): void;
62
+ /**
63
+ * Crontab schedules don't have a constant interval (`0 9 * * 1-5`
64
+ * fires once per weekday at 9 AM, with gaps of varying length), so
65
+ * we self-reschedule with `setTimeout` after every fire instead of
66
+ * using `setInterval`.
67
+ *
68
+ * @param {object} args - Options.
69
+ * @param {import("../configuration-types.js").ScheduledBackgroundJobConfiguration} args.jobConfiguration - Job configuration.
70
+ * @param {string} args.jobKey - Job key.
71
+ * @returns {void}
72
+ */
73
+ scheduleCronJob({ jobConfiguration, jobKey }: {
74
+ jobConfiguration: import("../configuration-types.js").ScheduledBackgroundJobConfiguration;
75
+ jobKey: string;
76
+ }): void;
50
77
  /**
51
78
  * @param {object} args - Options.
52
79
  * @param {import("../configuration-types.js").ScheduledBackgroundJobConfiguration} args.jobConfiguration - Job configuration.
@@ -58,10 +85,10 @@ export default class BackgroundJobsScheduler {
58
85
  jobKey: string;
59
86
  }): Promise<void>;
60
87
  /**
61
- * @param {import("../configuration-types.js").ScheduledBackgroundJobConfiguration["every"]} every - Every config.
88
+ * @param {NonNullable<import("../configuration-types.js").ScheduledBackgroundJobConfiguration["every"]>} every - Every config (caller must guarantee not undefined).
62
89
  * @returns {{everyValue: number | string, firstInValue?: number | string}} - Normalized interval and first-run delay values.
63
90
  */
64
- normalizeEvery(every: import("../configuration-types.js").ScheduledBackgroundJobConfiguration["every"]): {
91
+ normalizeEvery(every: NonNullable<import("../configuration-types.js").ScheduledBackgroundJobConfiguration["every"]>): {
65
92
  everyValue: number | string;
66
93
  firstInValue?: number | string;
67
94
  };
@@ -1 +1 @@
1
- {"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../../../src/background-jobs/scheduler.js"],"names":[],"mappings":"AAsBA,gEAAgE;AAEhE;;;;GAIG;AACH,8CAJW,MAAM,GAAG,MAAM,aACf,MAAM,GACJ,MAAM,CA8BlB;AAED,0DAA0D;AAC1D;IACE;;;;OAIG;IACH,2CAHG;QAAoD,aAAa,EAAzD,OAAO,qBAAqB,EAAE,OAAO;QAC0H,UAAU,EAAzK,CAAS,IAA8H,EAA9H;YAAC,IAAI,EAAE,GAAG,EAAE,CAAC;YAAC,QAAQ,EAAE,cAAc,UAAU,EAAE,OAAO,CAAC;YAAC,MAAM,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,OAAO,YAAY,EAAE,oBAAoB,CAAA;SAAC,KAAI,OAAO,CAAC,IAAI,CAAC;KAClK,EASA;IAPC,qDAAkC;IAClC,mBAJkB;QAAC,IAAI,EAAE,GAAG,EAAE,CAAC;QAAC,QAAQ,EAAE,cAAc,UAAU,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,YAAY,EAAE,oBAAoB,CAAA;KAAC,KAAI,OAAO,CAAC,IAAI,CAAC,CAIrI;IAC5B,eAA8B;IAC9B,oDAAoD;IACpD,aADW,KAAK,CAAC,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC,CAC3B;IACrB,mDAAmD;IACnD,YADW,KAAK,CAAC,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC,CAC3B;IAGtB,+BAA+B;IAC/B,SADc,OAAO,CAAC,IAAI,CAAC,CAiB1B;IAED,sBAAsB;IACtB,QADc,IAAI,CAYjB;IAED;;;;;OAKG;IACH,0CAJG;QAAsF,gBAAgB,EAA9F,OAAO,2BAA2B,EAAE,mCAAmC;QAC1D,MAAM,EAAnB,MAAM;KACd,GAAU,IAAI,CA0BhB;IAED;;;;;OAKG;IACH,kDAJG;QAAsF,gBAAgB,EAA9F,OAAO,2BAA2B,EAAE,mCAAmC;QAC1D,MAAM,EAAnB,MAAM;KACd,GAAU,OAAO,CAAC,IAAI,CAAC,CAazB;IAED;;;OAGG;IACH,sBAHW,OAAO,2BAA2B,EAAE,mCAAmC,CAAC,OAAO,CAAC,GAC9E;QAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;KAAC,CAczE;CACF;2BA3Ja,MAAM,OAAO,oBAAoB;mBApB5B,cAAc"}
1
+ {"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../../../src/background-jobs/scheduler.js"],"names":[],"mappings":"AAuBA,gEAAgE;AAEhE;;;;GAIG;AACH,8CAJW,MAAM,GAAG,MAAM,aACf,MAAM,GACJ,MAAM,CA8BlB;AAED,0DAA0D;AAC1D;IACE;;;;OAIG;IACH,2CAHG;QAAoD,aAAa,EAAzD,OAAO,qBAAqB,EAAE,OAAO;QAC0H,UAAU,EAAzK,CAAS,IAA8H,EAA9H;YAAC,IAAI,EAAE,GAAG,EAAE,CAAC;YAAC,QAAQ,EAAE,cAAc,UAAU,EAAE,OAAO,CAAC;YAAC,MAAM,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,OAAO,YAAY,EAAE,oBAAoB,CAAA;SAAC,KAAI,OAAO,CAAC,IAAI,CAAC;KAClK,EAWA;IATC,qDAAkC;IAClC,mBAJkB;QAAC,IAAI,EAAE,GAAG,EAAE,CAAC;QAAC,QAAQ,EAAE,cAAc,UAAU,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,YAAY,EAAE,oBAAoB,CAAA;KAAC,KAAI,OAAO,CAAC,IAAI,CAAC,CAIrI;IAC5B,eAA8B;IAC9B,oDAAoD;IACpD,aADW,KAAK,CAAC,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC,CAC3B;IACrB,mDAAmD;IACnD,YADW,KAAK,CAAC,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC,CAC3B;IACpB,wKAAwK;IACxK,SADW,OAAO,CACE;IAGtB,+BAA+B;IAC/B,SADc,OAAO,CAAC,IAAI,CAAC,CAmB1B;IAED,sBAAsB;IACtB,QADc,IAAI,CAcjB;IAED;;;;;OAKG;IACH,0CAJG;QAAsF,gBAAgB,EAA9F,OAAO,2BAA2B,EAAE,mCAAmC;QAC1D,MAAM,EAAnB,MAAM;KACd,GAAU,IAAI,CAsBhB;IAED;;;;;OAKG;IACH,+CAJG;QAAsF,gBAAgB,EAA9F,OAAO,2BAA2B,EAAE,mCAAmC;QAC1D,MAAM,EAAnB,MAAM;KACd,GAAU,IAAI,CAuBhB;IAED;;;;;;;;;;OAUG;IACH,8CAJG;QAAsF,gBAAgB,EAA9F,OAAO,2BAA2B,EAAE,mCAAmC;QAC1D,MAAM,EAAnB,MAAM;KACd,GAAU,IAAI,CA+BhB;IAED;;;;;OAKG;IACH,kDAJG;QAAsF,gBAAgB,EAA9F,OAAO,2BAA2B,EAAE,mCAAmC;QAC1D,MAAM,EAAnB,MAAM;KACd,GAAU,OAAO,CAAC,IAAI,CAAC,CAazB;IAED;;;OAGG;IACH,sBAHW,WAAW,CAAC,OAAO,2BAA2B,EAAE,mCAAmC,CAAC,OAAO,CAAC,CAAC,GAC3F;QAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;KAAC,CAczE;CACF;2BApOa,MAAM,OAAO,oBAAoB;mBArB5B,cAAc"}
@@ -1,5 +1,6 @@
1
1
  // @ts-check
2
2
  import Logger from "../logger.js";
3
+ import { nextCronFireDate, parseCronExpression } from "./cron-expression.js";
3
4
  const DURATION_MULTIPLIERS = {
4
5
  d: 24 * 60 * 60 * 1000,
5
6
  day: 24 * 60 * 60 * 1000,
@@ -61,9 +62,12 @@ export default class BackgroundJobsScheduler {
61
62
  this.intervalIds = [];
62
63
  /** @type {Array<ReturnType<typeof setTimeout>>} */
63
64
  this.timeoutIds = [];
65
+ /** @type {boolean} - True between stop() and the next start(); cron self-rescheduler checks this so a stop() during an in-flight enqueue doesn't immediately re-arm. */
66
+ this.stopped = false;
64
67
  }
65
68
  /** @returns {Promise<void>} */
66
69
  async start() {
70
+ this.stopped = false;
67
71
  const scheduledBackgroundJobsConfig = await this.configuration.getScheduledBackgroundJobsConfig();
68
72
  if (!scheduledBackgroundJobsConfig?.jobs) {
69
73
  return;
@@ -78,6 +82,7 @@ export default class BackgroundJobsScheduler {
78
82
  }
79
83
  /** @returns {void} */
80
84
  stop() {
85
+ this.stopped = true;
81
86
  for (const intervalId of this.intervalIds) {
82
87
  clearInterval(intervalId);
83
88
  }
@@ -94,15 +99,35 @@ export default class BackgroundJobsScheduler {
94
99
  * @returns {void}
95
100
  */
96
101
  scheduleJob({ jobConfiguration, jobKey }) {
97
- const { everyValue, firstInValue } = this.normalizeEvery(jobConfiguration.every);
102
+ if (!jobConfiguration.class || typeof jobConfiguration.class.performLaterWithOptions !== "function") {
103
+ throw new Error(`Scheduled background job ${jobKey} must define a job class.`);
104
+ }
105
+ if (jobConfiguration.cron !== undefined && jobConfiguration.every !== undefined) {
106
+ throw new Error(`Scheduled background job ${jobKey} must define either "every" or "cron", not both.`);
107
+ }
108
+ if (jobConfiguration.cron !== undefined) {
109
+ this.scheduleCronJob({ jobConfiguration, jobKey });
110
+ return;
111
+ }
112
+ if (jobConfiguration.every === undefined) {
113
+ throw new Error(`Scheduled background job ${jobKey} must define either "every" or "cron".`);
114
+ }
115
+ this.scheduleEveryJob({ jobConfiguration, jobKey });
116
+ }
117
+ /**
118
+ * @param {object} args - Options.
119
+ * @param {import("../configuration-types.js").ScheduledBackgroundJobConfiguration} args.jobConfiguration - Job configuration.
120
+ * @param {string} args.jobKey - Job key.
121
+ * @returns {void}
122
+ */
123
+ scheduleEveryJob({ jobConfiguration, jobKey }) {
124
+ const everyConfig = /** @type {NonNullable<typeof jobConfiguration.every>} */ (jobConfiguration.every);
125
+ const { everyValue, firstInValue } = this.normalizeEvery(everyConfig);
98
126
  const intervalMs = parseScheduledDuration(everyValue, `${jobKey}.every`);
99
127
  const firstInMs = firstInValue !== undefined ? parseScheduledDuration(firstInValue, `${jobKey}.first_in`) : intervalMs;
100
128
  if (intervalMs < 1) {
101
129
  throw new Error(`Scheduled background job ${jobKey}.every must be at least 1 millisecond.`);
102
130
  }
103
- if (!jobConfiguration.class || typeof jobConfiguration.class.performLaterWithOptions !== "function") {
104
- throw new Error(`Scheduled background job ${jobKey} must define a job class.`);
105
- }
106
131
  const timeoutId = setTimeout(() => {
107
132
  void this.enqueueScheduledJob({ jobConfiguration, jobKey });
108
133
  const intervalId = setInterval(() => {
@@ -112,6 +137,42 @@ export default class BackgroundJobsScheduler {
112
137
  }, firstInMs);
113
138
  this.timeoutIds.push(timeoutId);
114
139
  }
140
+ /**
141
+ * Crontab schedules don't have a constant interval (`0 9 * * 1-5`
142
+ * fires once per weekday at 9 AM, with gaps of varying length), so
143
+ * we self-reschedule with `setTimeout` after every fire instead of
144
+ * using `setInterval`.
145
+ *
146
+ * @param {object} args - Options.
147
+ * @param {import("../configuration-types.js").ScheduledBackgroundJobConfiguration} args.jobConfiguration - Job configuration.
148
+ * @param {string} args.jobKey - Job key.
149
+ * @returns {void}
150
+ */
151
+ scheduleCronJob({ jobConfiguration, jobKey }) {
152
+ const cronExpression = jobConfiguration.cron;
153
+ if (typeof cronExpression !== "string") {
154
+ throw new Error(`Scheduled background job ${jobKey}.cron must be a string.`);
155
+ }
156
+ const parsed = parseCronExpression(cronExpression);
157
+ const scheduleNext = () => {
158
+ if (this.stopped)
159
+ return;
160
+ const nextDate = nextCronFireDate(parsed, new Date());
161
+ const delayMs = Math.max(1, nextDate.getTime() - Date.now());
162
+ const timeoutId = setTimeout(async () => {
163
+ if (this.stopped)
164
+ return;
165
+ await this.enqueueScheduledJob({ jobConfiguration, jobKey });
166
+ // The await above can yield to a stop() call. Re-check before
167
+ // re-arming so we don't keep firing after shutdown.
168
+ if (this.stopped)
169
+ return;
170
+ scheduleNext();
171
+ }, delayMs);
172
+ this.timeoutIds.push(timeoutId);
173
+ };
174
+ scheduleNext();
175
+ }
115
176
  /**
116
177
  * @param {object} args - Options.
117
178
  * @param {import("../configuration-types.js").ScheduledBackgroundJobConfiguration} args.jobConfiguration - Job configuration.
@@ -132,7 +193,7 @@ export default class BackgroundJobsScheduler {
132
193
  }
133
194
  }
134
195
  /**
135
- * @param {import("../configuration-types.js").ScheduledBackgroundJobConfiguration["every"]} every - Every config.
196
+ * @param {NonNullable<import("../configuration-types.js").ScheduledBackgroundJobConfiguration["every"]>} every - Every config (caller must guarantee not undefined).
136
197
  * @returns {{everyValue: number | string, firstInValue?: number | string}} - Normalized interval and first-run delay values.
137
198
  */
138
199
  normalizeEvery(every) {
@@ -146,4 +207,4 @@ export default class BackgroundJobsScheduler {
146
207
  return { everyValue: every };
147
208
  }
148
209
  }
149
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2NoZWR1bGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2JhY2tncm91bmQtam9icy9zY2hlZHVsZXIuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsWUFBWTtBQUVaLE9BQU8sTUFBTSxNQUFNLGNBQWMsQ0FBQTtBQUVqQyxNQUFNLG9CQUFvQixHQUFHO0lBQzNCLENBQUMsRUFBRSxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxJQUFJO0lBQ3RCLEdBQUcsRUFBRSxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxJQUFJO0lBQ3hCLElBQUksRUFBRSxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxJQUFJO0lBQ3pCLENBQUMsRUFBRSxFQUFFLEdBQUcsRUFBRSxHQUFHLElBQUk7SUFDakIsSUFBSSxFQUFFLEVBQUUsR0FBRyxFQUFFLEdBQUcsSUFBSTtJQUNwQixLQUFLLEVBQUUsRUFBRSxHQUFHLEVBQUUsR0FBRyxJQUFJO0lBQ3JCLENBQUMsRUFBRSxFQUFFLEdBQUcsSUFBSTtJQUNaLE1BQU0sRUFBRSxFQUFFLEdBQUcsSUFBSTtJQUNqQixPQUFPLEVBQUUsRUFBRSxHQUFHLElBQUk7SUFDbEIsRUFBRSxFQUFFLENBQUM7SUFDTCxDQUFDLEVBQUUsSUFBSTtJQUNQLE1BQU0sRUFBRSxJQUFJO0lBQ1osT0FBTyxFQUFFLElBQUk7SUFDYixDQUFDLEVBQUUsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLElBQUk7SUFDMUIsSUFBSSxFQUFFLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxJQUFJO0lBQzdCLEtBQUssRUFBRSxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsSUFBSTtDQUMvQixDQUFBO0FBQ0QsZ0VBQWdFO0FBRWhFOzs7O0dBSUc7QUFDSCxNQUFNLFVBQVUsc0JBQXNCLENBQUMsS0FBSyxFQUFFLFNB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LFVBQVUsRUFBRSxZQUFZLENBQUMsR0FBRyxLQUFLLENBQUE7WUFFeEMsSUFBSSxDQUFDLFlBQVksSUFBSSxPQUFPLFlBQVksS0FBSyxRQUFRLElBQUksS0FBSyxDQUFDLE9BQU8sQ0FBQyxZQUFZLENBQUMsRUFBRSxDQUFDO2dCQUNyRixPQUFPLEVBQUMsVUFBVSxFQUFDLENBQUE7WUFDckIsQ0FBQztZQUVELE9BQU8sRUFBQyxVQUFVLEVBQUUsWUFBWSxFQUFFLFlBQVksQ0FBQyxPQUFPLElBQUksWUFBWSxDQUFDLFFBQVEsRUFBQyxDQUFBO1FBQ2xGLENBQUM7UUFFRCxPQUFPLEVBQUMsVUFBVSxFQUFFLEtBQUssRUFBQyxDQUFBO0lBQzVCLENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbIi8vIEB0cy1jaGVja1xuXG5pbXBvcnQgTG9nZ2VyIGZyb20gXCIuLi9sb2dnZXIuanNcIlxuXG5jb25zdCBEVVJBVElPTl9NVUxUSVBMSUVSUyA9IHtcbiAgZDogMjQgKiA2MCAqIDYwICogMTAwMCxcbiAgZGF5OiAyNCAqIDYwICogNjAgKiAxMDAwLFxuICBkYXlzOiAyNCAqIDYwICogNjAgKiAxMDAwLFxuICBoOiA2MCAqIDYwICogMTAwMCxcbiAgaG91cjogNjAgKiA2MCAqIDEwMDAsXG4gIGhvdXJzOiA2MCAqIDYwICogMTAwMCxcbiAgbTogNjAgKiAxMDAwLFxuICBtaW51dGU6IDYwICogMTAwMCxcbiAgbWludXRlczogNjAgKiAxMDAwLFxuICBtczogMSxcbiAgczogMTAwMCxcbiAgc2Vjb25kOiAxMDAwLFxuICBzZWNvbmRzOiAxMDAwLFxuICB3OiA3ICogMjQgKiA2MCAqIDYwICogMTAwMCxcbiAgd2VlazogNyAqIDI0ICogNjAgKiA2MCAqIDEwMDAsXG4gIHdlZWtzOiA3ICogMjQgKiA2MCAqIDYwICogMTAwMFxufVxuLyoqIEB0eXBlZGVmIHtrZXlvZiB0eXBlb2YgRFVSQVRJT05fTVVMVElQTElFUlN9IER1cmF0aW9uVW5pdCAqL1xuXG4vKipcbiAqIEBwYXJhbSB7bnVtYmVyIHwgc3RyaW5nfSB2YWx1ZSAtIER1cmF0aW9uIHZhbHVlLlxuICogQHBhcmFtIHtzdHJpbmd9IGZpZWxkTmFtZSAtIEZpZWxkIG5hbWUgZm9yIGVycm9ycy5cbiAqIEByZXR1cm5zIHtudW1iZXJ9IC0gRHVyYXRpb24gaW4gbWlsbGlzZWNvbmRzLlxuICovXG5leHBvcnQgZnVuY3Rpb24gcGFyc2VTY2hlZHVsZWREdXJhdGlvbih2YWx1ZSwgZmllbGROYW1lKSB7XG4gIGlmICh0eXBlb2YgdmFsdWUgPT09IFwibnVtYmVyXCIpIHtcbiAgICBpZiAoIU51bWJlci5pc0Zpbml0ZSh2YWx1ZSkgfHwgdmFsdWUgPCAxKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYFNjaGVkdWxlZCBiYWNrZ3JvdW5kIGpvYiAke2ZpZWxkTmFtZX0gbXVzdCBiZSBhIHBvc2l0aXZlIG51bWJlciBvZiBtaWxsaXNlY29uZHMuYClcbiAgICB9XG5cbiAgICByZXR1cm4gdmFsdWVcbiAgfVxuXG4gIGlmICh0eXBlb2YgdmFsdWUgIT09IFwic3RyaW5nXCIgfHwgIXZhbHVlLnRyaW0oKSkge1xuICAgIHRocm93IG5ldyBFcnJvcihgU2NoZWR1bGVkIGJhY2tncm91bmQgam9iICR7ZmllbGROYW1lfSBtdXN0IGJlIGEgbm9uLWVtcHR5IHN0cmluZyBvciBudW1iZXIuYClcbiAgfVxuXG4gIGNvbnN0IG5vcm1hbGl6ZWRWYWx1ZSA9IHZhbHVlLnRyaW0oKS50b0xvd2VyQ2FzZSgpXG4gIGNvbnN0IG1hdGNoID0gbm9ybWFsaXplZFZhbHVlLm1hdGNoKC9eKFxcZCsoPzpcXC5cXGQrKT8pXFxzKihtc3xzfG18aHxkfHd8c2Vjb25kfHNlY29uZHN8bWludXRlfG1pbnV0ZXN8aG91cnxob3Vyc3xkYXl8ZGF5c3x3ZWVrfHdlZWtzKSQvKVxuXG4gIGlmICghbWF0Y2gpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYEludmFsaWQgc2NoZWR1bGVkIGJhY2tncm91bmQgam9iICR7ZmllbGROYW1lfTogJHt2YWx1ZX1gKVxuICB9XG5cbiAgY29uc3QgbnVtZXJpY1ZhbHVlID0gTnVtYmVyKG1hdGNoWzFdKVxuICBjb25zdCBtdWx0aXBsaWVyID0gRFVSQVRJT05fTVVMVElQTElFUlNbLyoqIEB0eXBlIHtEdXJhdGlvblVuaXR9ICovIChtYXRjaFsyXSldXG5cbiAgaWYgKCFtdWx0aXBsaWVyKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkIHNjaGVkdWxlZCBiYWNrZ3JvdW5kIGpvYiAke2ZpZWxkTmFtZX06ICR7dmFsdWV9YClcbiAgfVxuXG4gIHJldHVybiBNYXRoLnJvdW5kKG51bWVyaWNWYWx1ZSAqIG11bHRpcGxpZXIpXG59XG5cbi8qKiBSdW5zIGNvbmZpZ3VyZWQgcmVjdXJyaW5nIGJhY2tncm91bmQgam9iIHNjaGVkdWxlcy4gKi9cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIEJhY2tncm91bmRKb2JzU2NoZWR1bGVyIHtcbiAgLyoqXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gT3B0aW9ucy5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuLi9jb25maWd1cmF0aW9uLmpzXCIpLmRlZmF1bHR9IGFyZ3MuY29uZmlndXJhdGlvbiAtIENvbmZpZ3VyYXRpb24uXG4gICAqIEBwYXJhbSB7ZnVuY3Rpb24oe2FyZ3M6IGFueVtdLCBqb2JDbGFzczogdHlwZW9mIGltcG9ydChcIi4vam9iLmpzXCIpLmRlZmF1bHQsIGpvYktleTogc3RyaW5nLCBvcHRpb25zOiBpbXBvcnQoXCIuL3R5cGVzLmpzXCIpLkJhY2tncm91bmRKb2JPcHRpb25zfSkgOiBQcm9taXNlPHZvaWQ+fSBhcmdzLmVucXVldWVKb2IgLSBFbnF1ZXVlIGNhbGxiYWNrLlxuICAgKi9cbiAgY29uc3RydWN0b3Ioe2NvbmZpZ3VyYXRpb24sIGVucXVldWVKb2J9KSB7XG4gICAgdGhpcy5jb25maWd1cmF0aW9uID0gY29uZmlndXJhdGlvblxuICAgIHRoaXMuZW5xdWV1ZUpvYiA9IGVucXVldWVKb2JcbiAgICB0aGlzLmxvZ2dlciA9IG5ldyBMb2dnZXIodGhpcylcbiAgICAvKiogQHR5cGUge0FycmF5PFJldHVyblR5cGU8dHlwZW9mIHNldEludGVydmFsPj59ICovXG4gICAgdGhpcy5pbnRlcnZhbElkcyA9IFtdXG4gICAgLyoqIEB0eXBlIHtBcnJheTxSZXR1cm5UeXBlPHR5cGVvZiBzZXRUaW1lb3V0Pj59ICovXG4gICAgdGhpcy50aW1lb3V0SWRzID0gW11cbiAgfVxuXG4gIC8qKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gKi9cbiAgYXN5bmMgc3RhcnQoKSB7XG4gICAgY29uc3Qgc2NoZWR1bGVkQmFja2dyb3VuZEpvYnNDb25maWcgPSBhd2FpdCB0aGlzLmNvbmZpZ3VyYXRpb24uZ2V0U2NoZWR1bGVkQmFja2dyb3VuZEpvYnNDb25maWcoKVxuXG4gICAgaWYgKCFzY2hlZHVsZWRCYWNrZ3JvdW5kSm9ic0NvbmZpZz8uam9icykge1xuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgZm9yIChjb25zdCBqb2JLZXkgb2YgT2JqZWN0LmtleXMoc2NoZWR1bGVkQmFja2dyb3VuZEpvYnNDb25maWcuam9icykpIHtcbiAgICAgIGNvbnN0IGpvYkNvbmZpZ3VyYXRpb24gPSBzY2hlZHVsZWRCYWNrZ3JvdW5kSm9ic0NvbmZpZy5qb2JzW2pvYktleV1cblxuICAgICAgaWYgKCFqb2JDb25maWd1cmF0aW9uIHx8IGpvYkNvbmZpZ3VyYXRpb24uZW5hYmxlZCA9PT0gZmFsc2UpIHtcbiAgICAgICAgY29udGludWVcbiAgICAgIH1cblxuICAgICAgdGhpcy5zY2hlZHVsZUpvYih7am9iQ29uZmlndXJhdGlvbiwgam9iS2V5fSlcbiAgICB9XG4gIH1cblxuICAvKiogQHJldHVybnMge3ZvaWR9ICovXG4gIHN0b3AoKSB7XG4gICAgZm9yIChjb25zdCBpbnRlcnZhbElkIG9mIHRoaXMuaW50ZXJ2YWxJZHMpIHtcbiAgICAgIGNsZWFySW50ZXJ2YWwoaW50ZXJ2YWxJZClcbiAgICB9XG5cbiAgICBmb3IgKGNvbnN0IHRpbWVvdXRJZCBvZiB0aGlzLnRpbWVvdXRJZHMpIHtcbiAgICAgIGNsZWFyVGltZW91dCh0aW1lb3V0SWQpXG4gICAgfVxuXG4gICAgdGhpcy5pbnRlcnZhbElkcyA9IFtdXG4gICAgdGhpcy50aW1lb3V0SWRzID0gW11cbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge29iamVjdH0gYXJncyAtIE9wdGlvbnMuXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi4vY29uZmlndXJhdGlvbi10eXBlcy5qc1wiKS5TY2hlZHVsZWRCYWNrZ3JvdW5kSm9iQ29uZmlndXJhdGlvbn0gYXJncy5qb2JDb25maWd1cmF0aW9uIC0gSm9iIGNvbmZpZ3VyYXRpb24uXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhcmdzLmpvYktleSAtIEpvYiBrZXkuXG4gICAqIEByZXR1cm5zIHt2b2lkfVxuICAgKi9cbiAgc2NoZWR1bGVKb2Ioe2pvYkNvbmZpZ3VyYXRpb24sIGpvYktleX0pIHtcbiAgICBjb25zdCB7ZXZlcnlWYWx1ZSwgZmlyc3RJblZhbHVlfSA9IHRoaXMubm9ybWFsaXplRXZlcnkoam9iQ29uZmlndXJhdGlvbi5ldmVyeSlcbiAgICBjb25zdCBpbnRlcnZhbE1zID0gcGFyc2VTY2hlZHVsZWREdXJhdGlvbihldmVyeVZhbHVlLCBgJHtqb2JLZXl9LmV2ZXJ5YClcbiAgICBjb25zdCBmaXJzdEluTXMgPSBmaXJzdEluVmFsdWUgIT09IHVuZGVmaW5lZCA/IHBhcnNlU2NoZWR1bGVkRHVyYXRpb24oZmlyc3RJblZhbHVlLCBgJHtqb2JLZXl9LmZpcnN0X2luYCkgOiBpbnRlcnZhbE1zXG5cbiAgICBpZiAoaW50ZXJ2YWxNcyA8IDEpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgU2NoZWR1bGVkIGJhY2tncm91bmQgam9iICR7am9iS2V5fS5ldmVyeSBtdXN0IGJlIGF0IGxlYXN0IDEgbWlsbGlzZWNvbmQuYClcbiAgICB9XG5cbiAgICBpZiAoIWpvYkNvbmZpZ3VyYXRpb24uY2xhc3MgfHwgdHlwZW9mIGpvYkNvbmZpZ3VyYXRpb24uY2xhc3MucGVyZm9ybUxhdGVyV2l0aE9wdGlvbnMgIT09IFwiZnVuY3Rpb25cIikge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBTY2hlZHVsZWQgYmFja2dyb3VuZCBqb2IgJHtqb2JLZXl9IG11c3QgZGVmaW5lIGEgam9iIGNsYXNzLmApXG4gICAgfVxuXG4gICAgY29uc3QgdGltZW91dElkID0gc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICB2b2lkIHRoaXMuZW5xdWV1ZVNjaGVkdWxlZEpvYih7am9iQ29uZmlndXJhdGlvbiwgam9iS2V5fSlcblxuICAgICAgY29uc3QgaW50ZXJ2YWxJZCA9IHNldEludGVydmFsKCgpID0+IHtcbiAgICAgICAgdm9pZCB0aGlzLmVucXVldWVTY2hlZHVsZWRKb2Ioe2pvYkNvbmZpZ3VyYXRpb24sIGpvYktleX0pXG4gICAgICB9LCBpbnRlcnZhbE1zKVxuXG4gICAgICB0aGlzLmludGVydmFsSWRzLnB1c2goaW50ZXJ2YWxJZClcbiAgICB9LCBmaXJzdEluTXMpXG5cbiAgICB0aGlzLnRpbWVvdXRJZHMucHVzaCh0aW1lb3V0SWQpXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtvYmplY3R9IGFyZ3MgLSBPcHRpb25zLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4uL2NvbmZpZ3VyYXRpb24tdHlwZXMuanNcIikuU2NoZWR1bGVkQmFja2dyb3VuZEpvYkNvbmZpZ3VyYXRpb259IGFyZ3Muam9iQ29uZmlndXJhdGlvbiAtIEpvYiBjb25maWd1cmF0aW9uLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gYXJncy5qb2JLZXkgLSBKb2Iga2V5LlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn1cbiAgICovXG4gIGFzeW5jIGVucXVldWVTY2hlZHVsZWRKb2Ioe2pvYkNvbmZpZ3VyYXRpb24sIGpvYktleX0pIHtcbiAgICB0cnkge1xuICAgICAgYXdhaXQgdGhpcy5lbnF1ZXVlSm9iKHtcbiAgICAgICAgYXJnczogQXJyYXkuaXNBcnJheShqb2JDb25maWd1cmF0aW9uLmFyZ3MpID8gam9iQ29uZmlndXJhdGlvbi5hcmdzIDogW10sXG4gICAgICAgIGpvYkNsYXNzOiBqb2JDb25maWd1cmF0aW9uLmNsYXNzLFxuICAgICAgICBqb2JLZXksXG4gICAgICAgIG9wdGlvbnM6IGpvYkNvbmZpZ3VyYXRpb24ub3B0aW9ucyB8fCB7fVxuICAgICAgfSlcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgYXdhaXQgdGhpcy5sb2dnZXIuZXJyb3IoKCkgPT4gW1wiRmFpbGVkIHRvIGVucXVldWUgc2NoZWR1bGVkIGJhY2tncm91bmQgam9iXCIsIHtqb2JLZXksIGpvYk5hbWU6IGpvYkNvbmZpZ3VyYXRpb24uY2xhc3Muam9iTmFtZSgpfSwgZXJyb3JdKVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4uL2NvbmZpZ3VyYXRpb24tdHlwZXMuanNcIikuU2NoZWR1bGVkQmFja2dyb3VuZEpvYkNvbmZpZ3VyYXRpb25bXCJldmVyeVwiXX0gZXZlcnkgLSBFdmVyeSBjb25maWcuXG4gICAqIEByZXR1cm5zIHt7ZXZlcnlWYWx1ZTogbnVtYmVyIHwgc3RyaW5nLCBmaXJzdEluVmFsdWU/OiBudW1iZXIgfCBzdHJpbmd9fSAtIE5vcm1hbGl6ZWQgaW50ZXJ2YWwgYW5kIGZpcnN0LXJ1biBkZWxheSB2YWx1ZXMuXG4gICAqL1xuICBub3JtYWxpemVFdmVyeShldmVyeSkge1xuICAgIGlmIChBcnJheS5pc0FycmF5KGV2ZXJ5KSkge1xuICAgICAgY29uc3QgW2V2ZXJ5VmFsdWUsIGV2ZXJ5T3B0aW9uc10gPSBldmVyeVxuXG4gICAgICBpZiAoIWV2ZXJ5T3B0aW9ucyB8fCB0eXBlb2YgZXZlcnlPcHRpb25zICE9PSBcIm9iamVjdFwiIHx8IEFycmF5LmlzQXJyYXkoZXZlcnlPcHRpb25zKSkge1xuICAgICAgICByZXR1cm4ge2V2ZXJ5VmFsdWV9XG4gICAgICB9XG5cbiAgICAgIHJldHVybiB7ZXZlcnlWYWx1ZSwgZmlyc3RJblZhbHVlOiBldmVyeU9wdGlvbnMuZmlyc3RJbiA/PyBldmVyeU9wdGlvbnMuZmlyc3RfaW59XG4gICAgfVxuXG4gICAgcmV0dXJuIHtldmVyeVZhbHVlOiBldmVyeX1cbiAgfVxufVxuIl19
210
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2NoZWR1bGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2JhY2tncm91bmQtam9icy9zY2hlZHVsZXIuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsWUFBWTtBQUVaLE9BQU8sTUFBTSxNQUFNLGNBQWMsQ0FBQTtBQUNqQyxPQUFPLEVBQUMsZ0JBQWdCLEVBQUUsbUJBQW1CLEVBQUMsTUFBTSxzQkFBc0IsQ0FBQTtBQUUxRSxNQUFNLG9CQUFvQixHQUFHO0lBQzNCLENBQUMsRUFBRSxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxJQUFJO0lBQ3RCLEdBQUcsRUFBRSxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxJQUFJO0lBQ3hCLElBQUksRUFBRSxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxJQUFJO0lBQ3pCLENBQUMsRUFBRSxFQUFFLEdBQUcsRUFBRSxHQUFHLElBQUk7SUFDakIsSUFBSSxFQUFFLEVBQUUsR0FBRyxFQUFFLEdBQUcsSUFBSTtJQUNwQixLQUFLLEVBQUUsRUFBRSxHQUFHLEVBQUUsR0FBRyxJQUFJO0lBQ3JCLENBQUMsRUFBRSxFQUFFLEdBQUcsSUFBSTtJQUNaLE1BQU0sRUFBRSxFQUFFLEdBQUcsSUFBSTtJQUNqQixPQUFPLEVBQUUsRUFBRSxHQUFHLElBQUk7SUFDbEIsRUFBRSxFQUFFLENBQUM7SUFDTCxDQUFDLEVBQUUsSUFBSTtJQUNQLE1BQU0sRUFBRSxJQUFJO0lBQ1osT0FBTyxFQUFFLElBQUk7SUFDYixDQUFDLEVBQUUsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLElBQUk7SUFDMUIsSUFBSSxFQUFFLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxJQUFJO0lBQzdCLEtBQUssRUFBRSxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsSUFBSTtDQUMvQixDQUFBO0FBQ0QsZ0VB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T0FBTyxFQUFDLFVBQVUsRUFBRSxZQUFZLEVBQUUsWUFBWSxDQUFDLE9BQU8sSUFBSSxZQUFZLENBQUMsUUFBUSxFQUFDLENBQUE7UUFDbEYsQ0FBQztRQUVELE9BQU8sRUFBQyxVQUFVLEVBQUUsS0FBSyxFQUFDLENBQUE7SUFDNUIsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQHRzLWNoZWNrXG5cbmltcG9ydCBMb2dnZXIgZnJvbSBcIi4uL2xvZ2dlci5qc1wiXG5pbXBvcnQge25leHRDcm9uRmlyZURhdGUsIHBhcnNlQ3JvbkV4cHJlc3Npb259IGZyb20gXCIuL2Nyb24tZXhwcmVzc2lvbi5qc1wiXG5cbmNvbnN0IERVUkFUSU9OX01VTFRJUExJRVJTID0ge1xuICBkOiAyNCAqIDYwICogNjAgKiAxMDAwLFxuICBkYXk6IDI0ICogNjAgKiA2MCAqIDEwMDAsXG4gIGRheXM6IDI0ICogNjAgKiA2MCAqIDEwMDAsXG4gIGg6IDYwICogNjAgKiAxMDAwLFxuICBob3VyOiA2MCAqIDYwICogMTAwMCxcbiAgaG91cnM6IDYwICogNjAgKiAxMDAwLFxuICBtOiA2MCAqIDEwMDAsXG4gIG1pbnV0ZTogNjAgKiAxMDAwLFxuICBtaW51dGVzOiA2MCAqIDEwMDAsXG4gIG1zOiAxLFxuICBzOiAxMDAwLFxuICBzZWNvbmQ6IDEwMDAsXG4gIHNlY29uZHM6IDEwMDAsXG4gIHc6IDcgKiAyNCAqIDYwICogNjAgKiAxMDAwLFxuICB3ZWVrOiA3ICogMjQgKiA2MCAqIDYwICogMTAwMCxcbiAgd2Vla3M6IDcgKiAyNCAqIDYwICogNjAgKiAxMDAwXG59XG4vKiogQHR5cGVkZWYge2tleW9mIHR5cGVvZiBEVVJBVElPTl9NVUxUSVBMSUVSU30gRHVyYXRpb25Vbml0ICovXG5cbi8qKlxuICogQHBhcmFtIHtudW1iZXIgfCBzdHJpbmd9IHZhbHVlIC0gRHVyYXRpb24gdmFsdWUuXG4gKiBAcGFyYW0ge3N0cmluZ30gZmllbGROYW1lIC0gRmllbGQgbmFtZSBmb3IgZXJyb3JzLlxuICogQHJldHVybnMge251bWJlcn0gLSBEdXJhdGlvbiBpbiBtaWxsaXNlY29uZHMuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBwYXJzZVNjaGVkdWxlZER1cmF0aW9uKHZhbHVlLCBmaWVsZE5hbWUpIHtcbiAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gXCJudW1iZXJcIikge1xuICAgIGlmICghTnVtYmVyLmlzRmluaXRlKHZhbHVlKSB8fCB2YWx1ZSA8IDEpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgU2NoZWR1bGVkIGJhY2tncm91bmQgam9iICR7ZmllbGROYW1lfSBtdXN0IGJlIGEgcG9zaXRpdmUgbnVtYmVyIG9mIG1pbGxpc2Vjb25kcy5gKVxuICAgIH1cblxuICAgIHJldHVybiB2YWx1ZVxuICB9XG5cbiAgaWYgKHR5cGVvZiB2YWx1ZSAhPT0gXCJzdHJpbmdcIiB8fCAhdmFsdWUudHJpbSgpKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBTY2hlZHVsZWQgYmFja2dyb3VuZCBqb2IgJHtmaWVsZE5hbWV9IG11c3QgYmUgYSBub24tZW1wdHkgc3RyaW5nIG9yIG51bWJlci5gKVxuICB9XG5cbiAgY29uc3Qgbm9ybWFsaXplZFZhbHVlID0gdmFsdWUudHJpbSgpLnRvTG93ZXJDYXNlKClcbiAgY29uc3QgbWF0Y2ggPSBub3JtYWxpemVkVmFsdWUubWF0Y2goL14oXFxkKyg/OlxcLlxcZCspPylcXHMqKG1zfHN8bXxofGR8d3xzZWNvbmR8c2Vjb25kc3xtaW51dGV8bWludXRlc3xob3VyfGhvdXJzfGRheXxkYXlzfHdlZWt8d2Vla3MpJC8pXG5cbiAgaWYgKCFtYXRjaCkge1xuICAgIHRocm93IG5ldyBFcnJvcihgSW52YWxpZCBzY2hlZHVsZWQgYmFja2dyb3VuZCBqb2IgJHtmaWVsZE5hbWV9OiAke3ZhbHVlfWApXG4gIH1cblxuICBjb25zdCBudW1lcmljVmFsdWUgPSBOdW1iZXIobWF0Y2hbMV0pXG4gIGNvbnN0IG11bHRpcGxpZXIgPSBEVVJBVElPTl9NVUxUSVBMSUVSU1svKiogQHR5cGUge0R1cmF0aW9uVW5pdH0gKi8gKG1hdGNoWzJdKV1cblxuICBpZiAoIW11bHRpcGxpZXIpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYEludmFsaWQgc2NoZWR1bGVkIGJhY2tncm91bmQgam9iICR7ZmllbGROYW1lfTogJHt2YWx1ZX1gKVxuICB9XG5cbiAgcmV0dXJuIE1hdGgucm91bmQobnVtZXJpY1ZhbHVlICogbXVsdGlwbGllcilcbn1cblxuLyoqIFJ1bnMgY29uZmlndXJlZCByZWN1cnJpbmcgYmFja2dyb3VuZCBqb2Igc2NoZWR1bGVzLiAqL1xuZXhwb3J0IGRlZmF1bHQgY2xhc3MgQmFja2dyb3VuZEpvYnNTY2hlZHVsZXIge1xuICAvKipcbiAgICogQHBhcmFtIHtvYmplY3R9IGFyZ3MgLSBPcHRpb25zLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4uL2NvbmZpZ3VyYXRpb24uanNcIikuZGVmYXVsdH0gYXJncy5jb25maWd1cmF0aW9uIC0gQ29uZmlndXJhdGlvbi5cbiAgICogQHBhcmFtIHtmdW5jdGlvbih7YXJnczogYW55W10sIGpvYkNsYXNzOiB0eXBlb2YgaW1wb3J0KFwiLi9qb2IuanNcIikuZGVmYXVsdCwgam9iS2V5OiBzdHJpbmcsIG9wdGlvbnM6IGltcG9ydChcIi4vdHlwZXMuanNcIikuQmFja2dyb3VuZEpvYk9wdGlvbnN9KSA6IFByb21pc2U8dm9pZD59IGFyZ3MuZW5xdWV1ZUpvYiAtIEVucXVldWUgY2FsbGJhY2suXG4gICAqL1xuICBjb25zdHJ1Y3Rvcih7Y29uZmlndXJhdGlvbiwgZW5xdWV1ZUpvYn0pIHtcbiAgICB0aGlzLmNvbmZpZ3VyYXRpb24gPSBjb25maWd1cmF0aW9uXG4gICAgdGhpcy5lbnF1ZXVlSm9iID0gZW5xdWV1ZUpvYlxuICAgIHRoaXMubG9nZ2VyID0gbmV3IExvZ2dlcih0aGlzKVxuICAgIC8qKiBAdHlwZSB7QXJyYXk8UmV0dXJuVHlwZTx0eXBlb2Ygc2V0SW50ZXJ2YWw+Pn0gKi9cbiAgICB0aGlzLmludGVydmFsSWRzID0gW11cbiAgICAvKiogQHR5cGUge0FycmF5PFJldHVyblR5cGU8dHlwZW9mIHNldFRpbWVvdXQ+Pn0gKi9cbiAgICB0aGlzLnRpbWVvdXRJZHMgPSBbXVxuICAgIC8qKiBAdHlwZSB7Ym9vbGVhbn0gLSBUcnVlIGJldHdlZW4gc3RvcCgpIGFuZCB0aGUgbmV4dCBzdGFydCgpOyBjcm9uIHNlbGYtcmVzY2hlZHVsZXIgY2hlY2tzIHRoaXMgc28gYSBzdG9wKCkgZHVyaW5nIGFuIGluLWZsaWdodCBlbnF1ZXVlIGRvZXNuJ3QgaW1tZWRpYXRlbHkgcmUtYXJtLiAqL1xuICAgIHRoaXMuc3RvcHBlZCA9IGZhbHNlXG4gIH1cblxuICAvKiogQHJldHVybnMge1Byb21pc2U8dm9pZD59ICovXG4gIGFzeW5jIHN0YXJ0KCkge1xuICAgIHRoaXMuc3RvcHBlZCA9IGZhbHNlXG5cbiAgICBjb25zdCBzY2hlZHVsZWRCYWNrZ3JvdW5kSm9ic0NvbmZpZyA9IGF3YWl0IHRoaXMuY29uZmlndXJhdGlvbi5nZXRTY2hlZHVsZWRCYWNrZ3JvdW5kSm9ic0NvbmZpZygpXG5cbiAgICBpZiAoIXNjaGVkdWxlZEJhY2tncm91bmRKb2JzQ29uZmlnPy5qb2JzKSB7XG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICBmb3IgKGNvbnN0IGpvYktleSBvZiBPYmplY3Qua2V5cyhzY2hlZHVsZWRCYWNrZ3JvdW5kSm9ic0NvbmZpZy5qb2JzKSkge1xuICAgICAgY29uc3Qgam9iQ29uZmlndXJhdGlvbiA9IHNjaGVkdWxlZEJhY2tncm91bmRKb2JzQ29uZmlnLmpvYnNbam9iS2V5XVxuXG4gICAgICBpZiAoIWpvYkNvbmZpZ3VyYXRpb24gfHwgam9iQ29uZmlndXJhdGlvbi5lbmFibGVkID09PSBmYWxzZSkge1xuICAgICAgICBjb250aW51ZVxuICAgICAgfVxuXG4gICAgICB0aGlzLnNjaGVkdWxlSm9iKHtqb2JDb25maWd1cmF0aW9uLCBqb2JLZXl9KVxuICAgIH1cbiAgfVxuXG4gIC8qKiBAcmV0dXJucyB7dm9pZH0gKi9cbiAgc3RvcCgpIHtcbiAgICB0aGlzLnN0b3BwZWQgPSB0cnVlXG5cbiAgICBmb3IgKGNvbnN0IGludGVydmFsSWQgb2YgdGhpcy5pbnRlcnZhbElkcykge1xuICAgICAgY2xlYXJJbnRlcnZhbChpbnRlcnZhbElkKVxuICAgIH1cblxuICAgIGZvciAoY29uc3QgdGltZW91dElkIG9mIHRoaXMudGltZW91dElkcykge1xuICAgICAgY2xlYXJUaW1lb3V0KHRpbWVvdXRJZClcbiAgICB9XG5cbiAgICB0aGlzLmludGVydmFsSWRzID0gW11cbiAgICB0aGlzLnRpbWVvdXRJZHMgPSBbXVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gT3B0aW9ucy5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuLi9jb25maWd1cmF0aW9uLXR5cGVzLmpzXCIpLlNjaGVkdWxlZEJhY2tncm91bmRKb2JDb25maWd1cmF0aW9ufSBhcmdzLmpvYkNvbmZpZ3VyYXRpb24gLSBKb2IgY29uZmlndXJhdGlvbi5cbiAgICogQHBhcmFtIHtzdHJpbmd9IGFyZ3Muam9iS2V5IC0gSm9iIGtleS5cbiAgICogQHJldHVybnMge3ZvaWR9XG4gICAqL1xuICBzY2hlZHVsZUpvYih7am9iQ29uZmlndXJhdGlvbiwgam9iS2V5fSkge1xuICAgIGlmICgham9iQ29uZmlndXJhdGlvbi5jbGFzcyB8fCB0eXBlb2Ygam9iQ29uZmlndXJhdGlvbi5jbGFzcy5wZXJmb3JtTGF0ZXJXaXRoT3B0aW9ucyAhPT0gXCJmdW5jdGlvblwiKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYFNjaGVkdWxlZCBiYWNrZ3JvdW5kIGpvYiAke2pvYktleX0gbXVzdCBkZWZpbmUgYSBqb2IgY2xhc3MuYClcbiAgICB9XG5cbiAgICBpZiAoam9iQ29uZmlndXJhdGlvbi5jcm9uICE9PSB1bmRlZmluZWQgJiYgam9iQ29uZmlndXJhdGlvbi5ldmVyeSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYFNjaGVkdWxlZCBiYWNrZ3JvdW5kIGpvYiAke2pvYktleX0gbXVzdCBkZWZpbmUgZWl0aGVyIFwiZXZlcnlcIiBvciBcImNyb25cIiwgbm90IGJvdGguYClcbiAgICB9XG5cbiAgICBpZiAoam9iQ29uZmlndXJhdGlvbi5jcm9uICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIHRoaXMuc2NoZWR1bGVDcm9uSm9iKHtqb2JDb25maWd1cmF0aW9uLCBqb2JLZXl9KVxuXG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICBpZiAoam9iQ29uZmlndXJhdGlvbi5ldmVyeSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYFNjaGVkdWxlZCBiYWNrZ3JvdW5kIGpvYiAke2pvYktleX0gbXVzdCBkZWZpbmUgZWl0aGVyIFwiZXZlcnlcIiBvciBcImNyb25cIi5gKVxuICAgIH1cblxuICAgIHRoaXMuc2NoZWR1bGVFdmVyeUpvYih7am9iQ29uZmlndXJhdGlvbiwgam9iS2V5fSlcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge29iamVjdH0gYXJncyAtIE9wdGlvbnMuXG4gICAqIEBwYXJhbSB7aW1wb3J0KFwiLi4vY29uZmlndXJhdGlvbi10eXBlcy5qc1wiKS5TY2hlZHVsZWRCYWNrZ3JvdW5kSm9iQ29uZmlndXJhdGlvbn0gYXJncy5qb2JDb25maWd1cmF0aW9uIC0gSm9iIGNvbmZpZ3VyYXRpb24uXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhcmdzLmpvYktleSAtIEpvYiBrZXkuXG4gICAqIEByZXR1cm5zIHt2b2lkfVxuICAgKi9cbiAgc2NoZWR1bGVFdmVyeUpvYih7am9iQ29uZmlndXJhdGlvbiwgam9iS2V5fSkge1xuICAgIGNvbnN0IGV2ZXJ5Q29uZmlnID0gLyoqIEB0eXBlIHtOb25OdWxsYWJsZTx0eXBlb2Ygam9iQ29uZmlndXJhdGlvbi5ldmVyeT59ICovIChqb2JDb25maWd1cmF0aW9uLmV2ZXJ5KVxuICAgIGNvbnN0IHtldmVyeVZhbHVlLCBmaXJzdEluVmFsdWV9ID0gdGhpcy5ub3JtYWxpemVFdmVyeShldmVyeUNvbmZpZylcbiAgICBjb25zdCBpbnRlcnZhbE1zID0gcGFyc2VTY2hlZHVsZWREdXJhdGlvbihldmVyeVZhbHVlLCBgJHtqb2JLZXl9LmV2ZXJ5YClcbiAgICBjb25zdCBmaXJzdEluTXMgPSBmaXJzdEluVmFsdWUgIT09IHVuZGVmaW5lZCA/IHBhcnNlU2NoZWR1bGVkRHVyYXRpb24oZmlyc3RJblZhbHVlLCBgJHtqb2JLZXl9LmZpcnN0X2luYCkgOiBpbnRlcnZhbE1zXG5cbiAgICBpZiAoaW50ZXJ2YWxNcyA8IDEpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgU2NoZWR1bGVkIGJhY2tncm91bmQgam9iICR7am9iS2V5fS5ldmVyeSBtdXN0IGJlIGF0IGxlYXN0IDEgbWlsbGlzZWNvbmQuYClcbiAgICB9XG5cbiAgICBjb25zdCB0aW1lb3V0SWQgPSBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgIHZvaWQgdGhpcy5lbnF1ZXVlU2NoZWR1bGVkSm9iKHtqb2JDb25maWd1cmF0aW9uLCBqb2JLZXl9KVxuXG4gICAgICBjb25zdCBpbnRlcnZhbElkID0gc2V0SW50ZXJ2YWwoKCkgPT4ge1xuICAgICAgICB2b2lkIHRoaXMuZW5xdWV1ZVNjaGVkdWxlZEpvYih7am9iQ29uZmlndXJhdGlvbiwgam9iS2V5fSlcbiAgICAgIH0sIGludGVydmFsTXMpXG5cbiAgICAgIHRoaXMuaW50ZXJ2YWxJZHMucHVzaChpbnRlcnZhbElkKVxuICAgIH0sIGZpcnN0SW5NcylcblxuICAgIHRoaXMudGltZW91dElkcy5wdXNoKHRpbWVvdXRJZClcbiAgfVxuXG4gIC8qKlxuICAgKiBDcm9udGFiIHNjaGVkdWxlcyBkb24ndCBoYXZlIGEgY29uc3RhbnQgaW50ZXJ2YWwgKGAwIDkgKiAqIDEtNWBcbiAgICogZmlyZXMgb25jZSBwZXIgd2Vla2RheSBhdCA5IEFNLCB3aXRoIGdhcHMgb2YgdmFyeWluZyBsZW5ndGgpLCBzb1xuICAgKiB3ZSBzZWxmLXJlc2NoZWR1bGUgd2l0aCBgc2V0VGltZW91dGAgYWZ0ZXIgZXZlcnkgZmlyZSBpbnN0ZWFkIG9mXG4gICAqIHVzaW5nIGBzZXRJbnRlcnZhbGAuXG4gICAqXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gT3B0aW9ucy5cbiAgICogQHBhcmFtIHtpbXBvcnQoXCIuLi9jb25maWd1cmF0aW9uLXR5cGVzLmpzXCIpLlNjaGVkdWxlZEJhY2tncm91bmRKb2JDb25maWd1cmF0aW9ufSBhcmdzLmpvYkNvbmZpZ3VyYXRpb24gLSBKb2IgY29uZmlndXJhdGlvbi5cbiAgICogQHBhcmFtIHtzdHJpbmd9IGFyZ3Muam9iS2V5IC0gSm9iIGtleS5cbiAgICogQHJldHVybnMge3ZvaWR9XG4gICAqL1xuICBzY2hlZHVsZUNyb25Kb2Ioe2pvYkNvbmZpZ3VyYXRpb24sIGpvYktleX0pIHtcbiAgICBjb25zdCBjcm9uRXhwcmVzc2lvbiA9IGpvYkNvbmZpZ3VyYXRpb24uY3JvblxuXG4gICAgaWYgKHR5cGVvZiBjcm9uRXhwcmVzc2lvbiAhPT0gXCJzdHJpbmdcIikge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBTY2hlZHVsZWQgYmFja2dyb3VuZCBqb2IgJHtqb2JLZXl9LmNyb24gbXVzdCBiZSBhIHN0cmluZy5gKVxuICAgIH1cblxuICAgIGNvbnN0IHBhcnNlZCA9IHBhcnNlQ3JvbkV4cHJlc3Npb24oY3JvbkV4cHJlc3Npb24pXG4gICAgY29uc3Qgc2NoZWR1bGVOZXh0ID0gKCkgPT4ge1xuICAgICAgaWYgKHRoaXMuc3RvcHBlZCkgcmV0dXJuXG5cbiAgICAgIGNvbnN0IG5leHREYXRlID0gbmV4dENyb25GaXJlRGF0ZShwYXJzZWQsIG5ldyBEYXRlKCkpXG4gICAgICBjb25zdCBkZWxheU1zID0gTWF0aC5tYXgoMSwgbmV4dERhdGUuZ2V0VGltZSgpIC0gRGF0ZS5ub3coKSlcbiAgICAgIGNvbnN0IHRpbWVvdXRJZCA9IHNldFRpbWVvdXQoYXN5bmMgKCkgPT4ge1xuICAgICAgICBpZiAodGhpcy5zdG9wcGVkKSByZXR1cm5cblxuICAgICAgICBhd2FpdCB0aGlzLmVucXVldWVTY2hlZHVsZWRKb2Ioe2pvYkNvbmZpZ3VyYXRpb24sIGpvYktleX0pXG5cbiAgICAgICAgLy8gVGhlIGF3YWl0IGFib3ZlIGNhbiB5aWVsZCB0byBhIHN0b3AoKSBjYWxsLiBSZS1jaGVjayBiZWZvcmVcbiAgICAgICAgLy8gcmUtYXJtaW5nIHNvIHdlIGRvbid0IGtlZXAgZmlyaW5nIGFmdGVyIHNodXRkb3duLlxuICAgICAgICBpZiAodGhpcy5zdG9wcGVkKSByZXR1cm5cblxuICAgICAgICBzY2hlZHVsZU5leHQoKVxuICAgICAgfSwgZGVsYXlNcylcblxuICAgICAgdGhpcy50aW1lb3V0SWRzLnB1c2godGltZW91dElkKVxuICAgIH1cblxuICAgIHNjaGVkdWxlTmV4dCgpXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtvYmplY3R9IGFyZ3MgLSBPcHRpb25zLlxuICAgKiBAcGFyYW0ge2ltcG9ydChcIi4uL2NvbmZpZ3VyYXRpb24tdHlwZXMuanNcIikuU2NoZWR1bGVkQmFja2dyb3VuZEpvYkNvbmZpZ3VyYXRpb259IGFyZ3Muam9iQ29uZmlndXJhdGlvbiAtIEpvYiBjb25maWd1cmF0aW9uLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gYXJncy5qb2JLZXkgLSBKb2Iga2V5LlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn1cbiAgICovXG4gIGFzeW5jIGVucXVldWVTY2hlZHVsZWRKb2Ioe2pvYkNvbmZpZ3VyYXRpb24sIGpvYktleX0pIHtcbiAgICB0cnkge1xuICAgICAgYXdhaXQgdGhpcy5lbnF1ZXVlSm9iKHtcbiAgICAgICAgYXJnczogQXJyYXkuaXNBcnJheShqb2JDb25maWd1cmF0aW9uLmFyZ3MpID8gam9iQ29uZmlndXJhdGlvbi5hcmdzIDogW10sXG4gICAgICAgIGpvYkNsYXNzOiBqb2JDb25maWd1cmF0aW9uLmNsYXNzLFxuICAgICAgICBqb2JLZXksXG4gICAgICAgIG9wdGlvbnM6IGpvYkNvbmZpZ3VyYXRpb24ub3B0aW9ucyB8fCB7fVxuICAgICAgfSlcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgYXdhaXQgdGhpcy5sb2dnZXIuZXJyb3IoKCkgPT4gW1wiRmFpbGVkIHRvIGVucXVldWUgc2NoZWR1bGVkIGJhY2tncm91bmQgam9iXCIsIHtqb2JLZXksIGpvYk5hbWU6IGpvYkNvbmZpZ3VyYXRpb24uY2xhc3Muam9iTmFtZSgpfSwgZXJyb3JdKVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge05vbk51bGxhYmxlPGltcG9ydChcIi4uL2NvbmZpZ3VyYXRpb24tdHlwZXMuanNcIikuU2NoZWR1bGVkQmFja2dyb3VuZEpvYkNvbmZpZ3VyYXRpb25bXCJldmVyeVwiXT59IGV2ZXJ5IC0gRXZlcnkgY29uZmlnIChjYWxsZXIgbXVzdCBndWFyYW50ZWUgbm90IHVuZGVmaW5lZCkuXG4gICAqIEByZXR1cm5zIHt7ZXZlcnlWYWx1ZTogbnVtYmVyIHwgc3RyaW5nLCBmaXJzdEluVmFsdWU/OiBudW1iZXIgfCBzdHJpbmd9fSAtIE5vcm1hbGl6ZWQgaW50ZXJ2YWwgYW5kIGZpcnN0LXJ1biBkZWxheSB2YWx1ZXMuXG4gICAqL1xuICBub3JtYWxpemVFdmVyeShldmVyeSkge1xuICAgIGlmIChBcnJheS5pc0FycmF5KGV2ZXJ5KSkge1xuICAgICAgY29uc3QgW2V2ZXJ5VmFsdWUsIGV2ZXJ5T3B0aW9uc10gPSBldmVyeVxuXG4gICAgICBpZiAoIWV2ZXJ5T3B0aW9ucyB8fCB0eXBlb2YgZXZlcnlPcHRpb25zICE9PSBcIm9iamVjdFwiIHx8IEFycmF5LmlzQXJyYXkoZXZlcnlPcHRpb25zKSkge1xuICAgICAgICByZXR1cm4ge2V2ZXJ5VmFsdWV9XG4gICAgICB9XG5cbiAgICAgIHJldHVybiB7ZXZlcnlWYWx1ZSwgZmlyc3RJblZhbHVlOiBldmVyeU9wdGlvbnMuZmlyc3RJbiA/PyBldmVyeU9wdGlvbnMuZmlyc3RfaW59XG4gICAgfVxuXG4gICAgcmV0dXJuIHtldmVyeVZhbHVlOiBldmVyeX1cbiAgfVxufVxuIl19