@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.
- package/lib/api/alert-destinations.d.ts +17 -0
- package/lib/api/alert-destinations.js +36 -0
- package/lib/api/reporting/analytics-queries.d.ts +274 -0
- package/lib/api/reporting/analytics-queries.js +598 -0
- package/lib/api/reporting/daily-rollup.d.ts +41 -0
- package/lib/api/reporting/daily-rollup.js +173 -0
- package/lib/api/reporting/deploy-rollup.d.ts +78 -0
- package/lib/api/reporting/deploy-rollup.js +203 -0
- package/lib/api/reporting/dora-scoring.d.ts +14 -0
- package/lib/api/reporting/dora-scoring.js +25 -1
- package/lib/api/reporting/dora.js +30 -6
- package/lib/api/reporting/failure-classifier.d.ts +54 -0
- package/lib/api/reporting/failure-classifier.js +179 -0
- package/lib/api/reporting/ingest.d.ts +2 -2
- package/lib/api/reporting/ingest.js +129 -4
- package/lib/api/reporting/last-deploy.d.ts +31 -0
- package/lib/api/reporting/last-deploy.js +93 -0
- package/lib/api/reporting/needs-attention.d.ts +93 -0
- package/lib/api/reporting/needs-attention.js +152 -0
- package/lib/api/reporting/plugin-exposure.d.ts +129 -0
- package/lib/api/reporting/plugin-exposure.js +312 -0
- package/lib/api/reporting/retention-sweep.js +9 -1
- package/lib/api/reporting/sql-helpers.d.ts +35 -2
- package/lib/api/reporting/sql-helpers.js +39 -1
- package/lib/api/reporting/stakeholder/compose.d.ts +115 -0
- package/lib/api/reporting/stakeholder/compose.js +234 -0
- package/lib/api/reporting/stakeholder/index.d.ts +14 -0
- package/lib/api/reporting/stakeholder/index.js +17 -0
- package/lib/api/reporting/stakeholder/period.d.ts +102 -0
- package/lib/api/reporting/stakeholder/period.js +273 -0
- package/lib/api/reporting/stakeholder/report-html.d.ts +93 -0
- package/lib/api/reporting/stakeholder/report-html.js +208 -0
- package/lib/api/reporting/stakeholder/sections.d.ts +117 -0
- package/lib/api/reporting/stakeholder/sections.js +249 -0
- package/lib/api/reporting/stakeholder/store.d.ts +450 -0
- package/lib/api/reporting/stakeholder/store.js +981 -0
- package/lib/api/reporting/stakeholder/templates.d.ts +21 -0
- package/lib/api/reporting/stakeholder/templates.js +104 -0
- package/lib/api/reporting/types.d.ts +73 -0
- package/lib/api/reporting/types.js +1 -1
- package/lib/api/reporting-service.d.ts +94 -5
- package/lib/api/reporting-service.js +169 -10
- package/lib/api/soft-delete-sweep.d.ts +11 -2
- package/lib/api/soft-delete-sweep.js +17 -6
- package/lib/database/drizzle-schema.d.ts +2263 -385
- package/lib/database/drizzle-schema.js +18 -7
- package/lib/database/index.d.ts +1 -1
- package/lib/database/index.js +4 -2
- package/lib/database/schema/compliance.d.ts +0 -682
- package/lib/database/schema/compliance.js +6 -66
- package/lib/database/schema/ecosystem.d.ts +20 -157
- package/lib/database/schema/ecosystem.js +23 -16
- package/lib/database/schema/pipeline.d.ts +200 -0
- package/lib/database/schema/pipeline.js +100 -1
- package/lib/database/schema/plugin.d.ts +20 -1
- package/lib/database/schema/plugin.js +13 -1
- package/lib/database/schema/reporting-analytics.d.ts +1212 -0
- package/lib/database/schema/reporting-analytics.js +250 -0
- package/lib/database/schema/reporting-stakeholder.d.ts +1405 -0
- package/lib/database/schema/reporting-stakeholder.js +251 -0
- package/lib/index.d.ts +10 -1
- package/lib/index.js +15 -1
- package/lib/testing/exports.json +67 -4
- package/package.json +4 -3
|
@@ -0,0 +1,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[];
|