@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
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
import { createHash } from "crypto";
|
|
2
2
|
import { generateIdempotencyKey } from "../utils/idempotency.mjs";
|
|
3
|
+
import { canonicalize } from "../utils/jcs.mjs";
|
|
4
|
+
import {
|
|
5
|
+
detectReportingContentMismatch
|
|
6
|
+
} from "./content-mismatch.mjs";
|
|
3
7
|
import { isReportingControlTotals, isReportingReceiptEvidence, isReportingVerificationEvidence } from "./evidence.mjs";
|
|
4
8
|
import {
|
|
5
9
|
createReportingManifestInspector,
|
|
6
10
|
ReportingInspectionError
|
|
7
11
|
} from "./inspection.mjs";
|
|
12
|
+
const CONSUMER_STATUS_BATCH_MAX = 100;
|
|
13
|
+
const MAX_CONSUMED_REVISION_BYTES = 256 * 1024 * 1024;
|
|
14
|
+
const MAX_ROW_ESTIMATE_CONTAINERS = 262144;
|
|
15
|
+
const OVERFLOWED_INSTANT = "\0overflow";
|
|
16
|
+
const MAX_ROW_ESTIMATE_DEPTH = 64;
|
|
8
17
|
class ReportingReconciliationError extends Error {
|
|
9
18
|
constructor(code, message) {
|
|
10
19
|
super(message);
|
|
@@ -227,6 +236,7 @@ async function loadReportingLedger(client, request, maxSnapshotRestarts = 2, lim
|
|
|
227
236
|
const revisions = /* @__PURE__ */ new Map();
|
|
228
237
|
const materializations = /* @__PURE__ */ new Map();
|
|
229
238
|
const receipts = /* @__PURE__ */ new Map();
|
|
239
|
+
const consumerStatuses = /* @__PURE__ */ new Map();
|
|
230
240
|
const seenCursors = /* @__PURE__ */ new Set();
|
|
231
241
|
let cursor;
|
|
232
242
|
let snapshotId;
|
|
@@ -316,6 +326,14 @@ async function loadReportingLedger(client, request, maxSnapshotRestarts = 2, lim
|
|
|
316
326
|
for (const item of response.materializations ?? [])
|
|
317
327
|
addImmutable(materializations, item.reporting_materialization_id, item, "materialization");
|
|
318
328
|
for (const item of response.receipts ?? []) addImmutable(receipts, item.reporting_receipt_id, item, "receipt");
|
|
329
|
+
for (const item of response.consumer_statuses ?? [])
|
|
330
|
+
addImmutable(consumerStatuses, item.reporting_status_id, item, "consumer status");
|
|
331
|
+
if (consumerStatuses.size > maxRecords) {
|
|
332
|
+
throw new ReportingReconciliationError(
|
|
333
|
+
"LEDGER_LIMIT_EXCEEDED",
|
|
334
|
+
"reporting consumer status history exceeds record limit"
|
|
335
|
+
);
|
|
336
|
+
}
|
|
319
337
|
if (obligations.size + revisions.size + materializations.size + receipts.size > maxRecords) {
|
|
320
338
|
throw new ReportingReconciliationError("LEDGER_LIMIT_EXCEEDED", "reporting ledger exceeds record limit");
|
|
321
339
|
}
|
|
@@ -345,6 +363,7 @@ async function loadReportingLedger(client, request, maxSnapshotRestarts = 2, lim
|
|
|
345
363
|
ledgerAsOf,
|
|
346
364
|
accountId,
|
|
347
365
|
scope,
|
|
366
|
+
consumerStatuses: [...consumerStatuses.values()],
|
|
348
367
|
obligations: [...obligations.values()],
|
|
349
368
|
revisions: [...revisions.values()],
|
|
350
369
|
materializations: [...materializations.values()],
|
|
@@ -485,7 +504,7 @@ function selectCurrent(obligation, ledger, expected) {
|
|
|
485
504
|
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)) {
|
|
486
505
|
reasons.push("REVISION_SCOPE_MISMATCH");
|
|
487
506
|
}
|
|
488
|
-
if (obligation.scope_resolved_at !== obligation.period
|
|
507
|
+
if (obligation.scope_resolved_at !== obligation.period?.end) reasons.push("SCOPE_CUTOFF_MISMATCH");
|
|
489
508
|
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)) {
|
|
490
509
|
reasons.push("COVERAGE_SCOPE_MISMATCH");
|
|
491
510
|
}
|
|
@@ -569,7 +588,505 @@ function selectCurrent(obligation, ledger, expected) {
|
|
|
569
588
|
function countMismatch(declared, observed, required) {
|
|
570
589
|
return declared === void 0 ? required : declared !== observed;
|
|
571
590
|
}
|
|
591
|
+
function planReportingConsumerStatuses(ledger, expectedPeriods, missingExpectedPeriods, now) {
|
|
592
|
+
const missing = new Set(missingExpectedPeriods);
|
|
593
|
+
return expectedPeriods.map((expected) => {
|
|
594
|
+
const obligationForPeriod = ledger.obligations.find(
|
|
595
|
+
(candidate) => expectedPeriodMatches(expected, candidate, ledger)
|
|
596
|
+
);
|
|
597
|
+
const declaredSourceTimezone = obligationForPeriod?.period?.source_timezone;
|
|
598
|
+
const resolvedSourceTimezone = ianaTimeZone(expected.periodSourceTimezone) ?? ianaTimeZone(declaredSourceTimezone);
|
|
599
|
+
const periodIdentityUnknown = resolvedSourceTimezone === void 0 && declaredSourceTimezone !== void 0;
|
|
600
|
+
const period = {
|
|
601
|
+
start: expected.periodStart,
|
|
602
|
+
end: expected.periodEnd,
|
|
603
|
+
source_timezone: resolvedSourceTimezone ?? "UTC"
|
|
604
|
+
};
|
|
605
|
+
const base = {
|
|
606
|
+
deliveryConfigId: expected.deliveryConfigId,
|
|
607
|
+
deliveryConfigVersion: expected.deliveryConfigVersion,
|
|
608
|
+
reportDefinitionId: expected.reportDefinitionId,
|
|
609
|
+
period
|
|
610
|
+
};
|
|
611
|
+
const derivedExpectedAt = reportingExpectedAt(obligationForPeriod, expected);
|
|
612
|
+
const expectedAtOverflowed = derivedExpectedAt === OVERFLOWED_INSTANT;
|
|
613
|
+
const expectedAt = expectedAtOverflowed || derivedExpectedAt === void 0 ? void 0 : latestInstant([derivedExpectedAt, expected.periodEnd]);
|
|
614
|
+
const declaredExpectedAt = obligationForPeriod?.expected_at;
|
|
615
|
+
const malformedExpectedAt = expectedAt === void 0 && !expectedAtOverflowed && declaredExpectedAt !== void 0 ? typeof declaredExpectedAt === "string" ? declaredExpectedAt : `<${typeof declaredExpectedAt}>` : void 0;
|
|
616
|
+
const schedule = consumerStatusSchedule(expectedAt, expected, now, malformedExpectedAt, expectedAtOverflowed);
|
|
617
|
+
const pinnedExpectation = pinnedExpectedAt(expected);
|
|
618
|
+
const deadlineBeyondPin = expectedAt !== void 0 && pinnedExpectation !== void 0 && Date.parse(expectedAt) > Date.parse(pinnedExpectation) + (expected.automatedRecoveryWindowSeconds ?? 0) * 1e3 ? { declared: expectedAt, pinned: pinnedExpectation } : void 0;
|
|
619
|
+
const leaf = currentConsumerLeaf(
|
|
620
|
+
ledger,
|
|
621
|
+
base,
|
|
622
|
+
obligationForPeriod?.current_consumer_status_id
|
|
623
|
+
);
|
|
624
|
+
const establishedAt = latestInstant([expectedAt, expected.periodEnd]) ?? expected.periodEnd;
|
|
625
|
+
if (missing.has(expected) || !obligationForPeriod) {
|
|
626
|
+
return finalizeConsumerStatusPlan(
|
|
627
|
+
{
|
|
628
|
+
...base,
|
|
629
|
+
...schedule,
|
|
630
|
+
...periodIdentityUnknown ? { periodIdentityUnknown: true } : {},
|
|
631
|
+
...deadlineBeyondPin ? { deadlineBeyondPin } : {},
|
|
632
|
+
...deadlineBeyondPin ? { deadlineBeyondPin } : {},
|
|
633
|
+
consumerStatus: "obligation_missing",
|
|
634
|
+
establishedAt,
|
|
635
|
+
reason: missing.has(expected) ? "the independently expected period is absent from the seller ledger" : "no obligation in the ledger matches this expected period"
|
|
636
|
+
},
|
|
637
|
+
leaf,
|
|
638
|
+
now
|
|
639
|
+
);
|
|
640
|
+
}
|
|
641
|
+
const obligation = obligationForPeriod;
|
|
642
|
+
const selected = selectCurrent(obligation, ledger, expected);
|
|
643
|
+
const revision = selected.revision;
|
|
644
|
+
const forked = selected.reasons.includes("AMBIGUOUS_REVISION_CHAIN");
|
|
645
|
+
const predecessorMissing = selected.reasons.includes("REVISION_PREDECESSOR_MISSING");
|
|
646
|
+
const indeterminate = forked || predecessorMissing;
|
|
647
|
+
if (!revision || indeterminate) {
|
|
648
|
+
return finalizeConsumerStatusPlan(
|
|
649
|
+
{
|
|
650
|
+
...base,
|
|
651
|
+
...schedule,
|
|
652
|
+
...periodIdentityUnknown ? { periodIdentityUnknown: true } : {},
|
|
653
|
+
...deadlineBeyondPin ? { deadlineBeyondPin } : {},
|
|
654
|
+
...deadlineBeyondPin ? { deadlineBeyondPin } : {},
|
|
655
|
+
reportingObligationId: obligation.reporting_obligation_id,
|
|
656
|
+
consumerStatus: "revision_missing",
|
|
657
|
+
...indeterminate ? { indeterminate: true } : {},
|
|
658
|
+
establishedAt,
|
|
659
|
+
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"
|
|
660
|
+
},
|
|
661
|
+
leaf,
|
|
662
|
+
now
|
|
663
|
+
);
|
|
664
|
+
}
|
|
665
|
+
const disqualifying = selected.reasons.filter((reason) => REVISION_DISQUALIFYING_REASONS.has(reason));
|
|
666
|
+
if (disqualifying.length > 0) {
|
|
667
|
+
return finalizeConsumerStatusPlan(
|
|
668
|
+
{
|
|
669
|
+
...base,
|
|
670
|
+
...schedule,
|
|
671
|
+
...periodIdentityUnknown ? { periodIdentityUnknown: true } : {},
|
|
672
|
+
...deadlineBeyondPin ? { deadlineBeyondPin } : {},
|
|
673
|
+
...deadlineBeyondPin ? { deadlineBeyondPin } : {},
|
|
674
|
+
reportingObligationId: obligation.reporting_obligation_id,
|
|
675
|
+
consumerStatus: "revision_missing",
|
|
676
|
+
establishedAt,
|
|
677
|
+
reason: `the published revision does not satisfy the accepted generation (${disqualifying.join(", ")})`
|
|
678
|
+
},
|
|
679
|
+
leaf,
|
|
680
|
+
now
|
|
681
|
+
);
|
|
682
|
+
}
|
|
683
|
+
const mismatch = detectReportingContentMismatch(contractFactsFor(obligation, expected), revision);
|
|
684
|
+
return finalizeConsumerStatusPlan(
|
|
685
|
+
{
|
|
686
|
+
...base,
|
|
687
|
+
...schedule,
|
|
688
|
+
...periodIdentityUnknown ? { periodIdentityUnknown: true } : {},
|
|
689
|
+
...deadlineBeyondPin ? { deadlineBeyondPin } : {},
|
|
690
|
+
reportingObligationId: obligation.reporting_obligation_id,
|
|
691
|
+
reportingRevisionId: revision.reporting_revision_id,
|
|
692
|
+
consumerStatus: mismatch ? "content_mismatch" : "received",
|
|
693
|
+
...mismatch ? { mismatchCode: mismatch.mismatchCode } : {},
|
|
694
|
+
// Not the revision's own `observed_at`: `status_as_of` is when the
|
|
695
|
+
// revision became consumable *to this consumer*, and the spec is
|
|
696
|
+
// explicit that sellers must not silently substitute publication time.
|
|
697
|
+
// The floor stands in until the buyer has actually read it.
|
|
698
|
+
establishedAt,
|
|
699
|
+
requiresConsumption: true,
|
|
700
|
+
reason: mismatch?.detail ?? "the named revision is ready to consume and honors every frozen contract fact"
|
|
701
|
+
},
|
|
702
|
+
leaf,
|
|
703
|
+
now
|
|
704
|
+
);
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
const REVISION_DISQUALIFYING_REASONS = /* @__PURE__ */ new Set([
|
|
708
|
+
"FINALITY_NOT_MET",
|
|
709
|
+
"EXPECTED_FINALITY_POLICY_MISMATCH",
|
|
710
|
+
"EXPECTED_CONTRACT_MISMATCH",
|
|
711
|
+
"EXPECTED_CONTRACT_MISSING",
|
|
712
|
+
"REVISION_SCOPE_MISMATCH"
|
|
713
|
+
// Deliberately *not* REVISION_CHAIN_SCOPE_MISMATCH: it fires when any
|
|
714
|
+
// candidate in the chain is off-scope, including a long-superseded one, so a
|
|
715
|
+
// perfectly valid current revision would be reported as missing. Nothing is
|
|
716
|
+
// lost by dropping it — the head-only predicate REVISION_SCOPE_MISMATCH
|
|
717
|
+
// tests the same fields against the revision actually being named.
|
|
718
|
+
//
|
|
719
|
+
// It is a live path, not defence in depth: for a direct-Core obligation the
|
|
720
|
+
// graph assertion does refuse an off-scope revision, but a revision joined
|
|
721
|
+
// through a materialization is checked only for `account_id`, so under
|
|
722
|
+
// managed delivery an off-scope predecessor survives the load and becomes a
|
|
723
|
+
// candidate. The test builds exactly that fixture.
|
|
724
|
+
]);
|
|
725
|
+
function finalizeConsumerStatusPlan(draft, leaf, now) {
|
|
726
|
+
const statusAsOfFloor = latestInstant([draft.establishedAt, usableLeafInstant(leaf.statement, now)]) ?? draft.establishedAt;
|
|
727
|
+
const { establishedAt: _establishedAt, deadlineGap, indeterminate, periodIdentityUnknown, ...carried } = draft;
|
|
728
|
+
const plan = {
|
|
729
|
+
...carried,
|
|
730
|
+
...leaf.statusId ? { supersedesReportingStatusId: leaf.statusId } : {},
|
|
731
|
+
statusAsOfFloor,
|
|
732
|
+
// A statement the buyer can already date is dated now; one that still owes
|
|
733
|
+
// a read is left open for `attestConsumerStatusPlan`.
|
|
734
|
+
...draft.requiresConsumption ? {} : { statusAsOf: statusAsOfFloor }
|
|
735
|
+
};
|
|
736
|
+
if (periodIdentityUnknown) {
|
|
737
|
+
return {
|
|
738
|
+
...plan,
|
|
739
|
+
suppressed: "period_identity_unknown",
|
|
740
|
+
reason: suppressionReason("period_identity_unknown", plan.reason)
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
if (indeterminate) {
|
|
744
|
+
return {
|
|
745
|
+
...plan,
|
|
746
|
+
suppressed: "chain_indeterminate",
|
|
747
|
+
reason: suppressionReason("chain_indeterminate", plan.reason)
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
if (deadlineGap) {
|
|
751
|
+
return {
|
|
752
|
+
...plan,
|
|
753
|
+
suppressed: "deadline_unknown",
|
|
754
|
+
reason: deadlineGapReason(deadlineGap)
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
const suppressed = consumerStatusSuppression(plan, leaf);
|
|
758
|
+
return suppressed ? { ...plan, suppressed, reason: suppressionReason(suppressed, plan.reason) } : plan;
|
|
759
|
+
}
|
|
760
|
+
function deadlineGapReason(gap) {
|
|
761
|
+
switch (gap.cause) {
|
|
762
|
+
case "missing_pin":
|
|
763
|
+
return `no posting deadline: record ExpectedReportingPeriod.${gap.pin} to derive one`;
|
|
764
|
+
case "deadline_overflow":
|
|
765
|
+
return `no posting deadline: ${gap.field} puts it outside the representable range`;
|
|
766
|
+
default:
|
|
767
|
+
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`;
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
function suppressionReason(suppressed, reason) {
|
|
771
|
+
switch (suppressed) {
|
|
772
|
+
case "unchanged":
|
|
773
|
+
return `not posted: the current leaf already says this (${reason})`;
|
|
774
|
+
case "leaf_undisclosed":
|
|
775
|
+
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";
|
|
776
|
+
case "consumption_unavailable":
|
|
777
|
+
return "not posted: no client.getMediaBuyDelivery is wired, so consumption cannot be attested";
|
|
778
|
+
case "posting_unavailable":
|
|
779
|
+
return "not posted: no client.syncReportingStatus is wired, so the buyer cannot append to the status chain";
|
|
780
|
+
case "period_identity_unknown":
|
|
781
|
+
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";
|
|
782
|
+
case "local_budget_exhausted":
|
|
783
|
+
return "not posted: the buyer's own ledgerLimits read budget ran out before the revision could be consumed";
|
|
784
|
+
case "chain_indeterminate":
|
|
785
|
+
return `not posted: ${reason}`;
|
|
786
|
+
default:
|
|
787
|
+
return reason;
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
function consumerStatusSuppression(plan, leaf) {
|
|
791
|
+
if (leaf.undisclosed) return "leaf_undisclosed";
|
|
792
|
+
if (plan.requiresConsumption) return void 0;
|
|
793
|
+
if (leaf.statement && sameConsumerStatement(plan, leaf.statement)) return "unchanged";
|
|
794
|
+
return void 0;
|
|
795
|
+
}
|
|
796
|
+
function sameConsumerStatement(plan, statement) {
|
|
797
|
+
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);
|
|
798
|
+
}
|
|
799
|
+
function sameOptionalSha256(left, right) {
|
|
800
|
+
if (left === void 0 || right === void 0) return left === right;
|
|
801
|
+
return sameSha256(left, right);
|
|
802
|
+
}
|
|
803
|
+
function currentConsumerLeaf(ledger, key, declaredLeafId) {
|
|
804
|
+
const declared = typeof declaredLeafId === "string" ? declaredLeafId : void 0;
|
|
805
|
+
const chain = (ledger.consumerStatuses ?? []).filter(
|
|
806
|
+
(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
|
|
807
|
+
);
|
|
808
|
+
if (declared) {
|
|
809
|
+
const statement2 = chain.find((candidate) => candidate.reporting_status_id === declared);
|
|
810
|
+
return statement2 ? { statusId: declared, statement: statement2, undisclosed: false } : { statusId: declared, undisclosed: true };
|
|
811
|
+
}
|
|
812
|
+
const superseded = new Set(
|
|
813
|
+
chain.map((statement2) => statement2.supersedes_reporting_status_id).filter((value) => typeof value === "string")
|
|
814
|
+
);
|
|
815
|
+
const leaves = chain.filter((statement2) => !superseded.has(statement2.reporting_status_id));
|
|
816
|
+
if (leaves.length !== 1) return { undisclosed: leaves.length > 1 };
|
|
817
|
+
const statement = leaves[0];
|
|
818
|
+
return { statusId: statement.reporting_status_id, statement, undisclosed: false };
|
|
819
|
+
}
|
|
820
|
+
function reportingExpectedAt(obligation, expected) {
|
|
821
|
+
const declared = normalizedInstant(obligation?.expected_at);
|
|
822
|
+
if (declared !== void 0) return declared;
|
|
823
|
+
if (obligation?.expected_at !== void 0) return void 0;
|
|
824
|
+
const slaSeconds = expected.requiredFinality === "official" ? expected.officialAfterSeconds ?? expected.deliverySlaSeconds : expected.deliverySlaSeconds;
|
|
825
|
+
const periodEnd = Date.parse(expected.periodEnd);
|
|
826
|
+
if (typeof slaSeconds === "number" && Number.isFinite(slaSeconds) && slaSeconds >= 0 && Number.isFinite(periodEnd)) {
|
|
827
|
+
const pinned = periodEnd + slaSeconds * 1e3;
|
|
828
|
+
return isRepresentableInstant(pinned) ? new Date(pinned).toISOString() : OVERFLOWED_INSTANT;
|
|
829
|
+
}
|
|
830
|
+
return reportingScheduledExpectedAt(obligation, expected.periodEnd);
|
|
831
|
+
}
|
|
832
|
+
function pinnedExpectedAt(expected) {
|
|
833
|
+
const slaSeconds = expected.requiredFinality === "official" ? expected.officialAfterSeconds ?? expected.deliverySlaSeconds : expected.deliverySlaSeconds;
|
|
834
|
+
const periodEnd = Date.parse(expected.periodEnd);
|
|
835
|
+
if (typeof slaSeconds !== "number" || !Number.isFinite(slaSeconds) || slaSeconds < 0) return void 0;
|
|
836
|
+
if (!Number.isFinite(periodEnd)) return void 0;
|
|
837
|
+
const pinned = periodEnd + slaSeconds * 1e3;
|
|
838
|
+
return isRepresentableInstant(pinned) ? new Date(pinned).toISOString() : void 0;
|
|
839
|
+
}
|
|
840
|
+
function localExpectedAtPin(expected) {
|
|
841
|
+
return expected.requiredFinality === "official" ? "officialAfterSeconds" : "deliverySlaSeconds";
|
|
842
|
+
}
|
|
843
|
+
function reportingScheduledExpectedAt(obligation, periodEnd) {
|
|
844
|
+
const schedule = obligation?.schedule;
|
|
845
|
+
if (typeof schedule?.delivery_sla !== "string") return void 0;
|
|
846
|
+
const duration = parseIso8601Duration(schedule.delivery_sla);
|
|
847
|
+
const anchor = Date.parse(periodEnd);
|
|
848
|
+
if (!duration || !Number.isFinite(anchor)) return void 0;
|
|
849
|
+
if (duration.years === 0 && duration.months === 0 && duration.days === 0) {
|
|
850
|
+
const exact = anchor + duration.seconds * 1e3;
|
|
851
|
+
return isRepresentableInstant(exact) ? new Date(exact).toISOString() : OVERFLOWED_INSTANT;
|
|
852
|
+
}
|
|
853
|
+
const timeZone = calendarTimeZone(obligation, schedule);
|
|
854
|
+
if (timeZone === void 0) return void 0;
|
|
855
|
+
const shifted = addCalendarDuration(anchor, duration, timeZone);
|
|
856
|
+
if (shifted === void 0) return void 0;
|
|
857
|
+
return isRepresentableInstant(shifted) ? new Date(shifted).toISOString() : OVERFLOWED_INSTANT;
|
|
858
|
+
}
|
|
859
|
+
function calendarTimeZone(obligation, schedule) {
|
|
860
|
+
if (schedule.period_timezone !== void 0) return ianaTimeZone(schedule.period_timezone);
|
|
861
|
+
if (schedule.alignment === "utc") return "UTC";
|
|
862
|
+
return ianaTimeZone(obligation?.period?.source_timezone);
|
|
863
|
+
}
|
|
864
|
+
function ianaTimeZone(value) {
|
|
865
|
+
const name = boundedSourceTimezone(value);
|
|
866
|
+
if (name === void 0) return void 0;
|
|
867
|
+
if (/^[+-]/.test(name)) return void 0;
|
|
868
|
+
try {
|
|
869
|
+
new Intl.DateTimeFormat("en-US", { timeZone: name });
|
|
870
|
+
return name;
|
|
871
|
+
} catch {
|
|
872
|
+
return void 0;
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
function parseIso8601Duration(value) {
|
|
876
|
+
const match = /^P(?=\d|T)(?=.*\d)(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)D)?(?:T(?=\d)(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?)?$/.exec(value);
|
|
877
|
+
if (!match) return void 0;
|
|
878
|
+
const [, years, months, days, hours, minutes, seconds] = match;
|
|
879
|
+
return {
|
|
880
|
+
years: Number(years ?? 0),
|
|
881
|
+
months: Number(months ?? 0),
|
|
882
|
+
days: Number(days ?? 0),
|
|
883
|
+
seconds: Number(hours ?? 0) * 3600 + Number(minutes ?? 0) * 60 + Number(seconds ?? 0)
|
|
884
|
+
};
|
|
885
|
+
}
|
|
886
|
+
function addCalendarDuration(instant, duration, timeZone) {
|
|
887
|
+
const parts = zonedParts(instant, timeZone);
|
|
888
|
+
if (!parts) return void 0;
|
|
889
|
+
const totalMonths = parts.month - 1 + duration.years * 12 + duration.months;
|
|
890
|
+
const year = parts.year + Math.floor(totalMonths / 12);
|
|
891
|
+
const month = totalMonths % 12 + 1;
|
|
892
|
+
const clamped = Math.min(parts.day, daysInMonth(year, month));
|
|
893
|
+
const wall = utcWallTime(year, month, clamped + duration.days, parts.hour, parts.minute, parts.second);
|
|
894
|
+
if (wall === void 0) return void 0;
|
|
895
|
+
const resolved = instantForWallTime(wall, timeZone);
|
|
896
|
+
if (resolved === void 0) return void 0;
|
|
897
|
+
const subSecond = (instant % 1e3 + 1e3) % 1e3;
|
|
898
|
+
return resolved + subSecond + duration.seconds * 1e3;
|
|
899
|
+
}
|
|
900
|
+
function isRepresentableInstant(value) {
|
|
901
|
+
return Number.isFinite(value) && value >= MIN_RFC3339_INSTANT && value <= MAX_RFC3339_INSTANT;
|
|
902
|
+
}
|
|
903
|
+
const MIN_RFC3339_INSTANT = -621672192e5;
|
|
904
|
+
const MAX_RFC3339_INSTANT = 253402300799999;
|
|
905
|
+
function daysInMonth(year, month) {
|
|
906
|
+
const probe = /* @__PURE__ */ new Date(0);
|
|
907
|
+
probe.setUTCFullYear(year, month, 0);
|
|
908
|
+
return probe.getUTCDate();
|
|
909
|
+
}
|
|
910
|
+
function utcWallTime(year, month, day, hour, minute, second) {
|
|
911
|
+
const value = /* @__PURE__ */ new Date(0);
|
|
912
|
+
value.setUTCFullYear(year, month - 1, day);
|
|
913
|
+
value.setUTCHours(hour, minute, second, 0);
|
|
914
|
+
const time = value.getTime();
|
|
915
|
+
return isRepresentableInstant(time) ? time : void 0;
|
|
916
|
+
}
|
|
917
|
+
function zonedParts(instant, timeZone) {
|
|
918
|
+
try {
|
|
919
|
+
const formatted = new Intl.DateTimeFormat("en-US", {
|
|
920
|
+
timeZone,
|
|
921
|
+
// Read the era so a proleptic year can be refused rather than silently
|
|
922
|
+
// relocated: without it `en-US` renders year 0 (1 BC) as year `1`.
|
|
923
|
+
era: "short",
|
|
924
|
+
hour12: false,
|
|
925
|
+
year: "numeric",
|
|
926
|
+
month: "2-digit",
|
|
927
|
+
day: "2-digit",
|
|
928
|
+
hour: "2-digit",
|
|
929
|
+
minute: "2-digit",
|
|
930
|
+
second: "2-digit"
|
|
931
|
+
}).formatToParts(new Date(instant));
|
|
932
|
+
const field = (type) => Number(formatted.find((part) => part.type === type)?.value);
|
|
933
|
+
if (/^b/i.test(formatted.find((part) => part.type === "era")?.value ?? "")) return void 0;
|
|
934
|
+
const parts = {
|
|
935
|
+
year: field("year"),
|
|
936
|
+
month: field("month"),
|
|
937
|
+
day: field("day"),
|
|
938
|
+
// Intl renders midnight as hour 24 in some locales' hourCycle.
|
|
939
|
+
hour: field("hour") % 24,
|
|
940
|
+
minute: field("minute"),
|
|
941
|
+
second: field("second")
|
|
942
|
+
};
|
|
943
|
+
return Object.values(parts).every(Number.isFinite) ? parts : void 0;
|
|
944
|
+
} catch {
|
|
945
|
+
return void 0;
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
function instantForWallTime(wall, timeZone) {
|
|
949
|
+
const dayMs = 864e5;
|
|
950
|
+
const offsetBefore = zoneOffset(wall - dayMs, timeZone);
|
|
951
|
+
const offsetAfter = zoneOffset(wall + dayMs, timeZone);
|
|
952
|
+
if (offsetBefore === void 0 || offsetAfter === void 0) return void 0;
|
|
953
|
+
const fromBefore = wall - offsetBefore;
|
|
954
|
+
const fromAfter = wall - offsetAfter;
|
|
955
|
+
if (readsBackAs(fromBefore, wall, timeZone)) return fromBefore;
|
|
956
|
+
if (readsBackAs(fromAfter, wall, timeZone)) return fromAfter;
|
|
957
|
+
return fromBefore;
|
|
958
|
+
}
|
|
959
|
+
function zoneOffset(instant, timeZone) {
|
|
960
|
+
if (!isRepresentableInstant(instant)) return void 0;
|
|
961
|
+
const parts = zonedParts(instant, timeZone);
|
|
962
|
+
if (!parts) return void 0;
|
|
963
|
+
const asUtc = utcWallTime(parts.year, parts.month, parts.day, parts.hour, parts.minute, parts.second);
|
|
964
|
+
return asUtc === void 0 ? void 0 : asUtc - instant;
|
|
965
|
+
}
|
|
966
|
+
function readsBackAs(instant, wall, timeZone) {
|
|
967
|
+
if (!isRepresentableInstant(instant)) return false;
|
|
968
|
+
const parts = zonedParts(instant, timeZone);
|
|
969
|
+
if (!parts) return false;
|
|
970
|
+
return utcWallTime(parts.year, parts.month, parts.day, parts.hour, parts.minute, parts.second) === wall;
|
|
971
|
+
}
|
|
972
|
+
function consumerStatusSchedule(expectedAt, expected, now, malformedExpectedAt, expectedAtOverflowed = false, overflowField = "the derived expected_at") {
|
|
973
|
+
const windowSeconds = expected.automatedRecoveryWindowSeconds;
|
|
974
|
+
if (expectedAtOverflowed) {
|
|
975
|
+
return {
|
|
976
|
+
overdue: false,
|
|
977
|
+
deadlineGap: { cause: "deadline_overflow", field: overflowField }
|
|
978
|
+
};
|
|
979
|
+
}
|
|
980
|
+
if (expectedAt === void 0) {
|
|
981
|
+
return {
|
|
982
|
+
overdue: false,
|
|
983
|
+
deadlineGap: malformedExpectedAt !== void 0 ? { cause: "unreadable_expected_at", value: malformedExpectedAt } : { cause: "missing_pin", pin: localExpectedAtPin(expected) }
|
|
984
|
+
};
|
|
985
|
+
}
|
|
986
|
+
if (typeof windowSeconds !== "number" || !Number.isFinite(windowSeconds) || windowSeconds < 0) {
|
|
987
|
+
return { overdue: false, deadlineGap: { cause: "missing_pin", pin: "automatedRecoveryWindowSeconds" } };
|
|
988
|
+
}
|
|
989
|
+
const deadlineAt = Date.parse(expectedAt) + windowSeconds * 1e3;
|
|
990
|
+
if (!isRepresentableInstant(deadlineAt)) {
|
|
991
|
+
return {
|
|
992
|
+
overdue: false,
|
|
993
|
+
deadlineGap: { cause: "deadline_overflow", field: "ExpectedReportingPeriod.automatedRecoveryWindowSeconds" }
|
|
994
|
+
};
|
|
995
|
+
}
|
|
996
|
+
const deadline = new Date(deadlineAt).toISOString();
|
|
997
|
+
return { deadline, overdue: now.getTime() >= Date.parse(deadline) };
|
|
998
|
+
}
|
|
999
|
+
const RFC3339_INSTANT = /^(\d{4})-(\d{2})-(\d{2})[Tt\s](\d{2}):(\d{2}):(\d{2})(?:\.\d+)?(?:[Zz]|[+-]\d{2}(?::?(\d{2}))?)$/;
|
|
1000
|
+
function normalizedInstant(value) {
|
|
1001
|
+
if (typeof value !== "string") return void 0;
|
|
1002
|
+
const match = RFC3339_INSTANT.exec(value);
|
|
1003
|
+
if (!match) return void 0;
|
|
1004
|
+
const [, isoYear, isoMonth, isoDay, isoHour, isoMinute, isoSecond, offsetMinute] = match;
|
|
1005
|
+
const year = Number(isoYear);
|
|
1006
|
+
const month = Number(isoMonth);
|
|
1007
|
+
const day = Number(isoDay);
|
|
1008
|
+
if (month < 1 || month > 12 || day < 1 || day > daysInMonth(year, month)) return void 0;
|
|
1009
|
+
if (Number(isoHour) > 23 || Number(isoMinute) > 59) return void 0;
|
|
1010
|
+
if (offsetMinute !== void 0 && Number(offsetMinute) > 59) return void 0;
|
|
1011
|
+
const leapSecond = isoSecond === "60";
|
|
1012
|
+
if (leapSecond && utcMinuteOfDay(value, Number(isoHour), Number(isoMinute)) !== 23 * 60 + 59) return void 0;
|
|
1013
|
+
let candidate = value.replace(/([+-]\d{2})$/, "$1:00");
|
|
1014
|
+
if (leapSecond) candidate = candidate.replace(/:60/, ":59");
|
|
1015
|
+
const parsed = Date.parse(candidate);
|
|
1016
|
+
if (!Number.isFinite(parsed)) return void 0;
|
|
1017
|
+
const instant = parsed + (leapSecond ? 1e3 : 0);
|
|
1018
|
+
return isRepresentableInstant(instant) ? new Date(instant).toISOString() : void 0;
|
|
1019
|
+
}
|
|
1020
|
+
function boundedSourceTimezone(value) {
|
|
1021
|
+
return typeof value === "string" && value.length > 0 && value.length <= 255 ? value : void 0;
|
|
1022
|
+
}
|
|
1023
|
+
function utcMinuteOfDay(value, hour, minute) {
|
|
1024
|
+
const offset = /([+-])(\d{2}):?(\d{2})?$/.exec(value);
|
|
1025
|
+
const offsetMinutes = offset ? (offset[1] === "-" ? -1 : 1) * (Number(offset[2]) * 60 + Number(offset[3] ?? 0)) : 0;
|
|
1026
|
+
return ((hour * 60 + minute - offsetMinutes) % 1440 + 1440) % 1440;
|
|
1027
|
+
}
|
|
1028
|
+
function boundedDiagnostic(value) {
|
|
1029
|
+
if (typeof value !== "string") return "";
|
|
1030
|
+
return value.slice(0, 256).replace(/[\u0000-\u001f\u007f]+/g, " ");
|
|
1031
|
+
}
|
|
1032
|
+
function latestInstant(values) {
|
|
1033
|
+
let latest;
|
|
1034
|
+
for (const value of values) {
|
|
1035
|
+
const normalized = normalizedInstant(value);
|
|
1036
|
+
if (normalized === void 0) continue;
|
|
1037
|
+
const parsed = Date.parse(normalized);
|
|
1038
|
+
if (latest === void 0 || parsed > Date.parse(latest)) latest = normalized;
|
|
1039
|
+
}
|
|
1040
|
+
return latest;
|
|
1041
|
+
}
|
|
1042
|
+
function usableLeafInstant(statement, now) {
|
|
1043
|
+
const value = normalizedInstant(statement?.status_as_of);
|
|
1044
|
+
if (value === void 0 || Date.parse(value) > now.getTime()) return void 0;
|
|
1045
|
+
return value;
|
|
1046
|
+
}
|
|
1047
|
+
function contractFactsFor(obligation, expected) {
|
|
1048
|
+
return {
|
|
1049
|
+
...Array.isArray(obligation.media_buy_ids) ? { mediaBuyIds: obligation.media_buy_ids } : {},
|
|
1050
|
+
...obligation.coverage?.covered_package_ids ? { coveredPackageIds: obligation.coverage.covered_package_ids } : {},
|
|
1051
|
+
period: { start: expected.periodStart, end: expected.periodEnd },
|
|
1052
|
+
...expected.committedMetrics ? { committedMetrics: expected.committedMetrics } : {},
|
|
1053
|
+
...expected.metricUnits ? { metricUnits: expected.metricUnits } : {}
|
|
1054
|
+
};
|
|
1055
|
+
}
|
|
1056
|
+
function collectReportingEscalations(ledger, options) {
|
|
1057
|
+
const operationsContact = options?.operationsContact;
|
|
1058
|
+
const escalations = [];
|
|
1059
|
+
for (const obligation of ledger.obligations) {
|
|
1060
|
+
const declaredIssues = obligation.issues;
|
|
1061
|
+
for (const candidate of Array.isArray(declaredIssues) ? declaredIssues : []) {
|
|
1062
|
+
if (typeof candidate !== "object" || candidate === null || Array.isArray(candidate)) continue;
|
|
1063
|
+
const issue = candidate;
|
|
1064
|
+
const recommendedAction = boundedDiagnostic(String(issue.recommended_action ?? ""));
|
|
1065
|
+
escalations.push({
|
|
1066
|
+
reportingObligationId: obligation.reporting_obligation_id,
|
|
1067
|
+
issueId: boundedDiagnostic(String(issue.issue_id ?? "")),
|
|
1068
|
+
code: boundedDiagnostic(String(issue.code ?? "")),
|
|
1069
|
+
severity: boundedDiagnostic(String(issue.severity ?? "")),
|
|
1070
|
+
responsibleParty: boundedDiagnostic(String(issue.responsible_party ?? "")),
|
|
1071
|
+
recommendedAction,
|
|
1072
|
+
...typeof issue.opened_at === "string" ? { openedAt: issue.opened_at } : {},
|
|
1073
|
+
...typeof issue.issue_state === "string" ? { issueState: issue.issue_state } : {},
|
|
1074
|
+
...typeof issue.external_ref === "string" ? { externalRef: boundedDiagnostic(issue.external_ref) } : {},
|
|
1075
|
+
...typeof issue.reporting_status_id === "string" ? { reportingStatusId: issue.reporting_status_id } : {},
|
|
1076
|
+
...operationsContact ? { operationsContact } : {},
|
|
1077
|
+
requiresHumanContact: recommendedAction.startsWith("contact_")
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
return escalations;
|
|
1082
|
+
}
|
|
1083
|
+
function isReportingPeriodShape(value) {
|
|
1084
|
+
if (typeof value !== "object" || value === null) return false;
|
|
1085
|
+
const period = value;
|
|
1086
|
+
return typeof period.start === "string" && typeof period.end === "string";
|
|
1087
|
+
}
|
|
572
1088
|
function expectedPeriodMatches(expected, obligation, ledger) {
|
|
1089
|
+
if (!isReportingPeriodShape(obligation.period)) return false;
|
|
573
1090
|
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)) {
|
|
574
1091
|
return false;
|
|
575
1092
|
}
|
|
@@ -598,8 +1115,8 @@ function expectedIdentityKey(value) {
|
|
|
598
1115
|
value.feed_purpose,
|
|
599
1116
|
value.reporting_profile,
|
|
600
1117
|
value.destination_ref,
|
|
601
|
-
value.period
|
|
602
|
-
value.period
|
|
1118
|
+
value.period?.start,
|
|
1119
|
+
value.period?.end
|
|
603
1120
|
]);
|
|
604
1121
|
}
|
|
605
1122
|
function buildExpectedIdentityIndex(obligations, expectedPeriods) {
|
|
@@ -615,7 +1132,7 @@ function buildExpectedIdentityIndex(obligations, expectedPeriods) {
|
|
|
615
1132
|
}
|
|
616
1133
|
return { expectedByIdentity, obligationCounts };
|
|
617
1134
|
}
|
|
618
|
-
function evaluateReportingLedger(ledger, expectedPeriods, now = /* @__PURE__ */ new Date()) {
|
|
1135
|
+
function evaluateReportingLedger(ledger, expectedPeriods, now = /* @__PURE__ */ new Date(), operationsContact) {
|
|
619
1136
|
assertDirectReportingLedgerGraph(ledger);
|
|
620
1137
|
const obligationResults = [];
|
|
621
1138
|
const uniqueRevisions = /* @__PURE__ */ new Map();
|
|
@@ -631,7 +1148,10 @@ function evaluateReportingLedger(ledger, expectedPeriods, now = /* @__PURE__ */
|
|
|
631
1148
|
const selected = selectCurrent(obligation, ledger, expected);
|
|
632
1149
|
const reasons = [...selected.reasons];
|
|
633
1150
|
if (matchingExpected.length > 0 && !bijective) reasons.push("EXPECTED_PERIOD_NOT_BIJECTIVE");
|
|
634
|
-
if (obligation.health !== "complete")
|
|
1151
|
+
if (obligation.health !== "complete") {
|
|
1152
|
+
const health = typeof obligation.health === "string" ? boundedDiagnostic(obligation.health) : "UNKNOWN";
|
|
1153
|
+
reasons.push(`OBLIGATION_${health.toUpperCase()}`);
|
|
1154
|
+
}
|
|
635
1155
|
if (selected.materialization?.resource && new Date(selected.materialization.resource.expires_at) <= now)
|
|
636
1156
|
reasons.push("RESOURCE_EXPIRED");
|
|
637
1157
|
if (!obligation.resource_retained_until || selected.materialization?.resource && Date.parse(selected.materialization.resource.expires_at) < Date.parse(obligation.resource_retained_until)) {
|
|
@@ -655,12 +1175,18 @@ function evaluateReportingLedger(ledger, expectedPeriods, now = /* @__PURE__ */
|
|
|
655
1175
|
const missingExpectedPeriods = (expectedPeriods ?? []).filter(
|
|
656
1176
|
(expected) => !ledger.obligations.some((obligation) => expectedPeriodMatches(expected, obligation, ledger))
|
|
657
1177
|
);
|
|
1178
|
+
const consumerStatuses = planReportingConsumerStatuses(ledger, expectedPeriods ?? [], missingExpectedPeriods, now);
|
|
1179
|
+
const escalations = collectReportingEscalations(ledger, { ...operationsContact ? { operationsContact } : {} });
|
|
658
1180
|
const scopeDefinitive = ledger.scope.scope_closed && ledger.scope.coverage_complete;
|
|
659
1181
|
return {
|
|
660
1182
|
definitive: expectedPeriods !== void 0 && scopeDefinitive && missingExpectedPeriods.length === 0 && obligationResults.every((item) => item.definitive),
|
|
661
1183
|
ledger,
|
|
662
1184
|
obligations: obligationResults,
|
|
663
1185
|
missingExpectedPeriods,
|
|
1186
|
+
consumerStatuses,
|
|
1187
|
+
postedConsumerStatuses: [],
|
|
1188
|
+
failedConsumerStatuses: [],
|
|
1189
|
+
escalations,
|
|
664
1190
|
totalsByRevision: [...uniqueRevisions.values()].map((item) => ({
|
|
665
1191
|
reportingRevisionId: item.reporting_revision_id,
|
|
666
1192
|
rowCount: item.row_count,
|
|
@@ -871,11 +1397,531 @@ async function reconcileReporting(options) {
|
|
|
871
1397
|
options.ledgerLimits
|
|
872
1398
|
);
|
|
873
1399
|
}
|
|
1400
|
+
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
1401
|
+
const evaluated = evaluateReportingLedger(ledger, options.expectedPeriods, now, options.operationsContact);
|
|
1402
|
+
const readDeadline = Date.now() + (options.ledgerLimits?.maxLoadMs ?? 6e4);
|
|
1403
|
+
const attested = [];
|
|
1404
|
+
let budgetExhausted = false;
|
|
1405
|
+
for (const plan of evaluated.consumerStatuses) {
|
|
1406
|
+
if (!plan.overdue) {
|
|
1407
|
+
attested.push(plan);
|
|
1408
|
+
continue;
|
|
1409
|
+
}
|
|
1410
|
+
if (plan.suppressed !== void 0) {
|
|
1411
|
+
attested.push(plan);
|
|
1412
|
+
continue;
|
|
1413
|
+
}
|
|
1414
|
+
if (budgetExhausted || plan.requiresConsumption && Date.now() >= readDeadline) {
|
|
1415
|
+
attested.push(
|
|
1416
|
+
plan.requiresConsumption ? {
|
|
1417
|
+
...plan,
|
|
1418
|
+
suppressed: "local_budget_exhausted",
|
|
1419
|
+
reason: suppressionReason("local_budget_exhausted", plan.reason)
|
|
1420
|
+
} : plan
|
|
1421
|
+
);
|
|
1422
|
+
continue;
|
|
1423
|
+
}
|
|
1424
|
+
const next = await attestConsumerStatusPlan(plan, ledger, options, readDeadline, now);
|
|
1425
|
+
if (next.suppressed === "local_budget_exhausted" && next.budgetScope === "run") budgetExhausted = true;
|
|
1426
|
+
const { budgetScope: _scope, ...carried } = next;
|
|
1427
|
+
attested.push(carried);
|
|
1428
|
+
}
|
|
1429
|
+
const consumerStatuses = attested;
|
|
1430
|
+
const postedConsumerStatuses = [];
|
|
1431
|
+
const failedConsumerStatuses = [];
|
|
1432
|
+
const confirmed = [];
|
|
1433
|
+
if (!options.client.syncReportingStatus) {
|
|
1434
|
+
for (const [index, plan] of consumerStatuses.entries()) {
|
|
1435
|
+
if (!plan.overdue || plan.suppressed !== void 0) continue;
|
|
1436
|
+
consumerStatuses[index] = {
|
|
1437
|
+
...plan,
|
|
1438
|
+
suppressed: "posting_unavailable",
|
|
1439
|
+
reason: suppressionReason("posting_unavailable", plan.reason)
|
|
1440
|
+
};
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
const owed = [];
|
|
1444
|
+
const claimedChains = /* @__PURE__ */ new Set();
|
|
1445
|
+
for (const plan of consumerStatuses) {
|
|
1446
|
+
if (!plan.overdue || plan.suppressed !== void 0) continue;
|
|
1447
|
+
const chain = canonical(pendingConsumerStatusKey(ledger.accountId, plan));
|
|
1448
|
+
if (claimedChains.has(chain)) {
|
|
1449
|
+
failedConsumerStatuses.push({
|
|
1450
|
+
plan,
|
|
1451
|
+
errors: [
|
|
1452
|
+
{
|
|
1453
|
+
code: "DUPLICATE_STATUS_CHAIN",
|
|
1454
|
+
message: "two expected periods resolve to one consumer-status chain; a batch may carry at most one statement per chain"
|
|
1455
|
+
}
|
|
1456
|
+
]
|
|
1457
|
+
});
|
|
1458
|
+
continue;
|
|
1459
|
+
}
|
|
1460
|
+
claimedChains.add(chain);
|
|
1461
|
+
owed.push(plan);
|
|
1462
|
+
}
|
|
1463
|
+
if (owed.length > 0 && options.client.syncReportingStatus) {
|
|
1464
|
+
for (let offset = 0; offset < owed.length; offset += CONSUMER_STATUS_BATCH_MAX) {
|
|
1465
|
+
const batch = owed.slice(offset, offset + CONSUMER_STATUS_BATCH_MAX);
|
|
1466
|
+
const deadline = Date.now() + (options.ledgerLimits?.maxLoadMs ?? 6e4);
|
|
1467
|
+
const wireStatuses = [];
|
|
1468
|
+
const posted = [];
|
|
1469
|
+
for (const plan of batch) {
|
|
1470
|
+
const key = pendingConsumerStatusKey(ledger.accountId, plan);
|
|
1471
|
+
const fingerprint = consumerStatusClaimFingerprint(plan);
|
|
1472
|
+
const pending = await options.pendingConsumerStatusStore?.get(key);
|
|
1473
|
+
if (pending && pending.claimFingerprint === fingerprint && replayMatchesPlan(pending.statement, plan, now)) {
|
|
1474
|
+
wireStatuses.push(pending.statement);
|
|
1475
|
+
posted.push({
|
|
1476
|
+
...plan,
|
|
1477
|
+
statusAsOf: String(pending.statement.status_as_of),
|
|
1478
|
+
...typeof pending.statement.observed_revision_content_sha256 === "string" ? { observedRevisionContentSha256: pending.statement.observed_revision_content_sha256 } : {}
|
|
1479
|
+
});
|
|
1480
|
+
continue;
|
|
1481
|
+
}
|
|
1482
|
+
const statement = wireConsumerStatus(plan);
|
|
1483
|
+
await options.pendingConsumerStatusStore?.put(key, { statement, claimFingerprint: fingerprint });
|
|
1484
|
+
wireStatuses.push(statement);
|
|
1485
|
+
posted.push(plan);
|
|
1486
|
+
}
|
|
1487
|
+
let response;
|
|
1488
|
+
try {
|
|
1489
|
+
response = await callBeforeDeadline(
|
|
1490
|
+
(signal) => options.client.syncReportingStatus(
|
|
1491
|
+
{
|
|
1492
|
+
...options.request.account ? { account: options.request.account } : {},
|
|
1493
|
+
idempotency_key: consumerStatusBatchKey(wireStatuses),
|
|
1494
|
+
statuses: wireStatuses
|
|
1495
|
+
},
|
|
1496
|
+
{ signal }
|
|
1497
|
+
),
|
|
1498
|
+
deadline,
|
|
1499
|
+
"CONSUMER_STATUS_WRITE_FAILED",
|
|
1500
|
+
"sync_reporting_status exceeded the reporting request deadline"
|
|
1501
|
+
);
|
|
1502
|
+
} catch (error) {
|
|
1503
|
+
recordConsumerStatusBatchFailure(
|
|
1504
|
+
failedConsumerStatuses,
|
|
1505
|
+
posted.length === batch.length ? posted : batch,
|
|
1506
|
+
boundedDiagnostic(error instanceof Error ? error.message : "sync_reporting_status failed")
|
|
1507
|
+
);
|
|
1508
|
+
break;
|
|
1509
|
+
}
|
|
1510
|
+
const results = Array.isArray(response.results) ? response.results : [];
|
|
1511
|
+
if (response.status !== "completed" || results.length !== batch.length) {
|
|
1512
|
+
recordConsumerStatusBatchFailure(
|
|
1513
|
+
failedConsumerStatuses,
|
|
1514
|
+
batch,
|
|
1515
|
+
"seller did not return one result per submitted consumer status"
|
|
1516
|
+
);
|
|
1517
|
+
break;
|
|
1518
|
+
}
|
|
1519
|
+
posted.forEach((plan, index) => {
|
|
1520
|
+
const result = results[index];
|
|
1521
|
+
if (result && ["recorded", "unchanged"].includes(result.result ?? "")) {
|
|
1522
|
+
postedConsumerStatuses.push(plan);
|
|
1523
|
+
confirmed.push(pendingConsumerStatusKey(ledger.accountId, plan));
|
|
1524
|
+
return;
|
|
1525
|
+
}
|
|
1526
|
+
failedConsumerStatuses.push({
|
|
1527
|
+
plan,
|
|
1528
|
+
...typeof result?.reporting_status_id === "string" ? { reportingStatusId: result.reporting_status_id } : {},
|
|
1529
|
+
// Bounded: seller objects of arbitrary size and shape that land
|
|
1530
|
+
// wherever the adopter logs its reconciliation result.
|
|
1531
|
+
errors: Array.isArray(result?.errors) ? result.errors.slice(0, 16) : []
|
|
1532
|
+
});
|
|
1533
|
+
});
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
for (const key of confirmed) await options.pendingConsumerStatusStore?.clear(key);
|
|
1537
|
+
const consumerStatusPending = await readReportingConsumerStatusPending(options);
|
|
1538
|
+
return {
|
|
1539
|
+
...evaluated,
|
|
1540
|
+
consumerStatuses,
|
|
1541
|
+
submittedReceipts: newReceipts,
|
|
1542
|
+
postedConsumerStatuses,
|
|
1543
|
+
failedConsumerStatuses,
|
|
1544
|
+
...consumerStatusPending !== void 0 ? { consumerStatusPending } : {}
|
|
1545
|
+
};
|
|
1546
|
+
}
|
|
1547
|
+
async function attestConsumerStatusPlan(plan, ledger, options, deadline, now) {
|
|
1548
|
+
if (!plan.requiresConsumption || !plan.reportingRevisionId) return plan;
|
|
1549
|
+
if (!options.client.getMediaBuyDelivery) {
|
|
1550
|
+
return {
|
|
1551
|
+
...plan,
|
|
1552
|
+
suppressed: "consumption_unavailable",
|
|
1553
|
+
reason: suppressionReason("consumption_unavailable", plan.reason)
|
|
1554
|
+
};
|
|
1555
|
+
}
|
|
1556
|
+
const outcome = await consumeReportingRevision(options, plan.reportingRevisionId, deadline, now);
|
|
1557
|
+
const leaf = currentConsumerLeaf(ledger, plan, plan.supersedesReportingStatusId);
|
|
1558
|
+
if ("budgetExhausted" in outcome) {
|
|
1559
|
+
return {
|
|
1560
|
+
...plan,
|
|
1561
|
+
suppressed: "local_budget_exhausted",
|
|
1562
|
+
reason: suppressionReason("local_budget_exhausted", plan.reason),
|
|
1563
|
+
budgetScope: outcome.budgetExhausted
|
|
1564
|
+
};
|
|
1565
|
+
}
|
|
1566
|
+
if ("failureCode" in outcome) {
|
|
1567
|
+
return withSuppression(
|
|
1568
|
+
{
|
|
1569
|
+
...plan,
|
|
1570
|
+
consumerStatus: "unreadable",
|
|
1571
|
+
failureCode: outcome.failureCode,
|
|
1572
|
+
mismatchCode: void 0,
|
|
1573
|
+
observedRevisionContentSha256: void 0,
|
|
1574
|
+
requiresConsumption: false,
|
|
1575
|
+
statusAsOf: latestInstant([now.toISOString(), plan.statusAsOfFloor]) ?? plan.statusAsOfFloor,
|
|
1576
|
+
reason: outcome.detail
|
|
1577
|
+
},
|
|
1578
|
+
leaf
|
|
1579
|
+
);
|
|
1580
|
+
}
|
|
1581
|
+
const declared = ledger.revisions.find(
|
|
1582
|
+
(revision2) => revision2.reporting_revision_id === plan.reportingRevisionId
|
|
1583
|
+
)?.revision_content_sha256;
|
|
1584
|
+
if (declared !== void 0 && !sameSha256(outcome.digest, declared)) {
|
|
1585
|
+
return withSuppression(
|
|
1586
|
+
{
|
|
1587
|
+
...plan,
|
|
1588
|
+
consumerStatus: "unreadable",
|
|
1589
|
+
failureCode: "integrity_mismatch",
|
|
1590
|
+
mismatchCode: void 0,
|
|
1591
|
+
observedRevisionContentSha256: void 0,
|
|
1592
|
+
requiresConsumption: false,
|
|
1593
|
+
statusAsOf: latestInstant([outcome.consumedAt, plan.statusAsOfFloor]) ?? plan.statusAsOfFloor,
|
|
1594
|
+
reason: "the consumed rows do not hash to the revision_content_sha256 the ledger declares"
|
|
1595
|
+
},
|
|
1596
|
+
leaf
|
|
1597
|
+
);
|
|
1598
|
+
}
|
|
1599
|
+
const expected = options.expectedPeriods.find(
|
|
1600
|
+
(candidate) => candidate.deliveryConfigId === plan.deliveryConfigId && candidate.deliveryConfigVersion === plan.deliveryConfigVersion && candidate.reportDefinitionId === plan.reportDefinitionId && candidate.periodStart === plan.period.start && candidate.periodEnd === plan.period.end
|
|
1601
|
+
);
|
|
1602
|
+
const obligation = ledger.obligations.find(
|
|
1603
|
+
(candidate) => candidate.reporting_obligation_id === plan.reportingObligationId
|
|
1604
|
+
);
|
|
1605
|
+
const revision = ledger.revisions.find((candidate) => candidate.reporting_revision_id === plan.reportingRevisionId);
|
|
1606
|
+
const mismatch = expected && obligation && revision ? detectReportingContentMismatch(
|
|
1607
|
+
contractFactsFor(obligation, expected),
|
|
1608
|
+
revision,
|
|
1609
|
+
rowEvidenceFor(contractFactsFor(obligation, expected), revision, outcome.rows)
|
|
1610
|
+
) : void 0;
|
|
1611
|
+
return withSuppression(
|
|
1612
|
+
{
|
|
1613
|
+
...plan,
|
|
1614
|
+
consumerStatus: mismatch ? "content_mismatch" : "received",
|
|
1615
|
+
mismatchCode: mismatch?.mismatchCode,
|
|
1616
|
+
observedRevisionContentSha256: outcome.digest,
|
|
1617
|
+
requiresConsumption: false,
|
|
1618
|
+
// `status_as_of` is when the revision became consumable to *this*
|
|
1619
|
+
// consumer, floored by the superseded leaf so the chain never moves
|
|
1620
|
+
// backwards.
|
|
1621
|
+
statusAsOf: latestInstant([outcome.consumedAt, plan.statusAsOfFloor]) ?? plan.statusAsOfFloor,
|
|
1622
|
+
reason: mismatch?.detail ?? "the exact revision content was consumed and honors every frozen contract fact"
|
|
1623
|
+
},
|
|
1624
|
+
leaf
|
|
1625
|
+
);
|
|
1626
|
+
}
|
|
1627
|
+
function withSuppression(plan, leaf) {
|
|
1628
|
+
const suppressed = consumerStatusSuppression(plan, leaf);
|
|
1629
|
+
if (suppressed) return { ...plan, suppressed, reason: suppressionReason(suppressed, plan.reason) };
|
|
1630
|
+
const { suppressed: _cleared, ...unsuppressed } = plan;
|
|
1631
|
+
return unsuppressed;
|
|
1632
|
+
}
|
|
1633
|
+
async function consumeReportingRevision(options, reportingRevisionId, deadline, now) {
|
|
1634
|
+
const read = options.client.getMediaBuyDelivery;
|
|
1635
|
+
const maxPages = options.ledgerLimits?.maxPages ?? 1e3;
|
|
1636
|
+
const maxRows = options.ledgerLimits?.maxRevisionRows ?? 1e5;
|
|
1637
|
+
const rows = [];
|
|
1638
|
+
let bytes = 0;
|
|
1639
|
+
let totalCount;
|
|
1640
|
+
const seenCursors = /* @__PURE__ */ new Set();
|
|
1641
|
+
let binding;
|
|
1642
|
+
let cursor;
|
|
1643
|
+
let pages = 0;
|
|
1644
|
+
try {
|
|
1645
|
+
do {
|
|
1646
|
+
pages += 1;
|
|
1647
|
+
if (pages > maxPages) return { budgetExhausted: "revision" };
|
|
1648
|
+
const response = await callBeforeDeadline(
|
|
1649
|
+
(signal) => read(
|
|
1650
|
+
{
|
|
1651
|
+
...options.request.account ? { account: options.request.account } : {},
|
|
1652
|
+
reporting_revision_id: reportingRevisionId,
|
|
1653
|
+
...cursor ? { pagination: { cursor } } : {}
|
|
1654
|
+
},
|
|
1655
|
+
{ signal }
|
|
1656
|
+
),
|
|
1657
|
+
deadline,
|
|
1658
|
+
"CONSUMER_STATUS_READ_FAILED",
|
|
1659
|
+
"get_media_buy_delivery exceeded the reporting request deadline"
|
|
1660
|
+
);
|
|
1661
|
+
if (response.status !== void 0 && response.status !== "completed") {
|
|
1662
|
+
return { failureCode: "reader_incompatible", detail: "the exact-revision read did not complete" };
|
|
1663
|
+
}
|
|
1664
|
+
const page = response.reporting_revision_binding;
|
|
1665
|
+
if (!page || page.reporting_revision_id !== reportingRevisionId || typeof page.content_sha256 !== "string" || typeof page.row_count !== "number" || !Array.isArray(page.control_totals)) {
|
|
1666
|
+
return {
|
|
1667
|
+
failureCode: "reader_incompatible",
|
|
1668
|
+
detail: "the seller did not return a reporting_revision_binding for the exact revision requested"
|
|
1669
|
+
};
|
|
1670
|
+
}
|
|
1671
|
+
if (binding && !same(binding, page)) {
|
|
1672
|
+
return { failureCode: "integrity_mismatch", detail: "the revision binding changed between cursor pages" };
|
|
1673
|
+
}
|
|
1674
|
+
binding = page;
|
|
1675
|
+
if (typeof response.pagination?.total_count === "number") totalCount = response.pagination.total_count;
|
|
1676
|
+
for (const row of response.reporting_rows ?? []) {
|
|
1677
|
+
rows.push(row);
|
|
1678
|
+
const sized = approximateRowBytes(row);
|
|
1679
|
+
if (sized === TOO_DEEP_TO_SIZE) {
|
|
1680
|
+
return {
|
|
1681
|
+
failureCode: "reader_incompatible",
|
|
1682
|
+
detail: "a revision row nests deeper than this reader will walk"
|
|
1683
|
+
};
|
|
1684
|
+
}
|
|
1685
|
+
if (sized === void 0) {
|
|
1686
|
+
return { budgetExhausted: "revision" };
|
|
1687
|
+
}
|
|
1688
|
+
bytes += sized;
|
|
1689
|
+
if (rows.length > maxRows || bytes > MAX_CONSUMED_REVISION_BYTES) {
|
|
1690
|
+
return { budgetExhausted: "revision" };
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
if (response.pagination?.has_more) {
|
|
1694
|
+
const next = response.pagination.cursor;
|
|
1695
|
+
if (!next || seenCursors.has(next)) {
|
|
1696
|
+
return { failureCode: "transport_failed", detail: "revision row pagination did not advance" };
|
|
1697
|
+
}
|
|
1698
|
+
seenCursors.add(next);
|
|
1699
|
+
cursor = next;
|
|
1700
|
+
} else {
|
|
1701
|
+
cursor = void 0;
|
|
1702
|
+
}
|
|
1703
|
+
} while (cursor);
|
|
1704
|
+
} catch (error) {
|
|
1705
|
+
if (error instanceof ReportingReconciliationError && error.code === "CONSUMER_STATUS_READ_FAILED") {
|
|
1706
|
+
return { budgetExhausted: "run" };
|
|
1707
|
+
}
|
|
1708
|
+
return { failureCode: "transport_failed", detail: "the exact-revision read failed before the rows were complete" };
|
|
1709
|
+
}
|
|
1710
|
+
const consumedAt = now.toISOString();
|
|
1711
|
+
if (!binding) {
|
|
1712
|
+
return { failureCode: "reader_incompatible", detail: "the exact-revision read returned no binding" };
|
|
1713
|
+
}
|
|
1714
|
+
if (totalCount !== void 0 && totalCount !== binding.row_count) {
|
|
1715
|
+
return {
|
|
1716
|
+
failureCode: "integrity_mismatch",
|
|
1717
|
+
detail: `the read declares ${totalCount} total rows and the binding declares ${binding.row_count}`
|
|
1718
|
+
};
|
|
1719
|
+
}
|
|
1720
|
+
if (rows.length !== binding.row_count) {
|
|
1721
|
+
return {
|
|
1722
|
+
failureCode: "integrity_mismatch",
|
|
1723
|
+
detail: `the revision declares ${binding.row_count} rows and the read returned ${rows.length}`
|
|
1724
|
+
};
|
|
1725
|
+
}
|
|
1726
|
+
let digest;
|
|
1727
|
+
try {
|
|
1728
|
+
digest = createHash("sha256").update(
|
|
1729
|
+
canonicalize({
|
|
1730
|
+
reporting_revision_id: reportingRevisionId,
|
|
1731
|
+
row_count: binding.row_count,
|
|
1732
|
+
control_totals: binding.control_totals,
|
|
1733
|
+
reporting_rows: rows
|
|
1734
|
+
}),
|
|
1735
|
+
"utf8"
|
|
1736
|
+
).digest("hex");
|
|
1737
|
+
} catch (error) {
|
|
1738
|
+
if (error instanceof RangeError) return { budgetExhausted: "revision" };
|
|
1739
|
+
const detail = error instanceof Error ? boundedDiagnostic(error.message) : "";
|
|
1740
|
+
return {
|
|
1741
|
+
failureCode: "reader_incompatible",
|
|
1742
|
+
detail: detail ? `the revision rows could not be canonicalized: ${detail}` : "the revision rows could not be canonicalized"
|
|
1743
|
+
};
|
|
1744
|
+
}
|
|
1745
|
+
if (!sameSha256(digest, binding.content_sha256)) {
|
|
1746
|
+
return {
|
|
1747
|
+
failureCode: "integrity_mismatch",
|
|
1748
|
+
detail: "the recomputed revision binding digest does not match the content_sha256 the seller published"
|
|
1749
|
+
};
|
|
1750
|
+
}
|
|
1751
|
+
return { digest, consumedAt, rowCount: rows.length, rows };
|
|
1752
|
+
}
|
|
1753
|
+
const TOO_DEEP_TO_SIZE = -1;
|
|
1754
|
+
function approximateRowBytes(row) {
|
|
1755
|
+
let containers = MAX_ROW_ESTIMATE_CONTAINERS;
|
|
1756
|
+
const visit = (value, depth) => {
|
|
1757
|
+
if (typeof value === "string") return 16 + value.length * 2;
|
|
1758
|
+
if (value === null || typeof value !== "object") return 8;
|
|
1759
|
+
if (depth >= MAX_ROW_ESTIMATE_DEPTH) return TOO_DEEP_TO_SIZE;
|
|
1760
|
+
containers -= 1;
|
|
1761
|
+
if (containers < 0) return void 0;
|
|
1762
|
+
if (Array.isArray(value)) {
|
|
1763
|
+
let total2 = 40 + 8 * value.length;
|
|
1764
|
+
for (const item of value) {
|
|
1765
|
+
const child = visit(item, depth + 1);
|
|
1766
|
+
if (child === void 0 || child === TOO_DEEP_TO_SIZE) return child;
|
|
1767
|
+
total2 += child;
|
|
1768
|
+
}
|
|
1769
|
+
return total2;
|
|
1770
|
+
}
|
|
1771
|
+
const entries = Object.entries(value);
|
|
1772
|
+
let total = 40 + 8 * entries.length;
|
|
1773
|
+
for (const [key, child] of entries) {
|
|
1774
|
+
const sized = visit(child, depth + 1);
|
|
1775
|
+
if (sized === void 0 || sized === TOO_DEEP_TO_SIZE) return sized;
|
|
1776
|
+
total += key.length * 2 + sized;
|
|
1777
|
+
}
|
|
1778
|
+
return total;
|
|
1779
|
+
};
|
|
1780
|
+
return visit(row, 0);
|
|
1781
|
+
}
|
|
1782
|
+
function rowEvidenceFor(facts, revision, rows) {
|
|
1783
|
+
if (!facts.committedMetrics) return {};
|
|
1784
|
+
const observed = new Set((revision.control_totals ?? []).map((total) => total.name));
|
|
1785
|
+
const record = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
1786
|
+
for (const row of rows) {
|
|
1787
|
+
const fields = record(row);
|
|
1788
|
+
if (!fields) continue;
|
|
1789
|
+
for (const key of Object.keys(fields)) observed.add(key);
|
|
1790
|
+
const totals = record(fields.totals);
|
|
1791
|
+
if (totals) for (const key of Object.keys(totals)) observed.add(key);
|
|
1792
|
+
}
|
|
1793
|
+
return { observedMetricNames: [...observed] };
|
|
1794
|
+
}
|
|
1795
|
+
const REPLAYABLE_STATEMENT_KEYS = /* @__PURE__ */ new Set([
|
|
1796
|
+
"reporting_status_id",
|
|
1797
|
+
"supersedes_reporting_status_id",
|
|
1798
|
+
"delivery_config_id",
|
|
1799
|
+
"delivery_config_version",
|
|
1800
|
+
"report_definition_id",
|
|
1801
|
+
"period",
|
|
1802
|
+
"consumer_status",
|
|
1803
|
+
"status_as_of",
|
|
1804
|
+
"reporting_obligation_id",
|
|
1805
|
+
"reporting_revision_id",
|
|
1806
|
+
"observed_revision_content_sha256",
|
|
1807
|
+
"mismatch_code",
|
|
1808
|
+
"failure_code"
|
|
1809
|
+
]);
|
|
1810
|
+
function replayMatchesPlan(statement, plan, now) {
|
|
1811
|
+
const period = statement.period;
|
|
1812
|
+
if (typeof statement.status_as_of !== "string") return false;
|
|
1813
|
+
if (usableLeafInstant({ status_as_of: statement.status_as_of }, now) === void 0) {
|
|
1814
|
+
return false;
|
|
1815
|
+
}
|
|
1816
|
+
if (Object.keys(statement).some((key) => !REPLAYABLE_STATEMENT_KEYS.has(key))) return false;
|
|
1817
|
+
if (!sameOptionalSha256(
|
|
1818
|
+
statement.observed_revision_content_sha256,
|
|
1819
|
+
plan.observedRevisionContentSha256
|
|
1820
|
+
)) {
|
|
1821
|
+
return false;
|
|
1822
|
+
}
|
|
1823
|
+
if (statement.reporting_status_id !== consumerStatusId({ ...plan, statusAsOf: statement.status_as_of })) {
|
|
1824
|
+
return false;
|
|
1825
|
+
}
|
|
1826
|
+
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";
|
|
1827
|
+
}
|
|
1828
|
+
function recordConsumerStatusBatchFailure(failed, batch, message) {
|
|
1829
|
+
for (const plan of batch) {
|
|
1830
|
+
failed.push({ plan, errors: [{ code: "CONSUMER_STATUS_WRITE_FAILED", message }] });
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
function consumerStatusId(plan) {
|
|
1834
|
+
return `adcp-sdk.${createHash("sha256").update(canonicalize([...consumerStatusClaim(plan), plan.statusAsOf ?? null])).digest("hex").slice(0, 32)}`;
|
|
1835
|
+
}
|
|
1836
|
+
function consumerStatusClaim(plan) {
|
|
1837
|
+
return [
|
|
1838
|
+
plan.deliveryConfigId,
|
|
1839
|
+
plan.deliveryConfigVersion,
|
|
1840
|
+
plan.reportDefinitionId,
|
|
1841
|
+
plan.period,
|
|
1842
|
+
plan.consumerStatus,
|
|
1843
|
+
plan.mismatchCode ?? null,
|
|
1844
|
+
plan.failureCode ?? null,
|
|
1845
|
+
plan.reportingRevisionId ?? null,
|
|
1846
|
+
plan.observedRevisionContentSha256 ?? null,
|
|
1847
|
+
plan.supersedesReportingStatusId ?? null
|
|
1848
|
+
];
|
|
1849
|
+
}
|
|
1850
|
+
function consumerStatusClaimFingerprint(plan) {
|
|
1851
|
+
return createHash("sha256").update(canonicalize(consumerStatusClaim(plan))).digest("hex");
|
|
1852
|
+
}
|
|
1853
|
+
function pendingConsumerStatusKey(accountId, plan) {
|
|
874
1854
|
return {
|
|
875
|
-
|
|
876
|
-
|
|
1855
|
+
accountId,
|
|
1856
|
+
deliveryConfigId: plan.deliveryConfigId,
|
|
1857
|
+
deliveryConfigVersion: plan.deliveryConfigVersion,
|
|
1858
|
+
reportDefinitionId: plan.reportDefinitionId,
|
|
1859
|
+
periodStart: plan.period.start,
|
|
1860
|
+
periodEnd: plan.period.end
|
|
877
1861
|
};
|
|
878
1862
|
}
|
|
1863
|
+
function consumerStatusBatchKey(statuses) {
|
|
1864
|
+
return `adcp-sdk-batch.${createHash("sha256").update(canonicalize(statuses)).digest("hex").slice(0, 32)}`;
|
|
1865
|
+
}
|
|
1866
|
+
function wireConsumerStatus(plan) {
|
|
1867
|
+
if (!plan.statusAsOf) {
|
|
1868
|
+
throw new ReportingReconciliationError(
|
|
1869
|
+
"CONSUMER_STATUS_UNATTESTED",
|
|
1870
|
+
"cannot post a consumer status before its status_as_of is established"
|
|
1871
|
+
);
|
|
1872
|
+
}
|
|
1873
|
+
if ((plan.consumerStatus === "received" || plan.consumerStatus === "content_mismatch") && !plan.observedRevisionContentSha256) {
|
|
1874
|
+
throw new ReportingReconciliationError(
|
|
1875
|
+
"CONSUMER_STATUS_UNATTESTED",
|
|
1876
|
+
"received and content_mismatch require a locally recomputed revision binding digest"
|
|
1877
|
+
);
|
|
1878
|
+
}
|
|
1879
|
+
return {
|
|
1880
|
+
reporting_status_id: consumerStatusId(plan),
|
|
1881
|
+
...plan.supersedesReportingStatusId ? { supersedes_reporting_status_id: plan.supersedesReportingStatusId } : {},
|
|
1882
|
+
delivery_config_id: plan.deliveryConfigId,
|
|
1883
|
+
delivery_config_version: plan.deliveryConfigVersion,
|
|
1884
|
+
report_definition_id: plan.reportDefinitionId,
|
|
1885
|
+
period: plan.period,
|
|
1886
|
+
consumer_status: plan.consumerStatus,
|
|
1887
|
+
// The buyer's own instant, and part of the ID derivation above so the two
|
|
1888
|
+
// can never disagree. For `received` the spec wants when the revision
|
|
1889
|
+
// became consumable to this consumer, which genuinely moves between
|
|
1890
|
+
// re-plans; `pendingConsumerStatusStore` is what makes a retry reuse it.
|
|
1891
|
+
status_as_of: plan.statusAsOf,
|
|
1892
|
+
...plan.reportingObligationId ? { reporting_obligation_id: plan.reportingObligationId } : {},
|
|
1893
|
+
...plan.reportingRevisionId ? { reporting_revision_id: plan.reportingRevisionId } : {},
|
|
1894
|
+
...plan.observedRevisionContentSha256 ? { observed_revision_content_sha256: plan.observedRevisionContentSha256 } : {},
|
|
1895
|
+
...plan.mismatchCode ? { mismatch_code: plan.mismatchCode } : {},
|
|
1896
|
+
...plan.failureCode ? { failure_code: plan.failureCode } : {}
|
|
1897
|
+
};
|
|
1898
|
+
}
|
|
1899
|
+
async function readReportingConsumerStatusPending(options) {
|
|
1900
|
+
if (!options.client.syncReportingStatus) return void 0;
|
|
1901
|
+
const {
|
|
1902
|
+
health: _health,
|
|
1903
|
+
changes_after: _changesAfter,
|
|
1904
|
+
pagination: _pagination,
|
|
1905
|
+
reporting_revision_id: _revisionId,
|
|
1906
|
+
...summaryRequest
|
|
1907
|
+
} = options.request;
|
|
1908
|
+
try {
|
|
1909
|
+
const summary = await callBeforeDeadline(
|
|
1910
|
+
(signal) => options.client.getReportingStatus(
|
|
1911
|
+
{ ...summaryRequest, view: "summary" },
|
|
1912
|
+
{ signal }
|
|
1913
|
+
),
|
|
1914
|
+
Date.now() + (options.ledgerLimits?.maxLoadMs ?? 6e4),
|
|
1915
|
+
"CONSUMER_STATUS_PENDING_READ_FAILED",
|
|
1916
|
+
"summary get_reporting_status exceeded the reporting request deadline"
|
|
1917
|
+
);
|
|
1918
|
+
const counts = summary.obligation_counts;
|
|
1919
|
+
const pending = counts?.consumer_status_pending;
|
|
1920
|
+
return typeof pending === "number" && Number.isInteger(pending) && pending >= 0 ? pending : void 0;
|
|
1921
|
+
} catch {
|
|
1922
|
+
return void 0;
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
879
1925
|
export {
|
|
880
1926
|
ReportingReconciliationError,
|
|
881
1927
|
buildReportingReceipt,
|