@pipeline-builder/pipeline-data 4.1.24 → 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.
Files changed (64) hide show
  1. package/lib/api/alert-destinations.d.ts +17 -0
  2. package/lib/api/alert-destinations.js +36 -0
  3. package/lib/api/reporting/analytics-queries.d.ts +274 -0
  4. package/lib/api/reporting/analytics-queries.js +598 -0
  5. package/lib/api/reporting/daily-rollup.d.ts +41 -0
  6. package/lib/api/reporting/daily-rollup.js +173 -0
  7. package/lib/api/reporting/deploy-rollup.d.ts +78 -0
  8. package/lib/api/reporting/deploy-rollup.js +203 -0
  9. package/lib/api/reporting/dora-scoring.d.ts +14 -0
  10. package/lib/api/reporting/dora-scoring.js +25 -1
  11. package/lib/api/reporting/dora.js +30 -6
  12. package/lib/api/reporting/failure-classifier.d.ts +54 -0
  13. package/lib/api/reporting/failure-classifier.js +179 -0
  14. package/lib/api/reporting/ingest.d.ts +2 -2
  15. package/lib/api/reporting/ingest.js +129 -4
  16. package/lib/api/reporting/last-deploy.d.ts +31 -0
  17. package/lib/api/reporting/last-deploy.js +93 -0
  18. package/lib/api/reporting/needs-attention.d.ts +93 -0
  19. package/lib/api/reporting/needs-attention.js +152 -0
  20. package/lib/api/reporting/plugin-exposure.d.ts +129 -0
  21. package/lib/api/reporting/plugin-exposure.js +312 -0
  22. package/lib/api/reporting/retention-sweep.js +9 -1
  23. package/lib/api/reporting/sql-helpers.d.ts +35 -2
  24. package/lib/api/reporting/sql-helpers.js +39 -1
  25. package/lib/api/reporting/stakeholder/compose.d.ts +115 -0
  26. package/lib/api/reporting/stakeholder/compose.js +234 -0
  27. package/lib/api/reporting/stakeholder/index.d.ts +14 -0
  28. package/lib/api/reporting/stakeholder/index.js +17 -0
  29. package/lib/api/reporting/stakeholder/period.d.ts +102 -0
  30. package/lib/api/reporting/stakeholder/period.js +273 -0
  31. package/lib/api/reporting/stakeholder/report-html.d.ts +93 -0
  32. package/lib/api/reporting/stakeholder/report-html.js +208 -0
  33. package/lib/api/reporting/stakeholder/sections.d.ts +117 -0
  34. package/lib/api/reporting/stakeholder/sections.js +249 -0
  35. package/lib/api/reporting/stakeholder/store.d.ts +450 -0
  36. package/lib/api/reporting/stakeholder/store.js +981 -0
  37. package/lib/api/reporting/stakeholder/templates.d.ts +21 -0
  38. package/lib/api/reporting/stakeholder/templates.js +104 -0
  39. package/lib/api/reporting/types.d.ts +73 -0
  40. package/lib/api/reporting/types.js +1 -1
  41. package/lib/api/reporting-service.d.ts +94 -5
  42. package/lib/api/reporting-service.js +169 -10
  43. package/lib/api/soft-delete-sweep.d.ts +11 -2
  44. package/lib/api/soft-delete-sweep.js +17 -6
  45. package/lib/database/drizzle-schema.d.ts +2263 -385
  46. package/lib/database/drizzle-schema.js +18 -7
  47. package/lib/database/index.d.ts +1 -1
  48. package/lib/database/index.js +4 -2
  49. package/lib/database/schema/compliance.d.ts +0 -682
  50. package/lib/database/schema/compliance.js +6 -66
  51. package/lib/database/schema/ecosystem.d.ts +20 -157
  52. package/lib/database/schema/ecosystem.js +23 -16
  53. package/lib/database/schema/pipeline.d.ts +200 -0
  54. package/lib/database/schema/pipeline.js +100 -1
  55. package/lib/database/schema/plugin.d.ts +20 -1
  56. package/lib/database/schema/plugin.js +13 -1
  57. package/lib/database/schema/reporting-analytics.d.ts +1212 -0
  58. package/lib/database/schema/reporting-analytics.js +250 -0
  59. package/lib/database/schema/reporting-stakeholder.d.ts +1405 -0
  60. package/lib/database/schema/reporting-stakeholder.js +251 -0
  61. package/lib/index.d.ts +10 -1
  62. package/lib/index.js +15 -1
  63. package/lib/testing/exports.json +67 -4
  64. 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;