@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,115 @@
1
+ /**
2
+ * The composer: turn a definition's section list into the snapshot a manager reads.
3
+ *
4
+ * Three things it does that the raw queries do not:
5
+ *
6
+ * - PERIOD-OVER-PERIOD. Every comparable section is run twice, for this period
7
+ * and the one before, so the snapshot carries "91% (down 3 points)". The trend
8
+ * is what a manager acts on; the absolute number alone rarely is.
9
+ * - LOCKED SECTIONS. A section whose feature the org lacks renders as locked
10
+ * instead of failing the run. A report that dies because one gated section was
11
+ * included is a support ticket; a locked panel is an upsell.
12
+ * - DATA-QUALITY NOTES. Missing deploy tags and tiny samples are stated, not
13
+ * silently rendered as zero. "0 deploys" and "we cannot see your deploys" look
14
+ * identical on a chart and mean opposite things — a manager reading the first
15
+ * when it is really the second draws exactly the wrong conclusion.
16
+ */
17
+ import type { ResolvedPeriod } from './period.js';
18
+ import { type SectionDataSource } from './sections.js';
19
+ /** Below this many observations a rate is noise, and says so. */
20
+ export declare const LOW_SAMPLE_THRESHOLD = 5;
21
+ /** Which way a metric moved. `flat` when the change rounds to nothing. */
22
+ export type TrendDirection = 'up' | 'down' | 'flat' | 'unknown';
23
+ /** A data-quality caveat attached to the whole snapshot. */
24
+ export interface DataQualityNote {
25
+ code: 'missing_deploy_tag' | 'missing_commit_data' | 'low_sample' | 'section_failed' | 'section_locked';
26
+ /** Manager-readable. Not a metric name and not a stack trace. */
27
+ message: string;
28
+ /** The section it concerns, when it is section-specific. */
29
+ sectionId?: string;
30
+ }
31
+ /** One composed section. */
32
+ export interface ComposedSection {
33
+ id: string;
34
+ title: string;
35
+ /** `locked` when the org lacks the feature; `failed` when the query threw. */
36
+ state: 'ok' | 'locked' | 'failed';
37
+ /** The feature needed, when locked — drives the upsell copy. */
38
+ requiresFeature?: string;
39
+ /** This period's raw result, exactly as the dashboard would show it. */
40
+ current?: unknown;
41
+ /** The previous period's result, for comparable sections. */
42
+ previous?: unknown;
43
+ /** The single number a manager reads first, when the section has one. */
44
+ headline?: {
45
+ label: string;
46
+ value: number;
47
+ unit?: string;
48
+ };
49
+ /** Change versus the previous period, for comparable sections. */
50
+ change?: {
51
+ absolute: number;
52
+ percent: number | null;
53
+ direction: TrendDirection;
54
+ };
55
+ }
56
+ /** The frozen output of one run. */
57
+ export interface ReportSnapshot {
58
+ /** Period bounds + label, copied in so the snapshot is self-describing. */
59
+ period: {
60
+ start: string;
61
+ end: string;
62
+ label: string;
63
+ };
64
+ previousPeriod: {
65
+ start: string;
66
+ end: string;
67
+ };
68
+ timezone: string;
69
+ weekStart: 'monday' | 'sunday';
70
+ sections: ComposedSection[];
71
+ notes: DataQualityNote[];
72
+ /**
73
+ * How every number here was produced, shown in the report footer. Managers
74
+ * compare reports to the dashboard; saying plainly that both run the same
75
+ * queries over the same window is what makes a difference investigable rather
76
+ * than a reason to distrust both.
77
+ */
78
+ methodology: string;
79
+ generatedAt: string;
80
+ }
81
+ /** What the composer needs to know about the org's entitlements. */
82
+ export interface ComposeOptions {
83
+ source: SectionDataSource;
84
+ period: ResolvedPeriod;
85
+ timezone: string;
86
+ weekStart: 'monday' | 'sunday';
87
+ orgId: string;
88
+ orgIds?: string[];
89
+ /** Features the org holds. A section needing one that is absent renders locked. */
90
+ features: readonly string[];
91
+ now?: Date;
92
+ }
93
+ /**
94
+ * Features that, inside a REPORT, stand in for another.
95
+ *
96
+ * Buying Stakeholder Reports buys the DORA SECTIONS of a report. It does not buy the
97
+ * live DORA dashboard, which stays behind `advanced_reporting` and is gated on its own
98
+ * routes — so this substitution is deliberately scoped to composition and exists
99
+ * nowhere else. Without it, a Pro customer who bought the reports add-on would open
100
+ * their first weekly report and find its headline panels locked behind a second
101
+ * purchase, which is not what the add-on's own description promises.
102
+ *
103
+ * Stated as data rather than an `if`, so adding a section that needs a different
104
+ * feature cannot silently inherit the carve-out.
105
+ */
106
+ export declare const REPORT_FEATURE_SUBSTITUTES: Readonly<Record<string, readonly string[]>>;
107
+ /**
108
+ * Compose one period's snapshot.
109
+ *
110
+ * Never throws for a section-level problem: a section that fails is marked
111
+ * `failed` with a note, and the rest of the report still reaches the lead. A run
112
+ * that produces nine good sections and one failure is useful; a run that produces
113
+ * an exception is not.
114
+ */
115
+ export declare function composeSnapshot(sectionIdList: readonly string[], opts: ComposeOptions): Promise<ReportSnapshot>;
@@ -0,0 +1,234 @@
1
+ // Copyright 2026 Pipeline Builder Contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { getSection } from './sections.js';
4
+ /** Below this many observations a rate is noise, and says so. */
5
+ export const LOW_SAMPLE_THRESHOLD = 5;
6
+ /**
7
+ * Features that, inside a REPORT, stand in for another.
8
+ *
9
+ * Buying Stakeholder Reports buys the DORA SECTIONS of a report. It does not buy the
10
+ * live DORA dashboard, which stays behind `advanced_reporting` and is gated on its own
11
+ * routes — so this substitution is deliberately scoped to composition and exists
12
+ * nowhere else. Without it, a Pro customer who bought the reports add-on would open
13
+ * their first weekly report and find its headline panels locked behind a second
14
+ * purchase, which is not what the add-on's own description promises.
15
+ *
16
+ * Stated as data rather than an `if`, so adding a section that needs a different
17
+ * feature cannot silently inherit the carve-out.
18
+ */
19
+ export const REPORT_FEATURE_SUBSTITUTES = Object.freeze({
20
+ advanced_reporting: ['stakeholder_reports'],
21
+ });
22
+ /** Does the org hold `required`, or something that stands in for it in a report? */
23
+ function satisfies(features, required) {
24
+ if (features.includes(required))
25
+ return true;
26
+ return (REPORT_FEATURE_SUBSTITUTES[required] ?? []).some((alt) => features.includes(alt));
27
+ }
28
+ /** Pull a comparable number out of a section result, or null when there isn't one. */
29
+ function headlineOf(sectionId, value) {
30
+ if (value === null || value === undefined)
31
+ return null;
32
+ // Time series (success rate, build success): the LAST bucket is "now".
33
+ if (Array.isArray(value) && value.length > 0) {
34
+ const last = value[value.length - 1];
35
+ if (typeof last?.success_pct === 'number')
36
+ return { label: 'Success rate', value: last.success_pct, unit: '%' };
37
+ // Execution counts: total runs across pipelines.
38
+ //
39
+ // A total of ZERO is a real headline, not missing data: "0 deploys, down from
40
+ // 5" is the single most important thing a weekly report can tell a manager,
41
+ // and treating 0 as "no value" made exactly that case disappear — the change
42
+ // came back `unknown` and no arrow was shown. So the test is whether any row
43
+ // CARRIED a count, not whether the sum is positive.
44
+ let counted = false;
45
+ const total = value.reduce((sum, row) => {
46
+ const n = row.total ?? row.count ?? row.executions;
47
+ if (typeof n !== 'number')
48
+ return sum;
49
+ counted = true;
50
+ return sum + n;
51
+ }, 0);
52
+ if (counted)
53
+ return { label: 'Total', value: total };
54
+ return null;
55
+ }
56
+ const obj = value;
57
+ // DORA: deployment frequency is the headline a manager recognises.
58
+ const envs = obj.environments;
59
+ if (Array.isArray(envs) && envs.length > 0) {
60
+ const first = envs[0];
61
+ const df = first.deploymentFrequency?.deployments;
62
+ if (typeof df === 'number')
63
+ return { label: 'Deploys', value: df };
64
+ }
65
+ // Plugin summary and friends: a plain total.
66
+ // Zero is a value here too — `plugin_versions` reporting 0 outdated plugins is
67
+ // good news a manager should see, not an absent section.
68
+ for (const key of ['total', 'count', 'deployments']) {
69
+ if (typeof obj[key] === 'number')
70
+ return { label: sectionId === 'plugin_versions' ? 'Outdated' : 'Total', value: obj[key] };
71
+ }
72
+ return null;
73
+ }
74
+ /** Change between two headline numbers. */
75
+ function changeOf(current, previous) {
76
+ if (!current || !previous)
77
+ return { absolute: 0, percent: null, direction: 'unknown' };
78
+ const absolute = Number((current.value - previous.value).toFixed(2));
79
+ // A percent change off a zero baseline is undefined, not infinite: going from 0
80
+ // to 5 deploys is "5 more", never "+∞%".
81
+ const percent = previous.value === 0 ? null : Number(((absolute / previous.value) * 100).toFixed(1));
82
+ const direction = absolute === 0 ? 'flat' : absolute > 0 ? 'up' : 'down';
83
+ return { absolute, percent, direction };
84
+ }
85
+ /** Count the observations behind a section, for the low-sample note. */
86
+ function sampleSize(value) {
87
+ if (Array.isArray(value)) {
88
+ return value.reduce((sum, row) => {
89
+ const n = row.total ?? row.count ?? row.executions ?? row.deployments;
90
+ return sum + (typeof n === 'number' ? n : 0);
91
+ }, 0);
92
+ }
93
+ const obj = value;
94
+ const envs = obj?.environments;
95
+ if (Array.isArray(envs)) {
96
+ return envs.reduce((sum, e) => {
97
+ const attempts = e.changeFailureRate?.attempts;
98
+ return sum + (typeof attempts === 'number' ? attempts : 0);
99
+ }, 0);
100
+ }
101
+ return null;
102
+ }
103
+ /** Deploy-coverage notes: the difference between "zero deploys" and "blind". */
104
+ function deployCoverageNotes(dora) {
105
+ const notes = [];
106
+ const coverage = dora?.coverage;
107
+ const without = coverage?.withoutDeploys;
108
+ if (typeof without === 'number' && without > 0) {
109
+ notes.push({
110
+ code: 'missing_deploy_tag',
111
+ sectionId: 'dora',
112
+ message: `Deploy data unavailable for ${without} pipeline${without === 1 ? '' : 's'}: they have not re-synthed with deploy tags, so their deploys are not counted here. This is not "zero deploys".`,
113
+ });
114
+ }
115
+ const envs = dora?.environments;
116
+ if (Array.isArray(envs)) {
117
+ const noLead = envs.filter((e) => {
118
+ const lt = e.leadTime;
119
+ return lt && lt.medianSeconds === null;
120
+ });
121
+ if (noLead.length > 0) {
122
+ notes.push({
123
+ code: 'missing_commit_data',
124
+ sectionId: 'dora',
125
+ message: `Lead time unavailable for ${noLead.length} environment${noLead.length === 1 ? '' : 's'}: no successful deploy carried a resolvable commit timestamp, so commit-to-deploy time could not be measured.`,
126
+ });
127
+ }
128
+ }
129
+ return notes;
130
+ }
131
+ /**
132
+ * Compose one period's snapshot.
133
+ *
134
+ * Never throws for a section-level problem: a section that fails is marked
135
+ * `failed` with a note, and the rest of the report still reaches the lead. A run
136
+ * that produces nine good sections and one failure is useful; a run that produces
137
+ * an exception is not.
138
+ */
139
+ export async function composeSnapshot(sectionIdList, opts) {
140
+ const { source, period, timezone, weekStart, orgId, orgIds, features } = opts;
141
+ const notes = [];
142
+ const sections = [];
143
+ const ctxFor = (from, to) => ({
144
+ orgId,
145
+ ...(orgIds ? { orgIds } : {}),
146
+ from: from.toISOString(),
147
+ to: to.toISOString(),
148
+ tz: timezone,
149
+ weekStart,
150
+ // The previous period travels WITH the context, for the analytics sections
151
+ // whose trend is per category, per stage or per pipeline. Those cannot be
152
+ // handled by running the section twice and comparing one headline — the
153
+ // comparison is a join, and doing it in the composer afterwards would mean
154
+ // re-implementing it for every shape.
155
+ previousFrom: period.prevStart.toISOString(),
156
+ previousTo: period.prevEnd.toISOString(),
157
+ });
158
+ const currentCtx = ctxFor(period.start, period.end);
159
+ const previousCtx = ctxFor(period.prevStart, period.prevEnd);
160
+ for (const id of sectionIdList) {
161
+ const spec = getSection(id);
162
+ if (!spec) {
163
+ // An unknown id is a definition referencing a section that no longer exists.
164
+ // Recorded rather than dropped, so the lead can see why a panel vanished.
165
+ sections.push({ id, title: id, state: 'failed' });
166
+ notes.push({ code: 'section_failed', sectionId: id, message: `Unknown report section "${id}" — it may have been removed.` });
167
+ continue;
168
+ }
169
+ if (spec.requiresFeature && !satisfies(features, spec.requiresFeature)) {
170
+ sections.push({
171
+ id, title: spec.title, state: 'locked', requiresFeature: spec.requiresFeature,
172
+ });
173
+ notes.push({
174
+ code: 'section_locked',
175
+ sectionId: id,
176
+ message: `"${spec.title}" needs the ${spec.requiresFeature} add-on. The rest of this report is unaffected.`,
177
+ });
178
+ continue;
179
+ }
180
+ try {
181
+ const current = await spec.run(source, currentCtx);
182
+ const composed = { id, title: spec.title, state: 'ok', current };
183
+ const head = headlineOf(id, current);
184
+ if (head)
185
+ composed.headline = head;
186
+ if (spec.comparable) {
187
+ // The comparison is best-effort: a failed previous period must not cost the
188
+ // lead this period's numbers.
189
+ try {
190
+ const previous = await spec.run(source, previousCtx);
191
+ composed.previous = previous;
192
+ composed.change = changeOf(head, headlineOf(id, previous));
193
+ }
194
+ catch {
195
+ composed.change = { absolute: 0, percent: null, direction: 'unknown' };
196
+ }
197
+ }
198
+ const sample = sampleSize(current);
199
+ if (sample !== null && sample > 0 && sample < LOW_SAMPLE_THRESHOLD) {
200
+ notes.push({
201
+ code: 'low_sample',
202
+ sectionId: id,
203
+ message: `"${spec.title}" is based on ${sample} observation${sample === 1 ? '' : 's'} — percentages swing wildly at this volume. Read the counts, not the rate.`,
204
+ });
205
+ }
206
+ if (id === 'dora')
207
+ notes.push(...deployCoverageNotes(current));
208
+ sections.push(composed);
209
+ }
210
+ catch (err) {
211
+ sections.push({ id, title: spec.title, state: 'failed' });
212
+ notes.push({
213
+ code: 'section_failed',
214
+ sectionId: id,
215
+ message: `"${spec.title}" could not be computed for this period. The rest of the report is unaffected.`,
216
+ });
217
+ void err; // the reason is logged by the caller, never shown to a manager
218
+ }
219
+ }
220
+ return {
221
+ period: { start: period.start.toISOString(), end: period.end.toISOString(), label: period.label },
222
+ previousPeriod: { start: period.prevStart.toISOString(), end: period.prevEnd.toISOString() },
223
+ timezone,
224
+ weekStart,
225
+ sections,
226
+ notes,
227
+ methodology: `Computed from pipeline events between ${period.start.toISOString()} and ${period.end.toISOString()}, `
228
+ + `bucketed in ${timezone} with weeks starting ${weekStart}. Comparison period: `
229
+ + `${period.prevStart.toISOString()} to ${period.prevEnd.toISOString()}. `
230
+ + 'These are the same queries the dashboard runs over the same window, so the two agree.',
231
+ generatedAt: (opts.now ?? new Date()).toISOString(),
232
+ };
233
+ }
234
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29tcG9zZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9hcGkvcmVwb3J0aW5nL3N0YWtlaG9sZGVyL2NvbXBvc2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsK0NBQStDO0FBQy9DLHNDQUFzQztBQW9CdEMsT0FBTyxFQUFFLFVBQVUsRUFBaUUsTUFBTSxlQUFlLENBQUM7QUFFMUcsaUVBQWlFO0FBQ2pFLE1BQU0sQ0FBQyxNQUFNLG9CQUFvQixHQUFHLENBQUMsQ0FBQztBQWdFdEM7Ozs7Ozs7Ozs7OztHQVlHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sMEJBQTBCLEdBQWdELE1BQU0sQ0FBQyxNQUFNLENBQUM7SUFDbkcsa0JBQWtCLEVBQUUsQ0FBQyxxQkFBcUIsQ0FBQztDQUM1QyxDQUFDLENBQUM7QUFFSCxvRkFBb0Y7QUFDcEYsU0FBUyxTQUFTLENBQUMsUUFBMkIsRUFBRSxRQUFnQjtJQUM5RCxJQUFJLFFBQVEsQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDO1FBQUUsT0FBTyxJQUFJLENBQUM7SUFDN0MsT0FBTyxDQUFDLDBCQUEwQixDQUFDLFFBQVEsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLEdBQUcsRUFBRSxFQUFFLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO0FBQzVGLENBQUM7QUFFRCxzRkFBc0Y7QUFDdEYsU0FBUyxVQUFVLENBQUMsU0FBaUIsRUFBRSxLQUFjO0lBQ25ELElBQUksS0FBSyxLQUFLLElBQUksSUFBSSxLQUFLLEtBQUssU0FBUztRQUFFLE9BQU8sSUFBSSxDQUFDO0lBRXZELHVFQUF1RTtJQUN2RSxJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLElBQUksS0FBSyxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztRQUM3QyxNQUFNLElBQUksR0FBRyxLQUFLLENBQUMsS0FBSyxDQUFDLE1BQU0s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TSxDQUFDLEtBQUssQ0FBQyxXQUFXLEVBQUUsUUFBUSxNQUFNLENBQUMsR0FBRyxDQUFDLFdBQVcsRUFBRSxJQUFJO2NBQ3JHLGVBQWUsUUFBUSx3QkFBd0IsU0FBUyx1QkFBdUI7Y0FDL0UsR0FBRyxNQUFNLENBQUMsU0FBUyxDQUFDLFdBQVcsRUFBRSxPQUFPLE1BQU0sQ0FBQyxPQUFPLENBQUMsV0FBVyxFQUFFLElBQUk7Y0FDeEUsdUZBQXVGO1FBQzNGLFdBQVcsRUFBRSxDQUFDLElBQUksQ0FBQyxHQUFHLElBQUksSUFBSSxJQUFJLEVBQUUsQ0FBQyxDQUFDLFdBQVcsRUFBRTtLQUNwRCxDQUFDO0FBQ0osQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCAyMDI2IFBpcGVsaW5lIEJ1aWxkZXIgQ29udHJpYnV0b3JzXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG4vKipcbiAqIFRoZSBjb21wb3NlcjogdHVybiBhIGRlZmluaXRpb24ncyBzZWN0aW9uIGxpc3QgaW50byB0aGUgc25hcHNob3QgYSBtYW5hZ2VyIHJlYWRzLlxuICpcbiAqIFRocmVlIHRoaW5ncyBpdCBkb2VzIHRoYXQgdGhlIHJhdyBxdWVyaWVzIGRvIG5vdDpcbiAqXG4gKiAgLSBQRVJJT0QtT1ZFUi1QRVJJT0QuIEV2ZXJ5IGNvbXBhcmFibGUgc2VjdGlvbiBpcyBydW4gdHdpY2UsIGZvciB0aGlzIHBlcmlvZFxuICogICAgYW5kIHRoZSBvbmUgYmVmb3JlLCBzbyB0aGUgc25hcHNob3QgY2FycmllcyBcIjkxJSAoZG93biAzIHBvaW50cylcIi4gVGhlIHRyZW5kXG4gKiAgICBpcyB3aGF0IGEgbWFuYWdlciBhY3RzIG9uOyB0aGUgYWJzb2x1dGUgbnVtYmVyIGFsb25lIHJhcmVseSBpcy5cbiAqICAtIExPQ0tFRCBTRUNUSU9OUy4gQSBzZWN0aW9uIHdob3NlIGZlYXR1cmUgdGhlIG9yZyBsYWNrcyByZW5kZXJzIGFzIGxvY2tlZFxuICogICAgaW5zdGVhZCBvZiBmYWlsaW5nIHRoZSBydW4uIEEgcmVwb3J0IHRoYXQgZGllcyBiZWNhdXNlIG9uZSBnYXRlZCBzZWN0aW9uIHdhc1xuICogICAgaW5jbHVkZWQgaXMgYSBzdXBwb3J0IHRpY2tldDsgYSBsb2NrZWQgcGFuZWwgaXMgYW4gdXBzZWxsLlxuICogIC0gREFUQS1RVUFMSVRZIE5PVEVTLiBNaXNzaW5nIGRlcGxveSB0YWdzIGFuZCB0aW55IHNhbXBsZXMgYXJlIHN0YXRlZCwgbm90XG4gKiAgICBzaWxlbnRseSByZW5kZXJlZCBhcyB6ZXJvLiBcIjAgZGVwbG95c1wiIGFuZCBcIndlIGNhbm5vdCBzZWUgeW91ciBkZXBsb3lzXCIgbG9va1xuICogICAgaWRlbnRpY2FsIG9uIGEgY2hhcnQgYW5kIG1lYW4gb3Bwb3NpdGUgdGhpbmdzIOKAlCBhIG1hbmFnZXIgcmVhZGluZyB0aGUgZmlyc3RcbiAqICAgIHdoZW4gaXQgaXMgcmVhbGx5IHRoZSBzZWNvbmQgZHJhd3MgZXhhY3RseSB0aGUgd3JvbmcgY29uY2x1c2lvbi5cbiAqL1xuXG5pbXBvcnQgdHlwZSB7IFJlc29sdmVkUGVyaW9kIH0gZnJvbSAnLi9wZXJpb2QuanMnO1xuaW1wb3J0IHsgZ2V0U2VjdGlvbiwgdHlwZSBTZWN0aW9uQ29udGV4dCwgdHlwZSBTZWN0aW9uRGF0YVNvdXJjZSwgdHlwZSBTZWN0aW9uU3BlYyB9IGZyb20gJy4vc2VjdGlvbnMuanMnO1xuXG4vKiogQmVsb3cgdGhpcyBtYW55IG9ic2VydmF0aW9ucyBhIHJhdGUgaXMgbm9pc2UsIGFuZCBzYXlzIHNvLiAqL1xuZXhwb3J0IGNvbnN0IExPV19TQU1QTEVfVEhSRVNIT0xEID0gNTtcblxuLyoqIFdoaWNoIHdheSBhIG1ldHJpYyBtb3ZlZC4gYGZsYXRgIHdoZW4gdGhlIGNoYW5nZSByb3VuZHMgdG8gbm90aGluZy4gKi9cbmV4cG9ydCB0eXBlIFRyZW5kRGlyZWN0aW9uID0gJ3VwJyB8ICdkb3duJyB8ICdmbGF0JyB8ICd1bmtub3duJztcblxuLyoqIEEgZGF0YS1xdWFsaXR5IGNhdmVhdCBhdHRhY2hlZCB0byB0aGUgd2hvbGUgc25hcHNob3QuICovXG5leHBvcnQgaW50ZXJmYWNlIERhdGFRdWFsaXR5Tm90ZSB7XG4gIGNvZGU6ICdtaXNzaW5nX2RlcGxveV90YWcnIHwgJ21pc3NpbmdfY29tbWl0X2RhdGEnIHwgJ2xvd19zYW1wbGUnIHwgJ3NlY3Rpb25fZmFpbGVkJyB8ICdzZWN0aW9uX2xvY2tlZCc7XG4gIC8qKiBNYW5hZ2VyLXJlYWRhYmxlLiBOb3QgYSBtZXRyaWMgbmFtZSBhbmQgbm90IGEgc3RhY2sgdHJhY2UuICovXG4gIG1lc3NhZ2U6IHN0cmluZztcbiAgLyoqIFRoZSBzZWN0aW9uIGl0IGNvbmNlcm5zLCB3aGVuIGl0IGlzIHNlY3Rpb24tc3BlY2lmaWMuICovXG4gIHNlY3Rpb25JZD86IHN0cmluZztcbn1cblxuLyoqIE9uZSBjb21wb3NlZCBzZWN0aW9uLiAqL1xuZXhwb3J0IGludGVyZmFjZSBDb21wb3NlZFNlY3Rpb24ge1xuICBpZDogc3RyaW5nO1xuICB0aXRsZTogc3RyaW5nO1xuICAvKiogYGxvY2tlZGAgd2hlbiB0aGUgb3JnIGxhY2tzIHRoZSBmZWF0dXJlOyBgZmFpbGVkYCB3aGVuIHRoZSBxdWVyeSB0aHJldy4gKi9cbiAgc3RhdGU6ICdvaycgfCAnbG9ja2VkJyB8ICdmYWlsZWQnO1xuICAvKiogVGhlIGZlYXR1cmUgbmVlZGVkLCB3aGVuIGxvY2tlZCDigJQgZHJpdmVzIHRoZSB1cHNlbGwgY29weS4gKi9cbiAgcmVxdWlyZXNGZWF0dXJlPzogc3RyaW5nO1xuICAvKiogVGhpcyBwZXJpb2QncyByYXcgcmVzdWx0LCBleGFjdGx5IGFzIHRoZSBkYXNoYm9hcmQgd291bGQgc2hvdyBpdC4gKi9cbiAgY3VycmVudD86IHVua25vd247XG4gIC8qKiBUaGUgcHJldmlvdXMgcGVyaW9kJ3MgcmVzdWx0LCBmb3IgY29tcGFyYWJsZSBzZWN0aW9ucy4gKi9cbiAgcHJldmlvdXM/OiB1bmtub3duO1xuICAvKiogVGhlIHNpbmdsZSBudW1iZXIgYSBtYW5hZ2VyIHJlYWRzIGZpcnN0LCB3aGVuIHRoZSBzZWN0aW9uIGhhcyBvbmUuICovXG4gIGhlYWRsaW5lPzogeyBsYWJlbDogc3RyaW5nOyB2YWx1ZTogbnVtYmVyOyB1bml0Pzogc3RyaW5nIH07XG4gIC8qKiBDaGFuZ2UgdmVyc3VzIHRoZSBwcmV2aW91cyBwZXJpb2QsIGZvciBjb21wYXJhYmxlIHNlY3Rpb25zLiAqL1xuICBjaGFuZ2U/OiB7IGFic29sdXRlOiBudW1iZXI7IHBlcmNlbnQ6IG51bWJlciB8IG51bGw7IGRpcmVjdGlvbjogVHJlbmREaXJlY3Rpb24gfTtcbn1cblxuLyoqIFRoZSBmcm96ZW4gb3V0cHV0IG9mIG9uZSBydW4uICovXG5leHBvcnQgaW50ZXJmYWNlIFJlcG9ydFNuYXBzaG90IHtcbiAgLyoqIFBlcmlvZCBib3VuZHMgKyBsYWJlbCwgY29waWVkIGluIHNvIHRoZSBzbmFwc2hvdCBpcyBzZWxmLWRlc2NyaWJpbmcuICovXG4gIHBlcmlvZDogeyBzdGFydDogc3RyaW5nOyBlbmQ6IHN0cmluZzsgbGFiZWw6IHN0cmluZyB9O1xuICBwcmV2aW91c1BlcmlvZDogeyBzdGFydDogc3RyaW5nOyBlbmQ6IHN0cmluZyB9O1xuICB0aW1lem9uZTogc3RyaW5nO1xuICB3ZWVrU3RhcnQ6ICdtb25kYXknIHwgJ3N1bmRheSc7XG4gIHNlY3Rpb25zOiBDb21wb3NlZFNlY3Rpb25bXTtcbiAgbm90ZXM6IERhdGFRdWFsaXR5Tm90ZVtdO1xuICAvKipcbiAgICogSG93IGV2ZXJ5IG51bWJlciBoZXJlIHdhcyBwcm9kdWNlZCwgc2hvd24gaW4gdGhlIHJlcG9ydCBmb290ZXIuIE1hbmFnZXJzXG4gICAqIGNvbXBhcmUgcmVwb3J0cyB0byB0aGUgZGFzaGJvYXJkOyBzYXlpbmcgcGxhaW5seSB0aGF0IGJvdGggcnVuIHRoZSBzYW1lXG4gICAqIHF1ZXJpZXMgb3ZlciB0aGUgc2FtZSB3aW5kb3cgaXMgd2hhdCBtYWtlcyBhIGRpZmZlcmVuY2UgaW52ZXN0aWdhYmxlIHJhdGhlclxuICAgKiB0aGFuIGEgcmVhc29uIHRvIGRpc3RydXN0IGJvdGguXG4gICAqL1xuICBtZXRob2RvbG9neTogc3RyaW5nO1xuICBnZW5lcmF0ZWRBdDogc3RyaW5nO1xufVxuXG4vKiogV2hhdCB0aGUgY29tcG9zZXIgbmVlZHMgdG8ga25vdyBhYm91dCB0aGUgb3JnJ3MgZW50aXRsZW1lbnRzLiAqL1xuZXhwb3J0IGludGVyZmFjZSBDb21wb3NlT3B0aW9ucyB7XG4gIHNvdXJjZTogU2VjdGlvbkRhdGFTb3VyY2U7XG4gIHBlcmlvZDogUmVzb2x2ZWRQZXJpb2Q7XG4gIHRpbWV6b25lOiBzdHJpbmc7XG4gIHdlZWtTdGFydDogJ21vbmRheScgfCAnc3VuZGF5JztcbiAgb3JnSWQ6IHN0cmluZztcbiAgb3JnSWRzPzogc3RyaW5nW107XG4gIC8qKiBGZWF0dXJlcyB0aGUgb3JnIGhvbGRzLiBBIHNlY3Rpb24gbmVlZGluZyBvbmUgdGhhdCBpcyBhYnNlbnQgcmVuZGVycyBsb2NrZWQuICovXG4gIGZlYXR1cmVzOiByZWFkb25seSBzdHJpbmdbXTtcbiAgbm93PzogRGF0ZTtcbn1cblxuLyoqXG4gKiBGZWF0dXJlcyB0aGF0LCBpbnNpZGUgYSBSRVBPUlQsIHN0YW5kIGluIGZvciBhbm90aGVyLlxuICpcbiAqIEJ1eWluZyBTdGFrZWhvbGRlciBSZXBvcnRzIGJ1eXMgdGhlIERPUkEgU0VDVElPTlMgb2YgYSByZXBvcnQuIEl0IGRvZXMgbm90IGJ1eSB0aGVcbiAqIGxpdmUgRE9SQSBkYXNoYm9hcmQsIHdoaWNoIHN0YXlzIGJlaGluZCBgYWR2YW5jZWRfcmVwb3J0aW5nYCBhbmQgaXMgZ2F0ZWQgb24gaXRzIG93blxuICogcm91dGVzIOKAlCBzbyB0aGlzIHN1YnN0aXR1dGlvbiBpcyBkZWxpYmVyYXRlbHkgc2NvcGVkIHRvIGNvbXBvc2l0aW9uIGFuZCBleGlzdHNcbiAqIG5vd2hlcmUgZWxzZS4gV2l0aG91dCBpdCwgYSBQcm8gY3VzdG9tZXIgd2hvIGJvdWdodCB0aGUgcmVwb3J0cyBhZGQtb24gd291bGQgb3BlblxuICogdGhlaXIgZmlyc3Qgd2Vla2x5IHJlcG9ydCBhbmQgZmluZCBpdHMgaGVhZGxpbmUgcGFuZWxzIGxvY2tlZCBiZWhpbmQgYSBzZWNvbmRcbiAqIHB1cmNoYXNlLCB3aGljaCBpcyBub3Qgd2hhdCB0aGUgYWRkLW9uJ3Mgb3duIGRlc2NyaXB0aW9uIHByb21pc2VzLlxuICpcbiAqIFN0YXRlZCBhcyBkYXRhIHJhdGhlciB0aGFuIGFuIGBpZmAsIHNvIGFkZGluZyBhIHNlY3Rpb24gdGhhdCBuZWVkcyBhIGRpZmZlcmVudFxuICogZmVhdHVyZSBjYW5ub3Qgc2lsZW50bHkgaW5oZXJpdCB0aGUgY2FydmUtb3V0LlxuICovXG5leHBvcnQgY29uc3QgUkVQT1JUX0ZFQVRVUkVfU1VCU1RJVFVURVM6IFJlYWRvbmx5PFJlY29yZDxzdHJpbmcsIHJlYWRvbmx5IHN0cmluZ1tdPj4gPSBPYmplY3QuZnJlZXplKHtcbiAgYWR2YW5jZWRfcmVwb3J0aW5nOiBbJ3N0YWtlaG9sZGVyX3JlcG9ydHMnXSxcbn0pO1xuXG4vKiogRG9lcyB0aGUgb3JnIGhvbGQgYHJlcXVpcmVkYCwgb3Igc29tZXRoaW5nIHRoYXQgc3RhbmRzIGluIGZvciBpdCBpbiBhIHJlcG9ydD8gKi9cbmZ1bmN0aW9uIHNhdGlzZmllcyhmZWF0dXJlczogcmVhZG9ubHkgc3RyaW5nW10sIHJlcXVpcmVkOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgaWYgKGZlYXR1cmVzLmluY2x1ZGVzKHJlcXVpcmVkKSkgcmV0dXJuIHRydWU7XG4gIHJldHVybiAoUkVQT1JUX0ZFQVRVUkVfU1VCU1RJVFVURVNbcmVxdWlyZWRdID8/IFtdKS5zb21lKChhbHQpID0+IGZlYXR1cmVzLmluY2x1ZGVzKGFsdCkpO1xufVxuXG4vKiogUHVsbCBhIGNvbXBhcmFibGUgbnVtYmVyIG91dCBvZiBhIHNlY3Rpb24gcmVzdWx0LCBvciBudWxsIHdoZW4gdGhlcmUgaXNuJ3Qgb25lLiAqL1xuZnVuY3Rpb24gaGVhZGxpbmVPZihzZWN0aW9uSWQ6IHN0cmluZywgdmFsdWU6IHVua25vd24pOiB7IGxhYmVsOiBzdHJpbmc7IHZhbHVlOiBudW1iZXI7IHVuaXQ/OiBzdHJpbmcgfSB8IG51bGwge1xuICBpZiAodmFsdWUgPT09IG51bGwgfHwgdmFsdWUgPT09IHVuZGVmaW5lZCkgcmV0dXJuIG51bGw7XG5cbiAgLy8gVGltZSBzZXJpZXMgKHN1Y2Nlc3MgcmF0ZSwgYnVpbGQgc3VjY2Vzcyk6IHRoZSBMQVNUIGJ1Y2tldCBpcyBcIm5vd1wiLlxuICBpZiAoQXJyYXkuaXNBcnJheSh2YWx1ZSkgJiYgdmFsdWUubGVuZ3RoID4gMCkge1xuICAgIGNvbnN0IGxhc3QgPSB2YWx1ZVt2YWx1ZS5sZW5ndGggLSAxXSBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPjtcbiAgICBpZiAodHlwZW9mIGxhc3Q/LnN1Y2Nlc3NfcGN0ID09PSAnbnVtYmVyJykgcmV0dXJuIHsgbGFiZWw6ICdTdWNjZXNzIHJhdGUnLCB2YWx1ZTogbGFzdC5zdWNjZXNzX3BjdCwgdW5pdDogJyUnIH07XG4gICAgLy8gRXhlY3V0aW9uIGNvdW50czogdG90YWwgcnVucyBhY3Jvc3MgcGlwZWxpbmVzLlxuICAgIC8vXG4gICAgLy8gQSB0b3RhbCBvZiBaRVJPIGlzIGEgcmVhbCBoZWFkbGluZSwgbm90IG1pc3NpbmcgZGF0YTogXCIwIGRlcGxveXMsIGRvd24gZnJvbVxuICAgIC8vIDVcIiBpcyB0aGUgc2luZ2xlIG1vc3QgaW1wb3J0YW50IHRoaW5nIGEgd2Vla2x5IHJlcG9ydCBjYW4gdGVsbCBhIG1hbmFnZXIsXG4gICAgLy8gYW5kIHRyZWF0aW5nIDAgYXMgXCJubyB2YWx1ZVwiIG1hZGUgZXhhY3RseSB0aGF0IGNhc2UgZGlzYXBwZWFyIOKAlCB0aGUgY2hhbmdlXG4gICAgLy8gY2FtZSBiYWNrIGB1bmtub3duYCBhbmQgbm8gYXJyb3cgd2FzIHNob3duLiBTbyB0aGUgdGVzdCBpcyB3aGV0aGVyIGFueSByb3dcbiAgICAvLyBDQVJSSUVEIGEgY291bnQsIG5vdCB3aGV0aGVyIHRoZSBzdW0gaXMgcG9zaXRpdmUuXG4gICAgbGV0IGNvdW50ZWQgPSBmYWxzZTtcbiAgICBjb25zdCB0b3RhbCA9ICh2YWx1ZSBhcyBBcnJheTxSZWNvcmQ8c3RyaW5nLCB1bmtub3duPj4pLnJlZHVjZSgoc3VtLCByb3cpID0+IHtcbiAgICAgIGNvbnN0IG4gPSByb3cudG90YWwgPz8gcm93LmNvdW50ID8/IHJvdy5leGVjdXRpb25zO1xuICAgICAgaWYgKHR5cGVvZiBuICE9PSAnbnVtYmVyJykgcmV0dXJuIHN1bTtcbiAgICAgIGNvdW50ZWQgPSB0cnVlO1xuICAgICAgcmV0dXJuIHN1bSArIG47XG4gICAgfSwgMCk7XG4gICAgaWYgKGNvdW50ZWQpIHJldHVybiB7IGxhYmVsOiAnVG90YWwnLCB2YWx1ZTogdG90YWwgfTtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIGNvbnN0IG9iaiA9IHZhbHVlIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xuICAvLyBET1JBOiBkZXBsb3ltZW50IGZyZXF1ZW5jeSBpcyB0aGUgaGVhZGxpbmUgYSBtYW5hZ2VyIHJlY29nbmlzZXMuXG4gIGNvbnN0IGVudnMgPSBvYmouZW52aXJvbm1lbnRzO1xuICBpZiAoQXJyYXkuaXNBcnJheShlbnZzKSAmJiBlbnZzLmxlbmd0aCA+IDApIHtcbiAgICBjb25zdCBmaXJzdCA9IGVudnNbMF0gYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj47XG4gICAgY29uc3QgZGYgPSAoZmlyc3QuZGVwbG95bWVudEZyZXF1ZW5jeSBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB8IHVuZGVmaW5lZCk/LmRlcGxveW1lbnRzO1xuICAgIGlmICh0eXBlb2YgZGYgPT09ICdudW1iZXInKSByZXR1cm4geyBsYWJlbDogJ0RlcGxveXMnLCB2YWx1ZTogZGYgfTtcbiAgfVxuICAvLyBQbHVnaW4gc3VtbWFyeSBhbmQgZnJpZW5kczogYSBwbGFpbiB0b3RhbC5cbiAgLy8gWmVybyBpcyBhIHZhbHVlIGhlcmUgdG9vIOKAlCBgcGx1Z2luX3ZlcnNpb25zYCByZXBvcnRpbmcgMCBvdXRkYXRlZCBwbHVnaW5zIGlzXG4gIC8vIGdvb2QgbmV3cyBhIG1hbmFnZXIgc2hvdWxkIHNlZSwgbm90IGFuIGFic2VudCBzZWN0aW9uLlxuICBmb3IgKGNvbnN0IGtleSBvZiBbJ3RvdGFsJywgJ2NvdW50JywgJ2RlcGxveW1lbnRzJ10pIHtcbiAgICBpZiAodHlwZW9mIG9ialtrZXldID09PSAnbnVtYmVyJykgcmV0dXJuIHsgbGFiZWw6IHNlY3Rpb25JZCA9PT0gJ3BsdWdpbl92ZXJzaW9ucycgPyAnT3V0ZGF0ZWQnIDogJ1RvdGFsJywgdmFsdWU6IG9ialtrZXldIGFzIG51bWJlciB9O1xuICB9XG4gIHJldHVybiBudWxsO1xufVxuXG4vKiogQ2hhbmdlIGJldHdlZW4gdHdvIGhlYWRsaW5lIG51bWJlcnMuICovXG5mdW5jdGlvbiBjaGFuZ2VPZihcbiAgY3VycmVudDogeyB2YWx1ZTogbnVtYmVyIH0gfCBudWxsLFxuICBwcmV2aW91czogeyB2YWx1ZTogbnVtYmVyIH0gfCBudWxsLFxuKTogQ29tcG9zZWRTZWN0aW9uWydjaGFuZ2UnXSB7XG4gIGlmICghY3VycmVudCB8fCAhcHJldmlvdXMpIHJldHVybiB7IGFic29sdXRlOiAwLCBwZXJjZW50OiBudWxsLCBkaXJlY3Rpb246ICd1bmtub3duJyB9O1xuICBjb25zdCBhYnNvbHV0ZSA9IE51bWJlcigoY3VycmVudC52YWx1ZSAtIHByZXZpb3VzLnZhbHVlKS50b0ZpeGVkKDIpKTtcbiAgLy8gQSBwZXJjZW50IGNoYW5nZSBvZmYgYSB6ZXJvIGJhc2VsaW5lIGlzIHVuZGVmaW5lZCwgbm90IGluZmluaXRlOiBnb2luZyBmcm9tIDBcbiAgLy8gdG8gNSBkZXBsb3lzIGlzIFwiNSBtb3JlXCIsIG5ldmVyIFwiK+KIniVcIi5cbiAgY29uc3QgcGVyY2VudCA9IHByZXZpb3VzLnZhbHVlID09PSAwID8gbnVsbCA6IE51bWJlcigoKGFic29sdXRlIC8gcHJldmlvdXMudmFsdWUpICogMTAwKS50b0ZpeGVkKDEpKTtcbiAgY29uc3QgZGlyZWN0aW9uOiBUcmVuZERpcmVjdGlvbiA9IGFic29sdXRlID09PSAwID8gJ2ZsYXQnIDogYWJzb2x1dGUgPiAwID8gJ3VwJyA6ICdkb3duJztcbiAgcmV0dXJuIHsgYWJzb2x1dGUsIHBlcmNlbnQsIGRpcmVjdGlvbiB9O1xufVxuXG4vKiogQ291bnQgdGhlIG9ic2VydmF0aW9ucyBiZWhpbmQgYSBzZWN0aW9uLCBmb3IgdGhlIGxvdy1zYW1wbGUgbm90ZS4gKi9cbmZ1bmN0aW9uIHNhbXBsZVNpemUodmFsdWU6IHVua25vd24pOiBudW1iZXIgfCBudWxsIHtcbiAgaWYgKEFycmF5LmlzQXJyYXkodmFsdWUpKSB7XG4gICAgcmV0dXJuICh2YWx1ZSBhcyBBcnJheTxSZWNvcmQ8c3RyaW5nLCB1bmtub3duPj4pLnJlZHVjZSgoc3VtLCByb3cpID0+IHtcbiAgICAgIGNvbnN0IG4gPSByb3cudG90YWwgPz8gcm93LmNvdW50ID8/IHJvdy5leGVjdXRpb25zID8/IHJvdy5kZXBsb3ltZW50cztcbiAgICAgIHJldHVybiBzdW0gKyAodHlwZW9mIG4gPT09ICdudW1iZXInID8gbiA6IDApO1xuICAgIH0sIDApO1xuICB9XG4gIGNvbnN0IG9iaiA9IHZhbHVlIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+IHwgbnVsbDtcbiAgY29uc3QgZW52cyA9IG9iaj8uZW52aXJvbm1lbnRzO1xuICBpZiAoQXJyYXkuaXNBcnJheShlbnZzKSkge1xuICAgIHJldHVybiBlbnZzLnJlZHVjZSgoc3VtOiBudW1iZXIsIGUpID0+IHtcbiAgICAgIGNvbnN0IGF0dGVtcHRzID0gKChlIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+KS5jaGFuZ2VGYWlsdXJlUmF0ZSBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB8IHVuZGVmaW5lZCk/LmF0dGVtcHRzO1xuICAgICAgcmV0dXJuIHN1bSArICh0eXBlb2YgYXR0ZW1wdHMgPT09ICdudW1iZXInID8gYXR0ZW1wdHMgOiAwKTtcbiAgICB9LCAwKTtcbiAgfVxuICByZXR1cm4gbnVsbDtcbn1cblxuLyoqIERlcGxveS1jb3ZlcmFnZSBub3RlczogdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBcInplcm8gZGVwbG95c1wiIGFuZCBcImJsaW5kXCIuICovXG5mdW5jdGlvbiBkZXBsb3lDb3ZlcmFnZU5vdGVzKGRvcmE6IHVua25vd24pOiBEYXRhUXVhbGl0eU5vdGVbXSB7XG4gIGNvbnN0IG5vdGVzOiBEYXRhUXVhbGl0eU5vdGVbXSA9IFtdO1xuICBjb25zdCBjb3ZlcmFnZSA9IChkb3JhIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+IHwgbnVsbCk/LmNvdmVyYWdlIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+IHwgdW5kZWZpbmVkO1xuICBjb25zdCB3aXRob3V0ID0gY292ZXJhZ2U/LndpdGhvdXREZXBsb3lzO1xuICBpZiAodHlwZW9mIHdpdGhvdXQgPT09ICdudW1iZXInICYmIHdpdGhvdXQgPiAwKSB7XG4gICAgbm90ZXMucHVzaCh7XG4gICAgICBjb2RlOiAnbWlzc2luZ19kZXBsb3lfdGFnJyxcbiAgICAgIHNlY3Rpb25JZDogJ2RvcmEnLFxuICAgICAgbWVzc2FnZTogYERlcGxveSBkYXRhIHVuYXZhaWxhYmxlIGZvciAke3dpdGhvdXR9IHBpcGVsaW5lJHt3aXRob3V0ID09PSAxID8gJycgOiAncyd9OiB0aGV5IGhhdmUgbm90IHJlLXN5bnRoZWQgd2l0aCBkZXBsb3kgdGFncywgc28gdGhlaXIgZGVwbG95cyBhcmUgbm90IGNvdW50ZWQgaGVyZS4gVGhpcyBpcyBub3QgXCJ6ZXJvIGRlcGxveXNcIi5gLFxuICAgIH0pO1xuICB9XG4gIGNvbnN0IGVudnMgPSAoZG9yYSBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB8IG51bGwpPy5lbnZpcm9ubWVudHM7XG4gIGlmIChBcnJheS5pc0FycmF5KGVudnMpKSB7XG4gICAgY29uc3Qgbm9MZWFkID0gKGVudnMgYXMgQXJyYXk8UmVjb3JkPHN0cmluZywgdW5rbm93bj4+KS5maWx0ZXIoKGUpID0+IHtcbiAgICAgIGNvbnN0IGx0ID0gZS5sZWFkVGltZSBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB8IHVuZGVmaW5lZDtcbiAgICAgIHJldHVybiBsdCAmJiBsdC5tZWRpYW5TZWNvbmRzID09PSBudWxsO1xuICAgIH0pO1xuICAgIGlmIChub0xlYWQubGVuZ3RoID4gMCkge1xuICAgICAgbm90ZXMucHVzaCh7XG4gICAgICAgIGNvZGU6ICdtaXNzaW5nX2NvbW1pdF9kYXRhJyxcbiAgICAgICAgc2VjdGlvbklkOiAnZG9yYScsXG4gICAgICAgIG1lc3NhZ2U6IGBMZWFkIHRpbWUgdW5hdmFpbGFibGUgZm9yICR7bm9MZWFkLmxlbmd0aH0gZW52aXJvbm1lbnQke25vTGVhZC5sZW5ndGggPT09IDEgPyAnJyA6ICdzJ306IG5vIHN1Y2Nlc3NmdWwgZGVwbG95IGNhcnJpZWQgYSByZXNvbHZhYmxlIGNvbW1pdCB0aW1lc3RhbXAsIHNvIGNvbW1pdC10by1kZXBsb3kgdGltZSBjb3VsZCBub3QgYmUgbWVhc3VyZWQuYCxcbiAgICAgIH0pO1xuICAgIH1cbiAgfVxuICByZXR1cm4gbm90ZXM7XG59XG5cbi8qKlxuICogQ29tcG9zZSBvbmUgcGVyaW9kJ3Mgc25hcHNob3QuXG4gKlxuICogTmV2ZXIgdGhyb3dzIGZvciBhIHNlY3Rpb24tbGV2ZWwgcHJvYmxlbTogYSBzZWN0aW9uIHRoYXQgZmFpbHMgaXMgbWFya2VkXG4gKiBgZmFpbGVkYCB3aXRoIGEgbm90ZSwgYW5kIHRoZSByZXN0IG9mIHRoZSByZXBvcnQgc3RpbGwgcmVhY2hlcyB0aGUgbGVhZC4gQSBydW5cbiAqIHRoYXQgcHJvZHVjZXMgbmluZSBnb29kIHNlY3Rpb25zIGFuZCBvbmUgZmFpbHVyZSBpcyB1c2VmdWw7IGEgcnVuIHRoYXQgcHJvZHVjZXNcbiAqIGFuIGV4Y2VwdGlvbiBpcyBub3QuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjb21wb3NlU25hcHNob3Qoc2VjdGlvbklkTGlzdDogcmVhZG9ubHkgc3RyaW5nW10sIG9wdHM6IENvbXBvc2VPcHRpb25zKTogUHJvbWlzZTxSZXBvcnRTbmFwc2hvdD4ge1xuICBjb25zdCB7IHNvdXJjZSwgcGVyaW9kLCB0aW1lem9uZSwgd2Vla1N0YXJ0LCBvcmdJZCwgb3JnSWRzLCBmZWF0dXJlcyB9ID0gb3B0cztcbiAgY29uc3Qgbm90ZXM6IERhdGFRdWFsaXR5Tm90ZVtdID0gW107XG4gIGNvbnN0IHNlY3Rpb25zOiBDb21wb3NlZFNlY3Rpb25bXSA9IFtdO1xuXG4gIGNvbnN0IGN0eEZvciA9IChmcm9tOiBEYXRlLCB0bzogRGF0ZSk6IFNlY3Rpb25Db250ZXh0ID0+ICh7XG4gICAgb3JnSWQsXG4gICAgLi4uKG9yZ0lkcyA/IHsgb3JnSWRzIH0gOiB7fSksXG4gICAgZnJvbTogZnJvbS50b0lTT1N0cmluZygpLFxuICAgIHRvOiB0by50b0lTT1N0cmluZygpLFxuICAgIHR6OiB0aW1lem9uZSxcbiAgICB3ZWVrU3RhcnQsXG4gICAgLy8gVGhlIHByZXZpb3VzIHBlcmlvZCB0cmF2ZWxzIFdJVEggdGhlIGNvbnRleHQsIGZvciB0aGUgYW5hbHl0aWNzIHNlY3Rpb25zXG4gICAgLy8gd2hvc2UgdHJlbmQgaXMgcGVyIGNhdGVnb3J5LCBwZXIgc3RhZ2Ugb3IgcGVyIHBpcGVsaW5lLiBUaG9zZSBjYW5ub3QgYmVcbiAgICAvLyBoYW5kbGVkIGJ5IHJ1bm5pbmcgdGhlIHNlY3Rpb24gdHdpY2UgYW5kIGNvbXBhcmluZyBvbmUgaGVhZGxpbmUg4oCUIHRoZVxuICAgIC8vIGNvbXBhcmlzb24gaXMgYSBqb2luLCBhbmQgZG9pbmcgaXQgaW4gdGhlIGNvbXBvc2VyIGFmdGVyd2FyZHMgd291bGQgbWVhblxuICAgIC8vIHJlLWltcGxlbWVudGluZyBpdCBmb3IgZXZlcnkgc2hhcGUuXG4gICAgcHJldmlvdXNGcm9tOiBwZXJpb2QucHJldlN0YXJ0LnRvSVNPU3RyaW5nKCksXG4gICAgcHJldmlvdXNUbzogcGVyaW9kLnByZXZFbmQudG9JU09TdHJpbmcoKSxcbiAgfSk7XG4gIGNvbnN0IGN1cnJlbnRDdHggPSBjdHhGb3IocGVyaW9kLnN0YXJ0LCBwZXJpb2QuZW5kKTtcbiAgY29uc3QgcHJldmlvdXNDdHggPSBjdHhGb3IocGVyaW9kLnByZXZTdGFydCwgcGVyaW9kLnByZXZFbmQpO1xuXG4gIGZvciAoY29uc3QgaWQgb2Ygc2VjdGlvbklkTGlzdCkge1xuICAgIGNvbnN0IHNwZWM6IFNlY3Rpb25TcGVjIHwgdW5kZWZpbmVkID0gZ2V0U2VjdGlvbihpZCk7XG4gICAgaWYgKCFzcGVjKSB7XG4gICAgICAvLyBBbiB1bmtub3duIGlkIGlzIGEgZGVmaW5pdGlvbiByZWZlcmVuY2luZyBhIHNlY3Rpb24gdGhhdCBubyBsb25nZXIgZXhpc3RzLlxuICAgICAgLy8gUmVjb3JkZWQgcmF0aGVyIHRoYW4gZHJvcHBlZCwgc28gdGhlIGxlYWQgY2FuIHNlZSB3aHkgYSBwYW5lbCB2YW5pc2hlZC5cbiAgICAgIHNlY3Rpb25zLnB1c2goeyBpZCwgdGl0bGU6IGlkLCBzdGF0ZTogJ2ZhaWxlZCcgfSk7XG4gICAgICBub3Rlcy5wdXNoKHsgY29kZTogJ3NlY3Rpb25fZmFpbGVkJywgc2VjdGlvbklkOiBpZCwgbWVzc2FnZTogYFVua25vd24gcmVwb3J0IHNlY3Rpb24gXCIke2lkfVwiIOKAlCBpdCBtYXkgaGF2ZSBiZWVuIHJlbW92ZWQuYCB9KTtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cblxuICAgIGlmIChzcGVjLnJlcXVpcmVzRmVhdHVyZSAmJiAhc2F0aXNmaWVzKGZlYXR1cmVzLCBzcGVjLnJlcXVpcmVzRmVhdHVyZSkpIHtcbiAgICAgIHNlY3Rpb25zLnB1c2goe1xuICAgICAgICBpZCwgdGl0bGU6IHNwZWMudGl0bGUsIHN0YXRlOiAnbG9ja2VkJywgcmVxdWlyZXNGZWF0dXJlOiBzcGVjLnJlcXVpcmVzRmVhdHVyZSxcbiAgICAgIH0pO1xuICAgICAgbm90ZXMucHVzaCh7XG4gICAgICAgIGNvZGU6ICdzZWN0aW9uX2xvY2tlZCcsXG4gICAgICAgIHNlY3Rpb25JZDogaWQsXG4gICAgICAgIG1lc3NhZ2U6IGBcIiR7c3BlYy50aXRsZX1cIiBuZWVkcyB0aGUgJHtzcGVjLnJlcXVpcmVzRmVhdHVyZX0gYWRkLW9uLiBUaGUgcmVzdCBvZiB0aGlzIHJlcG9ydCBpcyB1bmFmZmVjdGVkLmAsXG4gICAgICB9KTtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cblxuICAgIHRyeSB7XG4gICAgICBjb25zdCBjdXJyZW50ID0gYXdhaXQgc3BlYy5ydW4oc291cmNlLCBjdXJyZW50Q3R4KTtcbiAgICAgIGNvbnN0IGNvbXBvc2VkOiBDb21wb3NlZFNlY3Rpb24gPSB7IGlkLCB0aXRsZTogc3BlYy50aXRsZSwgc3RhdGU6ICdvaycsIGN1cnJlbnQgfTtcblxuICAgICAgY29uc3QgaGVhZCA9IGhlYWRsaW5lT2YoaWQsIGN1cnJlbnQpO1xuICAgICAgaWYgKGhlYWQpIGNvbXBvc2VkLmhlYWRsaW5lID0gaGVhZDtcblxuICAgICAgaWYgKHNwZWMuY29tcGFyYWJsZSkge1xuICAgICAgICAvLyBUaGUgY29tcGFyaXNvbiBpcyBiZXN0LWVmZm9ydDogYSBmYWlsZWQgcHJldmlvdXMgcGVyaW9kIG11c3Qgbm90IGNvc3QgdGhlXG4gICAgICAgIC8vIGxlYWQgdGhpcyBwZXJpb2QncyBudW1iZXJzLlxuICAgICAgICB0cnkge1xuICAgICAgICAgIGNvbnN0IHByZXZpb3VzID0gYXdhaXQgc3BlYy5ydW4oc291cmNlLCBwcmV2aW91c0N0eCk7XG4gICAgICAgICAgY29tcG9zZWQucHJldmlvdXMgPSBwcmV2aW91cztcbiAgICAgICAgICBjb21wb3NlZC5jaGFuZ2UgPSBjaGFuZ2VPZihoZWFkLCBoZWFkbGluZU9mKGlkLCBwcmV2aW91cykpO1xuICAgICAgICB9IGNhdGNoIHtcbiAgICAgICAgICBjb21wb3NlZC5jaGFuZ2UgPSB7IGFic29sdXRlOiAwLCBwZXJjZW50OiBudWxsLCBkaXJlY3Rpb246ICd1bmtub3duJyB9O1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IHNhbXBsZSA9IHNhbXBsZVNpemUoY3VycmVudCk7XG4gICAgICBpZiAoc2FtcGxlICE9PSBudWxsICYmIHNhbXBsZSA+IDAgJiYgc2FtcGxlIDwgTE9XX1NBTVBMRV9USFJFU0hPTEQpIHtcbiAgICAgICAgbm90ZXMucHVzaCh7XG4gICAgICAgICAgY29kZTogJ2xvd19zYW1wbGUnLFxuICAgICAgICAgIHNlY3Rpb25JZDogaWQsXG4gICAgICAgICAgbWVzc2FnZTogYFwiJHtzcGVjLnRpdGxlfVwiIGlzIGJhc2VkIG9uICR7c2FtcGxlfSBvYnNlcnZhdGlvbiR7c2FtcGxlID09PSAxID8gJycgOiAncyd9IOKAlCBwZXJjZW50YWdlcyBzd2luZyB3aWxkbHkgYXQgdGhpcyB2b2x1bWUuIFJlYWQgdGhlIGNvdW50cywgbm90IHRoZSByYXRlLmAsXG4gICAgICAgIH0pO1xuICAgICAgfVxuXG4gICAgICBpZiAoaWQgPT09ICdkb3JhJykgbm90ZXMucHVzaCguLi5kZXBsb3lDb3ZlcmFnZU5vdGVzKGN1cnJlbnQpKTtcbiAgICAgIHNlY3Rpb25zLnB1c2goY29tcG9zZWQpO1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgc2VjdGlvbnMucHVzaCh7IGlkLCB0aXRsZTogc3BlYy50aXRsZSwgc3RhdGU6ICdmYWlsZWQnIH0pO1xuICAgICAgbm90ZXMucHVzaCh7XG4gICAgICAgIGNvZGU6ICdzZWN0aW9uX2ZhaWxlZCcsXG4gICAgICAgIHNlY3Rpb25JZDogaWQsXG4gICAgICAgIG1lc3NhZ2U6IGBcIiR7c3BlYy50aXRsZX1cIiBjb3VsZCBub3QgYmUgY29tcHV0ZWQgZm9yIHRoaXMgcGVyaW9kLiBUaGUgcmVzdCBvZiB0aGUgcmVwb3J0IGlzIHVuYWZmZWN0ZWQuYCxcbiAgICAgIH0pO1xuICAgICAgdm9pZCBlcnI7IC8vIHRoZSByZWFzb24gaXMgbG9nZ2VkIGJ5IHRoZSBjYWxsZXIsIG5ldmVyIHNob3duIHRvIGEgbWFuYWdlclxuICAgIH1cbiAgfVxuXG4gIHJldHVybiB7XG4gICAgcGVyaW9kOiB7IHN0YXJ0OiBwZXJpb2Quc3RhcnQudG9JU09TdHJpbmcoKSwgZW5kOiBwZXJpb2QuZW5kLnRvSVNPU3RyaW5nKCksIGxhYmVsOiBwZXJpb2QubGFiZWwgfSxcbiAgICBwcmV2aW91c1BlcmlvZDogeyBzdGFydDogcGVyaW9kLnByZXZTdGFydC50b0lTT1N0cmluZygpLCBlbmQ6IHBlcmlvZC5wcmV2RW5kLnRvSVNPU3RyaW5nKCkgfSxcbiAgICB0aW1lem9uZSxcbiAgICB3ZWVrU3RhcnQsXG4gICAgc2VjdGlvbnMsXG4gICAgbm90ZXMsXG4gICAgbWV0aG9kb2xvZ3k6XG4gICAgICBgQ29tcHV0ZWQgZnJvbSBwaXBlbGluZSBldmVudHMgYmV0d2VlbiAke3BlcmlvZC5zdGFydC50b0lTT1N0cmluZygpfSBhbmQgJHtwZXJpb2QuZW5kLnRvSVNPU3RyaW5nKCl9LCBgXG4gICAgICArIGBidWNrZXRlZCBpbiAke3RpbWV6b25lfSB3aXRoIHdlZWtzIHN0YXJ0aW5nICR7d2Vla1N0YXJ0fS4gQ29tcGFyaXNvbiBwZXJpb2Q6IGBcbiAgICAgICsgYCR7cGVyaW9kLnByZXZTdGFydC50b0lTT1N0cmluZygpfSB0byAke3BlcmlvZC5wcmV2RW5kLnRvSVNPU3RyaW5nKCl9LiBgXG4gICAgICArICdUaGVzZSBhcmUgdGhlIHNhbWUgcXVlcmllcyB0aGUgZGFzaGJvYXJkIHJ1bnMgb3ZlciB0aGUgc2FtZSB3aW5kb3csIHNvIHRoZSB0d28gYWdyZWUuJyxcbiAgICBnZW5lcmF0ZWRBdDogKG9wdHMubm93ID8/IG5ldyBEYXRlKCkpLnRvSVNPU3RyaW5nKCksXG4gIH07XG59XG4iXX0=
@@ -0,0 +1,14 @@
1
+ /**
2
+ * The stakeholder-report engine: period resolution, the section registry, the
3
+ * templates, and the composer that turns them into a frozen snapshot.
4
+ *
5
+ * Enumerated rather than `export *` so the module's internals (the civil-date
6
+ * helpers, the headline heuristics) stay private — the same discipline
7
+ * reporting-service.ts applies to the reporting modules.
8
+ */
9
+ export { resolvePeriod, resolvePeriodByLabel, rejectUnreportablePeriod, nextPeriodBoundary, completePeriodsSince, type ResolvedPeriod, type PeriodRejection, } from './period.js';
10
+ export { registerSection, getSection, allSections, sectionIds, type SectionSpec, type SectionContext, type SectionDataSource, } from './sections.js';
11
+ export { REPORT_TEMPLATE_SPECS, getTemplate, assertTemplatesResolve, type TemplateSpec, } from './templates.js';
12
+ export { composeSnapshot, LOW_SAMPLE_THRESHOLD, REPORT_FEATURE_SUBSTITUTES, type ReportSnapshot, type ComposedSection, type DataQualityNote, type ComposeOptions, type TrendDirection, } from './compose.js';
13
+ export { renderReportHtml, reportFileName, trendSentence, type ReportHtmlInput, } from './report-html.js';
14
+ export { StakeholderReportStore, stakeholderReportStore, hashToken, mintToken, DEFAULT_SHARE_LINK_TTL_DAYS, MAX_SHARE_LINK_TTL_DAYS, MAX_BOUNCES, VERIFICATION_TTL_MS, type CreateDefinitionInput, type UpdateDefinitionInput, type MintedShareLink, type ResolvedShareLink, } from './store.js';
@@ -0,0 +1,17 @@
1
+ // Copyright 2026 Pipeline Builder Contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * The stakeholder-report engine: period resolution, the section registry, the
5
+ * templates, and the composer that turns them into a frozen snapshot.
6
+ *
7
+ * Enumerated rather than `export *` so the module's internals (the civil-date
8
+ * helpers, the headline heuristics) stay private — the same discipline
9
+ * reporting-service.ts applies to the reporting modules.
10
+ */
11
+ export { resolvePeriod, resolvePeriodByLabel, rejectUnreportablePeriod, nextPeriodBoundary, completePeriodsSince, } from './period.js';
12
+ export { registerSection, getSection, allSections, sectionIds, } from './sections.js';
13
+ export { REPORT_TEMPLATE_SPECS, getTemplate, assertTemplatesResolve, } from './templates.js';
14
+ export { composeSnapshot, LOW_SAMPLE_THRESHOLD, REPORT_FEATURE_SUBSTITUTES, } from './compose.js';
15
+ export { renderReportHtml, reportFileName, trendSentence, } from './report-html.js';
16
+ export { StakeholderReportStore, stakeholderReportStore, hashToken, mintToken, DEFAULT_SHARE_LINK_TTL_DAYS, MAX_SHARE_LINK_TTL_DAYS, MAX_BOUNCES, VERIFICATION_TTL_MS, } from './store.js';
17
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvYXBpL3JlcG9ydGluZy9zdGFrZWhvbGRlci9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDOzs7Ozs7O0dBT0c7QUFFSCxPQUFPLEVBQ0wsYUFBYSxFQUNiLG9CQUFvQixFQUNwQix3QkFBd0IsRUFDeEIsa0JBQWtCLEVBQ2xCLG9CQUFvQixHQUdyQixNQUFNLGFBQWEsQ0FBQztBQUVyQixPQUFPLEVBQ0wsZUFBZSxFQUNmLFVBQVUsRUFDVixXQUFXLEVBQ1gsVUFBVSxHQUlYLE1BQU0sZUFBZSxDQUFDO0FBRXZCLE9BQU8sRUFDTCxxQkFBcUIsRUFDckIsV0FBVyxFQUNYLHNCQUFzQixHQUV2QixNQUFNLGdCQUFnQixDQUFDO0FBRXhCLE9BQU8sRUFDTCxlQUFlLEVBQ2Ysb0JBQW9CLEVBQ3BCLDBCQUEwQixHQU0zQixNQUFNLGNBQWMsQ0FBQztBQUV0QixPQUFPLEVBQ0wsZ0JBQWdCLEVBQ2hCLGNBQWMsRUFDZCxhQUFhLEdBRWQsTUFBTSxrQkFBa0IsQ0FBQztBQUUxQixPQUFPLEVBQ0wsc0JBQXNCLEVBQ3RCLHNCQUFzQixFQUN0QixTQUFTLEVBQ1QsU0FBUyxFQUNULDJCQUEyQixFQUMzQix1QkFBdUIsRUFDdkIsV0FBVyxFQUNYLG1CQUFtQixHQUtwQixNQUFNLFlBQVksQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCAyMDI2IFBpcGVsaW5lIEJ1aWxkZXIgQ29udHJpYnV0b3JzXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG4vKipcbiAqIFRoZSBzdGFrZWhvbGRlci1yZXBvcnQgZW5naW5lOiBwZXJpb2QgcmVzb2x1dGlvbiwgdGhlIHNlY3Rpb24gcmVnaXN0cnksIHRoZVxuICogdGVtcGxhdGVzLCBhbmQgdGhlIGNvbXBvc2VyIHRoYXQgdHVybnMgdGhlbSBpbnRvIGEgZnJvemVuIHNuYXBzaG90LlxuICpcbiAqIEVudW1lcmF0ZWQgcmF0aGVyIHRoYW4gYGV4cG9ydCAqYCBzbyB0aGUgbW9kdWxlJ3MgaW50ZXJuYWxzICh0aGUgY2l2aWwtZGF0ZVxuICogaGVscGVycywgdGhlIGhlYWRsaW5lIGhldXJpc3RpY3MpIHN0YXkgcHJpdmF0ZSDigJQgdGhlIHNhbWUgZGlzY2lwbGluZVxuICogcmVwb3J0aW5nLXNlcnZpY2UudHMgYXBwbGllcyB0byB0aGUgcmVwb3J0aW5nIG1vZHVsZXMuXG4gKi9cblxuZXhwb3J0IHtcbiAgcmVzb2x2ZVBlcmlvZCxcbiAgcmVzb2x2ZVBlcmlvZEJ5TGFiZWwsXG4gIHJlamVjdFVucmVwb3J0YWJsZVBlcmlvZCxcbiAgbmV4dFBlcmlvZEJvdW5kYXJ5LFxuICBjb21wbGV0ZVBlcmlvZHNTaW5jZSxcbiAgdHlwZSBSZXNvbHZlZFBlcmlvZCxcbiAgdHlwZSBQZXJpb2RSZWplY3Rpb24sXG59IGZyb20gJy4vcGVyaW9kLmpzJztcblxuZXhwb3J0IHtcbiAgcmVnaXN0ZXJTZWN0aW9uLFxuICBnZXRTZWN0aW9uLFxuICBhbGxTZWN0aW9ucyxcbiAgc2VjdGlvbklkcyxcbiAgdHlwZSBTZWN0aW9uU3BlYyxcbiAgdHlwZSBTZWN0aW9uQ29udGV4dCxcbiAgdHlwZSBTZWN0aW9uRGF0YVNvdXJjZSxcbn0gZnJvbSAnLi9zZWN0aW9ucy5qcyc7XG5cbmV4cG9ydCB7XG4gIFJFUE9SVF9URU1QTEFURV9TUEVDUyxcbiAgZ2V0VGVtcGxhdGUsXG4gIGFzc2VydFRlbXBsYXRlc1Jlc29sdmUsXG4gIHR5cGUgVGVtcGxhdGVTcGVjLFxufSBmcm9tICcuL3RlbXBsYXRlcy5qcyc7XG5cbmV4cG9ydCB7XG4gIGNvbXBvc2VTbmFwc2hvdCxcbiAgTE9XX1NBTVBMRV9USFJFU0hPTEQsXG4gIFJFUE9SVF9GRUFUVVJFX1NVQlNUSVRVVEVTLFxuICB0eXBlIFJlcG9ydFNuYXBzaG90LFxuICB0eXBlIENvbXBvc2VkU2VjdGlvbixcbiAgdHlwZSBEYXRhUXVhbGl0eU5vdGUsXG4gIHR5cGUgQ29tcG9zZU9wdGlvbnMsXG4gIHR5cGUgVHJlbmREaXJlY3Rpb24sXG59IGZyb20gJy4vY29tcG9zZS5qcyc7XG5cbmV4cG9ydCB7XG4gIHJlbmRlclJlcG9ydEh0bWwsXG4gIHJlcG9ydEZpbGVOYW1lLFxuICB0cmVuZFNlbnRlbmNlLFxuICB0eXBlIFJlcG9ydEh0bWxJbnB1dCxcbn0gZnJvbSAnLi9yZXBvcnQtaHRtbC5qcyc7XG5cbmV4cG9ydCB7XG4gIFN0YWtlaG9sZGVyUmVwb3J0U3RvcmUsXG4gIHN0YWtlaG9sZGVyUmVwb3J0U3RvcmUsXG4gIGhhc2hUb2tlbixcbiAgbWludFRva2VuLFxuICBERUZBVUxUX1NIQVJFX0xJTktfVFRMX0RBWVMsXG4gIE1BWF9TSEFSRV9MSU5LX1RUTF9EQVlTLFxuICBNQVhfQk9VTkNFUyxcbiAgVkVSSUZJQ0FUSU9OX1RUTF9NUyxcbiAgdHlwZSBDcmVhdGVEZWZpbml0aW9uSW5wdXQsXG4gIHR5cGUgVXBkYXRlRGVmaW5pdGlvbklucHV0LFxuICB0eXBlIE1pbnRlZFNoYXJlTGluayxcbiAgdHlwZSBSZXNvbHZlZFNoYXJlTGluayxcbn0gZnJvbSAnLi9zdG9yZS5qcyc7XG4iXX0=
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Period resolution for scheduled reports: which complete week, month or quarter
3
+ * a run covers, and the one before it for comparison.
4
+ *
5
+ * A report covers the LAST COMPLETE period, never a partial one. "Deploys this
6
+ * week" on a Monday morning means last week — a manager reading a half-finished
7
+ * week sees numbers that change under them.
8
+ *
9
+ * Everything here is computed in the report's IANA timezone, without a date
10
+ * library. `Intl.DateTimeFormat` with a `timeZone` is the only tz database Node
11
+ * ships, so the approach is: read the wall-clock parts in that zone, do civil
12
+ * arithmetic on them (which is exact — months and quarters are defined on the
13
+ * calendar, not on elapsed milliseconds), then convert the resulting civil
14
+ * midnight back to a UTC instant by measuring the zone's offset AT that instant.
15
+ *
16
+ * Measuring the offset at the target instant rather than at `now` is what makes
17
+ * DST correct: a weekly period that starts before a transition and ends after it
18
+ * is not 168 hours long, and a resolver that added `7 * 24 * 3600_000` would
19
+ * silently shift every boundary by an hour for half the year.
20
+ */
21
+ import type { ReportCadence } from '../../../database/schema/reporting-stakeholder.js';
22
+ /** One resolved reporting period, plus the previous one for comparison. */
23
+ export interface ResolvedPeriod {
24
+ /** Inclusive start of the period being reported (UTC instant). */
25
+ start: Date;
26
+ /** Exclusive end — also the start of the period in progress. */
27
+ end: Date;
28
+ /** Inclusive start of the PREVIOUS period, for period-over-period change. */
29
+ prevStart: Date;
30
+ /** Exclusive end of the previous period (=== `start`). */
31
+ prevEnd: Date;
32
+ /** Human label in the report's timezone: `2026-W38`, `2026-08`, `2026-Q3`. */
33
+ label: string;
34
+ }
35
+ /**
36
+ * Resolve the last COMPLETE period for `cadence` in `tz`.
37
+ *
38
+ * The period in progress is excluded: its end is the boundary, so a Monday run
39
+ * reports last week and a run on the 1st reports last month.
40
+ */
41
+ export declare function resolvePeriod(cadence: ReportCadence, tz: string, weekStart?: 'monday' | 'sunday', now?: Date): ResolvedPeriod;
42
+ /**
43
+ * Resolve a SPECIFIC period by its label (`2026-W38`, `2026-08`, `2026-Q3`), for
44
+ * on-demand runs and backfills. Returns null when the label does not parse or
45
+ * does not match the cadence.
46
+ */
47
+ export declare function resolvePeriodByLabel(label: string, cadence: ReportCadence, tz: string, weekStart?: 'monday' | 'sunday'): ResolvedPeriod | null;
48
+ /**
49
+ * Why a period cannot be reported. Returned rather than thrown so a route can
50
+ * turn it into a 4xx with the reason, and the scheduler can pause with it.
51
+ */
52
+ export type PeriodRejection = {
53
+ reason: 'in_progress';
54
+ message: string;
55
+ } | {
56
+ reason: 'before_retention';
57
+ message: string;
58
+ };
59
+ /**
60
+ * Refuse a period the org's data cannot support.
61
+ *
62
+ * A window older than the retention horizon must be REFUSED with the reason, not
63
+ * silently truncated: a report labelled `2026-Q1` that quietly covers only the
64
+ * last 30 days of it is worse than no report, because a manager cannot tell.
65
+ *
66
+ * `minFromMs` is the retention floor from `orgRetentionWindowFromSettings`
67
+ * (0 = unlimited).
68
+ */
69
+ export declare function rejectUnreportablePeriod(period: ResolvedPeriod, opts: {
70
+ minFromMs: number;
71
+ now?: Date;
72
+ includePrevious?: boolean;
73
+ }): PeriodRejection | null;
74
+ /**
75
+ * The instant the period CONTAINING `from` ends — that is, the moment the next
76
+ * complete period becomes reportable.
77
+ *
78
+ * This is what a definition's `nextRunAt` is derived from, and it is deliberately
79
+ * the boundary rather than "last run + 7 days": a weekly report whose next run is
80
+ * computed by adding elapsed milliseconds drifts an hour at every DST transition
81
+ * and eventually fires on a Sunday. The boundary is a calendar fact in the
82
+ * report's own timezone, so it is the same answer whatever the previous run did.
83
+ *
84
+ * Strictly after `from`: a `from` sitting exactly on a boundary gets the NEXT one,
85
+ * so a run that completes at the instant of the boundary cannot re-claim it.
86
+ */
87
+ export declare function nextPeriodBoundary(cadence: ReportCadence, tz: string, weekStart?: 'monday' | 'sunday', from?: Date): Date;
88
+ /**
89
+ * Every COMPLETE period that ended after `since`, oldest first.
90
+ *
91
+ * This is the catch-up list: a service that was down over a weekend, a definition
92
+ * created with a back-dated `nextRunAt`, or a scheduler disabled by its kill switch
93
+ * for a fortnight all leave periods that were never reported. Returning them in
94
+ * order matters — a manager reading three weekly reports that arrive at once needs
95
+ * them to make sense read downwards.
96
+ *
97
+ * `max` bounds the walk. A definition untouched for two years must not produce 104
98
+ * weekly reports in one cycle; the cap means the oldest are skipped, which is the
99
+ * right trade — nobody wants last April's weekly status, and the snapshot for it
100
+ * would be past the retention horizon anyway.
101
+ */
102
+ export declare function completePeriodsSince(cadence: ReportCadence, tz: string, weekStart: 'monday' | 'sunday', since: Date | null, now?: Date, max?: number): ResolvedPeriod[];