@pipeline-builder/pipeline-data 4.1.22 → 4.1.23

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.
@@ -0,0 +1,210 @@
1
+ // Copyright 2026 Pipeline Builder Contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * Event ingest: the WRITE half of reporting. Resolves incoming pipeline events
5
+ * against the pipeline registry, scrubs every free-form string, batch-inserts
6
+ * the matched rows idempotently, and invalidates the affected orgs' report
7
+ * caches. Separate from the report queries because this is reporting's only
8
+ * durable persistence boundary — everything else in the service only reads.
9
+ */
10
+ import { createLogger, errorMessage, scrubAwsIdentifiers } from '@pipeline-builder/api-core';
11
+ import { inArray } from 'drizzle-orm';
12
+ import { invalidateOrgReports } from './caches.js';
13
+ import { scrubOptional } from './sql-helpers.js';
14
+ import { schema } from '../../database/drizzle-schema.js';
15
+ import { withTenantTx } from '../../database/tenancy.js';
16
+ const logger = createLogger('reporting-ingest');
17
+ /** An action/build event that can be attributed to a plugin step. */
18
+ function isAttributable(e) {
19
+ return (e.eventType === 'ACTION' || e.eventType === 'BUILD')
20
+ && (e.eventSource === 'codepipeline' || e.eventSource === 'codebuild')
21
+ && !!e.stageName && !!e.actionName;
22
+ }
23
+ function stepKey(pipelineId, stageName, actionName) {
24
+ return `${pipelineId}\u0000${stageName}\u0000${actionName}`;
25
+ }
26
+ /**
27
+ * Plugin attribution: the step manifests of every pipeline with an attributable
28
+ * event, in ONE read per batch, keyed for an in-memory join. Names are compared
29
+ * post-scrub on both sides (the manifest stores them scrubbed the same way).
30
+ */
31
+ async function loadStepPlugins(tx, attributable) {
32
+ const pipelineIds = [...new Set(attributable.map((e) => e.pipelineId))];
33
+ if (pipelineIds.length === 0)
34
+ return new Map();
35
+ const rows = await tx
36
+ .select({
37
+ pipelineId: schema.pipelineStepManifest.pipelineId,
38
+ orgId: schema.pipelineStepManifest.orgId,
39
+ stageName: schema.pipelineStepManifest.stageName,
40
+ actionName: schema.pipelineStepManifest.actionName,
41
+ pluginPublisher: schema.pipelineStepManifest.pluginPublisher,
42
+ pluginPublisherId: schema.pipelineStepManifest.pluginPublisherId,
43
+ pluginName: schema.pipelineStepManifest.pluginName,
44
+ pluginVersion: schema.pipelineStepManifest.pluginVersion,
45
+ })
46
+ .from(schema.pipelineStepManifest)
47
+ .where(inArray(schema.pipelineStepManifest.pipelineId, pipelineIds));
48
+ return new Map(rows.map((m) => [stepKey(m.pipelineId, m.stageName, m.actionName), m]));
49
+ }
50
+ /**
51
+ * The persisted row for one ingested event. This is the DURABLE persistence
52
+ * boundary: every user/AWS-derived free-form string is scrubbed here, and
53
+ * tenancy comes from the pipeline registry, never the caller's claimed org.
54
+ */
55
+ function toEventRow(event, registry, plugin) {
56
+ return {
57
+ // registry.pipelineId === event.pipelineId; the registry's is always a row
58
+ // that exists (FK) and its orgId is the trusted tenant.
59
+ pipelineId: registry.pipelineId,
60
+ orgId: registry.orgId,
61
+ eventSource: event.eventSource,
62
+ eventType: event.eventType,
63
+ status: event.status,
64
+ // An AWS-ASSIGNED UUID, not free-form text: it can't carry an account id,
65
+ // and scrubbing a 12-digit run inside it would corrupt the correlation key.
66
+ executionId: event.executionId,
67
+ // USER-AUTHORED pipeline-structure names — same untrusted origin as
68
+ // errorMessage, so scrubbed (a stage named with an ARN must not persist).
69
+ stageName: scrubOptional(event.stageName),
70
+ actionName: scrubOptional(event.actionName),
71
+ // HARD CONSTRAINT: an AWS account id must NEVER be persisted. Failure detail
72
+ // and messages routinely carry ARNs and bare 12-digit account ids.
73
+ errorMessage: scrubOptional(event.errorMessage),
74
+ startedAt: event.startedAt ? new Date(event.startedAt) : undefined,
75
+ completedAt: event.completedAt ? new Date(event.completedAt) : undefined,
76
+ durationMs: event.durationMs,
77
+ // Deploy attribution — scrubbed like the other free-form strings.
78
+ commitSha: scrubOptional(event.commitSha),
79
+ commitRef: scrubOptional(event.commitRef),
80
+ environment: scrubOptional(event.environment),
81
+ // Measured lead time: the oldest unshipped commit's time + the count. An
82
+ // SCM-derived instant and a plain integer — nothing to scrub.
83
+ commitTimestamp: event.commitTimestamp ? new Date(event.commitTimestamp) : undefined,
84
+ commitCount: event.commitCount,
85
+ // NULL for non-plugin actions and for pipelines deployed without a manifest.
86
+ pluginPublisher: plugin?.pluginPublisher ?? null,
87
+ pluginPublisherId: plugin?.pluginPublisherId ?? null,
88
+ pluginName: plugin?.pluginName ?? null,
89
+ pluginVersion: plugin?.pluginVersion ?? null,
90
+ detail: event.detail !== undefined ? scrubAwsIdentifiers(event.detail) : undefined,
91
+ };
92
+ }
93
+ /**
94
+ * Fan terminal STAGE outcomes into the route's metric hook (pipeline-data can't
95
+ * import api-server's registry) after the insert commits. Driven off the
96
+ * INSERTED rows, so a re-delivered event the dedup index swallowed is never
97
+ * counted twice. A non-null environment marks a deploy.
98
+ */
99
+ function emitStageMetrics(rows, onMetric) {
100
+ for (const r of rows) {
101
+ if (r.eventType !== 'STAGE' || (r.status !== 'SUCCEEDED' && r.status !== 'FAILED') || !r.pipelineId)
102
+ continue;
103
+ onMetric({
104
+ pipelineId: r.pipelineId,
105
+ orgId: r.orgId,
106
+ stage: r.stageName ?? '',
107
+ environment: r.environment ?? null,
108
+ result: r.status === 'SUCCEEDED' ? 'succeeded' : 'failed',
109
+ });
110
+ }
111
+ }
112
+ /**
113
+ * Resolve incoming events against the pipeline registry, batch-insert the
114
+ * matched ones, and invalidate reporting caches for affected orgs.
115
+ * Events for unregistered pipeline ids are dropped (and logged at WARN
116
+ * with sample ids so an operator can see when EventBridge is delivering
117
+ * events for pipelines that haven't called POST /pipelines/registry yet).
118
+ *
119
+ * Returns counts + a sample of unregistered pipeline ids for observability.
120
+ */
121
+ export async function ingestEvents(events, onMetric) {
122
+ // Multi-org batch insert: the caller resolves to multiple orgs via the
123
+ // pipeline-registry lookup below, so the route layer MUST establish a
124
+ // `runWithTenantContext({ isSuperAdmin: true }, ...)` scope before calling
125
+ // this function. Under FORCE'd RLS, a single tx with `app.org_id = <one
126
+ // org>` could only write events for that org; bypass via sysadmin is
127
+ // the right gate for this server-internal cross-tenant endpoint. See
128
+ // api/reporting/src/routes/event-ingest.ts for the wrapper.
129
+ // Caches are invalidated AFTER the tx resolves: doing it inside held the pg
130
+ // locks open across unrelated cache round-trips. The TTL is 2-5 min, so a
131
+ // fire-and-forget post-commit invalidation is an acceptable trade.
132
+ const { inserted, skipped, unregisteredPipelineIds, affectedOrgs, insertedRows } = await withTenantTx(async (tx) => {
133
+ // Batch-resolve all unique pipeline ids in one query
134
+ const uniqueIds = [...new Set(events.map(e => e.pipelineId))];
135
+ const registryRows = await tx
136
+ .select({
137
+ pipelineId: schema.pipelineRegistry.pipelineId,
138
+ orgId: schema.pipelineRegistry.orgId,
139
+ })
140
+ .from(schema.pipelineRegistry)
141
+ .where(inArray(schema.pipelineRegistry.pipelineId, uniqueIds));
142
+ const idMap = new Map(registryRows.map(r => [r.pipelineId, r]));
143
+ const stepPlugins = await loadStepPlugins(tx, events.filter((e) => isAttributable(e) && idMap.has(e.pipelineId)));
144
+ // Build insert batch (skip events whose pipeline isn't registered)
145
+ const rows = [];
146
+ let skippedLocal = 0;
147
+ const unregisteredLocal = [];
148
+ for (const event of events) {
149
+ const registry = idMap.get(event.pipelineId);
150
+ if (!registry) {
151
+ skippedLocal++;
152
+ unregisteredLocal.push(event.pipelineId);
153
+ continue;
154
+ }
155
+ const stageName = scrubOptional(event.stageName);
156
+ const actionName = scrubOptional(event.actionName);
157
+ const step = isAttributable(event) && stageName && actionName
158
+ ? stepPlugins.get(stepKey(registry.pipelineId, stageName, actionName))
159
+ : undefined;
160
+ // Same-org guard: a manifest row is only ever written by the org that
161
+ // owns the pipeline, but the ingest runs cross-tenant, so don't lean on it.
162
+ rows.push(toEventRow(event, registry, step && step.orgId === registry.orgId ? step : undefined));
163
+ }
164
+ // SQS is at-least-once, so EventBridge can deliver the same state-change
165
+ // twice. `onConflictDoNothing` + the partial unique index on
166
+ // (pipeline_id, execution_id, event_type, status, stage_name, action_name)
167
+ // makes re-delivery idempotent. `returning` gives the REAL inserted set so
168
+ // counts, metrics and cache invalidation all ignore duplicates.
169
+ const landed = rows.length > 0
170
+ ? await tx.insert(schema.pipelineEvent).values(rows)
171
+ .onConflictDoNothing()
172
+ .returning({
173
+ orgId: schema.pipelineEvent.orgId,
174
+ pipelineId: schema.pipelineEvent.pipelineId,
175
+ eventType: schema.pipelineEvent.eventType,
176
+ status: schema.pipelineEvent.status,
177
+ stageName: schema.pipelineEvent.stageName,
178
+ environment: schema.pipelineEvent.environment,
179
+ })
180
+ : [];
181
+ return {
182
+ inserted: landed.length,
183
+ skipped: skippedLocal,
184
+ unregisteredPipelineIds: unregisteredLocal,
185
+ affectedOrgs: [...new Set(landed.map(r => r.orgId))],
186
+ insertedRows: landed,
187
+ };
188
+ });
189
+ if (onMetric)
190
+ emitStageMetrics(insertedRows, onMetric);
191
+ // Surface the silent skip: an unregistered pipeline id usually means the
192
+ // pipeline hasn't called POST /pipelines/registry yet (or its
193
+ // pb.pipeline-id tag is missing/unreadable by the Lambda). Logging it
194
+ // makes a broken join visible instead of looking like "no activity".
195
+ if (unregisteredPipelineIds.length > 0) {
196
+ logger.warn('Pipeline events skipped: pipeline id not found in registry', {
197
+ count: unregisteredPipelineIds.length,
198
+ sample: unregisteredPipelineIds.slice(0, 3),
199
+ });
200
+ }
201
+ // Post-commit cache invalidation. Fire-and-forget with logging — TTL is
202
+ // short enough that a missed invalidation self-heals.
203
+ if (affectedOrgs.length > 0) {
204
+ void Promise.all(affectedOrgs.map((org) => invalidateOrgReports(org).catch((err) => {
205
+ logger.warn('Reporting cache invalidation failed', { orgId: org, error: errorMessage(err) });
206
+ })));
207
+ }
208
+ return { inserted, skipped, unregisteredPipelineIds, affectedOrgs };
209
+ }
210
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5nZXN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2FwaS9yZXBvcnRpbmcvaW5nZXN0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFFdEM7Ozs7OztHQU1HO0FBRUgsT0FBTyxFQUFFLFlBQVksRUFBRSxZQUFZLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUM3RixPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ3RDLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUNuRCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFFakQsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLGtDQUFrQyxDQUFDO0FBQzFELE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUd6RCxNQUFNLE1BQU0sR0FBRyxZQUFZLENBQUMsa0JBQWtCLENBQUMsQ0FBQztBQWNoRCxxRUFBcUU7QUFDckUsU0FBUyxjQUFjLENBQUMsQ0FBYztJQUNwQyxPQUFPLENBQUMsQ0FBQyxDQUFDLFNBQVMsS0FBSyxRQUFRLElBQUksQ0FBQyxDQUFDLFNBQVMsS0FBSyxPQUFPLENBQUM7V0FDdkQsQ0FBQyxDQUFDLENBQUMsV0FBVyxLQUFLLGNBQWMsSUFBSSxDQUFDLENBQUMsV0FBVyxLQUFLLFdBQVcsQ0FBQztXQUNuRSxDQUFDLENBQUMsQ0FBQyxDQUFDLFNBQVMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLFVBQVUsQ0FBQztBQUN2QyxDQUFDO0FBRUQsU0FBUyxPQUFPLENBQUMsVUFBa0IsRUFBRSxTQUFpQixFQUFFLFVBQWtCO0lBQ3hFLE9BQU8sR0FBRyxVQUFVLFNBQVMsU0FBUyxTQUFTLFVBQVUsRUFBRSxDQUFDO0FBQzlELENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsS0FBSyxVQUFVLGVBQWUsQ0FBQyxF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QUFDLFlBQVksRUFBRSxRQUFRLENBQUMsQ0FBQztJQUV2RCx5RUFBeUU7SUFDekUsOERBQThEO0lBQzlELHNFQUFzRTtJQUN0RSxxRUFBcUU7SUFDckUsSUFBSSx1QkFBdUIsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFLENBQUM7UUFDdkMsTUFBTSxDQUFDLElBQUksQ0FBQyw0REFBNEQsRUFBRTtZQUN4RSxLQUFLLEVBQUUsdUJBQXVCLENBQUMsTUFBTTtZQUNyQyxNQUFNLEVBQUUsdUJBQXVCLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUM7U0FDNUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVELHdFQUF3RTtJQUN4RSxzREFBc0Q7SUFDdEQsSUFBSSxZQUFZLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxDQUFDO1FBQzVCLEtBQUssT0FBTyxDQUFDLEdBQUcsQ0FBQyxZQUFZLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBRyxFQUFFLEVBQUUsQ0FDeEMsb0JBQW9CLENBQUMsR0FBRyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsR0FBRyxFQUFFLEVBQUU7WUFDdEMsTUFBTSxDQUFDLElBQUksQ0FBQyxxQ0FBcUMsRUFBRSxFQUFFLEtBQUssRUFBRSxHQUFHLEVBQUUsS0FBSyxFQUFFLFlBQVksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDL0YsQ0FBQyxDQUFDLENBQ0gsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVELE9BQU8sRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLHVCQUF1QixFQUFFLFlBQVksRUFBRSxDQUFDO0FBQ3RFLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuLyoqXG4gKiBFdmVudCBpbmdlc3Q6IHRoZSBXUklURSBoYWxmIG9mIHJlcG9ydGluZy4gUmVzb2x2ZXMgaW5jb21pbmcgcGlwZWxpbmUgZXZlbnRzXG4gKiBhZ2FpbnN0IHRoZSBwaXBlbGluZSByZWdpc3RyeSwgc2NydWJzIGV2ZXJ5IGZyZWUtZm9ybSBzdHJpbmcsIGJhdGNoLWluc2VydHNcbiAqIHRoZSBtYXRjaGVkIHJvd3MgaWRlbXBvdGVudGx5LCBhbmQgaW52YWxpZGF0ZXMgdGhlIGFmZmVjdGVkIG9yZ3MnIHJlcG9ydFxuICogY2FjaGVzLiBTZXBhcmF0ZSBmcm9tIHRoZSByZXBvcnQgcXVlcmllcyBiZWNhdXNlIHRoaXMgaXMgcmVwb3J0aW5nJ3Mgb25seVxuICogZHVyYWJsZSBwZXJzaXN0ZW5jZSBib3VuZGFyeSDigJQgZXZlcnl0aGluZyBlbHNlIGluIHRoZSBzZXJ2aWNlIG9ubHkgcmVhZHMuXG4gKi9cblxuaW1wb3J0IHsgY3JlYXRlTG9nZ2VyLCBlcnJvck1lc3NhZ2UsIHNjcnViQXdzSWRlbnRpZmllcnMgfSBmcm9tICdAcGlwZWxpbmUtYnVpbGRlci9hcGktY29yZSc7XG5pbXBvcnQgeyBpbkFycmF5IH0gZnJvbSAnZHJpenpsZS1vcm0nO1xuaW1wb3J0IHsgaW52YWxpZGF0ZU9yZ1JlcG9ydHMgfSBmcm9tICcuL2NhY2hlcy5qcyc7XG5pbXBvcnQgeyBzY3J1Yk9wdGlvbmFsIH0gZnJvbSAnLi9zcWwtaGVscGVycy5qcyc7XG5pbXBvcnQgdHlwZSB7IEluZ2VzdEV2ZW50LCBJbmdlc3RNZXRyaWMsIEluZ2VzdFJlc3VsdCB9IGZyb20gJy4vdHlwZXMuanMnO1xuaW1wb3J0IHsgc2NoZW1hIH0gZnJvbSAnLi4vLi4vZGF0YWJhc2UvZHJpenpsZS1zY2hlbWEuanMnO1xuaW1wb3J0IHsgd2l0aFRlbmFudFR4IH0gZnJvbSAnLi4vLi4vZGF0YWJhc2UvdGVuYW5jeS5qcyc7XG5pbXBvcnQgdHlwZSB7IENydWRUeCB9IGZyb20gJy4uL2NydWQtc2VydmljZS5qcyc7XG5cbmNvbnN0IGxvZ2dlciA9IGNyZWF0ZUxvZ2dlcigncmVwb3J0aW5nLWluZ2VzdCcpO1xuXG50eXBlIFBpcGVsaW5lRXZlbnRJbnNlcnQgPSB0eXBlb2Ygc2NoZW1hLnBpcGVsaW5lRXZlbnQuJGluZmVySW5zZXJ0O1xuXG5cbi8qKiBUaGUgcGx1Z2luIGEgcGlwZWxpbmUgc3RlcCBydW5zLCBmcm9tIHRoZSBzdGVwIG1hbmlmZXN0IGl0cyBsYXN0IGRlcGxveSByZWdpc3RlcmVkLiAqL1xuaW50ZXJmYWNlIFN0ZXBQbHVnaW4ge1xuICBvcmdJZDogc3RyaW5nO1xuICBwbHVnaW5QdWJsaXNoZXI6IHN0cmluZyB8IG51bGw7XG4gIHBsdWdpblB1Ymxpc2hlcklkOiBzdHJpbmcgfCBudWxsO1xuICBwbHVnaW5OYW1lOiBzdHJpbmcgfCBudWxsO1xuICBwbHVnaW5WZXJzaW9uOiBzdHJpbmcgfCBudWxsO1xufVxuXG4vKiogQW4gYWN0aW9uL2J1aWxkIGV2ZW50IHRoYXQgY2FuIGJlIGF0dHJpYnV0ZWQgdG8gYSBwbHVnaW4gc3RlcC4gKi9cbmZ1bmN0aW9uIGlzQXR0cmlidXRhYmxlKGU6IEluZ2VzdEV2ZW50KTogYm9vbGVhbiB7XG4gIHJldHVybiAoZS5ldmVudFR5cGUgPT09ICdBQ1RJT04nIHx8IGUuZXZlbnRUeXBlID09PSAnQlVJTEQnKVxuICAgICYmIChlLmV2ZW50U291cmNlID09PSAnY29kZXBpcGVsaW5lJyB8fCBlLmV2ZW50U291cmNlID09PSAnY29kZWJ1aWxkJylcbiAgICAmJiAhIWUuc3RhZ2VOYW1lICYmICEhZS5hY3Rpb25OYW1lO1xufVxuXG5mdW5jdGlvbiBzdGVwS2V5KHBpcGVsaW5lSWQ6IHN0cmluZywgc3RhZ2VOYW1lOiBzdHJpbmcsIGFjdGlvbk5hbWU6IHN0cmluZyk6IHN0cmluZyB7XG4gIHJldHVybiBgJHtwaXBlbGluZUlkfVxcdTAwMDAke3N0YWdlTmFtZX1cXHUwMDAwJHthY3Rpb25OYW1lfWA7XG59XG5cbi8qKlxuICogUGx1Z2luIGF0dHJpYnV0aW9uOiB0aGUgc3RlcCBtYW5pZmVzdHMgb2YgZXZlcnkgcGlwZWxpbmUgd2l0aCBhbiBhdHRyaWJ1dGFibGVcbiAqIGV2ZW50LCBpbiBPTkUgcmVhZCBwZXIgYmF0Y2gsIGtleWVkIGZvciBhbiBpbi1tZW1vcnkgam9pbi4gTmFtZXMgYXJlIGNvbXBhcmVkXG4gKiBwb3N0LXNjcnViIG9uIGJvdGggc2lkZXMgKHRoZSBtYW5pZmVzdCBzdG9yZXMgdGhlbSBzY3J1YmJlZCB0aGUgc2FtZSB3YXkpLlxuICovXG5hc3luYyBmdW5jdGlvbiBsb2FkU3RlcFBsdWdpbnModHg6IENydWRUeCwgYXR0cmlidXRhYmxlOiBJbmdlc3RFdmVudFtdKTogUHJvbWlzZTxNYXA8c3RyaW5nLCBTdGVwUGx1Z2luPj4ge1xuICBjb25zdCBwaXBlbGluZUlkcyA9IFsuLi5uZXcgU2V0KGF0dHJpYnV0YWJsZS5tYXAoKGUpID0+IGUucGlwZWxpbmVJZCkpXTtcbiAgaWYgKHBpcGVsaW5lSWRzLmxlbmd0aCA9PT0gMCkgcmV0dXJuIG5ldyBNYXAoKTtcbiAgY29uc3Qgcm93cyA9IGF3YWl0IHR4XG4gICAgLnNlbGVjdCh7XG4gICAgICBwaXBlbGluZUlkOiBzY2hlbWEucGlwZWxpbmVTdGVwTWFuaWZlc3QucGlwZWxpbmVJZCxcbiAgICAgIG9yZ0lkOiBzY2hlbWEucGlwZWxpbmVTdGVwTWFuaWZlc3Qub3JnSWQsXG4gICAgICBzdGFnZU5hbWU6IHNjaGVtYS5waXBlbGluZVN0ZXBNYW5pZmVzdC5zdGFnZU5hbWUsXG4gICAgICBhY3Rpb25OYW1lOiBzY2hlbWEucGlwZWxpbmVTdGVwTWFuaWZlc3QuYWN0aW9uTmFtZSxcbiAgICAgIHBsdWdpblB1Ymxpc2hlcjogc2NoZW1hLnBpcGVsaW5lU3RlcE1hbmlmZXN0LnBsdWdpblB1Ymxpc2hlcixcbiAgICAgIHBsdWdpblB1Ymxpc2hlcklkOiBzY2hlbWEucGlwZWxpbmVTdGVwTWFuaWZlc3QucGx1Z2luUHVibGlzaGVySWQsXG4gICAgICBwbHVnaW5OYW1lOiBzY2hlbWEucGlwZWxpbmVTdGVwTWFuaWZlc3QucGx1Z2luTmFtZSxcbiAgICAgIHBsdWdpblZlcnNpb246IHNjaGVtYS5waXBlbGluZVN0ZXBNYW5pZmVzdC5wbHVnaW5WZXJzaW9uLFxuICAgIH0pXG4gICAgLmZyb20oc2NoZW1hLnBpcGVsaW5lU3RlcE1hbmlmZXN0KVxuICAgIC53aGVyZShpbkFycmF5KHNjaGVtYS5waXBlbGluZVN0ZXBNYW5pZmVzdC5waXBlbGluZUlkLCBwaXBlbGluZUlkcykpO1xuICByZXR1cm4gbmV3IE1hcChyb3dzLm1hcCgobSkgPT4gW3N0ZXBLZXkobS5waXBlbGluZUlkLCBtLnN0YWdlTmFtZSwgbS5hY3Rpb25OYW1lKSwgbV0pKTtcbn1cblxuLyoqXG4gKiBUaGUgcGVyc2lzdGVkIHJvdyBmb3Igb25lIGluZ2VzdGVkIGV2ZW50LiBUaGlzIGlzIHRoZSBEVVJBQkxFIHBlcnNpc3RlbmNlXG4gKiBib3VuZGFyeTogZXZlcnkgdXNlci9BV1MtZGVyaXZlZCBmcmVlLWZvcm0gc3RyaW5nIGlzIHNjcnViYmVkIGhlcmUsIGFuZFxuICogdGVuYW5jeSBjb21lcyBmcm9tIHRoZSBwaXBlbGluZSByZWdpc3RyeSwgbmV2ZXIgdGhlIGNhbGxlcidzIGNsYWltZWQgb3JnLlxuICovXG5mdW5jdGlvbiB0b0V2ZW50Um93KGV2ZW50OiBJbmdlc3RFdmVudCwgcmVnaXN0cnk6IHsgcGlwZWxpbmVJZDogc3RyaW5nOyBvcmdJZDogc3RyaW5nIH0sIHBsdWdpbjogU3RlcFBsdWdpbiB8IHVuZGVmaW5lZCk6IFBpcGVsaW5lRXZlbnRJbnNlcnQge1xuICByZXR1cm4ge1xuICAgIC8vIHJlZ2lzdHJ5LnBpcGVsaW5lSWQgPT09IGV2ZW50LnBpcGVsaW5lSWQ7IHRoZSByZWdpc3RyeSdzIGlzIGFsd2F5cyBhIHJvd1xuICAgIC8vIHRoYXQgZXhpc3RzIChGSykgYW5kIGl0cyBvcmdJZCBpcyB0aGUgdHJ1c3RlZCB0ZW5hbnQuXG4gICAgcGlwZWxpbmVJZDogcmVnaXN0cnkucGlwZWxpbmVJZCxcbiAgICBvcmdJZDogcmVnaXN0cnkub3JnSWQsXG4gICAgZXZlbnRTb3VyY2U6IGV2ZW50LmV2ZW50U291cmNlLFxuICAgIGV2ZW50VHlwZTogZXZlbnQuZXZlbnRUeXBlLFxuICAgIHN0YXR1czogZXZlbnQuc3RhdHVzLFxuICAgIC8vIEFuIEFXUy1BU1NJR05FRCBVVUlELCBub3QgZnJlZS1mb3JtIHRleHQ6IGl0IGNhbid0IGNhcnJ5IGFuIGFjY291bnQgaWQsXG4gICAgLy8gYW5kIHNjcnViYmluZyBhIDEyLWRpZ2l0IHJ1biBpbnNpZGUgaXQgd291bGQgY29ycnVwdCB0aGUgY29ycmVsYXRpb24ga2V5LlxuICAgIGV4ZWN1dGlvbklkOiBldmVudC5leGVjdXRpb25JZCxcbiAgICAvLyBVU0VSLUFVVEhPUkVEIHBpcGVsaW5lLXN0cnVjdHVyZSBuYW1lcyDigJQgc2FtZSB1bnRydXN0ZWQgb3JpZ2luIGFzXG4gICAgLy8gZXJyb3JNZXNzYWdlLCBzbyBzY3J1YmJlZCAoYSBzdGFnZSBuYW1lZCB3aXRoIGFuIEFSTiBtdXN0IG5vdCBwZXJzaXN0KS5cbiAgICBzdGFnZU5hbWU6IHNjcnViT3B0aW9uYWwoZXZlbnQuc3RhZ2VOYW1lKSxcbiAgICBhY3Rpb25OYW1lOiBzY3J1Yk9wdGlvbmFsKGV2ZW50LmFjdGlvbk5hbWUpLFxuICAgIC8vIEhBUkQgQ09OU1RSQUlOVDogYW4gQVdTIGFjY291bnQgaWQgbXVzdCBORVZFUiBiZSBwZXJzaXN0ZWQuIEZhaWx1cmUgZGV0YWlsXG4gICAgLy8gYW5kIG1lc3NhZ2VzIHJvdXRpbmVseSBjYXJyeSBBUk5zIGFuZCBiYXJlIDEyLWRpZ2l0IGFjY291bnQgaWRzLlxuICAgIGVycm9yTWVzc2FnZTogc2NydWJPcHRpb25hbChldmVudC5lcnJvck1lc3NhZ2UpLFxuICAgIHN0YXJ0ZWRBdDogZXZlbnQuc3RhcnRlZEF0ID8gbmV3IERhdGUoZXZlbnQuc3RhcnRlZEF0KSA6IHVuZGVmaW5lZCxcbiAgICBjb21wbGV0ZWRBdDogZXZlbnQuY29tcGxldGVkQXQgPyBuZXcgRGF0ZShldmVudC5jb21wbGV0ZWRBdCkgOiB1bmRlZmluZWQsXG4gICAgZHVyYXRpb25NczogZXZlbnQuZHVyYXRpb25NcyxcbiAgICAvLyBEZXBsb3kgYXR0cmlidXRpb24g4oCUIHNjcnViYmVkIGxpa2UgdGhlIG90aGVyIGZyZWUtZm9ybSBzdHJpbmdzLlxuICAgIGNvbW1pdFNoYTogc2NydWJPcHRpb25hbChldmVudC5jb21taXRTaGEpLFxuICAgIGNvbW1pdFJlZjogc2NydWJPcHRpb25hbChldmVudC5jb21taXRSZWYpLFxuICAgIGVudmlyb25tZW50OiBzY3J1Yk9wdGlvbmFsKGV2ZW50LmVudmlyb25tZW50KSxcbiAgICAvLyBNZWFzdXJlZCBsZWFkIHRpbWU6IHRoZSBvbGRlc3QgdW5zaGlwcGVkIGNvbW1pdCdzIHRpbWUgKyB0aGUgY291bnQuIEFuXG4gICAgLy8gU0NNLWRlcml2ZWQgaW5zdGFudCBhbmQgYSBwbGFpbiBpbnRlZ2VyIOKAlCBub3RoaW5nIHRvIHNjcnViLlxuICAgIGNvbW1pdFRpbWVzdGFtcDogZXZlbnQuY29tbWl0VGltZXN0YW1wID8gbmV3IERhdGUoZXZlbnQuY29tbWl0VGltZXN0YW1wKSA6IHVuZGVmaW5lZCxcbiAgICBjb21taXRDb3VudDogZXZlbnQuY29tbWl0Q291bnQsXG4gICAgLy8gTlVMTCBmb3Igbm9uLXBsdWdpbiBhY3Rpb25zIGFuZCBmb3IgcGlwZWxpbmVzIGRlcGxveWVkIHdpdGhvdXQgYSBtYW5pZmVzdC5cbiAgICBwbHVnaW5QdWJsaXNoZXI6IHBsdWdpbj8ucGx1Z2luUHVibGlzaGVyID8/IG51bGwsXG4gICAgcGx1Z2luUHVibGlzaGVySWQ6IHBsdWdpbj8ucGx1Z2luUHVibGlzaGVySWQgPz8gbnVsbCxcbiAgICBwbHVnaW5OYW1lOiBwbHVnaW4/LnBsdWdpbk5hbWUgPz8gbnVsbCxcbiAgICBwbHVnaW5WZXJzaW9uOiBwbHVnaW4/LnBsdWdpblZlcnNpb24gPz8gbnVsbCxcbiAgICBkZXRhaWw6IGV2ZW50LmRldGFpbCAhPT0gdW5kZWZpbmVkID8gc2NydWJBd3NJZGVudGlmaWVycyhldmVudC5kZXRhaWwpIDogdW5kZWZpbmVkLFxuICB9O1xufVxuXG4vKipcbiAqIEZhbiB0ZXJtaW5hbCBTVEFHRSBvdXRjb21lcyBpbnRvIHRoZSByb3V0ZSdzIG1ldHJpYyBob29rIChwaXBlbGluZS1kYXRhIGNhbid0XG4gKiBpbXBvcnQgYXBpLXNlcnZlcidzIHJlZ2lzdHJ5KSBhZnRlciB0aGUgaW5zZXJ0IGNvbW1pdHMuIERyaXZlbiBvZmYgdGhlXG4gKiBJTlNFUlRFRCByb3dzLCBzbyBhIHJlLWRlbGl2ZXJlZCBldmVudCB0aGUgZGVkdXAgaW5kZXggc3dhbGxvd2VkIGlzIG5ldmVyXG4gKiBjb3VudGVkIHR3aWNlLiBBIG5vbi1udWxsIGVudmlyb25tZW50IG1hcmtzIGEgZGVwbG95LlxuICovXG5mdW5jdGlvbiBlbWl0U3RhZ2VNZXRyaWNzKFxuICByb3dzOiBSZWFkb25seUFycmF5PHsgb3JnSWQ6IHN0cmluZzsgcGlwZWxpbmVJZDogc3RyaW5nIHwgbnVsbDsgZXZlbnRUeXBlOiBzdHJpbmc7IHN0YXR1czogc3RyaW5nOyBzdGFnZU5hbWU6IHN0cmluZyB8IG51bGw7IGVudmlyb25tZW50OiBzdHJpbmcgfCBudWxsIH0+LFxuICBvbk1ldHJpYzogKG06IEluZ2VzdE1ldHJpYykgPT4gdm9pZCxcbik6IHZvaWQge1xuICBmb3IgKGNvbnN0IHIgb2Ygcm93cykge1xuICAgIGlmIChyLmV2ZW50VHlwZSAhPT0gJ1NUQUdFJyB8fCAoci5zdGF0dXMgIT09ICdTVUNDRUVERUQnICYmIHIuc3RhdHVzICE9PSAnRkFJTEVEJykgfHwgIXIucGlwZWxpbmVJZCkgY29udGludWU7XG4gICAgb25NZXRyaWMoe1xuICAgICAgcGlwZWxpbmVJZDogci5waXBlbGluZUlkLFxuICAgICAgb3JnSWQ6IHIub3JnSWQsXG4gICAgICBzdGFnZTogci5zdGFnZU5hbWUgPz8gJycsXG4gICAgICBlbnZpcm9ubWVudDogci5lbnZpcm9ubWVudCA/PyBudWxsLFxuICAgICAgcmVzdWx0OiByLnN0YXR1cyA9PT0gJ1NVQ0NFRURFRCcgPyAnc3VjY2VlZGVkJyA6ICdmYWlsZWQnLFxuICAgIH0pO1xuICB9XG59XG5cbi8qKlxuICogUmVzb2x2ZSBpbmNvbWluZyBldmVudHMgYWdhaW5zdCB0aGUgcGlwZWxpbmUgcmVnaXN0cnksIGJhdGNoLWluc2VydCB0aGVcbiAqIG1hdGNoZWQgb25lcywgYW5kIGludmFsaWRhdGUgcmVwb3J0aW5nIGNhY2hlcyBmb3IgYWZmZWN0ZWQgb3Jncy5cbiAqIEV2ZW50cyBmb3IgdW5yZWdpc3RlcmVkIHBpcGVsaW5lIGlkcyBhcmUgZHJvcHBlZCAoYW5kIGxvZ2dlZCBhdCBXQVJOXG4gKiB3aXRoIHNhbXBsZSBpZHMgc28gYW4gb3BlcmF0b3IgY2FuIHNlZSB3aGVuIEV2ZW50QnJpZGdlIGlzIGRlbGl2ZXJpbmdcbiAqIGV2ZW50cyBmb3IgcGlwZWxpbmVzIHRoYXQgaGF2ZW4ndCBjYWxsZWQgUE9TVCAvcGlwZWxpbmVzL3JlZ2lzdHJ5IHlldCkuXG4gKlxuICogUmV0dXJucyBjb3VudHMgKyBhIHNhbXBsZSBvZiB1bnJlZ2lzdGVyZWQgcGlwZWxpbmUgaWRzIGZvciBvYnNlcnZhYmlsaXR5LlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gaW5nZXN0RXZlbnRzKGV2ZW50czogSW5nZXN0RXZlbnRbXSwgb25NZXRyaWM/OiAobTogSW5nZXN0TWV0cmljKSA9PiB2b2lkKTogUHJvbWlzZTxJbmdlc3RSZXN1bHQ+IHtcbiAgLy8gTXVsdGktb3JnIGJhdGNoIGluc2VydDogdGhlIGNhbGxlciByZXNvbHZlcyB0byBtdWx0aXBsZSBvcmdzIHZpYSB0aGVcbiAgLy8gcGlwZWxpbmUtcmVnaXN0cnkgbG9va3VwIGJlbG93LCBzbyB0aGUgcm91dGUgbGF5ZXIgTVVTVCBlc3RhYmxpc2ggYVxuICAvLyBgcnVuV2l0aFRlbmFudENvbnRleHQoeyBpc1N1cGVyQWRtaW46IHRydWUgfSwgLi4uKWAgc2NvcGUgYmVmb3JlIGNhbGxpbmdcbiAgLy8gdGhpcyBmdW5jdGlvbi4gVW5kZXIgRk9SQ0UnZCBSTFMsIGEgc2luZ2xlIHR4IHdpdGggYGFwcC5vcmdfaWQgPSA8b25lXG4gIC8vIG9yZz5gIGNvdWxkIG9ubHkgd3JpdGUgZXZlbnRzIGZvciB0aGF0IG9yZzsgYnlwYXNzIHZpYSBzeXNhZG1pbiBpc1xuICAvLyB0aGUgcmlnaHQgZ2F0ZSBmb3IgdGhpcyBzZXJ2ZXItaW50ZXJuYWwgY3Jvc3MtdGVuYW50IGVuZHBvaW50LiBTZWVcbiAgLy8gYXBpL3JlcG9ydGluZy9zcmMvcm91dGVzL2V2ZW50LWluZ2VzdC50cyBmb3IgdGhlIHdyYXBwZXIuXG4gIC8vIENhY2hlcyBhcmUgaW52YWxpZGF0ZWQgQUZURVIgdGhlIHR4IHJlc29sdmVzOiBkb2luZyBpdCBpbnNpZGUgaGVsZCB0aGUgcGdcbiAgLy8gbG9ja3Mgb3BlbiBhY3Jvc3MgdW5yZWxhdGVkIGNhY2hlIHJvdW5kLXRyaXBzLiBUaGUgVFRMIGlzIDItNSBtaW4sIHNvIGFcbiAgLy8gZmlyZS1hbmQtZm9yZ2V0IHBvc3QtY29tbWl0IGludmFsaWRhdGlvbiBpcyBhbiBhY2NlcHRhYmxlIHRyYWRlLlxuICBjb25zdCB7IGluc2VydGVkLCBza2lwcGVkLCB1bnJlZ2lzdGVyZWRQaXBlbGluZUlkcywgYWZmZWN0ZWRPcmdzLCBpbnNlcnRlZFJvd3MgfSA9IGF3YWl0IHdpdGhUZW5hbnRUeChhc3luYyAodHgpID0+IHtcbiAgICAvLyBCYXRjaC1yZXNvbHZlIGFsbCB1bmlxdWUgcGlwZWxpbmUgaWRzIGluIG9uZSBxdWVyeVxuICAgIGNvbnN0IHVuaXF1ZUlkcyA9IFsuLi5uZXcgU2V0KGV2ZW50cy5tYXAoZSA9PiBlLnBpcGVsaW5lSWQpKV07XG4gICAgY29uc3QgcmVnaXN0cnlSb3dzID0gYXdhaXQgdHhcbiAgICAgIC5zZWxlY3Qoe1xuICAgICAgICBwaXBlbGluZUlkOiBzY2hlbWEucGlwZWxpbmVSZWdpc3RyeS5waXBlbGluZUlkLFxuICAgICAgICBvcmdJZDogc2NoZW1hLnBpcGVsaW5lUmVnaXN0cnkub3JnSWQsXG4gICAgICB9KVxuICAgICAgLmZyb20oc2NoZW1hLnBpcGVsaW5lUmVnaXN0cnkpXG4gICAgICAud2hlcmUoaW5BcnJheShzY2hlbWEucGlwZWxpbmVSZWdpc3RyeS5waXBlbGluZUlkLCB1bmlxdWVJZHMpKTtcblxuICAgIGNvbnN0IGlkTWFwID0gbmV3IE1hcChyZWdpc3RyeVJvd3MubWFwKHIgPT4gW3IucGlwZWxpbmVJZCwgcl0pKTtcbiAgICBjb25zdCBzdGVwUGx1Z2lucyA9IGF3YWl0IGxvYWRTdGVwUGx1Z2lucyh0eCwgZXZlbnRzLmZpbHRlcigoZSkgPT4gaXNBdHRyaWJ1dGFibGUoZSkgJiYgaWRNYXAuaGFzKGUucGlwZWxpbmVJZCkpKTtcblxuICAgIC8vIEJ1aWxkIGluc2VydCBiYXRjaCAoc2tpcCBldmVudHMgd2hvc2UgcGlwZWxpbmUgaXNuJ3QgcmVnaXN0ZXJlZClcbiAgICBjb25zdCByb3dzOiBQaXBlbGluZUV2ZW50SW5zZXJ0W10gPSBbXTtcbiAgICBsZXQgc2tpcHBlZExvY2FsID0gMDtcbiAgICBjb25zdCB1bnJlZ2lzdGVyZWRMb2NhbDogc3RyaW5nW10gPSBbXTtcblxuICAgIGZvciAoY29uc3QgZXZlbnQgb2YgZXZlbnRzKSB7XG4gICAgICBjb25zdCByZWdpc3RyeSA9IGlkTWFwLmdldChldmVudC5waXBlbGluZUlkKTtcbiAgICAgIGlmICghcmVnaXN0cnkpIHtcbiAgICAgICAgc2tpcHBlZExvY2FsKys7XG4gICAgICAgIHVucmVnaXN0ZXJlZExvY2FsLnB1c2goZXZlbnQucGlwZWxpbmVJZCk7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuICAgICAgY29uc3Qgc3RhZ2VOYW1lID0gc2NydWJPcHRpb25hbChldmVudC5zdGFnZU5hbWUpO1xuICAgICAgY29uc3QgYWN0aW9uTmFtZSA9IHNjcnViT3B0aW9uYWwoZXZlbnQuYWN0aW9uTmFtZSk7XG4gICAgICBjb25zdCBzdGVwID0gaXNBdHRyaWJ1dGFibGUoZXZlbnQpICYmIHN0YWdlTmFtZSAmJiBhY3Rpb25OYW1lXG4gICAgICAgID8gc3RlcFBsdWdpbnMuZ2V0KHN0ZXBLZXkocmVnaXN0cnkucGlwZWxpbmVJZCwgc3RhZ2VOYW1lLCBhY3Rpb25OYW1lKSlcbiAgICAgICAgOiB1bmRlZmluZWQ7XG4gICAgICAvLyBTYW1lLW9yZyBndWFyZDogYSBtYW5pZmVzdCByb3cgaXMgb25seSBldmVyIHdyaXR0ZW4gYnkgdGhlIG9yZyB0aGF0XG4gICAgICAvLyBvd25zIHRoZSBwaXBlbGluZSwgYnV0IHRoZSBpbmdlc3QgcnVucyBjcm9zcy10ZW5hbnQsIHNvIGRvbid0IGxlYW4gb24gaXQuXG4gICAgICByb3dzLnB1c2godG9FdmVudFJvdyhldmVudCwgcmVnaXN0cnksIHN0ZXAgJiYgc3RlcC5vcmdJZCA9PT0gcmVnaXN0cnkub3JnSWQgPyBzdGVwIDogdW5kZWZpbmVkKSk7XG4gICAgfVxuXG4gICAgLy8gU1FTIGlzIGF0LWxlYXN0LW9uY2UsIHNvIEV2ZW50QnJpZGdlIGNhbiBkZWxpdmVyIHRoZSBzYW1lIHN0YXRlLWNoYW5nZVxuICAgIC8vIHR3aWNlLiBgb25Db25mbGljdERvTm90aGluZ2AgKyB0aGUgcGFydGlhbCB1bmlxdWUgaW5kZXggb25cbiAgICAvLyAocGlwZWxpbmVfaWQsIGV4ZWN1dGlvbl9pZCwgZXZlbnRfdHlwZSwgc3RhdHVzLCBzdGFnZV9uYW1lLCBhY3Rpb25fbmFtZSlcbiAgICAvLyBtYWtlcyByZS1kZWxpdmVyeSBpZGVtcG90ZW50LiBgcmV0dXJuaW5nYCBnaXZlcyB0aGUgUkVBTCBpbnNlcnRlZCBzZXQgc29cbiAgICAvLyBjb3VudHMsIG1ldHJpY3MgYW5kIGNhY2hlIGludmFsaWRhdGlvbiBhbGwgaWdub3JlIGR1cGxpY2F0ZXMuXG4gICAgY29uc3QgbGFuZGVkID0gcm93cy5sZW5ndGggPiAwXG4gICAgICA/IGF3YWl0IHR4Lmluc2VydChzY2hlbWEucGlwZWxpbmVFdmVudCkudmFsdWVzKHJvd3MpXG4gICAgICAgIC5vbkNvbmZsaWN0RG9Ob3RoaW5nKClcbiAgICAgICAgLnJldHVybmluZyh7XG4gICAgICAgICAgb3JnSWQ6IHNjaGVtYS5waXBlbGluZUV2ZW50Lm9yZ0lkLFxuICAgICAgICAgIHBpcGVsaW5lSWQ6IHNjaGVtYS5waXBlbGluZUV2ZW50LnBpcGVsaW5lSWQsXG4gICAgICAgICAgZXZlbnRUeXBlOiBzY2hlbWEucGlwZWxpbmVFdmVudC5ldmVudFR5cGUsXG4gICAgICAgICAgc3RhdHVzOiBzY2hlbWEucGlwZWxpbmVFdmVudC5zdGF0dXMsXG4gICAgICAgICAgc3RhZ2VOYW1lOiBzY2hlbWEucGlwZWxpbmVFdmVudC5zdGFnZU5hbWUsXG4gICAgICAgICAgZW52aXJvbm1lbnQ6IHNjaGVtYS5waXBlbGluZUV2ZW50LmVudmlyb25tZW50LFxuICAgICAgICB9KVxuICAgICAgOiBbXTtcblxuICAgIHJldHVybiB7XG4gICAgICBpbnNlcnRlZDogbGFuZGVkLmxlbmd0aCxcbiAgICAgIHNraXBwZWQ6IHNraXBwZWRMb2NhbCxcbiAgICAgIHVucmVnaXN0ZXJlZFBpcGVsaW5lSWRzOiB1bnJlZ2lzdGVyZWRMb2NhbCxcbiAgICAgIGFmZmVjdGVkT3JnczogWy4uLm5ldyBTZXQobGFuZGVkLm1hcChyID0+IHIub3JnSWQpKV0sXG4gICAgICBpbnNlcnRlZFJvd3M6IGxhbmRlZCxcbiAgICB9O1xuICB9KTtcblxuICBpZiAob25NZXRyaWMpIGVtaXRTdGFnZU1ldHJpY3MoaW5zZXJ0ZWRSb3dzLCBvbk1ldHJpYyk7XG5cbiAgLy8gU3VyZmFjZSB0aGUgc2lsZW50IHNraXA6IGFuIHVucmVnaXN0ZXJlZCBwaXBlbGluZSBpZCB1c3VhbGx5IG1lYW5zIHRoZVxuICAvLyBwaXBlbGluZSBoYXNuJ3QgY2FsbGVkIFBPU1QgL3BpcGVsaW5lcy9yZWdpc3RyeSB5ZXQgKG9yIGl0c1xuICAvLyBwYi5waXBlbGluZS1pZCB0YWcgaXMgbWlzc2luZy91bnJlYWRhYmxlIGJ5IHRoZSBMYW1iZGEpLiBMb2dnaW5nIGl0XG4gIC8vIG1ha2VzIGEgYnJva2VuIGpvaW4gdmlzaWJsZSBpbnN0ZWFkIG9mIGxvb2tpbmcgbGlrZSBcIm5vIGFjdGl2aXR5XCIuXG4gIGlmICh1bnJlZ2lzdGVyZWRQaXBlbGluZUlkcy5sZW5ndGggPiAwKSB7XG4gICAgbG9nZ2VyLndhcm4oJ1BpcGVsaW5lIGV2ZW50cyBza2lwcGVkOiBwaXBlbGluZSBpZCBub3QgZm91bmQgaW4gcmVnaXN0cnknLCB7XG4gICAgICBjb3VudDogdW5yZWdpc3RlcmVkUGlwZWxpbmVJZHMubGVuZ3RoLFxuICAgICAgc2FtcGxlOiB1bnJlZ2lzdGVyZWRQaXBlbGluZUlkcy5zbGljZSgwLCAzKSxcbiAgICB9KTtcbiAgfVxuXG4gIC8vIFBvc3QtY29tbWl0IGNhY2hlIGludmFsaWRhdGlvbi4gRmlyZS1hbmQtZm9yZ2V0IHdpdGggbG9nZ2luZyDigJQgVFRMIGlzXG4gIC8vIHNob3J0IGVub3VnaCB0aGF0IGEgbWlzc2VkIGludmFsaWRhdGlvbiBzZWxmLWhlYWxzLlxuICBpZiAoYWZmZWN0ZWRPcmdzLmxlbmd0aCA+IDApIHtcbiAgICB2b2lkIFByb21pc2UuYWxsKGFmZmVjdGVkT3Jncy5tYXAoKG9yZykgPT5cbiAgICAgIGludmFsaWRhdGVPcmdSZXBvcnRzKG9yZykuY2F0Y2goKGVycikgPT4ge1xuICAgICAgICBsb2dnZXIud2FybignUmVwb3J0aW5nIGNhY2hlIGludmFsaWRhdGlvbiBmYWlsZWQnLCB7IG9yZ0lkOiBvcmcsIGVycm9yOiBlcnJvck1lc3NhZ2UoZXJyKSB9KTtcbiAgICAgIH0pLFxuICAgICkpO1xuICB9XG5cbiAgcmV0dXJuIHsgaW5zZXJ0ZWQsIHNraXBwZWQsIHVucmVnaXN0ZXJlZFBpcGVsaW5lSWRzLCBhZmZlY3RlZE9yZ3MgfTtcbn1cbiJdfQ==
@@ -0,0 +1,17 @@
1
+ import type { ReportingRetentionCounts, ReportingRetentionOptions } from './types.js';
2
+ /**
3
+ * Reporting retention sweep. Hard-deletes rows older than their
4
+ * retention window, by `created_at`, across every org that has reporting data —
5
+ * a **split** policy so high-volume standard events expire faster than the
6
+ * low-volume DORA source:
7
+ * - `pipeline_events WHERE environment IS NULL` → standard-event window.
8
+ * - `pipeline_events WHERE environment IS NOT NULL` (deploy stages),
9
+ * `deployment_outcomes`, and `incidents` → DORA-source window.
10
+ * Each org's windows come from its `dora_settings` override, else the global
11
+ * env defaults (see {@link resolveEventRetentionDays} / {@link resolveDoraRetentionDays}).
12
+ * `ingest_health` and `dora_settings` are never purged. One `now` for the whole
13
+ * tick (rows crossing the boundary mid-sweep wait for the next). Establishes a
14
+ * sysadmin tenant scope so the deletes span all orgs / bypass RLS — this is a
15
+ * cross-tenant housekeeping job. Returns (and logs) per-window purge tallies.
16
+ */
17
+ export declare function purgeExpiredReportingData(opts?: ReportingRetentionOptions): Promise<ReportingRetentionCounts>;
@@ -0,0 +1,155 @@
1
+ // Copyright 2026 Pipeline Builder Contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * The reporting retention SWEEP: the batched hard-delete that enforces the
5
+ * windows `./retention.ts` resolves. Split from the windows module so the pure,
6
+ * env-only policy stays free of database code, and from the reporting service
7
+ * because this is cross-tenant housekeeping, not a report.
8
+ */
9
+ import { createLogger, errorMessage } from '@pipeline-builder/api-core';
10
+ import { sql } from 'drizzle-orm';
11
+ import { resolveEventRetentionDays, resolveDoraRetentionDays, retentionCutoff } from './retention.js';
12
+ import { schema } from '../../database/drizzle-schema.js';
13
+ import { withTenantTx, runWithTenantContext } from '../../database/tenancy.js';
14
+ import { drizzleRows } from '../crud-service.js';
15
+ const logger = createLogger('reporting-retention');
16
+ /**
17
+ * Batched hard-DELETE of one reporting table's rows expired past a `created_at`
18
+ * cutoff, scoped to an org and an optional row predicate. Uses a
19
+ * `ctid`-in-subquery LIMIT so each statement touches at most `batchSize` rows
20
+ * (short lock windows, no long table scan under lock); `RETURNING 1` lets us
21
+ * count via `.rows.length` without depending on the driver's `rowCount`. Loops
22
+ * until a short batch (drained) or the per-tick cap, then defers the rest.
23
+ * MUST run inside a sysadmin tenant scope (see purgeExpiredReportingData) so it
24
+ * bypasses RLS and can delete across the org's rows.
25
+ */
26
+ async function purgeReportingTableBatched(tableSql, predicate, batchSize, maxBatches) {
27
+ let total = 0;
28
+ for (let i = 0; i < maxBatches; i++) {
29
+ const res = await withTenantTx((tx) => tx.execute(sql `
30
+ DELETE FROM ${tableSql}
31
+ WHERE ctid IN (
32
+ SELECT ctid FROM ${tableSql}
33
+ WHERE ${predicate}
34
+ LIMIT ${batchSize}
35
+ )
36
+ RETURNING 1
37
+ `));
38
+ const purged = drizzleRows(res.rows).length;
39
+ total += purged;
40
+ if (purged < batchSize)
41
+ break; // drained
42
+ if (i === maxBatches - 1) {
43
+ logger.warn('Reporting retention hit per-tick cap; remaining rows deferred', {
44
+ purgedThisTick: total,
45
+ });
46
+ }
47
+ }
48
+ return total;
49
+ }
50
+ /**
51
+ * Reporting retention sweep. Hard-deletes rows older than their
52
+ * retention window, by `created_at`, across every org that has reporting data —
53
+ * a **split** policy so high-volume standard events expire faster than the
54
+ * low-volume DORA source:
55
+ * - `pipeline_events WHERE environment IS NULL` → standard-event window.
56
+ * - `pipeline_events WHERE environment IS NOT NULL` (deploy stages),
57
+ * `deployment_outcomes`, and `incidents` → DORA-source window.
58
+ * Each org's windows come from its `dora_settings` override, else the global
59
+ * env defaults (see {@link resolveEventRetentionDays} / {@link resolveDoraRetentionDays}).
60
+ * `ingest_health` and `dora_settings` are never purged. One `now` for the whole
61
+ * tick (rows crossing the boundary mid-sweep wait for the next). Establishes a
62
+ * sysadmin tenant scope so the deletes span all orgs / bypass RLS — this is a
63
+ * cross-tenant housekeeping job. Returns (and logs) per-window purge tallies.
64
+ */
65
+ export async function purgeExpiredReportingData(opts = {}) {
66
+ const batchSize = Math.max(1, opts.batchSize ?? 1000);
67
+ const maxBatches = Math.max(1, opts.maxBatchesPerTable ?? 50);
68
+ const now = opts.now ?? new Date();
69
+ const resolveRetentionOrgId = opts.resolveRetentionOrgId ?? (async (orgId) => orgId);
70
+ const counts = {
71
+ orgs: 0, standardEvents: 0, doraEvents: 0, deploymentOutcomes: 0, incidents: 0,
72
+ };
73
+ return runWithTenantContext({ isSuperAdmin: true }, async () => {
74
+ // Enumerate every org with reporting data (union across the three tables).
75
+ const orgRows = drizzleRows((await withTenantTx((tx) => tx.execute(sql `
76
+ SELECT DISTINCT org_id FROM (
77
+ SELECT ${schema.pipelineEvent.orgId} AS org_id FROM ${schema.pipelineEvent}
78
+ UNION
79
+ SELECT ${schema.deploymentOutcome.orgId} AS org_id FROM ${schema.deploymentOutcome}
80
+ UNION
81
+ SELECT ${schema.incident.orgId} AS org_id FROM ${schema.incident}
82
+ ) AS orgs
83
+ `))).rows);
84
+ if (orgRows.length === 0)
85
+ return counts;
86
+ // Per-org retention overrides (a single read; orgs without a row use defaults).
87
+ const overrideRows = drizzleRows((await withTenantTx((tx) => tx.execute(sql `
88
+ SELECT ${schema.doraSettings.orgId} AS org_id,
89
+ ${schema.doraSettings.eventRetentionDays} AS event_retention_days,
90
+ ${schema.doraSettings.doraRetentionDays} AS dora_retention_days
91
+ FROM ${schema.doraSettings}
92
+ `))).rows);
93
+ const overrides = new Map(overrideRows.map((r) => [r.org_id, r]));
94
+ const eventsTable = sql `${schema.pipelineEvent}`;
95
+ const outcomesTable = sql `${schema.deploymentOutcome}`;
96
+ const incidentsTable = sql `${schema.incident}`;
97
+ for (const { org_id: orgId } of orgRows) {
98
+ // Retention follows the account ROOT's entitlement (billing syncs it onto
99
+ // the root only). An unresolvable root ⇒ skip this org this tick rather
100
+ // than purge a team's rows on the (shorter) env default.
101
+ let retentionOrgId;
102
+ try {
103
+ retentionOrgId = await resolveRetentionOrgId(orgId);
104
+ }
105
+ catch (err) {
106
+ logger.warn('Reporting retention: root resolution threw; skipping org this tick', { orgId, error: errorMessage(err) });
107
+ retentionOrgId = null;
108
+ }
109
+ if (retentionOrgId === null) {
110
+ logger.warn('Reporting retention: could not resolve the retention root; org skipped this tick', { orgId });
111
+ continue;
112
+ }
113
+ const ov = overrides.get(retentionOrgId);
114
+ const eventDays = resolveEventRetentionDays(ov?.event_retention_days);
115
+ const doraDays = resolveDoraRetentionDays(ov?.dora_retention_days);
116
+ // `-1` = unlimited: keep forever, skip that window's deletes for
117
+ // this org. Standard-event and DORA-source windows are independent; an org
118
+ // with both `-1` is fully skipped. Log the skip so it's observable.
119
+ const skipEvents = eventDays === -1;
120
+ const skipDora = doraDays === -1;
121
+ if (skipEvents || skipDora) {
122
+ logger.info('Reporting retention sweep skipping unlimited window(s)', {
123
+ orgId,
124
+ standardEvents: skipEvents ? 'unlimited' : eventDays,
125
+ doraSource: skipDora ? 'unlimited' : doraDays,
126
+ });
127
+ }
128
+ if (!skipEvents) {
129
+ const eventCutoff = retentionCutoff(now, eventDays);
130
+ counts.standardEvents += await purgeReportingTableBatched(eventsTable,
131
+ // Spare rows carrying a commit timestamp: they are DORA SOURCE data.
132
+ // Lead time joins deploys to the execution's earliest
133
+ // `commit_timestamp`, and that enrichment rides the PIPELINE/source
134
+ // event (environment IS NULL), never the deploy STAGE row. Purging
135
+ // them on this short window while the deploys they explain live on
136
+ // the DORA window made lead time read "unknown" for every range
137
+ // older than the standard retention — silently, with the deploys
138
+ // still counted. They follow the DORA window below instead.
139
+ sql `org_id = ${orgId} AND environment IS NULL AND commit_timestamp IS NULL AND created_at < ${eventCutoff}`, batchSize, maxBatches);
140
+ }
141
+ if (!skipDora) {
142
+ const doraCutoff = retentionCutoff(now, doraDays);
143
+ counts.doraEvents += await purgeReportingTableBatched(eventsTable, sql `org_id = ${orgId} AND (environment IS NOT NULL OR commit_timestamp IS NOT NULL) AND created_at < ${doraCutoff}`, batchSize, maxBatches);
144
+ counts.deploymentOutcomes += await purgeReportingTableBatched(outcomesTable, sql `org_id = ${orgId} AND created_at < ${doraCutoff}`, batchSize, maxBatches);
145
+ counts.incidents += await purgeReportingTableBatched(incidentsTable, sql `org_id = ${orgId} AND created_at < ${doraCutoff}`, batchSize, maxBatches);
146
+ }
147
+ counts.orgs += 1;
148
+ }
149
+ const purgedAny = counts.standardEvents + counts.doraEvents + counts.deploymentOutcomes + counts.incidents;
150
+ if (purgedAny > 0)
151
+ logger.info('Reporting retention sweep purged expired rows', { ...counts });
152
+ return counts;
153
+ });
154
+ }
155
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmV0ZW50aW9uLXN3ZWVwLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2FwaS9yZXBvcnRpbmcvcmV0ZW50aW9uLXN3ZWVwLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFFdEM7Ozs7O0dBS0c7QUFFSCxPQUFPLEVBQUUsWUFBWSxFQUFFLFlBQVksRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQ3hFLE9BQU8sRUFBRSxHQUFHLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDbEMsT0FBTyxFQUFFLHlCQUF5QixFQUFFLHdCQUF3QixFQUFFLGVBQWUsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRXRHLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxrQ0FBa0MsQ0FBQztBQUMxRCxPQUFPLEVBQUUsWUFBWSxFQUFFLG9CQUFvQixFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFDL0UsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBRWpELE1BQU0sTUFBTSxHQUFHLFlBQVksQ0FBQyxxQkFBcUIsQ0FBQyxDQUFDO0FBRW5EOzs7Ozs7Ozs7R0FTRztBQUNILEtBQUssVUFBVSwwQkFBMEIsQ0FDdkMsUUFBZ0MsRUFDaEMsU0FBaUMsRUFDakMsU0FBaUIsRUFDakIsVUFBa0I7SUFFbEIsSUFBSSxLQUFLLEdBQUcsQ0FBQyxDQUFDO0lBQ2QsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLFVBQVUsRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDO1FBQ3BDLE1BQU0sR0FBRyxHQUFHLE1BQU0sWUFBWSxDQUFDLENBQUMsRUFBRSxFQUFFLEVBQUUsQ0FBQyxFQUFFLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQTtvQkFDckMsUUFBUTs7MkJBRUQsUUFBUTtnQkFDbkIsU0FBUztnQkFDVCxTQUFTOzs7S0FHcEIsQ0FBQyxDQUFDLENBQUM7UUFDSixNQUFNLE1BQU0s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TUFBTSwwQkFBMEIsQ0FDbEQsY0FBYyxFQUNkLEdBQUcsQ0FBQSxZQUFZLEtBQUsscUJBQXFCLFVBQVUsRUFBRSxFQUNyRCxTQUFTLEVBQUUsVUFBVSxDQUN0QixDQUFDO1lBQ0osQ0FBQztZQUNELE1BQU0sQ0FBQyxJQUFJLElBQUksQ0FBQyxDQUFDO1FBQ25CLENBQUM7UUFFRCxNQUFNLFNBQVMsR0FBRyxNQUFNLENBQUMsY0FBYyxHQUFHLE1BQU0sQ0FBQyxVQUFVLEdBQUcsTUFBTSxDQUFDLGtCQUFrQixHQUFHLE1BQU0sQ0FBQyxTQUFTLENBQUM7UUFDM0csSUFBSSxTQUFTLEdBQUcsQ0FBQztZQUFFLE1BQU0sQ0FBQyxJQUFJLENBQUMsK0NBQStDLEVBQUUsRUFBRSxHQUFHLE1BQU0sRUFBRSxDQUFDLENBQUM7UUFDL0YsT0FBTyxNQUFNLENBQUM7SUFDaEIsQ0FBQyxDQUFDLENBQUM7QUFDTCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbi8qKlxuICogVGhlIHJlcG9ydGluZyByZXRlbnRpb24gU1dFRVA6IHRoZSBiYXRjaGVkIGhhcmQtZGVsZXRlIHRoYXQgZW5mb3JjZXMgdGhlXG4gKiB3aW5kb3dzIGAuL3JldGVudGlvbi50c2AgcmVzb2x2ZXMuIFNwbGl0IGZyb20gdGhlIHdpbmRvd3MgbW9kdWxlIHNvIHRoZSBwdXJlLFxuICogZW52LW9ubHkgcG9saWN5IHN0YXlzIGZyZWUgb2YgZGF0YWJhc2UgY29kZSwgYW5kIGZyb20gdGhlIHJlcG9ydGluZyBzZXJ2aWNlXG4gKiBiZWNhdXNlIHRoaXMgaXMgY3Jvc3MtdGVuYW50IGhvdXNla2VlcGluZywgbm90IGEgcmVwb3J0LlxuICovXG5cbmltcG9ydCB7IGNyZWF0ZUxvZ2dlciwgZXJyb3JNZXNzYWdlIH0gZnJvbSAnQHBpcGVsaW5lLWJ1aWxkZXIvYXBpLWNvcmUnO1xuaW1wb3J0IHsgc3FsIH0gZnJvbSAnZHJpenpsZS1vcm0nO1xuaW1wb3J0IHsgcmVzb2x2ZUV2ZW50UmV0ZW50aW9uRGF5cywgcmVzb2x2ZURvcmFSZXRlbnRpb25EYXlzLCByZXRlbnRpb25DdXRvZmYgfSBmcm9tICcuL3JldGVudGlvbi5qcyc7XG5pbXBvcnQgdHlwZSB7IFJlcG9ydGluZ1JldGVudGlvbkNvdW50cywgUmVwb3J0aW5nUmV0ZW50aW9uT3B0aW9ucyB9IGZyb20gJy4vdHlwZXMuanMnO1xuaW1wb3J0IHsgc2NoZW1hIH0gZnJvbSAnLi4vLi4vZGF0YWJhc2UvZHJpenpsZS1zY2hlbWEuanMnO1xuaW1wb3J0IHsgd2l0aFRlbmFudFR4LCBydW5XaXRoVGVuYW50Q29udGV4dCB9IGZyb20gJy4uLy4uL2RhdGFiYXNlL3RlbmFuY3kuanMnO1xuaW1wb3J0IHsgZHJpenpsZVJvd3MgfSBmcm9tICcuLi9jcnVkLXNlcnZpY2UuanMnO1xuXG5jb25zdCBsb2dnZXIgPSBjcmVhdGVMb2dnZXIoJ3JlcG9ydGluZy1yZXRlbnRpb24nKTtcblxuLyoqXG4gKiBCYXRjaGVkIGhhcmQtREVMRVRFIG9mIG9uZSByZXBvcnRpbmcgdGFibGUncyByb3dzIGV4cGlyZWQgcGFzdCBhIGBjcmVhdGVkX2F0YFxuICogY3V0b2ZmLCBzY29wZWQgdG8gYW4gb3JnIGFuZCBhbiBvcHRpb25hbCByb3cgcHJlZGljYXRlLiBVc2VzIGFcbiAqIGBjdGlkYC1pbi1zdWJxdWVyeSBMSU1JVCBzbyBlYWNoIHN0YXRlbWVudCB0b3VjaGVzIGF0IG1vc3QgYGJhdGNoU2l6ZWAgcm93c1xuICogKHNob3J0IGxvY2sgd2luZG93cywgbm8gbG9uZyB0YWJsZSBzY2FuIHVuZGVyIGxvY2spOyBgUkVUVVJOSU5HIDFgIGxldHMgdXNcbiAqIGNvdW50IHZpYSBgLnJvd3MubGVuZ3RoYCB3aXRob3V0IGRlcGVuZGluZyBvbiB0aGUgZHJpdmVyJ3MgYHJvd0NvdW50YC4gTG9vcHNcbiAqIHVudGlsIGEgc2hvcnQgYmF0Y2ggKGRyYWluZWQpIG9yIHRoZSBwZXItdGljayBjYXAsIHRoZW4gZGVmZXJzIHRoZSByZXN0LlxuICogTVVTVCBydW4gaW5zaWRlIGEgc3lzYWRtaW4gdGVuYW50IHNjb3BlIChzZWUgcHVyZ2VFeHBpcmVkUmVwb3J0aW5nRGF0YSkgc28gaXRcbiAqIGJ5cGFzc2VzIFJMUyBhbmQgY2FuIGRlbGV0ZSBhY3Jvc3MgdGhlIG9yZydzIHJvd3MuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIHB1cmdlUmVwb3J0aW5nVGFibGVCYXRjaGVkKFxuICB0YWJsZVNxbDogUmV0dXJuVHlwZTx0eXBlb2Ygc3FsPixcbiAgcHJlZGljYXRlOiBSZXR1cm5UeXBlPHR5cGVvZiBzcWw+LFxuICBiYXRjaFNpemU6IG51bWJlcixcbiAgbWF4QmF0Y2hlczogbnVtYmVyLFxuKTogUHJvbWlzZTxudW1iZXI+IHtcbiAgbGV0IHRvdGFsID0gMDtcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCBtYXhCYXRjaGVzOyBpKyspIHtcbiAgICBjb25zdCByZXMgPSBhd2FpdCB3aXRoVGVuYW50VHgoKHR4KSA9PiB0eC5leGVjdXRlKHNxbGBcbiAgICAgIERFTEVURSBGUk9NICR7dGFibGVTcWx9XG4gICAgICBXSEVSRSBjdGlkIElOIChcbiAgICAgICAgU0VMRUNUIGN0aWQgRlJPTSAke3RhYmxlU3FsfVxuICAgICAgICBXSEVSRSAke3ByZWRpY2F0ZX1cbiAgICAgICAgTElNSVQgJHtiYXRjaFNpemV9XG4gICAgICApXG4gICAgICBSRVRVUk5JTkcgMVxuICAgIGApKTtcbiAgICBjb25zdCBwdXJnZWQgPSBkcml6emxlUm93czx1bmtub3duPihyZXMucm93cykubGVuZ3RoO1xuICAgIHRvdGFsICs9IHB1cmdlZDtcbiAgICBpZiAocHVyZ2VkIDwgYmF0Y2hTaXplKSBicmVhazsgLy8gZHJhaW5lZFxuICAgIGlmIChpID09PSBtYXhCYXRjaGVzIC0gMSkge1xuICAgICAgbG9nZ2VyLndhcm4oJ1JlcG9ydGluZyByZXRlbnRpb24gaGl0IHBlci10aWNrIGNhcDsgcmVtYWluaW5nIHJvd3MgZGVmZXJyZWQnLCB7XG4gICAgICAgIHB1cmdlZFRoaXNUaWNrOiB0b3RhbCxcbiAgICAgIH0pO1xuICAgIH1cbiAgfVxuICByZXR1cm4gdG90YWw7XG59XG5cbi8qKlxuICogUmVwb3J0aW5nIHJldGVudGlvbiBzd2VlcC4gSGFyZC1kZWxldGVzIHJvd3Mgb2xkZXIgdGhhbiB0aGVpclxuICogcmV0ZW50aW9uIHdpbmRvdywgYnkgYGNyZWF0ZWRfYXRgLCBhY3Jvc3MgZXZlcnkgb3JnIHRoYXQgaGFzIHJlcG9ydGluZyBkYXRhIOKAlFxuICogYSAqKnNwbGl0KiogcG9saWN5IHNvIGhpZ2gtdm9sdW1lIHN0YW5kYXJkIGV2ZW50cyBleHBpcmUgZmFzdGVyIHRoYW4gdGhlXG4gKiBsb3ctdm9sdW1lIERPUkEgc291cmNlOlxuICogIC0gYHBpcGVsaW5lX2V2ZW50cyBXSEVSRSBlbnZpcm9ubWVudCBJUyBOVUxMYCDihpIgc3RhbmRhcmQtZXZlbnQgd2luZG93LlxuICogIC0gYHBpcGVsaW5lX2V2ZW50cyBXSEVSRSBlbnZpcm9ubWVudCBJUyBOT1QgTlVMTGAgKGRlcGxveSBzdGFnZXMpLFxuICogICAgYGRlcGxveW1lbnRfb3V0Y29tZXNgLCBhbmQgYGluY2lkZW50c2Ag4oaSIERPUkEtc291cmNlIHdpbmRvdy5cbiAqIEVhY2ggb3JnJ3Mgd2luZG93cyBjb21lIGZyb20gaXRzIGBkb3JhX3NldHRpbmdzYCBvdmVycmlkZSwgZWxzZSB0aGUgZ2xvYmFsXG4gKiBlbnYgZGVmYXVsdHMgKHNlZSB7QGxpbmsgcmVzb2x2ZUV2ZW50UmV0ZW50aW9uRGF5c30gLyB7QGxpbmsgcmVzb2x2ZURvcmFSZXRlbnRpb25EYXlzfSkuXG4gKiBgaW5nZXN0X2hlYWx0aGAgYW5kIGBkb3JhX3NldHRpbmdzYCBhcmUgbmV2ZXIgcHVyZ2VkLiBPbmUgYG5vd2AgZm9yIHRoZSB3aG9sZVxuICogdGljayAocm93cyBjcm9zc2luZyB0aGUgYm91bmRhcnkgbWlkLXN3ZWVwIHdhaXQgZm9yIHRoZSBuZXh0KS4gRXN0YWJsaXNoZXMgYVxuICogc3lzYWRtaW4gdGVuYW50IHNjb3BlIHNvIHRoZSBkZWxldGVzIHNwYW4gYWxsIG9yZ3MgLyBieXBhc3MgUkxTIOKAlCB0aGlzIGlzIGFcbiAqIGNyb3NzLXRlbmFudCBob3VzZWtlZXBpbmcgam9iLiBSZXR1cm5zIChhbmQgbG9ncykgcGVyLXdpbmRvdyBwdXJnZSB0YWxsaWVzLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcHVyZ2VFeHBpcmVkUmVwb3J0aW5nRGF0YShvcHRzOiBSZXBvcnRpbmdSZXRlbnRpb25PcHRpb25zID0ge30pOiBQcm9taXNlPFJlcG9ydGluZ1JldGVudGlvbkNvdW50cz4ge1xuICBjb25zdCBiYXRjaFNpemUgPSBNYXRoLm1heCgxLCBvcHRzLmJhdGNoU2l6ZSA/PyAxMDAwKTtcbiAgY29uc3QgbWF4QmF0Y2hlcyA9IE1hdGgubWF4KDEsIG9wdHMubWF4QmF0Y2hlc1BlclRhYmxlID8/IDUwKTtcbiAgY29uc3Qgbm93ID0gb3B0cy5ub3cgPz8gbmV3IERhdGUoKTtcbiAgY29uc3QgcmVzb2x2ZVJldGVudGlvbk9yZ0lkID0gb3B0cy5yZXNvbHZlUmV0ZW50aW9uT3JnSWQgPz8gKGFzeW5jIChvcmdJZDogc3RyaW5nKSA9PiBvcmdJZCk7XG4gIGNvbnN0IGNvdW50czogUmVwb3J0aW5nUmV0ZW50aW9uQ291bnRzID0ge1xuICAgIG9yZ3M6IDAsIHN0YW5kYXJkRXZlbnRzOiAwLCBkb3JhRXZlbnRzOiAwLCBkZXBsb3ltZW50T3V0Y29tZXM6IDAsIGluY2lkZW50czogMCxcbiAgfTtcblxuICByZXR1cm4gcnVuV2l0aFRlbmFudENvbnRleHQoeyBpc1N1cGVyQWRtaW46IHRydWUgfSwgYXN5bmMgKCkgPT4ge1xuICAgIC8vIEVudW1lcmF0ZSBldmVyeSBvcmcgd2l0aCByZXBvcnRpbmcgZGF0YSAodW5pb24gYWNyb3NzIHRoZSB0aHJlZSB0YWJsZXMpLlxuICAgIGNvbnN0IG9yZ1Jvd3MgPSBkcml6emxlUm93czx7IG9yZ19pZDogc3RyaW5nIH0+KChhd2FpdCB3aXRoVGVuYW50VHgoKHR4KSA9PiB0eC5leGVjdXRlKHNxbGBcbiAgICAgIFNFTEVDVCBESVNUSU5DVCBvcmdfaWQgRlJPTSAoXG4gICAgICAgIFNFTEVDVCAke3NjaGVtYS5waXBlbGluZUV2ZW50Lm9yZ0lkfSBBUyBvcmdfaWQgRlJPTSAke3NjaGVtYS5waXBlbGluZUV2ZW50fVxuICAgICAgICBVTklPTlxuICAgICAgICBTRUxFQ1QgJHtzY2hlbWEuZGVwbG95bWVudE91dGNvbWUub3JnSWR9IEFTIG9yZ19pZCBGUk9NICR7c2NoZW1hLmRlcGxveW1lbnRPdXRjb21lfVxuICAgICAgICBVTklPTlxuICAgICAgICBTRUxFQ1QgJHtzY2hlbWEuaW5jaWRlbnQub3JnSWR9IEFTIG9yZ19pZCBGUk9NICR7c2NoZW1hLmluY2lkZW50fVxuICAgICAgKSBBUyBvcmdzXG4gICAgYCkpKS5yb3dzKTtcbiAgICBpZiAob3JnUm93cy5sZW5ndGggPT09IDApIHJldHVybiBjb3VudHM7XG5cbiAgICAvLyBQZXItb3JnIHJldGVudGlvbiBvdmVycmlkZXMgKGEgc2luZ2xlIHJlYWQ7IG9yZ3Mgd2l0aG91dCBhIHJvdyB1c2UgZGVmYXVsdHMpLlxuICAgIGNvbnN0IG92ZXJyaWRlUm93cyA9IGRyaXp6bGVSb3dzPHtcbiAgICAgIG9yZ19pZDogc3RyaW5nO1xuICAgICAgZXZlbnRfcmV0ZW50aW9uX2RheXM6IG51bWJlciB8IG51bGw7XG4gICAgICBkb3JhX3JldGVudGlvbl9kYXlzOiBudW1iZXIgfCBudWxsO1xuICAgIH0+KChhd2FpdCB3aXRoVGVuYW50VHgoKHR4KSA9PiB0eC5leGVjdXRlKHNxbGBcbiAgICAgIFNFTEVDVCAke3NjaGVtYS5kb3JhU2V0dGluZ3Mub3JnSWR9IEFTIG9yZ19pZCxcbiAgICAgICAgICAgICAke3NjaGVtYS5kb3JhU2V0dGluZ3MuZXZlbnRSZXRlbnRpb25EYXlzfSBBUyBldmVudF9yZXRlbnRpb25fZGF5cyxcbiAgICAgICAgICAgICAke3NjaGVtYS5kb3JhU2V0dGluZ3MuZG9yYVJldGVudGlvbkRheXN9IEFTIGRvcmFfcmV0ZW50aW9uX2RheXNcbiAgICAgIEZST00gJHtzY2hlbWEuZG9yYVNldHRpbmdzfVxuICAgIGApKSkucm93cyk7XG4gICAgY29uc3Qgb3ZlcnJpZGVzID0gbmV3IE1hcChvdmVycmlkZVJvd3MubWFwKChyKSA9PiBbci5vcmdfaWQsIHJdKSk7XG5cbiAgICBjb25zdCBldmVudHNUYWJsZSA9IHNxbGAke3NjaGVtYS5waXBlbGluZUV2ZW50fWA7XG4gICAgY29uc3Qgb3V0Y29tZXNUYWJsZSA9IHNxbGAke3NjaGVtYS5kZXBsb3ltZW50T3V0Y29tZX1gO1xuICAgIGNvbnN0IGluY2lkZW50c1RhYmxlID0gc3FsYCR7c2NoZW1hLmluY2lkZW50fWA7XG5cbiAgICBmb3IgKGNvbnN0IHsgb3JnX2lkOiBvcmdJZCB9IG9mIG9yZ1Jvd3MpIHtcbiAgICAgIC8vIFJldGVudGlvbiBmb2xsb3dzIHRoZSBhY2NvdW50IFJPT1QncyBlbnRpdGxlbWVudCAoYmlsbGluZyBzeW5jcyBpdCBvbnRvXG4gICAgICAvLyB0aGUgcm9vdCBvbmx5KS4gQW4gdW5yZXNvbHZhYmxlIHJvb3Qg4oeSIHNraXAgdGhpcyBvcmcgdGhpcyB0aWNrIHJhdGhlclxuICAgICAgLy8gdGhhbiBwdXJnZSBhIHRlYW0ncyByb3dzIG9uIHRoZSAoc2hvcnRlcikgZW52IGRlZmF1bHQuXG4gICAgICBsZXQgcmV0ZW50aW9uT3JnSWQ6IHN0cmluZyB8IG51bGw7XG4gICAgICB0cnkge1xuICAgICAgICByZXRlbnRpb25PcmdJZCA9IGF3YWl0IHJlc29sdmVSZXRlbnRpb25PcmdJZChvcmdJZCk7XG4gICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgbG9nZ2VyLndhcm4oJ1JlcG9ydGluZyByZXRlbnRpb246IHJvb3QgcmVzb2x1dGlvbiB0aHJldzsgc2tpcHBpbmcgb3JnIHRoaXMgdGljaycsIHsgb3JnSWQsIGVycm9yOiBlcnJvck1lc3NhZ2UoZXJyKSB9KTtcbiAgICAgICAgcmV0ZW50aW9uT3JnSWQgPSBudWxsO1xuICAgICAgfVxuICAgICAgaWYgKHJldGVudGlvbk9yZ0lkID09PSBudWxsKSB7XG4gICAgICAgIGxvZ2dlci53YXJuKCdSZXBvcnRpbmcgcmV0ZW50aW9uOiBjb3VsZCBub3QgcmVzb2x2ZSB0aGUgcmV0ZW50aW9uIHJvb3Q7IG9yZyBza2lwcGVkIHRoaXMgdGljaycsIHsgb3JnSWQgfSk7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuICAgICAgY29uc3Qgb3YgPSBvdmVycmlkZXMuZ2V0KHJldGVudGlvbk9yZ0lkKTtcbiAgICAgIGNvbnN0IGV2ZW50RGF5cyA9IHJlc29sdmVFdmVudFJldGVudGlvbkRheXMob3Y/LmV2ZW50X3JldGVudGlvbl9kYXlzKTtcbiAgICAgIGNvbnN0IGRvcmFEYXlzID0gcmVzb2x2ZURvcmFSZXRlbnRpb25EYXlzKG92Py5kb3JhX3JldGVudGlvbl9kYXlzKTtcbiAgICAgIC8vIGAtMWAgPSB1bmxpbWl0ZWQ6IGtlZXAgZm9yZXZlciwgc2tpcCB0aGF0IHdpbmRvdydzIGRlbGV0ZXMgZm9yXG4gICAgICAvLyB0aGlzIG9yZy4gU3RhbmRhcmQtZXZlbnQgYW5kIERPUkEtc291cmNlIHdpbmRvd3MgYXJlIGluZGVwZW5kZW50OyBhbiBvcmdcbiAgICAgIC8vIHdpdGggYm90aCBgLTFgIGlzIGZ1bGx5IHNraXBwZWQuIExvZyB0aGUgc2tpcCBzbyBpdCdzIG9ic2VydmFibGUuXG4gICAgICBjb25zdCBza2lwRXZlbnRzID0gZXZlbnREYXlzID09PSAtMTtcbiAgICAgIGNvbnN0IHNraXBEb3JhID0gZG9yYURheXMgPT09IC0xO1xuICAgICAgaWYgKHNraXBFdmVudHMgfHwgc2tpcERvcmEpIHtcbiAgICAgICAgbG9nZ2VyLmluZm8oJ1JlcG9ydGluZyByZXRlbnRpb24gc3dlZXAgc2tpcHBpbmcgdW5saW1pdGVkIHdpbmRvdyhzKScsIHtcbiAgICAgICAgICBvcmdJZCxcbiAgICAgICAgICBzdGFuZGFyZEV2ZW50czogc2tpcEV2ZW50cyA/ICd1bmxpbWl0ZWQnIDogZXZlbnREYXlzLFxuICAgICAgICAgIGRvcmFTb3VyY2U6IHNraXBEb3JhID8gJ3VubGltaXRlZCcgOiBkb3JhRGF5cyxcbiAgICAgICAgfSk7XG4gICAgICB9XG5cbiAgICAgIGlmICghc2tpcEV2ZW50cykge1xuICAgICAgICBjb25zdCBldmVudEN1dG9mZiA9IHJldGVudGlvbkN1dG9mZihub3csIGV2ZW50RGF5cyk7XG4gICAgICAgIGNvdW50cy5zdGFuZGFyZEV2ZW50cyArPSBhd2FpdCBwdXJnZVJlcG9ydGluZ1RhYmxlQmF0Y2hlZChcbiAgICAgICAgICBldmVudHNUYWJsZSxcbiAgICAgICAgICAvLyBTcGFyZSByb3dzIGNhcnJ5aW5nIGEgY29tbWl0IHRpbWVzdGFtcDogdGhleSBhcmUgRE9SQSBTT1VSQ0UgZGF0YS5cbiAgICAgICAgICAvLyBMZWFkIHRpbWUgam9pbnMgZGVwbG95cyB0byB0aGUgZXhlY3V0aW9uJ3MgZWFybGllc3RcbiAgICAgICAgICAvLyBgY29tbWl0X3RpbWVzdGFtcGAsIGFuZCB0aGF0IGVucmljaG1lbnQgcmlkZXMgdGhlIFBJUEVMSU5FL3NvdXJjZVxuICAgICAgICAgIC8vIGV2ZW50IChlbnZpcm9ubWVudCBJUyBOVUxMKSwgbmV2ZXIgdGhlIGRlcGxveSBTVEFHRSByb3cuIFB1cmdpbmdcbiAgICAgICAgICAvLyB0aGVtIG9uIHRoaXMgc2hvcnQgd2luZG93IHdoaWxlIHRoZSBkZXBsb3lzIHRoZXkgZXhwbGFpbiBsaXZlIG9uXG4gICAgICAgICAgLy8gdGhlIERPUkEgd2luZG93IG1hZGUgbGVhZCB0aW1lIHJlYWQgXCJ1bmtub3duXCIgZm9yIGV2ZXJ5IHJhbmdlXG4gICAgICAgICAgLy8gb2xkZXIgdGhhbiB0aGUgc3RhbmRhcmQgcmV0ZW50aW9uIOKAlCBzaWxlbnRseSwgd2l0aCB0aGUgZGVwbG95c1xuICAgICAgICAgIC8vIHN0aWxsIGNvdW50ZWQuIFRoZXkgZm9sbG93IHRoZSBET1JBIHdpbmRvdyBiZWxvdyBpbnN0ZWFkLlxuICAgICAgICAgIHNxbGBvcmdfaWQgPSAke29yZ0lkfSBBTkQgZW52aXJvbm1lbnQgSVMgTlVMTCBBTkQgY29tbWl0X3RpbWVzdGFtcCBJUyBOVUxMIEFORCBjcmVhdGVkX2F0IDwgJHtldmVudEN1dG9mZn1gLFxuICAgICAgICAgIGJhdGNoU2l6ZSwgbWF4QmF0Y2hlcyxcbiAgICAgICAgKTtcbiAgICAgIH1cblxuICAgICAgaWYgKCFza2lwRG9yYSkge1xuICAgICAgICBjb25zdCBkb3JhQ3V0b2ZmID0gcmV0ZW50aW9uQ3V0b2ZmKG5vdywgZG9yYURheXMpO1xuICAgICAgICBjb3VudHMuZG9yYUV2ZW50cyArPSBhd2FpdCBwdXJnZVJlcG9ydGluZ1RhYmxlQmF0Y2hlZChcbiAgICAgICAgICBldmVudHNUYWJsZSxcbiAgICAgICAgICBzcWxgb3JnX2lkID0gJHtvcmdJZH0gQU5EIChlbnZpcm9ubWVudCBJUyBOT1QgTlVMTCBPUiBjb21taXRfdGltZXN0YW1wIElTIE5PVCBOVUxMKSBBTkQgY3JlYXRlZF9hdCA8ICR7ZG9yYUN1dG9mZn1gLFxuICAgICAgICAgIGJhdGNoU2l6ZSwgbWF4QmF0Y2hlcyxcbiAgICAgICAgKTtcbiAgICAgICAgY291bnRzLmRlcGxveW1lbnRPdXRjb21lcyArPSBhd2FpdCBwdXJnZVJlcG9ydGluZ1RhYmxlQmF0Y2hlZChcbiAgICAgICAgICBvdXRjb21lc1RhYmxlLFxuICAgICAgICAgIHNxbGBvcmdfaWQgPSAke29yZ0lkfSBBTkQgY3JlYXRlZF9hdCA8ICR7ZG9yYUN1dG9mZn1gLFxuICAgICAgICAgIGJhdGNoU2l6ZSwgbWF4QmF0Y2hlcyxcbiAgICAgICAgKTtcbiAgICAgICAgY291bnRzLmluY2lkZW50cyArPSBhd2FpdCBwdXJnZVJlcG9ydGluZ1RhYmxlQmF0Y2hlZChcbiAgICAgICAgICBpbmNpZGVudHNUYWJsZSxcbiAgICAgICAgICBzcWxgb3JnX2lkID0gJHtvcmdJZH0gQU5EIGNyZWF0ZWRfYXQgPCAke2RvcmFDdXRvZmZ9YCxcbiAgICAgICAgICBiYXRjaFNpemUsIG1heEJhdGNoZXMsXG4gICAgICAgICk7XG4gICAgICB9XG4gICAgICBjb3VudHMub3JncyArPSAxO1xuICAgIH1cblxuICAgIGNvbnN0IHB1cmdlZEFueSA9IGNvdW50cy5zdGFuZGFyZEV2ZW50cyArIGNvdW50cy5kb3JhRXZlbnRzICsgY291bnRzLmRlcGxveW1lbnRPdXRjb21lcyArIGNvdW50cy5pbmNpZGVudHM7XG4gICAgaWYgKHB1cmdlZEFueSA+IDApIGxvZ2dlci5pbmZvKCdSZXBvcnRpbmcgcmV0ZW50aW9uIHN3ZWVwIHB1cmdlZCBleHBpcmVkIHJvd3MnLCB7IC4uLmNvdW50cyB9KTtcbiAgICByZXR1cm4gY291bnRzO1xuICB9KTtcbn1cbiJdfQ==
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Reporting retention windows. Records in `pipeline_events`,
3
3
  * `deployment_outcomes`, and `incidents` grow unbounded without a sweep, so a
4
- * split, per-org retention purge (see {@link ReportingService.purgeExpiredReportingData})
4
+ * split, per-org retention purge (see `purgeExpiredReportingData` in ./retention-sweep.ts)
5
5
  * hard-deletes rows older than these windows, by `created_at`:
6
6
  * - **Standard events** — `pipeline_events` with `environment IS NULL`
7
7
  * (non-deploy STAGE/ACTION/build). High volume → short default (30 days).
@@ -8,7 +8,7 @@ import { envInt, RETENTION_MAX_DAYS, RETENTION_MIN_DAYS } from '@pipeline-builde
8
8
  /**
9
9
  * Reporting retention windows. Records in `pipeline_events`,
10
10
  * `deployment_outcomes`, and `incidents` grow unbounded without a sweep, so a
11
- * split, per-org retention purge (see {@link ReportingService.purgeExpiredReportingData})
11
+ * split, per-org retention purge (see `purgeExpiredReportingData` in ./retention-sweep.ts)
12
12
  * hard-deletes rows older than these windows, by `created_at`:
13
13
  * - **Standard events** — `pipeline_events` with `environment IS NULL`
14
14
  * (non-deploy STAGE/ACTION/build). High volume → short default (30 days).
@@ -58,4 +58,4 @@ export function retentionCutoff(now, days) {
58
58
  return new Date(now.getTime() - days * 86_400_000);
59
59
  }
60
60
  /** Per-org retention override row. Both nullable ⇒ global defaults. */
61
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmV0ZW50aW9uLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2FwaS9yZXBvcnRpbmcvcmV0ZW50aW9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFFdEM7OztHQUdHO0FBRUgsT0FBTyxFQUFFLE1BQU0sRUFBRSxrQkFBa0IsRUFBRSxrQkFBa0IsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBRTVGOzs7Ozs7Ozs7Ozs7OztHQWNHO0FBRUgsa0ZBQWtGO0FBQ2xGLE1BQU0sVUFBVSxnQkFBZ0IsQ0FBQyxJQUFZLEVBQUUsUUFBZ0I7SUFDN0QsTUFBTSxJQUFJLEdBQUcsTUFBTSxDQUFDLElBQUksRUFBRSxRQUFRLENBQUMsQ0FBQztJQUNwQyxPQUFPLElBQUksR0FBRyxrQkFBa0IsQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksRUFBRSxrQkFBa0IsQ0FBQyxDQUFDO0FBQ25GLENBQUM7QUFFRCxNQUFNLENBQUMsTUFBTSw4QkFBOEIsR0FBRyxnQkFBZ0IsQ0FBQyxnQ0FBZ0MsRUFBRSxFQUFFLENBQUMsQ0FBQztBQUNyRyxNQUFNLENBQUMsTUFBTSw2QkFBNkIsR0FBRyxnQkFBZ0IsQ0FBQywrQkFBK0IsRUFBRSxHQUFHLENBQUMsQ0FBQztBQUVwRzs7Ozs7O21CQU1tQjtBQUNuQixNQUFNLFVBQVUseUJBQXlCLENBQUMsUUFBd0I7SUFDaEUsSUFBSSxRQUFRLEtBQUssQ0FBQyxDQUFDO1FBQUUsT0FBTyxDQUFDLENBQUMsQ0FBQztJQUMvQixPQUFPLFFBQVEsSUFBSSxJQUFJLElBQUksTUFBTSxDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUMsSUFBSSxRQUFRLElBQUksa0JBQWtCO1FBQ3JGLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLFFBQVEsRUFBRSxrQkFBa0IsQ0FBQyxDQUFDLENBQUMsQ0FBQyw4QkFBOEIsQ0FBQztBQUM5RSxDQUFDO0FBRUQ7Ozs7O21CQUttQjtBQUNuQixNQUFNLFVBQVUsd0JBQXdCLENBQUMsUUFBd0I7SUFDL0QsSUFBSSxRQUFRLEtBQUssQ0FBQyxDQUFDO1FBQUUsT0FBTyxDQUFDLENBQUMsQ0FBQztJQUMvQixPQUFPLFFBQVEsSUFBSSxJQUFJLElBQUksTUFBTSxDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUMsSUFBSSxRQUFRLElBQUksa0JBQWtCO1FBQ3JGLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLFFBQVEsRUFBRSxrQkFBa0IsQ0FBQyxDQUFDLENBQUMsQ0FBQyw2QkFBNkIsQ0FBQztBQUM3RSxDQUFDO0FBRUQ7NkVBQzZFO0FBQzdFLE1BQU0sVUFBVSxlQUFlLENBQUMsR0FBUyxFQUFFLElBQVk7SUFDckQsT0FBTyxJQUFJLElBQUksQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLEdBQUcsSUFBSSxHQUFHLFVBQVUsQ0FBQyxDQUFDO0FBQ3JELENBQUM7QUFFRCx1RUFBdUUiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuLyoqXG4gKiBSZXRlbnRpb24gd2luZG93cyBmb3IgcmVwb3J0aW5nIGRhdGE6IGVudi1jb25maWd1cmFibGUsIGNsYW1wZWQgdG8gYSBzYW5lXG4gKiByYW5nZSwgYW5kIG92ZXJyaWRhYmxlIHBlci1vcmcgYnkgdGhlIHNldHRpbmdzIHJvdy5cbiAqL1xuXG5pbXBvcnQgeyBlbnZJbnQsIFJFVEVOVElPTl9NQVhfREFZUywgUkVURU5USU9OX01JTl9EQVlTIH0gZnJvbSAnQHBpcGVsaW5lLWJ1aWxkZXIvYXBpLWNvcmUnO1xuXG4vKipcbiAqIFJlcG9ydGluZyByZXRlbnRpb24gd2luZG93cy4gUmVjb3JkcyBpbiBgcGlwZWxpbmVfZXZlbnRzYCxcbiAqIGBkZXBsb3ltZW50X291dGNvbWVzYCwgYW5kIGBpbmNpZGVudHNgIGdyb3cgdW5ib3VuZGVkIHdpdGhvdXQgYSBzd2VlcCwgc28gYVxuICogc3BsaXQsIHBlci1vcmcgcmV0ZW50aW9uIHB1cmdlIChzZWUge0BsaW5rIFJlcG9ydGluZ1NlcnZpY2UucHVyZ2VFeHBpcmVkUmVwb3J0aW5nRGF0YX0pXG4gKiBoYXJkLWRlbGV0ZXMgcm93cyBvbGRlciB0aGFuIHRoZXNlIHdpbmRvd3MsIGJ5IGBjcmVhdGVkX2F0YDpcbiAqICAtICoqU3RhbmRhcmQgZXZlbnRzKiog4oCUIGBwaXBlbGluZV9ldmVudHNgIHdpdGggYGVudmlyb25tZW50IElTIE5VTExgXG4gKiAgICAobm9uLWRlcGxveSBTVEFHRS9BQ1RJT04vYnVpbGQpLiBIaWdoIHZvbHVtZSDihpIgc2hvcnQgZGVmYXVsdCAoMzAgZGF5cykuXG4gKiAgLSAqKkRPUkEgc291cmNlKiog4oCUIGBwaXBlbGluZV9ldmVudHNgIHdpdGggYGVudmlyb25tZW50IElTIE5PVCBOVUxMYCAoZGVwbG95XG4gKiAgICBzdGFnZXMpIHBsdXMgYWxsIG9mIGBkZXBsb3ltZW50X291dGNvbWVzYCBhbmQgYGluY2lkZW50c2AuIExvdyB2b2x1bWUg4oaSXG4gKiAgICBsb25nZXIgZGVmYXVsdCAoMTgwIGRheXMpLiBET1JBIGhpc3RvcnkgaXMgdGhlcmVmb3JlIGJvdW5kZWQgYnkgdGhpcyB3aW5kb3c7XG4gKiAgICB0aGUgcmVwb3J0IHF1ZXJ5IHN0aWxsIGhhcmQtY2FwcyBhdCAzNjUgZGF5cyAoYSBsb25nZXIgcGVyLW9yZyBvdmVycmlkZSBvbmx5XG4gKiAgICByZXRhaW5zIHJhdyBzb3VyY2Ugcm93cywgbmV2ZXIgd2lkZW5zIGEgcmVwb3J0KS5cbiAqIGBpbmdlc3RfaGVhbHRoYCBhbmQgYGRvcmFfc2V0dGluZ3NgIGFyZSBuZXZlciBwdXJnZWQuIEVudi1vdmVycmlkYWJsZSBnbG9iYWxzO1xuICogYSBwZXItb3JnIG92ZXJyaWRlIGluIGBkb3JhX3NldHRpbmdzYCB3aW5zIChzZWUge0BsaW5rIHJlc29sdmVFdmVudFJldGVudGlvbkRheXN9KS5cbiAqL1xuXG4vKiogUGFyc2UgYSBwb3NpdGl2ZS1pbnQgZW52IGRheS1jb3VudCwgY2xhbXBlZCB0byBbTUlOLCBNQVhdLCBlbHNlIGBmYWxsYmFja2AuICovXG5leHBvcnQgZnVuY3Rpb24gcmV0ZW50aW9uRW52RGF5cyhuYW1lOiBzdHJpbmcsIGZhbGxiYWNrOiBudW1iZXIpOiBudW1iZXIge1xuICBjb25zdCBkYXlzID0gZW52SW50KG5hbWUsIGZhbGxiYWNrKTtcbiAgcmV0dXJuIGRheXMgPCBSRVRFTlRJT05fTUlOX0RBWVMgPyBmYWxsYmFjayA6IE1hdGgubWluKGRheXMsIFJFVEVOVElPTl9NQVhfREFZUyk7XG59XG5cbmV4cG9ydCBjb25zdCBSRVBPUlRJTkdfRVZFTlRfUkVURU5USU9OX0RBWVMgPSByZXRlbnRpb25FbnZEYXlzKCdSRVBPUlRJTkdfRVZFTlRfUkVURU5USU9OX0RBWVMnLCAzMCk7XG5leHBvcnQgY29uc3QgUkVQT1JUSU5HX0RPUkFfUkVURU5USU9OX0RBWVMgPSByZXRlbnRpb25FbnZEYXlzKCdSRVBPUlRJTkdfRE9SQV9SRVRFTlRJT05fREFZUycsIDE4MCk7XG5cbi8qKiBFZmZlY3RpdmUgc3RhbmRhcmQtZXZlbnQgcmV0ZW50aW9uIChkYXlzKTogYSB2YWxpZCBwZXItb3JnIG92ZXJyaWRlIHdpbnMsXG4gKiAgZWxzZSB0aGUgZ2xvYmFsIGVudiBkZWZhdWx0LiBTaGFyZWQgYnkgdGhlIHN3ZWVwICsgdGhlIHNldHRpbmdzIHN1cmZhY2Ugc29cbiAqICB0aGV5IGNhbid0IGRyaWZ0LiBUaGUgYC0xYCB1bmxpbWl0ZWQgc2VudGluZWwgcGFzc2VzIHN0cmFpZ2h0XG4gKiAgdGhyb3VnaCDigJQgdGhlIHN3ZWVwIHJlYWRzIGl0IGFzIFwia2VlcCBmb3JldmVyLCBza2lwIHRoaXMgb3JnJ3Mgc3RhbmRhcmRcbiAqICBldmVudHNcIi4gQSBwb3NpdGl2ZSBvdmVycmlkZSBjbGFtcHMgdG8gWzEsIFJFVEVOVElPTl9NQVhfREFZU107IGEgYG51bGxgLCBhXG4gKiAgbm9uLWludGVnZXIsIG9yIGFueSBvdGhlciBvdXQtb2YtcmFuZ2UgdmFsdWUgKGUuZy4gMCwgLTIpIGZhbGxzIGJhY2sgdG8gdGhlXG4gKiAgZW52IGRlZmF1bHQuICovXG5leHBvcnQgZnVuY3Rpb24gcmVzb2x2ZUV2ZW50UmV0ZW50aW9uRGF5cyhvdmVycmlkZT86IG51bWJlciB8IG51bGwpOiBudW1iZXIge1xuICBpZiAob3ZlcnJpZGUgPT09IC0xKSByZXR1cm4gLTE7XG4gIHJldHVybiBvdmVycmlkZSAhPSBudWxsICYmIE51bWJlci5pc0ludGVnZXIob3ZlcnJpZGUpICYmIG92ZXJyaWRlID49IFJFVEVOVElPTl9NSU5fREFZU1xuICAgID8gTWF0aC5taW4ob3ZlcnJpZGUsIFJFVEVOVElPTl9NQVhfREFZUykgOiBSRVBPUlRJTkdfRVZFTlRfUkVURU5USU9OX0RBWVM7XG59XG5cbi8qKiBFZmZlY3RpdmUgRE9SQS1zb3VyY2UgcmV0ZW50aW9uIChkYXlzKTogYSB2YWxpZCBwZXItb3JnIG92ZXJyaWRlIHdpbnMsIGVsc2VcbiAqICB0aGUgZ2xvYmFsIGVudiBkZWZhdWx0LiBUaGUgYC0xYCB1bmxpbWl0ZWQgc2VudGluZWwgcGFzc2VzIHN0cmFpZ2h0XG4gKiAgdGhyb3VnaCDigJQgdGhlIHN3ZWVwIHJlYWRzIGl0IGFzIFwia2VlcCBmb3JldmVyLCBza2lwIHRoaXMgb3JnJ3MgRE9SQS1zb3VyY2VcbiAqICB3aW5kb3dzXCIuIEEgcG9zaXRpdmUgb3ZlcnJpZGUgY2xhbXBzIHRvIFsxLCBSRVRFTlRJT05fTUFYX0RBWVNdOyBhIGBudWxsYCwgYVxuICogIG5vbi1pbnRlZ2VyLCBvciBhbnkgb3RoZXIgb3V0LW9mLXJhbmdlIHZhbHVlIChlLmcuIDAsIC0yKSBmYWxscyBiYWNrIHRvIHRoZVxuICogIGVudiBkZWZhdWx0LiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlc29sdmVEb3JhUmV0ZW50aW9uRGF5cyhvdmVycmlkZT86IG51bWJlciB8IG51bGwpOiBudW1iZXIge1xuICBpZiAob3ZlcnJpZGUgPT09IC0xKSByZXR1cm4gLTE7XG4gIHJldHVybiBvdmVycmlkZSAhPSBudWxsICYmIE51bWJlci5pc0ludGVnZXIob3ZlcnJpZGUpICYmIG92ZXJyaWRlID49IFJFVEVOVElPTl9NSU5fREFZU1xuICAgID8gTWF0aC5taW4ob3ZlcnJpZGUsIFJFVEVOVElPTl9NQVhfREFZUykgOiBSRVBPUlRJTkdfRE9SQV9SRVRFTlRJT05fREFZUztcbn1cblxuLyoqIFRoZSBjdXRvZmYgaW5zdGFudCBmb3IgYSByZXRlbnRpb24gd2luZG93OiByb3dzIHdpdGggYGNyZWF0ZWRfYXRgIHN0cmljdGx5XG4gKiAgYmVmb3JlIHRoaXMgKGA8IGN1dG9mZmApIGFyZSBleHBpcmVkLiBEZXRlcm1pbmlzdGljIGZvciBhIGZpeGVkIGBub3dgLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJldGVudGlvbkN1dG9mZihub3c6IERhdGUsIGRheXM6IG51bWJlcik6IERhdGUge1xuICByZXR1cm4gbmV3IERhdGUobm93LmdldFRpbWUoKSAtIGRheXMgKiA4Nl80MDBfMDAwKTtcbn1cblxuLyoqIFBlci1vcmcgcmV0ZW50aW9uIG92ZXJyaWRlIHJvdy4gQm90aCBudWxsYWJsZSDih5IgZ2xvYmFsIGRlZmF1bHRzLiAqL1xuIl19
61
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmV0ZW50aW9uLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2FwaS9yZXBvcnRpbmcvcmV0ZW50aW9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFFdEM7OztHQUdHO0FBRUgsT0FBTyxFQUFFLE1BQU0sRUFBRSxrQkFBa0IsRUFBRSxrQkFBa0IsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBRTVGOzs7Ozs7Ozs7Ozs7OztHQWNHO0FBRUgsa0ZBQWtGO0FBQ2xGLE1BQU0sVUFBVSxnQkFBZ0IsQ0FBQyxJQUFZLEVBQUUsUUFBZ0I7SUFDN0QsTUFBTSxJQUFJLEdBQUcsTUFBTSxDQUFDLElBQUksRUFBRSxRQUFRLENBQUMsQ0FBQztJQUNwQyxPQUFPLElBQUksR0FBRyxrQkFBa0IsQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksRUFBRSxrQkFBa0IsQ0FBQyxDQUFDO0FBQ25GLENBQUM7QUFFRCxNQUFNLENBQUMsTUFBTSw4QkFBOEIsR0FBRyxnQkFBZ0IsQ0FBQyxnQ0FBZ0MsRUFBRSxFQUFFLENBQUMsQ0FBQztBQUNyRyxNQUFNLENBQUMsTUFBTSw2QkFBNkIsR0FBRyxnQkFBZ0IsQ0FBQywrQkFBK0IsRUFBRSxHQUFHLENBQUMsQ0FBQztBQUVwRzs7Ozs7O21CQU1tQjtBQUNuQixNQUFNLFVBQVUseUJBQXlCLENBQUMsUUFBd0I7SUFDaEUsSUFBSSxRQUFRLEtBQUssQ0FBQyxDQUFDO1FBQUUsT0FBTyxDQUFDLENBQUMsQ0FBQztJQUMvQixPQUFPLFFBQVEsSUFBSSxJQUFJLElBQUksTUFBTSxDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUMsSUFBSSxRQUFRLElBQUksa0JBQWtCO1FBQ3JGLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLFFBQVEsRUFBRSxrQkFBa0IsQ0FBQyxDQUFDLENBQUMsQ0FBQyw4QkFBOEIsQ0FBQztBQUM5RSxDQUFDO0FBRUQ7Ozs7O21CQUttQjtBQUNuQixNQUFNLFVBQVUsd0JBQXdCLENBQUMsUUFBd0I7SUFDL0QsSUFBSSxRQUFRLEtBQUssQ0FBQyxDQUFDO1FBQUUsT0FBTyxDQUFDLENBQUMsQ0FBQztJQUMvQixPQUFPLFFBQVEsSUFBSSxJQUFJLElBQUksTUFBTSxDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUMsSUFBSSxRQUFRLElBQUksa0JBQWtCO1FBQ3JGLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLFFBQVEsRUFBRSxrQkFBa0IsQ0FBQyxDQUFDLENBQUMsQ0FBQyw2QkFBNkIsQ0FBQztBQUM3RSxDQUFDO0FBRUQ7NkVBQzZFO0FBQzdFLE1BQU0sVUFBVSxlQUFlLENBQUMsR0FBUyxFQUFFLElBQVk7SUFDckQsT0FBTyxJQUFJLElBQUksQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLEdBQUcsSUFBSSxHQUFHLFVBQVUsQ0FBQyxDQUFDO0FBQ3JELENBQUM7QUFFRCx1RUFBdUUiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuLyoqXG4gKiBSZXRlbnRpb24gd2luZG93cyBmb3IgcmVwb3J0aW5nIGRhdGE6IGVudi1jb25maWd1cmFibGUsIGNsYW1wZWQgdG8gYSBzYW5lXG4gKiByYW5nZSwgYW5kIG92ZXJyaWRhYmxlIHBlci1vcmcgYnkgdGhlIHNldHRpbmdzIHJvdy5cbiAqL1xuXG5pbXBvcnQgeyBlbnZJbnQsIFJFVEVOVElPTl9NQVhfREFZUywgUkVURU5USU9OX01JTl9EQVlTIH0gZnJvbSAnQHBpcGVsaW5lLWJ1aWxkZXIvYXBpLWNvcmUnO1xuXG4vKipcbiAqIFJlcG9ydGluZyByZXRlbnRpb24gd2luZG93cy4gUmVjb3JkcyBpbiBgcGlwZWxpbmVfZXZlbnRzYCxcbiAqIGBkZXBsb3ltZW50X291dGNvbWVzYCwgYW5kIGBpbmNpZGVudHNgIGdyb3cgdW5ib3VuZGVkIHdpdGhvdXQgYSBzd2VlcCwgc28gYVxuICogc3BsaXQsIHBlci1vcmcgcmV0ZW50aW9uIHB1cmdlIChzZWUgYHB1cmdlRXhwaXJlZFJlcG9ydGluZ0RhdGFgIGluIC4vcmV0ZW50aW9uLXN3ZWVwLnRzKVxuICogaGFyZC1kZWxldGVzIHJvd3Mgb2xkZXIgdGhhbiB0aGVzZSB3aW5kb3dzLCBieSBgY3JlYXRlZF9hdGA6XG4gKiAgLSAqKlN0YW5kYXJkIGV2ZW50cyoqIOKAlCBgcGlwZWxpbmVfZXZlbnRzYCB3aXRoIGBlbnZpcm9ubWVudCBJUyBOVUxMYFxuICogICAgKG5vbi1kZXBsb3kgU1RBR0UvQUNUSU9OL2J1aWxkKS4gSGlnaCB2b2x1bWUg4oaSIHNob3J0IGRlZmF1bHQgKDMwIGRheXMpLlxuICogIC0gKipET1JBIHNvdXJjZSoqIOKAlCBgcGlwZWxpbmVfZXZlbnRzYCB3aXRoIGBlbnZpcm9ubWVudCBJUyBOT1QgTlVMTGAgKGRlcGxveVxuICogICAgc3RhZ2VzKSBwbHVzIGFsbCBvZiBgZGVwbG95bWVudF9vdXRjb21lc2AgYW5kIGBpbmNpZGVudHNgLiBMb3cgdm9sdW1lIOKGklxuICogICAgbG9uZ2VyIGRlZmF1bHQgKDE4MCBkYXlzKS4gRE9SQSBoaXN0b3J5IGlzIHRoZXJlZm9yZSBib3VuZGVkIGJ5IHRoaXMgd2luZG93O1xuICogICAgdGhlIHJlcG9ydCBxdWVyeSBzdGlsbCBoYXJkLWNhcHMgYXQgMzY1IGRheXMgKGEgbG9uZ2VyIHBlci1vcmcgb3ZlcnJpZGUgb25seVxuICogICAgcmV0YWlucyByYXcgc291cmNlIHJvd3MsIG5ldmVyIHdpZGVucyBhIHJlcG9ydCkuXG4gKiBgaW5nZXN0X2hlYWx0aGAgYW5kIGBkb3JhX3NldHRpbmdzYCBhcmUgbmV2ZXIgcHVyZ2VkLiBFbnYtb3ZlcnJpZGFibGUgZ2xvYmFscztcbiAqIGEgcGVyLW9yZyBvdmVycmlkZSBpbiBgZG9yYV9zZXR0aW5nc2Agd2lucyAoc2VlIHtAbGluayByZXNvbHZlRXZlbnRSZXRlbnRpb25EYXlzfSkuXG4gKi9cblxuLyoqIFBhcnNlIGEgcG9zaXRpdmUtaW50IGVudiBkYXktY291bnQsIGNsYW1wZWQgdG8gW01JTiwgTUFYXSwgZWxzZSBgZmFsbGJhY2tgLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJldGVudGlvbkVudkRheXMobmFtZTogc3RyaW5nLCBmYWxsYmFjazogbnVtYmVyKTogbnVtYmVyIHtcbiAgY29uc3QgZGF5cyA9IGVudkludChuYW1lLCBmYWxsYmFjayk7XG4gIHJldHVybiBkYXlzIDwgUkVURU5USU9OX01JTl9EQVlTID8gZmFsbGJhY2sgOiBNYXRoLm1pbihkYXlzLCBSRVRFTlRJT05fTUFYX0RBWVMpO1xufVxuXG5leHBvcnQgY29uc3QgUkVQT1JUSU5HX0VWRU5UX1JFVEVOVElPTl9EQVlTID0gcmV0ZW50aW9uRW52RGF5cygnUkVQT1JUSU5HX0VWRU5UX1JFVEVOVElPTl9EQVlTJywgMzApO1xuZXhwb3J0IGNvbnN0IFJFUE9SVElOR19ET1JBX1JFVEVOVElPTl9EQVlTID0gcmV0ZW50aW9uRW52RGF5cygnUkVQT1JUSU5HX0RPUkFfUkVURU5USU9OX0RBWVMnLCAxODApO1xuXG4vKiogRWZmZWN0aXZlIHN0YW5kYXJkLWV2ZW50IHJldGVudGlvbiAoZGF5cyk6IGEgdmFsaWQgcGVyLW9yZyBvdmVycmlkZSB3aW5zLFxuICogIGVsc2UgdGhlIGdsb2JhbCBlbnYgZGVmYXVsdC4gU2hhcmVkIGJ5IHRoZSBzd2VlcCArIHRoZSBzZXR0aW5ncyBzdXJmYWNlIHNvXG4gKiAgdGhleSBjYW4ndCBkcmlmdC4gVGhlIGAtMWAgdW5saW1pdGVkIHNlbnRpbmVsIHBhc3NlcyBzdHJhaWdodFxuICogIHRocm91Z2gg4oCUIHRoZSBzd2VlcCByZWFkcyBpdCBhcyBcImtlZXAgZm9yZXZlciwgc2tpcCB0aGlzIG9yZydzIHN0YW5kYXJkXG4gKiAgZXZlbnRzXCIuIEEgcG9zaXRpdmUgb3ZlcnJpZGUgY2xhbXBzIHRvIFsxLCBSRVRFTlRJT05fTUFYX0RBWVNdOyBhIGBudWxsYCwgYVxuICogIG5vbi1pbnRlZ2VyLCBvciBhbnkgb3RoZXIgb3V0LW9mLXJhbmdlIHZhbHVlIChlLmcuIDAsIC0yKSBmYWxscyBiYWNrIHRvIHRoZVxuICogIGVudiBkZWZhdWx0LiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlc29sdmVFdmVudFJldGVudGlvbkRheXMob3ZlcnJpZGU/OiBudW1iZXIgfCBudWxsKTogbnVtYmVyIHtcbiAgaWYgKG92ZXJyaWRlID09PSAtMSkgcmV0dXJuIC0xO1xuICByZXR1cm4gb3ZlcnJpZGUgIT0gbnVsbCAmJiBOdW1iZXIuaXNJbnRlZ2VyKG92ZXJyaWRlKSAmJiBvdmVycmlkZSA+PSBSRVRFTlRJT05fTUlOX0RBWVNcbiAgICA/IE1hdGgubWluKG92ZXJyaWRlLCBSRVRFTlRJT05fTUFYX0RBWVMpIDogUkVQT1JUSU5HX0VWRU5UX1JFVEVOVElPTl9EQVlTO1xufVxuXG4vKiogRWZmZWN0aXZlIERPUkEtc291cmNlIHJldGVudGlvbiAoZGF5cyk6IGEgdmFsaWQgcGVyLW9yZyBvdmVycmlkZSB3aW5zLCBlbHNlXG4gKiAgdGhlIGdsb2JhbCBlbnYgZGVmYXVsdC4gVGhlIGAtMWAgdW5saW1pdGVkIHNlbnRpbmVsIHBhc3NlcyBzdHJhaWdodFxuICogIHRocm91Z2gg4oCUIHRoZSBzd2VlcCByZWFkcyBpdCBhcyBcImtlZXAgZm9yZXZlciwgc2tpcCB0aGlzIG9yZydzIERPUkEtc291cmNlXG4gKiAgd2luZG93c1wiLiBBIHBvc2l0aXZlIG92ZXJyaWRlIGNsYW1wcyB0byBbMSwgUkVURU5USU9OX01BWF9EQVlTXTsgYSBgbnVsbGAsIGFcbiAqICBub24taW50ZWdlciwgb3IgYW55IG90aGVyIG91dC1vZi1yYW5nZSB2YWx1ZSAoZS5nLiAwLCAtMikgZmFsbHMgYmFjayB0byB0aGVcbiAqICBlbnYgZGVmYXVsdC4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZXNvbHZlRG9yYVJldGVudGlvbkRheXMob3ZlcnJpZGU/OiBudW1iZXIgfCBudWxsKTogbnVtYmVyIHtcbiAgaWYgKG92ZXJyaWRlID09PSAtMSkgcmV0dXJuIC0xO1xuICByZXR1cm4gb3ZlcnJpZGUgIT0gbnVsbCAmJiBOdW1iZXIuaXNJbnRlZ2VyKG92ZXJyaWRlKSAmJiBvdmVycmlkZSA+PSBSRVRFTlRJT05fTUlOX0RBWVNcbiAgICA/IE1hdGgubWluKG92ZXJyaWRlLCBSRVRFTlRJT05fTUFYX0RBWVMpIDogUkVQT1JUSU5HX0RPUkFfUkVURU5USU9OX0RBWVM7XG59XG5cbi8qKiBUaGUgY3V0b2ZmIGluc3RhbnQgZm9yIGEgcmV0ZW50aW9uIHdpbmRvdzogcm93cyB3aXRoIGBjcmVhdGVkX2F0YCBzdHJpY3RseVxuICogIGJlZm9yZSB0aGlzIChgPCBjdXRvZmZgKSBhcmUgZXhwaXJlZC4gRGV0ZXJtaW5pc3RpYyBmb3IgYSBmaXhlZCBgbm93YC4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZXRlbnRpb25DdXRvZmYobm93OiBEYXRlLCBkYXlzOiBudW1iZXIpOiBEYXRlIHtcbiAgcmV0dXJuIG5ldyBEYXRlKG5vdy5nZXRUaW1lKCkgLSBkYXlzICogODZfNDAwXzAwMCk7XG59XG5cbi8qKiBQZXItb3JnIHJldGVudGlvbiBvdmVycmlkZSByb3cuIEJvdGggbnVsbGFibGUg4oeSIGdsb2JhbCBkZWZhdWx0cy4gKi9cbiJdfQ==
@@ -7,11 +7,8 @@ export declare class ReportingService {
7
7
  /**
8
8
  * Resolve incoming events against the pipeline registry, batch-insert the
9
9
  * matched ones, and invalidate reporting caches for affected orgs.
10
- * Events for unregistered pipeline ids are dropped (and logged at WARN
11
- * with sample ids so an operator can see when EventBridge is delivering
12
- * events for pipelines that haven't called POST /pipelines/registry yet).
13
- *
14
- * Returns counts + a sample of unregistered pipeline ids for observability.
10
+ * See ./reporting/ingest.ts — the caller must already hold a sysadmin
11
+ * tenant scope, because a batch spans whatever orgs the registry resolves to.
15
12
  */
16
13
  ingestEvents(events: IngestEvent[], onMetric?: (m: IngestMetric) => void): Promise<IngestResult>;
17
14
  /** Execution count per pipeline with status breakdown. */
@@ -189,19 +186,9 @@ export declare class ReportingService {
189
186
  */
190
187
  getIngestHealth(orgId: string): Promise<IngestHealthStatus | null>;
191
188
  /**
192
- * Reporting retention sweep. Hard-deletes rows older than their
193
- * retention window, by `created_at`, across every org that has reporting data —
194
- * a **split** policy so high-volume standard events expire faster than the
195
- * low-volume DORA source:
196
- * - `pipeline_events WHERE environment IS NULL` → standard-event window.
197
- * - `pipeline_events WHERE environment IS NOT NULL` (deploy stages),
198
- * `deployment_outcomes`, and `incidents` → DORA-source window.
199
- * Each org's windows come from its `dora_settings` override, else the global
200
- * env defaults (see {@link resolveEventRetentionDays} / {@link resolveDoraRetentionDays}).
201
- * `ingest_health` and `dora_settings` are never purged. One `now` for the whole
202
- * tick (rows crossing the boundary mid-sweep wait for the next). Establishes a
203
- * sysadmin tenant scope so the deletes span all orgs / bypass RLS — this is a
204
- * cross-tenant housekeeping job. Returns (and logs) per-window purge tallies.
189
+ * Reporting retention sweep: hard-deletes rows past their per-org retention
190
+ * window (split standard-event vs DORA-source). Cross-tenant housekeeping —
191
+ * it establishes its own sysadmin scope. See ./reporting/retention-sweep.ts.
205
192
  */
206
193
  purgeExpiredReportingData(opts?: ReportingRetentionOptions): Promise<ReportingRetentionCounts>;
207
194
  }