@adcp/sdk 14.0.0-rc.37 → 14.0.0-rc.39
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/bin/adcp-storyboard-summary.js +286 -0
- package/bin/adcp.js +221 -10
- package/dist/lib/conformance/schemaLoader.d.mts +1 -1
- package/dist/lib/conformance/schemaLoader.d.ts +1 -1
- package/dist/lib/core/AgentClient.d.mts +6 -1
- package/dist/lib/core/AgentClient.d.ts +6 -1
- package/dist/lib/core/AgentClient.js +9 -0
- package/dist/lib/core/AgentClient.mjs +9 -0
- package/dist/lib/core/ConversationTypes.d.mts +29 -0
- package/dist/lib/core/ConversationTypes.d.ts +29 -0
- package/dist/lib/core/SingleAgentClient.d.mts +3 -1
- package/dist/lib/core/SingleAgentClient.d.ts +3 -1
- package/dist/lib/core/SingleAgentClient.js +9 -1
- package/dist/lib/core/SingleAgentClient.mjs +9 -1
- package/dist/lib/core/TaskExecutor.d.mts +13 -1
- package/dist/lib/core/TaskExecutor.d.ts +13 -1
- package/dist/lib/core/TaskExecutor.js +216 -18
- package/dist/lib/core/TaskExecutor.mjs +216 -19
- package/dist/lib/index.d.mts +5 -2
- package/dist/lib/index.d.ts +5 -2
- package/dist/lib/index.js +10 -1
- package/dist/lib/index.mjs +10 -1
- package/dist/lib/media-buy/action-assessment.d.mts +77 -0
- package/dist/lib/media-buy/action-assessment.d.ts +77 -0
- package/dist/lib/media-buy/action-assessment.js +300 -0
- package/dist/lib/media-buy/action-assessment.mjs +284 -0
- package/dist/lib/media-buy/action-constraints.d.mts +9 -0
- package/dist/lib/media-buy/action-constraints.d.ts +9 -0
- package/dist/lib/media-buy/action-constraints.js +170 -0
- package/dist/lib/media-buy/action-constraints.mjs +146 -0
- package/dist/lib/media-buy/action-contracts.d.mts +40 -0
- package/dist/lib/media-buy/action-contracts.d.ts +40 -0
- package/dist/lib/media-buy/action-contracts.js +304 -0
- package/dist/lib/media-buy/action-contracts.mjs +267 -0
- package/dist/lib/media-buy/action-metadata.generated.d.mts +37 -0
- package/dist/lib/media-buy/action-metadata.generated.d.ts +37 -0
- package/dist/lib/media-buy/action-metadata.generated.js +128 -0
- package/dist/lib/media-buy/action-metadata.generated.mjs +101 -0
- package/dist/lib/media-buy/action-types.d.mts +106 -0
- package/dist/lib/media-buy/action-types.d.ts +106 -0
- package/dist/lib/{types/v3-1-beta/tools.generated.js → media-buy/action-types.js} +2 -2
- package/dist/lib/media-buy/actions.d.mts +6 -0
- package/dist/lib/media-buy/actions.d.ts +6 -0
- package/dist/lib/media-buy/actions.js +38 -0
- package/dist/lib/media-buy/actions.mjs +9 -0
- package/dist/lib/media-buy/available-actions.d.mts +2 -1
- package/dist/lib/media-buy/available-actions.d.ts +2 -1
- package/dist/lib/media-buy/available-actions.js +4 -3
- package/dist/lib/media-buy/available-actions.mjs +4 -3
- package/dist/lib/media-buy/compatibility.js +3 -0
- package/dist/lib/media-buy/compatibility.mjs +3 -0
- package/dist/lib/media-buy/index.d.mts +4 -0
- package/dist/lib/media-buy/index.d.ts +4 -0
- package/dist/lib/media-buy/index.js +3 -1
- package/dist/lib/media-buy/index.mjs +1 -0
- package/dist/lib/media-buy/legacy-action-ids.d.mts +1 -0
- package/dist/lib/media-buy/legacy-action-ids.d.ts +1 -0
- package/dist/lib/media-buy/legacy-action-ids.js +50 -0
- package/dist/lib/media-buy/legacy-action-ids.mjs +26 -0
- package/dist/lib/media-buy/mutations.d.mts +70 -0
- package/dist/lib/media-buy/mutations.d.ts +70 -0
- package/dist/lib/media-buy/mutations.js +525 -0
- package/dist/lib/media-buy/mutations.mjs +498 -0
- package/dist/lib/media-buy/preflight.d.mts +20 -69
- package/dist/lib/media-buy/preflight.d.ts +20 -69
- package/dist/lib/media-buy/preflight.js +100 -336
- package/dist/lib/media-buy/preflight.mjs +111 -333
- package/dist/lib/media-buy/targeting-input.d.mts +7 -4
- package/dist/lib/media-buy/targeting-input.d.ts +7 -4
- package/dist/lib/media-buy/types.d.mts +55 -7
- package/dist/lib/media-buy/types.d.ts +55 -7
- package/dist/lib/protocols/rawResponseCapture.d.mts +12 -0
- package/dist/lib/protocols/rawResponseCapture.d.ts +12 -0
- package/dist/lib/protocols/rawResponseCapture.js +33 -3
- package/dist/lib/protocols/rawResponseCapture.mjs +32 -3
- package/dist/lib/registry/types.generated.d.mts +377 -9
- package/dist/lib/registry/types.generated.d.ts +377 -9
- package/dist/lib/reporting/content-mismatch.d.mts +126 -0
- package/dist/lib/reporting/content-mismatch.d.ts +126 -0
- package/dist/lib/reporting/content-mismatch.js +90 -0
- package/dist/lib/reporting/content-mismatch.mjs +66 -0
- package/dist/lib/reporting/evidence.d.mts +1 -0
- package/dist/lib/reporting/evidence.d.ts +1 -0
- package/dist/lib/reporting/evidence.js +32 -3
- package/dist/lib/reporting/evidence.mjs +31 -3
- package/dist/lib/reporting/index.d.mts +3 -1
- package/dist/lib/reporting/index.d.ts +3 -1
- package/dist/lib/reporting/index.js +3 -0
- package/dist/lib/reporting/index.mjs +2 -0
- package/dist/lib/reporting/ledger/handler.d.mts +51 -1
- package/dist/lib/reporting/ledger/handler.d.ts +51 -1
- package/dist/lib/reporting/ledger/handler.js +306 -38
- package/dist/lib/reporting/ledger/handler.mjs +304 -37
- package/dist/lib/reporting/ledger/health.d.mts +12 -0
- package/dist/lib/reporting/ledger/health.d.ts +12 -0
- package/dist/lib/reporting/ledger/health.js +15 -2
- package/dist/lib/reporting/ledger/health.mjs +13 -1
- package/dist/lib/reporting/ledger/index.d.mts +3 -0
- package/dist/lib/reporting/ledger/index.d.ts +3 -0
- package/dist/lib/reporting/ledger/index.js +7 -1
- package/dist/lib/reporting/ledger/index.mjs +3 -0
- package/dist/lib/reporting/ledger/lifecycle.d.mts +31 -4
- package/dist/lib/reporting/ledger/lifecycle.d.ts +31 -4
- package/dist/lib/reporting/ledger/lifecycle.js +244 -37
- package/dist/lib/reporting/ledger/lifecycle.mjs +244 -37
- package/dist/lib/reporting/ledger/managed-postgres.d.mts +231 -0
- package/dist/lib/reporting/ledger/managed-postgres.d.ts +231 -0
- package/dist/lib/reporting/ledger/managed-postgres.js +1797 -0
- package/dist/lib/reporting/ledger/managed-postgres.mjs +1776 -0
- package/dist/lib/reporting/ledger/managed.d.mts +355 -0
- package/dist/lib/reporting/ledger/managed.d.ts +355 -0
- package/dist/lib/reporting/ledger/managed.js +661 -0
- package/dist/lib/reporting/ledger/managed.mjs +633 -0
- package/dist/lib/reporting/ledger/notification-activity.d.mts +222 -0
- package/dist/lib/reporting/ledger/notification-activity.d.ts +222 -0
- package/dist/lib/reporting/ledger/notification-activity.js +1139 -0
- package/dist/lib/reporting/ledger/notification-activity.mjs +1110 -0
- package/dist/lib/reporting/ledger/postgres.d.mts +161 -1
- package/dist/lib/reporting/ledger/postgres.d.ts +161 -1
- package/dist/lib/reporting/ledger/postgres.js +1153 -21
- package/dist/lib/reporting/ledger/postgres.mjs +1152 -21
- package/dist/lib/reporting/ledger/producer.d.mts +8 -0
- package/dist/lib/reporting/ledger/producer.d.ts +8 -0
- package/dist/lib/reporting/ledger/producer.js +146 -52
- package/dist/lib/reporting/ledger/producer.mjs +146 -51
- package/dist/lib/reporting/ledger/types.d.mts +358 -1
- package/dist/lib/reporting/ledger/types.d.ts +358 -1
- package/dist/lib/reporting/ledger/types.js +3 -0
- package/dist/lib/reporting/ledger/types.mjs +2 -0
- package/dist/lib/reporting/reconciliation.d.mts +362 -2
- package/dist/lib/reporting/reconciliation.d.ts +362 -2
- package/dist/lib/reporting/reconciliation.js +1051 -7
- package/dist/lib/reporting/reconciliation.mjs +1053 -7
- package/dist/lib/reporting/service/conformance.d.mts +35 -0
- package/dist/lib/reporting/service/conformance.d.ts +35 -0
- package/dist/lib/reporting/service/conformance.js +89 -0
- package/dist/lib/reporting/service/conformance.mjs +68 -0
- package/dist/lib/reporting/service/index.d.mts +182 -0
- package/dist/lib/reporting/service/index.d.ts +182 -0
- package/dist/lib/reporting/service/index.js +793 -0
- package/dist/lib/reporting/service/index.mjs +787 -0
- package/dist/lib/reporting/source/index.d.mts +2 -1
- package/dist/lib/reporting/source/index.d.ts +2 -1
- package/dist/lib/reporting/source/index.js +16 -3
- package/dist/lib/reporting/source/index.mjs +12 -1
- package/dist/lib/reporting/source/inline.d.mts +88 -1
- package/dist/lib/reporting/source/inline.d.ts +88 -1
- package/dist/lib/reporting/source/inline.js +1161 -163
- package/dist/lib/reporting/source/inline.mjs +1166 -163
- package/dist/lib/reporting/source/manifest.js +12 -7
- package/dist/lib/reporting/source/manifest.mjs +12 -7
- package/dist/lib/reporting/source/source.d.mts +26 -0
- package/dist/lib/reporting/source/source.d.ts +26 -0
- package/dist/lib/reporting/source/source.js +71 -2
- package/dist/lib/reporting/source/source.mjs +65 -1
- package/dist/lib/schemas/index.d.mts +22 -1
- package/dist/lib/schemas/index.d.ts +22 -1
- package/dist/lib/schemas/index.js +7 -0
- package/dist/lib/schemas/index.mjs +7 -0
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/server/create-adcp-server.d.mts +18 -0
- package/dist/lib/server/create-adcp-server.d.ts +18 -0
- package/dist/lib/server/create-adcp-server.js +52 -2
- package/dist/lib/server/create-adcp-server.mjs +52 -2
- package/dist/lib/server/decisioning/account.d.mts +1 -1
- package/dist/lib/server/decisioning/account.d.ts +1 -1
- package/dist/lib/server/decisioning/index.d.mts +1 -0
- package/dist/lib/server/decisioning/index.d.ts +1 -0
- package/dist/lib/server/decisioning/platform.d.mts +3 -0
- package/dist/lib/server/decisioning/platform.d.ts +3 -0
- package/dist/lib/server/decisioning/runtime/from-platform.js +88 -10
- package/dist/lib/server/decisioning/runtime/from-platform.mjs +88 -10
- package/dist/lib/server/decisioning/specialisms/reporting.d.mts +38 -0
- package/dist/lib/server/decisioning/specialisms/reporting.d.ts +38 -0
- package/dist/lib/{types/v3-1-beta/index.js → server/decisioning/specialisms/reporting.js} +2 -8
- package/dist/lib/server/decisioning/specialisms/reporting.mjs +0 -0
- package/dist/lib/server/index.d.mts +4 -2
- package/dist/lib/server/index.d.ts +4 -2
- package/dist/lib/server/index.js +5 -0
- package/dist/lib/server/index.mjs +4 -0
- package/dist/lib/server/media-buy-action-resolver.d.mts +63 -0
- package/dist/lib/server/media-buy-action-resolver.d.ts +63 -0
- package/dist/lib/server/media-buy-action-resolver.js +297 -0
- package/dist/lib/server/media-buy-action-resolver.mjs +285 -0
- package/dist/lib/server/media-buy-actions.d.mts +7 -8
- package/dist/lib/server/media-buy-actions.d.ts +7 -8
- package/dist/lib/server/media-buy-actions.js +57 -14
- package/dist/lib/server/media-buy-actions.mjs +63 -14
- package/dist/lib/server/notification-subscriptions/index.d.mts +2 -2
- package/dist/lib/server/notification-subscriptions/index.d.ts +2 -2
- package/dist/lib/server/notification-subscriptions/index.js +2 -0
- package/dist/lib/server/notification-subscriptions/index.mjs +2 -0
- package/dist/lib/server/notification-subscriptions/postgres-runtime.d.mts +3 -1
- package/dist/lib/server/notification-subscriptions/postgres-runtime.d.ts +3 -1
- package/dist/lib/server/notification-subscriptions/postgres-runtime.js +1 -0
- package/dist/lib/server/notification-subscriptions/postgres-runtime.mjs +1 -0
- package/dist/lib/server/notification-subscriptions/runtime.d.mts +12 -1
- package/dist/lib/server/notification-subscriptions/runtime.d.ts +12 -1
- package/dist/lib/server/notification-subscriptions/runtime.js +102 -16
- package/dist/lib/server/notification-subscriptions/runtime.mjs +101 -16
- package/dist/lib/server/notification-subscriptions/types.d.mts +104 -1
- package/dist/lib/server/notification-subscriptions/types.d.ts +104 -1
- package/dist/lib/server/webhook-emitter.d.mts +40 -5
- package/dist/lib/server/webhook-emitter.d.ts +40 -5
- package/dist/lib/server/webhook-emitter.js +23 -12
- package/dist/lib/server/webhook-emitter.mjs +23 -12
- package/dist/lib/storage/interfaces.d.mts +14 -2
- package/dist/lib/storage/interfaces.d.ts +14 -2
- package/dist/lib/testing/compliance/comply.d.mts +9 -3
- package/dist/lib/testing/compliance/comply.d.ts +9 -3
- package/dist/lib/testing/compliance/comply.js +158 -3
- package/dist/lib/testing/compliance/comply.mjs +159 -4
- package/dist/lib/testing/compliance/storyboard-tracks.js +7 -5
- package/dist/lib/testing/compliance/storyboard-tracks.mjs +7 -5
- package/dist/lib/testing/compliance/summary.js +10 -1
- package/dist/lib/testing/compliance/summary.mjs +10 -1
- package/dist/lib/testing/index.d.mts +1 -1
- package/dist/lib/testing/index.d.ts +1 -1
- package/dist/lib/testing/index.js +2 -0
- package/dist/lib/testing/index.mjs +3 -1
- package/dist/lib/testing/storyboard/agent-routing.d.mts +12 -43
- package/dist/lib/testing/storyboard/agent-routing.d.ts +12 -43
- package/dist/lib/testing/storyboard/agent-routing.js +75 -13
- package/dist/lib/testing/storyboard/agent-routing.mjs +70 -12
- package/dist/lib/testing/storyboard/index.d.mts +1 -0
- package/dist/lib/testing/storyboard/index.d.ts +1 -0
- package/dist/lib/testing/storyboard/index.js +3 -0
- package/dist/lib/testing/storyboard/index.mjs +2 -0
- package/dist/lib/testing/storyboard/junit.d.mts +1 -1
- package/dist/lib/testing/storyboard/junit.d.ts +1 -1
- package/dist/lib/testing/storyboard/junit.js +21 -2
- package/dist/lib/testing/storyboard/junit.mjs +21 -2
- package/dist/lib/testing/storyboard/probes.d.mts +130 -0
- package/dist/lib/testing/storyboard/probes.d.ts +130 -0
- package/dist/lib/testing/storyboard/probes.js +682 -10
- package/dist/lib/testing/storyboard/probes.mjs +683 -3
- package/dist/lib/testing/storyboard/request-signing/builder.d.mts +16 -5
- package/dist/lib/testing/storyboard/request-signing/builder.d.ts +16 -5
- package/dist/lib/testing/storyboard/request-signing/grader.d.mts +44 -1
- package/dist/lib/testing/storyboard/request-signing/grader.d.ts +44 -1
- package/dist/lib/testing/storyboard/request-signing/grader.js +46 -22
- package/dist/lib/testing/storyboard/request-signing/grader.mjs +43 -21
- package/dist/lib/testing/storyboard/request-signing/probe-dispatch.d.mts +86 -11
- package/dist/lib/testing/storyboard/request-signing/probe-dispatch.d.ts +86 -11
- package/dist/lib/testing/storyboard/request-signing/probe-dispatch.js +119 -25
- package/dist/lib/testing/storyboard/request-signing/probe-dispatch.mjs +117 -26
- package/dist/lib/testing/storyboard/request-signing/synthesize.js +22 -2
- package/dist/lib/testing/storyboard/request-signing/synthesize.mjs +22 -2
- package/dist/lib/testing/storyboard/runner.d.mts +103 -0
- package/dist/lib/testing/storyboard/runner.d.ts +103 -0
- package/dist/lib/testing/storyboard/runner.js +1090 -268
- package/dist/lib/testing/storyboard/runner.mjs +1096 -261
- package/dist/lib/testing/storyboard/seeding.d.mts +4 -1
- package/dist/lib/testing/storyboard/seeding.d.ts +4 -1
- package/dist/lib/testing/storyboard/seeding.js +55 -24
- package/dist/lib/testing/storyboard/seeding.mjs +55 -24
- package/dist/lib/testing/storyboard/test-kit.d.mts +37 -3
- package/dist/lib/testing/storyboard/test-kit.d.ts +37 -3
- package/dist/lib/testing/storyboard/test-kit.js +8 -2
- package/dist/lib/testing/storyboard/test-kit.mjs +7 -2
- package/dist/lib/testing/storyboard/types.d.mts +132 -10
- package/dist/lib/testing/storyboard/types.d.ts +132 -10
- package/dist/lib/testing/storyboard/types.js +17 -0
- package/dist/lib/testing/storyboard/types.mjs +16 -0
- package/dist/lib/testing/storyboard/validations.d.mts +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts +1 -1
- package/dist/lib/testing/storyboard/validations.js +16 -0
- package/dist/lib/testing/storyboard/validations.mjs +16 -0
- package/dist/lib/testing/test-controller.d.mts +1 -1
- package/dist/lib/testing/test-controller.d.ts +1 -1
- package/dist/lib/types/buy-products.d.ts +71 -6
- package/dist/lib/types/control-media-buy.d.ts +25 -11
- package/dist/lib/types/core.generated.d.mts +69 -10
- package/dist/lib/types/core.generated.d.ts +69 -10
- package/dist/lib/types/create-media-buy.d.ts +71 -6
- package/dist/lib/types/forward-compat-error-codes.d.mts +7 -6
- package/dist/lib/types/forward-compat-error-codes.d.ts +7 -6
- package/dist/lib/types/index.d.mts +2 -2
- package/dist/lib/types/index.d.ts +2 -2
- package/dist/lib/types/schemas.generated.d.ts +8040 -8116
- package/dist/lib/types/schemas.generated.js +1074 -309
- package/dist/lib/types/schemas.generated.mjs +1068 -309
- package/dist/lib/types/tools.generated.d.mts +65 -6
- package/dist/lib/types/tools.generated.d.ts +65 -6
- package/dist/lib/types/update-media-buy.d.ts +71 -6
- package/dist/lib/utils/redact-secrets.d.mts +1 -1
- package/dist/lib/utils/redact-secrets.d.ts +1 -1
- package/dist/lib/utils/redact-secrets.js +1 -1
- package/dist/lib/utils/redact-secrets.mjs +1 -1
- package/dist/lib/v2/projection/cache-versions.d.mts +0 -1
- package/dist/lib/v2/projection/cache-versions.d.ts +0 -1
- package/dist/lib/v2/projection/cache-versions.js +1 -15
- package/dist/lib/v2/projection/cache-versions.mjs +1 -14
- package/dist/lib/v2/projection/canonical-properties.js +2 -2
- package/dist/lib/v2/projection/canonical-properties.mjs +3 -3
- package/dist/lib/v2/projection/registry.d.mts +1 -1
- package/dist/lib/v2/projection/registry.d.ts +1 -1
- package/dist/lib/v2/projection/registry.js +2 -2
- package/dist/lib/v2/projection/registry.mjs +3 -3
- package/dist/lib/validation/schema-loader.d.mts +9 -1
- package/dist/lib/validation/schema-loader.d.ts +9 -1
- package/dist/lib/validation/schema-loader.js +65 -8
- package/dist/lib/validation/schema-loader.mjs +64 -8
- package/dist/lib/version.d.mts +5 -5
- package/dist/lib/version.d.ts +5 -5
- package/dist/lib/version.js +3 -8
- package/dist/lib/version.mjs +3 -8
- package/dist/lib/wholesale-feed-sync/index.d.mts +1 -0
- package/dist/lib/wholesale-feed-sync/index.d.ts +1 -0
- package/dist/lib/wholesale-feed-sync/protocol-types.d.mts +42 -0
- package/dist/lib/wholesale-feed-sync/protocol-types.d.ts +42 -0
- package/dist/lib/wholesale-feed-sync/protocol-types.js +16 -0
- package/dist/lib/wholesale-feed-sync/protocol-types.mjs +0 -0
- package/dist/lib/wholesale-feed-sync/sync.d.mts +6 -6
- package/dist/lib/wholesale-feed-sync/sync.d.ts +6 -6
- package/dist/lib/wholesale-feed-sync/sync.js +3 -1
- package/dist/lib/wholesale-feed-sync/sync.mjs +3 -1
- package/dist/lib/wholesale-feed-sync/types.d.mts +15 -14
- package/dist/lib/wholesale-feed-sync/types.d.ts +15 -14
- package/dist/lib/wholesale-feed-sync/webhook-notification.d.mts +7 -5
- package/dist/lib/wholesale-feed-sync/webhook-notification.d.ts +7 -5
- package/dist/lib/wholesale-feed-sync/webhook-notification.js +45 -0
- package/dist/lib/wholesale-feed-sync/webhook-notification.mjs +44 -0
- package/docs/CLI.md +1 -1
- package/docs/TYPE-SUMMARY.md +118 -4
- package/docs/guides/ASYNC-API-REFERENCE.md +62 -1
- package/docs/guides/ASYNC-DEVELOPER-GUIDE.md +25 -0
- package/docs/guides/ASYNC-DOCUMENTATION-INDEX.md +3 -0
- package/docs/guides/BUYER-QUICKSTART-3.2.md +5 -0
- package/docs/guides/DURABLE-BUYER-WRITES.md +337 -0
- package/docs/guides/EXISTING-PLATFORM.md +80 -1
- package/docs/guides/MEDIA-BUY-3.2-COMPATIBILITY.md +57 -0
- package/docs/guides/MEDIA-BUY-ACTION-ASSESSMENT.md +300 -0
- package/docs/guides/REPORTING-LEDGER.md +654 -3
- package/docs/guides/REPORTING-RECONCILIATION.md +50 -0
- package/docs/guides/REPORTING-SOURCE-EXECUTOR.md +63 -1
- package/docs/guides/SELLER-QUICKSTART-3.2.md +2 -0
- package/docs/guides/VALIDATE-YOUR-AGENT.md +35 -0
- package/docs/guides/idempotency-crash-recovery.md +4 -0
- package/docs/llms.txt +4 -2
- package/docs/migration-14.x-rc-worksheet.md +60 -5
- package/docs/migration-7.9-to-7.10.md +7 -5
- package/examples/README.md +17 -0
- package/examples/durable-buyer-writes/caller.ts +745 -0
- package/examples/durable-buyer-writes/worker.ts +240 -0
- package/examples/reliable-reporting-service/README.md +100 -0
- package/examples/seller-3.2-starter.ts +7 -8
- package/examples/targeting-input-existing-platform.ts +153 -0
- package/package.json +41 -29
- package/skills/build-seller-agent/SKILL.md +2 -0
- package/skills/build-seller-agent/specialisms/signed-requests.md +11 -1
- package/skills/cross-cutting.md +28 -11
- package/compliance/cache/3.1.0-beta.7/domains/brand/index.yaml +0 -160
- package/compliance/cache/3.1.0-beta.7/domains/brand/scenarios/distributed_brand_resolution.yaml +0 -415
- package/compliance/cache/3.1.0-beta.7/domains/brand/scenarios/single_side_trust_extension.yaml +0 -454
- package/compliance/cache/3.1.0-beta.7/domains/creative/index.yaml +0 -339
- package/compliance/cache/3.1.0-beta.7/domains/creative/scenarios/billing_out_of_band.yaml +0 -153
- package/compliance/cache/3.1.0-beta.7/domains/creative/scenarios/creative_lifecycle_webhooks.yaml +0 -389
- package/compliance/cache/3.1.0-beta.7/domains/creative/scenarios/native_in_feed.yaml +0 -543
- package/compliance/cache/3.1.0-beta.7/domains/governance/index.yaml +0 -682
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/index.yaml +0 -781
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/audience_buy_flow.yaml +0 -380
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/available_actions.yaml +0 -565
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/billing_finality_delivery.yaml +0 -354
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/canonical_formats.yaml +0 -711
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/clicks_buy_flow.yaml +0 -264
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/completed_views_buy_flow.yaml +0 -344
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/create_media_buy_async.yaml +0 -234
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/creative_fate_after_cancellation.yaml +0 -419
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/creative_reception.yaml +0 -247
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/delivery_reporting.yaml +0 -357
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/dependency_impairment.yaml +0 -633
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/dependency_impairment_cardinality.yaml +0 -800
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/event_dedup_flow.yaml +0 -399
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/frequency_cap_enforcement.yaml +0 -309
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/governance_approved.yaml +0 -214
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/governance_conditions.yaml +0 -199
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/governance_denied.yaml +0 -204
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/governance_denied_recovery.yaml +0 -252
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/invalid_transitions.yaml +0 -289
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/inventory_list_no_match.yaml +0 -148
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/inventory_list_targeting.yaml +0 -276
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/measurement_accountability.yaml +0 -244
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/measurement_terms_rejected.yaml +0 -203
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/pending_creatives_to_start.yaml +0 -274
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/per_creative_conversion_attribution.yaml +0 -500
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/performance_buy_flow.yaml +0 -428
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/performance_buy_flow_roas.yaml +0 -470
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/product_signal_targeting.yaml +0 -373
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/proposal_finalize.yaml +0 -399
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/proposal_finalize_asap_timing.yaml +0 -264
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/proposal_not_found_errors.yaml +0 -257
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/provenance_audit_observation.yaml +0 -333
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/provenance_enforcement.yaml +0 -517
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/provenance_truth_of_claim.yaml +0 -294
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/reach_buy_flow.yaml +0 -823
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/refine_finalize_exclusivity.yaml +0 -360
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/refine_products.yaml +0 -148
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/vendor_metric_accountability.yaml +0 -293
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/vendor_metric_catalog_precondition.yaml +0 -307
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/scenarios/vendor_metric_optimization_flow.yaml +0 -576
- package/compliance/cache/3.1.0-beta.7/domains/media-buy/state-machine.yaml +0 -442
- package/compliance/cache/3.1.0-beta.7/domains/signals/index.yaml +0 -266
- package/compliance/cache/3.1.0-beta.7/domains/sponsored-intelligence/index.yaml +0 -256
- package/compliance/cache/3.1.0-beta.7/index.json +0 -356
- package/compliance/cache/3.1.0-beta.7/protocols/brand/index.yaml +0 -160
- package/compliance/cache/3.1.0-beta.7/protocols/brand/scenarios/distributed_brand_resolution.yaml +0 -415
- package/compliance/cache/3.1.0-beta.7/protocols/brand/scenarios/single_side_trust_extension.yaml +0 -454
- package/compliance/cache/3.1.0-beta.7/protocols/creative/index.yaml +0 -339
- package/compliance/cache/3.1.0-beta.7/protocols/creative/scenarios/billing_out_of_band.yaml +0 -153
- package/compliance/cache/3.1.0-beta.7/protocols/creative/scenarios/creative_lifecycle_webhooks.yaml +0 -389
- package/compliance/cache/3.1.0-beta.7/protocols/creative/scenarios/native_in_feed.yaml +0 -543
- package/compliance/cache/3.1.0-beta.7/protocols/governance/index.yaml +0 -682
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/index.yaml +0 -781
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/audience_buy_flow.yaml +0 -380
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/available_actions.yaml +0 -565
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/billing_finality_delivery.yaml +0 -354
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/canonical_formats.yaml +0 -711
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/clicks_buy_flow.yaml +0 -264
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/completed_views_buy_flow.yaml +0 -344
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/create_media_buy_async.yaml +0 -234
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/creative_fate_after_cancellation.yaml +0 -419
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/creative_reception.yaml +0 -247
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/delivery_reporting.yaml +0 -357
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/dependency_impairment.yaml +0 -633
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/dependency_impairment_cardinality.yaml +0 -800
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/event_dedup_flow.yaml +0 -399
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/frequency_cap_enforcement.yaml +0 -309
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/governance_approved.yaml +0 -214
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/governance_conditions.yaml +0 -199
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/governance_denied.yaml +0 -204
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/governance_denied_recovery.yaml +0 -252
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/invalid_transitions.yaml +0 -289
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/inventory_list_no_match.yaml +0 -148
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/inventory_list_targeting.yaml +0 -276
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/measurement_accountability.yaml +0 -244
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/measurement_terms_rejected.yaml +0 -203
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/pending_creatives_to_start.yaml +0 -274
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/per_creative_conversion_attribution.yaml +0 -500
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/performance_buy_flow.yaml +0 -428
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/performance_buy_flow_roas.yaml +0 -470
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/product_signal_targeting.yaml +0 -373
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/proposal_finalize.yaml +0 -399
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/proposal_finalize_asap_timing.yaml +0 -264
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/proposal_not_found_errors.yaml +0 -257
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/provenance_audit_observation.yaml +0 -333
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/provenance_enforcement.yaml +0 -517
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/provenance_truth_of_claim.yaml +0 -294
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/reach_buy_flow.yaml +0 -823
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/refine_finalize_exclusivity.yaml +0 -360
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/refine_products.yaml +0 -148
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/vendor_metric_accountability.yaml +0 -293
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/vendor_metric_catalog_precondition.yaml +0 -307
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/scenarios/vendor_metric_optimization_flow.yaml +0 -576
- package/compliance/cache/3.1.0-beta.7/protocols/media-buy/state-machine.yaml +0 -442
- package/compliance/cache/3.1.0-beta.7/protocols/signals/index.yaml +0 -266
- package/compliance/cache/3.1.0-beta.7/protocols/sponsored-intelligence/index.yaml +0 -256
- package/compliance/cache/3.1.0-beta.7/specialisms/audience-sync/index.yaml +0 -313
- package/compliance/cache/3.1.0-beta.7/specialisms/brand-rights/index.yaml +0 -350
- package/compliance/cache/3.1.0-beta.7/specialisms/brand-rights/scenarios/governance_denied.yaml +0 -226
- package/compliance/cache/3.1.0-beta.7/specialisms/collection-lists/index.yaml +0 -359
- package/compliance/cache/3.1.0-beta.7/specialisms/content-standards/index.yaml +0 -572
- package/compliance/cache/3.1.0-beta.7/specialisms/creative-ad-server/index.yaml +0 -409
- package/compliance/cache/3.1.0-beta.7/specialisms/creative-generative/generative-seller.yaml +0 -807
- package/compliance/cache/3.1.0-beta.7/specialisms/creative-generative/index.yaml +0 -758
- package/compliance/cache/3.1.0-beta.7/specialisms/creative-template/index.yaml +0 -510
- package/compliance/cache/3.1.0-beta.7/specialisms/governance-aware-seller/index.yaml +0 -143
- package/compliance/cache/3.1.0-beta.7/specialisms/governance-aware-seller/scenarios/governance_multi_agent_rejected.yaml +0 -117
- package/compliance/cache/3.1.0-beta.7/specialisms/governance-delivery-monitor/index.yaml +0 -441
- package/compliance/cache/3.1.0-beta.7/specialisms/governance-spend-authority/denied.yaml +0 -221
- package/compliance/cache/3.1.0-beta.7/specialisms/governance-spend-authority/index.yaml +0 -330
- package/compliance/cache/3.1.0-beta.7/specialisms/property-lists/index.yaml +0 -482
- package/compliance/cache/3.1.0-beta.7/specialisms/sales-broadcast-tv/index.yaml +0 -738
- package/compliance/cache/3.1.0-beta.7/specialisms/sales-catalog-driven/index.yaml +0 -840
- package/compliance/cache/3.1.0-beta.7/specialisms/sales-guaranteed/index.yaml +0 -601
- package/compliance/cache/3.1.0-beta.7/specialisms/sales-non-guaranteed/index.yaml +0 -546
- package/compliance/cache/3.1.0-beta.7/specialisms/sales-proposal-mode/index.yaml +0 -586
- package/compliance/cache/3.1.0-beta.7/specialisms/sales-social/index.yaml +0 -919
- package/compliance/cache/3.1.0-beta.7/specialisms/signal-marketplace/index.yaml +0 -424
- package/compliance/cache/3.1.0-beta.7/specialisms/signal-marketplace/scenarios/governance_denied.yaml +0 -211
- package/compliance/cache/3.1.0-beta.7/specialisms/signal-owned/index.yaml +0 -317
- package/compliance/cache/3.1.0-beta.7/specialisms/sponsored-intelligence/index.yaml +0 -59
- package/compliance/cache/3.1.0-beta.7/test-kits/acme-outdoor-live.yaml +0 -78
- package/compliance/cache/3.1.0-beta.7/test-kits/acme-outdoor.yaml +0 -223
- package/compliance/cache/3.1.0-beta.7/test-kits/billing-gate-runner.yaml +0 -115
- package/compliance/cache/3.1.0-beta.7/test-kits/bistro-oranje.yaml +0 -126
- package/compliance/cache/3.1.0-beta.7/test-kits/distributed-brand-runner.yaml +0 -281
- package/compliance/cache/3.1.0-beta.7/test-kits/nova-motors.yaml +0 -262
- package/compliance/cache/3.1.0-beta.7/test-kits/osei-natural.yaml +0 -126
- package/compliance/cache/3.1.0-beta.7/test-kits/parallel-dispatch-runner.yaml +0 -196
- package/compliance/cache/3.1.0-beta.7/test-kits/rate-limit-trip-runner.yaml +0 -172
- package/compliance/cache/3.1.0-beta.7/test-kits/signed-requests-runner.yaml +0 -155
- package/compliance/cache/3.1.0-beta.7/test-kits/single-side-trust-runner.yaml +0 -294
- package/compliance/cache/3.1.0-beta.7/test-kits/substitution-observer-runner.yaml +0 -688
- package/compliance/cache/3.1.0-beta.7/test-kits/summit-foods.yaml +0 -125
- package/compliance/cache/3.1.0-beta.7/test-kits/webhook-receiver-runner.yaml +0 -265
- package/compliance/cache/3.1.0-beta.7/test-vectors/plan-hash/001-minimal-plan.json +0 -43
- package/compliance/cache/3.1.0-beta.7/test-vectors/plan-hash/002-full-plan.json +0 -217
- package/compliance/cache/3.1.0-beta.7/test-vectors/plan-hash/003-bookkeeping-stripped.json +0 -60
- package/compliance/cache/3.1.0-beta.7/test-vectors/plan-hash/004a-human-review-omitted.json +0 -43
- package/compliance/cache/3.1.0-beta.7/test-vectors/plan-hash/004b-human-review-explicit-null.json +0 -49
- package/compliance/cache/3.1.0-beta.7/test-vectors/plan-hash/005a-policy-categories-order-1.json +0 -53
- package/compliance/cache/3.1.0-beta.7/test-vectors/plan-hash/005b-policy-categories-order-2.json +0 -57
- package/compliance/cache/3.1.0-beta.7/test-vectors/plan-hash/006a-ext-trace-v1.json +0 -49
- package/compliance/cache/3.1.0-beta.7/test-vectors/plan-hash/006b-ext-trace-v2.json +0 -53
- package/compliance/cache/3.1.0-beta.7/test-vectors/plan-hash/007-unicode-objectives.json +0 -43
- package/compliance/cache/3.1.0-beta.7/test-vectors/plan-hash/008-numeric-canonicalization.json +0 -65
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/README.md +0 -220
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/canonicalization.json +0 -241
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/keys.json +0 -60
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/negative/001-no-signature-header.json +0 -24
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/negative/002-wrong-tag.json +0 -26
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/negative/003-expired-signature.json +0 -26
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/negative/004-window-too-long.json +0 -26
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/negative/005-alg-not-allowed.json +0 -26
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/negative/006-missing-covered-component.json +0 -26
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/negative/007-missing-content-digest.json +0 -26
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/negative/008-unknown-keyid.json +0 -26
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/negative/009-key-ops-missing-verify.json +0 -27
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/negative/010-content-digest-mismatch.json +0 -33
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/negative/011-malformed-header.json +0 -27
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/negative/012-missing-expires-param.json +0 -26
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/negative/013-expires-le-created.json +0 -27
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/negative/014-missing-nonce-param.json +0 -27
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/negative/015-signature-invalid.json +0 -28
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/negative/016-replayed-nonce.json +0 -35
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/negative/017-key-revoked.json +0 -38
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/negative/018-digest-covered-when-forbidden.json +0 -28
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/negative/019-signature-without-signature-input.json +0 -26
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/negative/020-rate-abuse.json +0 -34
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/negative/021-duplicate-signature-input-label.json +0 -31
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/negative/022-multi-valued-content-type.json +0 -31
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/negative/023-multi-valued-content-digest.json +0 -32
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/negative/024-unquoted-string-param.json +0 -31
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/negative/025-jwk-alg-crv-mismatch.json +0 -43
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/negative/026-non-ascii-host.json +0 -31
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/negative/027-webhook-registration-authentication-unsigned.json +0 -25
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/negative/028-unsigned-protocol-method-required.json +0 -26
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/positive/001-basic-post.json +0 -30
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/positive/002-post-with-content-digest.json +0 -31
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/positive/003-es256-post.json +0 -30
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/positive/004-multiple-signature-labels.json +0 -26
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/positive/005-default-port-stripped.json +0 -30
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/positive/006-dot-segment-path.json +0 -30
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/positive/007-query-byte-preserved.json +0 -30
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/positive/008-percent-encoded-path.json +0 -30
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/positive/009-percent-encoded-unreserved-decoded.json +0 -30
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/positive/010-percent-encoded-slash-preserved.json +0 -30
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/positive/011-ipv6-authority.json +0 -30
- package/compliance/cache/3.1.0-beta.7/test-vectors/request-signing/positive/012-ipv6-authority-default-port-stripped.json +0 -30
- package/compliance/cache/3.1.0-beta.7/test-vectors/webhook-signing/README.md +0 -211
- package/compliance/cache/3.1.0-beta.7/test-vectors/webhook-signing/keys.json +0 -61
- package/compliance/cache/3.1.0-beta.7/test-vectors/webhook-signing/negative/001-wrong-tag.json +0 -26
- package/compliance/cache/3.1.0-beta.7/test-vectors/webhook-signing/negative/002-expired-signature.json +0 -26
- package/compliance/cache/3.1.0-beta.7/test-vectors/webhook-signing/negative/003-window-too-long.json +0 -26
- package/compliance/cache/3.1.0-beta.7/test-vectors/webhook-signing/negative/004-alg-not-allowed.json +0 -26
- package/compliance/cache/3.1.0-beta.7/test-vectors/webhook-signing/negative/005-missing-authority-component.json +0 -26
- package/compliance/cache/3.1.0-beta.7/test-vectors/webhook-signing/negative/006-missing-content-digest.json +0 -25
- package/compliance/cache/3.1.0-beta.7/test-vectors/webhook-signing/negative/007-unknown-keyid.json +0 -26
- package/compliance/cache/3.1.0-beta.7/test-vectors/webhook-signing/negative/008-wrong-adcp-use.json +0 -26
- package/compliance/cache/3.1.0-beta.7/test-vectors/webhook-signing/negative/009-content-digest-mismatch.json +0 -26
- package/compliance/cache/3.1.0-beta.7/test-vectors/webhook-signing/negative/010-malformed-signature-input.json +0 -26
- package/compliance/cache/3.1.0-beta.7/test-vectors/webhook-signing/negative/011-signature-without-input.json +0 -25
- package/compliance/cache/3.1.0-beta.7/test-vectors/webhook-signing/negative/012-missing-expires-param.json +0 -26
- package/compliance/cache/3.1.0-beta.7/test-vectors/webhook-signing/negative/013-expires-le-created.json +0 -26
- package/compliance/cache/3.1.0-beta.7/test-vectors/webhook-signing/negative/014-missing-nonce-param.json +0 -26
- package/compliance/cache/3.1.0-beta.7/test-vectors/webhook-signing/negative/015-signature-invalid.json +0 -26
- package/compliance/cache/3.1.0-beta.7/test-vectors/webhook-signing/negative/016-replayed-nonce.json +0 -37
- package/compliance/cache/3.1.0-beta.7/test-vectors/webhook-signing/negative/017-key-revoked.json +0 -32
- package/compliance/cache/3.1.0-beta.7/test-vectors/webhook-signing/negative/018-rate-abuse.json +0 -33
- package/compliance/cache/3.1.0-beta.7/test-vectors/webhook-signing/negative/019-revocation-stale.json +0 -32
- package/compliance/cache/3.1.0-beta.7/test-vectors/webhook-signing/negative/020-key-ops-missing-verify.json +0 -41
- package/compliance/cache/3.1.0-beta.7/test-vectors/webhook-signing/negative/021-base64-alphabet-mixing.json +0 -26
- package/compliance/cache/3.1.0-beta.7/test-vectors/webhook-signing/positive/001-basic-post.json +0 -24
- package/compliance/cache/3.1.0-beta.7/test-vectors/webhook-signing/positive/002-es256-post.json +0 -24
- package/compliance/cache/3.1.0-beta.7/test-vectors/webhook-signing/positive/003-multiple-signature-labels.json +0 -24
- package/compliance/cache/3.1.0-beta.7/test-vectors/webhook-signing/positive/004-default-port-stripped.json +0 -24
- package/compliance/cache/3.1.0-beta.7/test-vectors/webhook-signing/positive/005-percent-encoded-path.json +0 -24
- package/compliance/cache/3.1.0-beta.7/test-vectors/webhook-signing/positive/006-query-byte-preserved.json +0 -24
- package/compliance/cache/3.1.0-beta.7/test-vectors/webhook-signing/positive/007-body-without-idempotency-key.json +0 -25
- package/compliance/cache/3.1.0-beta.7/universal/billing-gate-dispatch.yaml +0 -450
- package/compliance/cache/3.1.0-beta.7/universal/canonical-format-validate-input.yaml +0 -640
- package/compliance/cache/3.1.0-beta.7/universal/capability-discovery.yaml +0 -125
- package/compliance/cache/3.1.0-beta.7/universal/collection-lists-pagination-integrity.yaml +0 -306
- package/compliance/cache/3.1.0-beta.7/universal/comply-controller-mode-gate.yaml +0 -141
- package/compliance/cache/3.1.0-beta.7/universal/content-standards-pagination-integrity.yaml +0 -326
- package/compliance/cache/3.1.0-beta.7/universal/deterministic-testing.yaml +0 -1430
- package/compliance/cache/3.1.0-beta.7/universal/error-compliance-signals.yaml +0 -377
- package/compliance/cache/3.1.0-beta.7/universal/error-compliance.yaml +0 -528
- package/compliance/cache/3.1.0-beta.7/universal/fictional-entities.yaml +0 -307
- package/compliance/cache/3.1.0-beta.7/universal/get-media-buys-pagination-integrity.yaml +0 -160
- package/compliance/cache/3.1.0-beta.7/universal/get-signals-pagination-integrity.yaml +0 -211
- package/compliance/cache/3.1.0-beta.7/universal/idempotency.yaml +0 -861
- package/compliance/cache/3.1.0-beta.7/universal/notification-config-event-scope.yaml +0 -119
- package/compliance/cache/3.1.0-beta.7/universal/notification-config-lifecycle.yaml +0 -337
- package/compliance/cache/3.1.0-beta.7/universal/notification-config-rejections.yaml +0 -107
- package/compliance/cache/3.1.0-beta.7/universal/pagination-integrity-creative-formats.yaml +0 -265
- package/compliance/cache/3.1.0-beta.7/universal/pagination-integrity-list-accounts.yaml +0 -245
- package/compliance/cache/3.1.0-beta.7/universal/pagination-integrity.yaml +0 -263
- package/compliance/cache/3.1.0-beta.7/universal/property-lists-pagination-integrity.yaml +0 -307
- package/compliance/cache/3.1.0-beta.7/universal/read-tool-idempotency.yaml +0 -405
- package/compliance/cache/3.1.0-beta.7/universal/runner-output-contract.yaml +0 -1266
- package/compliance/cache/3.1.0-beta.7/universal/schema-validation-signals.yaml +0 -181
- package/compliance/cache/3.1.0-beta.7/universal/schema-validation.yaml +0 -548
- package/compliance/cache/3.1.0-beta.7/universal/security.yaml +0 -539
- package/compliance/cache/3.1.0-beta.7/universal/signed-requests.yaml +0 -217
- package/compliance/cache/3.1.0-beta.7/universal/stale-response-advisory.yaml +0 -295
- package/compliance/cache/3.1.0-beta.7/universal/storyboard-schema.yaml +0 -2136
- package/compliance/cache/3.1.0-beta.7/universal/v3-envelope-integrity.yaml +0 -117
- package/compliance/cache/3.1.0-beta.7/universal/version-negotiation.yaml +0 -130
- package/compliance/cache/3.1.0-beta.7/universal/webhook-emission.yaml +0 -411
- package/compliance/cache/3.1.0-beta.7/universal/wholesale-feed-bulk-webhooks.yaml +0 -82
- package/compliance/cache/3.1.0-beta.7/universal/wholesale-feed-product-webhooks.yaml +0 -83
- package/compliance/cache/3.1.0-beta.7/universal/wholesale-feed-products.yaml +0 -151
- package/compliance/cache/3.1.0-beta.7/universal/wholesale-feed-signal-webhooks.yaml +0 -83
- package/compliance/cache/3.1.0-beta.7/universal/wholesale-feed-signals.yaml +0 -149
- package/dist/lib/types/v3-1-beta/index.d.mts +0 -1
- package/dist/lib/types/v3-1-beta/index.d.ts +0 -1
- package/dist/lib/types/v3-1-beta/index.mjs +0 -1
- package/dist/lib/types/v3-1-beta/tools.generated.d.mts +0 -26788
- package/dist/lib/types/v3-1-beta/tools.generated.d.ts +0 -26788
- /package/dist/lib/{types/v3-1-beta/tools.generated.mjs → media-buy/action-types.mjs} +0 -0
|
@@ -28,8 +28,15 @@ __export(reconciliation_exports, {
|
|
|
28
28
|
module.exports = __toCommonJS(reconciliation_exports);
|
|
29
29
|
var import_crypto = require("crypto");
|
|
30
30
|
var import_idempotency = require('../utils/idempotency.js');
|
|
31
|
+
var import_jcs = require('../utils/jcs.js');
|
|
32
|
+
var import_content_mismatch = require('./content-mismatch.js');
|
|
31
33
|
var import_evidence = require('./evidence.js');
|
|
32
34
|
var import_inspection = require('./inspection.js');
|
|
35
|
+
const CONSUMER_STATUS_BATCH_MAX = 100;
|
|
36
|
+
const MAX_CONSUMED_REVISION_BYTES = 256 * 1024 * 1024;
|
|
37
|
+
const MAX_ROW_ESTIMATE_CONTAINERS = 262144;
|
|
38
|
+
const OVERFLOWED_INSTANT = "\0overflow";
|
|
39
|
+
const MAX_ROW_ESTIMATE_DEPTH = 64;
|
|
33
40
|
class ReportingReconciliationError extends Error {
|
|
34
41
|
constructor(code, message) {
|
|
35
42
|
super(message);
|
|
@@ -252,6 +259,7 @@ async function loadReportingLedger(client, request, maxSnapshotRestarts = 2, lim
|
|
|
252
259
|
const revisions = /* @__PURE__ */ new Map();
|
|
253
260
|
const materializations = /* @__PURE__ */ new Map();
|
|
254
261
|
const receipts = /* @__PURE__ */ new Map();
|
|
262
|
+
const consumerStatuses = /* @__PURE__ */ new Map();
|
|
255
263
|
const seenCursors = /* @__PURE__ */ new Set();
|
|
256
264
|
let cursor;
|
|
257
265
|
let snapshotId;
|
|
@@ -341,6 +349,14 @@ async function loadReportingLedger(client, request, maxSnapshotRestarts = 2, lim
|
|
|
341
349
|
for (const item of response.materializations ?? [])
|
|
342
350
|
addImmutable(materializations, item.reporting_materialization_id, item, "materialization");
|
|
343
351
|
for (const item of response.receipts ?? []) addImmutable(receipts, item.reporting_receipt_id, item, "receipt");
|
|
352
|
+
for (const item of response.consumer_statuses ?? [])
|
|
353
|
+
addImmutable(consumerStatuses, item.reporting_status_id, item, "consumer status");
|
|
354
|
+
if (consumerStatuses.size > maxRecords) {
|
|
355
|
+
throw new ReportingReconciliationError(
|
|
356
|
+
"LEDGER_LIMIT_EXCEEDED",
|
|
357
|
+
"reporting consumer status history exceeds record limit"
|
|
358
|
+
);
|
|
359
|
+
}
|
|
344
360
|
if (obligations.size + revisions.size + materializations.size + receipts.size > maxRecords) {
|
|
345
361
|
throw new ReportingReconciliationError("LEDGER_LIMIT_EXCEEDED", "reporting ledger exceeds record limit");
|
|
346
362
|
}
|
|
@@ -370,6 +386,7 @@ async function loadReportingLedger(client, request, maxSnapshotRestarts = 2, lim
|
|
|
370
386
|
ledgerAsOf,
|
|
371
387
|
accountId,
|
|
372
388
|
scope,
|
|
389
|
+
consumerStatuses: [...consumerStatuses.values()],
|
|
373
390
|
obligations: [...obligations.values()],
|
|
374
391
|
revisions: [...revisions.values()],
|
|
375
392
|
materializations: [...materializations.values()],
|
|
@@ -510,7 +527,7 @@ function selectCurrent(obligation, ledger, expected) {
|
|
|
510
527
|
if (revision.account_id !== obligation.account_id || revision.report_definition_id !== obligation.report_definition_id || revision.reporting_profile !== obligation.reporting_profile || !Array.isArray(revision.media_buy_ids) || !Array.isArray(obligation.media_buy_ids) || !same([...revision.media_buy_ids].sort(), [...obligation.media_buy_ids].sort()) || !same(revision.period, obligation.period)) {
|
|
511
528
|
reasons.push("REVISION_SCOPE_MISMATCH");
|
|
512
529
|
}
|
|
513
|
-
if (obligation.scope_resolved_at !== obligation.period
|
|
530
|
+
if (obligation.scope_resolved_at !== obligation.period?.end) reasons.push("SCOPE_CUTOFF_MISMATCH");
|
|
514
531
|
if (!isReportingCoverageEvidence(obligation.coverage) || obligation.coverage.evaluated_at !== obligation.scope_resolved_at || !sameStringSet(obligation.coverage.media_buy_ids, obligation.media_buy_ids ?? []) || !isReportingCoverageEvidence(revision.coverage) || !same(revision.coverage, obligation.coverage)) {
|
|
515
532
|
reasons.push("COVERAGE_SCOPE_MISMATCH");
|
|
516
533
|
}
|
|
@@ -594,7 +611,505 @@ function selectCurrent(obligation, ledger, expected) {
|
|
|
594
611
|
function countMismatch(declared, observed, required) {
|
|
595
612
|
return declared === void 0 ? required : declared !== observed;
|
|
596
613
|
}
|
|
614
|
+
function planReportingConsumerStatuses(ledger, expectedPeriods, missingExpectedPeriods, now) {
|
|
615
|
+
const missing = new Set(missingExpectedPeriods);
|
|
616
|
+
return expectedPeriods.map((expected) => {
|
|
617
|
+
const obligationForPeriod = ledger.obligations.find(
|
|
618
|
+
(candidate) => expectedPeriodMatches(expected, candidate, ledger)
|
|
619
|
+
);
|
|
620
|
+
const declaredSourceTimezone = obligationForPeriod?.period?.source_timezone;
|
|
621
|
+
const resolvedSourceTimezone = ianaTimeZone(expected.periodSourceTimezone) ?? ianaTimeZone(declaredSourceTimezone);
|
|
622
|
+
const periodIdentityUnknown = resolvedSourceTimezone === void 0 && declaredSourceTimezone !== void 0;
|
|
623
|
+
const period = {
|
|
624
|
+
start: expected.periodStart,
|
|
625
|
+
end: expected.periodEnd,
|
|
626
|
+
source_timezone: resolvedSourceTimezone ?? "UTC"
|
|
627
|
+
};
|
|
628
|
+
const base = {
|
|
629
|
+
deliveryConfigId: expected.deliveryConfigId,
|
|
630
|
+
deliveryConfigVersion: expected.deliveryConfigVersion,
|
|
631
|
+
reportDefinitionId: expected.reportDefinitionId,
|
|
632
|
+
period
|
|
633
|
+
};
|
|
634
|
+
const derivedExpectedAt = reportingExpectedAt(obligationForPeriod, expected);
|
|
635
|
+
const expectedAtOverflowed = derivedExpectedAt === OVERFLOWED_INSTANT;
|
|
636
|
+
const expectedAt = expectedAtOverflowed || derivedExpectedAt === void 0 ? void 0 : latestInstant([derivedExpectedAt, expected.periodEnd]);
|
|
637
|
+
const declaredExpectedAt = obligationForPeriod?.expected_at;
|
|
638
|
+
const malformedExpectedAt = expectedAt === void 0 && !expectedAtOverflowed && declaredExpectedAt !== void 0 ? typeof declaredExpectedAt === "string" ? declaredExpectedAt : `<${typeof declaredExpectedAt}>` : void 0;
|
|
639
|
+
const schedule = consumerStatusSchedule(expectedAt, expected, now, malformedExpectedAt, expectedAtOverflowed);
|
|
640
|
+
const pinnedExpectation = pinnedExpectedAt(expected);
|
|
641
|
+
const deadlineBeyondPin = expectedAt !== void 0 && pinnedExpectation !== void 0 && Date.parse(expectedAt) > Date.parse(pinnedExpectation) + (expected.automatedRecoveryWindowSeconds ?? 0) * 1e3 ? { declared: expectedAt, pinned: pinnedExpectation } : void 0;
|
|
642
|
+
const leaf = currentConsumerLeaf(
|
|
643
|
+
ledger,
|
|
644
|
+
base,
|
|
645
|
+
obligationForPeriod?.current_consumer_status_id
|
|
646
|
+
);
|
|
647
|
+
const establishedAt = latestInstant([expectedAt, expected.periodEnd]) ?? expected.periodEnd;
|
|
648
|
+
if (missing.has(expected) || !obligationForPeriod) {
|
|
649
|
+
return finalizeConsumerStatusPlan(
|
|
650
|
+
{
|
|
651
|
+
...base,
|
|
652
|
+
...schedule,
|
|
653
|
+
...periodIdentityUnknown ? { periodIdentityUnknown: true } : {},
|
|
654
|
+
...deadlineBeyondPin ? { deadlineBeyondPin } : {},
|
|
655
|
+
...deadlineBeyondPin ? { deadlineBeyondPin } : {},
|
|
656
|
+
consumerStatus: "obligation_missing",
|
|
657
|
+
establishedAt,
|
|
658
|
+
reason: missing.has(expected) ? "the independently expected period is absent from the seller ledger" : "no obligation in the ledger matches this expected period"
|
|
659
|
+
},
|
|
660
|
+
leaf,
|
|
661
|
+
now
|
|
662
|
+
);
|
|
663
|
+
}
|
|
664
|
+
const obligation = obligationForPeriod;
|
|
665
|
+
const selected = selectCurrent(obligation, ledger, expected);
|
|
666
|
+
const revision = selected.revision;
|
|
667
|
+
const forked = selected.reasons.includes("AMBIGUOUS_REVISION_CHAIN");
|
|
668
|
+
const predecessorMissing = selected.reasons.includes("REVISION_PREDECESSOR_MISSING");
|
|
669
|
+
const indeterminate = forked || predecessorMissing;
|
|
670
|
+
if (!revision || indeterminate) {
|
|
671
|
+
return finalizeConsumerStatusPlan(
|
|
672
|
+
{
|
|
673
|
+
...base,
|
|
674
|
+
...schedule,
|
|
675
|
+
...periodIdentityUnknown ? { periodIdentityUnknown: true } : {},
|
|
676
|
+
...deadlineBeyondPin ? { deadlineBeyondPin } : {},
|
|
677
|
+
...deadlineBeyondPin ? { deadlineBeyondPin } : {},
|
|
678
|
+
reportingObligationId: obligation.reporting_obligation_id,
|
|
679
|
+
consumerStatus: "revision_missing",
|
|
680
|
+
...indeterminate ? { indeterminate: true } : {},
|
|
681
|
+
establishedAt,
|
|
682
|
+
reason: forked ? "the revision chain forks, so no single current revision could be resolved" : predecessorMissing ? `a revision names a predecessor the buyer never saw, so ${revision ? `the head ${boundedDiagnostic(revision.reporting_revision_id)} ` : "no head "}could not be proven current` : "the obligation exists but no required revision was available"
|
|
683
|
+
},
|
|
684
|
+
leaf,
|
|
685
|
+
now
|
|
686
|
+
);
|
|
687
|
+
}
|
|
688
|
+
const disqualifying = selected.reasons.filter((reason) => REVISION_DISQUALIFYING_REASONS.has(reason));
|
|
689
|
+
if (disqualifying.length > 0) {
|
|
690
|
+
return finalizeConsumerStatusPlan(
|
|
691
|
+
{
|
|
692
|
+
...base,
|
|
693
|
+
...schedule,
|
|
694
|
+
...periodIdentityUnknown ? { periodIdentityUnknown: true } : {},
|
|
695
|
+
...deadlineBeyondPin ? { deadlineBeyondPin } : {},
|
|
696
|
+
...deadlineBeyondPin ? { deadlineBeyondPin } : {},
|
|
697
|
+
reportingObligationId: obligation.reporting_obligation_id,
|
|
698
|
+
consumerStatus: "revision_missing",
|
|
699
|
+
establishedAt,
|
|
700
|
+
reason: `the published revision does not satisfy the accepted generation (${disqualifying.join(", ")})`
|
|
701
|
+
},
|
|
702
|
+
leaf,
|
|
703
|
+
now
|
|
704
|
+
);
|
|
705
|
+
}
|
|
706
|
+
const mismatch = (0, import_content_mismatch.detectReportingContentMismatch)(contractFactsFor(obligation, expected), revision);
|
|
707
|
+
return finalizeConsumerStatusPlan(
|
|
708
|
+
{
|
|
709
|
+
...base,
|
|
710
|
+
...schedule,
|
|
711
|
+
...periodIdentityUnknown ? { periodIdentityUnknown: true } : {},
|
|
712
|
+
...deadlineBeyondPin ? { deadlineBeyondPin } : {},
|
|
713
|
+
reportingObligationId: obligation.reporting_obligation_id,
|
|
714
|
+
reportingRevisionId: revision.reporting_revision_id,
|
|
715
|
+
consumerStatus: mismatch ? "content_mismatch" : "received",
|
|
716
|
+
...mismatch ? { mismatchCode: mismatch.mismatchCode } : {},
|
|
717
|
+
// Not the revision's own `observed_at`: `status_as_of` is when the
|
|
718
|
+
// revision became consumable *to this consumer*, and the spec is
|
|
719
|
+
// explicit that sellers must not silently substitute publication time.
|
|
720
|
+
// The floor stands in until the buyer has actually read it.
|
|
721
|
+
establishedAt,
|
|
722
|
+
requiresConsumption: true,
|
|
723
|
+
reason: mismatch?.detail ?? "the named revision is ready to consume and honors every frozen contract fact"
|
|
724
|
+
},
|
|
725
|
+
leaf,
|
|
726
|
+
now
|
|
727
|
+
);
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
const REVISION_DISQUALIFYING_REASONS = /* @__PURE__ */ new Set([
|
|
731
|
+
"FINALITY_NOT_MET",
|
|
732
|
+
"EXPECTED_FINALITY_POLICY_MISMATCH",
|
|
733
|
+
"EXPECTED_CONTRACT_MISMATCH",
|
|
734
|
+
"EXPECTED_CONTRACT_MISSING",
|
|
735
|
+
"REVISION_SCOPE_MISMATCH"
|
|
736
|
+
// Deliberately *not* REVISION_CHAIN_SCOPE_MISMATCH: it fires when any
|
|
737
|
+
// candidate in the chain is off-scope, including a long-superseded one, so a
|
|
738
|
+
// perfectly valid current revision would be reported as missing. Nothing is
|
|
739
|
+
// lost by dropping it — the head-only predicate REVISION_SCOPE_MISMATCH
|
|
740
|
+
// tests the same fields against the revision actually being named.
|
|
741
|
+
//
|
|
742
|
+
// It is a live path, not defence in depth: for a direct-Core obligation the
|
|
743
|
+
// graph assertion does refuse an off-scope revision, but a revision joined
|
|
744
|
+
// through a materialization is checked only for `account_id`, so under
|
|
745
|
+
// managed delivery an off-scope predecessor survives the load and becomes a
|
|
746
|
+
// candidate. The test builds exactly that fixture.
|
|
747
|
+
]);
|
|
748
|
+
function finalizeConsumerStatusPlan(draft, leaf, now) {
|
|
749
|
+
const statusAsOfFloor = latestInstant([draft.establishedAt, usableLeafInstant(leaf.statement, now)]) ?? draft.establishedAt;
|
|
750
|
+
const { establishedAt: _establishedAt, deadlineGap, indeterminate, periodIdentityUnknown, ...carried } = draft;
|
|
751
|
+
const plan = {
|
|
752
|
+
...carried,
|
|
753
|
+
...leaf.statusId ? { supersedesReportingStatusId: leaf.statusId } : {},
|
|
754
|
+
statusAsOfFloor,
|
|
755
|
+
// A statement the buyer can already date is dated now; one that still owes
|
|
756
|
+
// a read is left open for `attestConsumerStatusPlan`.
|
|
757
|
+
...draft.requiresConsumption ? {} : { statusAsOf: statusAsOfFloor }
|
|
758
|
+
};
|
|
759
|
+
if (periodIdentityUnknown) {
|
|
760
|
+
return {
|
|
761
|
+
...plan,
|
|
762
|
+
suppressed: "period_identity_unknown",
|
|
763
|
+
reason: suppressionReason("period_identity_unknown", plan.reason)
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
if (indeterminate) {
|
|
767
|
+
return {
|
|
768
|
+
...plan,
|
|
769
|
+
suppressed: "chain_indeterminate",
|
|
770
|
+
reason: suppressionReason("chain_indeterminate", plan.reason)
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
if (deadlineGap) {
|
|
774
|
+
return {
|
|
775
|
+
...plan,
|
|
776
|
+
suppressed: "deadline_unknown",
|
|
777
|
+
reason: deadlineGapReason(deadlineGap)
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
const suppressed = consumerStatusSuppression(plan, leaf);
|
|
781
|
+
return suppressed ? { ...plan, suppressed, reason: suppressionReason(suppressed, plan.reason) } : plan;
|
|
782
|
+
}
|
|
783
|
+
function deadlineGapReason(gap) {
|
|
784
|
+
switch (gap.cause) {
|
|
785
|
+
case "missing_pin":
|
|
786
|
+
return `no posting deadline: record ExpectedReportingPeriod.${gap.pin} to derive one`;
|
|
787
|
+
case "deadline_overflow":
|
|
788
|
+
return `no posting deadline: ${gap.field} puts it outside the representable range`;
|
|
789
|
+
default:
|
|
790
|
+
return `no posting deadline: the seller's obligation.expected_at (${boundedDiagnostic(gap.value)}) is not a readable instant, and a present expected_at is never overridden locally \u2014 the seller has to correct it`;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
function suppressionReason(suppressed, reason) {
|
|
794
|
+
switch (suppressed) {
|
|
795
|
+
case "unchanged":
|
|
796
|
+
return `not posted: the current leaf already says this (${reason})`;
|
|
797
|
+
case "leaf_undisclosed":
|
|
798
|
+
return "not posted: the buyer's status chain has more than one unsuperseded leaf, or the seller named a current leaf it did not disclose \u2014 either way the buyer cannot tell whether it has anything new to say";
|
|
799
|
+
case "consumption_unavailable":
|
|
800
|
+
return "not posted: no client.getMediaBuyDelivery is wired, so consumption cannot be attested";
|
|
801
|
+
case "posting_unavailable":
|
|
802
|
+
return "not posted: no client.syncReportingStatus is wired, so the buyer cannot append to the status chain";
|
|
803
|
+
case "period_identity_unknown":
|
|
804
|
+
return "not posted: the seller's period.source_timezone is not a recognized IANA zone, and that value is part of the chain's logical key \u2014 record ExpectedReportingPeriod.periodSourceTimezone, or have the seller correct it";
|
|
805
|
+
case "local_budget_exhausted":
|
|
806
|
+
return "not posted: the buyer's own ledgerLimits read budget ran out before the revision could be consumed";
|
|
807
|
+
case "chain_indeterminate":
|
|
808
|
+
return `not posted: ${reason}`;
|
|
809
|
+
default:
|
|
810
|
+
return reason;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
function consumerStatusSuppression(plan, leaf) {
|
|
814
|
+
if (leaf.undisclosed) return "leaf_undisclosed";
|
|
815
|
+
if (plan.requiresConsumption) return void 0;
|
|
816
|
+
if (leaf.statement && sameConsumerStatement(plan, leaf.statement)) return "unchanged";
|
|
817
|
+
return void 0;
|
|
818
|
+
}
|
|
819
|
+
function sameConsumerStatement(plan, statement) {
|
|
820
|
+
return statement.consumer_status === plan.consumerStatus && (statement.reporting_revision_id ?? void 0) === plan.reportingRevisionId && (statement.mismatch_code ?? void 0) === plan.mismatchCode && (statement.failure_code ?? void 0) === plan.failureCode && sameOptionalSha256(statement.observed_revision_content_sha256, plan.observedRevisionContentSha256);
|
|
821
|
+
}
|
|
822
|
+
function sameOptionalSha256(left, right) {
|
|
823
|
+
if (left === void 0 || right === void 0) return left === right;
|
|
824
|
+
return sameSha256(left, right);
|
|
825
|
+
}
|
|
826
|
+
function currentConsumerLeaf(ledger, key, declaredLeafId) {
|
|
827
|
+
const declared = typeof declaredLeafId === "string" ? declaredLeafId : void 0;
|
|
828
|
+
const chain = (ledger.consumerStatuses ?? []).filter(
|
|
829
|
+
(statement2) => statement2.delivery_config_id === key.deliveryConfigId && statement2.delivery_config_version === key.deliveryConfigVersion && statement2.report_definition_id === key.reportDefinitionId && statement2.period?.start === key.period.start && statement2.period?.end === key.period.end
|
|
830
|
+
);
|
|
831
|
+
if (declared) {
|
|
832
|
+
const statement2 = chain.find((candidate) => candidate.reporting_status_id === declared);
|
|
833
|
+
return statement2 ? { statusId: declared, statement: statement2, undisclosed: false } : { statusId: declared, undisclosed: true };
|
|
834
|
+
}
|
|
835
|
+
const superseded = new Set(
|
|
836
|
+
chain.map((statement2) => statement2.supersedes_reporting_status_id).filter((value) => typeof value === "string")
|
|
837
|
+
);
|
|
838
|
+
const leaves = chain.filter((statement2) => !superseded.has(statement2.reporting_status_id));
|
|
839
|
+
if (leaves.length !== 1) return { undisclosed: leaves.length > 1 };
|
|
840
|
+
const statement = leaves[0];
|
|
841
|
+
return { statusId: statement.reporting_status_id, statement, undisclosed: false };
|
|
842
|
+
}
|
|
843
|
+
function reportingExpectedAt(obligation, expected) {
|
|
844
|
+
const declared = normalizedInstant(obligation?.expected_at);
|
|
845
|
+
if (declared !== void 0) return declared;
|
|
846
|
+
if (obligation?.expected_at !== void 0) return void 0;
|
|
847
|
+
const slaSeconds = expected.requiredFinality === "official" ? expected.officialAfterSeconds ?? expected.deliverySlaSeconds : expected.deliverySlaSeconds;
|
|
848
|
+
const periodEnd = Date.parse(expected.periodEnd);
|
|
849
|
+
if (typeof slaSeconds === "number" && Number.isFinite(slaSeconds) && slaSeconds >= 0 && Number.isFinite(periodEnd)) {
|
|
850
|
+
const pinned = periodEnd + slaSeconds * 1e3;
|
|
851
|
+
return isRepresentableInstant(pinned) ? new Date(pinned).toISOString() : OVERFLOWED_INSTANT;
|
|
852
|
+
}
|
|
853
|
+
return reportingScheduledExpectedAt(obligation, expected.periodEnd);
|
|
854
|
+
}
|
|
855
|
+
function pinnedExpectedAt(expected) {
|
|
856
|
+
const slaSeconds = expected.requiredFinality === "official" ? expected.officialAfterSeconds ?? expected.deliverySlaSeconds : expected.deliverySlaSeconds;
|
|
857
|
+
const periodEnd = Date.parse(expected.periodEnd);
|
|
858
|
+
if (typeof slaSeconds !== "number" || !Number.isFinite(slaSeconds) || slaSeconds < 0) return void 0;
|
|
859
|
+
if (!Number.isFinite(periodEnd)) return void 0;
|
|
860
|
+
const pinned = periodEnd + slaSeconds * 1e3;
|
|
861
|
+
return isRepresentableInstant(pinned) ? new Date(pinned).toISOString() : void 0;
|
|
862
|
+
}
|
|
863
|
+
function localExpectedAtPin(expected) {
|
|
864
|
+
return expected.requiredFinality === "official" ? "officialAfterSeconds" : "deliverySlaSeconds";
|
|
865
|
+
}
|
|
866
|
+
function reportingScheduledExpectedAt(obligation, periodEnd) {
|
|
867
|
+
const schedule = obligation?.schedule;
|
|
868
|
+
if (typeof schedule?.delivery_sla !== "string") return void 0;
|
|
869
|
+
const duration = parseIso8601Duration(schedule.delivery_sla);
|
|
870
|
+
const anchor = Date.parse(periodEnd);
|
|
871
|
+
if (!duration || !Number.isFinite(anchor)) return void 0;
|
|
872
|
+
if (duration.years === 0 && duration.months === 0 && duration.days === 0) {
|
|
873
|
+
const exact = anchor + duration.seconds * 1e3;
|
|
874
|
+
return isRepresentableInstant(exact) ? new Date(exact).toISOString() : OVERFLOWED_INSTANT;
|
|
875
|
+
}
|
|
876
|
+
const timeZone = calendarTimeZone(obligation, schedule);
|
|
877
|
+
if (timeZone === void 0) return void 0;
|
|
878
|
+
const shifted = addCalendarDuration(anchor, duration, timeZone);
|
|
879
|
+
if (shifted === void 0) return void 0;
|
|
880
|
+
return isRepresentableInstant(shifted) ? new Date(shifted).toISOString() : OVERFLOWED_INSTANT;
|
|
881
|
+
}
|
|
882
|
+
function calendarTimeZone(obligation, schedule) {
|
|
883
|
+
if (schedule.period_timezone !== void 0) return ianaTimeZone(schedule.period_timezone);
|
|
884
|
+
if (schedule.alignment === "utc") return "UTC";
|
|
885
|
+
return ianaTimeZone(obligation?.period?.source_timezone);
|
|
886
|
+
}
|
|
887
|
+
function ianaTimeZone(value) {
|
|
888
|
+
const name = boundedSourceTimezone(value);
|
|
889
|
+
if (name === void 0) return void 0;
|
|
890
|
+
if (/^[+-]/.test(name)) return void 0;
|
|
891
|
+
try {
|
|
892
|
+
new Intl.DateTimeFormat("en-US", { timeZone: name });
|
|
893
|
+
return name;
|
|
894
|
+
} catch {
|
|
895
|
+
return void 0;
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
function parseIso8601Duration(value) {
|
|
899
|
+
const match = /^P(?=\d|T)(?=.*\d)(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)D)?(?:T(?=\d)(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?)?$/.exec(value);
|
|
900
|
+
if (!match) return void 0;
|
|
901
|
+
const [, years, months, days, hours, minutes, seconds] = match;
|
|
902
|
+
return {
|
|
903
|
+
years: Number(years ?? 0),
|
|
904
|
+
months: Number(months ?? 0),
|
|
905
|
+
days: Number(days ?? 0),
|
|
906
|
+
seconds: Number(hours ?? 0) * 3600 + Number(minutes ?? 0) * 60 + Number(seconds ?? 0)
|
|
907
|
+
};
|
|
908
|
+
}
|
|
909
|
+
function addCalendarDuration(instant, duration, timeZone) {
|
|
910
|
+
const parts = zonedParts(instant, timeZone);
|
|
911
|
+
if (!parts) return void 0;
|
|
912
|
+
const totalMonths = parts.month - 1 + duration.years * 12 + duration.months;
|
|
913
|
+
const year = parts.year + Math.floor(totalMonths / 12);
|
|
914
|
+
const month = totalMonths % 12 + 1;
|
|
915
|
+
const clamped = Math.min(parts.day, daysInMonth(year, month));
|
|
916
|
+
const wall = utcWallTime(year, month, clamped + duration.days, parts.hour, parts.minute, parts.second);
|
|
917
|
+
if (wall === void 0) return void 0;
|
|
918
|
+
const resolved = instantForWallTime(wall, timeZone);
|
|
919
|
+
if (resolved === void 0) return void 0;
|
|
920
|
+
const subSecond = (instant % 1e3 + 1e3) % 1e3;
|
|
921
|
+
return resolved + subSecond + duration.seconds * 1e3;
|
|
922
|
+
}
|
|
923
|
+
function isRepresentableInstant(value) {
|
|
924
|
+
return Number.isFinite(value) && value >= MIN_RFC3339_INSTANT && value <= MAX_RFC3339_INSTANT;
|
|
925
|
+
}
|
|
926
|
+
const MIN_RFC3339_INSTANT = -621672192e5;
|
|
927
|
+
const MAX_RFC3339_INSTANT = 253402300799999;
|
|
928
|
+
function daysInMonth(year, month) {
|
|
929
|
+
const probe = /* @__PURE__ */ new Date(0);
|
|
930
|
+
probe.setUTCFullYear(year, month, 0);
|
|
931
|
+
return probe.getUTCDate();
|
|
932
|
+
}
|
|
933
|
+
function utcWallTime(year, month, day, hour, minute, second) {
|
|
934
|
+
const value = /* @__PURE__ */ new Date(0);
|
|
935
|
+
value.setUTCFullYear(year, month - 1, day);
|
|
936
|
+
value.setUTCHours(hour, minute, second, 0);
|
|
937
|
+
const time = value.getTime();
|
|
938
|
+
return isRepresentableInstant(time) ? time : void 0;
|
|
939
|
+
}
|
|
940
|
+
function zonedParts(instant, timeZone) {
|
|
941
|
+
try {
|
|
942
|
+
const formatted = new Intl.DateTimeFormat("en-US", {
|
|
943
|
+
timeZone,
|
|
944
|
+
// Read the era so a proleptic year can be refused rather than silently
|
|
945
|
+
// relocated: without it `en-US` renders year 0 (1 BC) as year `1`.
|
|
946
|
+
era: "short",
|
|
947
|
+
hour12: false,
|
|
948
|
+
year: "numeric",
|
|
949
|
+
month: "2-digit",
|
|
950
|
+
day: "2-digit",
|
|
951
|
+
hour: "2-digit",
|
|
952
|
+
minute: "2-digit",
|
|
953
|
+
second: "2-digit"
|
|
954
|
+
}).formatToParts(new Date(instant));
|
|
955
|
+
const field = (type) => Number(formatted.find((part) => part.type === type)?.value);
|
|
956
|
+
if (/^b/i.test(formatted.find((part) => part.type === "era")?.value ?? "")) return void 0;
|
|
957
|
+
const parts = {
|
|
958
|
+
year: field("year"),
|
|
959
|
+
month: field("month"),
|
|
960
|
+
day: field("day"),
|
|
961
|
+
// Intl renders midnight as hour 24 in some locales' hourCycle.
|
|
962
|
+
hour: field("hour") % 24,
|
|
963
|
+
minute: field("minute"),
|
|
964
|
+
second: field("second")
|
|
965
|
+
};
|
|
966
|
+
return Object.values(parts).every(Number.isFinite) ? parts : void 0;
|
|
967
|
+
} catch {
|
|
968
|
+
return void 0;
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
function instantForWallTime(wall, timeZone) {
|
|
972
|
+
const dayMs = 864e5;
|
|
973
|
+
const offsetBefore = zoneOffset(wall - dayMs, timeZone);
|
|
974
|
+
const offsetAfter = zoneOffset(wall + dayMs, timeZone);
|
|
975
|
+
if (offsetBefore === void 0 || offsetAfter === void 0) return void 0;
|
|
976
|
+
const fromBefore = wall - offsetBefore;
|
|
977
|
+
const fromAfter = wall - offsetAfter;
|
|
978
|
+
if (readsBackAs(fromBefore, wall, timeZone)) return fromBefore;
|
|
979
|
+
if (readsBackAs(fromAfter, wall, timeZone)) return fromAfter;
|
|
980
|
+
return fromBefore;
|
|
981
|
+
}
|
|
982
|
+
function zoneOffset(instant, timeZone) {
|
|
983
|
+
if (!isRepresentableInstant(instant)) return void 0;
|
|
984
|
+
const parts = zonedParts(instant, timeZone);
|
|
985
|
+
if (!parts) return void 0;
|
|
986
|
+
const asUtc = utcWallTime(parts.year, parts.month, parts.day, parts.hour, parts.minute, parts.second);
|
|
987
|
+
return asUtc === void 0 ? void 0 : asUtc - instant;
|
|
988
|
+
}
|
|
989
|
+
function readsBackAs(instant, wall, timeZone) {
|
|
990
|
+
if (!isRepresentableInstant(instant)) return false;
|
|
991
|
+
const parts = zonedParts(instant, timeZone);
|
|
992
|
+
if (!parts) return false;
|
|
993
|
+
return utcWallTime(parts.year, parts.month, parts.day, parts.hour, parts.minute, parts.second) === wall;
|
|
994
|
+
}
|
|
995
|
+
function consumerStatusSchedule(expectedAt, expected, now, malformedExpectedAt, expectedAtOverflowed = false, overflowField = "the derived expected_at") {
|
|
996
|
+
const windowSeconds = expected.automatedRecoveryWindowSeconds;
|
|
997
|
+
if (expectedAtOverflowed) {
|
|
998
|
+
return {
|
|
999
|
+
overdue: false,
|
|
1000
|
+
deadlineGap: { cause: "deadline_overflow", field: overflowField }
|
|
1001
|
+
};
|
|
1002
|
+
}
|
|
1003
|
+
if (expectedAt === void 0) {
|
|
1004
|
+
return {
|
|
1005
|
+
overdue: false,
|
|
1006
|
+
deadlineGap: malformedExpectedAt !== void 0 ? { cause: "unreadable_expected_at", value: malformedExpectedAt } : { cause: "missing_pin", pin: localExpectedAtPin(expected) }
|
|
1007
|
+
};
|
|
1008
|
+
}
|
|
1009
|
+
if (typeof windowSeconds !== "number" || !Number.isFinite(windowSeconds) || windowSeconds < 0) {
|
|
1010
|
+
return { overdue: false, deadlineGap: { cause: "missing_pin", pin: "automatedRecoveryWindowSeconds" } };
|
|
1011
|
+
}
|
|
1012
|
+
const deadlineAt = Date.parse(expectedAt) + windowSeconds * 1e3;
|
|
1013
|
+
if (!isRepresentableInstant(deadlineAt)) {
|
|
1014
|
+
return {
|
|
1015
|
+
overdue: false,
|
|
1016
|
+
deadlineGap: { cause: "deadline_overflow", field: "ExpectedReportingPeriod.automatedRecoveryWindowSeconds" }
|
|
1017
|
+
};
|
|
1018
|
+
}
|
|
1019
|
+
const deadline = new Date(deadlineAt).toISOString();
|
|
1020
|
+
return { deadline, overdue: now.getTime() >= Date.parse(deadline) };
|
|
1021
|
+
}
|
|
1022
|
+
const RFC3339_INSTANT = /^(\d{4})-(\d{2})-(\d{2})[Tt\s](\d{2}):(\d{2}):(\d{2})(?:\.\d+)?(?:[Zz]|[+-]\d{2}(?::?(\d{2}))?)$/;
|
|
1023
|
+
function normalizedInstant(value) {
|
|
1024
|
+
if (typeof value !== "string") return void 0;
|
|
1025
|
+
const match = RFC3339_INSTANT.exec(value);
|
|
1026
|
+
if (!match) return void 0;
|
|
1027
|
+
const [, isoYear, isoMonth, isoDay, isoHour, isoMinute, isoSecond, offsetMinute] = match;
|
|
1028
|
+
const year = Number(isoYear);
|
|
1029
|
+
const month = Number(isoMonth);
|
|
1030
|
+
const day = Number(isoDay);
|
|
1031
|
+
if (month < 1 || month > 12 || day < 1 || day > daysInMonth(year, month)) return void 0;
|
|
1032
|
+
if (Number(isoHour) > 23 || Number(isoMinute) > 59) return void 0;
|
|
1033
|
+
if (offsetMinute !== void 0 && Number(offsetMinute) > 59) return void 0;
|
|
1034
|
+
const leapSecond = isoSecond === "60";
|
|
1035
|
+
if (leapSecond && utcMinuteOfDay(value, Number(isoHour), Number(isoMinute)) !== 23 * 60 + 59) return void 0;
|
|
1036
|
+
let candidate = value.replace(/([+-]\d{2})$/, "$1:00");
|
|
1037
|
+
if (leapSecond) candidate = candidate.replace(/:60/, ":59");
|
|
1038
|
+
const parsed = Date.parse(candidate);
|
|
1039
|
+
if (!Number.isFinite(parsed)) return void 0;
|
|
1040
|
+
const instant = parsed + (leapSecond ? 1e3 : 0);
|
|
1041
|
+
return isRepresentableInstant(instant) ? new Date(instant).toISOString() : void 0;
|
|
1042
|
+
}
|
|
1043
|
+
function boundedSourceTimezone(value) {
|
|
1044
|
+
return typeof value === "string" && value.length > 0 && value.length <= 255 ? value : void 0;
|
|
1045
|
+
}
|
|
1046
|
+
function utcMinuteOfDay(value, hour, minute) {
|
|
1047
|
+
const offset = /([+-])(\d{2}):?(\d{2})?$/.exec(value);
|
|
1048
|
+
const offsetMinutes = offset ? (offset[1] === "-" ? -1 : 1) * (Number(offset[2]) * 60 + Number(offset[3] ?? 0)) : 0;
|
|
1049
|
+
return ((hour * 60 + minute - offsetMinutes) % 1440 + 1440) % 1440;
|
|
1050
|
+
}
|
|
1051
|
+
function boundedDiagnostic(value) {
|
|
1052
|
+
if (typeof value !== "string") return "";
|
|
1053
|
+
return value.slice(0, 256).replace(/[\u0000-\u001f\u007f]+/g, " ");
|
|
1054
|
+
}
|
|
1055
|
+
function latestInstant(values) {
|
|
1056
|
+
let latest;
|
|
1057
|
+
for (const value of values) {
|
|
1058
|
+
const normalized = normalizedInstant(value);
|
|
1059
|
+
if (normalized === void 0) continue;
|
|
1060
|
+
const parsed = Date.parse(normalized);
|
|
1061
|
+
if (latest === void 0 || parsed > Date.parse(latest)) latest = normalized;
|
|
1062
|
+
}
|
|
1063
|
+
return latest;
|
|
1064
|
+
}
|
|
1065
|
+
function usableLeafInstant(statement, now) {
|
|
1066
|
+
const value = normalizedInstant(statement?.status_as_of);
|
|
1067
|
+
if (value === void 0 || Date.parse(value) > now.getTime()) return void 0;
|
|
1068
|
+
return value;
|
|
1069
|
+
}
|
|
1070
|
+
function contractFactsFor(obligation, expected) {
|
|
1071
|
+
return {
|
|
1072
|
+
...Array.isArray(obligation.media_buy_ids) ? { mediaBuyIds: obligation.media_buy_ids } : {},
|
|
1073
|
+
...obligation.coverage?.covered_package_ids ? { coveredPackageIds: obligation.coverage.covered_package_ids } : {},
|
|
1074
|
+
period: { start: expected.periodStart, end: expected.periodEnd },
|
|
1075
|
+
...expected.committedMetrics ? { committedMetrics: expected.committedMetrics } : {},
|
|
1076
|
+
...expected.metricUnits ? { metricUnits: expected.metricUnits } : {}
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
function collectReportingEscalations(ledger, options) {
|
|
1080
|
+
const operationsContact = options?.operationsContact;
|
|
1081
|
+
const escalations = [];
|
|
1082
|
+
for (const obligation of ledger.obligations) {
|
|
1083
|
+
const declaredIssues = obligation.issues;
|
|
1084
|
+
for (const candidate of Array.isArray(declaredIssues) ? declaredIssues : []) {
|
|
1085
|
+
if (typeof candidate !== "object" || candidate === null || Array.isArray(candidate)) continue;
|
|
1086
|
+
const issue = candidate;
|
|
1087
|
+
const recommendedAction = boundedDiagnostic(String(issue.recommended_action ?? ""));
|
|
1088
|
+
escalations.push({
|
|
1089
|
+
reportingObligationId: obligation.reporting_obligation_id,
|
|
1090
|
+
issueId: boundedDiagnostic(String(issue.issue_id ?? "")),
|
|
1091
|
+
code: boundedDiagnostic(String(issue.code ?? "")),
|
|
1092
|
+
severity: boundedDiagnostic(String(issue.severity ?? "")),
|
|
1093
|
+
responsibleParty: boundedDiagnostic(String(issue.responsible_party ?? "")),
|
|
1094
|
+
recommendedAction,
|
|
1095
|
+
...typeof issue.opened_at === "string" ? { openedAt: issue.opened_at } : {},
|
|
1096
|
+
...typeof issue.issue_state === "string" ? { issueState: issue.issue_state } : {},
|
|
1097
|
+
...typeof issue.external_ref === "string" ? { externalRef: boundedDiagnostic(issue.external_ref) } : {},
|
|
1098
|
+
...typeof issue.reporting_status_id === "string" ? { reportingStatusId: issue.reporting_status_id } : {},
|
|
1099
|
+
...operationsContact ? { operationsContact } : {},
|
|
1100
|
+
requiresHumanContact: recommendedAction.startsWith("contact_")
|
|
1101
|
+
});
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
return escalations;
|
|
1105
|
+
}
|
|
1106
|
+
function isReportingPeriodShape(value) {
|
|
1107
|
+
if (typeof value !== "object" || value === null) return false;
|
|
1108
|
+
const period = value;
|
|
1109
|
+
return typeof period.start === "string" && typeof period.end === "string";
|
|
1110
|
+
}
|
|
597
1111
|
function expectedPeriodMatches(expected, obligation, ledger) {
|
|
1112
|
+
if (!isReportingPeriodShape(obligation.period)) return false;
|
|
598
1113
|
if (obligation.delivery_config_id !== expected.deliveryConfigId || obligation.delivery_config_version !== expected.deliveryConfigVersion || obligation.report_definition_id !== expected.reportDefinitionId || obligation.feed_purpose !== expected.feedPurpose || obligation.reporting_profile !== expected.reportingProfile || !Array.isArray(obligation.media_buy_ids) || !same([...obligation.media_buy_ids].sort(), [...expected.mediaBuyIds].sort()) || obligation.destination_ref !== expected.destinationRef || obligation.required_finality !== expected.requiredFinality || obligation.reconciliation_mode !== expected.reconciliationMode || expected.coverageRequirement === "full" && obligation.coverage?.status !== "full" || obligation.period.start !== expected.periodStart || obligation.period.end !== expected.periodEnd || !isReportingCoverageEvidence(obligation.coverage) || !coverageMatchesExpected(obligation.coverage, expected.coverage)) {
|
|
599
1114
|
return false;
|
|
600
1115
|
}
|
|
@@ -623,8 +1138,8 @@ function expectedIdentityKey(value) {
|
|
|
623
1138
|
value.feed_purpose,
|
|
624
1139
|
value.reporting_profile,
|
|
625
1140
|
value.destination_ref,
|
|
626
|
-
value.period
|
|
627
|
-
value.period
|
|
1141
|
+
value.period?.start,
|
|
1142
|
+
value.period?.end
|
|
628
1143
|
]);
|
|
629
1144
|
}
|
|
630
1145
|
function buildExpectedIdentityIndex(obligations, expectedPeriods) {
|
|
@@ -640,7 +1155,7 @@ function buildExpectedIdentityIndex(obligations, expectedPeriods) {
|
|
|
640
1155
|
}
|
|
641
1156
|
return { expectedByIdentity, obligationCounts };
|
|
642
1157
|
}
|
|
643
|
-
function evaluateReportingLedger(ledger, expectedPeriods, now = /* @__PURE__ */ new Date()) {
|
|
1158
|
+
function evaluateReportingLedger(ledger, expectedPeriods, now = /* @__PURE__ */ new Date(), operationsContact) {
|
|
644
1159
|
assertDirectReportingLedgerGraph(ledger);
|
|
645
1160
|
const obligationResults = [];
|
|
646
1161
|
const uniqueRevisions = /* @__PURE__ */ new Map();
|
|
@@ -656,7 +1171,10 @@ function evaluateReportingLedger(ledger, expectedPeriods, now = /* @__PURE__ */
|
|
|
656
1171
|
const selected = selectCurrent(obligation, ledger, expected);
|
|
657
1172
|
const reasons = [...selected.reasons];
|
|
658
1173
|
if (matchingExpected.length > 0 && !bijective) reasons.push("EXPECTED_PERIOD_NOT_BIJECTIVE");
|
|
659
|
-
if (obligation.health !== "complete")
|
|
1174
|
+
if (obligation.health !== "complete") {
|
|
1175
|
+
const health = typeof obligation.health === "string" ? boundedDiagnostic(obligation.health) : "UNKNOWN";
|
|
1176
|
+
reasons.push(`OBLIGATION_${health.toUpperCase()}`);
|
|
1177
|
+
}
|
|
660
1178
|
if (selected.materialization?.resource && new Date(selected.materialization.resource.expires_at) <= now)
|
|
661
1179
|
reasons.push("RESOURCE_EXPIRED");
|
|
662
1180
|
if (!obligation.resource_retained_until || selected.materialization?.resource && Date.parse(selected.materialization.resource.expires_at) < Date.parse(obligation.resource_retained_until)) {
|
|
@@ -680,12 +1198,18 @@ function evaluateReportingLedger(ledger, expectedPeriods, now = /* @__PURE__ */
|
|
|
680
1198
|
const missingExpectedPeriods = (expectedPeriods ?? []).filter(
|
|
681
1199
|
(expected) => !ledger.obligations.some((obligation) => expectedPeriodMatches(expected, obligation, ledger))
|
|
682
1200
|
);
|
|
1201
|
+
const consumerStatuses = planReportingConsumerStatuses(ledger, expectedPeriods ?? [], missingExpectedPeriods, now);
|
|
1202
|
+
const escalations = collectReportingEscalations(ledger, { ...operationsContact ? { operationsContact } : {} });
|
|
683
1203
|
const scopeDefinitive = ledger.scope.scope_closed && ledger.scope.coverage_complete;
|
|
684
1204
|
return {
|
|
685
1205
|
definitive: expectedPeriods !== void 0 && scopeDefinitive && missingExpectedPeriods.length === 0 && obligationResults.every((item) => item.definitive),
|
|
686
1206
|
ledger,
|
|
687
1207
|
obligations: obligationResults,
|
|
688
1208
|
missingExpectedPeriods,
|
|
1209
|
+
consumerStatuses,
|
|
1210
|
+
postedConsumerStatuses: [],
|
|
1211
|
+
failedConsumerStatuses: [],
|
|
1212
|
+
escalations,
|
|
689
1213
|
totalsByRevision: [...uniqueRevisions.values()].map((item) => ({
|
|
690
1214
|
reportingRevisionId: item.reporting_revision_id,
|
|
691
1215
|
rowCount: item.row_count,
|
|
@@ -896,11 +1420,531 @@ async function reconcileReporting(options) {
|
|
|
896
1420
|
options.ledgerLimits
|
|
897
1421
|
);
|
|
898
1422
|
}
|
|
1423
|
+
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
1424
|
+
const evaluated = evaluateReportingLedger(ledger, options.expectedPeriods, now, options.operationsContact);
|
|
1425
|
+
const readDeadline = Date.now() + (options.ledgerLimits?.maxLoadMs ?? 6e4);
|
|
1426
|
+
const attested = [];
|
|
1427
|
+
let budgetExhausted = false;
|
|
1428
|
+
for (const plan of evaluated.consumerStatuses) {
|
|
1429
|
+
if (!plan.overdue) {
|
|
1430
|
+
attested.push(plan);
|
|
1431
|
+
continue;
|
|
1432
|
+
}
|
|
1433
|
+
if (plan.suppressed !== void 0) {
|
|
1434
|
+
attested.push(plan);
|
|
1435
|
+
continue;
|
|
1436
|
+
}
|
|
1437
|
+
if (budgetExhausted || plan.requiresConsumption && Date.now() >= readDeadline) {
|
|
1438
|
+
attested.push(
|
|
1439
|
+
plan.requiresConsumption ? {
|
|
1440
|
+
...plan,
|
|
1441
|
+
suppressed: "local_budget_exhausted",
|
|
1442
|
+
reason: suppressionReason("local_budget_exhausted", plan.reason)
|
|
1443
|
+
} : plan
|
|
1444
|
+
);
|
|
1445
|
+
continue;
|
|
1446
|
+
}
|
|
1447
|
+
const next = await attestConsumerStatusPlan(plan, ledger, options, readDeadline, now);
|
|
1448
|
+
if (next.suppressed === "local_budget_exhausted" && next.budgetScope === "run") budgetExhausted = true;
|
|
1449
|
+
const { budgetScope: _scope, ...carried } = next;
|
|
1450
|
+
attested.push(carried);
|
|
1451
|
+
}
|
|
1452
|
+
const consumerStatuses = attested;
|
|
1453
|
+
const postedConsumerStatuses = [];
|
|
1454
|
+
const failedConsumerStatuses = [];
|
|
1455
|
+
const confirmed = [];
|
|
1456
|
+
if (!options.client.syncReportingStatus) {
|
|
1457
|
+
for (const [index, plan] of consumerStatuses.entries()) {
|
|
1458
|
+
if (!plan.overdue || plan.suppressed !== void 0) continue;
|
|
1459
|
+
consumerStatuses[index] = {
|
|
1460
|
+
...plan,
|
|
1461
|
+
suppressed: "posting_unavailable",
|
|
1462
|
+
reason: suppressionReason("posting_unavailable", plan.reason)
|
|
1463
|
+
};
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
const owed = [];
|
|
1467
|
+
const claimedChains = /* @__PURE__ */ new Set();
|
|
1468
|
+
for (const plan of consumerStatuses) {
|
|
1469
|
+
if (!plan.overdue || plan.suppressed !== void 0) continue;
|
|
1470
|
+
const chain = canonical(pendingConsumerStatusKey(ledger.accountId, plan));
|
|
1471
|
+
if (claimedChains.has(chain)) {
|
|
1472
|
+
failedConsumerStatuses.push({
|
|
1473
|
+
plan,
|
|
1474
|
+
errors: [
|
|
1475
|
+
{
|
|
1476
|
+
code: "DUPLICATE_STATUS_CHAIN",
|
|
1477
|
+
message: "two expected periods resolve to one consumer-status chain; a batch may carry at most one statement per chain"
|
|
1478
|
+
}
|
|
1479
|
+
]
|
|
1480
|
+
});
|
|
1481
|
+
continue;
|
|
1482
|
+
}
|
|
1483
|
+
claimedChains.add(chain);
|
|
1484
|
+
owed.push(plan);
|
|
1485
|
+
}
|
|
1486
|
+
if (owed.length > 0 && options.client.syncReportingStatus) {
|
|
1487
|
+
for (let offset = 0; offset < owed.length; offset += CONSUMER_STATUS_BATCH_MAX) {
|
|
1488
|
+
const batch = owed.slice(offset, offset + CONSUMER_STATUS_BATCH_MAX);
|
|
1489
|
+
const deadline = Date.now() + (options.ledgerLimits?.maxLoadMs ?? 6e4);
|
|
1490
|
+
const wireStatuses = [];
|
|
1491
|
+
const posted = [];
|
|
1492
|
+
for (const plan of batch) {
|
|
1493
|
+
const key = pendingConsumerStatusKey(ledger.accountId, plan);
|
|
1494
|
+
const fingerprint = consumerStatusClaimFingerprint(plan);
|
|
1495
|
+
const pending = await options.pendingConsumerStatusStore?.get(key);
|
|
1496
|
+
if (pending && pending.claimFingerprint === fingerprint && replayMatchesPlan(pending.statement, plan, now)) {
|
|
1497
|
+
wireStatuses.push(pending.statement);
|
|
1498
|
+
posted.push({
|
|
1499
|
+
...plan,
|
|
1500
|
+
statusAsOf: String(pending.statement.status_as_of),
|
|
1501
|
+
...typeof pending.statement.observed_revision_content_sha256 === "string" ? { observedRevisionContentSha256: pending.statement.observed_revision_content_sha256 } : {}
|
|
1502
|
+
});
|
|
1503
|
+
continue;
|
|
1504
|
+
}
|
|
1505
|
+
const statement = wireConsumerStatus(plan);
|
|
1506
|
+
await options.pendingConsumerStatusStore?.put(key, { statement, claimFingerprint: fingerprint });
|
|
1507
|
+
wireStatuses.push(statement);
|
|
1508
|
+
posted.push(plan);
|
|
1509
|
+
}
|
|
1510
|
+
let response;
|
|
1511
|
+
try {
|
|
1512
|
+
response = await callBeforeDeadline(
|
|
1513
|
+
(signal) => options.client.syncReportingStatus(
|
|
1514
|
+
{
|
|
1515
|
+
...options.request.account ? { account: options.request.account } : {},
|
|
1516
|
+
idempotency_key: consumerStatusBatchKey(wireStatuses),
|
|
1517
|
+
statuses: wireStatuses
|
|
1518
|
+
},
|
|
1519
|
+
{ signal }
|
|
1520
|
+
),
|
|
1521
|
+
deadline,
|
|
1522
|
+
"CONSUMER_STATUS_WRITE_FAILED",
|
|
1523
|
+
"sync_reporting_status exceeded the reporting request deadline"
|
|
1524
|
+
);
|
|
1525
|
+
} catch (error) {
|
|
1526
|
+
recordConsumerStatusBatchFailure(
|
|
1527
|
+
failedConsumerStatuses,
|
|
1528
|
+
posted.length === batch.length ? posted : batch,
|
|
1529
|
+
boundedDiagnostic(error instanceof Error ? error.message : "sync_reporting_status failed")
|
|
1530
|
+
);
|
|
1531
|
+
break;
|
|
1532
|
+
}
|
|
1533
|
+
const results = Array.isArray(response.results) ? response.results : [];
|
|
1534
|
+
if (response.status !== "completed" || results.length !== batch.length) {
|
|
1535
|
+
recordConsumerStatusBatchFailure(
|
|
1536
|
+
failedConsumerStatuses,
|
|
1537
|
+
batch,
|
|
1538
|
+
"seller did not return one result per submitted consumer status"
|
|
1539
|
+
);
|
|
1540
|
+
break;
|
|
1541
|
+
}
|
|
1542
|
+
posted.forEach((plan, index) => {
|
|
1543
|
+
const result = results[index];
|
|
1544
|
+
if (result && ["recorded", "unchanged"].includes(result.result ?? "")) {
|
|
1545
|
+
postedConsumerStatuses.push(plan);
|
|
1546
|
+
confirmed.push(pendingConsumerStatusKey(ledger.accountId, plan));
|
|
1547
|
+
return;
|
|
1548
|
+
}
|
|
1549
|
+
failedConsumerStatuses.push({
|
|
1550
|
+
plan,
|
|
1551
|
+
...typeof result?.reporting_status_id === "string" ? { reportingStatusId: result.reporting_status_id } : {},
|
|
1552
|
+
// Bounded: seller objects of arbitrary size and shape that land
|
|
1553
|
+
// wherever the adopter logs its reconciliation result.
|
|
1554
|
+
errors: Array.isArray(result?.errors) ? result.errors.slice(0, 16) : []
|
|
1555
|
+
});
|
|
1556
|
+
});
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
for (const key of confirmed) await options.pendingConsumerStatusStore?.clear(key);
|
|
1560
|
+
const consumerStatusPending = await readReportingConsumerStatusPending(options);
|
|
1561
|
+
return {
|
|
1562
|
+
...evaluated,
|
|
1563
|
+
consumerStatuses,
|
|
1564
|
+
submittedReceipts: newReceipts,
|
|
1565
|
+
postedConsumerStatuses,
|
|
1566
|
+
failedConsumerStatuses,
|
|
1567
|
+
...consumerStatusPending !== void 0 ? { consumerStatusPending } : {}
|
|
1568
|
+
};
|
|
1569
|
+
}
|
|
1570
|
+
async function attestConsumerStatusPlan(plan, ledger, options, deadline, now) {
|
|
1571
|
+
if (!plan.requiresConsumption || !plan.reportingRevisionId) return plan;
|
|
1572
|
+
if (!options.client.getMediaBuyDelivery) {
|
|
1573
|
+
return {
|
|
1574
|
+
...plan,
|
|
1575
|
+
suppressed: "consumption_unavailable",
|
|
1576
|
+
reason: suppressionReason("consumption_unavailable", plan.reason)
|
|
1577
|
+
};
|
|
1578
|
+
}
|
|
1579
|
+
const outcome = await consumeReportingRevision(options, plan.reportingRevisionId, deadline, now);
|
|
1580
|
+
const leaf = currentConsumerLeaf(ledger, plan, plan.supersedesReportingStatusId);
|
|
1581
|
+
if ("budgetExhausted" in outcome) {
|
|
1582
|
+
return {
|
|
1583
|
+
...plan,
|
|
1584
|
+
suppressed: "local_budget_exhausted",
|
|
1585
|
+
reason: suppressionReason("local_budget_exhausted", plan.reason),
|
|
1586
|
+
budgetScope: outcome.budgetExhausted
|
|
1587
|
+
};
|
|
1588
|
+
}
|
|
1589
|
+
if ("failureCode" in outcome) {
|
|
1590
|
+
return withSuppression(
|
|
1591
|
+
{
|
|
1592
|
+
...plan,
|
|
1593
|
+
consumerStatus: "unreadable",
|
|
1594
|
+
failureCode: outcome.failureCode,
|
|
1595
|
+
mismatchCode: void 0,
|
|
1596
|
+
observedRevisionContentSha256: void 0,
|
|
1597
|
+
requiresConsumption: false,
|
|
1598
|
+
statusAsOf: latestInstant([now.toISOString(), plan.statusAsOfFloor]) ?? plan.statusAsOfFloor,
|
|
1599
|
+
reason: outcome.detail
|
|
1600
|
+
},
|
|
1601
|
+
leaf
|
|
1602
|
+
);
|
|
1603
|
+
}
|
|
1604
|
+
const declared = ledger.revisions.find(
|
|
1605
|
+
(revision2) => revision2.reporting_revision_id === plan.reportingRevisionId
|
|
1606
|
+
)?.revision_content_sha256;
|
|
1607
|
+
if (declared !== void 0 && !sameSha256(outcome.digest, declared)) {
|
|
1608
|
+
return withSuppression(
|
|
1609
|
+
{
|
|
1610
|
+
...plan,
|
|
1611
|
+
consumerStatus: "unreadable",
|
|
1612
|
+
failureCode: "integrity_mismatch",
|
|
1613
|
+
mismatchCode: void 0,
|
|
1614
|
+
observedRevisionContentSha256: void 0,
|
|
1615
|
+
requiresConsumption: false,
|
|
1616
|
+
statusAsOf: latestInstant([outcome.consumedAt, plan.statusAsOfFloor]) ?? plan.statusAsOfFloor,
|
|
1617
|
+
reason: "the consumed rows do not hash to the revision_content_sha256 the ledger declares"
|
|
1618
|
+
},
|
|
1619
|
+
leaf
|
|
1620
|
+
);
|
|
1621
|
+
}
|
|
1622
|
+
const expected = options.expectedPeriods.find(
|
|
1623
|
+
(candidate) => candidate.deliveryConfigId === plan.deliveryConfigId && candidate.deliveryConfigVersion === plan.deliveryConfigVersion && candidate.reportDefinitionId === plan.reportDefinitionId && candidate.periodStart === plan.period.start && candidate.periodEnd === plan.period.end
|
|
1624
|
+
);
|
|
1625
|
+
const obligation = ledger.obligations.find(
|
|
1626
|
+
(candidate) => candidate.reporting_obligation_id === plan.reportingObligationId
|
|
1627
|
+
);
|
|
1628
|
+
const revision = ledger.revisions.find((candidate) => candidate.reporting_revision_id === plan.reportingRevisionId);
|
|
1629
|
+
const mismatch = expected && obligation && revision ? (0, import_content_mismatch.detectReportingContentMismatch)(
|
|
1630
|
+
contractFactsFor(obligation, expected),
|
|
1631
|
+
revision,
|
|
1632
|
+
rowEvidenceFor(contractFactsFor(obligation, expected), revision, outcome.rows)
|
|
1633
|
+
) : void 0;
|
|
1634
|
+
return withSuppression(
|
|
1635
|
+
{
|
|
1636
|
+
...plan,
|
|
1637
|
+
consumerStatus: mismatch ? "content_mismatch" : "received",
|
|
1638
|
+
mismatchCode: mismatch?.mismatchCode,
|
|
1639
|
+
observedRevisionContentSha256: outcome.digest,
|
|
1640
|
+
requiresConsumption: false,
|
|
1641
|
+
// `status_as_of` is when the revision became consumable to *this*
|
|
1642
|
+
// consumer, floored by the superseded leaf so the chain never moves
|
|
1643
|
+
// backwards.
|
|
1644
|
+
statusAsOf: latestInstant([outcome.consumedAt, plan.statusAsOfFloor]) ?? plan.statusAsOfFloor,
|
|
1645
|
+
reason: mismatch?.detail ?? "the exact revision content was consumed and honors every frozen contract fact"
|
|
1646
|
+
},
|
|
1647
|
+
leaf
|
|
1648
|
+
);
|
|
1649
|
+
}
|
|
1650
|
+
function withSuppression(plan, leaf) {
|
|
1651
|
+
const suppressed = consumerStatusSuppression(plan, leaf);
|
|
1652
|
+
if (suppressed) return { ...plan, suppressed, reason: suppressionReason(suppressed, plan.reason) };
|
|
1653
|
+
const { suppressed: _cleared, ...unsuppressed } = plan;
|
|
1654
|
+
return unsuppressed;
|
|
1655
|
+
}
|
|
1656
|
+
async function consumeReportingRevision(options, reportingRevisionId, deadline, now) {
|
|
1657
|
+
const read = options.client.getMediaBuyDelivery;
|
|
1658
|
+
const maxPages = options.ledgerLimits?.maxPages ?? 1e3;
|
|
1659
|
+
const maxRows = options.ledgerLimits?.maxRevisionRows ?? 1e5;
|
|
1660
|
+
const rows = [];
|
|
1661
|
+
let bytes = 0;
|
|
1662
|
+
let totalCount;
|
|
1663
|
+
const seenCursors = /* @__PURE__ */ new Set();
|
|
1664
|
+
let binding;
|
|
1665
|
+
let cursor;
|
|
1666
|
+
let pages = 0;
|
|
1667
|
+
try {
|
|
1668
|
+
do {
|
|
1669
|
+
pages += 1;
|
|
1670
|
+
if (pages > maxPages) return { budgetExhausted: "revision" };
|
|
1671
|
+
const response = await callBeforeDeadline(
|
|
1672
|
+
(signal) => read(
|
|
1673
|
+
{
|
|
1674
|
+
...options.request.account ? { account: options.request.account } : {},
|
|
1675
|
+
reporting_revision_id: reportingRevisionId,
|
|
1676
|
+
...cursor ? { pagination: { cursor } } : {}
|
|
1677
|
+
},
|
|
1678
|
+
{ signal }
|
|
1679
|
+
),
|
|
1680
|
+
deadline,
|
|
1681
|
+
"CONSUMER_STATUS_READ_FAILED",
|
|
1682
|
+
"get_media_buy_delivery exceeded the reporting request deadline"
|
|
1683
|
+
);
|
|
1684
|
+
if (response.status !== void 0 && response.status !== "completed") {
|
|
1685
|
+
return { failureCode: "reader_incompatible", detail: "the exact-revision read did not complete" };
|
|
1686
|
+
}
|
|
1687
|
+
const page = response.reporting_revision_binding;
|
|
1688
|
+
if (!page || page.reporting_revision_id !== reportingRevisionId || typeof page.content_sha256 !== "string" || typeof page.row_count !== "number" || !Array.isArray(page.control_totals)) {
|
|
1689
|
+
return {
|
|
1690
|
+
failureCode: "reader_incompatible",
|
|
1691
|
+
detail: "the seller did not return a reporting_revision_binding for the exact revision requested"
|
|
1692
|
+
};
|
|
1693
|
+
}
|
|
1694
|
+
if (binding && !same(binding, page)) {
|
|
1695
|
+
return { failureCode: "integrity_mismatch", detail: "the revision binding changed between cursor pages" };
|
|
1696
|
+
}
|
|
1697
|
+
binding = page;
|
|
1698
|
+
if (typeof response.pagination?.total_count === "number") totalCount = response.pagination.total_count;
|
|
1699
|
+
for (const row of response.reporting_rows ?? []) {
|
|
1700
|
+
rows.push(row);
|
|
1701
|
+
const sized = approximateRowBytes(row);
|
|
1702
|
+
if (sized === TOO_DEEP_TO_SIZE) {
|
|
1703
|
+
return {
|
|
1704
|
+
failureCode: "reader_incompatible",
|
|
1705
|
+
detail: "a revision row nests deeper than this reader will walk"
|
|
1706
|
+
};
|
|
1707
|
+
}
|
|
1708
|
+
if (sized === void 0) {
|
|
1709
|
+
return { budgetExhausted: "revision" };
|
|
1710
|
+
}
|
|
1711
|
+
bytes += sized;
|
|
1712
|
+
if (rows.length > maxRows || bytes > MAX_CONSUMED_REVISION_BYTES) {
|
|
1713
|
+
return { budgetExhausted: "revision" };
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1716
|
+
if (response.pagination?.has_more) {
|
|
1717
|
+
const next = response.pagination.cursor;
|
|
1718
|
+
if (!next || seenCursors.has(next)) {
|
|
1719
|
+
return { failureCode: "transport_failed", detail: "revision row pagination did not advance" };
|
|
1720
|
+
}
|
|
1721
|
+
seenCursors.add(next);
|
|
1722
|
+
cursor = next;
|
|
1723
|
+
} else {
|
|
1724
|
+
cursor = void 0;
|
|
1725
|
+
}
|
|
1726
|
+
} while (cursor);
|
|
1727
|
+
} catch (error) {
|
|
1728
|
+
if (error instanceof ReportingReconciliationError && error.code === "CONSUMER_STATUS_READ_FAILED") {
|
|
1729
|
+
return { budgetExhausted: "run" };
|
|
1730
|
+
}
|
|
1731
|
+
return { failureCode: "transport_failed", detail: "the exact-revision read failed before the rows were complete" };
|
|
1732
|
+
}
|
|
1733
|
+
const consumedAt = now.toISOString();
|
|
1734
|
+
if (!binding) {
|
|
1735
|
+
return { failureCode: "reader_incompatible", detail: "the exact-revision read returned no binding" };
|
|
1736
|
+
}
|
|
1737
|
+
if (totalCount !== void 0 && totalCount !== binding.row_count) {
|
|
1738
|
+
return {
|
|
1739
|
+
failureCode: "integrity_mismatch",
|
|
1740
|
+
detail: `the read declares ${totalCount} total rows and the binding declares ${binding.row_count}`
|
|
1741
|
+
};
|
|
1742
|
+
}
|
|
1743
|
+
if (rows.length !== binding.row_count) {
|
|
1744
|
+
return {
|
|
1745
|
+
failureCode: "integrity_mismatch",
|
|
1746
|
+
detail: `the revision declares ${binding.row_count} rows and the read returned ${rows.length}`
|
|
1747
|
+
};
|
|
1748
|
+
}
|
|
1749
|
+
let digest;
|
|
1750
|
+
try {
|
|
1751
|
+
digest = (0, import_crypto.createHash)("sha256").update(
|
|
1752
|
+
(0, import_jcs.canonicalize)({
|
|
1753
|
+
reporting_revision_id: reportingRevisionId,
|
|
1754
|
+
row_count: binding.row_count,
|
|
1755
|
+
control_totals: binding.control_totals,
|
|
1756
|
+
reporting_rows: rows
|
|
1757
|
+
}),
|
|
1758
|
+
"utf8"
|
|
1759
|
+
).digest("hex");
|
|
1760
|
+
} catch (error) {
|
|
1761
|
+
if (error instanceof RangeError) return { budgetExhausted: "revision" };
|
|
1762
|
+
const detail = error instanceof Error ? boundedDiagnostic(error.message) : "";
|
|
1763
|
+
return {
|
|
1764
|
+
failureCode: "reader_incompatible",
|
|
1765
|
+
detail: detail ? `the revision rows could not be canonicalized: ${detail}` : "the revision rows could not be canonicalized"
|
|
1766
|
+
};
|
|
1767
|
+
}
|
|
1768
|
+
if (!sameSha256(digest, binding.content_sha256)) {
|
|
1769
|
+
return {
|
|
1770
|
+
failureCode: "integrity_mismatch",
|
|
1771
|
+
detail: "the recomputed revision binding digest does not match the content_sha256 the seller published"
|
|
1772
|
+
};
|
|
1773
|
+
}
|
|
1774
|
+
return { digest, consumedAt, rowCount: rows.length, rows };
|
|
1775
|
+
}
|
|
1776
|
+
const TOO_DEEP_TO_SIZE = -1;
|
|
1777
|
+
function approximateRowBytes(row) {
|
|
1778
|
+
let containers = MAX_ROW_ESTIMATE_CONTAINERS;
|
|
1779
|
+
const visit = (value, depth) => {
|
|
1780
|
+
if (typeof value === "string") return 16 + value.length * 2;
|
|
1781
|
+
if (value === null || typeof value !== "object") return 8;
|
|
1782
|
+
if (depth >= MAX_ROW_ESTIMATE_DEPTH) return TOO_DEEP_TO_SIZE;
|
|
1783
|
+
containers -= 1;
|
|
1784
|
+
if (containers < 0) return void 0;
|
|
1785
|
+
if (Array.isArray(value)) {
|
|
1786
|
+
let total2 = 40 + 8 * value.length;
|
|
1787
|
+
for (const item of value) {
|
|
1788
|
+
const child = visit(item, depth + 1);
|
|
1789
|
+
if (child === void 0 || child === TOO_DEEP_TO_SIZE) return child;
|
|
1790
|
+
total2 += child;
|
|
1791
|
+
}
|
|
1792
|
+
return total2;
|
|
1793
|
+
}
|
|
1794
|
+
const entries = Object.entries(value);
|
|
1795
|
+
let total = 40 + 8 * entries.length;
|
|
1796
|
+
for (const [key, child] of entries) {
|
|
1797
|
+
const sized = visit(child, depth + 1);
|
|
1798
|
+
if (sized === void 0 || sized === TOO_DEEP_TO_SIZE) return sized;
|
|
1799
|
+
total += key.length * 2 + sized;
|
|
1800
|
+
}
|
|
1801
|
+
return total;
|
|
1802
|
+
};
|
|
1803
|
+
return visit(row, 0);
|
|
1804
|
+
}
|
|
1805
|
+
function rowEvidenceFor(facts, revision, rows) {
|
|
1806
|
+
if (!facts.committedMetrics) return {};
|
|
1807
|
+
const observed = new Set((revision.control_totals ?? []).map((total) => total.name));
|
|
1808
|
+
const record = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
1809
|
+
for (const row of rows) {
|
|
1810
|
+
const fields = record(row);
|
|
1811
|
+
if (!fields) continue;
|
|
1812
|
+
for (const key of Object.keys(fields)) observed.add(key);
|
|
1813
|
+
const totals = record(fields.totals);
|
|
1814
|
+
if (totals) for (const key of Object.keys(totals)) observed.add(key);
|
|
1815
|
+
}
|
|
1816
|
+
return { observedMetricNames: [...observed] };
|
|
1817
|
+
}
|
|
1818
|
+
const REPLAYABLE_STATEMENT_KEYS = /* @__PURE__ */ new Set([
|
|
1819
|
+
"reporting_status_id",
|
|
1820
|
+
"supersedes_reporting_status_id",
|
|
1821
|
+
"delivery_config_id",
|
|
1822
|
+
"delivery_config_version",
|
|
1823
|
+
"report_definition_id",
|
|
1824
|
+
"period",
|
|
1825
|
+
"consumer_status",
|
|
1826
|
+
"status_as_of",
|
|
1827
|
+
"reporting_obligation_id",
|
|
1828
|
+
"reporting_revision_id",
|
|
1829
|
+
"observed_revision_content_sha256",
|
|
1830
|
+
"mismatch_code",
|
|
1831
|
+
"failure_code"
|
|
1832
|
+
]);
|
|
1833
|
+
function replayMatchesPlan(statement, plan, now) {
|
|
1834
|
+
const period = statement.period;
|
|
1835
|
+
if (typeof statement.status_as_of !== "string") return false;
|
|
1836
|
+
if (usableLeafInstant({ status_as_of: statement.status_as_of }, now) === void 0) {
|
|
1837
|
+
return false;
|
|
1838
|
+
}
|
|
1839
|
+
if (Object.keys(statement).some((key) => !REPLAYABLE_STATEMENT_KEYS.has(key))) return false;
|
|
1840
|
+
if (!sameOptionalSha256(
|
|
1841
|
+
statement.observed_revision_content_sha256,
|
|
1842
|
+
plan.observedRevisionContentSha256
|
|
1843
|
+
)) {
|
|
1844
|
+
return false;
|
|
1845
|
+
}
|
|
1846
|
+
if (statement.reporting_status_id !== consumerStatusId({ ...plan, statusAsOf: statement.status_as_of })) {
|
|
1847
|
+
return false;
|
|
1848
|
+
}
|
|
1849
|
+
return statement.delivery_config_id === plan.deliveryConfigId && statement.delivery_config_version === plan.deliveryConfigVersion && statement.report_definition_id === plan.reportDefinitionId && period?.start === plan.period.start && period?.end === plan.period.end && period?.source_timezone === plan.period.source_timezone && statement.consumer_status === plan.consumerStatus && (statement.reporting_revision_id ?? void 0) === plan.reportingRevisionId && (statement.mismatch_code ?? void 0) === plan.mismatchCode && (statement.failure_code ?? void 0) === plan.failureCode && (statement.supersedes_reporting_status_id ?? void 0) === plan.supersedesReportingStatusId && typeof statement.reporting_status_id === "string" && typeof statement.status_as_of === "string";
|
|
1850
|
+
}
|
|
1851
|
+
function recordConsumerStatusBatchFailure(failed, batch, message) {
|
|
1852
|
+
for (const plan of batch) {
|
|
1853
|
+
failed.push({ plan, errors: [{ code: "CONSUMER_STATUS_WRITE_FAILED", message }] });
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
function consumerStatusId(plan) {
|
|
1857
|
+
return `adcp-sdk.${(0, import_crypto.createHash)("sha256").update((0, import_jcs.canonicalize)([...consumerStatusClaim(plan), plan.statusAsOf ?? null])).digest("hex").slice(0, 32)}`;
|
|
1858
|
+
}
|
|
1859
|
+
function consumerStatusClaim(plan) {
|
|
1860
|
+
return [
|
|
1861
|
+
plan.deliveryConfigId,
|
|
1862
|
+
plan.deliveryConfigVersion,
|
|
1863
|
+
plan.reportDefinitionId,
|
|
1864
|
+
plan.period,
|
|
1865
|
+
plan.consumerStatus,
|
|
1866
|
+
plan.mismatchCode ?? null,
|
|
1867
|
+
plan.failureCode ?? null,
|
|
1868
|
+
plan.reportingRevisionId ?? null,
|
|
1869
|
+
plan.observedRevisionContentSha256 ?? null,
|
|
1870
|
+
plan.supersedesReportingStatusId ?? null
|
|
1871
|
+
];
|
|
1872
|
+
}
|
|
1873
|
+
function consumerStatusClaimFingerprint(plan) {
|
|
1874
|
+
return (0, import_crypto.createHash)("sha256").update((0, import_jcs.canonicalize)(consumerStatusClaim(plan))).digest("hex");
|
|
1875
|
+
}
|
|
1876
|
+
function pendingConsumerStatusKey(accountId, plan) {
|
|
899
1877
|
return {
|
|
900
|
-
|
|
901
|
-
|
|
1878
|
+
accountId,
|
|
1879
|
+
deliveryConfigId: plan.deliveryConfigId,
|
|
1880
|
+
deliveryConfigVersion: plan.deliveryConfigVersion,
|
|
1881
|
+
reportDefinitionId: plan.reportDefinitionId,
|
|
1882
|
+
periodStart: plan.period.start,
|
|
1883
|
+
periodEnd: plan.period.end
|
|
902
1884
|
};
|
|
903
1885
|
}
|
|
1886
|
+
function consumerStatusBatchKey(statuses) {
|
|
1887
|
+
return `adcp-sdk-batch.${(0, import_crypto.createHash)("sha256").update((0, import_jcs.canonicalize)(statuses)).digest("hex").slice(0, 32)}`;
|
|
1888
|
+
}
|
|
1889
|
+
function wireConsumerStatus(plan) {
|
|
1890
|
+
if (!plan.statusAsOf) {
|
|
1891
|
+
throw new ReportingReconciliationError(
|
|
1892
|
+
"CONSUMER_STATUS_UNATTESTED",
|
|
1893
|
+
"cannot post a consumer status before its status_as_of is established"
|
|
1894
|
+
);
|
|
1895
|
+
}
|
|
1896
|
+
if ((plan.consumerStatus === "received" || plan.consumerStatus === "content_mismatch") && !plan.observedRevisionContentSha256) {
|
|
1897
|
+
throw new ReportingReconciliationError(
|
|
1898
|
+
"CONSUMER_STATUS_UNATTESTED",
|
|
1899
|
+
"received and content_mismatch require a locally recomputed revision binding digest"
|
|
1900
|
+
);
|
|
1901
|
+
}
|
|
1902
|
+
return {
|
|
1903
|
+
reporting_status_id: consumerStatusId(plan),
|
|
1904
|
+
...plan.supersedesReportingStatusId ? { supersedes_reporting_status_id: plan.supersedesReportingStatusId } : {},
|
|
1905
|
+
delivery_config_id: plan.deliveryConfigId,
|
|
1906
|
+
delivery_config_version: plan.deliveryConfigVersion,
|
|
1907
|
+
report_definition_id: plan.reportDefinitionId,
|
|
1908
|
+
period: plan.period,
|
|
1909
|
+
consumer_status: plan.consumerStatus,
|
|
1910
|
+
// The buyer's own instant, and part of the ID derivation above so the two
|
|
1911
|
+
// can never disagree. For `received` the spec wants when the revision
|
|
1912
|
+
// became consumable to this consumer, which genuinely moves between
|
|
1913
|
+
// re-plans; `pendingConsumerStatusStore` is what makes a retry reuse it.
|
|
1914
|
+
status_as_of: plan.statusAsOf,
|
|
1915
|
+
...plan.reportingObligationId ? { reporting_obligation_id: plan.reportingObligationId } : {},
|
|
1916
|
+
...plan.reportingRevisionId ? { reporting_revision_id: plan.reportingRevisionId } : {},
|
|
1917
|
+
...plan.observedRevisionContentSha256 ? { observed_revision_content_sha256: plan.observedRevisionContentSha256 } : {},
|
|
1918
|
+
...plan.mismatchCode ? { mismatch_code: plan.mismatchCode } : {},
|
|
1919
|
+
...plan.failureCode ? { failure_code: plan.failureCode } : {}
|
|
1920
|
+
};
|
|
1921
|
+
}
|
|
1922
|
+
async function readReportingConsumerStatusPending(options) {
|
|
1923
|
+
if (!options.client.syncReportingStatus) return void 0;
|
|
1924
|
+
const {
|
|
1925
|
+
health: _health,
|
|
1926
|
+
changes_after: _changesAfter,
|
|
1927
|
+
pagination: _pagination,
|
|
1928
|
+
reporting_revision_id: _revisionId,
|
|
1929
|
+
...summaryRequest
|
|
1930
|
+
} = options.request;
|
|
1931
|
+
try {
|
|
1932
|
+
const summary = await callBeforeDeadline(
|
|
1933
|
+
(signal) => options.client.getReportingStatus(
|
|
1934
|
+
{ ...summaryRequest, view: "summary" },
|
|
1935
|
+
{ signal }
|
|
1936
|
+
),
|
|
1937
|
+
Date.now() + (options.ledgerLimits?.maxLoadMs ?? 6e4),
|
|
1938
|
+
"CONSUMER_STATUS_PENDING_READ_FAILED",
|
|
1939
|
+
"summary get_reporting_status exceeded the reporting request deadline"
|
|
1940
|
+
);
|
|
1941
|
+
const counts = summary.obligation_counts;
|
|
1942
|
+
const pending = counts?.consumer_status_pending;
|
|
1943
|
+
return typeof pending === "number" && Number.isInteger(pending) && pending >= 0 ? pending : void 0;
|
|
1944
|
+
} catch {
|
|
1945
|
+
return void 0;
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
904
1948
|
// Annotate the CommonJS export names for ESM import in node:
|
|
905
1949
|
0 && (module.exports = {
|
|
906
1950
|
ReportingReconciliationError,
|