@pipeline-builder/pipeline-data 4.1.23 → 4.1.25
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/lib/api/alert-destinations.d.ts +17 -0
- package/lib/api/alert-destinations.js +36 -0
- package/lib/api/reporting/analytics-queries.d.ts +274 -0
- package/lib/api/reporting/analytics-queries.js +598 -0
- package/lib/api/reporting/daily-rollup.d.ts +41 -0
- package/lib/api/reporting/daily-rollup.js +173 -0
- package/lib/api/reporting/deploy-rollup.d.ts +78 -0
- package/lib/api/reporting/deploy-rollup.js +203 -0
- package/lib/api/reporting/dora-scoring.d.ts +14 -0
- package/lib/api/reporting/dora-scoring.js +25 -1
- package/lib/api/reporting/dora.js +30 -6
- package/lib/api/reporting/failure-classifier.d.ts +54 -0
- package/lib/api/reporting/failure-classifier.js +179 -0
- package/lib/api/reporting/ingest.d.ts +2 -2
- package/lib/api/reporting/ingest.js +129 -4
- package/lib/api/reporting/last-deploy.d.ts +31 -0
- package/lib/api/reporting/last-deploy.js +93 -0
- package/lib/api/reporting/needs-attention.d.ts +93 -0
- package/lib/api/reporting/needs-attention.js +152 -0
- package/lib/api/reporting/plugin-exposure.d.ts +129 -0
- package/lib/api/reporting/plugin-exposure.js +312 -0
- package/lib/api/reporting/retention-sweep.js +9 -1
- package/lib/api/reporting/sql-helpers.d.ts +35 -2
- package/lib/api/reporting/sql-helpers.js +39 -1
- package/lib/api/reporting/stakeholder/compose.d.ts +115 -0
- package/lib/api/reporting/stakeholder/compose.js +234 -0
- package/lib/api/reporting/stakeholder/index.d.ts +14 -0
- package/lib/api/reporting/stakeholder/index.js +17 -0
- package/lib/api/reporting/stakeholder/period.d.ts +102 -0
- package/lib/api/reporting/stakeholder/period.js +273 -0
- package/lib/api/reporting/stakeholder/report-html.d.ts +93 -0
- package/lib/api/reporting/stakeholder/report-html.js +208 -0
- package/lib/api/reporting/stakeholder/sections.d.ts +117 -0
- package/lib/api/reporting/stakeholder/sections.js +249 -0
- package/lib/api/reporting/stakeholder/store.d.ts +450 -0
- package/lib/api/reporting/stakeholder/store.js +981 -0
- package/lib/api/reporting/stakeholder/templates.d.ts +21 -0
- package/lib/api/reporting/stakeholder/templates.js +104 -0
- package/lib/api/reporting/types.d.ts +73 -0
- package/lib/api/reporting/types.js +1 -1
- package/lib/api/reporting-service.d.ts +94 -5
- package/lib/api/reporting-service.js +169 -10
- package/lib/api/soft-delete-sweep.d.ts +11 -2
- package/lib/api/soft-delete-sweep.js +17 -6
- package/lib/database/drizzle-schema.d.ts +2263 -385
- package/lib/database/drizzle-schema.js +18 -7
- package/lib/database/index.d.ts +1 -1
- package/lib/database/index.js +4 -2
- package/lib/database/schema/compliance.d.ts +0 -682
- package/lib/database/schema/compliance.js +6 -66
- package/lib/database/schema/ecosystem.d.ts +20 -157
- package/lib/database/schema/ecosystem.js +23 -16
- package/lib/database/schema/pipeline.d.ts +200 -0
- package/lib/database/schema/pipeline.js +100 -1
- package/lib/database/schema/plugin.d.ts +20 -1
- package/lib/database/schema/plugin.js +13 -1
- package/lib/database/schema/reporting-analytics.d.ts +1212 -0
- package/lib/database/schema/reporting-analytics.js +250 -0
- package/lib/database/schema/reporting-stakeholder.d.ts +1405 -0
- package/lib/database/schema/reporting-stakeholder.js +251 -0
- package/lib/index.d.ts +10 -1
- package/lib/index.js +15 -1
- package/lib/testing/exports.json +67 -4
- package/package.json +4 -3
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
// Copyright 2026 Pipeline Builder Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/** Read the wall-clock parts of `instant` as they read in `tz`. */
|
|
4
|
+
function civilIn(instant, tz) {
|
|
5
|
+
// `en-CA` yields ISO-ish `YYYY-MM-DD`, and hourCycle h23 avoids a 24:00 hour.
|
|
6
|
+
const parts = new Intl.DateTimeFormat('en-CA', {
|
|
7
|
+
timeZone: tz,
|
|
8
|
+
year: 'numeric',
|
|
9
|
+
month: '2-digit',
|
|
10
|
+
day: '2-digit',
|
|
11
|
+
hour: '2-digit',
|
|
12
|
+
minute: '2-digit',
|
|
13
|
+
second: '2-digit',
|
|
14
|
+
hourCycle: 'h23',
|
|
15
|
+
}).formatToParts(instant);
|
|
16
|
+
const get = (type) => Number(parts.find((p) => p.type === type)?.value ?? '0');
|
|
17
|
+
return {
|
|
18
|
+
year: get('year'),
|
|
19
|
+
month: get('month'),
|
|
20
|
+
day: get('day'),
|
|
21
|
+
hour: get('hour'),
|
|
22
|
+
minute: get('minute'),
|
|
23
|
+
second: get('second'),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The UTC instant at which the given civil time occurs in `tz`.
|
|
28
|
+
*
|
|
29
|
+
* Two passes: guess by treating the civil time as UTC, measure how far that guess
|
|
30
|
+
* actually lands from the target in `tz`, correct, then re-measure once. The
|
|
31
|
+
* second pass matters exactly at a DST transition, where the first correction can
|
|
32
|
+
* overshoot into the other offset.
|
|
33
|
+
*
|
|
34
|
+
* For a civil midnight that does not exist (spring-forward skips it) this settles
|
|
35
|
+
* on the instant just after the gap, which is the start of the day as that zone
|
|
36
|
+
* experienced it — the right answer for a period boundary.
|
|
37
|
+
*/
|
|
38
|
+
function instantOfCivil(c, tz) {
|
|
39
|
+
const asUtc = Date.UTC(c.year, c.month - 1, c.day, 0, 0, 0);
|
|
40
|
+
let guess = asUtc;
|
|
41
|
+
for (let i = 0; i < 2; i++) {
|
|
42
|
+
const back = civilIn(new Date(guess), tz);
|
|
43
|
+
const backUtc = Date.UTC(back.year, back.month - 1, back.day, back.hour, back.minute, back.second);
|
|
44
|
+
const drift = backUtc - asUtc;
|
|
45
|
+
if (drift === 0)
|
|
46
|
+
break;
|
|
47
|
+
guess -= drift;
|
|
48
|
+
}
|
|
49
|
+
return new Date(guess);
|
|
50
|
+
}
|
|
51
|
+
/** 0 = Monday … 6 = Sunday, for a civil date (Zeller-free: use a UTC probe). */
|
|
52
|
+
function civilWeekday(c) {
|
|
53
|
+
const dow = new Date(Date.UTC(c.year, c.month - 1, c.day)).getUTCDay(); // 0=Sun
|
|
54
|
+
return (dow + 6) % 7; // 0=Mon
|
|
55
|
+
}
|
|
56
|
+
/** Add `days` to a civil date, normalising via UTC arithmetic (no tz involved). */
|
|
57
|
+
function addCivilDays(c, days) {
|
|
58
|
+
const d = new Date(Date.UTC(c.year, c.month - 1, c.day + days));
|
|
59
|
+
return { year: d.getUTCFullYear(), month: d.getUTCMonth() + 1, day: d.getUTCDate() };
|
|
60
|
+
}
|
|
61
|
+
/** Add `months` to a civil date, clamping the day into the target month. */
|
|
62
|
+
function addCivilMonths(c, months) {
|
|
63
|
+
const zeroBased = (c.year * 12) + (c.month - 1) + months;
|
|
64
|
+
const year = Math.floor(zeroBased / 12);
|
|
65
|
+
const month = (zeroBased % 12) + 1;
|
|
66
|
+
const lastDay = new Date(Date.UTC(year, month, 0)).getUTCDate();
|
|
67
|
+
return { year, month, day: Math.min(c.day, lastDay) };
|
|
68
|
+
}
|
|
69
|
+
/** The civil start of the period containing `c`. */
|
|
70
|
+
function startOfPeriod(c, cadence, weekStart) {
|
|
71
|
+
const date = { year: c.year, month: c.month, day: c.day };
|
|
72
|
+
if (cadence === 'weekly') {
|
|
73
|
+
const mondayIndex = civilWeekday(date);
|
|
74
|
+
// Sunday weeks start one day earlier than the Monday index implies.
|
|
75
|
+
const back = weekStart === 'sunday' ? (mondayIndex + 1) % 7 : mondayIndex;
|
|
76
|
+
return addCivilDays(date, -back);
|
|
77
|
+
}
|
|
78
|
+
if (cadence === 'monthly')
|
|
79
|
+
return { year: c.year, month: c.month, day: 1 };
|
|
80
|
+
// Quarterly: Jan/Apr/Jul/Oct.
|
|
81
|
+
const qStartMonth = Math.floor((c.month - 1) / 3) * 3 + 1;
|
|
82
|
+
return { year: c.year, month: qStartMonth, day: 1 };
|
|
83
|
+
}
|
|
84
|
+
/** Shift a civil period start by `n` whole periods. */
|
|
85
|
+
function shiftPeriod(start, cadence, n) {
|
|
86
|
+
if (cadence === 'weekly')
|
|
87
|
+
return addCivilDays(start, 7 * n);
|
|
88
|
+
if (cadence === 'monthly')
|
|
89
|
+
return addCivilMonths(start, n);
|
|
90
|
+
return addCivilMonths(start, 3 * n);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* ISO-8601 week number of a civil date. Used only for the weekly LABEL, and
|
|
94
|
+
* deliberately ISO even for a Sunday-week org: `2026-W38` is a recognised
|
|
95
|
+
* notation and inventing a Sunday-based numbering would make labels
|
|
96
|
+
* non-comparable with anything else.
|
|
97
|
+
*/
|
|
98
|
+
function isoWeek(c) {
|
|
99
|
+
const d = new Date(Date.UTC(c.year, c.month - 1, c.day));
|
|
100
|
+
const day = (d.getUTCDay() + 6) % 7; // 0=Mon
|
|
101
|
+
d.setUTCDate(d.getUTCDate() - day + 3); // nearest Thursday
|
|
102
|
+
const isoYear = d.getUTCFullYear();
|
|
103
|
+
const firstThursday = new Date(Date.UTC(isoYear, 0, 4));
|
|
104
|
+
const firstDay = (firstThursday.getUTCDay() + 6) % 7;
|
|
105
|
+
firstThursday.setUTCDate(firstThursday.getUTCDate() - firstDay + 3);
|
|
106
|
+
const week = 1 + Math.round((d.getTime() - firstThursday.getTime()) / (7 * 86_400_000));
|
|
107
|
+
return { year: isoYear, week };
|
|
108
|
+
}
|
|
109
|
+
/** The label for a period, from its civil start. */
|
|
110
|
+
function formatLabel(start, cadence) {
|
|
111
|
+
if (cadence === 'weekly') {
|
|
112
|
+
const { year, week } = isoWeek(start);
|
|
113
|
+
return `${year}-W${String(week).padStart(2, '0')}`;
|
|
114
|
+
}
|
|
115
|
+
if (cadence === 'monthly')
|
|
116
|
+
return `${start.year}-${String(start.month).padStart(2, '0')}`;
|
|
117
|
+
return `${start.year}-Q${Math.floor((start.month - 1) / 3) + 1}`;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Resolve the last COMPLETE period for `cadence` in `tz`.
|
|
121
|
+
*
|
|
122
|
+
* The period in progress is excluded: its end is the boundary, so a Monday run
|
|
123
|
+
* reports last week and a run on the 1st reports last month.
|
|
124
|
+
*/
|
|
125
|
+
export function resolvePeriod(cadence, tz, weekStart = 'monday', now = new Date()) {
|
|
126
|
+
const nowCivil = civilIn(now, tz);
|
|
127
|
+
// Start of the period CONTAINING now = end of the last complete one.
|
|
128
|
+
const currentStart = startOfPeriod(nowCivil, cadence, weekStart);
|
|
129
|
+
const start = shiftPeriod(currentStart, cadence, -1);
|
|
130
|
+
const prevStart = shiftPeriod(currentStart, cadence, -2);
|
|
131
|
+
return {
|
|
132
|
+
start: instantOfCivil(start, tz),
|
|
133
|
+
end: instantOfCivil(currentStart, tz),
|
|
134
|
+
prevStart: instantOfCivil(prevStart, tz),
|
|
135
|
+
prevEnd: instantOfCivil(start, tz),
|
|
136
|
+
label: formatLabel(start, cadence),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Resolve a SPECIFIC period by its label (`2026-W38`, `2026-08`, `2026-Q3`), for
|
|
141
|
+
* on-demand runs and backfills. Returns null when the label does not parse or
|
|
142
|
+
* does not match the cadence.
|
|
143
|
+
*/
|
|
144
|
+
export function resolvePeriodByLabel(label, cadence, tz, weekStart = 'monday') {
|
|
145
|
+
let start = null;
|
|
146
|
+
const week = /^(\d{4})-W(\d{2})$/.exec(label);
|
|
147
|
+
const month = /^(\d{4})-(\d{2})$/.exec(label);
|
|
148
|
+
const quarter = /^(\d{4})-Q([1-4])$/.exec(label);
|
|
149
|
+
if (cadence === 'weekly' && week) {
|
|
150
|
+
const year = Number(week[1]);
|
|
151
|
+
const wk = Number(week[2]);
|
|
152
|
+
if (wk < 1 || wk > 53)
|
|
153
|
+
return null;
|
|
154
|
+
// ISO week 1 contains Jan 4th; walk back to that week's start.
|
|
155
|
+
const jan4 = { year, month: 1, day: 4 };
|
|
156
|
+
const week1Start = startOfPeriod({ ...jan4, hour: 0, minute: 0, second: 0 }, 'weekly', weekStart);
|
|
157
|
+
start = addCivilDays(week1Start, (wk - 1) * 7);
|
|
158
|
+
// A 53rd week that does not exist in this ISO year lands in the next one.
|
|
159
|
+
if (isoWeek(start).year !== year && wk === 53)
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
else if (cadence === 'monthly' && month) {
|
|
163
|
+
const m = Number(month[2]);
|
|
164
|
+
if (m < 1 || m > 12)
|
|
165
|
+
return null;
|
|
166
|
+
start = { year: Number(month[1]), month: m, day: 1 };
|
|
167
|
+
}
|
|
168
|
+
else if (cadence === 'quarterly' && quarter) {
|
|
169
|
+
start = { year: Number(quarter[1]), month: (Number(quarter[2]) - 1) * 3 + 1, day: 1 };
|
|
170
|
+
}
|
|
171
|
+
if (!start)
|
|
172
|
+
return null;
|
|
173
|
+
const end = shiftPeriod(start, cadence, 1);
|
|
174
|
+
const prevStart = shiftPeriod(start, cadence, -1);
|
|
175
|
+
return {
|
|
176
|
+
start: instantOfCivil(start, tz),
|
|
177
|
+
end: instantOfCivil(end, tz),
|
|
178
|
+
prevStart: instantOfCivil(prevStart, tz),
|
|
179
|
+
prevEnd: instantOfCivil(start, tz),
|
|
180
|
+
label: formatLabel(start, cadence),
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Refuse a period the org's data cannot support.
|
|
185
|
+
*
|
|
186
|
+
* A window older than the retention horizon must be REFUSED with the reason, not
|
|
187
|
+
* silently truncated: a report labelled `2026-Q1` that quietly covers only the
|
|
188
|
+
* last 30 days of it is worse than no report, because a manager cannot tell.
|
|
189
|
+
*
|
|
190
|
+
* `minFromMs` is the retention floor from `orgRetentionWindowFromSettings`
|
|
191
|
+
* (0 = unlimited).
|
|
192
|
+
*/
|
|
193
|
+
export function rejectUnreportablePeriod(period, opts) {
|
|
194
|
+
const now = opts.now ?? new Date();
|
|
195
|
+
if (period.end.getTime() > now.getTime()) {
|
|
196
|
+
return {
|
|
197
|
+
reason: 'in_progress',
|
|
198
|
+
message: `Period ${period.label} has not finished yet (ends ${period.end.toISOString()}). A report covers the last COMPLETE period.`,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
if (opts.minFromMs > 0) {
|
|
202
|
+
// The comparison period is part of the report when a section shows change, so
|
|
203
|
+
// its start is the real floor — otherwise "vs last week" silently reads zero.
|
|
204
|
+
const earliest = opts.includePrevious === false ? period.start : period.prevStart;
|
|
205
|
+
if (earliest.getTime() < opts.minFromMs) {
|
|
206
|
+
return {
|
|
207
|
+
reason: 'before_retention',
|
|
208
|
+
message: `Period ${period.label} starts ${earliest.toISOString()}, before this organization's reporting retention horizon (${new Date(opts.minFromMs).toISOString()}). The underlying events have been purged.`,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* The instant the period CONTAINING `from` ends — that is, the moment the next
|
|
216
|
+
* complete period becomes reportable.
|
|
217
|
+
*
|
|
218
|
+
* This is what a definition's `nextRunAt` is derived from, and it is deliberately
|
|
219
|
+
* the boundary rather than "last run + 7 days": a weekly report whose next run is
|
|
220
|
+
* computed by adding elapsed milliseconds drifts an hour at every DST transition
|
|
221
|
+
* and eventually fires on a Sunday. The boundary is a calendar fact in the
|
|
222
|
+
* report's own timezone, so it is the same answer whatever the previous run did.
|
|
223
|
+
*
|
|
224
|
+
* Strictly after `from`: a `from` sitting exactly on a boundary gets the NEXT one,
|
|
225
|
+
* so a run that completes at the instant of the boundary cannot re-claim it.
|
|
226
|
+
*/
|
|
227
|
+
export function nextPeriodBoundary(cadence, tz, weekStart = 'monday', from = new Date()) {
|
|
228
|
+
const containing = startOfPeriod(civilIn(from, tz), cadence, weekStart);
|
|
229
|
+
return instantOfCivil(shiftPeriod(containing, cadence, 1), tz);
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Every COMPLETE period that ended after `since`, oldest first.
|
|
233
|
+
*
|
|
234
|
+
* This is the catch-up list: a service that was down over a weekend, a definition
|
|
235
|
+
* created with a back-dated `nextRunAt`, or a scheduler disabled by its kill switch
|
|
236
|
+
* for a fortnight all leave periods that were never reported. Returning them in
|
|
237
|
+
* order matters — a manager reading three weekly reports that arrive at once needs
|
|
238
|
+
* them to make sense read downwards.
|
|
239
|
+
*
|
|
240
|
+
* `max` bounds the walk. A definition untouched for two years must not produce 104
|
|
241
|
+
* weekly reports in one cycle; the cap means the oldest are skipped, which is the
|
|
242
|
+
* right trade — nobody wants last April's weekly status, and the snapshot for it
|
|
243
|
+
* would be past the retention horizon anyway.
|
|
244
|
+
*/
|
|
245
|
+
export function completePeriodsSince(cadence, tz, weekStart, since, now = new Date(), max = 4) {
|
|
246
|
+
const latest = resolvePeriod(cadence, tz, weekStart, now);
|
|
247
|
+
if (max <= 0)
|
|
248
|
+
return [];
|
|
249
|
+
// No previous run: report the latest complete period only. A definition created
|
|
250
|
+
// today does not owe its owner a backfill they never asked for.
|
|
251
|
+
if (!since)
|
|
252
|
+
return [latest];
|
|
253
|
+
const out = [];
|
|
254
|
+
const latestStart = startOfPeriod(civilIn(latest.start, tz), cadence, weekStart);
|
|
255
|
+
for (let back = 0; back < max; back += 1) {
|
|
256
|
+
const start = shiftPeriod(latestStart, cadence, -back);
|
|
257
|
+
const instant = instantOfCivil(start, tz);
|
|
258
|
+
// `since` is the last run's period END for a run that happened, so a period
|
|
259
|
+
// whose end is at or before it has already been reported.
|
|
260
|
+
if (instantOfCivil(shiftPeriod(start, cadence, 1), tz).getTime() <= since.getTime())
|
|
261
|
+
break;
|
|
262
|
+
const prevStart = shiftPeriod(start, cadence, -1);
|
|
263
|
+
out.push({
|
|
264
|
+
start: instant,
|
|
265
|
+
end: instantOfCivil(shiftPeriod(start, cadence, 1), tz),
|
|
266
|
+
prevStart: instantOfCivil(prevStart, tz),
|
|
267
|
+
prevEnd: instant,
|
|
268
|
+
label: formatLabel(start, cadence),
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
return out.reverse();
|
|
272
|
+
}
|
|
273
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGVyaW9kLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2FwaS9yZXBvcnRpbmcvc3Rha2Vob2xkZXIvcGVyaW9kLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUEwQ3RDLG1FQUFtRTtBQUNuRSxTQUFTLE9BQU8sQ0FBQyxPQUFhLEVBQUUsRUFBVTtJQUN4Qyw4RUFBOEU7SUFDOUUsTUFBTSxLQUFLLEdBQUcsSUFBSSxJQUFJLENBQUMsY0FBYyxDQUFDLE9BQU8sRUFBRTtRQUM3QyxRQUFRLEVBQUUsRUFBRTtRQUNaLElBQUksRUFBRSxTQUFTO1FBQ2YsS0FBSyxFQUFFLFNBQVM7UUFDaEIsR0FBRyxFQUFFLFNBQVM7UUFDZCxJQUFJLEVBQUUsU0FBUztRQUNmLE1BQU0sRUFBRSxTQUFTO1FBQ2pCLE1BQU0sRUFBRSxTQUFTO1FBQ2pCLFNBQVMsRUFBRSxLQUFLO0tBQ2pCLENBQUMsQ0FBQyxhQUFhLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDMUIsTUFBTSxHQUFHLEdBQUcsQ0FBQyxJQUFZLEVBQVUsRUFBRSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsSUFBSSxLQUFLLElBQUksQ0FBQyxFQUFFLEtBQUssSUFBSSxHQUFHLENBQUMsQ0FBQztJQUMvRixPQUFPO1FBQ0wsSUFBSSxFQUFFLEdBQUcsQ0FBQyxNQUFNLENBQUM7UUFDakIsS0FBSyxFQUFFLEdBQUcsQ0FBQyxPQUFPLENBQUM7UUFDbkIsR0FBRyxFQUFFLEdBQUcsQ0FBQyxLQUFLLENBQUM7UUFDZixJQUFJLEVBQUUsR0FBRyxDQUFDLE1BQU0sQ0FBQztRQUNqQixNQUFNLEVBQUUsR0FBRyxDQUFDLFFBQVEsQ0FBQztRQUNyQixNQUFNLEVBQUUsR0FBRyxDQUFDLFFBQVEsQ0FBQztLQUN0QixDQUFDO0FBQ0osQ0FBQztBQUVEOzs7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QUFFLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQztZQUN2RCxTQUFTLEVBQUUsY0FBYyxDQUFDLFNBQVMsRUFBRSxFQUFFLENBQUM7WUFDeEMsT0FBTyxFQUFFLE9BQU87WUFDaEIsS0FBSyxFQUFFLFdBQVcsQ0FBQyxLQUFLLEVBQUUsT0FBTyxDQUFDO1NBQ25DLENBQUMsQ0FBQztJQUNMLENBQUM7SUFDRCxPQUFPLEdBQUcsQ0FBQyxPQUFPLEVBQUUsQ0FBQztBQUN2QixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbi8qKlxuICogUGVyaW9kIHJlc29sdXRpb24gZm9yIHNjaGVkdWxlZCByZXBvcnRzOiB3aGljaCBjb21wbGV0ZSB3ZWVrLCBtb250aCBvciBxdWFydGVyXG4gKiBhIHJ1biBjb3ZlcnMsIGFuZCB0aGUgb25lIGJlZm9yZSBpdCBmb3IgY29tcGFyaXNvbi5cbiAqXG4gKiBBIHJlcG9ydCBjb3ZlcnMgdGhlIExBU1QgQ09NUExFVEUgcGVyaW9kLCBuZXZlciBhIHBhcnRpYWwgb25lLiBcIkRlcGxveXMgdGhpc1xuICogd2Vla1wiIG9uIGEgTW9uZGF5IG1vcm5pbmcgbWVhbnMgbGFzdCB3ZWVrIOKAlCBhIG1hbmFnZXIgcmVhZGluZyBhIGhhbGYtZmluaXNoZWRcbiAqIHdlZWsgc2VlcyBudW1iZXJzIHRoYXQgY2hhbmdlIHVuZGVyIHRoZW0uXG4gKlxuICogRXZlcnl0aGluZyBoZXJlIGlzIGNvbXB1dGVkIGluIHRoZSByZXBvcnQncyBJQU5BIHRpbWV6b25lLCB3aXRob3V0IGEgZGF0ZVxuICogbGlicmFyeS4gYEludGwuRGF0ZVRpbWVGb3JtYXRgIHdpdGggYSBgdGltZVpvbmVgIGlzIHRoZSBvbmx5IHR6IGRhdGFiYXNlIE5vZGVcbiAqIHNoaXBzLCBzbyB0aGUgYXBwcm9hY2ggaXM6IHJlYWQgdGhlIHdhbGwtY2xvY2sgcGFydHMgaW4gdGhhdCB6b25lLCBkbyBjaXZpbFxuICogYXJpdGhtZXRpYyBvbiB0aGVtICh3aGljaCBpcyBleGFjdCDigJQgbW9udGhzIGFuZCBxdWFydGVycyBhcmUgZGVmaW5lZCBvbiB0aGVcbiAqIGNhbGVuZGFyLCBub3Qgb24gZWxhcHNlZCBtaWxsaXNlY29uZHMpLCB0aGVuIGNvbnZlcnQgdGhlIHJlc3VsdGluZyBjaXZpbFxuICogbWlkbmlnaHQgYmFjayB0byBhIFVUQyBpbnN0YW50IGJ5IG1lYXN1cmluZyB0aGUgem9uZSdzIG9mZnNldCBBVCB0aGF0IGluc3RhbnQuXG4gKlxuICogTWVhc3VyaW5nIHRoZSBvZmZzZXQgYXQgdGhlIHRhcmdldCBpbnN0YW50IHJhdGhlciB0aGFuIGF0IGBub3dgIGlzIHdoYXQgbWFrZXNcbiAqIERTVCBjb3JyZWN0OiBhIHdlZWtseSBwZXJpb2QgdGhhdCBzdGFydHMgYmVmb3JlIGEgdHJhbnNpdGlvbiBhbmQgZW5kcyBhZnRlciBpdFxuICogaXMgbm90IDE2OCBob3VycyBsb25nLCBhbmQgYSByZXNvbHZlciB0aGF0IGFkZGVkIGA3ICogMjQgKiAzNjAwXzAwMGAgd291bGRcbiAqIHNpbGVudGx5IHNoaWZ0IGV2ZXJ5IGJvdW5kYXJ5IGJ5IGFuIGhvdXIgZm9yIGhhbGYgdGhlIHllYXIuXG4gKi9cblxuaW1wb3J0IHR5cGUgeyBSZXBvcnRDYWRlbmNlIH0gZnJvbSAnLi4vLi4vLi4vZGF0YWJhc2Uvc2NoZW1hL3JlcG9ydGluZy1zdGFrZWhvbGRlci5qcyc7XG5cbi8qKiBPbmUgcmVzb2x2ZWQgcmVwb3J0aW5nIHBlcmlvZCwgcGx1cyB0aGUgcHJldmlvdXMgb25lIGZvciBjb21wYXJpc29uLiAqL1xuZXhwb3J0IGludGVyZmFjZSBSZXNvbHZlZFBlcmlvZCB7XG4gIC8qKiBJbmNsdXNpdmUgc3RhcnQgb2YgdGhlIHBlcmlvZCBiZWluZyByZXBvcnRlZCAoVVRDIGluc3RhbnQpLiAqL1xuICBzdGFydDogRGF0ZTtcbiAgLyoqIEV4Y2x1c2l2ZSBlbmQg4oCUIGFsc28gdGhlIHN0YXJ0IG9mIHRoZSBwZXJpb2QgaW4gcHJvZ3Jlc3MuICovXG4gIGVuZDogRGF0ZTtcbiAgLyoqIEluY2x1c2l2ZSBzdGFydCBvZiB0aGUgUFJFVklPVVMgcGVyaW9kLCBmb3IgcGVyaW9kLW92ZXItcGVyaW9kIGNoYW5nZS4gKi9cbiAgcHJldlN0YXJ0OiBEYXRlO1xuICAvKiogRXhjbHVzaXZlIGVuZCBvZiB0aGUgcHJldmlvdXMgcGVyaW9kICg9PT0gYHN0YXJ0YCkuICovXG4gIHByZXZFbmQ6IERhdGU7XG4gIC8qKiBIdW1hbiBsYWJlbCBpbiB0aGUgcmVwb3J0J3MgdGltZXpvbmU6IGAyMDI2LVczOGAsIGAyMDI2LTA4YCwgYDIwMjYtUTNgLiAqL1xuICBsYWJlbDogc3RyaW5nO1xufVxuXG4vKiogQ2l2aWwgKHdhbGwtY2xvY2spIGRhdGUgcGFydHMgaW4gYSBnaXZlbiB6b25lLiAqL1xuaW50ZXJmYWNlIENpdmlsIHsgeWVhcjogbnVtYmVyOyBtb250aDogbnVtYmVyOyBkYXk6IG51bWJlcjsgaG91cjogbnVtYmVyOyBtaW51dGU6IG51bWJlcjsgc2Vjb25kOiBudW1iZXIgfVxuXG4vKiogUmVhZCB0aGUgd2FsbC1jbG9jayBwYXJ0cyBvZiBgaW5zdGFudGAgYXMgdGhleSByZWFkIGluIGB0emAuICovXG5mdW5jdGlvbiBjaXZpbEluKGluc3RhbnQ6IERhdGUsIHR6OiBzdHJpbmcpOiBDaXZpbCB7XG4gIC8vIGBlbi1DQWAgeWllbGRzIElTTy1pc2ggYFlZWVktTU0tRERgLCBhbmQgaG91ckN5Y2xlIGgyMyBhdm9pZHMgYSAyNDowMCBob3VyLlxuICBjb25zdCBwYXJ0cyA9IG5ldyBJbnRsLkRhdGVUaW1lRm9ybWF0KCdlbi1DQScsIHtcbiAgICB0aW1lWm9uZTogdHosXG4gICAgeWVhcjogJ251bWVyaWMnLFxuICAgIG1vbnRoOiAnMi1kaWdpdCcsXG4gICAgZGF5OiAnMi1kaWdpdCcsXG4gICAgaG91cjogJzItZGlnaXQnLFxuICAgIG1pbnV0ZTogJzItZGlnaXQnLFxuICAgIHNlY29uZDogJzItZGlnaXQnLFxuICAgIGhvdXJDeWNsZTogJ2gyMycsXG4gIH0pLmZvcm1hdFRvUGFydHMoaW5zdGFudCk7XG4gIGNvbnN0IGdldCA9ICh0eXBlOiBzdHJpbmcpOiBudW1iZXIgPT4gTnVtYmVyKHBhcnRzLmZpbmQoKHApID0+IHAudHlwZSA9PT0gdHlwZSk/LnZhbHVlID8/ICcwJyk7XG4gIHJldHVybiB7XG4gICAgeWVhcjogZ2V0KCd5ZWFyJyksXG4gICAgbW9udGg6IGdldCgnbW9udGgnKSxcbiAgICBkYXk6IGdldCgnZGF5JyksXG4gICAgaG91cjogZ2V0KCdob3VyJyksXG4gICAgbWludXRlOiBnZXQoJ21pbnV0ZScpLFxuICAgIHNlY29uZDogZ2V0KCdzZWNvbmQnKSxcbiAgfTtcbn1cblxuLyoqXG4gKiBUaGUgVVRDIGluc3RhbnQgYXQgd2hpY2ggdGhlIGdpdmVuIGNpdmlsIHRpbWUgb2NjdXJzIGluIGB0emAuXG4gKlxuICogVHdvIHBhc3NlczogZ3Vlc3MgYnkgdHJlYXRpbmcgdGhlIGNpdmlsIHRpbWUgYXMgVVRDLCBtZWFzdXJlIGhvdyBmYXIgdGhhdCBndWVzc1xuICogYWN0dWFsbHkgbGFuZHMgZnJvbSB0aGUgdGFyZ2V0IGluIGB0emAsIGNvcnJlY3QsIHRoZW4gcmUtbWVhc3VyZSBvbmNlLiBUaGVcbiAqIHNlY29uZCBwYXNzIG1hdHRlcnMgZXhhY3RseSBhdCBhIERTVCB0cmFuc2l0aW9uLCB3aGVyZSB0aGUgZmlyc3QgY29ycmVjdGlvbiBjYW5cbiAqIG92ZXJzaG9vdCBpbnRvIHRoZSBvdGhlciBvZmZzZXQuXG4gKlxuICogRm9yIGEgY2l2aWwgbWlkbmlnaHQgdGhhdCBkb2VzIG5vdCBleGlzdCAoc3ByaW5nLWZvcndhcmQgc2tpcHMgaXQpIHRoaXMgc2V0dGxlc1xuICogb24gdGhlIGluc3RhbnQganVzdCBhZnRlciB0aGUgZ2FwLCB3aGljaCBpcyB0aGUgc3RhcnQgb2YgdGhlIGRheSBhcyB0aGF0IHpvbmVcbiAqIGV4cGVyaWVuY2VkIGl0IOKAlCB0aGUgcmlnaHQgYW5zd2VyIGZvciBhIHBlcmlvZCBib3VuZGFyeS5cbiAqL1xuZnVuY3Rpb24gaW5zdGFudE9mQ2l2aWwoYzogeyB5ZWFyOiBudW1iZXI7IG1vbnRoOiBudW1iZXI7IGRheTogbnVtYmVyIH0sIHR6OiBzdHJpbmcpOiBEYXRlIHtcbiAgY29uc3QgYXNVdGMgPSBEYXRlLlVUQyhjLnllYXIsIGMubW9udGggLSAxLCBjLmRheSwgMCwgMCwgMCk7XG4gIGxldCBndWVzcyA9IGFzVXRjO1xuICBmb3IgKGxldCBpID0gMDsgaSA8IDI7IGkrKykge1xuICAgIGNvbnN0IGJhY2sgPSBjaXZpbEluKG5ldyBEYXRlKGd1ZXNzKSwgdHopO1xuICAgIGNvbnN0IGJhY2tVdGMgPSBEYXRlLlVUQyhiYWNrLnllYXIsIGJhY2subW9udGggLSAxLCBiYWNrLmRheSwgYmFjay5ob3VyLCBiYWNrLm1pbnV0ZSwgYmFjay5zZWNvbmQpO1xuICAgIGNvbnN0IGRyaWZ0ID0gYmFja1V0YyAtIGFzVXRjO1xuICAgIGlmIChkcmlmdCA9PT0gMCkgYnJlYWs7XG4gICAgZ3Vlc3MgLT0gZHJpZnQ7XG4gIH1cbiAgcmV0dXJuIG5ldyBEYXRlKGd1ZXNzKTtcbn1cblxuLyoqIDAgPSBNb25kYXkg4oCmIDYgPSBTdW5kYXksIGZvciBhIGNpdmlsIGRhdGUgKFplbGxlci1mcmVlOiB1c2UgYSBVVEMgcHJvYmUpLiAqL1xuZnVuY3Rpb24gY2l2aWxXZWVrZGF5KGM6IHsgeWVhcjogbnVtYmVyOyBtb250aDogbnVtYmVyOyBkYXk6IG51bWJlciB9KTogbnVtYmVyIHtcbiAgY29uc3QgZG93ID0gbmV3IERhdGUoRGF0ZS5VVEMoYy55ZWFyLCBjLm1vbnRoIC0gMSwgYy5kYXkpKS5nZXRVVENEYXkoKTsgLy8gMD1TdW5cbiAgcmV0dXJuIChkb3cgKyA2KSAlIDc7IC8vIDA9TW9uXG59XG5cbi8qKiBBZGQgYGRheXNgIHRvIGEgY2l2aWwgZGF0ZSwgbm9ybWFsaXNpbmcgdmlhIFVUQyBhcml0aG1ldGljIChubyB0eiBpbnZvbHZlZCkuICovXG5mdW5jdGlvbiBhZGRDaXZpbERheXMoYzogeyB5ZWFyOiBudW1iZXI7IG1vbnRoOiBudW1iZXI7IGRheTogbnVtYmVyIH0sIGRheXM6IG51bWJlcik6IHsgeWVhcjogbnVtYmVyOyBtb250aDogbnVtYmVyOyBkYXk6IG51bWJlciB9IHtcbiAgY29uc3QgZCA9IG5ldyBEYXRlKERhdGUuVVRDKGMueWVhciwgYy5tb250aCAtIDEsIGMuZGF5ICsgZGF5cykpO1xuICByZXR1cm4geyB5ZWFyOiBkLmdldFVUQ0Z1bGxZZWFyKCksIG1vbnRoOiBkLmdldFVUQ01vbnRoKCkgKyAxLCBkYXk6IGQuZ2V0VVRDRGF0ZSgpIH07XG59XG5cbi8qKiBBZGQgYG1vbnRoc2AgdG8gYSBjaXZpbCBkYXRlLCBjbGFtcGluZyB0aGUgZGF5IGludG8gdGhlIHRhcmdldCBtb250aC4gKi9cbmZ1bmN0aW9uIGFkZENpdmlsTW9udGhzKGM6IHsgeWVhcjogbnVtYmVyOyBtb250aDogbnVtYmVyOyBkYXk6IG51bWJlciB9LCBtb250aHM6IG51bWJlcik6IHsgeWVhcjogbnVtYmVyOyBtb250aDogbnVtYmVyOyBkYXk6IG51bWJlciB9IHtcbiAgY29uc3QgemVyb0Jhc2VkID0gKGMueWVhciAqIDEyKSArIChjLm1vbnRoIC0gMSkgKyBtb250aHM7XG4gIGNvbnN0IHllYXIgPSBNYXRoLmZsb29yKHplcm9CYXNlZCAvIDEyKTtcbiAgY29uc3QgbW9udGggPSAoemVyb0Jhc2VkICUgMTIpICsgMTtcbiAgY29uc3QgbGFzdERheSA9IG5ldyBEYXRlKERhdGUuVVRDKHllYXIsIG1vbnRoLCAwKSkuZ2V0VVRDRGF0ZSgpO1xuICByZXR1cm4geyB5ZWFyLCBtb250aCwgZGF5OiBNYXRoLm1pbihjLmRheSwgbGFzdERheSkgfTtcbn1cblxuLyoqIFRoZSBjaXZpbCBzdGFydCBvZiB0aGUgcGVyaW9kIGNvbnRhaW5pbmcgYGNgLiAqL1xuZnVuY3Rpb24gc3RhcnRPZlBlcmlvZChcbiAgYzogQ2l2aWwsXG4gIGNhZGVuY2U6IFJlcG9ydENhZGVuY2UsXG4gIHdlZWtTdGFydDogJ21vbmRheScgfCAnc3VuZGF5Jyxcbik6IHsgeWVhcjogbnVtYmVyOyBtb250aDogbnVtYmVyOyBkYXk6IG51bWJlciB9IHtcbiAgY29uc3QgZGF0ZSA9IHsgeWVhcjogYy55ZWFyLCBtb250aDogYy5tb250aCwgZGF5OiBjLmRheSB9O1xuICBpZiAoY2FkZW5jZSA9PT0gJ3dlZWtseScpIHtcbiAgICBjb25zdCBtb25kYXlJbmRleCA9IGNpdmlsV2Vla2RheShkYXRlKTtcbiAgICAvLyBTdW5kYXkgd2Vla3Mgc3RhcnQgb25lIGRheSBlYXJsaWVyIHRoYW4gdGhlIE1vbmRheSBpbmRleCBpbXBsaWVzLlxuICAgIGNvbnN0IGJhY2sgPSB3ZWVrU3RhcnQgPT09ICdzdW5kYXknID8gKG1vbmRheUluZGV4ICsgMSkgJSA3IDogbW9uZGF5SW5kZXg7XG4gICAgcmV0dXJuIGFkZENpdmlsRGF5cyhkYXRlLCAtYmFjayk7XG4gIH1cbiAgaWYgKGNhZGVuY2UgPT09ICdtb250aGx5JykgcmV0dXJuIHsgeWVhcjogYy55ZWFyLCBtb250aDogYy5tb250aCwgZGF5OiAxIH07XG4gIC8vIFF1YXJ0ZXJseTogSmFuL0Fwci9KdWwvT2N0LlxuICBjb25zdCBxU3RhcnRNb250aCA9IE1hdGguZmxvb3IoKGMubW9udGggLSAxKSAvIDMpICogMyArIDE7XG4gIHJldHVybiB7IHllYXI6IGMueWVhciwgbW9udGg6IHFTdGFydE1vbnRoLCBkYXk6IDEgfTtcbn1cblxuLyoqIFNoaWZ0IGEgY2l2aWwgcGVyaW9kIHN0YXJ0IGJ5IGBuYCB3aG9sZSBwZXJpb2RzLiAqL1xuZnVuY3Rpb24gc2hpZnRQZXJpb2QoXG4gIHN0YXJ0OiB7IHllYXI6IG51bWJlcjsgbW9udGg6IG51bWJlcjsgZGF5OiBudW1iZXIgfSxcbiAgY2FkZW5jZTogUmVwb3J0Q2FkZW5jZSxcbiAgbjogbnVtYmVyLFxuKTogeyB5ZWFyOiBudW1iZXI7IG1vbnRoOiBudW1iZXI7IGRheTogbnVtYmVyIH0ge1xuICBpZiAoY2FkZW5jZSA9PT0gJ3dlZWtseScpIHJldHVybiBhZGRDaXZpbERheXMoc3RhcnQsIDcgKiBuKTtcbiAgaWYgKGNhZGVuY2UgPT09ICdtb250aGx5JykgcmV0dXJuIGFkZENpdmlsTW9udGhzKHN0YXJ0LCBuKTtcbiAgcmV0dXJuIGFkZENpdmlsTW9udGhzKHN0YXJ0LCAzICogbik7XG59XG5cbi8qKlxuICogSVNPLTg2MDEgd2VlayBudW1iZXIgb2YgYSBjaXZpbCBkYXRlLiBVc2VkIG9ubHkgZm9yIHRoZSB3ZWVrbHkgTEFCRUwsIGFuZFxuICogZGVsaWJlcmF0ZWx5IElTTyBldmVuIGZvciBhIFN1bmRheS13ZWVrIG9yZzogYDIwMjYtVzM4YCBpcyBhIHJlY29nbmlzZWRcbiAqIG5vdGF0aW9uIGFuZCBpbnZlbnRpbmcgYSBTdW5kYXktYmFzZWQgbnVtYmVyaW5nIHdvdWxkIG1ha2UgbGFiZWxzXG4gKiBub24tY29tcGFyYWJsZSB3aXRoIGFueXRoaW5nIGVsc2UuXG4gKi9cbmZ1bmN0aW9uIGlzb1dlZWsoYzogeyB5ZWFyOiBudW1iZXI7IG1vbnRoOiBudW1iZXI7IGRheTogbnVtYmVyIH0pOiB7IHllYXI6IG51bWJlcjsgd2VlazogbnVtYmVyIH0ge1xuICBjb25zdCBkID0gbmV3IERhdGUoRGF0ZS5VVEMoYy55ZWFyLCBjLm1vbnRoIC0gMSwgYy5kYXkpKTtcbiAgY29uc3QgZGF5ID0gKGQuZ2V0VVRDRGF5KCkgKyA2KSAlIDc7IC8vIDA9TW9uXG4gIGQuc2V0VVRDRGF0ZShkLmdldFVUQ0RhdGUoKSAtIGRheSArIDMpOyAvLyBuZWFyZXN0IFRodXJzZGF5XG4gIGNvbnN0IGlzb1llYXIgPSBkLmdldFVUQ0Z1bGxZZWFyKCk7XG4gIGNvbnN0IGZpcnN0VGh1cnNkYXkgPSBuZXcgRGF0ZShEYXRlLlVUQyhpc29ZZWFyLCAwLCA0KSk7XG4gIGNvbnN0IGZpcnN0RGF5ID0gKGZpcnN0VGh1cnNkYXkuZ2V0VVRDRGF5KCkgKyA2KSAlIDc7XG4gIGZpcnN0VGh1cnNkYXkuc2V0VVRDRGF0ZShmaXJzdFRodXJzZGF5LmdldFVUQ0RhdGUoKSAtIGZpcnN0RGF5ICsgMyk7XG4gIGNvbnN0IHdlZWsgPSAxICsgTWF0aC5yb3VuZCgoZC5nZXRUaW1lKCkgLSBmaXJzdFRodXJzZGF5LmdldFRpbWUoKSkgLyAoNyAqIDg2XzQwMF8wMDApKTtcbiAgcmV0dXJuIHsgeWVhcjogaXNvWWVhciwgd2VlayB9O1xufVxuXG4vKiogVGhlIGxhYmVsIGZvciBhIHBlcmlvZCwgZnJvbSBpdHMgY2l2aWwgc3RhcnQuICovXG5mdW5jdGlvbiBmb3JtYXRMYWJlbChzdGFydDogeyB5ZWFyOiBudW1iZXI7IG1vbnRoOiBudW1iZXI7IGRheTogbnVtYmVyIH0sIGNhZGVuY2U6IFJlcG9ydENhZGVuY2UpOiBzdHJpbmcge1xuICBpZiAoY2FkZW5jZSA9PT0gJ3dlZWtseScpIHtcbiAgICBjb25zdCB7IHllYXIsIHdlZWsgfSA9IGlzb1dlZWsoc3RhcnQpO1xuICAgIHJldHVybiBgJHt5ZWFyfS1XJHtTdHJpbmcod2VlaykucGFkU3RhcnQoMiwgJzAnKX1gO1xuICB9XG4gIGlmIChjYWRlbmNlID09PSAnbW9udGhseScpIHJldHVybiBgJHtzdGFydC55ZWFyfS0ke1N0cmluZyhzdGFydC5tb250aCkucGFkU3RhcnQoMiwgJzAnKX1gO1xuICByZXR1cm4gYCR7c3RhcnQueWVhcn0tUSR7TWF0aC5mbG9vcigoc3RhcnQubW9udGggLSAxKSAvIDMpICsgMX1gO1xufVxuXG4vKipcbiAqIFJlc29sdmUgdGhlIGxhc3QgQ09NUExFVEUgcGVyaW9kIGZvciBgY2FkZW5jZWAgaW4gYHR6YC5cbiAqXG4gKiBUaGUgcGVyaW9kIGluIHByb2dyZXNzIGlzIGV4Y2x1ZGVkOiBpdHMgZW5kIGlzIHRoZSBib3VuZGFyeSwgc28gYSBNb25kYXkgcnVuXG4gKiByZXBvcnRzIGxhc3Qgd2VlayBhbmQgYSBydW4gb24gdGhlIDFzdCByZXBvcnRzIGxhc3QgbW9udGguXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZXNvbHZlUGVyaW9kKFxuICBjYWRlbmNlOiBSZXBvcnRDYWRlbmNlLFxuICB0ejogc3RyaW5nLFxuICB3ZWVrU3RhcnQ6ICdtb25kYXknIHwgJ3N1bmRheScgPSAnbW9uZGF5JyxcbiAgbm93OiBEYXRlID0gbmV3IERhdGUoKSxcbik6IFJlc29sdmVkUGVyaW9kIHtcbiAgY29uc3Qgbm93Q2l2aWwgPSBjaXZpbEluKG5vdywgdHopO1xuICAvLyBTdGFydCBvZiB0aGUgcGVyaW9kIENPTlRBSU5JTkcgbm93ID0gZW5kIG9mIHRoZSBsYXN0IGNvbXBsZXRlIG9uZS5cbiAgY29uc3QgY3VycmVudFN0YXJ0ID0gc3RhcnRPZlBlcmlvZChub3dDaXZpbCwgY2FkZW5jZSwgd2Vla1N0YXJ0KTtcbiAgY29uc3Qgc3RhcnQgPSBzaGlmdFBlcmlvZChjdXJyZW50U3RhcnQsIGNhZGVuY2UsIC0xKTtcbiAgY29uc3QgcHJldlN0YXJ0ID0gc2hpZnRQZXJpb2QoY3VycmVudFN0YXJ0LCBjYWRlbmNlLCAtMik7XG4gIHJldHVybiB7XG4gICAgc3RhcnQ6IGluc3RhbnRPZkNpdmlsKHN0YXJ0LCB0eiksXG4gICAgZW5kOiBpbnN0YW50T2ZDaXZpbChjdXJyZW50U3RhcnQsIHR6KSxcbiAgICBwcmV2U3RhcnQ6IGluc3RhbnRPZkNpdmlsKHByZXZTdGFydCwgdHopLFxuICAgIHByZXZFbmQ6IGluc3RhbnRPZkNpdmlsKHN0YXJ0LCB0eiksXG4gICAgbGFiZWw6IGZvcm1hdExhYmVsKHN0YXJ0LCBjYWRlbmNlKSxcbiAgfTtcbn1cblxuLyoqXG4gKiBSZXNvbHZlIGEgU1BFQ0lGSUMgcGVyaW9kIGJ5IGl0cyBsYWJlbCAoYDIwMjYtVzM4YCwgYDIwMjYtMDhgLCBgMjAyNi1RM2ApLCBmb3JcbiAqIG9uLWRlbWFuZCBydW5zIGFuZCBiYWNrZmlsbHMuIFJldHVybnMgbnVsbCB3aGVuIHRoZSBsYWJlbCBkb2VzIG5vdCBwYXJzZSBvclxuICogZG9lcyBub3QgbWF0Y2ggdGhlIGNhZGVuY2UuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZXNvbHZlUGVyaW9kQnlMYWJlbChcbiAgbGFiZWw6IHN0cmluZyxcbiAgY2FkZW5jZTogUmVwb3J0Q2FkZW5jZSxcbiAgdHo6IHN0cmluZyxcbiAgd2Vla1N0YXJ0OiAnbW9uZGF5JyB8ICdzdW5kYXknID0gJ21vbmRheScsXG4pOiBSZXNvbHZlZFBlcmlvZCB8IG51bGwge1xuICBsZXQgc3RhcnQ6IHsgeWVhcjogbnVtYmVyOyBtb250aDogbnVtYmVyOyBkYXk6IG51bWJlciB9IHwgbnVsbCA9IG51bGw7XG5cbiAgY29uc3Qgd2VlayA9IC9eKFxcZHs0fSktVyhcXGR7Mn0pJC8uZXhlYyhsYWJlbCk7XG4gIGNvbnN0IG1vbnRoID0gL14oXFxkezR9KS0oXFxkezJ9KSQvLmV4ZWMobGFiZWwpO1xuICBjb25zdCBxdWFydGVyID0gL14oXFxkezR9KS1RKFsxLTRdKSQvLmV4ZWMobGFiZWwpO1xuXG4gIGlmIChjYWRlbmNlID09PSAnd2Vla2x5JyAmJiB3ZWVrKSB7XG4gICAgY29uc3QgeWVhciA9IE51bWJlcih3ZWVrWzFdKTtcbiAgICBjb25zdCB3ayA9IE51bWJlcih3ZWVrWzJdKTtcbiAgICBpZiAod2sgPCAxIHx8IHdrID4gNTMpIHJldHVybiBudWxsO1xuICAgIC8vIElTTyB3ZWVrIDEgY29udGFpbnMgSmFuIDR0aDsgd2FsayBiYWNrIHRvIHRoYXQgd2VlaydzIHN0YXJ0LlxuICAgIGNvbnN0IGphbjQgPSB7IHllYXIsIG1vbnRoOiAxLCBkYXk6IDQgfTtcbiAgICBjb25zdCB3ZWVrMVN0YXJ0ID0gc3RhcnRPZlBlcmlvZChcbiAgICAgIHsgLi4uamFuNCwgaG91cjogMCwgbWludXRlOiAwLCBzZWNvbmQ6IDAgfSxcbiAgICAgICd3ZWVrbHknLFxuICAgICAgd2Vla1N0YXJ0LFxuICAgICk7XG4gICAgc3RhcnQgPSBhZGRDaXZpbERheXMod2VlazFTdGFydCwgKHdrIC0gMSkgKiA3KTtcbiAgICAvLyBBIDUzcmQgd2VlayB0aGF0IGRvZXMgbm90IGV4aXN0IGluIHRoaXMgSVNPIHllYXIgbGFuZHMgaW4gdGhlIG5leHQgb25lLlxuICAgIGlmIChpc29XZWVrKHN0YXJ0KS55ZWFyICE9PSB5ZWFyICYmIHdrID09PSA1MykgcmV0dXJuIG51bGw7XG4gIH0gZWxzZSBpZiAoY2FkZW5jZSA9PT0gJ21vbnRobHknICYmIG1vbnRoKSB7XG4gICAgY29uc3QgbSA9IE51bWJlcihtb250aFsyXSk7XG4gICAgaWYgKG0gPCAxIHx8IG0gPiAxMikgcmV0dXJuIG51bGw7XG4gICAgc3RhcnQgPSB7IHllYXI6IE51bWJlcihtb250aFsxXSksIG1vbnRoOiBtLCBkYXk6IDEgfTtcbiAgfSBlbHNlIGlmIChjYWRlbmNlID09PSAncXVhcnRlcmx5JyAmJiBxdWFydGVyKSB7XG4gICAgc3RhcnQgPSB7IHllYXI6IE51bWJlcihxdWFydGVyWzFdKSwgbW9udGg6IChOdW1iZXIocXVhcnRlclsyXSkgLSAxKSAqIDMgKyAxLCBkYXk6IDEgfTtcbiAgfVxuICBpZiAoIXN0YXJ0KSByZXR1cm4gbnVsbDtcblxuICBjb25zdCBlbmQgPSBzaGlmdFBlcmlvZChzdGFydCwgY2FkZW5jZSwgMSk7XG4gIGNvbnN0IHByZXZTdGFydCA9IHNoaWZ0UGVyaW9kKHN0YXJ0LCBjYWRlbmNlLCAtMSk7XG4gIHJldHVybiB7XG4gICAgc3RhcnQ6IGluc3RhbnRPZkNpdmlsKHN0YXJ0LCB0eiksXG4gICAgZW5kOiBpbnN0YW50T2ZDaXZpbChlbmQsIHR6KSxcbiAgICBwcmV2U3RhcnQ6IGluc3RhbnRPZkNpdmlsKHByZXZTdGFydCwgdHopLFxuICAgIHByZXZFbmQ6IGluc3RhbnRPZkNpdmlsKHN0YXJ0LCB0eiksXG4gICAgbGFiZWw6IGZvcm1hdExhYmVsKHN0YXJ0LCBjYWRlbmNlKSxcbiAgfTtcbn1cblxuLyoqXG4gKiBXaHkgYSBwZXJpb2QgY2Fubm90IGJlIHJlcG9ydGVkLiBSZXR1cm5lZCByYXRoZXIgdGhhbiB0aHJvd24gc28gYSByb3V0ZSBjYW5cbiAqIHR1cm4gaXQgaW50byBhIDR4eCB3aXRoIHRoZSByZWFzb24sIGFuZCB0aGUgc2NoZWR1bGVyIGNhbiBwYXVzZSB3aXRoIGl0LlxuICovXG5leHBvcnQgdHlwZSBQZXJpb2RSZWplY3Rpb24gPVxuICB8IHsgcmVhc29uOiAnaW5fcHJvZ3Jlc3MnOyBtZXNzYWdlOiBzdHJpbmcgfVxuICB8IHsgcmVhc29uOiAnYmVmb3JlX3JldGVudGlvbic7IG1lc3NhZ2U6IHN0cmluZyB9O1xuXG4vKipcbiAqIFJlZnVzZSBhIHBlcmlvZCB0aGUgb3JnJ3MgZGF0YSBjYW5ub3Qgc3VwcG9ydC5cbiAqXG4gKiBBIHdpbmRvdyBvbGRlciB0aGFuIHRoZSByZXRlbnRpb24gaG9yaXpvbiBtdXN0IGJlIFJFRlVTRUQgd2l0aCB0aGUgcmVhc29uLCBub3RcbiAqIHNpbGVudGx5IHRydW5jYXRlZDogYSByZXBvcnQgbGFiZWxsZWQgYDIwMjYtUTFgIHRoYXQgcXVpZXRseSBjb3ZlcnMgb25seSB0aGVcbiAqIGxhc3QgMzAgZGF5cyBvZiBpdCBpcyB3b3JzZSB0aGFuIG5vIHJlcG9ydCwgYmVjYXVzZSBhIG1hbmFnZXIgY2Fubm90IHRlbGwuXG4gKlxuICogYG1pbkZyb21Nc2AgaXMgdGhlIHJldGVudGlvbiBmbG9vciBmcm9tIGBvcmdSZXRlbnRpb25XaW5kb3dGcm9tU2V0dGluZ3NgXG4gKiAoMCA9IHVubGltaXRlZCkuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZWplY3RVbnJlcG9ydGFibGVQZXJpb2QoXG4gIHBlcmlvZDogUmVzb2x2ZWRQZXJpb2QsXG4gIG9wdHM6IHsgbWluRnJvbU1zOiBudW1iZXI7IG5vdz86IERhdGU7IGluY2x1ZGVQcmV2aW91cz86IGJvb2xlYW4gfSxcbik6IFBlcmlvZFJlamVjdGlvbiB8IG51bGwge1xuICBjb25zdCBub3cgPSBvcHRzLm5vdyA/PyBuZXcgRGF0ZSgpO1xuICBpZiAocGVyaW9kLmVuZC5nZXRUaW1lKCkgPiBub3cuZ2V0VGltZSgpKSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIHJlYXNvbjogJ2luX3Byb2dyZXNzJyxcbiAgICAgIG1lc3NhZ2U6IGBQZXJpb2QgJHtwZXJpb2QubGFiZWx9IGhhcyBub3QgZmluaXNoZWQgeWV0IChlbmRzICR7cGVyaW9kLmVuZC50b0lTT1N0cmluZygpfSkuIEEgcmVwb3J0IGNvdmVycyB0aGUgbGFzdCBDT01QTEVURSBwZXJpb2QuYCxcbiAgICB9O1xuICB9XG4gIGlmIChvcHRzLm1pbkZyb21NcyA+IDApIHtcbiAgICAvLyBUaGUgY29tcGFyaXNvbiBwZXJpb2QgaXMgcGFydCBvZiB0aGUgcmVwb3J0IHdoZW4gYSBzZWN0aW9uIHNob3dzIGNoYW5nZSwgc29cbiAgICAvLyBpdHMgc3RhcnQgaXMgdGhlIHJlYWwgZmxvb3Ig4oCUIG90aGVyd2lzZSBcInZzIGxhc3Qgd2Vla1wiIHNpbGVudGx5IHJlYWRzIHplcm8uXG4gICAgY29uc3QgZWFybGllc3QgPSBvcHRzLmluY2x1ZGVQcmV2aW91cyA9PT0gZmFsc2UgPyBwZXJpb2Quc3RhcnQgOiBwZXJpb2QucHJldlN0YXJ0O1xuICAgIGlmIChlYXJsaWVzdC5nZXRUaW1lKCkgPCBvcHRzLm1pbkZyb21Ncykge1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgcmVhc29uOiAnYmVmb3JlX3JldGVudGlvbicsXG4gICAgICAgIG1lc3NhZ2U6IGBQZXJpb2QgJHtwZXJpb2QubGFiZWx9IHN0YXJ0cyAke2VhcmxpZXN0LnRvSVNPU3RyaW5nKCl9LCBiZWZvcmUgdGhpcyBvcmdhbml6YXRpb24ncyByZXBvcnRpbmcgcmV0ZW50aW9uIGhvcml6b24gKCR7bmV3IERhdGUob3B0cy5taW5Gcm9tTXMpLnRvSVNPU3RyaW5nKCl9KS4gVGhlIHVuZGVybHlpbmcgZXZlbnRzIGhhdmUgYmVlbiBwdXJnZWQuYCxcbiAgICAgIH07XG4gICAgfVxuICB9XG4gIHJldHVybiBudWxsO1xufVxuXG4vKipcbiAqIFRoZSBpbnN0YW50IHRoZSBwZXJpb2QgQ09OVEFJTklORyBgZnJvbWAgZW5kcyDigJQgdGhhdCBpcywgdGhlIG1vbWVudCB0aGUgbmV4dFxuICogY29tcGxldGUgcGVyaW9kIGJlY29tZXMgcmVwb3J0YWJsZS5cbiAqXG4gKiBUaGlzIGlzIHdoYXQgYSBkZWZpbml0aW9uJ3MgYG5leHRSdW5BdGAgaXMgZGVyaXZlZCBmcm9tLCBhbmQgaXQgaXMgZGVsaWJlcmF0ZWx5XG4gKiB0aGUgYm91bmRhcnkgcmF0aGVyIHRoYW4gXCJsYXN0IHJ1biArIDcgZGF5c1wiOiBhIHdlZWtseSByZXBvcnQgd2hvc2UgbmV4dCBydW4gaXNcbiAqIGNvbXB1dGVkIGJ5IGFkZGluZyBlbGFwc2VkIG1pbGxpc2Vjb25kcyBkcmlmdHMgYW4gaG91ciBhdCBldmVyeSBEU1QgdHJhbnNpdGlvblxuICogYW5kIGV2ZW50dWFsbHkgZmlyZXMgb24gYSBTdW5kYXkuIFRoZSBib3VuZGFyeSBpcyBhIGNhbGVuZGFyIGZhY3QgaW4gdGhlXG4gKiByZXBvcnQncyBvd24gdGltZXpvbmUsIHNvIGl0IGlzIHRoZSBzYW1lIGFuc3dlciB3aGF0ZXZlciB0aGUgcHJldmlvdXMgcnVuIGRpZC5cbiAqXG4gKiBTdHJpY3RseSBhZnRlciBgZnJvbWA6IGEgYGZyb21gIHNpdHRpbmcgZXhhY3RseSBvbiBhIGJvdW5kYXJ5IGdldHMgdGhlIE5FWFQgb25lLFxuICogc28gYSBydW4gdGhhdCBjb21wbGV0ZXMgYXQgdGhlIGluc3RhbnQgb2YgdGhlIGJvdW5kYXJ5IGNhbm5vdCByZS1jbGFpbSBpdC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIG5leHRQZXJpb2RCb3VuZGFyeShcbiAgY2FkZW5jZTogUmVwb3J0Q2FkZW5jZSxcbiAgdHo6IHN0cmluZyxcbiAgd2Vla1N0YXJ0OiAnbW9uZGF5JyB8ICdzdW5kYXknID0gJ21vbmRheScsXG4gIGZyb206IERhdGUgPSBuZXcgRGF0ZSgpLFxuKTogRGF0ZSB7XG4gIGNvbnN0IGNvbnRhaW5pbmcgPSBzdGFydE9mUGVyaW9kKGNpdmlsSW4oZnJvbSwgdHopLCBjYWRlbmNlLCB3ZWVrU3RhcnQpO1xuICByZXR1cm4gaW5zdGFudE9mQ2l2aWwoc2hpZnRQZXJpb2QoY29udGFpbmluZywgY2FkZW5jZSwgMSksIHR6KTtcbn1cblxuLyoqXG4gKiBFdmVyeSBDT01QTEVURSBwZXJpb2QgdGhhdCBlbmRlZCBhZnRlciBgc2luY2VgLCBvbGRlc3QgZmlyc3QuXG4gKlxuICogVGhpcyBpcyB0aGUgY2F0Y2gtdXAgbGlzdDogYSBzZXJ2aWNlIHRoYXQgd2FzIGRvd24gb3ZlciBhIHdlZWtlbmQsIGEgZGVmaW5pdGlvblxuICogY3JlYXRlZCB3aXRoIGEgYmFjay1kYXRlZCBgbmV4dFJ1bkF0YCwgb3IgYSBzY2hlZHVsZXIgZGlzYWJsZWQgYnkgaXRzIGtpbGwgc3dpdGNoXG4gKiBmb3IgYSBmb3J0bmlnaHQgYWxsIGxlYXZlIHBlcmlvZHMgdGhhdCB3ZXJlIG5ldmVyIHJlcG9ydGVkLiBSZXR1cm5pbmcgdGhlbSBpblxuICogb3JkZXIgbWF0dGVycyDigJQgYSBtYW5hZ2VyIHJlYWRpbmcgdGhyZWUgd2Vla2x5IHJlcG9ydHMgdGhhdCBhcnJpdmUgYXQgb25jZSBuZWVkc1xuICogdGhlbSB0byBtYWtlIHNlbnNlIHJlYWQgZG93bndhcmRzLlxuICpcbiAqIGBtYXhgIGJvdW5kcyB0aGUgd2Fsay4gQSBkZWZpbml0aW9uIHVudG91Y2hlZCBmb3IgdHdvIHllYXJzIG11c3Qgbm90IHByb2R1Y2UgMTA0XG4gKiB3ZWVrbHkgcmVwb3J0cyBpbiBvbmUgY3ljbGU7IHRoZSBjYXAgbWVhbnMgdGhlIG9sZGVzdCBhcmUgc2tpcHBlZCwgd2hpY2ggaXMgdGhlXG4gKiByaWdodCB0cmFkZSDigJQgbm9ib2R5IHdhbnRzIGxhc3QgQXByaWwncyB3ZWVrbHkgc3RhdHVzLCBhbmQgdGhlIHNuYXBzaG90IGZvciBpdFxuICogd291bGQgYmUgcGFzdCB0aGUgcmV0ZW50aW9uIGhvcml6b24gYW55d2F5LlxuICovXG5leHBvcnQgZnVuY3Rpb24gY29tcGxldGVQZXJpb2RzU2luY2UoXG4gIGNhZGVuY2U6IFJlcG9ydENhZGVuY2UsXG4gIHR6OiBzdHJpbmcsXG4gIHdlZWtTdGFydDogJ21vbmRheScgfCAnc3VuZGF5JyxcbiAgc2luY2U6IERhdGUgfCBudWxsLFxuICBub3c6IERhdGUgPSBuZXcgRGF0ZSgpLFxuICBtYXggPSA0LFxuKTogUmVzb2x2ZWRQZXJpb2RbXSB7XG4gIGNvbnN0IGxhdGVzdCA9IHJlc29sdmVQZXJpb2QoY2FkZW5jZSwgdHosIHdlZWtTdGFydCwgbm93KTtcbiAgaWYgKG1heCA8PSAwKSByZXR1cm4gW107XG4gIC8vIE5vIHByZXZpb3VzIHJ1bjogcmVwb3J0IHRoZSBsYXRlc3QgY29tcGxldGUgcGVyaW9kIG9ubHkuIEEgZGVmaW5pdGlvbiBjcmVhdGVkXG4gIC8vIHRvZGF5IGRvZXMgbm90IG93ZSBpdHMgb3duZXIgYSBiYWNrZmlsbCB0aGV5IG5ldmVyIGFza2VkIGZvci5cbiAgaWYgKCFzaW5jZSkgcmV0dXJuIFtsYXRlc3RdO1xuXG4gIGNvbnN0IG91dDogUmVzb2x2ZWRQZXJpb2RbXSA9IFtdO1xuICBjb25zdCBsYXRlc3RTdGFydCA9IHN0YXJ0T2ZQZXJpb2QoY2l2aWxJbihsYXRlc3Quc3RhcnQsIHR6KSwgY2FkZW5jZSwgd2Vla1N0YXJ0KTtcbiAgZm9yIChsZXQgYmFjayA9IDA7IGJhY2sgPCBtYXg7IGJhY2sgKz0gMSkge1xuICAgIGNvbnN0IHN0YXJ0ID0gc2hpZnRQZXJpb2QobGF0ZXN0U3RhcnQsIGNhZGVuY2UsIC1iYWNrKTtcbiAgICBjb25zdCBpbnN0YW50ID0gaW5zdGFudE9mQ2l2aWwoc3RhcnQsIHR6KTtcbiAgICAvLyBgc2luY2VgIGlzIHRoZSBsYXN0IHJ1bidzIHBlcmlvZCBFTkQgZm9yIGEgcnVuIHRoYXQgaGFwcGVuZWQsIHNvIGEgcGVyaW9kXG4gICAgLy8gd2hvc2UgZW5kIGlzIGF0IG9yIGJlZm9yZSBpdCBoYXMgYWxyZWFkeSBiZWVuIHJlcG9ydGVkLlxuICAgIGlmIChpbnN0YW50T2ZDaXZpbChzaGlmdFBlcmlvZChzdGFydCwgY2FkZW5jZSwgMSksIHR6KS5nZXRUaW1lKCkgPD0gc2luY2UuZ2V0VGltZSgpKSBicmVhaztcbiAgICBjb25zdCBwcmV2U3RhcnQgPSBzaGlmdFBlcmlvZChzdGFydCwgY2FkZW5jZSwgLTEpO1xuICAgIG91dC5wdXNoKHtcbiAgICAgIHN0YXJ0OiBpbnN0YW50LFxuICAgICAgZW5kOiBpbnN0YW50T2ZDaXZpbChzaGlmdFBlcmlvZChzdGFydCwgY2FkZW5jZSwgMSksIHR6KSxcbiAgICAgIHByZXZTdGFydDogaW5zdGFudE9mQ2l2aWwocHJldlN0YXJ0LCB0eiksXG4gICAgICBwcmV2RW5kOiBpbnN0YW50LFxuICAgICAgbGFiZWw6IGZvcm1hdExhYmVsKHN0YXJ0LCBjYWRlbmNlKSxcbiAgICB9KTtcbiAgfVxuICByZXR1cm4gb3V0LnJldmVyc2UoKTtcbn1cbiJdfQ==
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The snapshot, as one self-contained HTML document — the input the PDF renderer prints.
|
|
3
|
+
*
|
|
4
|
+
* WHY HTML AND NOT A PDF LIBRARY. A layout engine we would have to teach about page
|
|
5
|
+
* breaks, table widths and font metrics is a second renderer to maintain; Chromium
|
|
6
|
+
* already has one. So the pipeline is snapshot -> HTML -> `Page.printToPDF`, and this
|
|
7
|
+
* file owns the first arrow. It is pure: a string in, a string out, no I/O, no clock, no
|
|
8
|
+
* database. That is what makes the PDF's content testable without a browser.
|
|
9
|
+
*
|
|
10
|
+
* SELF-CONTAINED, AND THAT IS A SECURITY PROPERTY, not tidiness. No <script>, no
|
|
11
|
+
* stylesheet link, no web font, no image URL — everything is inline. The renderer loads
|
|
12
|
+
* this through `setContent` with the page offline, so a document that referenced anything
|
|
13
|
+
* external would either hang or quietly render without it. Stated here because the
|
|
14
|
+
* temptation to add a logo by URL will come up, and it would be the one change that gives
|
|
15
|
+
* the renderer a reason to talk to the network.
|
|
16
|
+
*
|
|
17
|
+
* EVERY INTERPOLATION IS ESCAPED. The snapshot carries org-controlled text — pipeline
|
|
18
|
+
* names, a lead's notes, a section title — into markup that a browser then parses. That
|
|
19
|
+
* is the same boundary as any XSS sink, and it does not stop mattering because the output
|
|
20
|
+
* is a PDF: unescaped notes could close a tag and rewrite the rest of the report, which is
|
|
21
|
+
* a document-forgery bug in a document people make decisions from. `esc()` is applied at
|
|
22
|
+
* every single insertion point and there is no "trusted" caller.
|
|
23
|
+
*
|
|
24
|
+
* NO COLOUR-ONLY MEANING, and no bare glyph carrying information (WCAG 1.4.1 / 1.1.1). A
|
|
25
|
+
* locked panel says "Not on your plan" in words; a trend says "down 3 points" in words
|
|
26
|
+
* rather than leaning on an arrow. There are no charts in a snapshot today — every section
|
|
27
|
+
* renders as a number, a word and a table row — so the plan's "alt text on charts" has
|
|
28
|
+
* nothing to attach to yet. The rule that replaces it is stricter and survives a chart
|
|
29
|
+
* being added later: if a reader with no colour vision, no images and a screen reader
|
|
30
|
+
* cannot get a fact out of this document, the fact is not in it.
|
|
31
|
+
*/
|
|
32
|
+
import type { ComposedSection, ReportSnapshot } from './compose.js';
|
|
33
|
+
/** What the renderer needs to produce one report document. */
|
|
34
|
+
export interface ReportHtmlInput {
|
|
35
|
+
/** The definition's name. Heads the document. */
|
|
36
|
+
title: string;
|
|
37
|
+
periodLabel: string;
|
|
38
|
+
periodStart: string;
|
|
39
|
+
periodEnd: string;
|
|
40
|
+
/** Regenerations are normal; a reader must be able to tell which one they hold. */
|
|
41
|
+
version: number;
|
|
42
|
+
publishedAt?: string | null;
|
|
43
|
+
/** The lead's own words. Rendered ABOVE the numbers — see the ordering note below. */
|
|
44
|
+
leadNotes?: string | null;
|
|
45
|
+
/**
|
|
46
|
+
* The AI-drafted executive summary, when the run has one and the caller is allowed to
|
|
47
|
+
* see it. Never fetched here: a caller that should not disclose it simply omits it.
|
|
48
|
+
*/
|
|
49
|
+
executiveSummary?: string | null;
|
|
50
|
+
snapshot: ReportSnapshot | null;
|
|
51
|
+
/** True when pipeline/project names were replaced — said in the document, not implied. */
|
|
52
|
+
namesRedacted?: boolean;
|
|
53
|
+
/** Shown in the footer of a shared copy, so a bookmarked link's death is not a surprise. */
|
|
54
|
+
expiresAt?: string | null;
|
|
55
|
+
/**
|
|
56
|
+
* Stamped in the footer as the moment the FILE was made, distinct from the snapshot's
|
|
57
|
+
* own `generatedAt`. Two dates because they answer different questions: "is this report
|
|
58
|
+
* current?" and "is this the copy I was sent?".
|
|
59
|
+
*/
|
|
60
|
+
renderedAt: string;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The trend, IN WORDS.
|
|
64
|
+
*
|
|
65
|
+
* "down 3 points" rather than a red arrow, because the arrow is the whole fact for a
|
|
66
|
+
* section whose headline barely moved, and an arrow is invisible to a screen reader and
|
|
67
|
+
* ambiguous in greyscale. `unknown` renders nothing at all rather than "flat" — a section
|
|
68
|
+
* with no comparable previous period has not held steady, we just cannot say.
|
|
69
|
+
*/
|
|
70
|
+
export declare function trendSentence(section: ComposedSection): string | null;
|
|
71
|
+
/**
|
|
72
|
+
* Render one report as a complete HTML document.
|
|
73
|
+
*
|
|
74
|
+
* ORDER IS MEANING, and it is the same order the shared web page uses: the lead's words,
|
|
75
|
+
* then the executive summary, then the numbers. A reader who meets the table first has
|
|
76
|
+
* already formed a view of the dip by the time they reach the sentence explaining it, so
|
|
77
|
+
* the context the data cannot supply has to come first. `a11y-stakeholder-reports.test.tsx`
|
|
78
|
+
* pins that ordering for the page; `report-html.test.ts` pins it here.
|
|
79
|
+
*
|
|
80
|
+
* A NULL SNAPSHOT STILL RENDERS. A run can be read before or without a snapshot (a failed
|
|
81
|
+
* compose, a draft), and answering an empty file would look like a broken download. The
|
|
82
|
+
* document says there is nothing to show instead.
|
|
83
|
+
*/
|
|
84
|
+
export declare function renderReportHtml(input: ReportHtmlInput): string;
|
|
85
|
+
/**
|
|
86
|
+
* The download's file name.
|
|
87
|
+
*
|
|
88
|
+
* Built from the period and the version rather than the run id, because the person saving
|
|
89
|
+
* it is filing it next to last month's, and `report-2026-W38-v2.pdf` sorts and reads while
|
|
90
|
+
* a uuid does not. Sanitizing is the ROUTE's job (`attachmentDisposition`) — this only
|
|
91
|
+
* chooses the words.
|
|
92
|
+
*/
|
|
93
|
+
export declare function reportFileName(title: string, periodLabel: string, version: number): string;
|