@adcp/sdk 15.7.1 → 15.8.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.
- package/dist/lib/core/SingleAgentClient.d.mts +14 -0
- package/dist/lib/core/SingleAgentClient.d.ts +14 -0
- package/dist/lib/core/SingleAgentClient.js +138 -54
- package/dist/lib/core/SingleAgentClient.mjs +141 -54
- package/dist/lib/core/agent-discovery-cache.d.mts +2 -0
- package/dist/lib/core/agent-discovery-cache.d.ts +2 -0
- package/dist/lib/core/creative-schema-support.d.mts +3 -0
- package/dist/lib/core/creative-schema-support.d.ts +3 -0
- package/dist/lib/core/creative-schema-support.js +113 -0
- package/dist/lib/core/creative-schema-support.mjs +89 -0
- package/dist/lib/core/product-cache.js +4 -3
- package/dist/lib/core/product-cache.mjs +4 -3
- package/dist/lib/reporting/ledger/handler.d.mts +10 -3
- package/dist/lib/reporting/ledger/handler.d.ts +10 -3
- package/dist/lib/reporting/ledger/handler.js +17 -4
- package/dist/lib/reporting/ledger/handler.mjs +17 -4
- package/dist/lib/reporting/ledger/managed.d.mts +3 -1
- package/dist/lib/reporting/ledger/managed.d.ts +3 -1
- package/dist/lib/reporting/ledger/managed.js +4 -1
- package/dist/lib/reporting/ledger/managed.mjs +4 -1
- package/dist/lib/reporting/ledger/postgres.d.mts +87 -0
- package/dist/lib/reporting/ledger/postgres.d.ts +87 -0
- package/dist/lib/reporting/ledger/postgres.js +316 -15
- package/dist/lib/reporting/ledger/postgres.mjs +316 -15
- package/dist/lib/reporting/ledger/row-storage.d.mts +23 -0
- package/dist/lib/reporting/ledger/row-storage.d.ts +23 -0
- package/dist/lib/reporting/ledger/row-storage.js +56 -0
- package/dist/lib/reporting/ledger/row-storage.mjs +56 -0
- package/dist/lib/reporting/ledger/types.d.mts +26 -0
- package/dist/lib/reporting/ledger/types.d.ts +26 -0
- package/dist/lib/reporting/service/index.d.mts +33 -3
- package/dist/lib/reporting/service/index.d.ts +33 -3
- package/dist/lib/reporting/service/index.js +20 -2
- package/dist/lib/reporting/service/index.mjs +20 -2
- package/dist/lib/reporting/service/maintenance.d.mts +20 -1
- package/dist/lib/reporting/service/maintenance.d.ts +20 -1
- package/dist/lib/reporting/service/maintenance.js +25 -2
- package/dist/lib/reporting/service/maintenance.mjs +25 -2
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/testing/storyboard/request-builder.js +14 -16
- package/dist/lib/testing/storyboard/request-builder.mjs +14 -16
- package/dist/lib/testing/storyboard/validations.d.mts +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts +1 -1
- package/dist/lib/v2/projection/creative-delivery.d.mts +22 -0
- package/dist/lib/v2/projection/creative-delivery.d.ts +22 -0
- package/dist/lib/v2/projection/creative-delivery.js +92 -0
- package/dist/lib/v2/projection/creative-delivery.mjs +91 -0
- package/dist/lib/v2/projection/legacy-metadata.d.mts +6 -3
- package/dist/lib/v2/projection/legacy-metadata.d.ts +6 -3
- package/dist/lib/v2/projection/legacy-metadata.js +17 -0
- package/dist/lib/v2/projection/legacy-metadata.mjs +15 -0
- package/dist/lib/version.d.mts +3 -3
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.js +3 -3
- package/dist/lib/version.mjs +3 -3
- package/docs/TYPE-SUMMARY.md +2 -2
- package/docs/guides/CREATIVE-DELIVERY.md +16 -0
- package/docs/guides/REPORTING-LEDGER-CUTOVER.md +405 -0
- package/docs/guides/REPORTING-LEDGER.md +130 -0
- package/docs/guides/REPORTING-OPERATIONS.md +12 -0
- package/docs/llms.txt +3 -2
- package/docs/migration-14.x-rc-worksheet.md +4 -4
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ReportingAdjustmentReceipt, ReportingMaterialization, ReportingReceipt } from '../../types/index.mjs';
|
|
2
2
|
import { projectReportingObligationHealthV1 } from './health.mjs';
|
|
3
|
-
import type { ReportingConsumerMismatchEscalationV1, ReportingHealthV1, ReportingLedgerAdjustmentSnapshotV1, ReportingLedgerCoverageV1, ReportingLedgerObligationV1, ReportingLedgerRevisionSnapshotV1, ReportingLedgerStore, ReportingManagedDeliveryBindingV1, ReportingDeliveryHandlerV1, ReportingStatusHandlerV1 } from './types.mjs';
|
|
3
|
+
import type { ReportingConsumerMismatchEscalationV1, ReportingHealthV1, ReportingLedgerAdjustmentSnapshotV1, ReportingLedgerCoverageV1, ReportingLedgerObligationV1, ReportingLedgerRevisionSnapshotV1, ReportingLedgerStore, ReportingManagedDeliveryBindingV1, CreateReportingDeliveryHandlerOptionsV1, ReportingDeliveryHandlerV1, ReportingStatusHandlerV1 } from './types.mjs';
|
|
4
4
|
export interface ReportingStatusConsumerScopeOptionsV1<TContext = unknown> {
|
|
5
5
|
resolveConsumerId(context: TContext): string | Promise<string>;
|
|
6
6
|
/**
|
|
@@ -21,8 +21,15 @@ export declare function createReportingStatusHandler<TContext = unknown>(store:
|
|
|
21
21
|
* them through the exact `get_media_buy_delivery` read.
|
|
22
22
|
*/
|
|
23
23
|
export declare const REPORTING_STATUS_REVISION_VIEW_MAX_ROWS = 10000;
|
|
24
|
-
/**
|
|
25
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Exact revision reader for createAdcpServer's getMediaBuyDelivery slot.
|
|
26
|
+
*
|
|
27
|
+
* With `legacyRevisionReader`, a revision the SDK ledger does not hold for the
|
|
28
|
+
* resolved account is offered to that reader (see
|
|
29
|
+
* {@link ReportingLegacyRevisionReaderV1}). The SDK always answers first, and
|
|
30
|
+
* only that one not-found case falls through.
|
|
31
|
+
*/
|
|
32
|
+
export declare function createReportingDeliveryHandler(store: ReportingLedgerStore, options?: CreateReportingDeliveryHandlerOptionsV1): ReportingDeliveryHandlerV1;
|
|
26
33
|
export interface ProjectManagedDeliveryInputV1 {
|
|
27
34
|
obligation: ReportingLedgerObligationV1;
|
|
28
35
|
binding?: ReportingManagedDeliveryBindingV1;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ReportingAdjustmentReceipt, ReportingMaterialization, ReportingReceipt } from '../../types';
|
|
2
2
|
import { projectReportingObligationHealthV1 } from './health';
|
|
3
|
-
import type { ReportingConsumerMismatchEscalationV1, ReportingHealthV1, ReportingLedgerAdjustmentSnapshotV1, ReportingLedgerCoverageV1, ReportingLedgerObligationV1, ReportingLedgerRevisionSnapshotV1, ReportingLedgerStore, ReportingManagedDeliveryBindingV1, ReportingDeliveryHandlerV1, ReportingStatusHandlerV1 } from './types';
|
|
3
|
+
import type { ReportingConsumerMismatchEscalationV1, ReportingHealthV1, ReportingLedgerAdjustmentSnapshotV1, ReportingLedgerCoverageV1, ReportingLedgerObligationV1, ReportingLedgerRevisionSnapshotV1, ReportingLedgerStore, ReportingManagedDeliveryBindingV1, CreateReportingDeliveryHandlerOptionsV1, ReportingDeliveryHandlerV1, ReportingStatusHandlerV1 } from './types';
|
|
4
4
|
export interface ReportingStatusConsumerScopeOptionsV1<TContext = unknown> {
|
|
5
5
|
resolveConsumerId(context: TContext): string | Promise<string>;
|
|
6
6
|
/**
|
|
@@ -21,8 +21,15 @@ export declare function createReportingStatusHandler<TContext = unknown>(store:
|
|
|
21
21
|
* them through the exact `get_media_buy_delivery` read.
|
|
22
22
|
*/
|
|
23
23
|
export declare const REPORTING_STATUS_REVISION_VIEW_MAX_ROWS = 10000;
|
|
24
|
-
/**
|
|
25
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Exact revision reader for createAdcpServer's getMediaBuyDelivery slot.
|
|
26
|
+
*
|
|
27
|
+
* With `legacyRevisionReader`, a revision the SDK ledger does not hold for the
|
|
28
|
+
* resolved account is offered to that reader (see
|
|
29
|
+
* {@link ReportingLegacyRevisionReaderV1}). The SDK always answers first, and
|
|
30
|
+
* only that one not-found case falls through.
|
|
31
|
+
*/
|
|
32
|
+
export declare function createReportingDeliveryHandler(store: ReportingLedgerStore, options?: CreateReportingDeliveryHandlerOptionsV1): ReportingDeliveryHandlerV1;
|
|
26
33
|
export interface ProjectManagedDeliveryInputV1 {
|
|
27
34
|
obligation: ReportingLedgerObligationV1;
|
|
28
35
|
binding?: ReportingManagedDeliveryBindingV1;
|
|
@@ -416,9 +416,14 @@ function reportingRevisionNotFound() {
|
|
|
416
416
|
field: "reporting_revision_id"
|
|
417
417
|
});
|
|
418
418
|
}
|
|
419
|
-
|
|
419
|
+
const SDK_REVISION_NOT_OWNED = /* @__PURE__ */ Symbol("sdk-revision-not-owned");
|
|
420
|
+
function createReportingDeliveryHandler(store, options = {}) {
|
|
421
|
+
const legacyRevisionReader = options.legacyRevisionReader;
|
|
422
|
+
if (legacyRevisionReader !== void 0 && typeof legacyRevisionReader !== "function") {
|
|
423
|
+
throw new TypeError("legacyRevisionReader must be a function");
|
|
424
|
+
}
|
|
420
425
|
const activeReadsByAccount = /* @__PURE__ */ new Map();
|
|
421
|
-
|
|
426
|
+
const readFromSdkLedger = async (request, context) => {
|
|
422
427
|
const accountId = resolvedAccountId(context.account);
|
|
423
428
|
let releaseReadSlot;
|
|
424
429
|
try {
|
|
@@ -440,9 +445,9 @@ function createReportingDeliveryHandler(store) {
|
|
|
440
445
|
});
|
|
441
446
|
}
|
|
442
447
|
const revision = await (0, import_revision_metadata.getRevisionMetadataFromStore)(store, raw.reporting_revision_id, accountId);
|
|
443
|
-
if (!revision)
|
|
448
|
+
if (!revision) return SDK_REVISION_NOT_OWNED;
|
|
444
449
|
const obligation = await store.getObligation(revision.reporting_obligation_id);
|
|
445
|
-
if (!obligation || obligation.account.account_id !== accountId)
|
|
450
|
+
if (!obligation || obligation.account.account_id !== accountId) return SDK_REVISION_NOT_OWNED;
|
|
446
451
|
const pagination = isRecord(raw.pagination) ? raw.pagination : {};
|
|
447
452
|
const maxResults = typeof pagination.max_results === "number" && Number.isFinite(pagination.max_results) ? Math.max(1, Math.min(500, Math.trunc(pagination.max_results))) : 100;
|
|
448
453
|
const totalCount = revision.binding.rowCount;
|
|
@@ -520,6 +525,14 @@ function createReportingDeliveryHandler(store) {
|
|
|
520
525
|
releaseReadSlot();
|
|
521
526
|
}
|
|
522
527
|
};
|
|
528
|
+
return async (request, context) => {
|
|
529
|
+
const result = await readFromSdkLedger(request, context);
|
|
530
|
+
if (result !== SDK_REVISION_NOT_OWNED) return result;
|
|
531
|
+
if (!legacyRevisionReader) throw reportingRevisionNotFound();
|
|
532
|
+
const legacy = await legacyRevisionReader(request, context);
|
|
533
|
+
if (legacy === null || legacy === void 0) throw reportingRevisionNotFound();
|
|
534
|
+
return legacy;
|
|
535
|
+
};
|
|
523
536
|
}
|
|
524
537
|
function resolvedAccountId(account) {
|
|
525
538
|
const resolved = isRecord(account) ? account : {};
|
|
@@ -399,9 +399,14 @@ function reportingRevisionNotFound() {
|
|
|
399
399
|
field: "reporting_revision_id"
|
|
400
400
|
});
|
|
401
401
|
}
|
|
402
|
-
|
|
402
|
+
const SDK_REVISION_NOT_OWNED = /* @__PURE__ */ Symbol("sdk-revision-not-owned");
|
|
403
|
+
function createReportingDeliveryHandler(store, options = {}) {
|
|
404
|
+
const legacyRevisionReader = options.legacyRevisionReader;
|
|
405
|
+
if (legacyRevisionReader !== void 0 && typeof legacyRevisionReader !== "function") {
|
|
406
|
+
throw new TypeError("legacyRevisionReader must be a function");
|
|
407
|
+
}
|
|
403
408
|
const activeReadsByAccount = /* @__PURE__ */ new Map();
|
|
404
|
-
|
|
409
|
+
const readFromSdkLedger = async (request, context) => {
|
|
405
410
|
const accountId = resolvedAccountId(context.account);
|
|
406
411
|
let releaseReadSlot;
|
|
407
412
|
try {
|
|
@@ -423,9 +428,9 @@ function createReportingDeliveryHandler(store) {
|
|
|
423
428
|
});
|
|
424
429
|
}
|
|
425
430
|
const revision = await getRevisionMetadataFromStore(store, raw.reporting_revision_id, accountId);
|
|
426
|
-
if (!revision)
|
|
431
|
+
if (!revision) return SDK_REVISION_NOT_OWNED;
|
|
427
432
|
const obligation = await store.getObligation(revision.reporting_obligation_id);
|
|
428
|
-
if (!obligation || obligation.account.account_id !== accountId)
|
|
433
|
+
if (!obligation || obligation.account.account_id !== accountId) return SDK_REVISION_NOT_OWNED;
|
|
429
434
|
const pagination = isRecord(raw.pagination) ? raw.pagination : {};
|
|
430
435
|
const maxResults = typeof pagination.max_results === "number" && Number.isFinite(pagination.max_results) ? Math.max(1, Math.min(500, Math.trunc(pagination.max_results))) : 100;
|
|
431
436
|
const totalCount = revision.binding.rowCount;
|
|
@@ -503,6 +508,14 @@ function createReportingDeliveryHandler(store) {
|
|
|
503
508
|
releaseReadSlot();
|
|
504
509
|
}
|
|
505
510
|
};
|
|
511
|
+
return async (request, context) => {
|
|
512
|
+
const result = await readFromSdkLedger(request, context);
|
|
513
|
+
if (result !== SDK_REVISION_NOT_OWNED) return result;
|
|
514
|
+
if (!legacyRevisionReader) throw reportingRevisionNotFound();
|
|
515
|
+
const legacy = await legacyRevisionReader(request, context);
|
|
516
|
+
if (legacy === null || legacy === void 0) throw reportingRevisionNotFound();
|
|
517
|
+
return legacy;
|
|
518
|
+
};
|
|
506
519
|
}
|
|
507
520
|
function resolvedAccountId(account) {
|
|
508
521
|
const resolved = isRecord(account) ? account : {};
|
|
@@ -2,7 +2,7 @@ import type { ReportingAdjustmentReceipt, ReportingDeliveryCapabilities, Reporti
|
|
|
2
2
|
import type { AdcpToolMap } from '../../server/create-adcp-server.mjs';
|
|
3
3
|
import { createReportingDeliveryHandler, createReportingStatusHandler } from './handler.mjs';
|
|
4
4
|
import type { ReportingStatusConsumerScopeOptionsV1 } from './handler.mjs';
|
|
5
|
-
import type { ReportingLedgerAdjustmentV1, ReportingLedgerObligationV1, ReportingLedgerRevisionV1, ReportingLedgerStore, ReportingManagedDeliveryBindingV1 } from './types.mjs';
|
|
5
|
+
import type { ReportingLedgerAdjustmentV1, ReportingLegacyRevisionReaderV1, ReportingLedgerObligationV1, ReportingLedgerRevisionV1, ReportingLedgerStore, ReportingManagedDeliveryBindingV1 } from './types.mjs';
|
|
6
6
|
export interface ReportingDestinationAuthorizationV1 {
|
|
7
7
|
account_id: string;
|
|
8
8
|
destination_ref: string;
|
|
@@ -274,6 +274,8 @@ export interface CreateReportingManagedDeliveryRuntimeOptionsV1<TContext extends
|
|
|
274
274
|
account?: unknown;
|
|
275
275
|
}> {
|
|
276
276
|
coreStore: ReportingLedgerStore;
|
|
277
|
+
/** Exact-revision fallback for revisions the SDK ledger does not hold. */
|
|
278
|
+
legacyRevisionReader?: ReportingLegacyRevisionReaderV1;
|
|
277
279
|
store: ReportingManagedDeliveryStore & Required<Pick<ReportingManagedDeliveryStore, 'adoptAdvertisedPolicies'>>;
|
|
278
280
|
adapter: ReportingManagedDeliveryAdapterV1;
|
|
279
281
|
offerings: ReportingDeliveryCapabilities['offerings'];
|
|
@@ -2,7 +2,7 @@ import type { ReportingAdjustmentReceipt, ReportingDeliveryCapabilities, Reporti
|
|
|
2
2
|
import type { AdcpToolMap } from '../../server/create-adcp-server';
|
|
3
3
|
import { createReportingDeliveryHandler, createReportingStatusHandler } from './handler';
|
|
4
4
|
import type { ReportingStatusConsumerScopeOptionsV1 } from './handler';
|
|
5
|
-
import type { ReportingLedgerAdjustmentV1, ReportingLedgerObligationV1, ReportingLedgerRevisionV1, ReportingLedgerStore, ReportingManagedDeliveryBindingV1 } from './types';
|
|
5
|
+
import type { ReportingLedgerAdjustmentV1, ReportingLegacyRevisionReaderV1, ReportingLedgerObligationV1, ReportingLedgerRevisionV1, ReportingLedgerStore, ReportingManagedDeliveryBindingV1 } from './types';
|
|
6
6
|
export interface ReportingDestinationAuthorizationV1 {
|
|
7
7
|
account_id: string;
|
|
8
8
|
destination_ref: string;
|
|
@@ -274,6 +274,8 @@ export interface CreateReportingManagedDeliveryRuntimeOptionsV1<TContext extends
|
|
|
274
274
|
account?: unknown;
|
|
275
275
|
}> {
|
|
276
276
|
coreStore: ReportingLedgerStore;
|
|
277
|
+
/** Exact-revision fallback for revisions the SDK ledger does not hold. */
|
|
278
|
+
legacyRevisionReader?: ReportingLegacyRevisionReaderV1;
|
|
277
279
|
store: ReportingManagedDeliveryStore & Required<Pick<ReportingManagedDeliveryStore, 'adoptAdvertisedPolicies'>>;
|
|
278
280
|
adapter: ReportingManagedDeliveryAdapterV1;
|
|
279
281
|
offerings: ReportingDeliveryCapabilities['offerings'];
|
|
@@ -89,7 +89,10 @@ async function createReportingManagedDeliveryRuntime(options) {
|
|
|
89
89
|
...options.consumerMismatchEscalation ? { consumerMismatchEscalation: options.consumerMismatchEscalation } : {}
|
|
90
90
|
} : void 0;
|
|
91
91
|
const getReportingStatus = (0, import_handler.createReportingStatusHandler)(options.coreStore, statusOptions);
|
|
92
|
-
const getMediaBuyDelivery = (0, import_handler.createReportingDeliveryHandler)(
|
|
92
|
+
const getMediaBuyDelivery = (0, import_handler.createReportingDeliveryHandler)(
|
|
93
|
+
options.coreStore,
|
|
94
|
+
options.legacyRevisionReader ? { legacyRevisionReader: options.legacyRevisionReader } : {}
|
|
95
|
+
);
|
|
93
96
|
const syncReportingReceipts = reconciledBilling ? createSyncReportingReceiptsHandler(options.store, options.resolveConsumerId) : void 0;
|
|
94
97
|
const reportingDeliveryCapabilities = {
|
|
95
98
|
supported: true,
|
|
@@ -62,7 +62,10 @@ async function createReportingManagedDeliveryRuntime(options) {
|
|
|
62
62
|
...options.consumerMismatchEscalation ? { consumerMismatchEscalation: options.consumerMismatchEscalation } : {}
|
|
63
63
|
} : void 0;
|
|
64
64
|
const getReportingStatus = createReportingStatusHandler(options.coreStore, statusOptions);
|
|
65
|
-
const getMediaBuyDelivery = createReportingDeliveryHandler(
|
|
65
|
+
const getMediaBuyDelivery = createReportingDeliveryHandler(
|
|
66
|
+
options.coreStore,
|
|
67
|
+
options.legacyRevisionReader ? { legacyRevisionReader: options.legacyRevisionReader } : {}
|
|
68
|
+
);
|
|
66
69
|
const syncReportingReceipts = reconciledBilling ? createSyncReportingReceiptsHandler(options.store, options.resolveConsumerId) : void 0;
|
|
67
70
|
const reportingDeliveryCapabilities = {
|
|
68
71
|
supported: true,
|
|
@@ -150,6 +150,12 @@ export declare class PostgresReportingLedgerStore implements ReportingLedgerStor
|
|
|
150
150
|
}>;
|
|
151
151
|
/** Append one host change-feed row inside the caller's account-locked transaction. */
|
|
152
152
|
private appendChange;
|
|
153
|
+
/**
|
|
154
|
+
* Throws `INVALID_INPUT` when any configured object row binding's key template omits
|
|
155
|
+
* `{revision_key}`, which makes snapshot compaction unsafe (see `compactSupersededSnapshots`).
|
|
156
|
+
* Called by `compactSupersededSnapshots` and by service construction when compaction is enabled.
|
|
157
|
+
*/
|
|
158
|
+
assertCompactionSafeRowBindings(): void;
|
|
153
159
|
/** Probe and register row storage when enabled. Safe to call repeatedly. */
|
|
154
160
|
readyRowStorage(): Promise<void>;
|
|
155
161
|
/**
|
|
@@ -318,6 +324,65 @@ export declare class PostgresReportingLedgerStore implements ReportingLedgerStor
|
|
|
318
324
|
}[];
|
|
319
325
|
}>;
|
|
320
326
|
private retirePeriod;
|
|
327
|
+
/**
|
|
328
|
+
* Compact superseded snapshot revisions to metadata only, as adcontextprotocol/adcp#8100
|
|
329
|
+
* permits (`reporting-revision.json` `content_retention`). Opt-in: nothing calls this
|
|
330
|
+
* unless the host enables `compaction` in maintenance, and it must not be enabled in
|
|
331
|
+
* production before the AdCP release that takes compaction into effect (the next
|
|
332
|
+
* eligible minor after November 20, 2026).
|
|
333
|
+
*
|
|
334
|
+
* A snapshot revision is compacted only when every one of these holds:
|
|
335
|
+
*
|
|
336
|
+
* - it is superseded: a later revision names it in `supersedes_reporting_revision_id`
|
|
337
|
+
* (so a current revision, and every official revision, is never compacted);
|
|
338
|
+
* - it is not the revision the current, unsuperseded revision names;
|
|
339
|
+
* - its recovery grace has passed: `max(automatedRecoveryWindowSeconds, 3600)` seconds
|
|
340
|
+
* since its successor's `created_at`;
|
|
341
|
+
* - while its window is open (`statusRetentionDays` since its successor's `created_at`),
|
|
342
|
+
* no caller's reporting receipt (any status) names it and no caller's current consumer
|
|
343
|
+
* status statement of `received`, `unreadable` or `content_mismatch` names it. The
|
|
344
|
+
* check spans every caller, so content any caller is entitled to is kept;
|
|
345
|
+
* - it has rows (nothing to reclaim otherwise, and an empty exact read is still correct);
|
|
346
|
+
* - no live obligation lease, no pending materialization of it, and no live change-feed
|
|
347
|
+
* consumer that has not yet passed its change row (the holds retirement applies,
|
|
348
|
+
* narrowed to this revision).
|
|
349
|
+
*
|
|
350
|
+
* Only row content is dropped: the revision header, its manifest and every metadata
|
|
351
|
+
* listing stay. The row set is marked `pruned`, so an exact read answers
|
|
352
|
+
* `ROWS_EXPIRED`, which the handlers surface as the same nondisclosing
|
|
353
|
+
* `REFERENCE_NOT_FOUND` as an unknown ID. No change-feed row is recorded: the revision's
|
|
354
|
+
* metadata is unchanged, and a sink still catching up holds compaction back.
|
|
355
|
+
*
|
|
356
|
+
* Compaction is resumable and runs in the same three phases as retirement, under the
|
|
357
|
+
* account lock where eligibility is decided:
|
|
358
|
+
*
|
|
359
|
+
* 1. re-check eligibility and mark the row set `pruning`, so reads answer as expired and
|
|
360
|
+
* no new revision can share its chunks;
|
|
361
|
+
* 2. delete external objects at their recorded versions, skipping any object another
|
|
362
|
+
* row set that is not being pruned still references (chunk sharing);
|
|
363
|
+
* 3. delete the chunk manifest rows and any PostgreSQL chunk body nothing else
|
|
364
|
+
* references, and mark the row set `pruned`.
|
|
365
|
+
*
|
|
366
|
+
* A crash after phase 1 leaves a `pruning` row set that the next pass resumes.
|
|
367
|
+
*/
|
|
368
|
+
compactSupersededSnapshots(input: {
|
|
369
|
+
statusRetentionDays: number;
|
|
370
|
+
automatedRecoveryWindowSeconds: number;
|
|
371
|
+
maxFeedHoldDays?: number;
|
|
372
|
+
limit?: number;
|
|
373
|
+
account_id?: string;
|
|
374
|
+
signal?: AbortSignal;
|
|
375
|
+
}): Promise<{
|
|
376
|
+
compacted: number;
|
|
377
|
+
failed: string[];
|
|
378
|
+
/** Secret-free cause per failed revision: a row-store code or the error class name. */
|
|
379
|
+
failures: {
|
|
380
|
+
reporting_obligation_id: string;
|
|
381
|
+
reporting_revision_id: string;
|
|
382
|
+
cause: string;
|
|
383
|
+
}[];
|
|
384
|
+
}>;
|
|
385
|
+
private compactSnapshot;
|
|
321
386
|
/**
|
|
322
387
|
* Resolve abandoned object uploads (spec §4.3). An expired `open` write
|
|
323
388
|
* intent whose ledger commit never happened is moved to `sweeping` under
|
|
@@ -335,6 +400,28 @@ export declare class PostgresReportingLedgerStore implements ReportingLedgerStor
|
|
|
335
400
|
objectsDeleted: number;
|
|
336
401
|
failed: number;
|
|
337
402
|
}>;
|
|
403
|
+
/**
|
|
404
|
+
* Delete external row objects, skipping any object another row set still references.
|
|
405
|
+
*
|
|
406
|
+
* A custom `keyTemplate` need only include `{content_sha256}` and `{chunk_index}`, so row sets
|
|
407
|
+
* from different obligations or accounts with identical chunk content can share one object (a
|
|
408
|
+
* create-only write adopts an existing object). An object is therefore deleted only when no
|
|
409
|
+
* chunk of a live (or unavailable) row set outside this operation references the same binding,
|
|
410
|
+
* key and native version, and no other open write intent created it. Row sets already `pruning`
|
|
411
|
+
* or `pruned` do not count: their own operation deletes the object, and counting them would let
|
|
412
|
+
* two concurrent retirements each skip the object and leak it. `scope` names what this
|
|
413
|
+
* operation is deleting: an obligation (retirement) or a row set (compaction).
|
|
414
|
+
*
|
|
415
|
+
* The check and the provider delete run in one transaction under a per-object advisory lock.
|
|
416
|
+
* A commit that adopts an existing object takes the same lock before it inserts its chunk rows
|
|
417
|
+
* and re-verifies the object under it (`writePreparedRowSet`), and holds it until commit. So
|
|
418
|
+
* either the delete wins the lock and the adopting commit then fails cleanly, or the commit wins
|
|
419
|
+
* and the delete sees its chunk rows and skips. A skipped object stays for the row set that owns
|
|
420
|
+
* it: that row set's own retirement, compaction or sweep deletes it, because by then nothing
|
|
421
|
+
* else references it. Locks are taken one object at a time, never while waiting on the account
|
|
422
|
+
* lock, so commits (account lock, then object locks) cannot deadlock with these deletes.
|
|
423
|
+
*/
|
|
424
|
+
private deleteUnreferencedObjects;
|
|
338
425
|
private optionalTables;
|
|
339
426
|
/**
|
|
340
427
|
* Upload an object-kind row set before its ledger commit (spec §3.5):
|
|
@@ -150,6 +150,12 @@ export declare class PostgresReportingLedgerStore implements ReportingLedgerStor
|
|
|
150
150
|
}>;
|
|
151
151
|
/** Append one host change-feed row inside the caller's account-locked transaction. */
|
|
152
152
|
private appendChange;
|
|
153
|
+
/**
|
|
154
|
+
* Throws `INVALID_INPUT` when any configured object row binding's key template omits
|
|
155
|
+
* `{revision_key}`, which makes snapshot compaction unsafe (see `compactSupersededSnapshots`).
|
|
156
|
+
* Called by `compactSupersededSnapshots` and by service construction when compaction is enabled.
|
|
157
|
+
*/
|
|
158
|
+
assertCompactionSafeRowBindings(): void;
|
|
153
159
|
/** Probe and register row storage when enabled. Safe to call repeatedly. */
|
|
154
160
|
readyRowStorage(): Promise<void>;
|
|
155
161
|
/**
|
|
@@ -318,6 +324,65 @@ export declare class PostgresReportingLedgerStore implements ReportingLedgerStor
|
|
|
318
324
|
}[];
|
|
319
325
|
}>;
|
|
320
326
|
private retirePeriod;
|
|
327
|
+
/**
|
|
328
|
+
* Compact superseded snapshot revisions to metadata only, as adcontextprotocol/adcp#8100
|
|
329
|
+
* permits (`reporting-revision.json` `content_retention`). Opt-in: nothing calls this
|
|
330
|
+
* unless the host enables `compaction` in maintenance, and it must not be enabled in
|
|
331
|
+
* production before the AdCP release that takes compaction into effect (the next
|
|
332
|
+
* eligible minor after November 20, 2026).
|
|
333
|
+
*
|
|
334
|
+
* A snapshot revision is compacted only when every one of these holds:
|
|
335
|
+
*
|
|
336
|
+
* - it is superseded: a later revision names it in `supersedes_reporting_revision_id`
|
|
337
|
+
* (so a current revision, and every official revision, is never compacted);
|
|
338
|
+
* - it is not the revision the current, unsuperseded revision names;
|
|
339
|
+
* - its recovery grace has passed: `max(automatedRecoveryWindowSeconds, 3600)` seconds
|
|
340
|
+
* since its successor's `created_at`;
|
|
341
|
+
* - while its window is open (`statusRetentionDays` since its successor's `created_at`),
|
|
342
|
+
* no caller's reporting receipt (any status) names it and no caller's current consumer
|
|
343
|
+
* status statement of `received`, `unreadable` or `content_mismatch` names it. The
|
|
344
|
+
* check spans every caller, so content any caller is entitled to is kept;
|
|
345
|
+
* - it has rows (nothing to reclaim otherwise, and an empty exact read is still correct);
|
|
346
|
+
* - no live obligation lease, no pending materialization of it, and no live change-feed
|
|
347
|
+
* consumer that has not yet passed its change row (the holds retirement applies,
|
|
348
|
+
* narrowed to this revision).
|
|
349
|
+
*
|
|
350
|
+
* Only row content is dropped: the revision header, its manifest and every metadata
|
|
351
|
+
* listing stay. The row set is marked `pruned`, so an exact read answers
|
|
352
|
+
* `ROWS_EXPIRED`, which the handlers surface as the same nondisclosing
|
|
353
|
+
* `REFERENCE_NOT_FOUND` as an unknown ID. No change-feed row is recorded: the revision's
|
|
354
|
+
* metadata is unchanged, and a sink still catching up holds compaction back.
|
|
355
|
+
*
|
|
356
|
+
* Compaction is resumable and runs in the same three phases as retirement, under the
|
|
357
|
+
* account lock where eligibility is decided:
|
|
358
|
+
*
|
|
359
|
+
* 1. re-check eligibility and mark the row set `pruning`, so reads answer as expired and
|
|
360
|
+
* no new revision can share its chunks;
|
|
361
|
+
* 2. delete external objects at their recorded versions, skipping any object another
|
|
362
|
+
* row set that is not being pruned still references (chunk sharing);
|
|
363
|
+
* 3. delete the chunk manifest rows and any PostgreSQL chunk body nothing else
|
|
364
|
+
* references, and mark the row set `pruned`.
|
|
365
|
+
*
|
|
366
|
+
* A crash after phase 1 leaves a `pruning` row set that the next pass resumes.
|
|
367
|
+
*/
|
|
368
|
+
compactSupersededSnapshots(input: {
|
|
369
|
+
statusRetentionDays: number;
|
|
370
|
+
automatedRecoveryWindowSeconds: number;
|
|
371
|
+
maxFeedHoldDays?: number;
|
|
372
|
+
limit?: number;
|
|
373
|
+
account_id?: string;
|
|
374
|
+
signal?: AbortSignal;
|
|
375
|
+
}): Promise<{
|
|
376
|
+
compacted: number;
|
|
377
|
+
failed: string[];
|
|
378
|
+
/** Secret-free cause per failed revision: a row-store code or the error class name. */
|
|
379
|
+
failures: {
|
|
380
|
+
reporting_obligation_id: string;
|
|
381
|
+
reporting_revision_id: string;
|
|
382
|
+
cause: string;
|
|
383
|
+
}[];
|
|
384
|
+
}>;
|
|
385
|
+
private compactSnapshot;
|
|
321
386
|
/**
|
|
322
387
|
* Resolve abandoned object uploads (spec §4.3). An expired `open` write
|
|
323
388
|
* intent whose ledger commit never happened is moved to `sweeping` under
|
|
@@ -335,6 +400,28 @@ export declare class PostgresReportingLedgerStore implements ReportingLedgerStor
|
|
|
335
400
|
objectsDeleted: number;
|
|
336
401
|
failed: number;
|
|
337
402
|
}>;
|
|
403
|
+
/**
|
|
404
|
+
* Delete external row objects, skipping any object another row set still references.
|
|
405
|
+
*
|
|
406
|
+
* A custom `keyTemplate` need only include `{content_sha256}` and `{chunk_index}`, so row sets
|
|
407
|
+
* from different obligations or accounts with identical chunk content can share one object (a
|
|
408
|
+
* create-only write adopts an existing object). An object is therefore deleted only when no
|
|
409
|
+
* chunk of a live (or unavailable) row set outside this operation references the same binding,
|
|
410
|
+
* key and native version, and no other open write intent created it. Row sets already `pruning`
|
|
411
|
+
* or `pruned` do not count: their own operation deletes the object, and counting them would let
|
|
412
|
+
* two concurrent retirements each skip the object and leak it. `scope` names what this
|
|
413
|
+
* operation is deleting: an obligation (retirement) or a row set (compaction).
|
|
414
|
+
*
|
|
415
|
+
* The check and the provider delete run in one transaction under a per-object advisory lock.
|
|
416
|
+
* A commit that adopts an existing object takes the same lock before it inserts its chunk rows
|
|
417
|
+
* and re-verifies the object under it (`writePreparedRowSet`), and holds it until commit. So
|
|
418
|
+
* either the delete wins the lock and the adopting commit then fails cleanly, or the commit wins
|
|
419
|
+
* and the delete sees its chunk rows and skips. A skipped object stays for the row set that owns
|
|
420
|
+
* it: that row set's own retirement, compaction or sweep deletes it, because by then nothing
|
|
421
|
+
* else references it. Locks are taken one object at a time, never while waiting on the account
|
|
422
|
+
* lock, so commits (account lock, then object locks) cannot deadlock with these deletes.
|
|
423
|
+
*/
|
|
424
|
+
private deleteUnreferencedObjects;
|
|
338
425
|
private optionalTables;
|
|
339
426
|
/**
|
|
340
427
|
* Upload an object-kind row set before its ledger commit (spec §3.5):
|