@adcp/sdk 15.6.0 → 15.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/lib/reporting/ledger/handler.js +20 -8
  2. package/dist/lib/reporting/ledger/handler.mjs +20 -8
  3. package/dist/lib/reporting/ledger/postgres.d.mts +11 -0
  4. package/dist/lib/reporting/ledger/postgres.d.ts +11 -0
  5. package/dist/lib/reporting/ledger/postgres.js +12 -0
  6. package/dist/lib/reporting/ledger/postgres.mjs +12 -0
  7. package/dist/lib/reporting/service/index.d.mts +36 -21
  8. package/dist/lib/reporting/service/index.d.ts +36 -21
  9. package/dist/lib/reporting/service/index.js +75 -65
  10. package/dist/lib/reporting/service/index.mjs +79 -64
  11. package/dist/lib/reporting/service/maintenance.d.mts +91 -0
  12. package/dist/lib/reporting/service/maintenance.d.ts +91 -0
  13. package/dist/lib/reporting/service/maintenance.js +140 -0
  14. package/dist/lib/reporting/service/maintenance.mjs +114 -0
  15. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  16. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  17. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  18. package/dist/lib/types/schemas.generated.js +2542 -2542
  19. package/dist/lib/types/schemas.generated.mjs +2542 -2542
  20. package/dist/lib/validation/bidding-policy.js +1 -1
  21. package/dist/lib/validation/bidding-policy.mjs +1 -1
  22. package/dist/lib/validation/budget-allocation.js +1 -1
  23. package/dist/lib/validation/budget-allocation.mjs +1 -1
  24. package/dist/lib/validation/get-products-request.js +3 -3
  25. package/dist/lib/validation/get-products-request.mjs +3 -3
  26. package/dist/lib/validation/sync-creatives.js +9 -13
  27. package/dist/lib/validation/sync-creatives.mjs +9 -13
  28. package/dist/lib/version.d.mts +3 -3
  29. package/dist/lib/version.d.ts +3 -3
  30. package/dist/lib/version.js +3 -3
  31. package/dist/lib/version.mjs +3 -3
  32. package/docs/TYPE-SUMMARY.md +2 -2
  33. package/docs/ZOD-SCHEMAS.md +11 -1
  34. package/docs/guides/REPORTING-LEDGER.md +89 -0
  35. package/docs/guides/REPORTING-OPERATIONS.md +6 -0
  36. package/docs/llms.txt +2 -2
  37. package/docs/migration-14.x-rc-worksheet.md +4 -4
  38. package/package.json +1 -1
@@ -30,6 +30,10 @@ import {
30
30
  createPostgresReportingWebhookActivityV1,
31
31
  projectListAccountsReportingWebhookActivityV1
32
32
  } from "../webhook-activity.mjs";
33
+ import {
34
+ assertReportingLedgerMaintenanceOptions,
35
+ runReportingLedgerMaintenance
36
+ } from "./maintenance.mjs";
33
37
  import { assertReportingCalendarDaySource, reportingCalendarDayOrigin } from "../ledger/schedule.mjs";
34
38
  import {
35
39
  ReportingCoverageConstituentIdentityV1Schema,
@@ -45,6 +49,7 @@ import {
45
49
  } from "../source/index.mjs";
46
50
  const ADAPTER_SCOPE_KEY = "_adcp_reporting_adapter";
47
51
  const PRODUCTION_MANAGED_COMPOSITION = /* @__PURE__ */ Symbol("production-managed-reporting-composition");
52
+ const DEFAULT_MAINTENANCE_INTERVAL_MILLISECONDS = 3e5;
48
53
  function createReliableReportingService(options) {
49
54
  const allowManagedDelivery = options[PRODUCTION_MANAGED_COMPOSITION] === true;
50
55
  const adapterEntries = Object.entries(options.adapters);
@@ -106,6 +111,42 @@ function createReliableReportingService(options) {
106
111
  if (hasConsumerStatus) assertConsumerStatusStore(options.store);
107
112
  const retentionDays = options.statusRetentionDays;
108
113
  positiveInteger(retentionDays, "statusRetentionDays");
114
+ const maintenance = options.maintenance;
115
+ let maintenanceStore;
116
+ let maintenanceIntervalMilliseconds = DEFAULT_MAINTENANCE_INTERVAL_MILLISECONDS;
117
+ if (maintenance !== void 0) {
118
+ if (maintenance === null || typeof maintenance !== "object") {
119
+ throw new TypeError("maintenance must be an options object");
120
+ }
121
+ const candidate = options.store;
122
+ for (const method of ["retireExpiredPeriods", "pruneChanges", "sweepRowWriteIntents"]) {
123
+ if (typeof candidate[method] !== "function") {
124
+ throw new TypeError(
125
+ `maintenance requires a PostgresReportingLedgerStore (or a store with ${method}); the configured store cannot run ledger maintenance`
126
+ );
127
+ }
128
+ }
129
+ maintenanceStore = candidate;
130
+ if (maintenance.changeFeed && maintenanceStore.changeFeedEnabled === false) {
131
+ throw new TypeError(
132
+ "maintenance.changeFeed requires the store to be built with changeFeed: true; otherwise pruning silently does nothing"
133
+ );
134
+ }
135
+ if (maintenance.intervalMilliseconds !== void 0) {
136
+ positiveInteger(maintenance.intervalMilliseconds, "maintenance.intervalMilliseconds");
137
+ maintenanceIntervalMilliseconds = maintenance.intervalMilliseconds;
138
+ }
139
+ assertReportingLedgerMaintenanceOptions(maintenance, retentionDays);
140
+ }
141
+ const runLedgerMaintenance = (signal, report) => runReportingLedgerMaintenance({
142
+ store: maintenanceStore,
143
+ ...typeof maintenanceStore.sweepExpiredState === "function" ? { sweepSnapshots: (limit) => maintenanceStore.sweepExpiredState(limit) } : {},
144
+ statusRetentionDays: retentionDays,
145
+ retention: maintenance.retention,
146
+ changeFeed: maintenance.changeFeed,
147
+ signal,
148
+ ...report ? { report } : {}
149
+ });
109
150
  const capabilities = deepFreeze({
110
151
  supported: true,
111
152
  reliable_reporting_version: "1.0",
@@ -278,12 +319,26 @@ function createReliableReportingService(options) {
278
319
  });
279
320
  return { planned: planned.length, ...result };
280
321
  },
322
+ async runMaintenance(maintenanceOptions) {
323
+ if (maintenance === void 0) {
324
+ throw new TypeError("runMaintenance requires the maintenance option");
325
+ }
326
+ maintenanceOptions?.signal?.throwIfAborted();
327
+ return runLedgerMaintenance(maintenanceOptions?.signal);
328
+ },
281
329
  start(scheduler) {
282
330
  if (schedulerPromise) throw new Error("Reliable reporting scheduler is already running");
283
331
  positiveInteger(scheduler.intervalMilliseconds, "intervalMilliseconds");
284
332
  schedulerAbort = new AbortController();
285
333
  const signal = schedulerAbort.signal;
286
- schedulerPromise = schedulerLoop(service, scheduler, signal, options.store).finally(() => {
334
+ const scheduledMaintenance = maintenance === void 0 ? void 0 : {
335
+ intervalMilliseconds: maintenanceIntervalMilliseconds,
336
+ run: () => runLedgerMaintenance(
337
+ signal,
338
+ (error) => reportSchedulerError(scheduler, signal, error, { phase: "maintenance" })
339
+ )
340
+ };
341
+ schedulerPromise = schedulerLoop(service, scheduler, signal, options.store, scheduledMaintenance).finally(() => {
287
342
  schedulerPromise = void 0;
288
343
  schedulerAbort = void 0;
289
344
  });
@@ -515,53 +570,15 @@ async function createPostgresReliableReportingProductionService(options) {
515
570
  for (const result of pruneResults) {
516
571
  if (result.status === "rejected" && !signal.aborted) await reportAuxiliaryError(scheduler, result.reason);
517
572
  }
518
- const ledgerMaintenance = [
519
- ["snapshot sweep", sweepExpiredReportingLedgerState(options.db, 1e3)],
520
- ["row upload sweep", coreStore.sweepRowWriteIntents({ limit: 100, signal })],
521
- ...options.changeFeed ? [
522
- [
523
- "change-feed pruning",
524
- coreStore.pruneChanges(
525
- typeof options.changeFeed === "object" ? {
526
- ...options.changeFeed.changeRetentionDays !== void 0 ? { changeRetentionDays: options.changeFeed.changeRetentionDays } : {},
527
- ...options.changeFeed.maxFeedHoldDays !== void 0 ? { maxFeedHoldDays: options.changeFeed.maxFeedHoldDays } : {}
528
- } : {}
529
- )
530
- ]
531
- ] : [],
532
- ...options.retention?.enabled ? [
533
- [
534
- "retention",
535
- coreStore.retireExpiredPeriods({
536
- statusRetentionDays: options.statusRetentionDays,
537
- ...options.retention.recordRetentionDays !== void 0 ? { recordRetentionDays: options.retention.recordRetentionDays } : {},
538
- ...typeof options.changeFeed === "object" && options.changeFeed.maxFeedHoldDays !== void 0 ? { maxFeedHoldDays: options.changeFeed.maxFeedHoldDays } : {},
539
- limit: options.retention.limit ?? 100,
540
- signal
541
- })
542
- ]
543
- ] : []
544
- ];
545
- const maintenanceResults = await Promise.allSettled(ledgerMaintenance.map(([, task]) => task));
546
- for (const [index, result] of maintenanceResults.entries()) {
547
- const name = ledgerMaintenance[index][0];
548
- if (result.status === "rejected") {
549
- if (!signal.aborted) await reportAuxiliaryError(scheduler, result.reason);
550
- continue;
551
- }
552
- const value = result.value;
553
- const failed = Array.isArray(value?.failed) ? value.failed.length : typeof value?.failed === "number" ? value.failed : 0;
554
- if (failed > 0 && !signal.aborted) {
555
- await reportAuxiliaryError(
556
- scheduler,
557
- new ReportingMaintenancePartialFailureError(
558
- name,
559
- failed,
560
- value?.failures?.length ? value.failures.map((entry) => `${entry.reporting_obligation_id} (${entry.cause})`) : Array.isArray(value?.failed) ? value.failed : []
561
- )
562
- );
563
- }
564
- }
573
+ await runReportingLedgerMaintenance({
574
+ store: coreStore,
575
+ sweepSnapshots: (limit) => sweepExpiredReportingLedgerState(options.db, limit),
576
+ statusRetentionDays: options.statusRetentionDays,
577
+ retention: options.retention,
578
+ changeFeed: options.changeFeed,
579
+ signal,
580
+ report: (error) => reportAuxiliaryError(scheduler, error)
581
+ });
565
582
  const accountIds = rotate(await deploymentWideAccountIds(coreStore), auxiliaryRotation);
566
583
  auxiliaryRotation += 1;
567
584
  for (const accountId of accountIds) {
@@ -687,8 +704,9 @@ function routeSource(sources, sourceScope) {
687
704
  if (!source) throw new TypeError("Reporting sourceScope has no installed adapter route");
688
705
  return source;
689
706
  }
690
- async function schedulerLoop(service, options, signal, store) {
707
+ async function schedulerLoop(service, options, signal, store, maintenance) {
691
708
  let rotation = 0;
709
+ let lastMaintenanceAt;
692
710
  while (!signal.aborted) {
693
711
  let accountIds = [];
694
712
  try {
@@ -725,6 +743,14 @@ async function schedulerLoop(service, options, signal, store) {
725
743
  await reportSchedulerError(options, signal, error, { phase: "cycle", accountId });
726
744
  }
727
745
  }
746
+ if (maintenance && !signal.aborted && (lastMaintenanceAt === void 0 || Date.now() - lastMaintenanceAt >= maintenance.intervalMilliseconds)) {
747
+ lastMaintenanceAt = Date.now();
748
+ try {
749
+ await maintenance.run();
750
+ } catch (error) {
751
+ await reportSchedulerError(options, signal, error, { phase: "maintenance" });
752
+ }
753
+ }
728
754
  if (!signal.aborted) await abortableDelay(options.intervalMilliseconds, signal);
729
755
  }
730
756
  }
@@ -741,7 +767,7 @@ async function reportSchedulerError(options, signal, error, site) {
741
767
  warnSchedulerFailure(options, site, error);
742
768
  }
743
769
  function warnSchedulerFailure(options, site, error, observerError) {
744
- const where = site.phase === "roster" ? "could not resolve its account roster, so this pass ran no tenants" : site.accountId === void 0 ? "deployment-wide cycle failed; the scheduler continues" : `cycle failed for account ${site.accountId}; later tenants continue`;
770
+ const where = site.phase === "roster" ? "could not resolve its account roster, so this pass ran no tenants" : site.phase === "maintenance" ? "ledger maintenance failed; the scheduler continues" : site.accountId === void 0 ? "deployment-wide cycle failed; the scheduler continues" : `cycle failed for account ${site.accountId}; later tenants continue`;
745
771
  const observed = observerError === void 0 ? "" : ` (the configured onError also threw: ${safeMessage(observerError)})`;
746
772
  const message = `[adcp/reporting] scheduler ${where}: ${safeMessage(error)}${observed}`;
747
773
  const logger = options.logger;
@@ -1210,20 +1236,9 @@ function deepFreeze(value) {
1210
1236
  return value;
1211
1237
  }
1212
1238
  export * from "./conformance.mjs";
1213
- class ReportingMaintenancePartialFailureError extends Error {
1214
- constructor(task, failedCount, failedIds) {
1215
- super(
1216
- `Reporting ${task} could not process ${failedCount} item(s)${failedIds.length ? `: ${failedIds.slice(0, 20).join(", ")}` : ""}`
1217
- );
1218
- this.task = task;
1219
- this.failedCount = failedCount;
1220
- this.failedIds = failedIds;
1221
- this.name = "ReportingMaintenancePartialFailureError";
1222
- }
1223
- task;
1224
- failedCount;
1225
- failedIds;
1226
- }
1239
+ import {
1240
+ ReportingMaintenancePartialFailureError
1241
+ } from "./maintenance.mjs";
1227
1242
  export {
1228
1243
  ReportingMaintenancePartialFailureError,
1229
1244
  createPostgresReliableReportingProductionService,
@@ -0,0 +1,91 @@
1
+ import type { PostgresReportingLedgerStore } from '../ledger/index.mjs';
2
+ /**
3
+ * Period-aligned retention. Whole periods are retired once
4
+ * `max(statusRetentionDays, recordRetentionDays)` has elapsed since both the
5
+ * period end and its latest publication. `statusRetentionDays` always comes
6
+ * from the service, so retention can never disagree with the commitment the
7
+ * service advertises to buyers.
8
+ */
9
+ export interface ReportingRetentionOptionsV1 {
10
+ enabled: true;
11
+ recordRetentionDays?: number;
12
+ limit?: number;
13
+ }
14
+ /** Change-feed pruning window and the consumer hold shared with retention. */
15
+ export type ReportingChangeFeedMaintenanceOptionsV1 = boolean | {
16
+ changeRetentionDays?: number;
17
+ maxFeedHoldDays?: number;
18
+ };
19
+ /** The ledger-store surface a maintenance pass drives. */
20
+ export type ReportingLedgerMaintenanceStoreV1 = Pick<PostgresReportingLedgerStore, 'sweepRowWriteIntents' | 'pruneChanges' | 'retireExpiredPeriods'>;
21
+ interface ReportingLedgerMaintenanceTaskOutcomesV1 {
22
+ 'snapshot sweep': Awaited<ReturnType<PostgresReportingLedgerStore['sweepExpiredState']>>;
23
+ 'row upload sweep': Awaited<ReturnType<PostgresReportingLedgerStore['sweepRowWriteIntents']>>;
24
+ 'change-feed pruning': Awaited<ReturnType<PostgresReportingLedgerStore['pruneChanges']>>;
25
+ retention: Awaited<ReturnType<PostgresReportingLedgerStore['retireExpiredPeriods']>>;
26
+ }
27
+ export type ReportingLedgerMaintenanceTaskNameV1 = keyof ReportingLedgerMaintenanceTaskOutcomesV1;
28
+ /**
29
+ * A scheduled maintenance pass completed but could not process some items
30
+ * (for example, a retention period whose objects could not be deleted). The
31
+ * pass resumes them next time; repeated reports need operator attention.
32
+ */
33
+ export declare class ReportingMaintenancePartialFailureError extends Error {
34
+ readonly task: string;
35
+ readonly failedCount: number;
36
+ readonly failedIds: readonly string[];
37
+ constructor(task: string, failedCount: number, failedIds: readonly string[]);
38
+ }
39
+ /**
40
+ * Outcome of one maintenance task. A task that resolved but could not process
41
+ * some items (a retention period whose objects would not delete, an upload that
42
+ * would not sweep) is `completed` with `partialFailure` set; the pass resumes
43
+ * those items next time.
44
+ */
45
+ export type ReportingLedgerMaintenanceTaskResultV1 = {
46
+ [Task in ReportingLedgerMaintenanceTaskNameV1]: {
47
+ readonly task: Task;
48
+ readonly status: 'completed';
49
+ readonly result: ReportingLedgerMaintenanceTaskOutcomesV1[Task];
50
+ readonly partialFailure?: ReportingMaintenancePartialFailureError;
51
+ } | {
52
+ readonly task: Task;
53
+ readonly status: 'failed';
54
+ readonly error: unknown;
55
+ };
56
+ }[ReportingLedgerMaintenanceTaskNameV1];
57
+ export interface RunReportingLedgerMaintenanceInputV1 {
58
+ store: ReportingLedgerMaintenanceStoreV1;
59
+ /**
60
+ * Expired cursor snapshot and checkpoint sweep. Omitted when the caller has
61
+ * no way to reach the database, in which case that task is not run.
62
+ */
63
+ sweepSnapshots?: (limit: number) => Promise<ReportingLedgerMaintenanceTaskOutcomesV1['snapshot sweep']>;
64
+ statusRetentionDays: number;
65
+ retention?: ReportingRetentionOptionsV1 | undefined;
66
+ changeFeed?: ReportingChangeFeedMaintenanceOptionsV1 | undefined;
67
+ signal?: AbortSignal | undefined;
68
+ /**
69
+ * Called, in task order and awaited, for every task that rejected or
70
+ * partially failed, unless `signal` has aborted (an abort is shutdown, not a
71
+ * fault).
72
+ */
73
+ report?: (error: unknown) => Promise<void>;
74
+ }
75
+ /**
76
+ * The single ledger-maintenance pass shared by the Core service and the
77
+ * production service, so partial-failure surfacing, the `maxFeedHoldDays` that
78
+ * retention honours, per-task limits and abort handling cannot drift between
79
+ * them. Tasks start together, in a fixed order, and are isolated: one task's
80
+ * failure never prevents another from running.
81
+ */
82
+ export declare function runReportingLedgerMaintenance(input: RunReportingLedgerMaintenanceInputV1): Promise<ReportingLedgerMaintenanceTaskResultV1[]>;
83
+ /**
84
+ * Fail fast on maintenance options the store would otherwise reject on every
85
+ * pass. Mirrors the checks `retireExpiredPeriods` and `pruneChanges` apply.
86
+ */
87
+ export declare function assertReportingLedgerMaintenanceOptions(input: {
88
+ retention?: ReportingRetentionOptionsV1 | undefined;
89
+ changeFeed?: ReportingChangeFeedMaintenanceOptionsV1 | undefined;
90
+ }, statusRetentionDays: number): void;
91
+ export {};
@@ -0,0 +1,91 @@
1
+ import type { PostgresReportingLedgerStore } from '../ledger';
2
+ /**
3
+ * Period-aligned retention. Whole periods are retired once
4
+ * `max(statusRetentionDays, recordRetentionDays)` has elapsed since both the
5
+ * period end and its latest publication. `statusRetentionDays` always comes
6
+ * from the service, so retention can never disagree with the commitment the
7
+ * service advertises to buyers.
8
+ */
9
+ export interface ReportingRetentionOptionsV1 {
10
+ enabled: true;
11
+ recordRetentionDays?: number;
12
+ limit?: number;
13
+ }
14
+ /** Change-feed pruning window and the consumer hold shared with retention. */
15
+ export type ReportingChangeFeedMaintenanceOptionsV1 = boolean | {
16
+ changeRetentionDays?: number;
17
+ maxFeedHoldDays?: number;
18
+ };
19
+ /** The ledger-store surface a maintenance pass drives. */
20
+ export type ReportingLedgerMaintenanceStoreV1 = Pick<PostgresReportingLedgerStore, 'sweepRowWriteIntents' | 'pruneChanges' | 'retireExpiredPeriods'>;
21
+ interface ReportingLedgerMaintenanceTaskOutcomesV1 {
22
+ 'snapshot sweep': Awaited<ReturnType<PostgresReportingLedgerStore['sweepExpiredState']>>;
23
+ 'row upload sweep': Awaited<ReturnType<PostgresReportingLedgerStore['sweepRowWriteIntents']>>;
24
+ 'change-feed pruning': Awaited<ReturnType<PostgresReportingLedgerStore['pruneChanges']>>;
25
+ retention: Awaited<ReturnType<PostgresReportingLedgerStore['retireExpiredPeriods']>>;
26
+ }
27
+ export type ReportingLedgerMaintenanceTaskNameV1 = keyof ReportingLedgerMaintenanceTaskOutcomesV1;
28
+ /**
29
+ * A scheduled maintenance pass completed but could not process some items
30
+ * (for example, a retention period whose objects could not be deleted). The
31
+ * pass resumes them next time; repeated reports need operator attention.
32
+ */
33
+ export declare class ReportingMaintenancePartialFailureError extends Error {
34
+ readonly task: string;
35
+ readonly failedCount: number;
36
+ readonly failedIds: readonly string[];
37
+ constructor(task: string, failedCount: number, failedIds: readonly string[]);
38
+ }
39
+ /**
40
+ * Outcome of one maintenance task. A task that resolved but could not process
41
+ * some items (a retention period whose objects would not delete, an upload that
42
+ * would not sweep) is `completed` with `partialFailure` set; the pass resumes
43
+ * those items next time.
44
+ */
45
+ export type ReportingLedgerMaintenanceTaskResultV1 = {
46
+ [Task in ReportingLedgerMaintenanceTaskNameV1]: {
47
+ readonly task: Task;
48
+ readonly status: 'completed';
49
+ readonly result: ReportingLedgerMaintenanceTaskOutcomesV1[Task];
50
+ readonly partialFailure?: ReportingMaintenancePartialFailureError;
51
+ } | {
52
+ readonly task: Task;
53
+ readonly status: 'failed';
54
+ readonly error: unknown;
55
+ };
56
+ }[ReportingLedgerMaintenanceTaskNameV1];
57
+ export interface RunReportingLedgerMaintenanceInputV1 {
58
+ store: ReportingLedgerMaintenanceStoreV1;
59
+ /**
60
+ * Expired cursor snapshot and checkpoint sweep. Omitted when the caller has
61
+ * no way to reach the database, in which case that task is not run.
62
+ */
63
+ sweepSnapshots?: (limit: number) => Promise<ReportingLedgerMaintenanceTaskOutcomesV1['snapshot sweep']>;
64
+ statusRetentionDays: number;
65
+ retention?: ReportingRetentionOptionsV1 | undefined;
66
+ changeFeed?: ReportingChangeFeedMaintenanceOptionsV1 | undefined;
67
+ signal?: AbortSignal | undefined;
68
+ /**
69
+ * Called, in task order and awaited, for every task that rejected or
70
+ * partially failed, unless `signal` has aborted (an abort is shutdown, not a
71
+ * fault).
72
+ */
73
+ report?: (error: unknown) => Promise<void>;
74
+ }
75
+ /**
76
+ * The single ledger-maintenance pass shared by the Core service and the
77
+ * production service, so partial-failure surfacing, the `maxFeedHoldDays` that
78
+ * retention honours, per-task limits and abort handling cannot drift between
79
+ * them. Tasks start together, in a fixed order, and are isolated: one task's
80
+ * failure never prevents another from running.
81
+ */
82
+ export declare function runReportingLedgerMaintenance(input: RunReportingLedgerMaintenanceInputV1): Promise<ReportingLedgerMaintenanceTaskResultV1[]>;
83
+ /**
84
+ * Fail fast on maintenance options the store would otherwise reject on every
85
+ * pass. Mirrors the checks `retireExpiredPeriods` and `pruneChanges` apply.
86
+ */
87
+ export declare function assertReportingLedgerMaintenanceOptions(input: {
88
+ retention?: ReportingRetentionOptionsV1 | undefined;
89
+ changeFeed?: ReportingChangeFeedMaintenanceOptionsV1 | undefined;
90
+ }, statusRetentionDays: number): void;
91
+ export {};
@@ -0,0 +1,140 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var maintenance_exports = {};
20
+ __export(maintenance_exports, {
21
+ ReportingMaintenancePartialFailureError: () => ReportingMaintenancePartialFailureError,
22
+ assertReportingLedgerMaintenanceOptions: () => assertReportingLedgerMaintenanceOptions,
23
+ runReportingLedgerMaintenance: () => runReportingLedgerMaintenance
24
+ });
25
+ module.exports = __toCommonJS(maintenance_exports);
26
+ class ReportingMaintenancePartialFailureError extends Error {
27
+ constructor(task, failedCount, failedIds) {
28
+ super(
29
+ `Reporting ${task} could not process ${failedCount} item(s)${failedIds.length ? `: ${failedIds.slice(0, 20).join(", ")}` : ""}`
30
+ );
31
+ this.task = task;
32
+ this.failedCount = failedCount;
33
+ this.failedIds = failedIds;
34
+ this.name = "ReportingMaintenancePartialFailureError";
35
+ }
36
+ task;
37
+ failedCount;
38
+ failedIds;
39
+ }
40
+ async function runReportingLedgerMaintenance(input) {
41
+ const { store, signal, changeFeed, retention } = input;
42
+ const start = (name, run) => {
43
+ try {
44
+ return [name, run()];
45
+ } catch (error) {
46
+ return [name, Promise.reject(error)];
47
+ }
48
+ };
49
+ const tasks = [
50
+ ...input.sweepSnapshots ? [start("snapshot sweep", () => input.sweepSnapshots(1e3))] : [],
51
+ start("row upload sweep", () => store.sweepRowWriteIntents({ limit: 100, ...signal ? { signal } : {} })),
52
+ ...changeFeed ? [
53
+ start(
54
+ "change-feed pruning",
55
+ () => store.pruneChanges(
56
+ typeof changeFeed === "object" ? {
57
+ ...changeFeed.changeRetentionDays !== void 0 ? { changeRetentionDays: changeFeed.changeRetentionDays } : {},
58
+ ...changeFeed.maxFeedHoldDays !== void 0 ? { maxFeedHoldDays: changeFeed.maxFeedHoldDays } : {}
59
+ } : {}
60
+ )
61
+ )
62
+ ] : [],
63
+ ...retention?.enabled ? [
64
+ start(
65
+ "retention",
66
+ () => store.retireExpiredPeriods({
67
+ statusRetentionDays: input.statusRetentionDays,
68
+ ...retention.recordRetentionDays !== void 0 ? { recordRetentionDays: retention.recordRetentionDays } : {},
69
+ ...typeof changeFeed === "object" && changeFeed.maxFeedHoldDays !== void 0 ? { maxFeedHoldDays: changeFeed.maxFeedHoldDays } : {},
70
+ limit: retention.limit ?? 100,
71
+ ...signal ? { signal } : {}
72
+ })
73
+ )
74
+ ] : []
75
+ ];
76
+ const settled = await Promise.allSettled(tasks.map(([, task]) => task));
77
+ const results = [];
78
+ for (const [index, outcome] of settled.entries()) {
79
+ const name = tasks[index][0];
80
+ if (outcome.status === "rejected") {
81
+ if (!signal?.aborted) await input.report?.(outcome.reason);
82
+ results.push({ task: name, status: "failed", error: outcome.reason });
83
+ continue;
84
+ }
85
+ const value = outcome.value;
86
+ const failed = Array.isArray(value?.failed) ? value.failed.length : typeof value?.failed === "number" ? value.failed : 0;
87
+ let partialFailure;
88
+ if (failed > 0) {
89
+ partialFailure = new ReportingMaintenancePartialFailureError(
90
+ name,
91
+ failed,
92
+ value?.failures?.length ? value.failures.map((entry) => `${entry.reporting_obligation_id} (${entry.cause})`) : Array.isArray(value?.failed) ? value.failed : []
93
+ );
94
+ if (!signal?.aborted) await input.report?.(partialFailure);
95
+ }
96
+ results.push({
97
+ task: name,
98
+ status: "completed",
99
+ result: outcome.value,
100
+ ...partialFailure ? { partialFailure } : {}
101
+ });
102
+ }
103
+ return results;
104
+ }
105
+ function assertReportingLedgerMaintenanceOptions(input, statusRetentionDays) {
106
+ const positive = (value, field) => {
107
+ if (!Number.isSafeInteger(value) || value <= 0) {
108
+ throw new TypeError(`${field} must be a positive safe integer`);
109
+ }
110
+ };
111
+ const { retention, changeFeed } = input;
112
+ if (retention !== void 0) {
113
+ if (retention === null || typeof retention !== "object" || retention.enabled !== true) {
114
+ throw new TypeError("maintenance.retention must be { enabled: true, ... }");
115
+ }
116
+ if (retention.recordRetentionDays !== void 0) {
117
+ positive(retention.recordRetentionDays, "maintenance.retention.recordRetentionDays");
118
+ if (retention.recordRetentionDays < statusRetentionDays) {
119
+ throw new RangeError(
120
+ "maintenance.retention.recordRetentionDays must be no shorter than statusRetentionDays, the retention advertised to buyers"
121
+ );
122
+ }
123
+ }
124
+ if (retention.limit !== void 0) positive(retention.limit, "maintenance.retention.limit");
125
+ }
126
+ if (typeof changeFeed === "object" && changeFeed !== null) {
127
+ if (changeFeed.changeRetentionDays !== void 0) {
128
+ positive(changeFeed.changeRetentionDays, "maintenance.changeFeed.changeRetentionDays");
129
+ }
130
+ if (changeFeed.maxFeedHoldDays !== void 0) {
131
+ positive(changeFeed.maxFeedHoldDays, "maintenance.changeFeed.maxFeedHoldDays");
132
+ }
133
+ }
134
+ }
135
+ // Annotate the CommonJS export names for ESM import in node:
136
+ 0 && (module.exports = {
137
+ ReportingMaintenancePartialFailureError,
138
+ assertReportingLedgerMaintenanceOptions,
139
+ runReportingLedgerMaintenance
140
+ });
@@ -0,0 +1,114 @@
1
+ class ReportingMaintenancePartialFailureError extends Error {
2
+ constructor(task, failedCount, failedIds) {
3
+ super(
4
+ `Reporting ${task} could not process ${failedCount} item(s)${failedIds.length ? `: ${failedIds.slice(0, 20).join(", ")}` : ""}`
5
+ );
6
+ this.task = task;
7
+ this.failedCount = failedCount;
8
+ this.failedIds = failedIds;
9
+ this.name = "ReportingMaintenancePartialFailureError";
10
+ }
11
+ task;
12
+ failedCount;
13
+ failedIds;
14
+ }
15
+ async function runReportingLedgerMaintenance(input) {
16
+ const { store, signal, changeFeed, retention } = input;
17
+ const start = (name, run) => {
18
+ try {
19
+ return [name, run()];
20
+ } catch (error) {
21
+ return [name, Promise.reject(error)];
22
+ }
23
+ };
24
+ const tasks = [
25
+ ...input.sweepSnapshots ? [start("snapshot sweep", () => input.sweepSnapshots(1e3))] : [],
26
+ start("row upload sweep", () => store.sweepRowWriteIntents({ limit: 100, ...signal ? { signal } : {} })),
27
+ ...changeFeed ? [
28
+ start(
29
+ "change-feed pruning",
30
+ () => store.pruneChanges(
31
+ typeof changeFeed === "object" ? {
32
+ ...changeFeed.changeRetentionDays !== void 0 ? { changeRetentionDays: changeFeed.changeRetentionDays } : {},
33
+ ...changeFeed.maxFeedHoldDays !== void 0 ? { maxFeedHoldDays: changeFeed.maxFeedHoldDays } : {}
34
+ } : {}
35
+ )
36
+ )
37
+ ] : [],
38
+ ...retention?.enabled ? [
39
+ start(
40
+ "retention",
41
+ () => store.retireExpiredPeriods({
42
+ statusRetentionDays: input.statusRetentionDays,
43
+ ...retention.recordRetentionDays !== void 0 ? { recordRetentionDays: retention.recordRetentionDays } : {},
44
+ ...typeof changeFeed === "object" && changeFeed.maxFeedHoldDays !== void 0 ? { maxFeedHoldDays: changeFeed.maxFeedHoldDays } : {},
45
+ limit: retention.limit ?? 100,
46
+ ...signal ? { signal } : {}
47
+ })
48
+ )
49
+ ] : []
50
+ ];
51
+ const settled = await Promise.allSettled(tasks.map(([, task]) => task));
52
+ const results = [];
53
+ for (const [index, outcome] of settled.entries()) {
54
+ const name = tasks[index][0];
55
+ if (outcome.status === "rejected") {
56
+ if (!signal?.aborted) await input.report?.(outcome.reason);
57
+ results.push({ task: name, status: "failed", error: outcome.reason });
58
+ continue;
59
+ }
60
+ const value = outcome.value;
61
+ const failed = Array.isArray(value?.failed) ? value.failed.length : typeof value?.failed === "number" ? value.failed : 0;
62
+ let partialFailure;
63
+ if (failed > 0) {
64
+ partialFailure = new ReportingMaintenancePartialFailureError(
65
+ name,
66
+ failed,
67
+ value?.failures?.length ? value.failures.map((entry) => `${entry.reporting_obligation_id} (${entry.cause})`) : Array.isArray(value?.failed) ? value.failed : []
68
+ );
69
+ if (!signal?.aborted) await input.report?.(partialFailure);
70
+ }
71
+ results.push({
72
+ task: name,
73
+ status: "completed",
74
+ result: outcome.value,
75
+ ...partialFailure ? { partialFailure } : {}
76
+ });
77
+ }
78
+ return results;
79
+ }
80
+ function assertReportingLedgerMaintenanceOptions(input, statusRetentionDays) {
81
+ const positive = (value, field) => {
82
+ if (!Number.isSafeInteger(value) || value <= 0) {
83
+ throw new TypeError(`${field} must be a positive safe integer`);
84
+ }
85
+ };
86
+ const { retention, changeFeed } = input;
87
+ if (retention !== void 0) {
88
+ if (retention === null || typeof retention !== "object" || retention.enabled !== true) {
89
+ throw new TypeError("maintenance.retention must be { enabled: true, ... }");
90
+ }
91
+ if (retention.recordRetentionDays !== void 0) {
92
+ positive(retention.recordRetentionDays, "maintenance.retention.recordRetentionDays");
93
+ if (retention.recordRetentionDays < statusRetentionDays) {
94
+ throw new RangeError(
95
+ "maintenance.retention.recordRetentionDays must be no shorter than statusRetentionDays, the retention advertised to buyers"
96
+ );
97
+ }
98
+ }
99
+ if (retention.limit !== void 0) positive(retention.limit, "maintenance.retention.limit");
100
+ }
101
+ if (typeof changeFeed === "object" && changeFeed !== null) {
102
+ if (changeFeed.changeRetentionDays !== void 0) {
103
+ positive(changeFeed.changeRetentionDays, "maintenance.changeFeed.changeRetentionDays");
104
+ }
105
+ if (changeFeed.maxFeedHoldDays !== void 0) {
106
+ positive(changeFeed.maxFeedHoldDays, "maintenance.changeFeed.maxFeedHoldDays");
107
+ }
108
+ }
109
+ }
110
+ export {
111
+ ReportingMaintenancePartialFailureError,
112
+ assertReportingLedgerMaintenanceOptions,
113
+ runReportingLedgerMaintenance
114
+ };
@@ -4,5 +4,5 @@
4
4
  "source_sha": "4e553ad955f83b49c7d221ab5c3ff78237ad02e3",
5
5
  "source_tarball_sha256": "580656d6466ef9f0d1119985e6726c2efea718dc671e2ad30957fcb2fd54af0f",
6
6
  "upstream_adcp_version": "2.5.3",
7
- "synced_at": "2026-10-09T12:16:02.843Z"
7
+ "synced_at": "2026-10-10T13:31:54.379Z"
8
8
  }
@@ -197,7 +197,7 @@ export interface UpstreamTrafficQueryResult {
197
197
  */
198
198
  export declare function runValidations(validations: StoryboardValidation[], context: ValidationContext): ValidationResult[];
199
199
  /** Capability semver emitted in run summaries and used for advisory expiry. */
200
- export declare const RUNNER_CAPABILITY_VERSION = "15.6.0";
200
+ export declare const RUNNER_CAPABILITY_VERSION = "15.7.1";
201
201
  /** True when a failed validation contributes to the owning step's grade. */
202
202
  export declare function validationFailsStep(result: ValidationResult): boolean;
203
203
  /**
@@ -197,7 +197,7 @@ export interface UpstreamTrafficQueryResult {
197
197
  */
198
198
  export declare function runValidations(validations: StoryboardValidation[], context: ValidationContext): ValidationResult[];
199
199
  /** Capability semver emitted in run summaries and used for advisory expiry. */
200
- export declare const RUNNER_CAPABILITY_VERSION = "15.6.0";
200
+ export declare const RUNNER_CAPABILITY_VERSION = "15.7.1";
201
201
  /** True when a failed validation contributes to the owning step's grade. */
202
202
  export declare function validationFailsStep(result: ValidationResult): boolean;
203
203
  /**