@pipeline-builder/pipeline-data 4.1.24 → 4.1.26
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 +2244 -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 +1373 -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,450 @@
|
|
|
1
|
+
import type { ReportCadence, ReportDefinition, ReportPauseReason, ReportRecipient, ReportRun, ReportRunStatus, ReportScope, ReportShareLink, ReportTemplate } from '../../../database/schema/reporting-stakeholder.js';
|
|
2
|
+
/** How long a freshly minted share link lives when the caller names no window. */
|
|
3
|
+
export declare const DEFAULT_SHARE_LINK_TTL_DAYS = 30;
|
|
4
|
+
/** The longest a share link may live. Beyond this it is a permanent URL. */
|
|
5
|
+
export declare const MAX_SHARE_LINK_TTL_DAYS = 180;
|
|
6
|
+
/** A recipient's verification link. Short: it is clicked within minutes or never. */
|
|
7
|
+
export declare const VERIFICATION_TTL_MS: number;
|
|
8
|
+
/**
|
|
9
|
+
* Bounces before an address is treated as undeliverable and skipped. Three, not
|
|
10
|
+
* one: a single bounce is often an out-of-office autoresponder or a full mailbox,
|
|
11
|
+
* and dropping a manager off the list for one of those is a silent failure.
|
|
12
|
+
*/
|
|
13
|
+
export declare const MAX_BOUNCES = 3;
|
|
14
|
+
/** SHA-256 hex of a token. The only form any token is ever stored in. */
|
|
15
|
+
export declare function hashToken(token: string): string;
|
|
16
|
+
/** A fresh URL-safe token, and its hash. The raw value is returned exactly once. */
|
|
17
|
+
export declare function mintToken(): {
|
|
18
|
+
token: string;
|
|
19
|
+
tokenHash: string;
|
|
20
|
+
};
|
|
21
|
+
/** What creating a definition needs. */
|
|
22
|
+
export interface CreateDefinitionInput {
|
|
23
|
+
orgId: string;
|
|
24
|
+
name: string;
|
|
25
|
+
template: ReportTemplate;
|
|
26
|
+
sections: string[];
|
|
27
|
+
cadence: ReportCadence;
|
|
28
|
+
timezone: string;
|
|
29
|
+
weekStart: 'monday' | 'sunday';
|
|
30
|
+
scope: ReportScope;
|
|
31
|
+
recipients: string[];
|
|
32
|
+
autoSend: boolean;
|
|
33
|
+
ownerId: string;
|
|
34
|
+
createdBy: string;
|
|
35
|
+
nextRunAt?: Date | null;
|
|
36
|
+
}
|
|
37
|
+
/** The mutable half of a definition. */
|
|
38
|
+
export interface UpdateDefinitionInput {
|
|
39
|
+
name?: string;
|
|
40
|
+
template?: ReportTemplate;
|
|
41
|
+
sections?: string[];
|
|
42
|
+
cadence?: ReportCadence;
|
|
43
|
+
timezone?: string;
|
|
44
|
+
weekStart?: 'monday' | 'sunday';
|
|
45
|
+
scope?: ReportScope;
|
|
46
|
+
recipients?: string[];
|
|
47
|
+
autoSend?: boolean;
|
|
48
|
+
isActive?: boolean;
|
|
49
|
+
pausedReason?: ReportPauseReason | null;
|
|
50
|
+
nextRunAt?: Date | null;
|
|
51
|
+
}
|
|
52
|
+
/** A share link plus the one-time token, returned only from the mint call. */
|
|
53
|
+
export interface MintedShareLink {
|
|
54
|
+
link: ReportShareLink;
|
|
55
|
+
/** Shown once. Never stored, never logged, never in a response again. */
|
|
56
|
+
token: string;
|
|
57
|
+
}
|
|
58
|
+
/** What the public route needs: the link, its run, and the owning org. */
|
|
59
|
+
export interface ResolvedShareLink {
|
|
60
|
+
link: ReportShareLink;
|
|
61
|
+
run: ReportRun;
|
|
62
|
+
}
|
|
63
|
+
export declare class StakeholderReportStore {
|
|
64
|
+
createDefinition(input: CreateDefinitionInput): Promise<ReportDefinition>;
|
|
65
|
+
listDefinitions(orgId: string): Promise<ReportDefinition[]>;
|
|
66
|
+
getDefinition(orgId: string, id: string): Promise<ReportDefinition | null>;
|
|
67
|
+
/** Throws {@link NotFoundError} rather than returning null, for route bodies. */
|
|
68
|
+
requireDefinition(orgId: string, id: string): Promise<ReportDefinition>;
|
|
69
|
+
updateDefinition(orgId: string, id: string, patch: UpdateDefinitionInput, updatedBy: string): Promise<ReportDefinition>;
|
|
70
|
+
/**
|
|
71
|
+
* Hand a definition to a new owner. The owner is who a scheduled run is
|
|
72
|
+
* authorized AS, so this is a privilege move, not a metadata edit — the route
|
|
73
|
+
* checks the new owner is a live member of the org before calling.
|
|
74
|
+
*/
|
|
75
|
+
transferOwner(orgId: string, id: string, newOwnerId: string, actorId: string): Promise<ReportDefinition>;
|
|
76
|
+
/**
|
|
77
|
+
* Pause every definition a user owns, with the reason recorded. Called when the
|
|
78
|
+
* owner is deactivated or loses `reports:author` — a report must stop when the
|
|
79
|
+
* person accountable for it can no longer see the data behind it.
|
|
80
|
+
*
|
|
81
|
+
* Returns the paused definitions so the caller can notify an org admin: a
|
|
82
|
+
* silently stopped weekly report is discovered by a manager not receiving it.
|
|
83
|
+
*/
|
|
84
|
+
pauseDefinitionsForOwner(orgId: string, ownerId: string, reason: ReportPauseReason): Promise<ReportDefinition[]>;
|
|
85
|
+
deleteDefinition(orgId: string, id: string, deletedBy: string): Promise<void>;
|
|
86
|
+
/**
|
|
87
|
+
* Insert a run for a period, or return the existing one.
|
|
88
|
+
*
|
|
89
|
+
* Idempotent on (definition, periodStart, version): a retry, a catch-up pass
|
|
90
|
+
* and a manual backfill can all ask for the same period, and without this each
|
|
91
|
+
* would insert a row and the lead would review three copies. `onConflictDoNothing`
|
|
92
|
+
* plus a re-read is deliberate — the unique index is the arbiter, so two pods
|
|
93
|
+
* racing produce one row whichever wins.
|
|
94
|
+
*/
|
|
95
|
+
createRun(input: {
|
|
96
|
+
orgId: string;
|
|
97
|
+
definitionId: string;
|
|
98
|
+
periodStart: Date;
|
|
99
|
+
periodEnd: Date;
|
|
100
|
+
periodLabel: string;
|
|
101
|
+
version?: number;
|
|
102
|
+
status?: ReportRunStatus;
|
|
103
|
+
snapshot?: Record<string, unknown> | null;
|
|
104
|
+
failureReason?: string | null;
|
|
105
|
+
}): Promise<{
|
|
106
|
+
run: ReportRun;
|
|
107
|
+
created: boolean;
|
|
108
|
+
}>;
|
|
109
|
+
/** The next version number for a period: `max(version) + 1`. */
|
|
110
|
+
nextVersion(definitionId: string, periodStart: Date): Promise<number>;
|
|
111
|
+
/** Point an older run at the version that replaced it. */
|
|
112
|
+
supersede(orgId: string, oldRunId: string, newRunId: string): Promise<void>;
|
|
113
|
+
listRuns(orgId: string, definitionId: string, limit?: number): Promise<ReportRun[]>;
|
|
114
|
+
getRun(orgId: string, id: string): Promise<ReportRun | null>;
|
|
115
|
+
requireRun(orgId: string, id: string): Promise<ReportRun>;
|
|
116
|
+
/** Store a computed snapshot and move the run to `ready_for_review`. */
|
|
117
|
+
completeRun(orgId: string, id: string, snapshot: Record<string, unknown>, status?: ReportRunStatus): Promise<ReportRun>;
|
|
118
|
+
failRun(orgId: string, id: string, failureReason: string): Promise<void>;
|
|
119
|
+
/**
|
|
120
|
+
* Edit the lead's own words on a run that has NOT been published.
|
|
121
|
+
*
|
|
122
|
+
* Refused after publish: the notes are part of what the manager read, and a
|
|
123
|
+
* report whose narrative can be edited after delivery is a report whose
|
|
124
|
+
* delivered version cannot be reconstructed. Regenerating produces version N+1,
|
|
125
|
+
* which is the supported way to correct a published report.
|
|
126
|
+
*/
|
|
127
|
+
setRunNotes(orgId: string, id: string, patch: {
|
|
128
|
+
leadNotes?: string;
|
|
129
|
+
aiDraft?: string;
|
|
130
|
+
}): Promise<ReportRun>;
|
|
131
|
+
/**
|
|
132
|
+
* Record what happened when a run was delivered.
|
|
133
|
+
*
|
|
134
|
+
* Deliberately NOT part of {@link setRunNotes}, which refuses a published run: delivery
|
|
135
|
+
* happens AT publish, so a method that refused published runs could never record it.
|
|
136
|
+
* This one writes exactly one column and nothing else, which is why it is safe to allow
|
|
137
|
+
* after publish — it cannot change a number a recipient has read.
|
|
138
|
+
*/
|
|
139
|
+
recordDelivery(orgId: string, id: string, delivery: Record<string, unknown>): Promise<void>;
|
|
140
|
+
/**
|
|
141
|
+
* Publish a run.
|
|
142
|
+
*
|
|
143
|
+
* Conditional on the row still being unpublished (`published_at IS NULL`), so
|
|
144
|
+
* two clicks — or two tabs — produce one publish and one set of deliveries
|
|
145
|
+
* rather than two. A run with no snapshot cannot be published: there would be
|
|
146
|
+
* nothing for a recipient to read.
|
|
147
|
+
*/
|
|
148
|
+
publishRun(orgId: string, id: string, publishedBy: string): Promise<{
|
|
149
|
+
run: ReportRun;
|
|
150
|
+
alreadyPublished: boolean;
|
|
151
|
+
}>;
|
|
152
|
+
/**
|
|
153
|
+
* Mint a read-only link to a published run. The raw token is returned here and
|
|
154
|
+
* nowhere else — only its hash is stored, so a database copy cannot be turned
|
|
155
|
+
* back into working URLs.
|
|
156
|
+
*/
|
|
157
|
+
createShareLink(input: {
|
|
158
|
+
orgId: string;
|
|
159
|
+
runId: string;
|
|
160
|
+
createdBy: string;
|
|
161
|
+
ttlDays?: number;
|
|
162
|
+
redactNames?: boolean;
|
|
163
|
+
now?: Date;
|
|
164
|
+
}): Promise<MintedShareLink>;
|
|
165
|
+
listShareLinks(orgId: string, runId: string): Promise<ReportShareLink[]>;
|
|
166
|
+
revokeShareLink(orgId: string, id: string, revokedBy: string): Promise<ReportShareLink>;
|
|
167
|
+
/** Revoke every live link to any run of a definition. Used on delete. */
|
|
168
|
+
revokeLinksForDefinition(orgId: string, definitionId: string, revokedBy: string): Promise<number>;
|
|
169
|
+
/**
|
|
170
|
+
* Resolve a share token to its link and run, for the UNAUTHENTICATED public
|
|
171
|
+
* route.
|
|
172
|
+
*
|
|
173
|
+
* This is the one read in this module that runs as sysadmin, and the reason is
|
|
174
|
+
* structural: the reader holds a token and nothing else — no account, no org,
|
|
175
|
+
* no JWT — so there is no tenant context for RLS to scope by, and the org can
|
|
176
|
+
* only be learned FROM the row. The escalation is kept as narrow as the problem:
|
|
177
|
+
*
|
|
178
|
+
* - The sysadmin scope covers the token-hash lookup ONLY. The run is read back
|
|
179
|
+
* under the link's OWN org, so a bug in the run query cannot reach another
|
|
180
|
+
* tenant's rows.
|
|
181
|
+
* - The lookup is by a 256-bit hash on a unique index. It is not a filter a
|
|
182
|
+
* caller can widen — there is no other predicate to influence.
|
|
183
|
+
* - Expired and revoked links resolve to null here, so the caller cannot
|
|
184
|
+
* accidentally serve one by forgetting a check.
|
|
185
|
+
*
|
|
186
|
+
* Returns null for every failure — unknown, revoked, expired, or pointing at a
|
|
187
|
+
* run that is no longer published — so the route answers one indistinguishable
|
|
188
|
+
* 404 and a holder of a dead link learns nothing about which of those it was.
|
|
189
|
+
*/
|
|
190
|
+
resolveShareLink(token: string, now?: Date): Promise<ResolvedShareLink | null>;
|
|
191
|
+
/**
|
|
192
|
+
* Count one human view of a shared report.
|
|
193
|
+
*
|
|
194
|
+
* Separate from {@link resolveShareLink} so the route can serve a link preview,
|
|
195
|
+
* a HEAD probe or a mail scanner's fetch WITHOUT counting it — a view count that
|
|
196
|
+
* jumps the moment a link is pasted into Slack tells the lead their manager read
|
|
197
|
+
* the report when nobody has.
|
|
198
|
+
*/
|
|
199
|
+
recordShareView(link: ReportShareLink, now?: Date): Promise<void>;
|
|
200
|
+
/**
|
|
201
|
+
* Add (or re-add) a delivery address.
|
|
202
|
+
*
|
|
203
|
+
* Upserts on (org, email) and deliberately does NOT reset `unsubscribed_at` or
|
|
204
|
+
* `verified_at`: a lead must not be able to undo someone's unsubscribe by
|
|
205
|
+
* removing and re-adding them, and an address already verified should not have
|
|
206
|
+
* to confirm twice. A verification token is minted only when the address is not
|
|
207
|
+
* yet verified.
|
|
208
|
+
*/
|
|
209
|
+
upsertRecipient(input: {
|
|
210
|
+
orgId: string;
|
|
211
|
+
email: string;
|
|
212
|
+
displayName?: string;
|
|
213
|
+
createdBy: string;
|
|
214
|
+
/** Set when an admin pre-approved an out-of-domain address. */
|
|
215
|
+
approvedBy?: string | null;
|
|
216
|
+
/** Org members skip verification — they already authenticated to this org. */
|
|
217
|
+
preVerified?: boolean;
|
|
218
|
+
now?: Date;
|
|
219
|
+
}): Promise<{
|
|
220
|
+
recipient: ReportRecipient;
|
|
221
|
+
verificationToken?: string;
|
|
222
|
+
}>;
|
|
223
|
+
listRecipients(orgId: string): Promise<ReportRecipient[]>;
|
|
224
|
+
getRecipients(orgId: string, ids: string[]): Promise<ReportRecipient[]>;
|
|
225
|
+
/** Mint a fresh verification token for a recipient that has not confirmed yet. */
|
|
226
|
+
resendVerification(orgId: string, id: string): Promise<{
|
|
227
|
+
recipient: ReportRecipient;
|
|
228
|
+
token: string;
|
|
229
|
+
}>;
|
|
230
|
+
/**
|
|
231
|
+
* Confirm an address from the emailed token. Cross-org by necessity (the person
|
|
232
|
+
* clicking has no session), so it runs as sysadmin over a unique 256-bit hash —
|
|
233
|
+
* the same shape, and the same reasoning, as {@link resolveShareLink}.
|
|
234
|
+
*
|
|
235
|
+
* The token is CONSUMED: it is cleared on success, so a forwarded confirmation
|
|
236
|
+
* link cannot be replayed by whoever the mail was forwarded to.
|
|
237
|
+
*/
|
|
238
|
+
verifyRecipientByToken(token: string, now?: Date): Promise<ReportRecipient | null>;
|
|
239
|
+
/** Honour an unsubscribe across every definition in the org. */
|
|
240
|
+
unsubscribeByEmail(orgId: string, email: string, now?: Date): Promise<boolean>;
|
|
241
|
+
/**
|
|
242
|
+
* Honour an unsubscribe from the link in a report email.
|
|
243
|
+
*
|
|
244
|
+
* Looked up by TOKEN across orgs, as sysadmin, for the same reason the share-link
|
|
245
|
+
* resolve is: the person clicking has no account and no tenant, and the token is the
|
|
246
|
+
* only thing that identifies the row. It is the narrowest possible shape — one indexed
|
|
247
|
+
* lookup, one column written, nothing returned to the caller but whether a row
|
|
248
|
+
* matched.
|
|
249
|
+
*
|
|
250
|
+
* Idempotent. A mail client that sends the one-click POST twice, or a manager who
|
|
251
|
+
* clicks again months later, gets the same answer rather than an error page — the
|
|
252
|
+
* second call finds the row already unsubscribed and reports success, because from the
|
|
253
|
+
* clicker's point of view it is.
|
|
254
|
+
*/
|
|
255
|
+
unsubscribeByToken(token: string, now?: Date): Promise<boolean>;
|
|
256
|
+
/** Record a delivery bounce. At {@link MAX_BOUNCES} the address is skipped. */
|
|
257
|
+
recordBounce(orgId: string, email: string, now?: Date): Promise<number>;
|
|
258
|
+
deleteRecipient(orgId: string, id: string, deletedBy: string): Promise<void>;
|
|
259
|
+
/**
|
|
260
|
+
* Is this address deliverable right now? The single place the delivery rules
|
|
261
|
+
* live, so the scheduler and the UI agree about why a recipient is being
|
|
262
|
+
* skipped.
|
|
263
|
+
*/
|
|
264
|
+
deliverability(recipient: ReportRecipient): {
|
|
265
|
+
deliverable: boolean;
|
|
266
|
+
reason?: string;
|
|
267
|
+
};
|
|
268
|
+
/**
|
|
269
|
+
* The org's admin-owned report policy. Read through the reporting settings row,
|
|
270
|
+
* which may not exist — an org that has never configured anything gets the
|
|
271
|
+
* closed defaults (no external sharing, no external domains, approval required).
|
|
272
|
+
*/
|
|
273
|
+
getReportPolicy(orgId: string): Promise<{
|
|
274
|
+
externalSharing: boolean;
|
|
275
|
+
recipientDomains: string[] | null;
|
|
276
|
+
requireApproval: boolean;
|
|
277
|
+
defaultTimezone: string | null;
|
|
278
|
+
defaultWeekStart: string | null;
|
|
279
|
+
}>;
|
|
280
|
+
setReportPolicy(orgId: string, patch: {
|
|
281
|
+
externalSharing?: boolean;
|
|
282
|
+
recipientDomains?: string[] | null;
|
|
283
|
+
requireApproval?: boolean;
|
|
284
|
+
defaultTimezone?: string | null;
|
|
285
|
+
defaultWeekStart?: string | null;
|
|
286
|
+
}): Promise<void>;
|
|
287
|
+
/**
|
|
288
|
+
* Apply a Stakeholder Reports entitlement change from billing: pause everything, or
|
|
289
|
+
* resume what the lapse paused.
|
|
290
|
+
*
|
|
291
|
+
* THE WATERMARK AND THE WORK ARE ONE TRANSACTION, and the watermark update is what
|
|
292
|
+
* serializes concurrent pushes. Billing's legs are best-effort and retried, so
|
|
293
|
+
* "lapsed at 10:00" and "renewed at 10:05" can arrive in either order; applying the
|
|
294
|
+
* older one last would leave a lapsed account producing reports, or a paying one
|
|
295
|
+
* paused. The conditional update takes a row lock on `dora_settings`, so a second
|
|
296
|
+
* push waits and then sees the newer watermark — no advisory lock needed.
|
|
297
|
+
*
|
|
298
|
+
* RESUME IS SCOPED TO `entitlement`. It must not un-pause a definition whose OWNER was
|
|
299
|
+
* deactivated or who lost the permission: those are different problems with different
|
|
300
|
+
* fixes, and resuming one by fixing the other is how a report starts running under
|
|
301
|
+
* somebody who left.
|
|
302
|
+
*
|
|
303
|
+
* `nextRunFor` is a callback because the period arithmetic needs each definition's
|
|
304
|
+
* cadence, timezone and week start, and that belongs with the period resolver rather
|
|
305
|
+
* than the store — while the write still has to happen inside this transaction.
|
|
306
|
+
*
|
|
307
|
+
* This leg makes the change IMMEDIATE; it is not what makes it safe. Every scheduled
|
|
308
|
+
* run re-checks the entitlement against platform anyway, so a lost push delays
|
|
309
|
+
* enforcement to the next run rather than defeating it.
|
|
310
|
+
*/
|
|
311
|
+
syncReportEntitlement(orgId: string, entitled: boolean, opts?: {
|
|
312
|
+
occurredAt?: Date;
|
|
313
|
+
nextRunFor?: (d: ReportDefinition) => Date;
|
|
314
|
+
now?: Date;
|
|
315
|
+
}): Promise<{
|
|
316
|
+
skipped: boolean;
|
|
317
|
+
paused: ReportDefinition[];
|
|
318
|
+
resumed: ReportDefinition[];
|
|
319
|
+
}>;
|
|
320
|
+
/**
|
|
321
|
+
* Claim the org's ONE free preview, or report that it is already spent.
|
|
322
|
+
*
|
|
323
|
+
* A CONDITIONAL insert-or-update, not read-then-write: two tabs clicking "see a sample"
|
|
324
|
+
* at the same moment must produce one preview, and a check followed by a separate write
|
|
325
|
+
* has a window between them wide enough for both to pass. `WHERE report_preview_used_at
|
|
326
|
+
* IS NULL` on the update, and `onConflictDoUpdate` for the row that may not exist yet,
|
|
327
|
+
* make the claim itself the guard.
|
|
328
|
+
*
|
|
329
|
+
* Returns whether THIS caller won it. A loser is told the preview is spent, which is
|
|
330
|
+
* the truth and is also the upsell.
|
|
331
|
+
*/
|
|
332
|
+
claimReportPreview(orgId: string, now?: Date): Promise<boolean>;
|
|
333
|
+
/** Whether the org has already spent its free preview. */
|
|
334
|
+
reportPreviewUsed(orgId: string): Promise<boolean>;
|
|
335
|
+
/**
|
|
336
|
+
* Is `email` admissible as a recipient for this org, and does it need an
|
|
337
|
+
* admin's approval first?
|
|
338
|
+
*
|
|
339
|
+
* `orgMemberEmails` is the set of addresses that already authenticate to this
|
|
340
|
+
* org — they are always admissible and never need verification, because the org
|
|
341
|
+
* has already established they belong to it. Everything else is measured against
|
|
342
|
+
* the allowed-domain list, and a NULL list means "no external addresses at all",
|
|
343
|
+
* not "any address": the closed reading is the one that cannot leak a report by
|
|
344
|
+
* an admin forgetting to configure something.
|
|
345
|
+
*/
|
|
346
|
+
admitRecipient(email: string, policy: {
|
|
347
|
+
recipientDomains: string[] | null;
|
|
348
|
+
requireApproval: boolean;
|
|
349
|
+
}, orgMemberEmails: ReadonlySet<string>): {
|
|
350
|
+
admitted: true;
|
|
351
|
+
member: boolean;
|
|
352
|
+
needsApproval: boolean;
|
|
353
|
+
} | {
|
|
354
|
+
admitted: false;
|
|
355
|
+
reason: string;
|
|
356
|
+
};
|
|
357
|
+
/**
|
|
358
|
+
* Hard-delete expired tombstones across every org, for the shared soft-delete
|
|
359
|
+
* sweep. Runs inside the sweep's own sysadmin scope.
|
|
360
|
+
*
|
|
361
|
+
* Runs go first: a purged definition with orphaned runs would leave snapshots
|
|
362
|
+
* nothing can reach, and share links are deleted with their runs so a purge
|
|
363
|
+
* cannot leave a live token pointing at a row that no longer exists.
|
|
364
|
+
*/
|
|
365
|
+
purgeableEntities(): Array<{
|
|
366
|
+
name: string;
|
|
367
|
+
purgeExpired(now: Date, limit?: number): Promise<number>;
|
|
368
|
+
}>;
|
|
369
|
+
private purgeRuns;
|
|
370
|
+
private purgeDefinitions;
|
|
371
|
+
private purgeRecipients;
|
|
372
|
+
/** Every definition the owner must be re-checked for, for the pause sweep. */
|
|
373
|
+
listOwnedDefinitions(orgId: string, ownerId: string): Promise<ReportDefinition[]>;
|
|
374
|
+
/**
|
|
375
|
+
* Definitions whose next run is due, across every org.
|
|
376
|
+
*
|
|
377
|
+
* Runs as SYSADMIN, because the scheduler has no tenant: it is looking for work
|
|
378
|
+
* in orgs it has not been told about. This is the one read here that crosses org
|
|
379
|
+
* boundaries, and it is kept to the narrowest possible shape — the rows needed to
|
|
380
|
+
* claim and execute, ordered oldest-due first so a backlog drains in order rather
|
|
381
|
+
* than starving whichever org sorts last by id.
|
|
382
|
+
*
|
|
383
|
+
* `limit` is the per-cycle ceiling: a cycle that tried to drain everything would
|
|
384
|
+
* hold the leader lock for as long as the slowest org's compose takes, times the
|
|
385
|
+
* number of definitions.
|
|
386
|
+
*/
|
|
387
|
+
dueDefinitions(now: Date, limit: number): Promise<ReportDefinition[]>;
|
|
388
|
+
/**
|
|
389
|
+
* Claim a due definition by advancing its `nextRunAt`, but only if it still holds
|
|
390
|
+
* the value the scan saw.
|
|
391
|
+
*
|
|
392
|
+
* THE CONDITIONAL UPDATE IS THE CONCURRENCY GUARD, not the leader lock. The lock
|
|
393
|
+
* stops two replicas sweeping in the same window; it does not stop a sweep that
|
|
394
|
+
* outlives its lock TTL from overlapping the next leader's sweep, and both would
|
|
395
|
+
* then select the same still-due definition. Without the `next_run_at = <seen>`
|
|
396
|
+
* predicate each would advance the schedule and each would compose and DELIVER a
|
|
397
|
+
* report — the same manager gets the same report twice, from two pods, which is
|
|
398
|
+
* the failure everyone notices.
|
|
399
|
+
*
|
|
400
|
+
* Runs as sysadmin for the same reason as the scan, and returns whether the claim
|
|
401
|
+
* won so the caller can simply skip a lost race.
|
|
402
|
+
*/
|
|
403
|
+
claimDefinition(id: string, seenNextRunAt: Date, nextRunAt: Date, now?: Date): Promise<boolean>;
|
|
404
|
+
/**
|
|
405
|
+
* Set (or clear) a definition's `nextRunAt` without claiming it.
|
|
406
|
+
*
|
|
407
|
+
* Used when a definition is created, resumed, or has its cadence/timezone changed
|
|
408
|
+
* — the schedule has to be re-derived from the calendar rather than left at
|
|
409
|
+
* whatever the old cadence produced.
|
|
410
|
+
*/
|
|
411
|
+
setNextRun(orgId: string, id: string, nextRunAt: Date | null): Promise<void>;
|
|
412
|
+
/**
|
|
413
|
+
* Pause ONE definition with the reason, from the scheduler.
|
|
414
|
+
*
|
|
415
|
+
* Separate from {@link pauseDefinitionsForOwner} because the trigger is different:
|
|
416
|
+
* that one is a membership change affecting every definition a person owns, this
|
|
417
|
+
* one is a per-run recheck that failed. Clears `nextRunAt` so a paused definition
|
|
418
|
+
* stops appearing in the due scan at all, rather than being re-claimed and
|
|
419
|
+
* re-rejected every cycle.
|
|
420
|
+
*
|
|
421
|
+
* Runs as sysadmin: the caller is the scheduler, which has no tenant, and the id
|
|
422
|
+
* came from its own cross-org scan.
|
|
423
|
+
*/
|
|
424
|
+
pauseDefinition(id: string, reason: ReportPauseReason): Promise<void>;
|
|
425
|
+
/**
|
|
426
|
+
* Resume a paused definition, clearing the reason and re-deriving its schedule.
|
|
427
|
+
*
|
|
428
|
+
* `nextRunAt` is supplied by the caller rather than computed here: the period
|
|
429
|
+
* arithmetic needs the definition's timezone and week start, and that belongs with
|
|
430
|
+
* the period resolver, not the store.
|
|
431
|
+
*/
|
|
432
|
+
resumeDefinition(orgId: string, id: string, nextRunAt: Date, actorId: string): Promise<ReportDefinition>;
|
|
433
|
+
/**
|
|
434
|
+
* Pause every ACTIVE definition in an org, with the reason.
|
|
435
|
+
*
|
|
436
|
+
* The entitlement lapse leg: billing tells reporting the add-on is gone, and every
|
|
437
|
+
* definition in the account stops. Returns the rows so the caller can notify.
|
|
438
|
+
*/
|
|
439
|
+
pauseDefinitionsForOrg(orgId: string, reason: ReportPauseReason): Promise<ReportDefinition[]>;
|
|
440
|
+
/**
|
|
441
|
+
* Every definition in an org paused for `reason`, for the resume leg.
|
|
442
|
+
*
|
|
443
|
+
* Scoped to the reason on purpose: re-subscribing must not un-pause a definition
|
|
444
|
+
* whose owner was deactivated. Those are different problems with different fixes,
|
|
445
|
+
* and resuming one by fixing the other is how a report starts running under a
|
|
446
|
+
* person who left.
|
|
447
|
+
*/
|
|
448
|
+
definitionsPausedFor(orgId: string, reason: ReportPauseReason): Promise<ReportDefinition[]>;
|
|
449
|
+
}
|
|
450
|
+
export declare const stakeholderReportStore: StakeholderReportStore;
|