@adcp/sdk 15.5.0 → 15.7.0

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 (81) hide show
  1. package/README.md +15 -0
  2. package/dist/lib/auth/oauth/index.d.mts +1 -0
  3. package/dist/lib/auth/oauth/index.d.ts +1 -0
  4. package/dist/lib/auth/oauth/index.js +5 -0
  5. package/dist/lib/auth/oauth/index.mjs +3 -0
  6. package/dist/lib/auth/oauth/pending-web-flow.d.mts +16 -0
  7. package/dist/lib/auth/oauth/pending-web-flow.d.ts +16 -0
  8. package/dist/lib/auth/oauth/pending-web-flow.js +66 -0
  9. package/dist/lib/auth/oauth/pending-web-flow.mjs +41 -0
  10. package/dist/lib/auth/oauth/web-flow.js +3 -3
  11. package/dist/lib/auth/oauth/web-flow.mjs +3 -3
  12. package/dist/lib/registry/types.generated.d.mts +63 -1
  13. package/dist/lib/registry/types.generated.d.ts +63 -1
  14. package/dist/lib/reporting/azure/index.d.mts +26 -0
  15. package/dist/lib/reporting/azure/index.d.ts +26 -0
  16. package/dist/lib/reporting/azure/index.js +247 -0
  17. package/dist/lib/reporting/azure/index.mjs +230 -0
  18. package/dist/lib/reporting/bigquery/index.d.mts +127 -0
  19. package/dist/lib/reporting/bigquery/index.d.ts +127 -0
  20. package/dist/lib/reporting/bigquery/index.js +379 -0
  21. package/dist/lib/reporting/bigquery/index.mjs +341 -0
  22. package/dist/lib/reporting/ledger/handler.js +20 -8
  23. package/dist/lib/reporting/ledger/handler.mjs +20 -8
  24. package/dist/lib/reporting/ledger/postgres.d.mts +21 -0
  25. package/dist/lib/reporting/ledger/postgres.d.ts +21 -0
  26. package/dist/lib/reporting/ledger/postgres.js +35 -2
  27. package/dist/lib/reporting/ledger/postgres.mjs +35 -2
  28. package/dist/lib/reporting/ledger/row-storage-object.js +31 -8
  29. package/dist/lib/reporting/ledger/row-storage-object.mjs +32 -9
  30. package/dist/lib/reporting/ledger/row-storage-provider-io.d.mts +0 -7
  31. package/dist/lib/reporting/ledger/row-storage-provider-io.d.ts +0 -7
  32. package/dist/lib/reporting/ledger/row-storage-provider-io.js +0 -10
  33. package/dist/lib/reporting/ledger/row-storage-provider-io.mjs +0 -8
  34. package/dist/lib/reporting/s3/index.d.mts +29 -0
  35. package/dist/lib/reporting/s3/index.d.ts +29 -0
  36. package/dist/lib/reporting/s3/index.js +324 -0
  37. package/dist/lib/reporting/s3/index.mjs +317 -0
  38. package/dist/lib/reporting/service/index.d.mts +39 -22
  39. package/dist/lib/reporting/service/index.d.ts +39 -22
  40. package/dist/lib/reporting/service/index.js +75 -64
  41. package/dist/lib/reporting/service/index.mjs +79 -63
  42. package/dist/lib/reporting/service/maintenance.d.mts +91 -0
  43. package/dist/lib/reporting/service/maintenance.d.ts +91 -0
  44. package/dist/lib/reporting/service/maintenance.js +140 -0
  45. package/dist/lib/reporting/service/maintenance.mjs +114 -0
  46. package/dist/lib/schemas/browser.d.mts +14 -0
  47. package/dist/lib/schemas/browser.d.ts +14 -0
  48. package/dist/lib/schemas/browser.js +48 -0
  49. package/dist/lib/schemas/browser.mjs +20 -0
  50. package/dist/lib/schemas/index.d.mts +2 -120
  51. package/dist/lib/schemas/index.d.ts +2 -120
  52. package/dist/lib/schemas/index.js +3 -11
  53. package/dist/lib/schemas/index.mjs +1 -9
  54. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  55. package/dist/lib/testing/index.d.mts +1 -0
  56. package/dist/lib/testing/index.d.ts +1 -0
  57. package/dist/lib/testing/index.js +3 -0
  58. package/dist/lib/testing/index.mjs +2 -0
  59. package/dist/lib/testing/pending-web-flow-store.d.mts +20 -0
  60. package/dist/lib/testing/pending-web-flow-store.d.ts +20 -0
  61. package/dist/lib/testing/pending-web-flow-store.js +132 -0
  62. package/dist/lib/testing/pending-web-flow-store.mjs +98 -0
  63. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  64. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  65. package/dist/lib/validation/get-products-request.d.mts +122 -0
  66. package/dist/lib/validation/get-products-request.d.ts +122 -0
  67. package/dist/lib/validation/get-products-request.js +39 -0
  68. package/dist/lib/validation/get-products-request.mjs +14 -0
  69. package/dist/lib/version.d.mts +3 -3
  70. package/dist/lib/version.d.ts +3 -3
  71. package/dist/lib/version.js +3 -3
  72. package/dist/lib/version.mjs +3 -3
  73. package/docs/TYPE-SUMMARY.md +1 -1
  74. package/docs/ZOD-SCHEMAS.md +13 -1
  75. package/docs/guides/OAUTH-ISSUER-BINDING.md +6 -0
  76. package/docs/guides/REPORTING-LEDGER.md +254 -0
  77. package/docs/guides/REPORTING-OPERATIONS.md +82 -1
  78. package/docs/guides/WEB-OAUTH.md +48 -1
  79. package/docs/llms.txt +1 -1
  80. package/docs/migration-14.x-rc-worksheet.md +6 -4
  81. package/package.json +64 -1
@@ -6,6 +6,7 @@ import type { ReportingDeliveryCapabilities, ReportingDeliveryOffering } from '.
6
6
  import { type CreatePostgresPersistentNotificationRuntimeOptions, type PostgresPersistentNotificationRuntime } from '../../server/notification-subscriptions/index.mjs';
7
7
  import { REPORTING_LEDGER_MIGRATION, PostgresReportingLedgerStore, PostgresReportingManagedDeliveryStore, type CreateReportingProducerOptionsV1, type ReportingConsumerMismatchEscalationV1, type ReportingLedgerConfigurationV1, type ReportingLedgerStore, type ReportingProducerContactV1, type ReportingProducerV1, type ReportingManagedDeliveryAdapterV1, type ReportingManagedDeliveryRuntimeV1, type PostgresReportingManagedDeliveryStoreOptions, type PostgresReportingNotificationActivityOptions, type PostgresReportingNotificationActivityRuntime, type ReportingPgPool, type ReportingRowStorageOptionsV1, type ReportingSourceWithReaderV1 } from '../ledger/index.mjs';
8
8
  import { type PostgresReportingWebhookActivityV1 } from '../webhook-activity.mjs';
9
+ import { type ReportingChangeFeedMaintenanceOptionsV1, type ReportingLedgerMaintenanceTaskResultV1, type ReportingRetentionOptionsV1 } from './maintenance.mjs';
9
10
  import type { InlineReportingReplayRetentionV1 } from '../source/index.mjs';
10
11
  import { type InlineReportingDeliveryFetchV1, type ReportingSourceOfferingV1 } from '../source/index.mjs';
11
12
  /** The intentionally small provider boundary: one bounded slice fetch plus immutable metadata. */
@@ -149,6 +150,29 @@ export interface CreateReliableReportingServiceOptionsV1<TCtxMeta = Record<strin
149
150
  /** Ledger metadata retention commitment advertised to buyers. */
150
151
  statusRetentionDays: number;
151
152
  subscribers?: CreateReportingProducerOptionsV1['subscribers'];
153
+ /**
154
+ * Opt in to scheduled ledger maintenance for a Core-only deployment on a
155
+ * `PostgresReportingLedgerStore`. `start()` runs it at most once per
156
+ * `intervalMilliseconds`; `runMaintenance()` runs it on demand for a host
157
+ * with its own scheduler. The production service does this itself and does
158
+ * not take this option.
159
+ *
160
+ * Always swept: expired cursor snapshots and checkpoints, and abandoned row
161
+ * uploads. `retention` and `changeFeed` have the same shape and meaning as
162
+ * on `createPostgresReliableReportingProductionService`. Retention uses
163
+ * this service's `statusRetentionDays`, so it can never retire a period
164
+ * earlier than the retention advertised to buyers. `changeFeed` requires the
165
+ * store to be built with `changeFeed: true`.
166
+ */
167
+ maintenance?: ReliableReportingMaintenanceOptionsV1;
168
+ }
169
+ export interface ReliableReportingMaintenanceOptionsV1 {
170
+ /** Minimum time between scheduled maintenance passes. Defaults to 300000 (5 minutes). */
171
+ intervalMilliseconds?: number;
172
+ /** Period-aligned retention; see the production service's `retention`. */
173
+ retention?: ReportingRetentionOptionsV1;
174
+ /** Change-feed pruning and consumer hold; see the production service's `changeFeed`. */
175
+ changeFeed?: ReportingChangeFeedMaintenanceOptionsV1;
152
176
  }
153
177
  export interface ReliableReportingSetupV1 {
154
178
  readonly component: 'reliable-reporting-core';
@@ -179,6 +203,14 @@ export interface ReliableReportingServiceV1<TCtxMeta = Record<string, unknown>>
179
203
  notReady: number;
180
204
  failed: number;
181
205
  }>;
206
+ /**
207
+ * Run one ledger-maintenance pass now and return each task's outcome. For
208
+ * hosts that run their own scheduler instead of `start()`. A task failure is
209
+ * reported in its result, not thrown. Requires the `maintenance` option.
210
+ */
211
+ runMaintenance(options?: {
212
+ signal?: AbortSignal;
213
+ }): Promise<ReportingLedgerMaintenanceTaskResultV1[]>;
182
214
  start(options: ReliableReportingSchedulerOptionsV1): void;
183
215
  stop(): Promise<void>;
184
216
  }
@@ -188,7 +220,7 @@ export interface ReliableReportingServiceV1<TCtxMeta = Record<string, unknown>>
188
220
  */
189
221
  export declare function createReliableReportingService<TCtxMeta = Record<string, unknown>>(options: CreateReliableReportingServiceOptionsV1<TCtxMeta>): ReliableReportingServiceV1<TCtxMeta>;
190
222
  type ProductionNotificationOptions = Omit<CreatePostgresPersistentNotificationRuntimeOptions, 'db' | 'publisherScope' | 'checkpointDeliveryAttempt' | 'supportedAccountEventTypes'>;
191
- export type CreatePostgresReliableReportingProductionServiceOptionsV1<TCtxMeta = Record<string, unknown>> = Omit<CreateReliableReportingServiceOptionsV1<TCtxMeta>, 'store' | 'subscribers'> & {
223
+ export type CreatePostgresReliableReportingProductionServiceOptionsV1<TCtxMeta = Record<string, unknown>> = Omit<CreateReliableReportingServiceOptionsV1<TCtxMeta>, 'store' | 'subscribers' | 'maintenance'> & {
192
224
  db: ReportingPgPool;
193
225
  /** Stable, non-secret deployment namespace shared by reporting workers. */
194
226
  namespace: string;
@@ -207,13 +239,11 @@ export type CreatePostgresReliableReportingProductionServiceOptionsV1<TCtxMeta =
207
239
  * periods once `max(statusRetentionDays, recordRetentionDays)` has elapsed
208
240
  * since both the period end and its latest publication, deleting their
209
241
  * rows and ledger records and leaving a tombstone. Abandoned row uploads
210
- * and expired cursor snapshots are always swept.
242
+ * and expired cursor snapshots are always swept. With `changeFeed`, a
243
+ * period is also held until every live registered feed consumer (updated
244
+ * within `changeFeed.maxFeedHoldDays`, default 7) has read its changes.
211
245
  */
212
- retention?: {
213
- enabled: true;
214
- recordRetentionDays?: number;
215
- limit?: number;
216
- };
246
+ retention?: ReportingRetentionOptionsV1;
217
247
  /**
218
248
  * Maintain the host change feed (`changesAfter`, `getCurrentRevision`,
219
249
  * `listCurrentRevisions` on `stores.core`). Adds
@@ -221,10 +251,7 @@ export type CreatePostgresReliableReportingProductionServiceOptionsV1<TCtxMeta =
221
251
  * changes older than `changeRetentionDays` (default 30) unless a registered
222
252
  * consumer updated within `maxFeedHoldDays` (default 7) still needs them.
223
253
  */
224
- changeFeed?: boolean | {
225
- changeRetentionDays?: number;
226
- maxFeedHoldDays?: number;
227
- };
254
+ changeFeed?: ReportingChangeFeedMaintenanceOptionsV1;
228
255
  notifications: ProductionNotificationOptions;
229
256
  managedDelivery: {
230
257
  adapter: ReportingManagedDeliveryAdapterV1;
@@ -302,14 +329,4 @@ export interface PostgresReliableReportingProductionServiceV1<TCtxMeta = Record<
302
329
  */
303
330
  export declare function createPostgresReliableReportingProductionService<TCtxMeta = Record<string, unknown>>(options: CreatePostgresReliableReportingProductionServiceOptionsV1<TCtxMeta>): Promise<PostgresReliableReportingProductionServiceV1<TCtxMeta>>;
304
331
  export * from './conformance.mjs';
305
- /**
306
- * A scheduled maintenance pass completed but could not process some items
307
- * (for example, a retention period whose objects could not be deleted). The
308
- * pass resumes them next time; repeated reports need operator attention.
309
- */
310
- export declare class ReportingMaintenancePartialFailureError extends Error {
311
- readonly task: string;
312
- readonly failedCount: number;
313
- readonly failedIds: readonly string[];
314
- constructor(task: string, failedCount: number, failedIds: readonly string[]);
315
- }
332
+ export { ReportingMaintenancePartialFailureError, type ReportingChangeFeedMaintenanceOptionsV1, type ReportingLedgerMaintenanceStoreV1, type ReportingLedgerMaintenanceTaskNameV1, type ReportingLedgerMaintenanceTaskResultV1, type ReportingRetentionOptionsV1, } from './maintenance.mjs';
@@ -6,6 +6,7 @@ import type { ReportingDeliveryCapabilities, ReportingDeliveryOffering } from '.
6
6
  import { type CreatePostgresPersistentNotificationRuntimeOptions, type PostgresPersistentNotificationRuntime } from '../../server/notification-subscriptions';
7
7
  import { REPORTING_LEDGER_MIGRATION, PostgresReportingLedgerStore, PostgresReportingManagedDeliveryStore, type CreateReportingProducerOptionsV1, type ReportingConsumerMismatchEscalationV1, type ReportingLedgerConfigurationV1, type ReportingLedgerStore, type ReportingProducerContactV1, type ReportingProducerV1, type ReportingManagedDeliveryAdapterV1, type ReportingManagedDeliveryRuntimeV1, type PostgresReportingManagedDeliveryStoreOptions, type PostgresReportingNotificationActivityOptions, type PostgresReportingNotificationActivityRuntime, type ReportingPgPool, type ReportingRowStorageOptionsV1, type ReportingSourceWithReaderV1 } from '../ledger';
8
8
  import { type PostgresReportingWebhookActivityV1 } from '../webhook-activity';
9
+ import { type ReportingChangeFeedMaintenanceOptionsV1, type ReportingLedgerMaintenanceTaskResultV1, type ReportingRetentionOptionsV1 } from './maintenance';
9
10
  import type { InlineReportingReplayRetentionV1 } from '../source';
10
11
  import { type InlineReportingDeliveryFetchV1, type ReportingSourceOfferingV1 } from '../source';
11
12
  /** The intentionally small provider boundary: one bounded slice fetch plus immutable metadata. */
@@ -149,6 +150,29 @@ export interface CreateReliableReportingServiceOptionsV1<TCtxMeta = Record<strin
149
150
  /** Ledger metadata retention commitment advertised to buyers. */
150
151
  statusRetentionDays: number;
151
152
  subscribers?: CreateReportingProducerOptionsV1['subscribers'];
153
+ /**
154
+ * Opt in to scheduled ledger maintenance for a Core-only deployment on a
155
+ * `PostgresReportingLedgerStore`. `start()` runs it at most once per
156
+ * `intervalMilliseconds`; `runMaintenance()` runs it on demand for a host
157
+ * with its own scheduler. The production service does this itself and does
158
+ * not take this option.
159
+ *
160
+ * Always swept: expired cursor snapshots and checkpoints, and abandoned row
161
+ * uploads. `retention` and `changeFeed` have the same shape and meaning as
162
+ * on `createPostgresReliableReportingProductionService`. Retention uses
163
+ * this service's `statusRetentionDays`, so it can never retire a period
164
+ * earlier than the retention advertised to buyers. `changeFeed` requires the
165
+ * store to be built with `changeFeed: true`.
166
+ */
167
+ maintenance?: ReliableReportingMaintenanceOptionsV1;
168
+ }
169
+ export interface ReliableReportingMaintenanceOptionsV1 {
170
+ /** Minimum time between scheduled maintenance passes. Defaults to 300000 (5 minutes). */
171
+ intervalMilliseconds?: number;
172
+ /** Period-aligned retention; see the production service's `retention`. */
173
+ retention?: ReportingRetentionOptionsV1;
174
+ /** Change-feed pruning and consumer hold; see the production service's `changeFeed`. */
175
+ changeFeed?: ReportingChangeFeedMaintenanceOptionsV1;
152
176
  }
153
177
  export interface ReliableReportingSetupV1 {
154
178
  readonly component: 'reliable-reporting-core';
@@ -179,6 +203,14 @@ export interface ReliableReportingServiceV1<TCtxMeta = Record<string, unknown>>
179
203
  notReady: number;
180
204
  failed: number;
181
205
  }>;
206
+ /**
207
+ * Run one ledger-maintenance pass now and return each task's outcome. For
208
+ * hosts that run their own scheduler instead of `start()`. A task failure is
209
+ * reported in its result, not thrown. Requires the `maintenance` option.
210
+ */
211
+ runMaintenance(options?: {
212
+ signal?: AbortSignal;
213
+ }): Promise<ReportingLedgerMaintenanceTaskResultV1[]>;
182
214
  start(options: ReliableReportingSchedulerOptionsV1): void;
183
215
  stop(): Promise<void>;
184
216
  }
@@ -188,7 +220,7 @@ export interface ReliableReportingServiceV1<TCtxMeta = Record<string, unknown>>
188
220
  */
189
221
  export declare function createReliableReportingService<TCtxMeta = Record<string, unknown>>(options: CreateReliableReportingServiceOptionsV1<TCtxMeta>): ReliableReportingServiceV1<TCtxMeta>;
190
222
  type ProductionNotificationOptions = Omit<CreatePostgresPersistentNotificationRuntimeOptions, 'db' | 'publisherScope' | 'checkpointDeliveryAttempt' | 'supportedAccountEventTypes'>;
191
- export type CreatePostgresReliableReportingProductionServiceOptionsV1<TCtxMeta = Record<string, unknown>> = Omit<CreateReliableReportingServiceOptionsV1<TCtxMeta>, 'store' | 'subscribers'> & {
223
+ export type CreatePostgresReliableReportingProductionServiceOptionsV1<TCtxMeta = Record<string, unknown>> = Omit<CreateReliableReportingServiceOptionsV1<TCtxMeta>, 'store' | 'subscribers' | 'maintenance'> & {
192
224
  db: ReportingPgPool;
193
225
  /** Stable, non-secret deployment namespace shared by reporting workers. */
194
226
  namespace: string;
@@ -207,13 +239,11 @@ export type CreatePostgresReliableReportingProductionServiceOptionsV1<TCtxMeta =
207
239
  * periods once `max(statusRetentionDays, recordRetentionDays)` has elapsed
208
240
  * since both the period end and its latest publication, deleting their
209
241
  * rows and ledger records and leaving a tombstone. Abandoned row uploads
210
- * and expired cursor snapshots are always swept.
242
+ * and expired cursor snapshots are always swept. With `changeFeed`, a
243
+ * period is also held until every live registered feed consumer (updated
244
+ * within `changeFeed.maxFeedHoldDays`, default 7) has read its changes.
211
245
  */
212
- retention?: {
213
- enabled: true;
214
- recordRetentionDays?: number;
215
- limit?: number;
216
- };
246
+ retention?: ReportingRetentionOptionsV1;
217
247
  /**
218
248
  * Maintain the host change feed (`changesAfter`, `getCurrentRevision`,
219
249
  * `listCurrentRevisions` on `stores.core`). Adds
@@ -221,10 +251,7 @@ export type CreatePostgresReliableReportingProductionServiceOptionsV1<TCtxMeta =
221
251
  * changes older than `changeRetentionDays` (default 30) unless a registered
222
252
  * consumer updated within `maxFeedHoldDays` (default 7) still needs them.
223
253
  */
224
- changeFeed?: boolean | {
225
- changeRetentionDays?: number;
226
- maxFeedHoldDays?: number;
227
- };
254
+ changeFeed?: ReportingChangeFeedMaintenanceOptionsV1;
228
255
  notifications: ProductionNotificationOptions;
229
256
  managedDelivery: {
230
257
  adapter: ReportingManagedDeliveryAdapterV1;
@@ -302,14 +329,4 @@ export interface PostgresReliableReportingProductionServiceV1<TCtxMeta = Record<
302
329
  */
303
330
  export declare function createPostgresReliableReportingProductionService<TCtxMeta = Record<string, unknown>>(options: CreatePostgresReliableReportingProductionServiceOptionsV1<TCtxMeta>): Promise<PostgresReliableReportingProductionServiceV1<TCtxMeta>>;
304
331
  export * from './conformance';
305
- /**
306
- * A scheduled maintenance pass completed but could not process some items
307
- * (for example, a retention period whose objects could not be deleted). The
308
- * pass resumes them next time; repeated reports need operator attention.
309
- */
310
- export declare class ReportingMaintenancePartialFailureError extends Error {
311
- readonly task: string;
312
- readonly failedCount: number;
313
- readonly failedIds: readonly string[];
314
- constructor(task: string, failedCount: number, failedIds: readonly string[]);
315
- }
332
+ export { ReportingMaintenancePartialFailureError, type ReportingChangeFeedMaintenanceOptionsV1, type ReportingLedgerMaintenanceStoreV1, type ReportingLedgerMaintenanceTaskNameV1, type ReportingLedgerMaintenanceTaskResultV1, type ReportingRetentionOptionsV1, } from './maintenance';
@@ -19,7 +19,7 @@ var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "defau
19
19
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
20
  var service_exports = {};
21
21
  __export(service_exports, {
22
- ReportingMaintenancePartialFailureError: () => ReportingMaintenancePartialFailureError,
22
+ ReportingMaintenancePartialFailureError: () => import_maintenance2.ReportingMaintenancePartialFailureError,
23
23
  createPostgresReliableReportingProductionService: () => createPostgresReliableReportingProductionService,
24
24
  createReliableReportingService: () => createReliableReportingService
25
25
  });
@@ -31,11 +31,14 @@ var import_schemas = require('../../types/schemas.generated.js');
31
31
  var import_notification_subscriptions = require('../../server/notification-subscriptions/index.js');
32
32
  var import_ledger = require('../ledger/index.js');
33
33
  var import_webhook_activity = require('../webhook-activity.js');
34
+ var import_maintenance = require('./maintenance.js');
34
35
  var import_schedule = require('../ledger/schedule.js');
35
36
  var import_source = require('../source/index.js');
36
37
  __reExport(service_exports, require('./conformance.js'), module.exports);
38
+ var import_maintenance2 = require('./maintenance.js');
37
39
  const ADAPTER_SCOPE_KEY = "_adcp_reporting_adapter";
38
40
  const PRODUCTION_MANAGED_COMPOSITION = /* @__PURE__ */ Symbol("production-managed-reporting-composition");
41
+ const DEFAULT_MAINTENANCE_INTERVAL_MILLISECONDS = 3e5;
39
42
  function createReliableReportingService(options) {
40
43
  const allowManagedDelivery = options[PRODUCTION_MANAGED_COMPOSITION] === true;
41
44
  const adapterEntries = Object.entries(options.adapters);
@@ -97,6 +100,42 @@ function createReliableReportingService(options) {
97
100
  if (hasConsumerStatus) assertConsumerStatusStore(options.store);
98
101
  const retentionDays = options.statusRetentionDays;
99
102
  positiveInteger(retentionDays, "statusRetentionDays");
103
+ const maintenance = options.maintenance;
104
+ let maintenanceStore;
105
+ let maintenanceIntervalMilliseconds = DEFAULT_MAINTENANCE_INTERVAL_MILLISECONDS;
106
+ if (maintenance !== void 0) {
107
+ if (maintenance === null || typeof maintenance !== "object") {
108
+ throw new TypeError("maintenance must be an options object");
109
+ }
110
+ const candidate = options.store;
111
+ for (const method of ["retireExpiredPeriods", "pruneChanges", "sweepRowWriteIntents"]) {
112
+ if (typeof candidate[method] !== "function") {
113
+ throw new TypeError(
114
+ `maintenance requires a PostgresReportingLedgerStore (or a store with ${method}); the configured store cannot run ledger maintenance`
115
+ );
116
+ }
117
+ }
118
+ maintenanceStore = candidate;
119
+ if (maintenance.changeFeed && maintenanceStore.changeFeedEnabled === false) {
120
+ throw new TypeError(
121
+ "maintenance.changeFeed requires the store to be built with changeFeed: true; otherwise pruning silently does nothing"
122
+ );
123
+ }
124
+ if (maintenance.intervalMilliseconds !== void 0) {
125
+ positiveInteger(maintenance.intervalMilliseconds, "maintenance.intervalMilliseconds");
126
+ maintenanceIntervalMilliseconds = maintenance.intervalMilliseconds;
127
+ }
128
+ (0, import_maintenance.assertReportingLedgerMaintenanceOptions)(maintenance, retentionDays);
129
+ }
130
+ const runLedgerMaintenance = (signal, report) => (0, import_maintenance.runReportingLedgerMaintenance)({
131
+ store: maintenanceStore,
132
+ ...typeof maintenanceStore.sweepExpiredState === "function" ? { sweepSnapshots: (limit) => maintenanceStore.sweepExpiredState(limit) } : {},
133
+ statusRetentionDays: retentionDays,
134
+ retention: maintenance.retention,
135
+ changeFeed: maintenance.changeFeed,
136
+ signal,
137
+ ...report ? { report } : {}
138
+ });
100
139
  const capabilities = deepFreeze({
101
140
  supported: true,
102
141
  reliable_reporting_version: "1.0",
@@ -269,12 +308,26 @@ function createReliableReportingService(options) {
269
308
  });
270
309
  return { planned: planned.length, ...result };
271
310
  },
311
+ async runMaintenance(maintenanceOptions) {
312
+ if (maintenance === void 0) {
313
+ throw new TypeError("runMaintenance requires the maintenance option");
314
+ }
315
+ maintenanceOptions?.signal?.throwIfAborted();
316
+ return runLedgerMaintenance(maintenanceOptions?.signal);
317
+ },
272
318
  start(scheduler) {
273
319
  if (schedulerPromise) throw new Error("Reliable reporting scheduler is already running");
274
320
  positiveInteger(scheduler.intervalMilliseconds, "intervalMilliseconds");
275
321
  schedulerAbort = new AbortController();
276
322
  const signal = schedulerAbort.signal;
277
- schedulerPromise = schedulerLoop(service, scheduler, signal, options.store).finally(() => {
323
+ const scheduledMaintenance = maintenance === void 0 ? void 0 : {
324
+ intervalMilliseconds: maintenanceIntervalMilliseconds,
325
+ run: () => runLedgerMaintenance(
326
+ signal,
327
+ (error) => reportSchedulerError(scheduler, signal, error, { phase: "maintenance" })
328
+ )
329
+ };
330
+ schedulerPromise = schedulerLoop(service, scheduler, signal, options.store, scheduledMaintenance).finally(() => {
278
331
  schedulerPromise = void 0;
279
332
  schedulerAbort = void 0;
280
333
  });
@@ -506,52 +559,15 @@ async function createPostgresReliableReportingProductionService(options) {
506
559
  for (const result of pruneResults) {
507
560
  if (result.status === "rejected" && !signal.aborted) await reportAuxiliaryError(scheduler, result.reason);
508
561
  }
509
- const ledgerMaintenance = [
510
- ["snapshot sweep", (0, import_ledger.sweepExpiredReportingLedgerState)(options.db, 1e3)],
511
- ["row upload sweep", coreStore.sweepRowWriteIntents({ limit: 100, signal })],
512
- ...options.changeFeed ? [
513
- [
514
- "change-feed pruning",
515
- coreStore.pruneChanges(
516
- typeof options.changeFeed === "object" ? {
517
- ...options.changeFeed.changeRetentionDays !== void 0 ? { changeRetentionDays: options.changeFeed.changeRetentionDays } : {},
518
- ...options.changeFeed.maxFeedHoldDays !== void 0 ? { maxFeedHoldDays: options.changeFeed.maxFeedHoldDays } : {}
519
- } : {}
520
- )
521
- ]
522
- ] : [],
523
- ...options.retention?.enabled ? [
524
- [
525
- "retention",
526
- coreStore.retireExpiredPeriods({
527
- statusRetentionDays: options.statusRetentionDays,
528
- ...options.retention.recordRetentionDays !== void 0 ? { recordRetentionDays: options.retention.recordRetentionDays } : {},
529
- limit: options.retention.limit ?? 100,
530
- signal
531
- })
532
- ]
533
- ] : []
534
- ];
535
- const maintenanceResults = await Promise.allSettled(ledgerMaintenance.map(([, task]) => task));
536
- for (const [index, result] of maintenanceResults.entries()) {
537
- const name = ledgerMaintenance[index][0];
538
- if (result.status === "rejected") {
539
- if (!signal.aborted) await reportAuxiliaryError(scheduler, result.reason);
540
- continue;
541
- }
542
- const value = result.value;
543
- const failed = Array.isArray(value?.failed) ? value.failed.length : typeof value?.failed === "number" ? value.failed : 0;
544
- if (failed > 0 && !signal.aborted) {
545
- await reportAuxiliaryError(
546
- scheduler,
547
- new ReportingMaintenancePartialFailureError(
548
- name,
549
- failed,
550
- value?.failures?.length ? value.failures.map((entry) => `${entry.reporting_obligation_id} (${entry.cause})`) : Array.isArray(value?.failed) ? value.failed : []
551
- )
552
- );
553
- }
554
- }
562
+ await (0, import_maintenance.runReportingLedgerMaintenance)({
563
+ store: coreStore,
564
+ sweepSnapshots: (limit) => (0, import_ledger.sweepExpiredReportingLedgerState)(options.db, limit),
565
+ statusRetentionDays: options.statusRetentionDays,
566
+ retention: options.retention,
567
+ changeFeed: options.changeFeed,
568
+ signal,
569
+ report: (error) => reportAuxiliaryError(scheduler, error)
570
+ });
555
571
  const accountIds = rotate(await deploymentWideAccountIds(coreStore), auxiliaryRotation);
556
572
  auxiliaryRotation += 1;
557
573
  for (const accountId of accountIds) {
@@ -677,8 +693,9 @@ function routeSource(sources, sourceScope) {
677
693
  if (!source) throw new TypeError("Reporting sourceScope has no installed adapter route");
678
694
  return source;
679
695
  }
680
- async function schedulerLoop(service, options, signal, store) {
696
+ async function schedulerLoop(service, options, signal, store, maintenance) {
681
697
  let rotation = 0;
698
+ let lastMaintenanceAt;
682
699
  while (!signal.aborted) {
683
700
  let accountIds = [];
684
701
  try {
@@ -715,6 +732,14 @@ async function schedulerLoop(service, options, signal, store) {
715
732
  await reportSchedulerError(options, signal, error, { phase: "cycle", accountId });
716
733
  }
717
734
  }
735
+ if (maintenance && !signal.aborted && (lastMaintenanceAt === void 0 || Date.now() - lastMaintenanceAt >= maintenance.intervalMilliseconds)) {
736
+ lastMaintenanceAt = Date.now();
737
+ try {
738
+ await maintenance.run();
739
+ } catch (error) {
740
+ await reportSchedulerError(options, signal, error, { phase: "maintenance" });
741
+ }
742
+ }
718
743
  if (!signal.aborted) await abortableDelay(options.intervalMilliseconds, signal);
719
744
  }
720
745
  }
@@ -731,7 +756,7 @@ async function reportSchedulerError(options, signal, error, site) {
731
756
  warnSchedulerFailure(options, site, error);
732
757
  }
733
758
  function warnSchedulerFailure(options, site, error, observerError) {
734
- 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`;
759
+ 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`;
735
760
  const observed = observerError === void 0 ? "" : ` (the configured onError also threw: ${safeMessage(observerError)})`;
736
761
  const message = `[adcp/reporting] scheduler ${where}: ${safeMessage(error)}${observed}`;
737
762
  const logger = options.logger;
@@ -1199,20 +1224,6 @@ function deepFreeze(value) {
1199
1224
  }
1200
1225
  return value;
1201
1226
  }
1202
- class ReportingMaintenancePartialFailureError extends Error {
1203
- constructor(task, failedCount, failedIds) {
1204
- super(
1205
- `Reporting ${task} could not process ${failedCount} item(s)${failedIds.length ? `: ${failedIds.slice(0, 20).join(", ")}` : ""}`
1206
- );
1207
- this.task = task;
1208
- this.failedCount = failedCount;
1209
- this.failedIds = failedIds;
1210
- this.name = "ReportingMaintenancePartialFailureError";
1211
- }
1212
- task;
1213
- failedCount;
1214
- failedIds;
1215
- }
1216
1227
  // Annotate the CommonJS export names for ESM import in node:
1217
1228
  0 && (module.exports = {
1218
1229
  ReportingMaintenancePartialFailureError,
@@ -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,52 +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
- limit: options.retention.limit ?? 100,
539
- signal
540
- })
541
- ]
542
- ] : []
543
- ];
544
- const maintenanceResults = await Promise.allSettled(ledgerMaintenance.map(([, task]) => task));
545
- for (const [index, result] of maintenanceResults.entries()) {
546
- const name = ledgerMaintenance[index][0];
547
- if (result.status === "rejected") {
548
- if (!signal.aborted) await reportAuxiliaryError(scheduler, result.reason);
549
- continue;
550
- }
551
- const value = result.value;
552
- const failed = Array.isArray(value?.failed) ? value.failed.length : typeof value?.failed === "number" ? value.failed : 0;
553
- if (failed > 0 && !signal.aborted) {
554
- await reportAuxiliaryError(
555
- scheduler,
556
- new ReportingMaintenancePartialFailureError(
557
- name,
558
- failed,
559
- value?.failures?.length ? value.failures.map((entry) => `${entry.reporting_obligation_id} (${entry.cause})`) : Array.isArray(value?.failed) ? value.failed : []
560
- )
561
- );
562
- }
563
- }
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
+ });
564
582
  const accountIds = rotate(await deploymentWideAccountIds(coreStore), auxiliaryRotation);
565
583
  auxiliaryRotation += 1;
566
584
  for (const accountId of accountIds) {
@@ -686,8 +704,9 @@ function routeSource(sources, sourceScope) {
686
704
  if (!source) throw new TypeError("Reporting sourceScope has no installed adapter route");
687
705
  return source;
688
706
  }
689
- async function schedulerLoop(service, options, signal, store) {
707
+ async function schedulerLoop(service, options, signal, store, maintenance) {
690
708
  let rotation = 0;
709
+ let lastMaintenanceAt;
691
710
  while (!signal.aborted) {
692
711
  let accountIds = [];
693
712
  try {
@@ -724,6 +743,14 @@ async function schedulerLoop(service, options, signal, store) {
724
743
  await reportSchedulerError(options, signal, error, { phase: "cycle", accountId });
725
744
  }
726
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
+ }
727
754
  if (!signal.aborted) await abortableDelay(options.intervalMilliseconds, signal);
728
755
  }
729
756
  }
@@ -740,7 +767,7 @@ async function reportSchedulerError(options, signal, error, site) {
740
767
  warnSchedulerFailure(options, site, error);
741
768
  }
742
769
  function warnSchedulerFailure(options, site, error, observerError) {
743
- 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`;
744
771
  const observed = observerError === void 0 ? "" : ` (the configured onError also threw: ${safeMessage(observerError)})`;
745
772
  const message = `[adcp/reporting] scheduler ${where}: ${safeMessage(error)}${observed}`;
746
773
  const logger = options.logger;
@@ -1209,20 +1236,9 @@ function deepFreeze(value) {
1209
1236
  return value;
1210
1237
  }
1211
1238
  export * from "./conformance.mjs";
1212
- class ReportingMaintenancePartialFailureError extends Error {
1213
- constructor(task, failedCount, failedIds) {
1214
- super(
1215
- `Reporting ${task} could not process ${failedCount} item(s)${failedIds.length ? `: ${failedIds.slice(0, 20).join(", ")}` : ""}`
1216
- );
1217
- this.task = task;
1218
- this.failedCount = failedCount;
1219
- this.failedIds = failedIds;
1220
- this.name = "ReportingMaintenancePartialFailureError";
1221
- }
1222
- task;
1223
- failedCount;
1224
- failedIds;
1225
- }
1239
+ import {
1240
+ ReportingMaintenancePartialFailureError
1241
+ } from "./maintenance.mjs";
1226
1242
  export {
1227
1243
  ReportingMaintenancePartialFailureError,
1228
1244
  createPostgresReliableReportingProductionService,