@adcp/sdk 12.0.0 → 12.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/adapters/content-standards-adapter.mjs +0 -6
- package/dist/lib/adapters/content-standards-adapter.mjs.map +1 -1
- package/dist/lib/adapters/derived-account-store.mjs +0 -6
- package/dist/lib/adapters/derived-account-store.mjs.map +1 -1
- package/dist/lib/adapters/governance-adapter.mjs +0 -6
- package/dist/lib/adapters/governance-adapter.mjs.map +1 -1
- package/dist/lib/adapters/implicit-account-store.mjs +0 -6
- package/dist/lib/adapters/implicit-account-store.mjs.map +1 -1
- package/dist/lib/adapters/index.mjs +0 -6
- package/dist/lib/adapters/index.mjs.map +1 -1
- package/dist/lib/adapters/legacy/v2-5/create_media_buy.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/create_media_buy.mjs.map +1 -1
- package/dist/lib/adapters/legacy/v2-5/get_products.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/get_products.mjs.map +1 -1
- package/dist/lib/adapters/legacy/v2-5/index.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/index.mjs.map +1 -1
- package/dist/lib/adapters/legacy/v2-5/list_creative_formats.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/list_creative_formats.mjs.map +1 -1
- package/dist/lib/adapters/legacy/v2-5/preview_creative.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/preview_creative.mjs.map +1 -1
- package/dist/lib/adapters/legacy/v2-5/sync_creatives.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/sync_creatives.mjs.map +1 -1
- package/dist/lib/adapters/legacy/v2-5/types.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/update_media_buy.mjs +0 -6
- package/dist/lib/adapters/legacy/v2-5/update_media_buy.mjs.map +1 -1
- package/dist/lib/adapters/oauth-passthrough-resolver.mjs +0 -6
- package/dist/lib/adapters/oauth-passthrough-resolver.mjs.map +1 -1
- package/dist/lib/adapters/property-list-adapter.mjs +0 -6
- package/dist/lib/adapters/property-list-adapter.mjs.map +1 -1
- package/dist/lib/adapters/roster-account-store.mjs +0 -6
- package/dist/lib/adapters/roster-account-store.mjs.map +1 -1
- package/dist/lib/adapters/si-session-manager.mjs +0 -6
- package/dist/lib/adapters/si-session-manager.mjs.map +1 -1
- package/dist/lib/adapters/version/3.0/brand-fields.mjs +0 -6
- package/dist/lib/adapters/version/3.0/brand-fields.mjs.map +1 -1
- package/dist/lib/adapters/version/3.0/get-products.mjs +0 -6
- package/dist/lib/adapters/version/3.0/get-products.mjs.map +1 -1
- package/dist/lib/adapters/version/3.0/index.mjs +0 -6
- package/dist/lib/adapters/version/3.0/index.mjs.map +1 -1
- package/dist/lib/adapters/version/3.0/sync-accounts.mjs +0 -6
- package/dist/lib/adapters/version/3.0/sync-accounts.mjs.map +1 -1
- package/dist/lib/adapters/version/index.mjs +0 -6
- package/dist/lib/adapters/version/index.mjs.map +1 -1
- package/dist/lib/adapters/version/types.mjs +0 -6
- package/dist/lib/advanced.mjs +0 -6
- package/dist/lib/advanced.mjs.map +1 -1
- package/dist/lib/agents/index.generated.mjs +0 -6
- package/dist/lib/agents/index.generated.mjs.map +1 -1
- package/dist/lib/auth/index.mjs +0 -6
- package/dist/lib/auth/index.mjs.map +1 -1
- package/dist/lib/auth/oauth/CLIFlowHandler.mjs +0 -6
- package/dist/lib/auth/oauth/CLIFlowHandler.mjs.map +1 -1
- package/dist/lib/auth/oauth/ClientCredentialsFlow.mjs +0 -6
- package/dist/lib/auth/oauth/ClientCredentialsFlow.mjs.map +1 -1
- package/dist/lib/auth/oauth/MCPOAuthProvider.mjs +0 -6
- package/dist/lib/auth/oauth/MCPOAuthProvider.mjs.map +1 -1
- package/dist/lib/auth/oauth/NonInteractiveFlowHandler.mjs +0 -6
- package/dist/lib/auth/oauth/NonInteractiveFlowHandler.mjs.map +1 -1
- package/dist/lib/auth/oauth/authorization-required.mjs +0 -6
- package/dist/lib/auth/oauth/authorization-required.mjs.map +1 -1
- package/dist/lib/auth/oauth/diagnose.mjs +0 -6
- package/dist/lib/auth/oauth/diagnose.mjs.map +1 -1
- package/dist/lib/auth/oauth/diagnostics.mjs +0 -6
- package/dist/lib/auth/oauth/diagnostics.mjs.map +1 -1
- package/dist/lib/auth/oauth/discovery.mjs +0 -6
- package/dist/lib/auth/oauth/discovery.mjs.map +1 -1
- package/dist/lib/auth/oauth/file-storage.mjs +0 -6
- package/dist/lib/auth/oauth/file-storage.mjs.map +1 -1
- package/dist/lib/auth/oauth/index.mjs +0 -6
- package/dist/lib/auth/oauth/index.mjs.map +1 -1
- package/dist/lib/auth/oauth/secret-resolver.mjs +0 -6
- package/dist/lib/auth/oauth/secret-resolver.mjs.map +1 -1
- package/dist/lib/auth/oauth/storage-registry.mjs +0 -6
- package/dist/lib/auth/oauth/storage-registry.mjs.map +1 -1
- package/dist/lib/auth/oauth/types.mjs +0 -6
- package/dist/lib/auth/oauth/types.mjs.map +1 -1
- package/dist/lib/auth/oauth/web-flow.mjs +0 -6
- package/dist/lib/auth/oauth/web-flow.mjs.map +1 -1
- package/dist/lib/brand/index.mjs +0 -6
- package/dist/lib/brand/index.mjs.map +1 -1
- package/dist/lib/canonical-references/index.mjs +6 -7
- package/dist/lib/canonical-references/index.mjs.map +1 -1
- package/dist/lib/client/index.mjs +0 -6
- package/dist/lib/client/index.mjs.map +1 -1
- package/dist/lib/compliance/index.mjs +0 -6
- package/dist/lib/compliance/index.mjs.map +1 -1
- package/dist/lib/compliance-fixtures/index.mjs +0 -6
- package/dist/lib/compliance-fixtures/index.mjs.map +1 -1
- package/dist/lib/compliance-fixtures/test-authorization-server.mjs +0 -6
- package/dist/lib/compliance-fixtures/test-authorization-server.mjs.map +1 -1
- package/dist/lib/conformance/index.mjs +0 -6
- package/dist/lib/conformance/index.mjs.map +1 -1
- package/dist/lib/conformance/invariants/uniformError.mjs +0 -6
- package/dist/lib/conformance/invariants/uniformError.mjs.map +1 -1
- package/dist/lib/conformance/invariants/uniformErrorComparator.mjs +0 -6
- package/dist/lib/conformance/invariants/uniformErrorComparator.mjs.map +1 -1
- package/dist/lib/conformance/oracle.mjs +0 -6
- package/dist/lib/conformance/oracle.mjs.map +1 -1
- package/dist/lib/conformance/runConformance.mjs +0 -6
- package/dist/lib/conformance/runConformance.mjs.map +1 -1
- package/dist/lib/conformance/runners.mjs +0 -6
- package/dist/lib/conformance/runners.mjs.map +1 -1
- package/dist/lib/conformance/schemaArbitrary.mjs +0 -6
- package/dist/lib/conformance/schemaArbitrary.mjs.map +1 -1
- package/dist/lib/conformance/schemaLoader.mjs +5 -6
- package/dist/lib/conformance/schemaLoader.mjs.map +1 -1
- package/dist/lib/conformance/seeder.mjs +0 -6
- package/dist/lib/conformance/seeder.mjs.map +1 -1
- package/dist/lib/conformance/types.mjs +0 -6
- package/dist/lib/conformance/types.mjs.map +1 -1
- package/dist/lib/conformance/webhook.mjs +0 -6
- package/dist/lib/conformance/webhook.mjs.map +1 -1
- package/dist/lib/core/ADCPMultiAgentClient.mjs +0 -6
- package/dist/lib/core/ADCPMultiAgentClient.mjs.map +1 -1
- package/dist/lib/core/AgentClient.mjs +0 -6
- package/dist/lib/core/AgentClient.mjs.map +1 -1
- package/dist/lib/core/AsyncHandler.mjs +0 -6
- package/dist/lib/core/AsyncHandler.mjs.map +1 -1
- package/dist/lib/core/ConfigurationManager.mjs +0 -6
- package/dist/lib/core/ConfigurationManager.mjs.map +1 -1
- package/dist/lib/core/ConversationTypes.mjs +0 -6
- package/dist/lib/core/CreativeAgentClient.mjs +0 -6
- package/dist/lib/core/CreativeAgentClient.mjs.map +1 -1
- package/dist/lib/core/GovernanceMiddleware.mjs +0 -6
- package/dist/lib/core/GovernanceMiddleware.mjs.map +1 -1
- package/dist/lib/core/GovernanceTypes.mjs +0 -6
- package/dist/lib/core/GovernanceTypes.mjs.map +1 -1
- package/dist/lib/core/ProtocolResponseParser.mjs +0 -6
- package/dist/lib/core/ProtocolResponseParser.mjs.map +1 -1
- package/dist/lib/core/ResponseValidator.mjs +0 -6
- package/dist/lib/core/ResponseValidator.mjs.map +1 -1
- package/dist/lib/core/SingleAgentClient.mjs +0 -6
- package/dist/lib/core/SingleAgentClient.mjs.map +1 -1
- package/dist/lib/core/TaskEventTypes.mjs +0 -6
- package/dist/lib/core/TaskEventTypes.mjs.map +1 -1
- package/dist/lib/core/TaskExecutor.mjs +0 -6
- package/dist/lib/core/TaskExecutor.mjs.map +1 -1
- package/dist/lib/core/match.mjs +0 -6
- package/dist/lib/core/match.mjs.map +1 -1
- package/dist/lib/core/task-status.mjs +0 -6
- package/dist/lib/core/task-status.mjs.map +1 -1
- package/dist/lib/core/webhook-url.mjs +0 -6
- package/dist/lib/core/webhook-url.mjs.map +1 -1
- package/dist/lib/discovery/adagents-redirects.mjs +0 -6
- package/dist/lib/discovery/adagents-redirects.mjs.map +1 -1
- package/dist/lib/discovery/agent-directory.mjs +0 -6
- package/dist/lib/discovery/agent-directory.mjs.map +1 -1
- package/dist/lib/discovery/inline-publisher-properties.mjs +0 -6
- package/dist/lib/discovery/inline-publisher-properties.mjs.map +1 -1
- package/dist/lib/discovery/network-consistency-checker.mjs +0 -6
- package/dist/lib/discovery/network-consistency-checker.mjs.map +1 -1
- package/dist/lib/discovery/property-crawler.mjs +0 -6
- package/dist/lib/discovery/property-crawler.mjs.map +1 -1
- package/dist/lib/discovery/property-index.mjs +0 -6
- package/dist/lib/discovery/property-index.mjs.map +1 -1
- package/dist/lib/discovery/publisher-property-selector.mjs +0 -6
- package/dist/lib/discovery/publisher-property-selector.mjs.map +1 -1
- package/dist/lib/discovery/resolve-agent-properties.mjs +0 -6
- package/dist/lib/discovery/resolve-agent-properties.mjs.map +1 -1
- package/dist/lib/discovery/types.mjs +0 -6
- package/dist/lib/discovery/validate-adagents.mjs +0 -6
- package/dist/lib/discovery/validate-adagents.mjs.map +1 -1
- package/dist/lib/enums.mjs +0 -6
- package/dist/lib/enums.mjs.map +1 -1
- package/dist/lib/errors/index.mjs +0 -6
- package/dist/lib/errors/index.mjs.map +1 -1
- package/dist/lib/express-mcp/index.mjs +0 -6
- package/dist/lib/express-mcp/index.mjs.map +1 -1
- package/dist/lib/governance/index.mjs +0 -6
- package/dist/lib/governance/index.mjs.map +1 -1
- package/dist/lib/handlers/types.mjs +0 -6
- package/dist/lib/handlers/types.mjs.map +1 -1
- package/dist/lib/index.mjs +0 -6
- package/dist/lib/index.mjs.map +1 -1
- package/dist/lib/media-buy/available-actions.mjs +0 -6
- package/dist/lib/media-buy/available-actions.mjs.map +1 -1
- package/dist/lib/media-buy/index.mjs +0 -6
- package/dist/lib/media-buy/index.mjs.map +1 -1
- package/dist/lib/media-buy/preflight.mjs +0 -6
- package/dist/lib/media-buy/preflight.mjs.map +1 -1
- package/dist/lib/media-buy/property-policy.mjs +0 -6
- package/dist/lib/media-buy/property-policy.mjs.map +1 -1
- package/dist/lib/media-buy/types.mjs +0 -6
- package/dist/lib/media-buy/types.mjs.map +1 -1
- package/dist/lib/media-buy/update-fields.generated.mjs +0 -6
- package/dist/lib/media-buy/update-fields.generated.mjs.map +1 -1
- package/dist/lib/mock-server/creative-ad-server/seed-data.mjs +0 -6
- package/dist/lib/mock-server/creative-ad-server/seed-data.mjs.map +1 -1
- package/dist/lib/mock-server/creative-ad-server/server.mjs +0 -6
- package/dist/lib/mock-server/creative-ad-server/server.mjs.map +1 -1
- package/dist/lib/mock-server/creative-template/seed-data.mjs +0 -6
- package/dist/lib/mock-server/creative-template/seed-data.mjs.map +1 -1
- package/dist/lib/mock-server/creative-template/server.mjs +0 -6
- package/dist/lib/mock-server/creative-template/server.mjs.map +1 -1
- package/dist/lib/mock-server/index.mjs +0 -6
- package/dist/lib/mock-server/index.mjs.map +1 -1
- package/dist/lib/mock-server/sales-guaranteed/recipe.mjs +0 -6
- package/dist/lib/mock-server/sales-guaranteed/recipe.mjs.map +1 -1
- package/dist/lib/mock-server/sales-guaranteed/seed-data.mjs +0 -6
- package/dist/lib/mock-server/sales-guaranteed/seed-data.mjs.map +1 -1
- package/dist/lib/mock-server/sales-guaranteed/server.mjs +0 -6
- package/dist/lib/mock-server/sales-guaranteed/server.mjs.map +1 -1
- package/dist/lib/mock-server/sales-non-guaranteed/recipe.mjs +0 -6
- package/dist/lib/mock-server/sales-non-guaranteed/recipe.mjs.map +1 -1
- package/dist/lib/mock-server/sales-non-guaranteed/seed-data.mjs +0 -6
- package/dist/lib/mock-server/sales-non-guaranteed/seed-data.mjs.map +1 -1
- package/dist/lib/mock-server/sales-non-guaranteed/server.mjs +0 -6
- package/dist/lib/mock-server/sales-non-guaranteed/server.mjs.map +1 -1
- package/dist/lib/mock-server/sales-social/seed-data.mjs +0 -6
- package/dist/lib/mock-server/sales-social/seed-data.mjs.map +1 -1
- package/dist/lib/mock-server/sales-social/server.mjs +0 -6
- package/dist/lib/mock-server/sales-social/server.mjs.map +1 -1
- package/dist/lib/mock-server/scenario.mjs +0 -6
- package/dist/lib/mock-server/scenario.mjs.map +1 -1
- package/dist/lib/mock-server/signal-marketplace/seed-data.mjs +0 -6
- package/dist/lib/mock-server/signal-marketplace/seed-data.mjs.map +1 -1
- package/dist/lib/mock-server/signal-marketplace/server.mjs +0 -6
- package/dist/lib/mock-server/signal-marketplace/server.mjs.map +1 -1
- package/dist/lib/mock-server/sponsored-intelligence/seed-data.mjs +0 -6
- package/dist/lib/mock-server/sponsored-intelligence/seed-data.mjs.map +1 -1
- package/dist/lib/mock-server/sponsored-intelligence/server.mjs +0 -6
- package/dist/lib/mock-server/sponsored-intelligence/server.mjs.map +1 -1
- package/dist/lib/net/address-guards.mjs +0 -6
- package/dist/lib/net/address-guards.mjs.map +1 -1
- package/dist/lib/net/index.mjs +0 -6
- package/dist/lib/net/index.mjs.map +1 -1
- package/dist/lib/net/ssrf-fetch.mjs +0 -6
- package/dist/lib/net/ssrf-fetch.mjs.map +1 -1
- package/dist/lib/observability/index.mjs +0 -6
- package/dist/lib/observability/index.mjs.map +1 -1
- package/dist/lib/observability/tracing.mjs +6 -7
- package/dist/lib/observability/tracing.mjs.map +1 -1
- package/dist/lib/protocols/a2a.mjs +0 -6
- package/dist/lib/protocols/a2a.mjs.map +1 -1
- package/dist/lib/protocols/abort.mjs +0 -6
- package/dist/lib/protocols/abort.mjs.map +1 -1
- package/dist/lib/protocols/index.mjs +0 -6
- package/dist/lib/protocols/index.mjs.map +1 -1
- package/dist/lib/protocols/mcp-modern.mjs +0 -6
- package/dist/lib/protocols/mcp-modern.mjs.map +1 -1
- package/dist/lib/protocols/mcp-tasks.mjs +0 -6
- package/dist/lib/protocols/mcp-tasks.mjs.map +1 -1
- package/dist/lib/protocols/mcp.mjs +0 -6
- package/dist/lib/protocols/mcp.mjs.map +1 -1
- package/dist/lib/protocols/rawResponseCapture.mjs +0 -6
- package/dist/lib/protocols/rawResponseCapture.mjs.map +1 -1
- package/dist/lib/protocols/responseSizeLimit.mjs +0 -6
- package/dist/lib/protocols/responseSizeLimit.mjs.map +1 -1
- package/dist/lib/protocols/transportDiagnostics.mjs +0 -6
- package/dist/lib/protocols/transportDiagnostics.mjs.map +1 -1
- package/dist/lib/registry/cursor-store.mjs +0 -6
- package/dist/lib/registry/cursor-store.mjs.map +1 -1
- package/dist/lib/registry/feed-stream.mjs +0 -6
- package/dist/lib/registry/feed-stream.mjs.map +1 -1
- package/dist/lib/registry/index.mjs +0 -6
- package/dist/lib/registry/index.mjs.map +1 -1
- package/dist/lib/registry/property-registry.mjs +0 -6
- package/dist/lib/registry/property-registry.mjs.map +1 -1
- package/dist/lib/registry/sync.mjs +0 -6
- package/dist/lib/registry/sync.mjs.map +1 -1
- package/dist/lib/registry/types.generated.mjs +0 -6
- package/dist/lib/registry/types.mjs +0 -6
- package/dist/lib/schemas/index.mjs +0 -6
- package/dist/lib/schemas/index.mjs.map +1 -1
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/server/a2a-adapter.mjs +0 -6
- package/dist/lib/server/a2a-adapter.mjs.map +1 -1
- package/dist/lib/server/account-mode.mjs +0 -6
- package/dist/lib/server/account-mode.mjs.map +1 -1
- package/dist/lib/server/adcp-server.mjs +0 -6
- package/dist/lib/server/adcp-server.mjs.map +1 -1
- package/dist/lib/server/auth-introspection.mjs +0 -6
- package/dist/lib/server/auth-introspection.mjs.map +1 -1
- package/dist/lib/server/auth-signature.mjs +0 -6
- package/dist/lib/server/auth-signature.mjs.map +1 -1
- package/dist/lib/server/auth.mjs +0 -6
- package/dist/lib/server/auth.mjs.map +1 -1
- package/dist/lib/server/create-adcp-server.mjs +0 -6
- package/dist/lib/server/create-adcp-server.mjs.map +1 -1
- package/dist/lib/server/credential-policy.mjs +0 -6
- package/dist/lib/server/credential-policy.mjs.map +1 -1
- package/dist/lib/server/ctx-metadata/backends/memory.mjs +0 -6
- package/dist/lib/server/ctx-metadata/backends/memory.mjs.map +1 -1
- package/dist/lib/server/ctx-metadata/backends/pg.mjs +0 -6
- package/dist/lib/server/ctx-metadata/backends/pg.mjs.map +1 -1
- package/dist/lib/server/ctx-metadata/backends/redis.mjs +0 -6
- package/dist/lib/server/ctx-metadata/backends/redis.mjs.map +1 -1
- package/dist/lib/server/ctx-metadata/index.mjs +0 -6
- package/dist/lib/server/ctx-metadata/index.mjs.map +1 -1
- package/dist/lib/server/ctx-metadata/store.mjs +0 -6
- package/dist/lib/server/ctx-metadata/store.mjs.map +1 -1
- package/dist/lib/server/ctx-metadata/wire-shape.mjs +0 -6
- package/dist/lib/server/ctx-metadata/wire-shape.mjs.map +1 -1
- package/dist/lib/server/decisioning/account.mjs +0 -6
- package/dist/lib/server/decisioning/account.mjs.map +1 -1
- package/dist/lib/server/decisioning/admin-router.mjs +0 -6
- package/dist/lib/server/decisioning/admin-router.mjs.map +1 -1
- package/dist/lib/server/decisioning/assembly-helpers.mjs +0 -6
- package/dist/lib/server/decisioning/assembly-helpers.mjs.map +1 -1
- package/dist/lib/server/decisioning/async-outcome.mjs +0 -6
- package/dist/lib/server/decisioning/async-outcome.mjs.map +1 -1
- package/dist/lib/server/decisioning/buyer-agent.mjs +0 -6
- package/dist/lib/server/decisioning/buyer-agent.mjs.map +1 -1
- package/dist/lib/server/decisioning/capabilities.mjs +0 -6
- package/dist/lib/server/decisioning/capabilities.mjs.map +1 -1
- package/dist/lib/server/decisioning/compose.mjs +0 -6
- package/dist/lib/server/decisioning/compose.mjs.map +1 -1
- package/dist/lib/server/decisioning/context.mjs +0 -6
- package/dist/lib/server/decisioning/errors-typed.mjs +0 -6
- package/dist/lib/server/decisioning/errors-typed.mjs.map +1 -1
- package/dist/lib/server/decisioning/helpers.mjs +0 -6
- package/dist/lib/server/decisioning/helpers.mjs.map +1 -1
- package/dist/lib/server/decisioning/index.mjs +0 -6
- package/dist/lib/server/decisioning/index.mjs.map +1 -1
- package/dist/lib/server/decisioning/list-helpers.mjs +0 -6
- package/dist/lib/server/decisioning/list-helpers.mjs.map +1 -1
- package/dist/lib/server/decisioning/manifest-helpers.mjs +0 -6
- package/dist/lib/server/decisioning/manifest-helpers.mjs.map +1 -1
- package/dist/lib/server/decisioning/pagination.mjs +0 -6
- package/dist/lib/server/decisioning/platform-helpers.mjs +0 -6
- package/dist/lib/server/decisioning/platform-helpers.mjs.map +1 -1
- package/dist/lib/server/decisioning/platform.mjs +0 -6
- package/dist/lib/server/decisioning/proposal/dispatch.mjs +0 -6
- package/dist/lib/server/decisioning/proposal/dispatch.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/index.mjs +0 -6
- package/dist/lib/server/decisioning/proposal/index.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/lifecycle.mjs +0 -6
- package/dist/lib/server/decisioning/proposal/lifecycle.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/mock-manager.mjs +0 -6
- package/dist/lib/server/decisioning/proposal/mock-manager.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/store.mjs +0 -6
- package/dist/lib/server/decisioning/proposal/store.mjs.map +1 -1
- package/dist/lib/server/decisioning/proposal/types.mjs +0 -6
- package/dist/lib/server/decisioning/proposal/types.mjs.map +1 -1
- package/dist/lib/server/decisioning/resolve-presets.mjs +0 -6
- package/dist/lib/server/decisioning/resolve-presets.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/entity-hydration.generated.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/entity-hydration.generated.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/from-platform.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/observed-modes.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/observed-modes.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/postgres-task-registry.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/postgres-task-registry.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/protocol-for-tool.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/protocol-for-tool.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/task-registry.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/task-registry.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/to-context.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/to-context.mjs.map +1 -1
- package/dist/lib/server/decisioning/runtime/validate-platform.mjs +0 -6
- package/dist/lib/server/decisioning/runtime/validate-platform.mjs.map +1 -1
- package/dist/lib/server/decisioning/specialisms/audiences.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/brand-rights.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/campaign-governance.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/content-standards.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/creative-ad-server.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/creative.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/lists.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/sales.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/signals.mjs +0 -6
- package/dist/lib/server/decisioning/specialisms/sponsored-intelligence.mjs +0 -6
- package/dist/lib/server/decisioning/start-time.mjs +0 -6
- package/dist/lib/server/decisioning/start-time.mjs.map +1 -1
- package/dist/lib/server/decisioning/status-changes.mjs +0 -6
- package/dist/lib/server/decisioning/status-changes.mjs.map +1 -1
- package/dist/lib/server/decisioning/status-mappers.mjs +0 -6
- package/dist/lib/server/decisioning/status-mappers.mjs.map +1 -1
- package/dist/lib/server/decisioning/tenant-registry.mjs +0 -6
- package/dist/lib/server/decisioning/tenant-registry.mjs.map +1 -1
- package/dist/lib/server/decisioning/tenant-store.mjs +0 -6
- package/dist/lib/server/decisioning/tenant-store.mjs.map +1 -1
- package/dist/lib/server/decisioning/validate-specialisms.mjs +0 -6
- package/dist/lib/server/decisioning/validate-specialisms.mjs.map +1 -1
- package/dist/lib/server/dynamic-registry.mjs +0 -6
- package/dist/lib/server/dynamic-registry.mjs.map +1 -1
- package/dist/lib/server/envelope-allowlist.mjs +0 -6
- package/dist/lib/server/envelope-allowlist.mjs.map +1 -1
- package/dist/lib/server/error-arm-tools.mjs +5 -6
- package/dist/lib/server/error-arm-tools.mjs.map +1 -1
- package/dist/lib/server/errors.mjs +0 -6
- package/dist/lib/server/errors.mjs.map +1 -1
- package/dist/lib/server/example-tld-guard.mjs +0 -6
- package/dist/lib/server/example-tld-guard.mjs.map +1 -1
- package/dist/lib/server/express-adapter.mjs +0 -6
- package/dist/lib/server/express-adapter.mjs.map +1 -1
- package/dist/lib/server/governance.mjs +0 -6
- package/dist/lib/server/governance.mjs.map +1 -1
- package/dist/lib/server/idempotency/backends/lazy.mjs +0 -6
- package/dist/lib/server/idempotency/backends/lazy.mjs.map +1 -1
- package/dist/lib/server/idempotency/backends/memory.mjs +0 -6
- package/dist/lib/server/idempotency/backends/memory.mjs.map +1 -1
- package/dist/lib/server/idempotency/backends/pg.mjs +0 -6
- package/dist/lib/server/idempotency/backends/pg.mjs.map +1 -1
- package/dist/lib/server/idempotency/backends/redis.mjs +0 -6
- package/dist/lib/server/idempotency/backends/redis.mjs.map +1 -1
- package/dist/lib/server/idempotency/index.mjs +0 -6
- package/dist/lib/server/idempotency/index.mjs.map +1 -1
- package/dist/lib/server/idempotency/store.mjs +0 -6
- package/dist/lib/server/idempotency/store.mjs.map +1 -1
- package/dist/lib/server/index.mjs +0 -6
- package/dist/lib/server/index.mjs.map +1 -1
- package/dist/lib/server/legacy/v5/index.mjs +0 -6
- package/dist/lib/server/legacy/v5/index.mjs.map +1 -1
- package/dist/lib/server/mcp-modern-server.mjs +0 -6
- package/dist/lib/server/mcp-modern-server.mjs.map +1 -1
- package/dist/lib/server/media-buy-actions.mjs +0 -6
- package/dist/lib/server/media-buy-actions.mjs.map +1 -1
- package/dist/lib/server/media-buy-helpers.mjs +0 -6
- package/dist/lib/server/media-buy-helpers.mjs.map +1 -1
- package/dist/lib/server/media-buy-store.mjs +0 -6
- package/dist/lib/server/media-buy-store.mjs.map +1 -1
- package/dist/lib/server/normalize-errors.mjs +0 -6
- package/dist/lib/server/normalize-errors.mjs.map +1 -1
- package/dist/lib/server/operational-platform.mjs +0 -6
- package/dist/lib/server/operational-platform.mjs.map +1 -1
- package/dist/lib/server/pick-safe-details.mjs +0 -6
- package/dist/lib/server/pick-safe-details.mjs.map +1 -1
- package/dist/lib/server/pin-and-bind-fetch.mjs +0 -6
- package/dist/lib/server/pin-and-bind-fetch.mjs.map +1 -1
- package/dist/lib/server/postgres-state-store.mjs +0 -6
- package/dist/lib/server/postgres-state-store.mjs.map +1 -1
- package/dist/lib/server/postgres-task-store.mjs +0 -6
- package/dist/lib/server/postgres-task-store.mjs.map +1 -1
- package/dist/lib/server/product-defaults.mjs +0 -6
- package/dist/lib/server/product-defaults.mjs.map +1 -1
- package/dist/lib/server/redact.mjs +0 -6
- package/dist/lib/server/redact.mjs.map +1 -1
- package/dist/lib/server/responses.mjs +0 -6
- package/dist/lib/server/responses.mjs.map +1 -1
- package/dist/lib/server/serve.mjs +0 -6
- package/dist/lib/server/serve.mjs.map +1 -1
- package/dist/lib/server/socket-mode/conformance-client.mjs +0 -6
- package/dist/lib/server/socket-mode/conformance-client.mjs.map +1 -1
- package/dist/lib/server/socket-mode/index.mjs +0 -6
- package/dist/lib/server/socket-mode/index.mjs.map +1 -1
- package/dist/lib/server/socket-mode/ws-transport.mjs +0 -6
- package/dist/lib/server/socket-mode/ws-transport.mjs.map +1 -1
- package/dist/lib/server/state-machine.mjs +0 -6
- package/dist/lib/server/state-machine.mjs.map +1 -1
- package/dist/lib/server/state-store.mjs +0 -6
- package/dist/lib/server/state-store.mjs.map +1 -1
- package/dist/lib/server/structured-serialize.mjs +0 -6
- package/dist/lib/server/structured-serialize.mjs.map +1 -1
- package/dist/lib/server/targeting-helpers.mjs +0 -6
- package/dist/lib/server/targeting-helpers.mjs.map +1 -1
- package/dist/lib/server/tasks.mjs +0 -6
- package/dist/lib/server/tasks.mjs.map +1 -1
- package/dist/lib/server/test-controller-bridge.mjs +0 -6
- package/dist/lib/server/test-controller-bridge.mjs.map +1 -1
- package/dist/lib/server/test-controller.mjs +0 -6
- package/dist/lib/server/test-controller.mjs.map +1 -1
- package/dist/lib/server/upstream-helpers.mjs +0 -6
- package/dist/lib/server/upstream-helpers.mjs.map +1 -1
- package/dist/lib/server/webhook-emitter.mjs +0 -6
- package/dist/lib/server/webhook-emitter.mjs.map +1 -1
- package/dist/lib/server/wire-safe.mjs +0 -6
- package/dist/lib/server/wire-safe.mjs.map +1 -1
- package/dist/lib/server/wire-spec-fields.generated.mjs +0 -6
- package/dist/lib/server/wire-spec-fields.generated.mjs.map +1 -1
- package/dist/lib/server/wrap-envelope.mjs +0 -6
- package/dist/lib/server/wrap-envelope.mjs.map +1 -1
- package/dist/lib/signing/agent-context.mjs +0 -6
- package/dist/lib/signing/agent-context.mjs.map +1 -1
- package/dist/lib/signing/agent-fetch.mjs +0 -6
- package/dist/lib/signing/agent-fetch.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/canonicalize.mjs +0 -6
- package/dist/lib/signing/agent-resolver/canonicalize.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/capabilities-types.mjs +0 -6
- package/dist/lib/signing/agent-resolver/capabilities-types.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/consistency.mjs +0 -6
- package/dist/lib/signing/agent-resolver/consistency.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/errors.mjs +0 -6
- package/dist/lib/signing/agent-resolver/errors.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/etld.mjs +0 -6
- package/dist/lib/signing/agent-resolver/etld.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/fetch-helpers.mjs +0 -6
- package/dist/lib/signing/agent-resolver/fetch-helpers.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/index.mjs +0 -6
- package/dist/lib/signing/agent-resolver/index.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/jwks-set.mjs +0 -6
- package/dist/lib/signing/agent-resolver/jwks-set.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/resolve-agent.mjs +0 -6
- package/dist/lib/signing/agent-resolver/resolve-agent.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/select-agent.mjs +0 -6
- package/dist/lib/signing/agent-resolver/select-agent.mjs.map +1 -1
- package/dist/lib/signing/agent-resolver/strict-json.mjs +0 -6
- package/dist/lib/signing/agent-resolver/strict-json.mjs.map +1 -1
- package/dist/lib/signing/brand-jwks.mjs +0 -6
- package/dist/lib/signing/brand-jwks.mjs.map +1 -1
- package/dist/lib/signing/canonicalize.mjs +0 -6
- package/dist/lib/signing/canonicalize.mjs.map +1 -1
- package/dist/lib/signing/capability-cache.mjs +0 -6
- package/dist/lib/signing/capability-cache.mjs.map +1 -1
- package/dist/lib/signing/capability-priming.mjs +0 -6
- package/dist/lib/signing/capability-priming.mjs.map +1 -1
- package/dist/lib/signing/client.mjs +0 -6
- package/dist/lib/signing/client.mjs.map +1 -1
- package/dist/lib/signing/content-digest.mjs +0 -6
- package/dist/lib/signing/content-digest.mjs.map +1 -1
- package/dist/lib/signing/crypto.mjs +0 -6
- package/dist/lib/signing/crypto.mjs.map +1 -1
- package/dist/lib/signing/ecdsa-encoding.mjs +0 -6
- package/dist/lib/signing/ecdsa-encoding.mjs.map +1 -1
- package/dist/lib/signing/errors.mjs +0 -6
- package/dist/lib/signing/errors.mjs.map +1 -1
- package/dist/lib/signing/fetch-async.mjs +0 -6
- package/dist/lib/signing/fetch-async.mjs.map +1 -1
- package/dist/lib/signing/fetch.mjs +0 -6
- package/dist/lib/signing/fetch.mjs.map +1 -1
- package/dist/lib/signing/index.mjs +0 -6
- package/dist/lib/signing/index.mjs.map +1 -1
- package/dist/lib/signing/jwks-helpers.mjs +0 -6
- package/dist/lib/signing/jwks-helpers.mjs.map +1 -1
- package/dist/lib/signing/jwks-https.mjs +0 -6
- package/dist/lib/signing/jwks-https.mjs.map +1 -1
- package/dist/lib/signing/jwks.mjs +0 -6
- package/dist/lib/signing/jwks.mjs.map +1 -1
- package/dist/lib/signing/middleware.mjs +0 -6
- package/dist/lib/signing/middleware.mjs.map +1 -1
- package/dist/lib/signing/parser.mjs +0 -6
- package/dist/lib/signing/parser.mjs.map +1 -1
- package/dist/lib/signing/postgres-replay-store.mjs +0 -6
- package/dist/lib/signing/postgres-replay-store.mjs.map +1 -1
- package/dist/lib/signing/protocol-response.mjs +0 -6
- package/dist/lib/signing/protocol-response.mjs.map +1 -1
- package/dist/lib/signing/provider.mjs +0 -6
- package/dist/lib/signing/redis-replay-store.mjs +0 -6
- package/dist/lib/signing/redis-replay-store.mjs.map +1 -1
- package/dist/lib/signing/replay.mjs +0 -6
- package/dist/lib/signing/replay.mjs.map +1 -1
- package/dist/lib/signing/request-context.mjs +0 -6
- package/dist/lib/signing/request-context.mjs.map +1 -1
- package/dist/lib/signing/revocation-https.mjs +0 -6
- package/dist/lib/signing/revocation-https.mjs.map +1 -1
- package/dist/lib/signing/revocation.mjs +0 -6
- package/dist/lib/signing/revocation.mjs.map +1 -1
- package/dist/lib/signing/server.mjs +0 -6
- package/dist/lib/signing/server.mjs.map +1 -1
- package/dist/lib/signing/signer-async.mjs +0 -6
- package/dist/lib/signing/signer-async.mjs.map +1 -1
- package/dist/lib/signing/signer.mjs +0 -6
- package/dist/lib/signing/signer.mjs.map +1 -1
- package/dist/lib/signing/testing.mjs +0 -6
- package/dist/lib/signing/testing.mjs.map +1 -1
- package/dist/lib/signing/types.mjs +0 -6
- package/dist/lib/signing/types.mjs.map +1 -1
- package/dist/lib/signing/verifier.mjs +0 -6
- package/dist/lib/signing/verifier.mjs.map +1 -1
- package/dist/lib/signing/webhook-auth-detection.mjs +0 -6
- package/dist/lib/signing/webhook-auth-detection.mjs.map +1 -1
- package/dist/lib/signing/webhook-verifier.mjs +0 -6
- package/dist/lib/signing/webhook-verifier.mjs.map +1 -1
- package/dist/lib/storage/MemoryStorage.mjs +0 -6
- package/dist/lib/storage/MemoryStorage.mjs.map +1 -1
- package/dist/lib/storage/interfaces.mjs +0 -6
- package/dist/lib/substitution/encoder/SubstitutionEncoder.mjs +0 -6
- package/dist/lib/substitution/encoder/SubstitutionEncoder.mjs.map +1 -1
- package/dist/lib/substitution/encoder/index.mjs +0 -6
- package/dist/lib/substitution/encoder/index.mjs.map +1 -1
- package/dist/lib/substitution/index.mjs +0 -6
- package/dist/lib/substitution/index.mjs.map +1 -1
- package/dist/lib/substitution/observer/SubstitutionObserver.mjs +0 -6
- package/dist/lib/substitution/observer/SubstitutionObserver.mjs.map +1 -1
- package/dist/lib/substitution/observer/alignment.mjs +0 -6
- package/dist/lib/substitution/observer/alignment.mjs.map +1 -1
- package/dist/lib/substitution/observer/assertions.mjs +0 -6
- package/dist/lib/substitution/observer/assertions.mjs.map +1 -1
- package/dist/lib/substitution/observer/html-parser.mjs +0 -6
- package/dist/lib/substitution/observer/html-parser.mjs.map +1 -1
- package/dist/lib/substitution/observer/index.mjs +0 -6
- package/dist/lib/substitution/observer/index.mjs.map +1 -1
- package/dist/lib/substitution/observer/ssrf.mjs +0 -6
- package/dist/lib/substitution/observer/ssrf.mjs.map +1 -1
- package/dist/lib/substitution/rfc3986.mjs +0 -6
- package/dist/lib/substitution/rfc3986.mjs.map +1 -1
- package/dist/lib/substitution/translate.mjs +0 -6
- package/dist/lib/substitution/translate.mjs.map +1 -1
- package/dist/lib/substitution/types.mjs +0 -6
- package/dist/lib/substitution/vectors.mjs +0 -6
- package/dist/lib/substitution/vectors.mjs.map +1 -1
- package/dist/lib/testing/agent-tester.mjs +0 -6
- package/dist/lib/testing/agent-tester.mjs.map +1 -1
- package/dist/lib/testing/client.mjs +0 -6
- package/dist/lib/testing/client.mjs.map +1 -1
- package/dist/lib/testing/compliance/briefs.mjs +0 -6
- package/dist/lib/testing/compliance/briefs.mjs.map +1 -1
- package/dist/lib/testing/compliance/comply.mjs +0 -6
- package/dist/lib/testing/compliance/comply.mjs.map +1 -1
- package/dist/lib/testing/compliance/index.mjs +0 -6
- package/dist/lib/testing/compliance/index.mjs.map +1 -1
- package/dist/lib/testing/compliance/spec-conformance.mjs +0 -6
- package/dist/lib/testing/compliance/spec-conformance.mjs.map +1 -1
- package/dist/lib/testing/compliance/storyboard-tracks.mjs +0 -6
- package/dist/lib/testing/compliance/storyboard-tracks.mjs.map +1 -1
- package/dist/lib/testing/compliance/summary.mjs +0 -6
- package/dist/lib/testing/compliance/summary.mjs.map +1 -1
- package/dist/lib/testing/compliance/types.mjs +0 -6
- package/dist/lib/testing/comply-controller.mjs +0 -6
- package/dist/lib/testing/comply-controller.mjs.map +1 -1
- package/dist/lib/testing/controller-assertions.mjs +0 -6
- package/dist/lib/testing/controller-assertions.mjs.map +1 -1
- package/dist/lib/testing/formatter.mjs +0 -6
- package/dist/lib/testing/formatter.mjs.map +1 -1
- package/dist/lib/testing/index.mjs +0 -6
- package/dist/lib/testing/index.mjs.map +1 -1
- package/dist/lib/testing/local-agent-runner.mjs +0 -6
- package/dist/lib/testing/local-agent-runner.mjs.map +1 -1
- package/dist/lib/testing/orchestrator.mjs +0 -6
- package/dist/lib/testing/orchestrator.mjs.map +1 -1
- package/dist/lib/testing/personas/index.mjs +0 -6
- package/dist/lib/testing/personas/index.mjs.map +1 -1
- package/dist/lib/testing/scenarios/brand-rights.mjs +0 -6
- package/dist/lib/testing/scenarios/brand-rights.mjs.map +1 -1
- package/dist/lib/testing/scenarios/capabilities.mjs +0 -6
- package/dist/lib/testing/scenarios/capabilities.mjs.map +1 -1
- package/dist/lib/testing/scenarios/creative.mjs +0 -6
- package/dist/lib/testing/scenarios/creative.mjs.map +1 -1
- package/dist/lib/testing/scenarios/deterministic.mjs +0 -6
- package/dist/lib/testing/scenarios/deterministic.mjs.map +1 -1
- package/dist/lib/testing/scenarios/discovery.mjs +0 -6
- package/dist/lib/testing/scenarios/discovery.mjs.map +1 -1
- package/dist/lib/testing/scenarios/edge-cases.mjs +0 -6
- package/dist/lib/testing/scenarios/edge-cases.mjs.map +1 -1
- package/dist/lib/testing/scenarios/error-compliance.mjs +0 -6
- package/dist/lib/testing/scenarios/error-compliance.mjs.map +1 -1
- package/dist/lib/testing/scenarios/governance.mjs +0 -6
- package/dist/lib/testing/scenarios/governance.mjs.map +1 -1
- package/dist/lib/testing/scenarios/health.mjs +0 -6
- package/dist/lib/testing/scenarios/health.mjs.map +1 -1
- package/dist/lib/testing/scenarios/index.mjs +0 -6
- package/dist/lib/testing/scenarios/index.mjs.map +1 -1
- package/dist/lib/testing/scenarios/media-buy.mjs +0 -6
- package/dist/lib/testing/scenarios/media-buy.mjs.map +1 -1
- package/dist/lib/testing/scenarios/schema-compliance.mjs +0 -6
- package/dist/lib/testing/scenarios/schema-compliance.mjs.map +1 -1
- package/dist/lib/testing/scenarios/signals.mjs +0 -6
- package/dist/lib/testing/scenarios/signals.mjs.map +1 -1
- package/dist/lib/testing/scenarios/sponsored-intelligence.mjs +0 -6
- package/dist/lib/testing/scenarios/sponsored-intelligence.mjs.map +1 -1
- package/dist/lib/testing/scenarios/trusted-match.mjs +0 -6
- package/dist/lib/testing/scenarios/trusted-match.mjs.map +1 -1
- package/dist/lib/testing/seed-merge.mjs +0 -6
- package/dist/lib/testing/seed-merge.mjs.map +1 -1
- package/dist/lib/testing/storyboard/agent-routing.mjs +0 -6
- package/dist/lib/testing/storyboard/agent-routing.mjs.map +1 -1
- package/dist/lib/testing/storyboard/assertions.mjs +0 -6
- package/dist/lib/testing/storyboard/assertions.mjs.map +1 -1
- package/dist/lib/testing/storyboard/canonical-format-satisfaction.mjs +0 -6
- package/dist/lib/testing/storyboard/canonical-format-satisfaction.mjs.map +1 -1
- package/dist/lib/testing/storyboard/compliance.mjs +5 -6
- package/dist/lib/testing/storyboard/compliance.mjs.map +1 -1
- package/dist/lib/testing/storyboard/context.mjs +0 -6
- package/dist/lib/testing/storyboard/context.mjs.map +1 -1
- package/dist/lib/testing/storyboard/default-invariants.mjs +0 -6
- package/dist/lib/testing/storyboard/default-invariants.mjs.map +1 -1
- package/dist/lib/testing/storyboard/index.mjs +0 -6
- package/dist/lib/testing/storyboard/index.mjs.map +1 -1
- package/dist/lib/testing/storyboard/junit.mjs +0 -6
- package/dist/lib/testing/storyboard/junit.mjs.map +1 -1
- package/dist/lib/testing/storyboard/loader.mjs +0 -6
- package/dist/lib/testing/storyboard/loader.mjs.map +1 -1
- package/dist/lib/testing/storyboard/parallel-dispatch.mjs +0 -6
- package/dist/lib/testing/storyboard/parallel-dispatch.mjs.map +1 -1
- package/dist/lib/testing/storyboard/path.mjs +0 -6
- package/dist/lib/testing/storyboard/path.mjs.map +1 -1
- package/dist/lib/testing/storyboard/probes.mjs +0 -6
- package/dist/lib/testing/storyboard/probes.mjs.map +1 -1
- package/dist/lib/testing/storyboard/rate-limit-trip.mjs +0 -6
- package/dist/lib/testing/storyboard/rate-limit-trip.mjs.map +1 -1
- package/dist/lib/testing/storyboard/rejection-hints.mjs +0 -6
- package/dist/lib/testing/storyboard/rejection-hints.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-builder.mjs +0 -6
- package/dist/lib/testing/storyboard/request-builder.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/builder.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/builder.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/grader.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/grader.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/index.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/index.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/probe-dispatch.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/probe-dispatch.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/probe.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/probe.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/synthesize.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/synthesize.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/test-kit.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/test-kit.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/types.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/types.mjs.map +1 -1
- package/dist/lib/testing/storyboard/request-signing/vector-loader.mjs +0 -6
- package/dist/lib/testing/storyboard/request-signing/vector-loader.mjs.map +1 -1
- package/dist/lib/testing/storyboard/runner.mjs +0 -6
- package/dist/lib/testing/storyboard/runner.mjs.map +1 -1
- package/dist/lib/testing/storyboard/sandbox-entities.mjs +0 -6
- package/dist/lib/testing/storyboard/sandbox-entities.mjs.map +1 -1
- package/dist/lib/testing/storyboard/seeding.mjs +0 -6
- package/dist/lib/testing/storyboard/seeding.mjs.map +1 -1
- package/dist/lib/testing/storyboard/shape-drift-hints.mjs +0 -6
- package/dist/lib/testing/storyboard/shape-drift-hints.mjs.map +1 -1
- package/dist/lib/testing/storyboard/signer-grader/grader.mjs +0 -6
- package/dist/lib/testing/storyboard/signer-grader/grader.mjs.map +1 -1
- package/dist/lib/testing/storyboard/signer-grader/index.mjs +0 -6
- package/dist/lib/testing/storyboard/signer-grader/index.mjs.map +1 -1
- package/dist/lib/testing/storyboard/signer-grader/types.mjs +0 -6
- package/dist/lib/testing/storyboard/strict-validation-hints.mjs +0 -6
- package/dist/lib/testing/storyboard/strict-validation-hints.mjs.map +1 -1
- package/dist/lib/testing/storyboard/task-map.mjs +0 -6
- package/dist/lib/testing/storyboard/task-map.mjs.map +1 -1
- package/dist/lib/testing/storyboard/test-kit.mjs +0 -6
- package/dist/lib/testing/storyboard/test-kit.mjs.map +1 -1
- package/dist/lib/testing/storyboard/types.mjs +0 -6
- package/dist/lib/testing/storyboard/types.mjs.map +1 -1
- package/dist/lib/testing/storyboard/validations.mjs +0 -6
- package/dist/lib/testing/storyboard/validations.mjs.map +1 -1
- package/dist/lib/testing/storyboard/webhook-assertions.mjs +0 -6
- package/dist/lib/testing/storyboard/webhook-assertions.mjs.map +1 -1
- package/dist/lib/testing/storyboard/webhook-receiver.mjs +0 -6
- package/dist/lib/testing/storyboard/webhook-receiver.mjs.map +1 -1
- package/dist/lib/testing/stubs/governance-agent-stub.mjs +0 -6
- package/dist/lib/testing/stubs/governance-agent-stub.mjs.map +1 -1
- package/dist/lib/testing/stubs/index.mjs +0 -6
- package/dist/lib/testing/stubs/index.mjs.map +1 -1
- package/dist/lib/testing/test-controller.mjs +0 -6
- package/dist/lib/testing/test-controller.mjs.map +1 -1
- package/dist/lib/testing/test-helpers.mjs +0 -6
- package/dist/lib/testing/test-helpers.mjs.map +1 -1
- package/dist/lib/testing/types.mjs +0 -6
- package/dist/lib/types/adcp.mjs +0 -6
- package/dist/lib/types/adcp.mjs.map +1 -1
- package/dist/lib/types/asset-instances.mjs +0 -6
- package/dist/lib/types/compat.mjs +0 -6
- package/dist/lib/types/compat.mjs.map +1 -1
- package/dist/lib/types/core.generated.mjs +0 -6
- package/dist/lib/types/enums.generated.mjs +0 -6
- package/dist/lib/types/enums.generated.mjs.map +1 -1
- package/dist/lib/types/error-codes.mjs +0 -6
- package/dist/lib/types/error-codes.mjs.map +1 -1
- package/dist/lib/types/error-details.aliases.mjs +0 -6
- package/dist/lib/types/error-details.aliases.mjs.map +1 -1
- package/dist/lib/types/format-asset-slots.mjs +0 -6
- package/dist/lib/types/forward-compat-error-codes.mjs +0 -6
- package/dist/lib/types/forward-compat-error-codes.mjs.map +1 -1
- package/dist/lib/types/index.mjs +0 -6
- package/dist/lib/types/index.mjs.map +1 -1
- package/dist/lib/types/inline-enums.aliases.mjs +0 -6
- package/dist/lib/types/inline-enums.aliases.mjs.map +1 -1
- package/dist/lib/types/inline-enums.generated.mjs +0 -6
- package/dist/lib/types/inline-enums.generated.mjs.map +1 -1
- package/dist/lib/types/manifest.generated.mjs +0 -6
- package/dist/lib/types/manifest.generated.mjs.map +1 -1
- package/dist/lib/types/schemas.generated.mjs +0 -6
- package/dist/lib/types/schemas.generated.mjs.map +1 -1
- package/dist/lib/types/server-payload-aliases.mjs +0 -6
- package/dist/lib/types/server-payload.mjs +0 -6
- package/dist/lib/types/sync-rows.mjs +0 -6
- package/dist/lib/types/tools.generated.mjs +0 -6
- package/dist/lib/types/v2-5/index.mjs +0 -6
- package/dist/lib/types/v2-5/index.mjs.map +1 -1
- package/dist/lib/types/v2-5/tools.generated.mjs +0 -6
- package/dist/lib/types/v3-1-beta/index.mjs +0 -6
- package/dist/lib/types/v3-1-beta/index.mjs.map +1 -1
- package/dist/lib/types/v3-1-beta/tools.generated.mjs +0 -6
- package/dist/lib/types/wellknown-schemas.generated.mjs +0 -6
- package/dist/lib/types/wellknown-schemas.generated.mjs.map +1 -1
- package/dist/lib/upstream-recorder/constants.mjs +0 -6
- package/dist/lib/upstream-recorder/constants.mjs.map +1 -1
- package/dist/lib/upstream-recorder/index.mjs +0 -6
- package/dist/lib/upstream-recorder/index.mjs.map +1 -1
- package/dist/lib/upstream-recorder/recorder.mjs +0 -6
- package/dist/lib/upstream-recorder/recorder.mjs.map +1 -1
- package/dist/lib/upstream-recorder/types.mjs +0 -6
- package/dist/lib/upstream-recorder/types.mjs.map +1 -1
- package/dist/lib/utils/a2a-artifacts.mjs +0 -6
- package/dist/lib/utils/a2a-artifacts.mjs.map +1 -1
- package/dist/lib/utils/a2a-discovery.mjs +0 -6
- package/dist/lib/utils/a2a-discovery.mjs.map +1 -1
- package/dist/lib/utils/activation-key-builders.mjs +0 -6
- package/dist/lib/utils/activation-key-builders.mjs.map +1 -1
- package/dist/lib/utils/adcp-version-config.mjs +0 -6
- package/dist/lib/utils/adcp-version-config.mjs.map +1 -1
- package/dist/lib/utils/asset-builders.mjs +0 -6
- package/dist/lib/utils/asset-builders.mjs.map +1 -1
- package/dist/lib/utils/build-creative-return-builders.mjs +0 -6
- package/dist/lib/utils/build-creative-return-builders.mjs.map +1 -1
- package/dist/lib/utils/buyer-retry-policy.mjs +0 -6
- package/dist/lib/utils/buyer-retry-policy.mjs.map +1 -1
- package/dist/lib/utils/capabilities.mjs +0 -6
- package/dist/lib/utils/capabilities.mjs.map +1 -1
- package/dist/lib/utils/capability-rollups.mjs +0 -6
- package/dist/lib/utils/capability-rollups.mjs.map +1 -1
- package/dist/lib/utils/creative-adapter.mjs +0 -6
- package/dist/lib/utils/creative-adapter.mjs.map +1 -1
- package/dist/lib/utils/creative-delivery.mjs +0 -6
- package/dist/lib/utils/creative-delivery.mjs.map +1 -1
- package/dist/lib/utils/deprecation.mjs +0 -6
- package/dist/lib/utils/deprecation.mjs.map +1 -1
- package/dist/lib/utils/envelope-status-compat.mjs +0 -6
- package/dist/lib/utils/envelope-status-compat.mjs.map +1 -1
- package/dist/lib/utils/error-extraction.mjs +0 -6
- package/dist/lib/utils/error-extraction.mjs.map +1 -1
- package/dist/lib/utils/extract-result.mjs +0 -6
- package/dist/lib/utils/extract-result.mjs.map +1 -1
- package/dist/lib/utils/format-asset-slot-builders.mjs +0 -6
- package/dist/lib/utils/format-asset-slot-builders.mjs.map +1 -1
- package/dist/lib/utils/format-assets.mjs +0 -6
- package/dist/lib/utils/format-assets.mjs.map +1 -1
- package/dist/lib/utils/format-render-builders.mjs +0 -6
- package/dist/lib/utils/format-render-builders.mjs.map +1 -1
- package/dist/lib/utils/format-renders.mjs +0 -6
- package/dist/lib/utils/format-renders.mjs.map +1 -1
- package/dist/lib/utils/get-products-cache-scope.mjs +0 -6
- package/dist/lib/utils/get-products-cache-scope.mjs.map +1 -1
- package/dist/lib/utils/glob.mjs +0 -6
- package/dist/lib/utils/glob.mjs.map +1 -1
- package/dist/lib/utils/global-async-local-storage.mjs +0 -6
- package/dist/lib/utils/global-async-local-storage.mjs.map +1 -1
- package/dist/lib/utils/idempotency.mjs +0 -6
- package/dist/lib/utils/idempotency.mjs.map +1 -1
- package/dist/lib/utils/index.mjs +0 -6
- package/dist/lib/utils/index.mjs.map +1 -1
- package/dist/lib/utils/jcs.mjs +0 -6
- package/dist/lib/utils/jcs.mjs.map +1 -1
- package/dist/lib/utils/json-depth.mjs +0 -6
- package/dist/lib/utils/json-depth.mjs.map +1 -1
- package/dist/lib/utils/logger.mjs +0 -6
- package/dist/lib/utils/logger.mjs.map +1 -1
- package/dist/lib/utils/media-buy-delivery-notification-builders.mjs +0 -6
- package/dist/lib/utils/media-buy-delivery-notification-builders.mjs.map +1 -1
- package/dist/lib/utils/media-buy-status.mjs +0 -6
- package/dist/lib/utils/media-buy-status.mjs.map +1 -1
- package/dist/lib/utils/pagination.mjs +0 -6
- package/dist/lib/utils/pagination.mjs.map +1 -1
- package/dist/lib/utils/preview-creative-builders.mjs +0 -6
- package/dist/lib/utils/preview-creative-builders.mjs.map +1 -1
- package/dist/lib/utils/preview-normalizer.mjs +0 -6
- package/dist/lib/utils/preview-normalizer.mjs.map +1 -1
- package/dist/lib/utils/preview-utils.mjs +0 -6
- package/dist/lib/utils/preview-utils.mjs.map +1 -1
- package/dist/lib/utils/pricing-adapter.mjs +0 -6
- package/dist/lib/utils/pricing-adapter.mjs.map +1 -1
- package/dist/lib/utils/probe-policy.mjs +0 -6
- package/dist/lib/utils/probe-policy.mjs.map +1 -1
- package/dist/lib/utils/protocol-detection.mjs +0 -6
- package/dist/lib/utils/protocol-detection.mjs.map +1 -1
- package/dist/lib/utils/redact-secrets.mjs +0 -6
- package/dist/lib/utils/redact-secrets.mjs.map +1 -1
- package/dist/lib/utils/redis-default-prefix-warn.mjs +0 -6
- package/dist/lib/utils/redis-default-prefix-warn.mjs.map +1 -1
- package/dist/lib/utils/render-builders.mjs +0 -6
- package/dist/lib/utils/render-builders.mjs.map +1 -1
- package/dist/lib/utils/request-normalizer.mjs +0 -6
- package/dist/lib/utils/request-normalizer.mjs.map +1 -1
- package/dist/lib/utils/response-schemas.mjs +0 -6
- package/dist/lib/utils/response-schemas.mjs.map +1 -1
- package/dist/lib/utils/response-unwrapper.mjs +0 -6
- package/dist/lib/utils/response-unwrapper.mjs.map +1 -1
- package/dist/lib/utils/retry.mjs +0 -6
- package/dist/lib/utils/retry.mjs.map +1 -1
- package/dist/lib/utils/signal-discovery-helpers.mjs +0 -6
- package/dist/lib/utils/signal-discovery-helpers.mjs.map +1 -1
- package/dist/lib/utils/signal-id-builders.mjs +0 -6
- package/dist/lib/utils/signal-id-builders.mjs.map +1 -1
- package/dist/lib/utils/sync-creatives-adapter.mjs +0 -6
- package/dist/lib/utils/sync-creatives-adapter.mjs.map +1 -1
- package/dist/lib/utils/task-state.mjs +0 -6
- package/dist/lib/utils/task-state.mjs.map +1 -1
- package/dist/lib/utils/tool-request-schemas.mjs +0 -6
- package/dist/lib/utils/tool-request-schemas.mjs.map +1 -1
- package/dist/lib/utils/typeGuards.mjs +0 -6
- package/dist/lib/utils/typeGuards.mjs.map +1 -1
- package/dist/lib/utils/union-errors.mjs +0 -6
- package/dist/lib/utils/union-errors.mjs.map +1 -1
- package/dist/lib/utils/validate-user-agent.mjs +0 -6
- package/dist/lib/utils/validate-user-agent.mjs.map +1 -1
- package/dist/lib/v2/format-schema/fetch.mjs +0 -6
- package/dist/lib/v2/format-schema/fetch.mjs.map +1 -1
- package/dist/lib/v2/format-schema/index.mjs +0 -6
- package/dist/lib/v2/format-schema/index.mjs.map +1 -1
- package/dist/lib/v2/format-schema/regex-safety.mjs +0 -6
- package/dist/lib/v2/format-schema/regex-safety.mjs.map +1 -1
- package/dist/lib/v2/format-schema/resolver.mjs +7 -8
- package/dist/lib/v2/format-schema/resolver.mjs.map +1 -1
- package/dist/lib/v2/format-schema/sandbox-refs.mjs +0 -6
- package/dist/lib/v2/format-schema/sandbox-refs.mjs.map +1 -1
- package/dist/lib/v2/projection/augment-response.mjs +0 -6
- package/dist/lib/v2/projection/augment-response.mjs.map +1 -1
- package/dist/lib/v2/projection/builders.mjs +0 -6
- package/dist/lib/v2/projection/builders.mjs.map +1 -1
- package/dist/lib/v2/projection/cache-versions.mjs +0 -6
- package/dist/lib/v2/projection/cache-versions.mjs.map +1 -1
- package/dist/lib/v2/projection/canonical-properties.mjs +5 -6
- package/dist/lib/v2/projection/canonical-properties.mjs.map +1 -1
- package/dist/lib/v2/projection/catalog.mjs +5 -6
- package/dist/lib/v2/projection/catalog.mjs.map +1 -1
- package/dist/lib/v2/projection/constants.mjs +0 -6
- package/dist/lib/v2/projection/constants.mjs.map +1 -1
- package/dist/lib/v2/projection/index.mjs +0 -6
- package/dist/lib/v2/projection/index.mjs.map +1 -1
- package/dist/lib/v2/projection/registry.mjs +5 -6
- package/dist/lib/v2/projection/registry.mjs.map +1 -1
- package/dist/lib/v2/projection/types.mjs +0 -6
- package/dist/lib/v2/projection/v1-to-v2.mjs +0 -6
- package/dist/lib/v2/projection/v1-to-v2.mjs.map +1 -1
- package/dist/lib/v2/projection/v2-to-v1.mjs +0 -6
- package/dist/lib/v2/projection/v2-to-v1.mjs.map +1 -1
- package/dist/lib/v2/projection/write-side.mjs +0 -6
- package/dist/lib/v2/projection/write-side.mjs.map +1 -1
- package/dist/lib/v2/publisher-catalog/formats.mjs +0 -6
- package/dist/lib/v2/publisher-catalog/formats.mjs.map +1 -1
- package/dist/lib/v2/publisher-catalog/index.mjs +0 -6
- package/dist/lib/v2/publisher-catalog/index.mjs.map +1 -1
- package/dist/lib/validation/client-hooks.mjs +0 -6
- package/dist/lib/validation/client-hooks.mjs.map +1 -1
- package/dist/lib/validation/hints.mjs +0 -6
- package/dist/lib/validation/hints.mjs.map +1 -1
- package/dist/lib/validation/index.mjs +0 -6
- package/dist/lib/validation/index.mjs.map +1 -1
- package/dist/lib/validation/schema-errors.mjs +0 -6
- package/dist/lib/validation/schema-errors.mjs.map +1 -1
- package/dist/lib/validation/schema-loader.mjs +5 -6
- package/dist/lib/validation/schema-loader.mjs.map +1 -1
- package/dist/lib/validation/schema-validator.mjs +0 -6
- package/dist/lib/validation/schema-validator.mjs.map +1 -1
- package/dist/lib/validation/sync-creatives.mjs +0 -6
- package/dist/lib/validation/sync-creatives.mjs.map +1 -1
- package/dist/lib/version.d.mts +3 -3
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.js +3 -3
- package/dist/lib/version.js.map +1 -1
- package/dist/lib/version.mjs +3 -9
- package/dist/lib/version.mjs.map +1 -1
- package/dist/lib/webhooks/index.mjs +0 -6
- package/dist/lib/webhooks/index.mjs.map +1 -1
- package/dist/lib/wholesale-feed-sync/index.mjs +0 -6
- package/dist/lib/wholesale-feed-sync/index.mjs.map +1 -1
- package/dist/lib/wholesale-feed-sync/sync.mjs +0 -6
- package/dist/lib/wholesale-feed-sync/sync.mjs.map +1 -1
- package/dist/lib/wholesale-feed-sync/types.mjs +0 -6
- package/dist/lib/wholesale-feed-sync/webhook-notification.mjs +0 -6
- package/dist/lib/wholesale-feed-sync/webhook-notification.mjs.map +1 -1
- package/package.json +2 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/lib/server/webhook-emitter.ts"],"sourcesContent":["/**\n * Publisher-side webhook emitter — the symmetric counterpart to PR #629's\n * receiver-side dedup. A seller / governance agent / rights agent building\n * with `@adcp/sdk` gets a one-call API that handles:\n *\n * - RFC 9421 webhook signing on every attempt (adcp#2423).\n * - A stable `idempotency_key` per logical event, reused across retries\n * (adcp#2417) — regenerating on retry is the #1 at-least-once-delivery\n * bug the runner-side conformance suite catches.\n * - Compact-separator JSON serialization once, signed once, posted once\n * (adcp#2478) — prevents the serialization-mismatch trap where a\n * signer's byte view differs from what the HTTP client writes.\n * - Retry/backoff on 5xx and 429. Terminal on 4xx and on 401 responses\n * carrying `WWW-Authenticate: Signature error=\"webhook_signature_*\"`\n * (spec says retrying a signature failure just fails identically).\n * - HMAC-SHA256 fallback for legacy buyers that registered\n * `push_notification_config.authentication.credentials` — still pinned\n * to compact separators per adcp#2478.\n *\n * Handler authors using `createAdcpServer` call `ctx.emitWebhook(...)` with\n * a `url`, `payload`, and `operation_id` — everything else is wired in.\n */\n\nimport { signWebhook, type SignerKey } from '../signing/signer';\nimport { signWebhookAsync } from '../signing/signer-async';\nimport type { SigningProvider } from '../signing/provider';\nimport type { RequestLike } from '../signing/canonicalize';\nimport { createPinAndBindFetch } from './pin-and-bind-fetch';\nimport { createHmac, randomUUID } from 'node:crypto';\n\n/**\n * Minimum pattern per adcp#2417 / core/mcp-webhook-payload.json.\n * Publisher-side check — catches `generateIdempotencyKey` overrides that\n * produce keys too short for a conformant receiver to accept.\n */\nconst IDEMPOTENCY_KEY_PATTERN = /^[A-Za-z0-9_.:-]{16,255}$/;\n\n/** WWW-Authenticate header pattern signaling a signature-layer reject. */\nconst TERMINAL_SIGNATURE_WWW_AUTH_RE = /Signature\\s+error=\"webhook_signature_/i;\n\n/**\n * Per-(operation_id) idempotency-key store. The key MUST be stable across\n * every retry of the same logical event — that's the load-bearing invariant\n * the receiver-side dedup depends on.\n *\n * Defaults to an in-memory Map. Production publishers with multi-replica\n * emitters SHOULD inject a durable backend (the same way\n * `AsyncHandlerConfig.webhookDedup` accepts a pluggable store on the\n * receiver side).\n */\nexport interface WebhookIdempotencyKeyStore {\n get(operation_id: string): Promise<string | undefined> | string | undefined;\n /** Called only on first-seen operation_id. Subsequent emits read via get(). */\n set(operation_id: string, key: string): Promise<void> | void;\n}\n\nexport function memoryWebhookKeyStore(): WebhookIdempotencyKeyStore {\n const m = new Map<string, string>();\n return {\n get: id => m.get(id),\n set: (id, key) => {\n m.set(id, key);\n },\n };\n}\n\n/**\n * Authentication mode for a single delivery. Omit / pass `null` to use the\n * 9421 baseline. `bearer` / `hmac_sha256` drop back to legacy flows for\n * buyers who populated `push_notification_config.authentication.credentials`.\n *\n * @deprecated The `hmac_sha256` variant is deprecated. HMAC remains in the\n * AdCP spec as a legacy fallback for buyers that registered\n * `push_notification_config.authentication.credentials`, so the SDK keeps\n * supporting it — but the spec-current path is RFC 9421 webhook signatures.\n * See docs/migration-4.x-to-5.x.md#webhook-hmac-legacy-deprecation.\n */\nexport type WebhookAuthentication = { type: 'bearer'; token: string } | { type: 'hmac_sha256'; secret: string } | null;\n\nlet hmacWarningFired = false;\n\nfunction maybeWarnHmacDeprecation(suppressLegacyWarnings?: boolean): void {\n if (hmacWarningFired) return;\n if (suppressLegacyWarnings) return;\n if (process.env.ADCP_SUPPRESS_HMAC_WARNING === '1') return;\n hmacWarningFired = true;\n console.warn(\n '[adcp] Warning: webhook HMAC-SHA256 authentication is deprecated. ' +\n 'HMAC remains supported in the AdCP spec as a legacy fallback but RFC ' +\n '9421 is the spec-current path; migrate when your counterparties are ' +\n 'ready. See docs/migration-4.x-to-5.x.md#webhook-hmac-legacy-deprecation. ' +\n 'Suppress with ADCP_SUPPRESS_HMAC_WARNING=1 (env) or ' +\n 'createWebhookEmitter({ suppressLegacyWarnings: true }) (programmatic).'\n );\n}\n\nexport interface WebhookRetryOptions {\n /** Max delivery attempts (≥1). Default 5. */\n maxAttempts?: number;\n /** Initial backoff in ms. Default 1000. */\n initialDelayMs?: number;\n /** Cap per-attempt backoff. Default 60000. */\n maxDelayMs?: number;\n /** Jitter factor ∈ [0,1]: 0 = none, 0.5 = ±50%. Default 0.25. */\n jitter?: number;\n}\n\nexport interface WebhookEmitterOptions {\n /**\n * In-process JWK signing key. Its JWKS entry SHOULD carry\n * `adcp_use: \"request-signing\"` — webhooks are signed with the agent's\n * request-signing key (the deprecated `\"webhook-signing\"` value is still\n * accepted by verifiers for backward compatibility). Mutually exclusive\n * with `signerProvider` — exactly one must be provided.\n */\n signerKey?: SignerKey;\n /**\n * Async KMS-backed signing provider (GCP KMS, AWS KMS, Azure Key Vault, etc.).\n * Routes webhook signing through `signWebhookAsync` so the private key never\n * enters process memory. Mutually exclusive with `signerKey` — exactly one\n * must be provided.\n *\n * **Key purpose.** Webhooks are signed with a `request-signing` key; domain\n * separation between requests and webhooks is carried by the signature\n * `tag`, not the `adcp_use` discriminator. The same `SigningProvider` used\n * for `request_signing.provider` MAY be reused here. To isolate webhook key\n * material (so a webhook-key compromise does not extend to request signing,\n * or to rotate independently), wrap a second `request-signing`\n * `cryptoKeyVersion` published under a distinct `kid` — isolation comes from\n * the `kid`, not a distinct `adcp_use`. (The deprecated `\"webhook-signing\"`\n * purpose is still accepted by verifiers for backward compatibility.)\n */\n signerProvider?: SigningProvider;\n retries?: WebhookRetryOptions;\n idempotencyKeyStore?: WebhookIdempotencyKeyStore;\n /**\n * Override the default idempotency-key generator. Must return a value\n * matching `/^[A-Za-z0-9_.:-]{16,255}$/` — the emitter rejects anything\n * else (a malformed key would fail the receiver's schema check, which\n * would report as a conformance violation of the publisher).\n */\n generateIdempotencyKey?: () => string;\n /**\n * Override the HTTP client. Defaults to `createPinAndBindFetch()`, which\n * defeats DNS-rebinding / SSRF attacks against the buyer-supplied\n * `push_notification_config.url` (https-only; loopback, private, and cloud\n * metadata ranges denied). See `docs/guides/SIGNING-GUIDE.md` § Webhook\n * SSRF defense.\n *\n * In-process storyboard / test harnesses that deliver to a loopback\n * `http://127.0.0.1:port` receiver must opt into the relaxed policy\n * explicitly: `fetch: createPinAndBindFetch({ policy:\n * LOOPBACK_OK_WEBHOOK_SSRF_POLICY })`. Passing `globalThis.fetch` here\n * restores the unguarded pre-9.8 behavior — only do so behind your own\n * URL validation.\n */\n fetch?: typeof fetch;\n /** Default `User-Agent` header. */\n userAgent?: string;\n /** Signing tag override. Defaults to `adcp/webhook-signing/v1`. */\n tag?: string;\n /** Observability hook called BEFORE each attempt. */\n onAttempt?: (info: WebhookEmitAttempt) => void;\n /** Observability hook called AFTER each attempt completes. */\n onAttemptResult?: (info: WebhookEmitAttemptResult) => void;\n /**\n * Sleeper override. Production uses `setTimeout`; tests inject a stub to\n * skip real backoff. Takes (ms, abortSignal) and resolves when slept.\n */\n sleep?: (ms: number) => Promise<void>;\n /**\n * Suppress the one-time `console.warn` emitted on first HMAC-SHA256\n * webhook delivery. Programmatic equivalent of\n * `ADCP_SUPPRESS_HMAC_WARNING=1`, for libraries embedded in agents where\n * setting an env var is awkward. Does not affect the `@deprecated` JSDoc\n * flag on `WebhookAuthentication` — the type-level deprecation signal\n * always shows up in IDEs.\n */\n suppressLegacyWarnings?: boolean;\n}\n\nexport interface WebhookEmitParams {\n /** Full destination URL. Typically from `push_notification_config.url`. */\n url: string;\n /** Object body. Serialized with compact separators (adcp#2478). */\n payload: Record<string, unknown>;\n /**\n * Stable logical event id. Two emits with the same operation_id reuse\n * the same `idempotency_key` — this is the cross-attempt AND\n * cross-process invariant the receiver dedups on.\n */\n operation_id: string;\n /**\n * Per-emit override of the delivery's authentication mode. Omit for the\n * 9421 default.\n */\n authentication?: WebhookAuthentication;\n /** Per-emit retries override. */\n retries?: WebhookRetryOptions;\n}\n\nexport interface WebhookEmitAttempt {\n operation_id: string;\n idempotency_key: string;\n attempt: number;\n url: string;\n}\n\nexport interface WebhookEmitAttemptResult extends WebhookEmitAttempt {\n status?: number;\n durationMs: number;\n error?: string;\n willRetry: boolean;\n}\n\nexport interface WebhookEmitResult {\n operation_id: string;\n idempotency_key: string;\n attempts: number;\n delivered: boolean;\n final_status?: number;\n /**\n * Per-attempt error messages (transport / signer / network failures).\n *\n * **Logging caution:** when a `signerProvider` rejection bubbles into\n * this array, the message text comes from the adapter and may include\n * infra-flavored detail (KMS resource names, IAM principals, project\n * IDs). Mirrors the same caution flagged on `SigningProvider.fingerprint`.\n * If you pipe `errors[]` into shared logs / observability pipelines,\n * sanitize or redact at your boundary — adapter messages aren't\n * guaranteed to be operator-safe.\n */\n errors: string[];\n}\n\nexport interface WebhookEmitter {\n emit(params: WebhookEmitParams): Promise<WebhookEmitResult>;\n}\n\n// ────────────────────────────────────────────────────────────\n// Factory\n// ────────────────────────────────────────────────────────────\n\nexport function createWebhookEmitter(options: WebhookEmitterOptions): WebhookEmitter {\n if (options.signerKey && options.signerProvider) {\n throw new TypeError('createWebhookEmitter: provide exactly one of signerKey or signerProvider, not both');\n }\n if (!options.signerKey && !options.signerProvider) {\n throw new TypeError('createWebhookEmitter: one of signerKey or signerProvider is required');\n }\n const store = options.idempotencyKeyStore ?? memoryWebhookKeyStore();\n const generateKey = options.generateIdempotencyKey ?? defaultGenerateIdempotencyKey;\n const fetchImpl = options.fetch ?? createPinAndBindFetch();\n const sleep = options.sleep ?? defaultSleep;\n\n return {\n async emit(params: WebhookEmitParams): Promise<WebhookEmitResult> {\n const retries = resolveRetries(params.retries ?? options.retries);\n const idempotency_key = await resolveIdempotencyKey(store, params.operation_id, generateKey);\n\n // Serialize ONCE with compact separators — the same bytes feed both\n // the content-digest input and the HTTP body on every attempt. This\n // is the load-bearing rule from adcp#2478.\n const bodyPayload = { ...params.payload, idempotency_key };\n const bodyBytes = JSON.stringify(bodyPayload);\n\n const errors: string[] = [];\n let lastStatus: number | undefined;\n\n for (let attempt = 1; attempt <= retries.maxAttempts; attempt++) {\n const attemptInfo: WebhookEmitAttempt = {\n operation_id: params.operation_id,\n idempotency_key,\n attempt,\n url: params.url,\n };\n options.onAttempt?.(attemptInfo);\n\n const started = Date.now();\n let status: number | undefined;\n let error: string | undefined;\n let terminal = false;\n\n try {\n const response = await deliverOnce({\n url: params.url,\n bodyBytes,\n signerKey: options.signerKey,\n signerProvider: options.signerProvider,\n authentication: params.authentication ?? null,\n tag: options.tag,\n userAgent: options.userAgent,\n fetch: fetchImpl,\n suppressLegacyWarnings: options.suppressLegacyWarnings,\n });\n status = response.status;\n lastStatus = status;\n\n if (status >= 200 && status < 300) {\n const durationMs = Date.now() - started;\n options.onAttemptResult?.({ ...attemptInfo, status, durationMs, willRetry: false });\n return {\n operation_id: params.operation_id,\n idempotency_key,\n attempts: attempt,\n delivered: true,\n final_status: status,\n errors,\n };\n }\n\n terminal = isTerminalStatus(status, response.wwwAuthenticate);\n error = `HTTP ${status}${response.wwwAuthenticate ? ` (${response.wwwAuthenticate})` : ''}`;\n } catch (err) {\n error = formatTransportError(err);\n // Network / transport errors are retryable — the delivery didn't\n // reach the receiver, so no risk of double-processing.\n // Pin-and-bind SSRF blocks are themselves terminal: the URL\n // (or its DNS resolution) violates policy and won't change on retry.\n if (errorContainsCode(err, 'EADCP_SSRF_BLOCKED')) {\n terminal = true;\n }\n }\n\n if (error) errors.push(`attempt ${attempt}: ${error}`);\n\n const willRetry = !terminal && attempt < retries.maxAttempts;\n options.onAttemptResult?.({\n ...attemptInfo,\n ...(status !== undefined && { status }),\n durationMs: Date.now() - started,\n ...(error !== undefined && { error }),\n willRetry,\n });\n\n if (!willRetry) break;\n\n await sleep(backoffDelay(attempt, retries));\n }\n\n return {\n operation_id: params.operation_id,\n idempotency_key,\n attempts: errors.length,\n delivered: false,\n ...(lastStatus !== undefined && { final_status: lastStatus }),\n errors,\n };\n },\n };\n}\n\n// ────────────────────────────────────────────────────────────\n// Delivery primitives\n// ────────────────────────────────────────────────────────────\n\ninterface DeliveryResponse {\n status: number;\n wwwAuthenticate?: string;\n}\n\nasync function deliverOnce(args: {\n url: string;\n bodyBytes: string;\n signerKey?: SignerKey;\n signerProvider?: SigningProvider;\n authentication: WebhookAuthentication;\n tag?: string;\n userAgent?: string;\n fetch: typeof fetch;\n suppressLegacyWarnings?: boolean;\n}): Promise<DeliveryResponse> {\n const headers = await buildHeaders(args);\n const response = await args.fetch(args.url, {\n method: 'POST',\n headers,\n body: args.bodyBytes,\n });\n return {\n status: response.status,\n ...(response.headers.get('www-authenticate') && { wwwAuthenticate: response.headers.get('www-authenticate')! }),\n };\n}\n\nasync function buildHeaders(args: {\n url: string;\n bodyBytes: string;\n signerKey?: SignerKey;\n signerProvider?: SigningProvider;\n authentication: WebhookAuthentication;\n tag?: string;\n userAgent?: string;\n suppressLegacyWarnings?: boolean;\n}): Promise<Record<string, string>> {\n const baseHeaders: Record<string, string> = {\n 'content-type': 'application/json',\n };\n if (args.userAgent) baseHeaders['user-agent'] = args.userAgent;\n\n // Legacy HMAC-SHA256 path. Matches docs/building/implementation/webhooks.mdx\n // §3.0 legacy section: X-ADCP-Signature + X-ADCP-Timestamp over\n // `${ts}.${raw_body_bytes}`.\n if (args.authentication?.type === 'hmac_sha256') {\n maybeWarnHmacDeprecation(args.suppressLegacyWarnings);\n const ts = Math.floor(Date.now() / 1000).toString();\n const hmac = createHmac('sha256', args.authentication.secret);\n hmac.update(`${ts}.${args.bodyBytes}`, 'utf8');\n return {\n ...baseHeaders,\n 'x-adcp-timestamp': ts,\n 'x-adcp-signature': `sha256=${hmac.digest('hex')}`,\n };\n }\n\n // Bearer fallback — the legacy path for buyers that registered an API\n // key in push_notification_config.authentication. No body signing —\n // just a header. Not recommended; strictly for interop with 2.x buyers.\n if (args.authentication?.type === 'bearer') {\n return { ...baseHeaders, authorization: `Bearer ${args.authentication.token}` };\n }\n\n // Default: 9421 webhook signing. Fresh nonce + fresh created/expires per\n // attempt, but the `idempotency_key` inside the body stays stable — the\n // signature covers the body bytes, which include the key; multiple\n // retries of the same logical event produce different signatures over\n // the same body, which is exactly what the receiver expects.\n const request: RequestLike = {\n method: 'POST',\n url: args.url,\n headers: baseHeaders,\n body: args.bodyBytes,\n };\n const signOpts = args.tag !== undefined ? { tag: args.tag } : {};\n const signed = args.signerProvider\n ? await signWebhookAsync(request, args.signerProvider, signOpts)\n : signWebhook(request, args.signerKey!, signOpts);\n return { ...baseHeaders, ...signed.headers };\n}\n\n// ────────────────────────────────────────────────────────────\n// Helpers\n// ────────────────────────────────────────────────────────────\n\nasync function resolveIdempotencyKey(\n store: WebhookIdempotencyKeyStore,\n operation_id: string,\n generate: () => string\n): Promise<string> {\n const existing = await store.get(operation_id);\n if (existing) {\n if (!IDEMPOTENCY_KEY_PATTERN.test(existing)) {\n throw new Error(\n `idempotency-key store returned \"${existing}\" for operation_id \"${operation_id}\"; ` +\n `does not match required pattern ${IDEMPOTENCY_KEY_PATTERN.source}`\n );\n }\n return existing;\n }\n const fresh = generate();\n if (!IDEMPOTENCY_KEY_PATTERN.test(fresh)) {\n throw new Error(\n `generateIdempotencyKey produced \"${fresh}\" for operation_id \"${operation_id}\"; ` +\n `must match ${IDEMPOTENCY_KEY_PATTERN.source}`\n );\n }\n await store.set(operation_id, fresh);\n return fresh;\n}\n\n/**\n * Default key generator — `evt_` prefix + a base64url 18-byte random.\n * Length 27 (comfortably within 16–255), only base64url-safe characters,\n * obviously-webhook-scoped prefix for log grepping.\n */\nfunction defaultGenerateIdempotencyKey(): string {\n const uuid = randomUUID().replace(/-/g, '');\n return `evt_${uuid.slice(0, 24)}`;\n}\n\nfunction isTerminalStatus(status: number, wwwAuthenticate?: string): boolean {\n if (status === 429) return false;\n if (status >= 500) return false;\n // 401 with a signature-layer reject is terminal per adcp#2423 —\n // retrying a signature failure produces identical bytes and identical\n // rejection. Non-signature 401s (opaque auth failures) are also\n // terminal; there's nothing the publisher can do by retrying.\n if (status === 401 && wwwAuthenticate && TERMINAL_SIGNATURE_WWW_AUTH_RE.test(wwwAuthenticate)) return true;\n if (status >= 400 && status < 500) return true;\n return false;\n}\n\nfunction backoffDelay(attempt: number, retries: Required<WebhookRetryOptions>): number {\n const base = Math.min(retries.initialDelayMs * Math.pow(2, attempt - 1), retries.maxDelayMs);\n if (retries.jitter <= 0) return base;\n const jitterWindow = base * retries.jitter;\n const offset = Math.random() * jitterWindow * 2 - jitterWindow;\n return Math.max(0, Math.floor(base + offset));\n}\n\nfunction resolveRetries(opts?: WebhookRetryOptions): Required<WebhookRetryOptions> {\n return {\n maxAttempts: Math.max(1, opts?.maxAttempts ?? 5),\n initialDelayMs: Math.max(0, opts?.initialDelayMs ?? 1000),\n maxDelayMs: Math.max(0, opts?.maxDelayMs ?? 60_000),\n jitter: Math.max(0, Math.min(1, opts?.jitter ?? 0.25)),\n };\n}\n\nfunction defaultSleep(ms: number): Promise<void> {\n return new Promise(r => {\n const t = setTimeout(r, ms);\n t.unref?.();\n });\n}\n\n/**\n * Format a transport-layer error for `result.errors[]`. Walks `cause` chains\n * (undici wraps the connector failure under a generic \"fetch failed\") so\n * operators see the actual rule that fired (e.g. SSRF policy block) instead\n * of an opaque outer message.\n */\nfunction formatTransportError(err: unknown): string {\n if (!(err instanceof Error)) return String(err);\n const parts: string[] = [];\n let cur: Error | undefined = err;\n let depth = 0;\n while (cur && depth < 5) {\n const code = (cur as NodeJS.ErrnoException).code;\n parts.push(code ? `${code}: ${cur.message}` : cur.message);\n cur = cur.cause instanceof Error ? cur.cause : undefined;\n depth++;\n }\n return parts.join(' — ');\n}\n\nfunction errorContainsCode(err: unknown, code: string): boolean {\n let cur: unknown = err;\n let depth = 0;\n while (cur instanceof Error && depth < 5) {\n if ((cur as NodeJS.ErrnoException).code === code) return true;\n cur = (cur as { cause?: unknown }).cause;\n depth++;\n }\n return false;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/server/webhook-emitter.ts"],"sourcesContent":["/**\n * Publisher-side webhook emitter — the symmetric counterpart to PR #629's\n * receiver-side dedup. A seller / governance agent / rights agent building\n * with `@adcp/sdk` gets a one-call API that handles:\n *\n * - RFC 9421 webhook signing on every attempt (adcp#2423).\n * - A stable `idempotency_key` per logical event, reused across retries\n * (adcp#2417) — regenerating on retry is the #1 at-least-once-delivery\n * bug the runner-side conformance suite catches.\n * - Compact-separator JSON serialization once, signed once, posted once\n * (adcp#2478) — prevents the serialization-mismatch trap where a\n * signer's byte view differs from what the HTTP client writes.\n * - Retry/backoff on 5xx and 429. Terminal on 4xx and on 401 responses\n * carrying `WWW-Authenticate: Signature error=\"webhook_signature_*\"`\n * (spec says retrying a signature failure just fails identically).\n * - HMAC-SHA256 fallback for legacy buyers that registered\n * `push_notification_config.authentication.credentials` — still pinned\n * to compact separators per adcp#2478.\n *\n * Handler authors using `createAdcpServer` call `ctx.emitWebhook(...)` with\n * a `url`, `payload`, and `operation_id` — everything else is wired in.\n */\n\nimport { signWebhook, type SignerKey } from '../signing/signer';\nimport { signWebhookAsync } from '../signing/signer-async';\nimport type { SigningProvider } from '../signing/provider';\nimport type { RequestLike } from '../signing/canonicalize';\nimport { createPinAndBindFetch } from './pin-and-bind-fetch';\nimport { createHmac, randomUUID } from 'node:crypto';\n\n/**\n * Minimum pattern per adcp#2417 / core/mcp-webhook-payload.json.\n * Publisher-side check — catches `generateIdempotencyKey` overrides that\n * produce keys too short for a conformant receiver to accept.\n */\nconst IDEMPOTENCY_KEY_PATTERN = /^[A-Za-z0-9_.:-]{16,255}$/;\n\n/** WWW-Authenticate header pattern signaling a signature-layer reject. */\nconst TERMINAL_SIGNATURE_WWW_AUTH_RE = /Signature\\s+error=\"webhook_signature_/i;\n\n/**\n * Per-(operation_id) idempotency-key store. The key MUST be stable across\n * every retry of the same logical event — that's the load-bearing invariant\n * the receiver-side dedup depends on.\n *\n * Defaults to an in-memory Map. Production publishers with multi-replica\n * emitters SHOULD inject a durable backend (the same way\n * `AsyncHandlerConfig.webhookDedup` accepts a pluggable store on the\n * receiver side).\n */\nexport interface WebhookIdempotencyKeyStore {\n get(operation_id: string): Promise<string | undefined> | string | undefined;\n /** Called only on first-seen operation_id. Subsequent emits read via get(). */\n set(operation_id: string, key: string): Promise<void> | void;\n}\n\nexport function memoryWebhookKeyStore(): WebhookIdempotencyKeyStore {\n const m = new Map<string, string>();\n return {\n get: id => m.get(id),\n set: (id, key) => {\n m.set(id, key);\n },\n };\n}\n\n/**\n * Authentication mode for a single delivery. Omit / pass `null` to use the\n * 9421 baseline. `bearer` / `hmac_sha256` drop back to legacy flows for\n * buyers who populated `push_notification_config.authentication.credentials`.\n *\n * @deprecated The `hmac_sha256` variant is deprecated. HMAC remains in the\n * AdCP spec as a legacy fallback for buyers that registered\n * `push_notification_config.authentication.credentials`, so the SDK keeps\n * supporting it — but the spec-current path is RFC 9421 webhook signatures.\n * See docs/migration-4.x-to-5.x.md#webhook-hmac-legacy-deprecation.\n */\nexport type WebhookAuthentication = { type: 'bearer'; token: string } | { type: 'hmac_sha256'; secret: string } | null;\n\nlet hmacWarningFired = false;\n\nfunction maybeWarnHmacDeprecation(suppressLegacyWarnings?: boolean): void {\n if (hmacWarningFired) return;\n if (suppressLegacyWarnings) return;\n if (process.env.ADCP_SUPPRESS_HMAC_WARNING === '1') return;\n hmacWarningFired = true;\n console.warn(\n '[adcp] Warning: webhook HMAC-SHA256 authentication is deprecated. ' +\n 'HMAC remains supported in the AdCP spec as a legacy fallback but RFC ' +\n '9421 is the spec-current path; migrate when your counterparties are ' +\n 'ready. See docs/migration-4.x-to-5.x.md#webhook-hmac-legacy-deprecation. ' +\n 'Suppress with ADCP_SUPPRESS_HMAC_WARNING=1 (env) or ' +\n 'createWebhookEmitter({ suppressLegacyWarnings: true }) (programmatic).'\n );\n}\n\nexport interface WebhookRetryOptions {\n /** Max delivery attempts (≥1). Default 5. */\n maxAttempts?: number;\n /** Initial backoff in ms. Default 1000. */\n initialDelayMs?: number;\n /** Cap per-attempt backoff. Default 60000. */\n maxDelayMs?: number;\n /** Jitter factor ∈ [0,1]: 0 = none, 0.5 = ±50%. Default 0.25. */\n jitter?: number;\n}\n\nexport interface WebhookEmitterOptions {\n /**\n * In-process JWK signing key. Its JWKS entry SHOULD carry\n * `adcp_use: \"request-signing\"` — webhooks are signed with the agent's\n * request-signing key (the deprecated `\"webhook-signing\"` value is still\n * accepted by verifiers for backward compatibility). Mutually exclusive\n * with `signerProvider` — exactly one must be provided.\n */\n signerKey?: SignerKey;\n /**\n * Async KMS-backed signing provider (GCP KMS, AWS KMS, Azure Key Vault, etc.).\n * Routes webhook signing through `signWebhookAsync` so the private key never\n * enters process memory. Mutually exclusive with `signerKey` — exactly one\n * must be provided.\n *\n * **Key purpose.** Webhooks are signed with a `request-signing` key; domain\n * separation between requests and webhooks is carried by the signature\n * `tag`, not the `adcp_use` discriminator. The same `SigningProvider` used\n * for `request_signing.provider` MAY be reused here. To isolate webhook key\n * material (so a webhook-key compromise does not extend to request signing,\n * or to rotate independently), wrap a second `request-signing`\n * `cryptoKeyVersion` published under a distinct `kid` — isolation comes from\n * the `kid`, not a distinct `adcp_use`. (The deprecated `\"webhook-signing\"`\n * purpose is still accepted by verifiers for backward compatibility.)\n */\n signerProvider?: SigningProvider;\n retries?: WebhookRetryOptions;\n idempotencyKeyStore?: WebhookIdempotencyKeyStore;\n /**\n * Override the default idempotency-key generator. Must return a value\n * matching `/^[A-Za-z0-9_.:-]{16,255}$/` — the emitter rejects anything\n * else (a malformed key would fail the receiver's schema check, which\n * would report as a conformance violation of the publisher).\n */\n generateIdempotencyKey?: () => string;\n /**\n * Override the HTTP client. Defaults to `createPinAndBindFetch()`, which\n * defeats DNS-rebinding / SSRF attacks against the buyer-supplied\n * `push_notification_config.url` (https-only; loopback, private, and cloud\n * metadata ranges denied). See `docs/guides/SIGNING-GUIDE.md` § Webhook\n * SSRF defense.\n *\n * In-process storyboard / test harnesses that deliver to a loopback\n * `http://127.0.0.1:port` receiver must opt into the relaxed policy\n * explicitly: `fetch: createPinAndBindFetch({ policy:\n * LOOPBACK_OK_WEBHOOK_SSRF_POLICY })`. Passing `globalThis.fetch` here\n * restores the unguarded pre-9.8 behavior — only do so behind your own\n * URL validation.\n */\n fetch?: typeof fetch;\n /** Default `User-Agent` header. */\n userAgent?: string;\n /** Signing tag override. Defaults to `adcp/webhook-signing/v1`. */\n tag?: string;\n /** Observability hook called BEFORE each attempt. */\n onAttempt?: (info: WebhookEmitAttempt) => void;\n /** Observability hook called AFTER each attempt completes. */\n onAttemptResult?: (info: WebhookEmitAttemptResult) => void;\n /**\n * Sleeper override. Production uses `setTimeout`; tests inject a stub to\n * skip real backoff. Takes (ms, abortSignal) and resolves when slept.\n */\n sleep?: (ms: number) => Promise<void>;\n /**\n * Suppress the one-time `console.warn` emitted on first HMAC-SHA256\n * webhook delivery. Programmatic equivalent of\n * `ADCP_SUPPRESS_HMAC_WARNING=1`, for libraries embedded in agents where\n * setting an env var is awkward. Does not affect the `@deprecated` JSDoc\n * flag on `WebhookAuthentication` — the type-level deprecation signal\n * always shows up in IDEs.\n */\n suppressLegacyWarnings?: boolean;\n}\n\nexport interface WebhookEmitParams {\n /** Full destination URL. Typically from `push_notification_config.url`. */\n url: string;\n /** Object body. Serialized with compact separators (adcp#2478). */\n payload: Record<string, unknown>;\n /**\n * Stable logical event id. Two emits with the same operation_id reuse\n * the same `idempotency_key` — this is the cross-attempt AND\n * cross-process invariant the receiver dedups on.\n */\n operation_id: string;\n /**\n * Per-emit override of the delivery's authentication mode. Omit for the\n * 9421 default.\n */\n authentication?: WebhookAuthentication;\n /** Per-emit retries override. */\n retries?: WebhookRetryOptions;\n}\n\nexport interface WebhookEmitAttempt {\n operation_id: string;\n idempotency_key: string;\n attempt: number;\n url: string;\n}\n\nexport interface WebhookEmitAttemptResult extends WebhookEmitAttempt {\n status?: number;\n durationMs: number;\n error?: string;\n willRetry: boolean;\n}\n\nexport interface WebhookEmitResult {\n operation_id: string;\n idempotency_key: string;\n attempts: number;\n delivered: boolean;\n final_status?: number;\n /**\n * Per-attempt error messages (transport / signer / network failures).\n *\n * **Logging caution:** when a `signerProvider` rejection bubbles into\n * this array, the message text comes from the adapter and may include\n * infra-flavored detail (KMS resource names, IAM principals, project\n * IDs). Mirrors the same caution flagged on `SigningProvider.fingerprint`.\n * If you pipe `errors[]` into shared logs / observability pipelines,\n * sanitize or redact at your boundary — adapter messages aren't\n * guaranteed to be operator-safe.\n */\n errors: string[];\n}\n\nexport interface WebhookEmitter {\n emit(params: WebhookEmitParams): Promise<WebhookEmitResult>;\n}\n\n// ────────────────────────────────────────────────────────────\n// Factory\n// ────────────────────────────────────────────────────────────\n\nexport function createWebhookEmitter(options: WebhookEmitterOptions): WebhookEmitter {\n if (options.signerKey && options.signerProvider) {\n throw new TypeError('createWebhookEmitter: provide exactly one of signerKey or signerProvider, not both');\n }\n if (!options.signerKey && !options.signerProvider) {\n throw new TypeError('createWebhookEmitter: one of signerKey or signerProvider is required');\n }\n const store = options.idempotencyKeyStore ?? memoryWebhookKeyStore();\n const generateKey = options.generateIdempotencyKey ?? defaultGenerateIdempotencyKey;\n const fetchImpl = options.fetch ?? createPinAndBindFetch();\n const sleep = options.sleep ?? defaultSleep;\n\n return {\n async emit(params: WebhookEmitParams): Promise<WebhookEmitResult> {\n const retries = resolveRetries(params.retries ?? options.retries);\n const idempotency_key = await resolveIdempotencyKey(store, params.operation_id, generateKey);\n\n // Serialize ONCE with compact separators — the same bytes feed both\n // the content-digest input and the HTTP body on every attempt. This\n // is the load-bearing rule from adcp#2478.\n const bodyPayload = { ...params.payload, idempotency_key };\n const bodyBytes = JSON.stringify(bodyPayload);\n\n const errors: string[] = [];\n let lastStatus: number | undefined;\n\n for (let attempt = 1; attempt <= retries.maxAttempts; attempt++) {\n const attemptInfo: WebhookEmitAttempt = {\n operation_id: params.operation_id,\n idempotency_key,\n attempt,\n url: params.url,\n };\n options.onAttempt?.(attemptInfo);\n\n const started = Date.now();\n let status: number | undefined;\n let error: string | undefined;\n let terminal = false;\n\n try {\n const response = await deliverOnce({\n url: params.url,\n bodyBytes,\n signerKey: options.signerKey,\n signerProvider: options.signerProvider,\n authentication: params.authentication ?? null,\n tag: options.tag,\n userAgent: options.userAgent,\n fetch: fetchImpl,\n suppressLegacyWarnings: options.suppressLegacyWarnings,\n });\n status = response.status;\n lastStatus = status;\n\n if (status >= 200 && status < 300) {\n const durationMs = Date.now() - started;\n options.onAttemptResult?.({ ...attemptInfo, status, durationMs, willRetry: false });\n return {\n operation_id: params.operation_id,\n idempotency_key,\n attempts: attempt,\n delivered: true,\n final_status: status,\n errors,\n };\n }\n\n terminal = isTerminalStatus(status, response.wwwAuthenticate);\n error = `HTTP ${status}${response.wwwAuthenticate ? ` (${response.wwwAuthenticate})` : ''}`;\n } catch (err) {\n error = formatTransportError(err);\n // Network / transport errors are retryable — the delivery didn't\n // reach the receiver, so no risk of double-processing.\n // Pin-and-bind SSRF blocks are themselves terminal: the URL\n // (or its DNS resolution) violates policy and won't change on retry.\n if (errorContainsCode(err, 'EADCP_SSRF_BLOCKED')) {\n terminal = true;\n }\n }\n\n if (error) errors.push(`attempt ${attempt}: ${error}`);\n\n const willRetry = !terminal && attempt < retries.maxAttempts;\n options.onAttemptResult?.({\n ...attemptInfo,\n ...(status !== undefined && { status }),\n durationMs: Date.now() - started,\n ...(error !== undefined && { error }),\n willRetry,\n });\n\n if (!willRetry) break;\n\n await sleep(backoffDelay(attempt, retries));\n }\n\n return {\n operation_id: params.operation_id,\n idempotency_key,\n attempts: errors.length,\n delivered: false,\n ...(lastStatus !== undefined && { final_status: lastStatus }),\n errors,\n };\n },\n };\n}\n\n// ────────────────────────────────────────────────────────────\n// Delivery primitives\n// ────────────────────────────────────────────────────────────\n\ninterface DeliveryResponse {\n status: number;\n wwwAuthenticate?: string;\n}\n\nasync function deliverOnce(args: {\n url: string;\n bodyBytes: string;\n signerKey?: SignerKey;\n signerProvider?: SigningProvider;\n authentication: WebhookAuthentication;\n tag?: string;\n userAgent?: string;\n fetch: typeof fetch;\n suppressLegacyWarnings?: boolean;\n}): Promise<DeliveryResponse> {\n const headers = await buildHeaders(args);\n const response = await args.fetch(args.url, {\n method: 'POST',\n headers,\n body: args.bodyBytes,\n });\n return {\n status: response.status,\n ...(response.headers.get('www-authenticate') && { wwwAuthenticate: response.headers.get('www-authenticate')! }),\n };\n}\n\nasync function buildHeaders(args: {\n url: string;\n bodyBytes: string;\n signerKey?: SignerKey;\n signerProvider?: SigningProvider;\n authentication: WebhookAuthentication;\n tag?: string;\n userAgent?: string;\n suppressLegacyWarnings?: boolean;\n}): Promise<Record<string, string>> {\n const baseHeaders: Record<string, string> = {\n 'content-type': 'application/json',\n };\n if (args.userAgent) baseHeaders['user-agent'] = args.userAgent;\n\n // Legacy HMAC-SHA256 path. Matches docs/building/implementation/webhooks.mdx\n // §3.0 legacy section: X-ADCP-Signature + X-ADCP-Timestamp over\n // `${ts}.${raw_body_bytes}`.\n if (args.authentication?.type === 'hmac_sha256') {\n maybeWarnHmacDeprecation(args.suppressLegacyWarnings);\n const ts = Math.floor(Date.now() / 1000).toString();\n const hmac = createHmac('sha256', args.authentication.secret);\n hmac.update(`${ts}.${args.bodyBytes}`, 'utf8');\n return {\n ...baseHeaders,\n 'x-adcp-timestamp': ts,\n 'x-adcp-signature': `sha256=${hmac.digest('hex')}`,\n };\n }\n\n // Bearer fallback — the legacy path for buyers that registered an API\n // key in push_notification_config.authentication. No body signing —\n // just a header. Not recommended; strictly for interop with 2.x buyers.\n if (args.authentication?.type === 'bearer') {\n return { ...baseHeaders, authorization: `Bearer ${args.authentication.token}` };\n }\n\n // Default: 9421 webhook signing. Fresh nonce + fresh created/expires per\n // attempt, but the `idempotency_key` inside the body stays stable — the\n // signature covers the body bytes, which include the key; multiple\n // retries of the same logical event produce different signatures over\n // the same body, which is exactly what the receiver expects.\n const request: RequestLike = {\n method: 'POST',\n url: args.url,\n headers: baseHeaders,\n body: args.bodyBytes,\n };\n const signOpts = args.tag !== undefined ? { tag: args.tag } : {};\n const signed = args.signerProvider\n ? await signWebhookAsync(request, args.signerProvider, signOpts)\n : signWebhook(request, args.signerKey!, signOpts);\n return { ...baseHeaders, ...signed.headers };\n}\n\n// ────────────────────────────────────────────────────────────\n// Helpers\n// ────────────────────────────────────────────────────────────\n\nasync function resolveIdempotencyKey(\n store: WebhookIdempotencyKeyStore,\n operation_id: string,\n generate: () => string\n): Promise<string> {\n const existing = await store.get(operation_id);\n if (existing) {\n if (!IDEMPOTENCY_KEY_PATTERN.test(existing)) {\n throw new Error(\n `idempotency-key store returned \"${existing}\" for operation_id \"${operation_id}\"; ` +\n `does not match required pattern ${IDEMPOTENCY_KEY_PATTERN.source}`\n );\n }\n return existing;\n }\n const fresh = generate();\n if (!IDEMPOTENCY_KEY_PATTERN.test(fresh)) {\n throw new Error(\n `generateIdempotencyKey produced \"${fresh}\" for operation_id \"${operation_id}\"; ` +\n `must match ${IDEMPOTENCY_KEY_PATTERN.source}`\n );\n }\n await store.set(operation_id, fresh);\n return fresh;\n}\n\n/**\n * Default key generator — `evt_` prefix + a base64url 18-byte random.\n * Length 27 (comfortably within 16–255), only base64url-safe characters,\n * obviously-webhook-scoped prefix for log grepping.\n */\nfunction defaultGenerateIdempotencyKey(): string {\n const uuid = randomUUID().replace(/-/g, '');\n return `evt_${uuid.slice(0, 24)}`;\n}\n\nfunction isTerminalStatus(status: number, wwwAuthenticate?: string): boolean {\n if (status === 429) return false;\n if (status >= 500) return false;\n // 401 with a signature-layer reject is terminal per adcp#2423 —\n // retrying a signature failure produces identical bytes and identical\n // rejection. Non-signature 401s (opaque auth failures) are also\n // terminal; there's nothing the publisher can do by retrying.\n if (status === 401 && wwwAuthenticate && TERMINAL_SIGNATURE_WWW_AUTH_RE.test(wwwAuthenticate)) return true;\n if (status >= 400 && status < 500) return true;\n return false;\n}\n\nfunction backoffDelay(attempt: number, retries: Required<WebhookRetryOptions>): number {\n const base = Math.min(retries.initialDelayMs * Math.pow(2, attempt - 1), retries.maxDelayMs);\n if (retries.jitter <= 0) return base;\n const jitterWindow = base * retries.jitter;\n const offset = Math.random() * jitterWindow * 2 - jitterWindow;\n return Math.max(0, Math.floor(base + offset));\n}\n\nfunction resolveRetries(opts?: WebhookRetryOptions): Required<WebhookRetryOptions> {\n return {\n maxAttempts: Math.max(1, opts?.maxAttempts ?? 5),\n initialDelayMs: Math.max(0, opts?.initialDelayMs ?? 1000),\n maxDelayMs: Math.max(0, opts?.maxDelayMs ?? 60_000),\n jitter: Math.max(0, Math.min(1, opts?.jitter ?? 0.25)),\n };\n}\n\nfunction defaultSleep(ms: number): Promise<void> {\n return new Promise(r => {\n const t = setTimeout(r, ms);\n t.unref?.();\n });\n}\n\n/**\n * Format a transport-layer error for `result.errors[]`. Walks `cause` chains\n * (undici wraps the connector failure under a generic \"fetch failed\") so\n * operators see the actual rule that fired (e.g. SSRF policy block) instead\n * of an opaque outer message.\n */\nfunction formatTransportError(err: unknown): string {\n if (!(err instanceof Error)) return String(err);\n const parts: string[] = [];\n let cur: Error | undefined = err;\n let depth = 0;\n while (cur && depth < 5) {\n const code = (cur as NodeJS.ErrnoException).code;\n parts.push(code ? `${code}: ${cur.message}` : cur.message);\n cur = cur.cause instanceof Error ? cur.cause : undefined;\n depth++;\n }\n return parts.join(' — ');\n}\n\nfunction errorContainsCode(err: unknown, code: string): boolean {\n let cur: unknown = err;\n let depth = 0;\n while (cur instanceof Error && depth < 5) {\n if ((cur as NodeJS.ErrnoException).code === code) return true;\n cur = (cur as { cause?: unknown }).cause;\n depth++;\n }\n return false;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import { fileURLToPath as __adcpFileURLToPath } from 'node:url';
|
|
2
|
-
import { dirname as __adcpDirname } from 'node:path';
|
|
3
|
-
import { createRequire as __adcpCreateRequire } from 'node:module';
|
|
4
|
-
const __filename = __adcpFileURLToPath(import.meta.url);
|
|
5
|
-
const __dirname = __adcpDirname(__filename);
|
|
6
|
-
const require = __adcpCreateRequire(import.meta.url);
|
|
7
1
|
import { scanArgsForCredentials } from "./credential-policy.mjs";
|
|
8
2
|
import { WIRE_SPEC_FIELDS } from "./wire-spec-fields.generated.mjs";
|
|
9
3
|
function pickWireSpecFields(request, schemaName) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/lib/server/wire-safe.ts"],"sourcesContent":["/**\n * Wire-spec field discipline at the operational fan-out boundary.\n *\n * `WireSafe<T>` is a branded type that signals \"this object has been\n * stripped to AdCP wire-spec fields and is safe to forward upstream.\"\n * The brand is constructed only by {@link pickWireSpecFields} (or\n * variants). Code that spreads a buyer request directly cannot satisfy\n * the brand — `{ ...buyerReq, paused: true }` is `T`, not\n * `WireSafe<T>`.\n *\n * The brand is the L2 of #1529: where L1 (`credentialPolicy`,\n * `src/lib/server/credential-policy.ts`) catches credential-shaped\n * keys at the buyer-facing dispatch boundary, L2 catches structural\n * leakage at the operational dispatch boundary — storefront fan-out\n * code that picks per-target args from a buyer request, where the\n * scrub happens BEFORE the credential scan would have run on the\n * upstream call.\n *\n * **The brand is opt-in at the type level.** `OperationalPlatform`\n * methods accept plain `UpdateMediaBuyRequest` etc. (not branded —\n * see #1530's interface), so adopters who don't use the helper\n * aren't broken. Adopters who DO use the helper get the safety\n * benefit at the picking site: assigning the result to a\n * `WireSafe<UpdateMediaBuyRequest>` variable forces `pickWireSpecFields`\n * to be the constructor; spreading the buyer request elsewhere fails\n * to satisfy the brand.\n *\n * **Migration footgun:** `pickWireSpecFields` ALONE doesn't close\n * the round-2 / round-3 vectors (nested `context.<x>_access_token`,\n * nested `ext.<x>_access_token`). `ext` and `context` are wire-spec\n * fields, so the helper preserves them whole. Storefronts that fan\n * out MUST chain {@link scrubExtensions} after the pick — that helper\n * filters ext/context to a caller-specified key allowlist AND\n * recursively drops credential-shaped keys at any depth (using the\n * L1 default pattern set or an adopter-supplied matcher). Calling\n * `pickWireSpecFields` without `scrubExtensions` reopens the\n * round-2/round-3 attack surface.\n *\n * @see docs/guides/CTX-METADATA-SAFETY.md\n * @see scripts/generate-wire-spec-fields.ts — codegen for the\n * allowlist constants this module reads.\n *\n * @public\n */\n\nimport { scanArgsForCredentials, type CredentialPatternsConfig } from './credential-policy';\nimport { WIRE_SPEC_FIELDS, type WireSpecRequestName } from './wire-spec-fields.generated';\n\ndeclare const __wireSafe: unique symbol;\n\n/**\n * A request shape that has been narrowed to AdCP wire-spec fields.\n * Cannot be produced by spreading a buyer request — only via\n * {@link pickWireSpecFields} or by typed `as WireSafe<T>` assertion\n * at a known-safe construction site (e.g. a poller that builds the\n * request from stored state, not from buyer input).\n *\n * The brand is what makes this load-bearing: `{ ...buyerRequest }` is\n * `T`, not `WireSafe<T>`, so passing it where `WireSafe<T>` is\n * required is a compile error.\n */\nexport type WireSafe<T> = T & { readonly [__wireSafe]: true };\n\nexport type { WireSpecRequestName };\n\nexport { WIRE_SPEC_FIELDS };\n\n/**\n * The wire-spec request type associated with a `WireSpecRequestName`.\n * Internal — adopters access it transparently via\n * `pickWireSpecFields`'s return narrowing.\n */\ntype WireSpecRequestShape<K extends WireSpecRequestName> = (typeof WIRE_SPEC_FIELDS)[K]['__type'];\n\n/**\n * Strip a buyer request to the wire-spec fields defined for\n * `schemaName` and return the result branded `WireSafe<T>`. The\n * canonical constructor of {@link WireSafe}.\n *\n * Drops every property NOT in the schema's `properties` allowlist,\n * including buyer-supplied unknown keys (`<platform>_access_token`,\n * `account: { brand: 'attacker.com' }`-style identity-pivot fields,\n * arbitrary attacker payload). The allowlist comes from codegen — the\n * AdCP request schema IS the allowlist; drift is structurally\n * impossible.\n *\n * **Top-level only.** `ext` and `context` are wire-spec fields and\n * are preserved verbatim by this helper. Storefronts MUST chain\n * {@link scrubExtensions} to drop nested credentials in those\n * envelopes. See the module-level migration footgun warning.\n *\n * Storefronts call this once per upstream target during fan-out:\n *\n * ```ts\n * const safe = pickWireSpecFields(buyerReq, 'UpdateMediaBuyRequest');\n * for (const target of targets) {\n * const perTarget = scrubExtensions(safe, { ... });\n * await operational.updateMediaBuy(ctxFor(target), perTarget);\n * }\n * ```\n *\n * Pollers that construct requests from stored state typically don't\n * need this — their inputs aren't buyer-controlled. They can opt in\n * for symmetric type safety with `as WireSafe<T>` if their type\n * system encourages it, or call this helper anyway (the strip is a\n * no-op when input is already wire-spec only).\n *\n * @param request - Buyer-supplied (or otherwise untrusted) input.\n * @param schemaName - PascalCase request type name from\n * {@link WIRE_SPEC_FIELDS}. TypeScript narrows the return type to\n * `WireSafe<RequestType>` based on this — so\n * `pickWireSpecFields(req, 'UpdateMediaBuyRequest')` returns\n * `WireSafe<UpdateMediaBuyRequest>`.\n *\n * @example\n * ```ts\n * import { pickWireSpecFields } from '@adcp/sdk/server';\n *\n * // Buyer sends:\n * // { media_buy_id: 'mb_1', paused: true, snap_access_token: 'attacker' }\n * const safe = pickWireSpecFields(buyerReq, 'UpdateMediaBuyRequest');\n * // safe: WireSafe<UpdateMediaBuyRequest>\n * // value: { media_buy_id: 'mb_1', paused: true } — credential dropped\n * ```\n */\nexport function pickWireSpecFields<K extends WireSpecRequestName>(\n request: unknown,\n schemaName: K\n): WireSafe<WireSpecRequestShape<K>> {\n const allowlist = WIRE_SPEC_FIELDS[schemaName].fields;\n const out: Record<string, unknown> = {};\n if (request === null || typeof request !== 'object') {\n // Defensive: caller passed something that isn't a request object.\n // Return an empty WireSafe — the upstream call will reject on\n // missing required fields (e.g. `idempotency_key`), which is the\n // right error class to surface (it's adopter-side, not buyer-side).\n return out as unknown as WireSafe<WireSpecRequestShape<K>>;\n }\n const source = request as Record<string, unknown>;\n // Indexed loop instead of for-of: avoids Array.prototype[Symbol.iterator]\n // dispatch, which a supply-chain dep could poison to inject extra field\n // names into the iteration at call time (freeze of the array object does\n // not prevent iterator-protocol interception).\n const len = allowlist.length;\n for (let i = 0; i < len; i++) {\n const field = allowlist[i]!; // non-null: i is bounds-checked by len\n if (Object.prototype.hasOwnProperty.call(source, field)) {\n out[field] = source[field];\n }\n }\n return out as unknown as WireSafe<WireSpecRequestShape<K>>;\n}\n\n/**\n * Apply an extension-object allowlist to a `WireSafe<T>` and\n * recursively scrub credential-shaped values. Closes the round-2 /\n * round-3 nested-credential vectors that `pickWireSpecFields` alone\n * leaves open (because `ext` and `context` are wire-spec fields and\n * are preserved verbatim by the pick).\n *\n * Three operations, applied in order:\n *\n * 1. **Top-level allowlist filter** (when `allowedExtKeys` is set):\n * drop keys from `ext` and `context` that aren't in the\n * allowlist. Pass an empty Set to drop both fields entirely;\n * pass `undefined` to leave the top level untouched.\n * 2. **Recursive credential scan** (when `recursiveCredentialScan`\n * is `true`, default): walk surviving values in `ext`/`context`\n * at any depth and drop nested keys that match the L1\n * credential-pattern set (or an adopter-supplied matcher). Closes\n * `ext.partner.token` and similar deep-nesting attack vectors.\n * 3. **Adopter inject** (when `inject` is set): merge\n * storefront-controlled values into `ext` and/or `context`.\n * Inject runs LAST so storefront-resolved credentials override\n * any allowlisted buyer values that collide.\n *\n * The `WireSafe<T>` brand survives — the operation is closed over\n * the wire-spec field set.\n *\n * @example\n * ```ts\n * const safe = pickWireSpecFields(buyerReq, 'UpdateMediaBuyRequest');\n * const perTarget = scrubExtensions(safe, {\n * allowedExtKeys: new Set(['scope3_api_key', 'partner_request_id']),\n * inject: {\n * context: {\n * managed_access_token: target.token,\n * managed_advertiser_id: target.advertiserId,\n * },\n * },\n * });\n * ```\n */\nexport interface ScrubExtensionsOptions {\n /**\n * Set of keys permitted to survive on `ext` and `context`. Keys\n * outside this set are dropped from both. Pass `undefined` to leave\n * `ext`/`context` top-level untouched (only the recursive scan and\n * `inject` apply); pass an empty set to drop both entirely.\n */\n allowedExtKeys?: ReadonlySet<string>;\n\n /**\n * When true (default), recursively walk surviving `ext`/`context`\n * values and drop nested keys matching the credential-pattern set.\n * Closes round-4-style deep-nesting attack vectors that the\n * top-level allowlist alone cannot — e.g. an adopter who allowlists\n * `partner` and forgets that `partner.token` is buyer-controlled.\n *\n * Set to `false` only when you've validated the surviving\n * `ext`/`context` shapes yourself (e.g. via Zod-typed extensions).\n */\n recursiveCredentialScan?: boolean;\n\n /**\n * Optional matcher overrides for the recursive scan. Defaults to\n * the L1 credential-policy default pattern set\n * ({@link DEFAULT_CREDENTIAL_PATTERNS}). Pass `extend` to add\n * adopter-specific patterns; pass `matcher` to fully replace the\n * regex set.\n */\n credentialPatterns?: CredentialPatternsConfig;\n\n /**\n * Adopter-controlled values to merge into `ext` and/or `context`\n * AFTER the allowlist filter and recursive scan run. Used to inject\n * storefront-owned routing tokens (e.g. resolved per-target\n * advertiser IDs) into the per-target request.\n *\n * **Adopter-side ONLY — values here MUST be storefront-derived,\n * never read from the incoming buyer request.** Threading buyer-\n * controlled values through `inject` defeats the discipline.\n * Values injected here bypass the recursive credential scan and\n * are merged in after it completes — ensure they are fully\n * storefront-derived, never shaped by buyer input.\n */\n inject?: {\n ext?: Record<string, unknown>;\n context?: Record<string, unknown>;\n };\n}\n\n/**\n * Implementation of {@link ScrubExtensionsOptions}. See JSDoc above.\n */\nexport function scrubExtensions<T extends object>(request: WireSafe<T>, options: ScrubExtensionsOptions): WireSafe<T> {\n const out = { ...(request as object) } as Record<string, unknown>;\n const { allowedExtKeys, recursiveCredentialScan = true, credentialPatterns, inject } = options;\n\n // Step 1: top-level allowlist filter. When `allowedExtKeys` is\n // undefined we leave the top level alone (recursive scan and\n // inject still run); when it's an empty set we drop both fields.\n if (allowedExtKeys !== undefined) {\n const sourceExt = (out.ext ?? {}) as Record<string, unknown>;\n const sourceCtx = (out.context ?? {}) as Record<string, unknown>;\n const filteredExt: Record<string, unknown> = {};\n const filteredCtx: Record<string, unknown> = {};\n for (const k of allowedExtKeys) {\n if (Object.prototype.hasOwnProperty.call(sourceExt, k)) filteredExt[k] = sourceExt[k];\n if (Object.prototype.hasOwnProperty.call(sourceCtx, k)) filteredCtx[k] = sourceCtx[k];\n }\n out.ext = filteredExt;\n out.context = filteredCtx;\n }\n\n // Step 2: recursive credential-shape scan. Strategy depends on\n // whether step 1 ran:\n //\n // - With `allowedExtKeys` set: top-level is already gated by\n // the explicit allowlist. The adopter has affirmed those keys\n // are legitimate (e.g. `scope3_api_key` allowlisted on a\n // specific tool), so we DON'T second-guess them by name. The\n // scan applies to depths 1+ — dropping nested credentials\n // INSIDE allowlisted values (`partner: { token: '...' }`\n // becomes `partner: {}`).\n //\n // - Without `allowedExtKeys`: no top-level gate; scan from\n // depth 0 to drop credential-shaped keys at any depth. This\n // is the path adopters take when they trust their own\n // ext/context shapes but want belt-and-suspenders coverage.\n if (recursiveCredentialScan) {\n const fromDepth = allowedExtKeys !== undefined ? 1 : 0;\n if (out.ext !== undefined && out.ext !== null && typeof out.ext === 'object') {\n out.ext = stripNestedCredentials(out.ext, credentialPatterns, fromDepth);\n }\n if (out.context !== undefined && out.context !== null && typeof out.context === 'object') {\n out.context = stripNestedCredentials(out.context, credentialPatterns, fromDepth);\n }\n }\n\n // Step 3: adopter-controlled inject. Runs AFTER the scan so\n // storefront-resolved credentials/IDs aren't accidentally dropped\n // by the recursive scan (e.g. injected `managed_access_token` is\n // intentional and belongs).\n if (inject?.ext) {\n out.ext = { ...(out.ext as Record<string, unknown> | undefined), ...inject.ext };\n }\n if (inject?.context) {\n out.context = { ...(out.context as Record<string, unknown> | undefined), ...inject.context };\n }\n\n return out as WireSafe<T>;\n}\n\n/**\n * Recursively walk a value and drop keys matching the credential\n * pattern set, starting from `fromDepth`. Used by `scrubExtensions`\n * to close round-4 nesting.\n *\n * `fromDepth = 0` scans every depth including the top level — used\n * when no `allowedExtKeys` is supplied.\n * `fromDepth = 1` skips the top level — used when the top-level\n * keys have already been gated by an explicit allowlist (the\n * adopter affirmed them legitimate by name).\n *\n * Returns a NEW object/array tree — input is not mutated. Cycle-safe\n * via `WeakSet`. Primitives pass through.\n */\nfunction stripNestedCredentials(value: unknown, patterns?: CredentialPatternsConfig, fromDepth = 0): unknown {\n // Identify credential-shaped paths via the L1 scanner.\n const hits = new Set(scanArgsForCredentials(value, patterns));\n if (hits.size === 0) return value;\n\n const seen = new WeakSet<object>();\n const walk = (node: unknown, path: readonly string[], depth: number): unknown => {\n if (node === null || typeof node !== 'object') return node;\n if (seen.has(node)) return node;\n seen.add(node);\n\n if (Array.isArray(node)) {\n return node.map((item, i) => walk(item, [...path, String(i)], depth + 1));\n }\n\n const out: Record<string, unknown> = {};\n for (const [key, child] of Object.entries(node as Record<string, unknown>)) {\n const childPath = [...path, key];\n // Only drop the key if we're AT or BEYOND `fromDepth`. Keys\n // shallower than fromDepth pass through unchecked because\n // they were already gated upstream.\n if (depth >= fromDepth && hits.has(childPath.join('.'))) {\n continue;\n }\n out[key] = walk(child, childPath, depth + 1);\n }\n return out;\n };\n return walk(value, [], 0);\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/server/wire-safe.ts"],"sourcesContent":["/**\n * Wire-spec field discipline at the operational fan-out boundary.\n *\n * `WireSafe<T>` is a branded type that signals \"this object has been\n * stripped to AdCP wire-spec fields and is safe to forward upstream.\"\n * The brand is constructed only by {@link pickWireSpecFields} (or\n * variants). Code that spreads a buyer request directly cannot satisfy\n * the brand — `{ ...buyerReq, paused: true }` is `T`, not\n * `WireSafe<T>`.\n *\n * The brand is the L2 of #1529: where L1 (`credentialPolicy`,\n * `src/lib/server/credential-policy.ts`) catches credential-shaped\n * keys at the buyer-facing dispatch boundary, L2 catches structural\n * leakage at the operational dispatch boundary — storefront fan-out\n * code that picks per-target args from a buyer request, where the\n * scrub happens BEFORE the credential scan would have run on the\n * upstream call.\n *\n * **The brand is opt-in at the type level.** `OperationalPlatform`\n * methods accept plain `UpdateMediaBuyRequest` etc. (not branded —\n * see #1530's interface), so adopters who don't use the helper\n * aren't broken. Adopters who DO use the helper get the safety\n * benefit at the picking site: assigning the result to a\n * `WireSafe<UpdateMediaBuyRequest>` variable forces `pickWireSpecFields`\n * to be the constructor; spreading the buyer request elsewhere fails\n * to satisfy the brand.\n *\n * **Migration footgun:** `pickWireSpecFields` ALONE doesn't close\n * the round-2 / round-3 vectors (nested `context.<x>_access_token`,\n * nested `ext.<x>_access_token`). `ext` and `context` are wire-spec\n * fields, so the helper preserves them whole. Storefronts that fan\n * out MUST chain {@link scrubExtensions} after the pick — that helper\n * filters ext/context to a caller-specified key allowlist AND\n * recursively drops credential-shaped keys at any depth (using the\n * L1 default pattern set or an adopter-supplied matcher). Calling\n * `pickWireSpecFields` without `scrubExtensions` reopens the\n * round-2/round-3 attack surface.\n *\n * @see docs/guides/CTX-METADATA-SAFETY.md\n * @see scripts/generate-wire-spec-fields.ts — codegen for the\n * allowlist constants this module reads.\n *\n * @public\n */\n\nimport { scanArgsForCredentials, type CredentialPatternsConfig } from './credential-policy';\nimport { WIRE_SPEC_FIELDS, type WireSpecRequestName } from './wire-spec-fields.generated';\n\ndeclare const __wireSafe: unique symbol;\n\n/**\n * A request shape that has been narrowed to AdCP wire-spec fields.\n * Cannot be produced by spreading a buyer request — only via\n * {@link pickWireSpecFields} or by typed `as WireSafe<T>` assertion\n * at a known-safe construction site (e.g. a poller that builds the\n * request from stored state, not from buyer input).\n *\n * The brand is what makes this load-bearing: `{ ...buyerRequest }` is\n * `T`, not `WireSafe<T>`, so passing it where `WireSafe<T>` is\n * required is a compile error.\n */\nexport type WireSafe<T> = T & { readonly [__wireSafe]: true };\n\nexport type { WireSpecRequestName };\n\nexport { WIRE_SPEC_FIELDS };\n\n/**\n * The wire-spec request type associated with a `WireSpecRequestName`.\n * Internal — adopters access it transparently via\n * `pickWireSpecFields`'s return narrowing.\n */\ntype WireSpecRequestShape<K extends WireSpecRequestName> = (typeof WIRE_SPEC_FIELDS)[K]['__type'];\n\n/**\n * Strip a buyer request to the wire-spec fields defined for\n * `schemaName` and return the result branded `WireSafe<T>`. The\n * canonical constructor of {@link WireSafe}.\n *\n * Drops every property NOT in the schema's `properties` allowlist,\n * including buyer-supplied unknown keys (`<platform>_access_token`,\n * `account: { brand: 'attacker.com' }`-style identity-pivot fields,\n * arbitrary attacker payload). The allowlist comes from codegen — the\n * AdCP request schema IS the allowlist; drift is structurally\n * impossible.\n *\n * **Top-level only.** `ext` and `context` are wire-spec fields and\n * are preserved verbatim by this helper. Storefronts MUST chain\n * {@link scrubExtensions} to drop nested credentials in those\n * envelopes. See the module-level migration footgun warning.\n *\n * Storefronts call this once per upstream target during fan-out:\n *\n * ```ts\n * const safe = pickWireSpecFields(buyerReq, 'UpdateMediaBuyRequest');\n * for (const target of targets) {\n * const perTarget = scrubExtensions(safe, { ... });\n * await operational.updateMediaBuy(ctxFor(target), perTarget);\n * }\n * ```\n *\n * Pollers that construct requests from stored state typically don't\n * need this — their inputs aren't buyer-controlled. They can opt in\n * for symmetric type safety with `as WireSafe<T>` if their type\n * system encourages it, or call this helper anyway (the strip is a\n * no-op when input is already wire-spec only).\n *\n * @param request - Buyer-supplied (or otherwise untrusted) input.\n * @param schemaName - PascalCase request type name from\n * {@link WIRE_SPEC_FIELDS}. TypeScript narrows the return type to\n * `WireSafe<RequestType>` based on this — so\n * `pickWireSpecFields(req, 'UpdateMediaBuyRequest')` returns\n * `WireSafe<UpdateMediaBuyRequest>`.\n *\n * @example\n * ```ts\n * import { pickWireSpecFields } from '@adcp/sdk/server';\n *\n * // Buyer sends:\n * // { media_buy_id: 'mb_1', paused: true, snap_access_token: 'attacker' }\n * const safe = pickWireSpecFields(buyerReq, 'UpdateMediaBuyRequest');\n * // safe: WireSafe<UpdateMediaBuyRequest>\n * // value: { media_buy_id: 'mb_1', paused: true } — credential dropped\n * ```\n */\nexport function pickWireSpecFields<K extends WireSpecRequestName>(\n request: unknown,\n schemaName: K\n): WireSafe<WireSpecRequestShape<K>> {\n const allowlist = WIRE_SPEC_FIELDS[schemaName].fields;\n const out: Record<string, unknown> = {};\n if (request === null || typeof request !== 'object') {\n // Defensive: caller passed something that isn't a request object.\n // Return an empty WireSafe — the upstream call will reject on\n // missing required fields (e.g. `idempotency_key`), which is the\n // right error class to surface (it's adopter-side, not buyer-side).\n return out as unknown as WireSafe<WireSpecRequestShape<K>>;\n }\n const source = request as Record<string, unknown>;\n // Indexed loop instead of for-of: avoids Array.prototype[Symbol.iterator]\n // dispatch, which a supply-chain dep could poison to inject extra field\n // names into the iteration at call time (freeze of the array object does\n // not prevent iterator-protocol interception).\n const len = allowlist.length;\n for (let i = 0; i < len; i++) {\n const field = allowlist[i]!; // non-null: i is bounds-checked by len\n if (Object.prototype.hasOwnProperty.call(source, field)) {\n out[field] = source[field];\n }\n }\n return out as unknown as WireSafe<WireSpecRequestShape<K>>;\n}\n\n/**\n * Apply an extension-object allowlist to a `WireSafe<T>` and\n * recursively scrub credential-shaped values. Closes the round-2 /\n * round-3 nested-credential vectors that `pickWireSpecFields` alone\n * leaves open (because `ext` and `context` are wire-spec fields and\n * are preserved verbatim by the pick).\n *\n * Three operations, applied in order:\n *\n * 1. **Top-level allowlist filter** (when `allowedExtKeys` is set):\n * drop keys from `ext` and `context` that aren't in the\n * allowlist. Pass an empty Set to drop both fields entirely;\n * pass `undefined` to leave the top level untouched.\n * 2. **Recursive credential scan** (when `recursiveCredentialScan`\n * is `true`, default): walk surviving values in `ext`/`context`\n * at any depth and drop nested keys that match the L1\n * credential-pattern set (or an adopter-supplied matcher). Closes\n * `ext.partner.token` and similar deep-nesting attack vectors.\n * 3. **Adopter inject** (when `inject` is set): merge\n * storefront-controlled values into `ext` and/or `context`.\n * Inject runs LAST so storefront-resolved credentials override\n * any allowlisted buyer values that collide.\n *\n * The `WireSafe<T>` brand survives — the operation is closed over\n * the wire-spec field set.\n *\n * @example\n * ```ts\n * const safe = pickWireSpecFields(buyerReq, 'UpdateMediaBuyRequest');\n * const perTarget = scrubExtensions(safe, {\n * allowedExtKeys: new Set(['scope3_api_key', 'partner_request_id']),\n * inject: {\n * context: {\n * managed_access_token: target.token,\n * managed_advertiser_id: target.advertiserId,\n * },\n * },\n * });\n * ```\n */\nexport interface ScrubExtensionsOptions {\n /**\n * Set of keys permitted to survive on `ext` and `context`. Keys\n * outside this set are dropped from both. Pass `undefined` to leave\n * `ext`/`context` top-level untouched (only the recursive scan and\n * `inject` apply); pass an empty set to drop both entirely.\n */\n allowedExtKeys?: ReadonlySet<string>;\n\n /**\n * When true (default), recursively walk surviving `ext`/`context`\n * values and drop nested keys matching the credential-pattern set.\n * Closes round-4-style deep-nesting attack vectors that the\n * top-level allowlist alone cannot — e.g. an adopter who allowlists\n * `partner` and forgets that `partner.token` is buyer-controlled.\n *\n * Set to `false` only when you've validated the surviving\n * `ext`/`context` shapes yourself (e.g. via Zod-typed extensions).\n */\n recursiveCredentialScan?: boolean;\n\n /**\n * Optional matcher overrides for the recursive scan. Defaults to\n * the L1 credential-policy default pattern set\n * ({@link DEFAULT_CREDENTIAL_PATTERNS}). Pass `extend` to add\n * adopter-specific patterns; pass `matcher` to fully replace the\n * regex set.\n */\n credentialPatterns?: CredentialPatternsConfig;\n\n /**\n * Adopter-controlled values to merge into `ext` and/or `context`\n * AFTER the allowlist filter and recursive scan run. Used to inject\n * storefront-owned routing tokens (e.g. resolved per-target\n * advertiser IDs) into the per-target request.\n *\n * **Adopter-side ONLY — values here MUST be storefront-derived,\n * never read from the incoming buyer request.** Threading buyer-\n * controlled values through `inject` defeats the discipline.\n * Values injected here bypass the recursive credential scan and\n * are merged in after it completes — ensure they are fully\n * storefront-derived, never shaped by buyer input.\n */\n inject?: {\n ext?: Record<string, unknown>;\n context?: Record<string, unknown>;\n };\n}\n\n/**\n * Implementation of {@link ScrubExtensionsOptions}. See JSDoc above.\n */\nexport function scrubExtensions<T extends object>(request: WireSafe<T>, options: ScrubExtensionsOptions): WireSafe<T> {\n const out = { ...(request as object) } as Record<string, unknown>;\n const { allowedExtKeys, recursiveCredentialScan = true, credentialPatterns, inject } = options;\n\n // Step 1: top-level allowlist filter. When `allowedExtKeys` is\n // undefined we leave the top level alone (recursive scan and\n // inject still run); when it's an empty set we drop both fields.\n if (allowedExtKeys !== undefined) {\n const sourceExt = (out.ext ?? {}) as Record<string, unknown>;\n const sourceCtx = (out.context ?? {}) as Record<string, unknown>;\n const filteredExt: Record<string, unknown> = {};\n const filteredCtx: Record<string, unknown> = {};\n for (const k of allowedExtKeys) {\n if (Object.prototype.hasOwnProperty.call(sourceExt, k)) filteredExt[k] = sourceExt[k];\n if (Object.prototype.hasOwnProperty.call(sourceCtx, k)) filteredCtx[k] = sourceCtx[k];\n }\n out.ext = filteredExt;\n out.context = filteredCtx;\n }\n\n // Step 2: recursive credential-shape scan. Strategy depends on\n // whether step 1 ran:\n //\n // - With `allowedExtKeys` set: top-level is already gated by\n // the explicit allowlist. The adopter has affirmed those keys\n // are legitimate (e.g. `scope3_api_key` allowlisted on a\n // specific tool), so we DON'T second-guess them by name. The\n // scan applies to depths 1+ — dropping nested credentials\n // INSIDE allowlisted values (`partner: { token: '...' }`\n // becomes `partner: {}`).\n //\n // - Without `allowedExtKeys`: no top-level gate; scan from\n // depth 0 to drop credential-shaped keys at any depth. This\n // is the path adopters take when they trust their own\n // ext/context shapes but want belt-and-suspenders coverage.\n if (recursiveCredentialScan) {\n const fromDepth = allowedExtKeys !== undefined ? 1 : 0;\n if (out.ext !== undefined && out.ext !== null && typeof out.ext === 'object') {\n out.ext = stripNestedCredentials(out.ext, credentialPatterns, fromDepth);\n }\n if (out.context !== undefined && out.context !== null && typeof out.context === 'object') {\n out.context = stripNestedCredentials(out.context, credentialPatterns, fromDepth);\n }\n }\n\n // Step 3: adopter-controlled inject. Runs AFTER the scan so\n // storefront-resolved credentials/IDs aren't accidentally dropped\n // by the recursive scan (e.g. injected `managed_access_token` is\n // intentional and belongs).\n if (inject?.ext) {\n out.ext = { ...(out.ext as Record<string, unknown> | undefined), ...inject.ext };\n }\n if (inject?.context) {\n out.context = { ...(out.context as Record<string, unknown> | undefined), ...inject.context };\n }\n\n return out as WireSafe<T>;\n}\n\n/**\n * Recursively walk a value and drop keys matching the credential\n * pattern set, starting from `fromDepth`. Used by `scrubExtensions`\n * to close round-4 nesting.\n *\n * `fromDepth = 0` scans every depth including the top level — used\n * when no `allowedExtKeys` is supplied.\n * `fromDepth = 1` skips the top level — used when the top-level\n * keys have already been gated by an explicit allowlist (the\n * adopter affirmed them legitimate by name).\n *\n * Returns a NEW object/array tree — input is not mutated. Cycle-safe\n * via `WeakSet`. Primitives pass through.\n */\nfunction stripNestedCredentials(value: unknown, patterns?: CredentialPatternsConfig, fromDepth = 0): unknown {\n // Identify credential-shaped paths via the L1 scanner.\n const hits = new Set(scanArgsForCredentials(value, patterns));\n if (hits.size === 0) return value;\n\n const seen = new WeakSet<object>();\n const walk = (node: unknown, path: readonly string[], depth: number): unknown => {\n if (node === null || typeof node !== 'object') return node;\n if (seen.has(node)) return node;\n seen.add(node);\n\n if (Array.isArray(node)) {\n return node.map((item, i) => walk(item, [...path, String(i)], depth + 1));\n }\n\n const out: Record<string, unknown> = {};\n for (const [key, child] of Object.entries(node as Record<string, unknown>)) {\n const childPath = [...path, key];\n // Only drop the key if we're AT or BEYOND `fromDepth`. Keys\n // shallower than fromDepth pass through unchecked because\n // they were already gated upstream.\n if (depth >= fromDepth && hits.has(childPath.join('.'))) {\n continue;\n }\n out[key] = walk(child, childPath, depth + 1);\n }\n return out;\n };\n return walk(value, [], 0);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import { fileURLToPath as __adcpFileURLToPath } from 'node:url';
|
|
2
|
-
import { dirname as __adcpDirname } from 'node:path';
|
|
3
|
-
import { createRequire as __adcpCreateRequire } from 'node:module';
|
|
4
|
-
const __filename = __adcpFileURLToPath(import.meta.url);
|
|
5
|
-
const __dirname = __adcpDirname(__filename);
|
|
6
|
-
const require = __adcpCreateRequire(import.meta.url);
|
|
7
1
|
const WIRE_SPEC_FIELDS = Object.freeze({
|
|
8
2
|
/** schemas/cache/3.1.2/brand/acquire-rights-request.json */
|
|
9
3
|
AcquireRightsRequest: Object.freeze({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/lib/server/wire-spec-fields.generated.ts"],"sourcesContent":["// AUTO-GENERATED by scripts/generate-wire-spec-fields.ts. DO NOT EDIT.\n// Source: schemas/cache/3.1.2/**/*-request.json\n// Generated at: 2026-07-08T16:17:05.403Z\n\nimport type {\n AcquireRightsRequest,\n ActivateSignalRequest,\n BuildCreativeRequest,\n CalibrateContentRequest,\n CreateCollectionListRequest,\n CreateContentStandardsRequest,\n CreateMediaBuyRequest,\n CreatePropertyListRequest,\n DeleteCollectionListRequest,\n DeletePropertyListRequest,\n GetMediaBuyDeliveryRequest,\n LogEventRequest,\n ProvidePerformanceFeedbackRequest,\n ReportPlanOutcomeRequest,\n ReportUsageRequest,\n SIInitiateSessionRequest,\n SISendMessageRequest,\n SyncAccountsRequest,\n SyncAudiencesRequest,\n SyncCatalogsRequest,\n SyncCreativesRequest,\n SyncEventSourcesRequest,\n SyncGovernanceRequest,\n SyncPlansRequest,\n UpdateCollectionListRequest,\n UpdateContentStandardsRequest,\n UpdateMediaBuyRequest,\n UpdatePropertyListRequest,\n UpdateRightsRequest,\n} from '../types';\n\n/**\n * Wire-spec field allowlists per request type. The `fields` values\n * are exact top-level property names from the AdCP request JSON\n * schemas; `pickWireSpecFields(req, schemaName)` uses them to strip\n * buyer-controlled args to schema-spec fields at the operational\n * fan-out boundary. Drift between this map and the schemas is\n * impossible by construction — both are emitted from the same\n * codegen pass.\n *\n * The `__type` phantom is type-only (`null as unknown as T`) — it\n * carries the wire-spec request shape so `pickWireSpecFields`'s\n * return type narrows per `schemaName`. Runtime cost: one null\n * property per entry.\n *\n * Arrays and entry objects are runtime-frozen via `Object.freeze`\n * so a misbehaving dependency cannot widen the scrub via prototype\n * pollution or shared-state mutation of the allowlist.\n */\nexport const WIRE_SPEC_FIELDS = Object.freeze({\n /** schemas/cache/3.1.2/brand/acquire-rights-request.json */\n AcquireRightsRequest: Object.freeze({\n fields: Object.freeze([\"account\",\"buyer\",\"campaign\",\"context\",\"ext\",\"idempotency_key\",\"pricing_option_id\",\"push_notification_config\",\"revocation_webhook\",\"rights_id\"]) as readonly string[],\n __type: null as unknown as AcquireRightsRequest,\n }),\n /** schemas/cache/3.1.2/signals/activate-signal-request.json */\n ActivateSignalRequest: Object.freeze({\n fields: Object.freeze([\"account\",\"action\",\"context\",\"destinations\",\"ext\",\"governance_context\",\"idempotency_key\",\"pricing_option_id\",\"signal_agent_segment_id\"]) as readonly string[],\n __type: null as unknown as ActivateSignalRequest,\n }),\n /** schemas/cache/3.1.2/media-buy/build-creative-request.json */\n BuildCreativeRequest: Object.freeze({\n fields: Object.freeze([\"account\",\"brand\",\"concept_id\",\"config\",\"context\",\"creative_id\",\"creative_manifest\",\"evaluator\",\"ext\",\"idempotency_key\",\"include_preview\",\"item_limit\",\"keep_mode\",\"macro_values\",\"max_creatives\",\"max_spend\",\"max_variants\",\"media_buy_id\",\"message\",\"mode\",\"package_id\",\"preview_inputs\",\"preview_output_format\",\"preview_quality\",\"push_notification_config\",\"quality\",\"refine_from_build_variant_id\",\"selection_strategy\",\"signal_conditions\",\"target_format_id\",\"target_format_ids\",\"transformer_id\",\"variant_axis\"]) as readonly string[],\n __type: null as unknown as BuildCreativeRequest,\n }),\n /** schemas/cache/3.1.2/content-standards/calibrate-content-request.json */\n CalibrateContentRequest: Object.freeze({\n fields: Object.freeze([\"artifact\",\"context\",\"ext\",\"idempotency_key\",\"standards_id\"]) as readonly string[],\n __type: null as unknown as CalibrateContentRequest,\n }),\n /** schemas/cache/3.1.2/collection/create-collection-list-request.json */\n CreateCollectionListRequest: Object.freeze({\n fields: Object.freeze([\"account\",\"base_collections\",\"brand\",\"context\",\"description\",\"ext\",\"filters\",\"idempotency_key\",\"name\"]) as readonly string[],\n __type: null as unknown as CreateCollectionListRequest,\n }),\n /** schemas/cache/3.1.2/content-standards/create-content-standards-request.json */\n CreateContentStandardsRequest: Object.freeze({\n fields: Object.freeze([\"calibration_exemplars\",\"context\",\"ext\",\"idempotency_key\",\"policies\",\"registry_policy_ids\",\"scope\"]) as readonly string[],\n __type: null as unknown as CreateContentStandardsRequest,\n }),\n /** schemas/cache/3.1.2/media-buy/create-media-buy-request.json */\n CreateMediaBuyRequest: Object.freeze({\n fields: Object.freeze([\"account\",\"advertiser_industry\",\"agency_estimate_number\",\"artifact_webhook\",\"brand\",\"context\",\"end_time\",\"ext\",\"idempotency_key\",\"invoice_recipient\",\"io_acceptance\",\"packages\",\"paused\",\"plan_id\",\"po_number\",\"proposal_id\",\"push_notification_config\",\"reporting_webhook\",\"start_time\",\"total_budget\"]) as readonly string[],\n __type: null as unknown as CreateMediaBuyRequest,\n }),\n /** schemas/cache/3.1.2/property/create-property-list-request.json */\n CreatePropertyListRequest: Object.freeze({\n fields: Object.freeze([\"account\",\"base_properties\",\"brand\",\"context\",\"description\",\"ext\",\"filters\",\"idempotency_key\",\"name\"]) as readonly string[],\n __type: null as unknown as CreatePropertyListRequest,\n }),\n /** schemas/cache/3.1.2/collection/delete-collection-list-request.json */\n DeleteCollectionListRequest: Object.freeze({\n fields: Object.freeze([\"account\",\"context\",\"ext\",\"idempotency_key\",\"list_id\"]) as readonly string[],\n __type: null as unknown as DeleteCollectionListRequest,\n }),\n /** schemas/cache/3.1.2/property/delete-property-list-request.json */\n DeletePropertyListRequest: Object.freeze({\n fields: Object.freeze([\"account\",\"context\",\"ext\",\"idempotency_key\",\"list_id\"]) as readonly string[],\n __type: null as unknown as DeletePropertyListRequest,\n }),\n /** schemas/cache/3.1.2/media-buy/get-media-buy-delivery-request.json */\n GetMediaBuyDeliveryRequest: Object.freeze({\n fields: Object.freeze([\"account\",\"attribution_window\",\"context\",\"end_date\",\"ext\",\"include_package_daily_breakdown\",\"include_window_breakdown\",\"media_buy_ids\",\"reporting_dimensions\",\"start_date\",\"status_filter\",\"time_granularity\"]) as readonly string[],\n __type: null as unknown as GetMediaBuyDeliveryRequest,\n }),\n /** schemas/cache/3.1.2/media-buy/log-event-request.json */\n LogEventRequest: Object.freeze({\n fields: Object.freeze([\"context\",\"event_source_id\",\"events\",\"ext\",\"idempotency_key\",\"test_event_code\"]) as readonly string[],\n __type: null as unknown as LogEventRequest,\n }),\n /** schemas/cache/3.1.2/media-buy/provide-performance-feedback-request.json */\n ProvidePerformanceFeedbackRequest: Object.freeze({\n fields: Object.freeze([\"context\",\"creative_id\",\"ext\",\"feedback_source\",\"idempotency_key\",\"measurement_period\",\"media_buy_id\",\"metric_type\",\"package_id\",\"performance_index\"]) as readonly string[],\n __type: null as unknown as ProvidePerformanceFeedbackRequest,\n }),\n /** schemas/cache/3.1.2/governance/report-plan-outcome-request.json */\n ReportPlanOutcomeRequest: Object.freeze({\n fields: Object.freeze([\"check_id\",\"context\",\"delivery\",\"error\",\"ext\",\"governance_context\",\"idempotency_key\",\"outcome\",\"plan_id\",\"purchase_type\",\"seller_response\"]) as readonly string[],\n __type: null as unknown as ReportPlanOutcomeRequest,\n }),\n /** schemas/cache/3.1.2/account/report-usage-request.json */\n ReportUsageRequest: Object.freeze({\n fields: Object.freeze([\"context\",\"ext\",\"idempotency_key\",\"reporting_period\",\"usage\"]) as readonly string[],\n __type: null as unknown as ReportUsageRequest,\n }),\n /** schemas/cache/3.1.2/sponsored-intelligence/si-initiate-session-request.json */\n SIInitiateSessionRequest: Object.freeze({\n fields: Object.freeze([\"context\",\"ext\",\"idempotency_key\",\"identity\",\"intent\",\"media_buy_id\",\"offering_id\",\"offering_token\",\"placement\",\"sponsored_context_receipt\",\"supported_capabilities\"]) as readonly string[],\n __type: null as unknown as SIInitiateSessionRequest,\n }),\n /** schemas/cache/3.1.2/sponsored-intelligence/si-send-message-request.json */\n SISendMessageRequest: Object.freeze({\n fields: Object.freeze([\"action_response\",\"context\",\"ext\",\"idempotency_key\",\"message\",\"session_id\",\"sponsored_context_receipt\"]) as readonly string[],\n __type: null as unknown as SISendMessageRequest,\n }),\n /** schemas/cache/3.1.2/account/sync-accounts-request.json */\n SyncAccountsRequest: Object.freeze({\n fields: Object.freeze([\"accounts\",\"context\",\"delete_missing\",\"dry_run\",\"ext\",\"idempotency_key\",\"push_notification_config\"]) as readonly string[],\n __type: null as unknown as SyncAccountsRequest,\n }),\n /** schemas/cache/3.1.2/media-buy/sync-audiences-request.json */\n SyncAudiencesRequest: Object.freeze({\n fields: Object.freeze([\"account\",\"audiences\",\"context\",\"delete_missing\",\"ext\",\"idempotency_key\"]) as readonly string[],\n __type: null as unknown as SyncAudiencesRequest,\n }),\n /** schemas/cache/3.1.2/media-buy/sync-catalogs-request.json */\n SyncCatalogsRequest: Object.freeze({\n fields: Object.freeze([\"account\",\"catalog_ids\",\"catalogs\",\"context\",\"delete_missing\",\"dry_run\",\"ext\",\"idempotency_key\",\"push_notification_config\",\"validation_mode\"]) as readonly string[],\n __type: null as unknown as SyncCatalogsRequest,\n }),\n /** schemas/cache/3.1.2/creative/sync-creatives-request.json */\n SyncCreativesRequest: Object.freeze({\n fields: Object.freeze([\"account\",\"assignments\",\"context\",\"creative_ids\",\"creatives\",\"delete_missing\",\"dry_run\",\"ext\",\"idempotency_key\",\"push_notification_config\",\"validation_mode\"]) as readonly string[],\n __type: null as unknown as SyncCreativesRequest,\n }),\n /** schemas/cache/3.1.2/media-buy/sync-event-sources-request.json */\n SyncEventSourcesRequest: Object.freeze({\n fields: Object.freeze([\"account\",\"context\",\"delete_missing\",\"event_sources\",\"ext\",\"idempotency_key\"]) as readonly string[],\n __type: null as unknown as SyncEventSourcesRequest,\n }),\n /** schemas/cache/3.1.2/account/sync-governance-request.json */\n SyncGovernanceRequest: Object.freeze({\n fields: Object.freeze([\"accounts\",\"context\",\"ext\",\"idempotency_key\"]) as readonly string[],\n __type: null as unknown as SyncGovernanceRequest,\n }),\n /** schemas/cache/3.1.2/governance/sync-plans-request.json */\n SyncPlansRequest: Object.freeze({\n fields: Object.freeze([\"context\",\"ext\",\"idempotency_key\",\"plans\"]) as readonly string[],\n __type: null as unknown as SyncPlansRequest,\n }),\n /** schemas/cache/3.1.2/collection/update-collection-list-request.json */\n UpdateCollectionListRequest: Object.freeze({\n fields: Object.freeze([\"account\",\"base_collections\",\"brand\",\"context\",\"description\",\"ext\",\"filters\",\"idempotency_key\",\"list_id\",\"name\",\"webhook_url\"]) as readonly string[],\n __type: null as unknown as UpdateCollectionListRequest,\n }),\n /** schemas/cache/3.1.2/content-standards/update-content-standards-request.json */\n UpdateContentStandardsRequest: Object.freeze({\n fields: Object.freeze([\"calibration_exemplars\",\"context\",\"ext\",\"idempotency_key\",\"policies\",\"registry_policy_ids\",\"scope\",\"standards_id\"]) as readonly string[],\n __type: null as unknown as UpdateContentStandardsRequest,\n }),\n /** schemas/cache/3.1.2/media-buy/update-media-buy-request.json */\n UpdateMediaBuyRequest: Object.freeze({\n fields: Object.freeze([\"account\",\"canceled\",\"cancellation_reason\",\"context\",\"end_time\",\"ext\",\"idempotency_key\",\"invoice_recipient\",\"media_buy_id\",\"new_packages\",\"packages\",\"paused\",\"push_notification_config\",\"reporting_webhook\",\"revision\",\"start_time\"]) as readonly string[],\n __type: null as unknown as UpdateMediaBuyRequest,\n }),\n /** schemas/cache/3.1.2/property/update-property-list-request.json */\n UpdatePropertyListRequest: Object.freeze({\n fields: Object.freeze([\"account\",\"base_properties\",\"brand\",\"context\",\"description\",\"ext\",\"filters\",\"idempotency_key\",\"list_id\",\"name\",\"webhook_url\"]) as readonly string[],\n __type: null as unknown as UpdatePropertyListRequest,\n }),\n /** schemas/cache/3.1.2/brand/update-rights-request.json */\n UpdateRightsRequest: Object.freeze({\n fields: Object.freeze([\"account\",\"context\",\"end_date\",\"ext\",\"idempotency_key\",\"impression_cap\",\"paused\",\"pricing_option_id\",\"push_notification_config\",\"rights_id\"]) as readonly string[],\n __type: null as unknown as UpdateRightsRequest,\n }),\n} as const);\n\nexport type WireSpecRequestName = keyof typeof WIRE_SPEC_FIELDS;\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/server/wire-spec-fields.generated.ts"],"sourcesContent":["// AUTO-GENERATED by scripts/generate-wire-spec-fields.ts. DO NOT EDIT.\n// Source: schemas/cache/3.1.2/**/*-request.json\n// Generated at: 2026-07-08T16:17:05.403Z\n\nimport type {\n AcquireRightsRequest,\n ActivateSignalRequest,\n BuildCreativeRequest,\n CalibrateContentRequest,\n CreateCollectionListRequest,\n CreateContentStandardsRequest,\n CreateMediaBuyRequest,\n CreatePropertyListRequest,\n DeleteCollectionListRequest,\n DeletePropertyListRequest,\n GetMediaBuyDeliveryRequest,\n LogEventRequest,\n ProvidePerformanceFeedbackRequest,\n ReportPlanOutcomeRequest,\n ReportUsageRequest,\n SIInitiateSessionRequest,\n SISendMessageRequest,\n SyncAccountsRequest,\n SyncAudiencesRequest,\n SyncCatalogsRequest,\n SyncCreativesRequest,\n SyncEventSourcesRequest,\n SyncGovernanceRequest,\n SyncPlansRequest,\n UpdateCollectionListRequest,\n UpdateContentStandardsRequest,\n UpdateMediaBuyRequest,\n UpdatePropertyListRequest,\n UpdateRightsRequest,\n} from '../types';\n\n/**\n * Wire-spec field allowlists per request type. The `fields` values\n * are exact top-level property names from the AdCP request JSON\n * schemas; `pickWireSpecFields(req, schemaName)` uses them to strip\n * buyer-controlled args to schema-spec fields at the operational\n * fan-out boundary. Drift between this map and the schemas is\n * impossible by construction — both are emitted from the same\n * codegen pass.\n *\n * The `__type` phantom is type-only (`null as unknown as T`) — it\n * carries the wire-spec request shape so `pickWireSpecFields`'s\n * return type narrows per `schemaName`. Runtime cost: one null\n * property per entry.\n *\n * Arrays and entry objects are runtime-frozen via `Object.freeze`\n * so a misbehaving dependency cannot widen the scrub via prototype\n * pollution or shared-state mutation of the allowlist.\n */\nexport const WIRE_SPEC_FIELDS = Object.freeze({\n /** schemas/cache/3.1.2/brand/acquire-rights-request.json */\n AcquireRightsRequest: Object.freeze({\n fields: Object.freeze([\"account\",\"buyer\",\"campaign\",\"context\",\"ext\",\"idempotency_key\",\"pricing_option_id\",\"push_notification_config\",\"revocation_webhook\",\"rights_id\"]) as readonly string[],\n __type: null as unknown as AcquireRightsRequest,\n }),\n /** schemas/cache/3.1.2/signals/activate-signal-request.json */\n ActivateSignalRequest: Object.freeze({\n fields: Object.freeze([\"account\",\"action\",\"context\",\"destinations\",\"ext\",\"governance_context\",\"idempotency_key\",\"pricing_option_id\",\"signal_agent_segment_id\"]) as readonly string[],\n __type: null as unknown as ActivateSignalRequest,\n }),\n /** schemas/cache/3.1.2/media-buy/build-creative-request.json */\n BuildCreativeRequest: Object.freeze({\n fields: Object.freeze([\"account\",\"brand\",\"concept_id\",\"config\",\"context\",\"creative_id\",\"creative_manifest\",\"evaluator\",\"ext\",\"idempotency_key\",\"include_preview\",\"item_limit\",\"keep_mode\",\"macro_values\",\"max_creatives\",\"max_spend\",\"max_variants\",\"media_buy_id\",\"message\",\"mode\",\"package_id\",\"preview_inputs\",\"preview_output_format\",\"preview_quality\",\"push_notification_config\",\"quality\",\"refine_from_build_variant_id\",\"selection_strategy\",\"signal_conditions\",\"target_format_id\",\"target_format_ids\",\"transformer_id\",\"variant_axis\"]) as readonly string[],\n __type: null as unknown as BuildCreativeRequest,\n }),\n /** schemas/cache/3.1.2/content-standards/calibrate-content-request.json */\n CalibrateContentRequest: Object.freeze({\n fields: Object.freeze([\"artifact\",\"context\",\"ext\",\"idempotency_key\",\"standards_id\"]) as readonly string[],\n __type: null as unknown as CalibrateContentRequest,\n }),\n /** schemas/cache/3.1.2/collection/create-collection-list-request.json */\n CreateCollectionListRequest: Object.freeze({\n fields: Object.freeze([\"account\",\"base_collections\",\"brand\",\"context\",\"description\",\"ext\",\"filters\",\"idempotency_key\",\"name\"]) as readonly string[],\n __type: null as unknown as CreateCollectionListRequest,\n }),\n /** schemas/cache/3.1.2/content-standards/create-content-standards-request.json */\n CreateContentStandardsRequest: Object.freeze({\n fields: Object.freeze([\"calibration_exemplars\",\"context\",\"ext\",\"idempotency_key\",\"policies\",\"registry_policy_ids\",\"scope\"]) as readonly string[],\n __type: null as unknown as CreateContentStandardsRequest,\n }),\n /** schemas/cache/3.1.2/media-buy/create-media-buy-request.json */\n CreateMediaBuyRequest: Object.freeze({\n fields: Object.freeze([\"account\",\"advertiser_industry\",\"agency_estimate_number\",\"artifact_webhook\",\"brand\",\"context\",\"end_time\",\"ext\",\"idempotency_key\",\"invoice_recipient\",\"io_acceptance\",\"packages\",\"paused\",\"plan_id\",\"po_number\",\"proposal_id\",\"push_notification_config\",\"reporting_webhook\",\"start_time\",\"total_budget\"]) as readonly string[],\n __type: null as unknown as CreateMediaBuyRequest,\n }),\n /** schemas/cache/3.1.2/property/create-property-list-request.json */\n CreatePropertyListRequest: Object.freeze({\n fields: Object.freeze([\"account\",\"base_properties\",\"brand\",\"context\",\"description\",\"ext\",\"filters\",\"idempotency_key\",\"name\"]) as readonly string[],\n __type: null as unknown as CreatePropertyListRequest,\n }),\n /** schemas/cache/3.1.2/collection/delete-collection-list-request.json */\n DeleteCollectionListRequest: Object.freeze({\n fields: Object.freeze([\"account\",\"context\",\"ext\",\"idempotency_key\",\"list_id\"]) as readonly string[],\n __type: null as unknown as DeleteCollectionListRequest,\n }),\n /** schemas/cache/3.1.2/property/delete-property-list-request.json */\n DeletePropertyListRequest: Object.freeze({\n fields: Object.freeze([\"account\",\"context\",\"ext\",\"idempotency_key\",\"list_id\"]) as readonly string[],\n __type: null as unknown as DeletePropertyListRequest,\n }),\n /** schemas/cache/3.1.2/media-buy/get-media-buy-delivery-request.json */\n GetMediaBuyDeliveryRequest: Object.freeze({\n fields: Object.freeze([\"account\",\"attribution_window\",\"context\",\"end_date\",\"ext\",\"include_package_daily_breakdown\",\"include_window_breakdown\",\"media_buy_ids\",\"reporting_dimensions\",\"start_date\",\"status_filter\",\"time_granularity\"]) as readonly string[],\n __type: null as unknown as GetMediaBuyDeliveryRequest,\n }),\n /** schemas/cache/3.1.2/media-buy/log-event-request.json */\n LogEventRequest: Object.freeze({\n fields: Object.freeze([\"context\",\"event_source_id\",\"events\",\"ext\",\"idempotency_key\",\"test_event_code\"]) as readonly string[],\n __type: null as unknown as LogEventRequest,\n }),\n /** schemas/cache/3.1.2/media-buy/provide-performance-feedback-request.json */\n ProvidePerformanceFeedbackRequest: Object.freeze({\n fields: Object.freeze([\"context\",\"creative_id\",\"ext\",\"feedback_source\",\"idempotency_key\",\"measurement_period\",\"media_buy_id\",\"metric_type\",\"package_id\",\"performance_index\"]) as readonly string[],\n __type: null as unknown as ProvidePerformanceFeedbackRequest,\n }),\n /** schemas/cache/3.1.2/governance/report-plan-outcome-request.json */\n ReportPlanOutcomeRequest: Object.freeze({\n fields: Object.freeze([\"check_id\",\"context\",\"delivery\",\"error\",\"ext\",\"governance_context\",\"idempotency_key\",\"outcome\",\"plan_id\",\"purchase_type\",\"seller_response\"]) as readonly string[],\n __type: null as unknown as ReportPlanOutcomeRequest,\n }),\n /** schemas/cache/3.1.2/account/report-usage-request.json */\n ReportUsageRequest: Object.freeze({\n fields: Object.freeze([\"context\",\"ext\",\"idempotency_key\",\"reporting_period\",\"usage\"]) as readonly string[],\n __type: null as unknown as ReportUsageRequest,\n }),\n /** schemas/cache/3.1.2/sponsored-intelligence/si-initiate-session-request.json */\n SIInitiateSessionRequest: Object.freeze({\n fields: Object.freeze([\"context\",\"ext\",\"idempotency_key\",\"identity\",\"intent\",\"media_buy_id\",\"offering_id\",\"offering_token\",\"placement\",\"sponsored_context_receipt\",\"supported_capabilities\"]) as readonly string[],\n __type: null as unknown as SIInitiateSessionRequest,\n }),\n /** schemas/cache/3.1.2/sponsored-intelligence/si-send-message-request.json */\n SISendMessageRequest: Object.freeze({\n fields: Object.freeze([\"action_response\",\"context\",\"ext\",\"idempotency_key\",\"message\",\"session_id\",\"sponsored_context_receipt\"]) as readonly string[],\n __type: null as unknown as SISendMessageRequest,\n }),\n /** schemas/cache/3.1.2/account/sync-accounts-request.json */\n SyncAccountsRequest: Object.freeze({\n fields: Object.freeze([\"accounts\",\"context\",\"delete_missing\",\"dry_run\",\"ext\",\"idempotency_key\",\"push_notification_config\"]) as readonly string[],\n __type: null as unknown as SyncAccountsRequest,\n }),\n /** schemas/cache/3.1.2/media-buy/sync-audiences-request.json */\n SyncAudiencesRequest: Object.freeze({\n fields: Object.freeze([\"account\",\"audiences\",\"context\",\"delete_missing\",\"ext\",\"idempotency_key\"]) as readonly string[],\n __type: null as unknown as SyncAudiencesRequest,\n }),\n /** schemas/cache/3.1.2/media-buy/sync-catalogs-request.json */\n SyncCatalogsRequest: Object.freeze({\n fields: Object.freeze([\"account\",\"catalog_ids\",\"catalogs\",\"context\",\"delete_missing\",\"dry_run\",\"ext\",\"idempotency_key\",\"push_notification_config\",\"validation_mode\"]) as readonly string[],\n __type: null as unknown as SyncCatalogsRequest,\n }),\n /** schemas/cache/3.1.2/creative/sync-creatives-request.json */\n SyncCreativesRequest: Object.freeze({\n fields: Object.freeze([\"account\",\"assignments\",\"context\",\"creative_ids\",\"creatives\",\"delete_missing\",\"dry_run\",\"ext\",\"idempotency_key\",\"push_notification_config\",\"validation_mode\"]) as readonly string[],\n __type: null as unknown as SyncCreativesRequest,\n }),\n /** schemas/cache/3.1.2/media-buy/sync-event-sources-request.json */\n SyncEventSourcesRequest: Object.freeze({\n fields: Object.freeze([\"account\",\"context\",\"delete_missing\",\"event_sources\",\"ext\",\"idempotency_key\"]) as readonly string[],\n __type: null as unknown as SyncEventSourcesRequest,\n }),\n /** schemas/cache/3.1.2/account/sync-governance-request.json */\n SyncGovernanceRequest: Object.freeze({\n fields: Object.freeze([\"accounts\",\"context\",\"ext\",\"idempotency_key\"]) as readonly string[],\n __type: null as unknown as SyncGovernanceRequest,\n }),\n /** schemas/cache/3.1.2/governance/sync-plans-request.json */\n SyncPlansRequest: Object.freeze({\n fields: Object.freeze([\"context\",\"ext\",\"idempotency_key\",\"plans\"]) as readonly string[],\n __type: null as unknown as SyncPlansRequest,\n }),\n /** schemas/cache/3.1.2/collection/update-collection-list-request.json */\n UpdateCollectionListRequest: Object.freeze({\n fields: Object.freeze([\"account\",\"base_collections\",\"brand\",\"context\",\"description\",\"ext\",\"filters\",\"idempotency_key\",\"list_id\",\"name\",\"webhook_url\"]) as readonly string[],\n __type: null as unknown as UpdateCollectionListRequest,\n }),\n /** schemas/cache/3.1.2/content-standards/update-content-standards-request.json */\n UpdateContentStandardsRequest: Object.freeze({\n fields: Object.freeze([\"calibration_exemplars\",\"context\",\"ext\",\"idempotency_key\",\"policies\",\"registry_policy_ids\",\"scope\",\"standards_id\"]) as readonly string[],\n __type: null as unknown as UpdateContentStandardsRequest,\n }),\n /** schemas/cache/3.1.2/media-buy/update-media-buy-request.json */\n UpdateMediaBuyRequest: Object.freeze({\n fields: Object.freeze([\"account\",\"canceled\",\"cancellation_reason\",\"context\",\"end_time\",\"ext\",\"idempotency_key\",\"invoice_recipient\",\"media_buy_id\",\"new_packages\",\"packages\",\"paused\",\"push_notification_config\",\"reporting_webhook\",\"revision\",\"start_time\"]) as readonly string[],\n __type: null as unknown as UpdateMediaBuyRequest,\n }),\n /** schemas/cache/3.1.2/property/update-property-list-request.json */\n UpdatePropertyListRequest: Object.freeze({\n fields: Object.freeze([\"account\",\"base_properties\",\"brand\",\"context\",\"description\",\"ext\",\"filters\",\"idempotency_key\",\"list_id\",\"name\",\"webhook_url\"]) as readonly string[],\n __type: null as unknown as UpdatePropertyListRequest,\n }),\n /** schemas/cache/3.1.2/brand/update-rights-request.json */\n UpdateRightsRequest: Object.freeze({\n fields: Object.freeze([\"account\",\"context\",\"end_date\",\"ext\",\"idempotency_key\",\"impression_cap\",\"paused\",\"pricing_option_id\",\"push_notification_config\",\"rights_id\"]) as readonly string[],\n __type: null as unknown as UpdateRightsRequest,\n }),\n} as const);\n\nexport type WireSpecRequestName = keyof typeof WIRE_SPEC_FIELDS;\n"],"mappings":"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","names":[]}
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import { fileURLToPath as __adcpFileURLToPath } from 'node:url';
|
|
2
|
-
import { dirname as __adcpDirname } from 'node:path';
|
|
3
|
-
import { createRequire as __adcpCreateRequire } from 'node:module';
|
|
4
|
-
const __filename = __adcpFileURLToPath(import.meta.url);
|
|
5
|
-
const __dirname = __adcpDirname(__filename);
|
|
6
|
-
const require = __adcpCreateRequire(import.meta.url);
|
|
7
1
|
import { DEFAULT_ERROR_ENVELOPE_FIELDS, ERROR_ENVELOPE_FIELD_ALLOWLIST } from "./envelope-allowlist.mjs";
|
|
8
2
|
import { applyAdcpErrorAllowlist } from "./errors.mjs";
|
|
9
3
|
function detectErrorCode(inner, override) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/lib/server/wrap-envelope.ts"],"sourcesContent":["/**\n * Public envelope helper for AdCP handler responses.\n *\n * `wrapEnvelope` attaches protocol envelope fields (`replayed`, `context`,\n * `operation_id`) to a handler's inner response object. Sellers that wire\n * their own MCP or A2A handlers — without going through `createAdcpServer` —\n * can use this to produce responses that round-trip with the framework's\n * replay, context echo, and async-operation semantics.\n *\n * Error responses (detected via `inner.adcp_error?.code`) are subject to a\n * per-code field allowlist (`ERROR_ENVELOPE_FIELD_ALLOWLIST` in\n * `./envelope-allowlist.ts`). `IDEMPOTENCY_CONFLICT` ships with an\n * allowlist that permits `context` and `operation_id` and deliberately\n * drops `replayed` — a conflict isn't a replay, and advertising\n * `replayed:true` on an error that was NOT served from cache would\n * mislead the caller.\n *\n * Unregistered error codes fail-closed to `DEFAULT_ERROR_ENVELOPE_FIELDS`\n * (`context` only). Sellers that want `replayed` or `operation_id` on a\n * bespoke error code must register it explicitly.\n *\n * The helper shallow-clones the input and never mutates it.\n */\n\nimport { DEFAULT_ERROR_ENVELOPE_FIELDS, ERROR_ENVELOPE_FIELD_ALLOWLIST } from './envelope-allowlist';\nimport { applyAdcpErrorAllowlist } from './errors';\n\n/**\n * Options accepted by {@link wrapEnvelope}.\n *\n * Every field is optional. When omitted, the corresponding envelope key is\n * NOT attached (as opposed to emitted with `undefined`). When `replayed`\n * is passed explicitly — including `replayed: false` — the value is\n * attached as-is, so sellers that want to emit an explicit marker on fresh\n * execution can. The framework's own replay bookkeeping stamps `true` on\n * replay and omits the field on fresh (envelope spec permits both).\n */\nexport interface WrapEnvelopeOptions {\n /**\n * Replay marker. Attached as-is when explicitly set. `protocol-envelope.json`\n * permits the field to be \"omitted when the request was executed fresh\",\n * so the framework's internal idempotency path omits it on fresh and\n * stamps `true` on replay. Sellers calling this helper directly may\n * pass `false` explicitly if they want the marker in-band. Dropped on\n * error codes whose allowlist excludes it (e.g. `IDEMPOTENCY_CONFLICT`).\n *\n * The asymmetry between the framework path (omits on fresh) and\n * wrapEnvelope callers (round-trip explicit `false`) is intentional:\n * the framework optimizes for spec-clean payloads, while wrapEnvelope\n * respects the caller's explicit intent. Don't \"fix\" this by collapsing\n * `false` to absent.\n */\n replayed?: boolean;\n\n /**\n * Echo-back context block. `context` must be a plain object. String,\n * `null`, array, and other non-object values are silently dropped to\n * match the framework's `injectContextIntoResponse` behavior — SI tools\n * (`si_get_offering`, `si_initiate_session`) legitimately override the\n * request `context` to a domain-specific string that must NOT echo into\n * the response envelope, because the response schema requires the\n * protocol echo object there. Pass an object explicitly if you want the\n * field to appear on the envelope.\n *\n * Subject to the per-error-code allowlist when `inner` carries an\n * `adcp_error.code`. Every shipped allowlist includes `context`.\n */\n context?: unknown;\n\n /**\n * Async operation identifier. Emitted on the envelope as snake_case\n * `operation_id` to match the AdCP field-naming convention.\n */\n operationId?: string;\n\n /**\n * Override the error-code lookup. When omitted, the helper reads\n * `inner.adcp_error?.code`. Pass this when the inner object isn't\n * shaped like an AdCP error envelope but you still want error-code\n * semantics (for example, when composing with a custom A2A error\n * representation).\n */\n errorCode?: string;\n}\n\ninterface AdcpErrorLike {\n adcp_error?: { code?: unknown };\n}\n\nfunction detectErrorCode(inner: object, override?: string): string | undefined {\n if (override != null) return override;\n const code = (inner as AdcpErrorLike).adcp_error?.code;\n return typeof code === 'string' ? code : undefined;\n}\n\nfunction isEchoableContext(value: unknown): boolean {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\n/**\n * Wrap an inner AdCP response object with envelope fields. Returns a\n * shallow clone — the original `inner` is never mutated.\n *\n * @param inner Handler response. For success this is the structured\n * response payload (e.g. `{ media_buy_id, status }`). For error this\n * is the AdCP error envelope (`{ adcp_error: { code, message, ... } }`).\n * @param opts Envelope fields to attach. See {@link WrapEnvelopeOptions}.\n *\n * @example Seller handler — success + IDEMPOTENCY_CONFLICT error paths\n * ```ts\n * import { wrapEnvelope } from '@adcp/sdk/server';\n *\n * async function handleCreateMediaBuy(request) {\n * try {\n * const inner = await buyService.create(request.params);\n * // Fresh-path success: omit `replayed` — envelope spec reads absence\n * // as fresh execution. Echo request.context for correlation tracing.\n * return wrapEnvelope(inner, {\n * context: request.context,\n * operationId: inner.operation_id,\n * });\n * } catch (err) {\n * if (err.code === 'IDEMPOTENCY_CONFLICT') {\n * // Conflict is NOT a replay — replayed is dropped by the allowlist,\n * // but context still echoes for correlation tracing.\n * return wrapEnvelope(\n * { adcp_error: { code: 'IDEMPOTENCY_CONFLICT', message: err.message, recovery: 'correctable' } },\n * { context: request.context }\n * );\n * }\n * throw err;\n * }\n * }\n * ```\n *\n * @example Success path (fresh execution, no explicit replayed)\n * ```ts\n * const response = wrapEnvelope(\n * { media_buy_id: 'mb_1', status: 'active' },\n * { context: { correlation_id: 'abc' }, operationId: 'op_123' }\n * );\n * // => { media_buy_id, status, context: {...}, operation_id: 'op_123' }\n * ```\n *\n * @example Replay path (stamp true)\n * ```ts\n * const response = wrapEnvelope(\n * { media_buy_id: 'mb_1', status: 'active' },\n * { replayed: true, context: { correlation_id: 'abc' }, operationId: 'op_123' }\n * );\n * // => { media_buy_id, status, replayed: true, context: {...}, operation_id: 'op_123' }\n * ```\n *\n * @example Conflict error — `replayed` is dropped, `context` is echoed\n * ```ts\n * const response = wrapEnvelope(\n * { adcp_error: { code: 'IDEMPOTENCY_CONFLICT', message: '...', recovery: 'correctable' } },\n * { replayed: true, context: { correlation_id: 'abc' }, operationId: 'op_123' }\n * );\n * // `replayed: true` is intentionally dropped — IDEMPOTENCY_CONFLICT's\n * // per-code allowlist excludes it (a conflict is not a cached replay).\n * // => { adcp_error: {...}, context: {...}, operation_id: 'op_123' }\n * ```\n *\n * @example Bespoke error code — fail-closed default\n * ```ts\n * const response = wrapEnvelope(\n * { adcp_error: { code: 'MY_CUSTOM_ERROR', message: '...', recovery: 'terminal' } },\n * { replayed: false, context: { correlation_id: 'abc' }, operationId: 'op_123' }\n * );\n * // An unregistered error code inherits DEFAULT_ERROR_ENVELOPE_FIELDS\n * // ({ context } only). `replayed` and `operation_id` are silently\n * // dropped. Sellers that need those fields on bespoke codes should\n * // build the envelope directly rather than calling wrapEnvelope, or\n * // open an issue to register the code in ERROR_ENVELOPE_FIELD_ALLOWLIST.\n * // => { adcp_error: {...}, context: {...} }\n * ```\n */\nexport function wrapEnvelope<T extends object>(\n inner: T,\n opts: WrapEnvelopeOptions\n): T & { replayed?: boolean; context?: object; operation_id?: string } {\n const clone: Record<string, unknown> = {\n ...(inner as Record<string, unknown>),\n };\n\n const errorCode = detectErrorCode(inner, opts.errorCode);\n const adcpError = clone.adcp_error;\n if (errorCode != null && adcpError != null && typeof adcpError === 'object' && !Array.isArray(adcpError)) {\n clone.adcp_error = applyAdcpErrorAllowlist(errorCode, adcpError as Record<string, unknown>);\n }\n\n // Error responses: registered codes use their explicit allowlist;\n // unregistered codes fail closed to DEFAULT_ERROR_ENVELOPE_FIELDS\n // (context only). Success responses (no error code) allow all fields.\n const allowlist =\n errorCode != null ? (ERROR_ENVELOPE_FIELD_ALLOWLIST[errorCode] ?? DEFAULT_ERROR_ENVELOPE_FIELDS) : undefined;\n\n const fieldAllowed = (field: string): boolean => {\n if (allowlist == null) return true;\n return allowlist.has(field);\n };\n\n if ('replayed' in opts && fieldAllowed('replayed')) {\n clone.replayed = opts.replayed;\n }\n\n // Context parity with `injectContextIntoResponse`: only attach when the\n // inner payload doesn't already carry a context the handler placed\n // itself. And honor the per-error-code allowlist — `context` is\n // listed explicitly in every shipped entry.\n if ('context' in opts && isEchoableContext(opts.context) && !('context' in clone) && fieldAllowed('context')) {\n clone.context = opts.context;\n }\n\n if (opts.operationId != null && fieldAllowed('operation_id')) {\n clone.operation_id = opts.operationId;\n }\n\n return clone as T & {\n replayed?: boolean;\n context?: object;\n operation_id?: string;\n };\n}\n"],"mappings":";;;;;;AAwBA,SAAS,+BAA+B,sCAAsC;AAC9E,SAAS,+BAA+B;AAgExC,SAAS,gBAAgB,OAAe,UAAuC;AAC7E,MAAI,YAAY,KAAM,QAAO;AAC7B,QAAM,OAAQ,MAAwB,YAAY;AAClD,SAAO,OAAO,SAAS,WAAW,OAAO;AAC3C;AAEA,SAAS,kBAAkB,OAAyB;AAClD,SAAO,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAiFO,SAAS,aACd,OACA,MACqE;AACrE,QAAM,QAAiC;AAAA,IACrC,GAAI;AAAA,EACN;AAEA,QAAM,YAAY,gBAAgB,OAAO,KAAK,SAAS;AACvD,QAAM,YAAY,MAAM;AACxB,MAAI,aAAa,QAAQ,aAAa,QAAQ,OAAO,cAAc,YAAY,CAAC,MAAM,QAAQ,SAAS,GAAG;AACxG,UAAM,aAAa,wBAAwB,WAAW,SAAoC;AAAA,EAC5F;AAKA,QAAM,YACJ,aAAa,OAAQ,+BAA+B,SAAS,KAAK,gCAAiC;AAErG,QAAM,eAAe,CAAC,UAA2B;AAC/C,QAAI,aAAa,KAAM,QAAO;AAC9B,WAAO,UAAU,IAAI,KAAK;AAAA,EAC5B;AAEA,MAAI,cAAc,QAAQ,aAAa,UAAU,GAAG;AAClD,UAAM,WAAW,KAAK;AAAA,EACxB;AAMA,MAAI,aAAa,QAAQ,kBAAkB,KAAK,OAAO,KAAK,EAAE,aAAa,UAAU,aAAa,SAAS,GAAG;AAC5G,UAAM,UAAU,KAAK;AAAA,EACvB;AAEA,MAAI,KAAK,eAAe,QAAQ,aAAa,cAAc,GAAG;AAC5D,UAAM,eAAe,KAAK;AAAA,EAC5B;AAEA,SAAO;AAKT;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/server/wrap-envelope.ts"],"sourcesContent":["/**\n * Public envelope helper for AdCP handler responses.\n *\n * `wrapEnvelope` attaches protocol envelope fields (`replayed`, `context`,\n * `operation_id`) to a handler's inner response object. Sellers that wire\n * their own MCP or A2A handlers — without going through `createAdcpServer` —\n * can use this to produce responses that round-trip with the framework's\n * replay, context echo, and async-operation semantics.\n *\n * Error responses (detected via `inner.adcp_error?.code`) are subject to a\n * per-code field allowlist (`ERROR_ENVELOPE_FIELD_ALLOWLIST` in\n * `./envelope-allowlist.ts`). `IDEMPOTENCY_CONFLICT` ships with an\n * allowlist that permits `context` and `operation_id` and deliberately\n * drops `replayed` — a conflict isn't a replay, and advertising\n * `replayed:true` on an error that was NOT served from cache would\n * mislead the caller.\n *\n * Unregistered error codes fail-closed to `DEFAULT_ERROR_ENVELOPE_FIELDS`\n * (`context` only). Sellers that want `replayed` or `operation_id` on a\n * bespoke error code must register it explicitly.\n *\n * The helper shallow-clones the input and never mutates it.\n */\n\nimport { DEFAULT_ERROR_ENVELOPE_FIELDS, ERROR_ENVELOPE_FIELD_ALLOWLIST } from './envelope-allowlist';\nimport { applyAdcpErrorAllowlist } from './errors';\n\n/**\n * Options accepted by {@link wrapEnvelope}.\n *\n * Every field is optional. When omitted, the corresponding envelope key is\n * NOT attached (as opposed to emitted with `undefined`). When `replayed`\n * is passed explicitly — including `replayed: false` — the value is\n * attached as-is, so sellers that want to emit an explicit marker on fresh\n * execution can. The framework's own replay bookkeeping stamps `true` on\n * replay and omits the field on fresh (envelope spec permits both).\n */\nexport interface WrapEnvelopeOptions {\n /**\n * Replay marker. Attached as-is when explicitly set. `protocol-envelope.json`\n * permits the field to be \"omitted when the request was executed fresh\",\n * so the framework's internal idempotency path omits it on fresh and\n * stamps `true` on replay. Sellers calling this helper directly may\n * pass `false` explicitly if they want the marker in-band. Dropped on\n * error codes whose allowlist excludes it (e.g. `IDEMPOTENCY_CONFLICT`).\n *\n * The asymmetry between the framework path (omits on fresh) and\n * wrapEnvelope callers (round-trip explicit `false`) is intentional:\n * the framework optimizes for spec-clean payloads, while wrapEnvelope\n * respects the caller's explicit intent. Don't \"fix\" this by collapsing\n * `false` to absent.\n */\n replayed?: boolean;\n\n /**\n * Echo-back context block. `context` must be a plain object. String,\n * `null`, array, and other non-object values are silently dropped to\n * match the framework's `injectContextIntoResponse` behavior — SI tools\n * (`si_get_offering`, `si_initiate_session`) legitimately override the\n * request `context` to a domain-specific string that must NOT echo into\n * the response envelope, because the response schema requires the\n * protocol echo object there. Pass an object explicitly if you want the\n * field to appear on the envelope.\n *\n * Subject to the per-error-code allowlist when `inner` carries an\n * `adcp_error.code`. Every shipped allowlist includes `context`.\n */\n context?: unknown;\n\n /**\n * Async operation identifier. Emitted on the envelope as snake_case\n * `operation_id` to match the AdCP field-naming convention.\n */\n operationId?: string;\n\n /**\n * Override the error-code lookup. When omitted, the helper reads\n * `inner.adcp_error?.code`. Pass this when the inner object isn't\n * shaped like an AdCP error envelope but you still want error-code\n * semantics (for example, when composing with a custom A2A error\n * representation).\n */\n errorCode?: string;\n}\n\ninterface AdcpErrorLike {\n adcp_error?: { code?: unknown };\n}\n\nfunction detectErrorCode(inner: object, override?: string): string | undefined {\n if (override != null) return override;\n const code = (inner as AdcpErrorLike).adcp_error?.code;\n return typeof code === 'string' ? code : undefined;\n}\n\nfunction isEchoableContext(value: unknown): boolean {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\n/**\n * Wrap an inner AdCP response object with envelope fields. Returns a\n * shallow clone — the original `inner` is never mutated.\n *\n * @param inner Handler response. For success this is the structured\n * response payload (e.g. `{ media_buy_id, status }`). For error this\n * is the AdCP error envelope (`{ adcp_error: { code, message, ... } }`).\n * @param opts Envelope fields to attach. See {@link WrapEnvelopeOptions}.\n *\n * @example Seller handler — success + IDEMPOTENCY_CONFLICT error paths\n * ```ts\n * import { wrapEnvelope } from '@adcp/sdk/server';\n *\n * async function handleCreateMediaBuy(request) {\n * try {\n * const inner = await buyService.create(request.params);\n * // Fresh-path success: omit `replayed` — envelope spec reads absence\n * // as fresh execution. Echo request.context for correlation tracing.\n * return wrapEnvelope(inner, {\n * context: request.context,\n * operationId: inner.operation_id,\n * });\n * } catch (err) {\n * if (err.code === 'IDEMPOTENCY_CONFLICT') {\n * // Conflict is NOT a replay — replayed is dropped by the allowlist,\n * // but context still echoes for correlation tracing.\n * return wrapEnvelope(\n * { adcp_error: { code: 'IDEMPOTENCY_CONFLICT', message: err.message, recovery: 'correctable' } },\n * { context: request.context }\n * );\n * }\n * throw err;\n * }\n * }\n * ```\n *\n * @example Success path (fresh execution, no explicit replayed)\n * ```ts\n * const response = wrapEnvelope(\n * { media_buy_id: 'mb_1', status: 'active' },\n * { context: { correlation_id: 'abc' }, operationId: 'op_123' }\n * );\n * // => { media_buy_id, status, context: {...}, operation_id: 'op_123' }\n * ```\n *\n * @example Replay path (stamp true)\n * ```ts\n * const response = wrapEnvelope(\n * { media_buy_id: 'mb_1', status: 'active' },\n * { replayed: true, context: { correlation_id: 'abc' }, operationId: 'op_123' }\n * );\n * // => { media_buy_id, status, replayed: true, context: {...}, operation_id: 'op_123' }\n * ```\n *\n * @example Conflict error — `replayed` is dropped, `context` is echoed\n * ```ts\n * const response = wrapEnvelope(\n * { adcp_error: { code: 'IDEMPOTENCY_CONFLICT', message: '...', recovery: 'correctable' } },\n * { replayed: true, context: { correlation_id: 'abc' }, operationId: 'op_123' }\n * );\n * // `replayed: true` is intentionally dropped — IDEMPOTENCY_CONFLICT's\n * // per-code allowlist excludes it (a conflict is not a cached replay).\n * // => { adcp_error: {...}, context: {...}, operation_id: 'op_123' }\n * ```\n *\n * @example Bespoke error code — fail-closed default\n * ```ts\n * const response = wrapEnvelope(\n * { adcp_error: { code: 'MY_CUSTOM_ERROR', message: '...', recovery: 'terminal' } },\n * { replayed: false, context: { correlation_id: 'abc' }, operationId: 'op_123' }\n * );\n * // An unregistered error code inherits DEFAULT_ERROR_ENVELOPE_FIELDS\n * // ({ context } only). `replayed` and `operation_id` are silently\n * // dropped. Sellers that need those fields on bespoke codes should\n * // build the envelope directly rather than calling wrapEnvelope, or\n * // open an issue to register the code in ERROR_ENVELOPE_FIELD_ALLOWLIST.\n * // => { adcp_error: {...}, context: {...} }\n * ```\n */\nexport function wrapEnvelope<T extends object>(\n inner: T,\n opts: WrapEnvelopeOptions\n): T & { replayed?: boolean; context?: object; operation_id?: string } {\n const clone: Record<string, unknown> = {\n ...(inner as Record<string, unknown>),\n };\n\n const errorCode = detectErrorCode(inner, opts.errorCode);\n const adcpError = clone.adcp_error;\n if (errorCode != null && adcpError != null && typeof adcpError === 'object' && !Array.isArray(adcpError)) {\n clone.adcp_error = applyAdcpErrorAllowlist(errorCode, adcpError as Record<string, unknown>);\n }\n\n // Error responses: registered codes use their explicit allowlist;\n // unregistered codes fail closed to DEFAULT_ERROR_ENVELOPE_FIELDS\n // (context only). Success responses (no error code) allow all fields.\n const allowlist =\n errorCode != null ? (ERROR_ENVELOPE_FIELD_ALLOWLIST[errorCode] ?? DEFAULT_ERROR_ENVELOPE_FIELDS) : undefined;\n\n const fieldAllowed = (field: string): boolean => {\n if (allowlist == null) return true;\n return allowlist.has(field);\n };\n\n if ('replayed' in opts && fieldAllowed('replayed')) {\n clone.replayed = opts.replayed;\n }\n\n // Context parity with `injectContextIntoResponse`: only attach when the\n // inner payload doesn't already carry a context the handler placed\n // itself. And honor the per-error-code allowlist — `context` is\n // listed explicitly in every shipped entry.\n if ('context' in opts && isEchoableContext(opts.context) && !('context' in clone) && fieldAllowed('context')) {\n clone.context = opts.context;\n }\n\n if (opts.operationId != null && fieldAllowed('operation_id')) {\n clone.operation_id = opts.operationId;\n }\n\n return clone as T & {\n replayed?: boolean;\n context?: object;\n operation_id?: string;\n };\n}\n"],"mappings":"AAwBA,SAAS,+BAA+B,sCAAsC;AAC9E,SAAS,+BAA+B;AAgExC,SAAS,gBAAgB,OAAe,UAAuC;AAC7E,MAAI,YAAY,KAAM,QAAO;AAC7B,QAAM,OAAQ,MAAwB,YAAY;AAClD,SAAO,OAAO,SAAS,WAAW,OAAO;AAC3C;AAEA,SAAS,kBAAkB,OAAyB;AAClD,SAAO,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAiFO,SAAS,aACd,OACA,MACqE;AACrE,QAAM,QAAiC;AAAA,IACrC,GAAI;AAAA,EACN;AAEA,QAAM,YAAY,gBAAgB,OAAO,KAAK,SAAS;AACvD,QAAM,YAAY,MAAM;AACxB,MAAI,aAAa,QAAQ,aAAa,QAAQ,OAAO,cAAc,YAAY,CAAC,MAAM,QAAQ,SAAS,GAAG;AACxG,UAAM,aAAa,wBAAwB,WAAW,SAAoC;AAAA,EAC5F;AAKA,QAAM,YACJ,aAAa,OAAQ,+BAA+B,SAAS,KAAK,gCAAiC;AAErG,QAAM,eAAe,CAAC,UAA2B;AAC/C,QAAI,aAAa,KAAM,QAAO;AAC9B,WAAO,UAAU,IAAI,KAAK;AAAA,EAC5B;AAEA,MAAI,cAAc,QAAQ,aAAa,UAAU,GAAG;AAClD,UAAM,WAAW,KAAK;AAAA,EACxB;AAMA,MAAI,aAAa,QAAQ,kBAAkB,KAAK,OAAO,KAAK,EAAE,aAAa,UAAU,aAAa,SAAS,GAAG;AAC5G,UAAM,UAAU,KAAK;AAAA,EACvB;AAEA,MAAI,KAAK,eAAe,QAAQ,aAAa,cAAc,GAAG;AAC5D,UAAM,eAAe,KAAK;AAAA,EAC5B;AAEA,SAAO;AAKT;","names":[]}
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import { fileURLToPath as __adcpFileURLToPath } from 'node:url';
|
|
2
|
-
import { dirname as __adcpDirname } from 'node:path';
|
|
3
|
-
import { createRequire as __adcpCreateRequire } from 'node:module';
|
|
4
|
-
const __filename = __adcpFileURLToPath(import.meta.url);
|
|
5
|
-
const __dirname = __adcpDirname(__filename);
|
|
6
|
-
const require = __adcpCreateRequire(import.meta.url);
|
|
7
1
|
import { createHash } from "node:crypto";
|
|
8
2
|
import { globalAsyncLocalStorage } from "../utils/global-async-local-storage.mjs";
|
|
9
3
|
import {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/lib/signing/agent-context.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport { globalAsyncLocalStorage } from '../utils/global-async-local-storage';\nimport type { AgentConfig, AgentRequestSigningConfig } from '../types/adcp';\nimport {\n buildCapabilityCacheKey,\n CapabilityCache,\n defaultCapabilityCache,\n type CachedCapability,\n} from './capability-cache';\nimport type { SigningProvider } from './provider';\nimport type { AdcpSignAlg } from './types';\n\n/**\n * Snapshot of the signing identity captured at context-build time. The\n * transport layer reads from this snapshot rather than from `provider.*`\n * directly so a provider object whose fields drift between context build\n * and outbound request can't desynchronize the on-wire `keyid` from the\n * cache key the connection was bound to. TypeScript's `readonly` modifier\n * is compile-time only; the snapshot is the runtime defense.\n */\nexport interface AgentSigningIdentitySnapshot {\n keyid: string;\n algorithm: AdcpSignAlg;\n /** Defensively hashed disambiguator — see {@link buildAgentSigningContext}. */\n fingerprint: string;\n}\n\n/**\n * Per-call signing context passed down to the MCP/A2A transport layer. Built\n * at `ProtocolClient.callTool` and consumed by the signing fetch wrapper\n * attached to the transport. Opaque to the transport helpers — they only\n * use `cacheKey` (to disambiguate connection-cache entries per signing\n * identity), `getCapability` (to read the cached seller advertisement on\n * each outbound request), and `signing` (to produce the signer key or\n * provider).\n */\nexport interface AgentSigningContext {\n /** Signing config copied from AgentConfig.request_signing. */\n signing: AgentRequestSigningConfig;\n /** Provider when `signing.kind === 'provider'`; undefined for inline keys. */\n provider?: SigningProvider;\n /** Snapshot of identity fields read from the provider at build time. */\n identity: AgentSigningIdentitySnapshot;\n /** Suffix to append to transport connection-cache keys so agents with different signing identities don't share a connection. */\n cacheKey: string;\n /** Lazy accessor for the currently cached capability for this agent. */\n getCapability: () => CachedCapability | undefined;\n /** Capability cache backing this context (for external invalidation). */\n cache: CapabilityCache;\n /** Stable cache key used against the capability cache itself. */\n capabilityCacheKey: string;\n /**\n * Evict this context's capability entry so the next outbound call\n * re-primes `get_adcp_capabilities`. Use after a seller-side rotation\n * signal — or on a 401 / protocol-signature error — without having to\n * rebuild the cache key from the agent's identifying fields.\n */\n invalidate(): void;\n}\n\n/**\n * AsyncLocalStorage carrying the signing context across the transport layer.\n * Top-level protocol entries (`callMCPTool`, `callA2ATool`, etc.) push a\n * context onto this storage for the duration of the call; internal helpers\n * (`withCachedConnection`, `connectMCPWithFallback`, `buildFetchImpl`) read\n * it when building cache keys and signing-fetch wrappers, avoiding the need\n * to thread `signingContext` through every intermediate signature.\n *\n * The top-level entries always call `run()` — including with `undefined` —\n * so a non-signing call cannot inherit a stale context from an enclosing\n * scope.\n */\nexport const signingContextStorage = globalAsyncLocalStorage<AgentSigningContext | undefined>('signingContext');\n\n/**\n * Build an `AgentSigningContext` from an `AgentConfig` when signing is\n * configured. Returns `undefined` when the agent has no `request_signing`\n * block — callers use this to branch into the no-op fast path.\n */\nexport function buildAgentSigningContext(\n agent: AgentConfig,\n options: { cache?: CapabilityCache } = {}\n): AgentSigningContext | undefined {\n const signing = agent.request_signing;\n if (!signing) return undefined;\n\n const cache = options.cache ?? defaultCapabilityCache;\n const identity = snapshotIdentity(signing);\n const cacheFingerprint = deriveCacheFingerprint(identity);\n const capabilityCacheKey = buildCapabilityCacheKey(agent.agent_uri, agent.auth_token, cacheFingerprint);\n // Transport-connection cache-key suffix binds to the defensively hashed\n // identity, not just the advertised `kid`. Two tenants that misconfigure\n // the same `kid` string but hold distinct keys must not collide on a\n // shared cached transport — that would sign one tenant's outbound\n // requests with the other tenant's key (same `kid`, different material),\n // an impersonation. The hash includes `algorithm` so an Ed25519/P-256\n // swap on the same `kid+fingerprint` doesn't alias either.\n const cacheKey = `sig=${cacheFingerprint}`;\n const provider = signing.kind === 'provider' ? signing.provider : undefined;\n\n return {\n signing,\n provider,\n identity,\n cacheKey,\n cache,\n capabilityCacheKey,\n getCapability: () => cache.get(capabilityCacheKey),\n invalidate: () => cache.invalidate(capabilityCacheKey),\n };\n}\n\n/**\n * Snapshot the signing identity at context-build time. For provider configs,\n * reads `keyid` / `algorithm` / `fingerprint` once and freezes them. For\n * inline configs, derives a fingerprint from the private scalar — same\n * 64-bit cache disambiguator the SDK has always used.\n */\nfunction snapshotIdentity(signing: AgentRequestSigningConfig): AgentSigningIdentitySnapshot {\n if (signing.kind === 'provider') {\n const provider = signing.provider;\n return {\n keyid: provider.keyid,\n algorithm: provider.algorithm,\n fingerprint: provider.fingerprint,\n };\n }\n return {\n keyid: signing.kid,\n algorithm: signing.alg,\n fingerprint: inlineFingerprint(signing.kid, signing.private_key.d),\n };\n}\n\n/**\n * Defensively hash the snapshot before composing cache keys. A\n * provider-supplied `fingerprint` is treated as untrusted input — a\n * confused integrator could return a constant, an empty string, or a\n * tenant-controlled value, any of which would re-enable cross-tenant\n * collisions on the same `kid`. Including `algorithm` in the digest\n * prevents an Ed25519/P-256 swap on identical `kid+fingerprint` from\n * aliasing.\n *\n * Truncated to 16 hex chars — a 64-bit collision-resistance budget against\n * random keys is plenty for a cache disambiguator, and we never rely on\n * this value as a security boundary.\n */\nfunction deriveCacheFingerprint(identity: AgentSigningIdentitySnapshot): string {\n return createHash('sha256')\n .update(identity.algorithm)\n .update('\\0')\n .update(identity.keyid)\n .update('\\0')\n .update(identity.fingerprint)\n .digest('hex')\n .slice(0, 16);\n}\n\nfunction inlineFingerprint(kid: string, d: string | undefined): string {\n if (!d) {\n throw new TypeError(\n `AgentRequestSigningConfig (kind: 'inline', kid='${kid}') is missing 'private_key.d' — JWK must include the private scalar.`\n );\n }\n return createHash('sha256').update(kid).update('\\0').update(d).digest('hex').slice(0, 16);\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/signing/agent-context.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport { globalAsyncLocalStorage } from '../utils/global-async-local-storage';\nimport type { AgentConfig, AgentRequestSigningConfig } from '../types/adcp';\nimport {\n buildCapabilityCacheKey,\n CapabilityCache,\n defaultCapabilityCache,\n type CachedCapability,\n} from './capability-cache';\nimport type { SigningProvider } from './provider';\nimport type { AdcpSignAlg } from './types';\n\n/**\n * Snapshot of the signing identity captured at context-build time. The\n * transport layer reads from this snapshot rather than from `provider.*`\n * directly so a provider object whose fields drift between context build\n * and outbound request can't desynchronize the on-wire `keyid` from the\n * cache key the connection was bound to. TypeScript's `readonly` modifier\n * is compile-time only; the snapshot is the runtime defense.\n */\nexport interface AgentSigningIdentitySnapshot {\n keyid: string;\n algorithm: AdcpSignAlg;\n /** Defensively hashed disambiguator — see {@link buildAgentSigningContext}. */\n fingerprint: string;\n}\n\n/**\n * Per-call signing context passed down to the MCP/A2A transport layer. Built\n * at `ProtocolClient.callTool` and consumed by the signing fetch wrapper\n * attached to the transport. Opaque to the transport helpers — they only\n * use `cacheKey` (to disambiguate connection-cache entries per signing\n * identity), `getCapability` (to read the cached seller advertisement on\n * each outbound request), and `signing` (to produce the signer key or\n * provider).\n */\nexport interface AgentSigningContext {\n /** Signing config copied from AgentConfig.request_signing. */\n signing: AgentRequestSigningConfig;\n /** Provider when `signing.kind === 'provider'`; undefined for inline keys. */\n provider?: SigningProvider;\n /** Snapshot of identity fields read from the provider at build time. */\n identity: AgentSigningIdentitySnapshot;\n /** Suffix to append to transport connection-cache keys so agents with different signing identities don't share a connection. */\n cacheKey: string;\n /** Lazy accessor for the currently cached capability for this agent. */\n getCapability: () => CachedCapability | undefined;\n /** Capability cache backing this context (for external invalidation). */\n cache: CapabilityCache;\n /** Stable cache key used against the capability cache itself. */\n capabilityCacheKey: string;\n /**\n * Evict this context's capability entry so the next outbound call\n * re-primes `get_adcp_capabilities`. Use after a seller-side rotation\n * signal — or on a 401 / protocol-signature error — without having to\n * rebuild the cache key from the agent's identifying fields.\n */\n invalidate(): void;\n}\n\n/**\n * AsyncLocalStorage carrying the signing context across the transport layer.\n * Top-level protocol entries (`callMCPTool`, `callA2ATool`, etc.) push a\n * context onto this storage for the duration of the call; internal helpers\n * (`withCachedConnection`, `connectMCPWithFallback`, `buildFetchImpl`) read\n * it when building cache keys and signing-fetch wrappers, avoiding the need\n * to thread `signingContext` through every intermediate signature.\n *\n * The top-level entries always call `run()` — including with `undefined` —\n * so a non-signing call cannot inherit a stale context from an enclosing\n * scope.\n */\nexport const signingContextStorage = globalAsyncLocalStorage<AgentSigningContext | undefined>('signingContext');\n\n/**\n * Build an `AgentSigningContext` from an `AgentConfig` when signing is\n * configured. Returns `undefined` when the agent has no `request_signing`\n * block — callers use this to branch into the no-op fast path.\n */\nexport function buildAgentSigningContext(\n agent: AgentConfig,\n options: { cache?: CapabilityCache } = {}\n): AgentSigningContext | undefined {\n const signing = agent.request_signing;\n if (!signing) return undefined;\n\n const cache = options.cache ?? defaultCapabilityCache;\n const identity = snapshotIdentity(signing);\n const cacheFingerprint = deriveCacheFingerprint(identity);\n const capabilityCacheKey = buildCapabilityCacheKey(agent.agent_uri, agent.auth_token, cacheFingerprint);\n // Transport-connection cache-key suffix binds to the defensively hashed\n // identity, not just the advertised `kid`. Two tenants that misconfigure\n // the same `kid` string but hold distinct keys must not collide on a\n // shared cached transport — that would sign one tenant's outbound\n // requests with the other tenant's key (same `kid`, different material),\n // an impersonation. The hash includes `algorithm` so an Ed25519/P-256\n // swap on the same `kid+fingerprint` doesn't alias either.\n const cacheKey = `sig=${cacheFingerprint}`;\n const provider = signing.kind === 'provider' ? signing.provider : undefined;\n\n return {\n signing,\n provider,\n identity,\n cacheKey,\n cache,\n capabilityCacheKey,\n getCapability: () => cache.get(capabilityCacheKey),\n invalidate: () => cache.invalidate(capabilityCacheKey),\n };\n}\n\n/**\n * Snapshot the signing identity at context-build time. For provider configs,\n * reads `keyid` / `algorithm` / `fingerprint` once and freezes them. For\n * inline configs, derives a fingerprint from the private scalar — same\n * 64-bit cache disambiguator the SDK has always used.\n */\nfunction snapshotIdentity(signing: AgentRequestSigningConfig): AgentSigningIdentitySnapshot {\n if (signing.kind === 'provider') {\n const provider = signing.provider;\n return {\n keyid: provider.keyid,\n algorithm: provider.algorithm,\n fingerprint: provider.fingerprint,\n };\n }\n return {\n keyid: signing.kid,\n algorithm: signing.alg,\n fingerprint: inlineFingerprint(signing.kid, signing.private_key.d),\n };\n}\n\n/**\n * Defensively hash the snapshot before composing cache keys. A\n * provider-supplied `fingerprint` is treated as untrusted input — a\n * confused integrator could return a constant, an empty string, or a\n * tenant-controlled value, any of which would re-enable cross-tenant\n * collisions on the same `kid`. Including `algorithm` in the digest\n * prevents an Ed25519/P-256 swap on identical `kid+fingerprint` from\n * aliasing.\n *\n * Truncated to 16 hex chars — a 64-bit collision-resistance budget against\n * random keys is plenty for a cache disambiguator, and we never rely on\n * this value as a security boundary.\n */\nfunction deriveCacheFingerprint(identity: AgentSigningIdentitySnapshot): string {\n return createHash('sha256')\n .update(identity.algorithm)\n .update('\\0')\n .update(identity.keyid)\n .update('\\0')\n .update(identity.fingerprint)\n .digest('hex')\n .slice(0, 16);\n}\n\nfunction inlineFingerprint(kid: string, d: string | undefined): string {\n if (!d) {\n throw new TypeError(\n `AgentRequestSigningConfig (kind: 'inline', kid='${kid}') is missing 'private_key.d' — JWK must include the private scalar.`\n );\n }\n return createHash('sha256').update(kid).update('\\0').update(d).digest('hex').slice(0, 16);\n}\n"],"mappings":"AAAA,SAAS,kBAAkB;AAC3B,SAAS,+BAA+B;AAExC;AAAA,EACE;AAAA,EAEA;AAAA,OAEK;AAgEA,MAAM,wBAAwB,wBAAyD,gBAAgB;AAOvG,SAAS,yBACd,OACA,UAAuC,CAAC,GACP;AACjC,QAAM,UAAU,MAAM;AACtB,MAAI,CAAC,QAAS,QAAO;AAErB,QAAM,QAAQ,QAAQ,SAAS;AAC/B,QAAM,WAAW,iBAAiB,OAAO;AACzC,QAAM,mBAAmB,uBAAuB,QAAQ;AACxD,QAAM,qBAAqB,wBAAwB,MAAM,WAAW,MAAM,YAAY,gBAAgB;AAQtG,QAAM,WAAW,OAAO,gBAAgB;AACxC,QAAM,WAAW,QAAQ,SAAS,aAAa,QAAQ,WAAW;AAElE,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,eAAe,MAAM,MAAM,IAAI,kBAAkB;AAAA,IACjD,YAAY,MAAM,MAAM,WAAW,kBAAkB;AAAA,EACvD;AACF;AAQA,SAAS,iBAAiB,SAAkE;AAC1F,MAAI,QAAQ,SAAS,YAAY;AAC/B,UAAM,WAAW,QAAQ;AACzB,WAAO;AAAA,MACL,OAAO,SAAS;AAAA,MAChB,WAAW,SAAS;AAAA,MACpB,aAAa,SAAS;AAAA,IACxB;AAAA,EACF;AACA,SAAO;AAAA,IACL,OAAO,QAAQ;AAAA,IACf,WAAW,QAAQ;AAAA,IACnB,aAAa,kBAAkB,QAAQ,KAAK,QAAQ,YAAY,CAAC;AAAA,EACnE;AACF;AAeA,SAAS,uBAAuB,UAAgD;AAC9E,SAAO,WAAW,QAAQ,EACvB,OAAO,SAAS,SAAS,EACzB,OAAO,IAAI,EACX,OAAO,SAAS,KAAK,EACrB,OAAO,IAAI,EACX,OAAO,SAAS,WAAW,EAC3B,OAAO,KAAK,EACZ,MAAM,GAAG,EAAE;AAChB;AAEA,SAAS,kBAAkB,KAAa,GAA+B;AACrE,MAAI,CAAC,GAAG;AACN,UAAM,IAAI;AAAA,MACR,mDAAmD,GAAG;AAAA,IACxD;AAAA,EACF;AACA,SAAO,WAAW,QAAQ,EAAE,OAAO,GAAG,EAAE,OAAO,IAAI,EAAE,OAAO,CAAC,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE;AAC1F;","names":[]}
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import { fileURLToPath as __adcpFileURLToPath } from 'node:url';
|
|
2
|
-
import { dirname as __adcpDirname } from 'node:path';
|
|
3
|
-
import { createRequire as __adcpCreateRequire } from 'node:module';
|
|
4
|
-
const __filename = __adcpFileURLToPath(import.meta.url);
|
|
5
|
-
const __dirname = __adcpDirname(__filename);
|
|
6
|
-
const require = __adcpCreateRequire(import.meta.url);
|
|
7
1
|
import { createSigningFetch } from "./fetch.mjs";
|
|
8
2
|
import { createSigningFetchAsync } from "./fetch-async.mjs";
|
|
9
3
|
import {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/lib/signing/agent-fetch.ts"],"sourcesContent":["import type {\n AgentRequestSigningConfig,\n AgentRequestSigningConfigInline,\n AgentRequestSigningConfigProvider,\n} from '../types/adcp';\nimport { createSigningFetch, type CoverContentDigestPredicate } from './fetch';\nimport { createSigningFetchAsync } from './fetch-async';\nimport {\n buildCapabilityCacheKey,\n defaultCapabilityCache,\n type CachedCapability,\n type CapabilityCache,\n} from './capability-cache';\nimport type { ContentDigestPolicy, VerifierCapability } from './types';\nimport type { SignerKey } from './signer';\nimport { containsWebhookAuthentication } from './webhook-auth-detection';\n\ntype FetchLike = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;\nconst MAX_WEBHOOK_AUTH_INSPECTION_BYTES = 1_048_576;\n\n/**\n * Resolve `globalThis.fetch` at the moment of each outbound request — not at\n * module import, and not at factory call. Late resolution lets a polyfill\n * installed after `buildAgentSigningFetch` returns (but before first request)\n * still take effect. The helper throws a clear error on environments lacking\n * global `fetch` instead of binding `undefined` and failing cryptically inside\n * the signing pipeline.\n */\nfunction defaultUpstream(): FetchLike {\n const f = (globalThis as { fetch?: FetchLike }).fetch;\n if (typeof f !== 'function') {\n throw new TypeError(\n 'buildAgentSigningFetch: no upstream fetch provided and globalThis.fetch is unavailable. Pass `upstream: yourFetch` explicitly.'\n );\n }\n return f;\n}\n\nfunction bodyToUtf8(body: unknown): string | undefined {\n if (body === undefined || body === null) return undefined;\n if (typeof body === 'string') return body.length ? body : undefined;\n if (body instanceof Uint8Array) return Buffer.from(body).toString('utf8');\n if (body instanceof ArrayBuffer) return Buffer.from(body).toString('utf8');\n // FormData / Blob / ReadableStream / async iterables fall through. Throw\n // rather than return `undefined` — a silent pass-through would ship the\n // request unsigned with no hint to the caller, defeating the seller's\n // `required_for` contract. Matches the strict posture of `createSigningFetch`\n // which also refuses unsupported body shapes.\n throw new TypeError(\n `buildAgentSigningFetch cannot extract an AdCP operation name from a body of type ${describeBody(body)}. The signer only supports string, Uint8Array, and ArrayBuffer bodies because the signature must cover the exact wire bytes.`\n );\n}\n\nfunction describeBody(body: unknown): string {\n if (body && typeof body === 'object') {\n const ctor = (body as { constructor?: { name?: string } }).constructor?.name;\n if (ctor) return ctor;\n }\n return typeof body;\n}\n\n/**\n * Extract the AdCP operation name from a JSON-RPC request body, if any.\n *\n * - MCP tool calls: `method === \"tools/call\"` → `params.name` is the op name.\n * - A2A `message/send` / `message/stream`: the op name lives on the first\n * data-kind part as `data.skill`.\n * - All other JSON-RPC methods (`initialize`, `tools/list`, notifications)\n * return `undefined` — those are protocol-layer housekeeping, not AdCP\n * operations subject to request-signing policy.\n *\n * Throws if the body is of a shape the signer can't read (Blob, FormData,\n * ReadableStream). The MCP / A2A SDKs both emit JSON strings today; a future\n * SDK version switching to streams would silently break signing otherwise.\n */\nexport function extractAdcpOperation(body: unknown): string | undefined {\n const text = bodyToUtf8(body);\n if (!text) return undefined;\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n return undefined;\n }\n if (!parsed || typeof parsed !== 'object') return undefined;\n const rpc = parsed as { method?: unknown; params?: unknown };\n\n if (rpc.method === 'tools/call') {\n const params = rpc.params as { name?: unknown } | undefined;\n return typeof params?.name === 'string' ? params.name : undefined;\n }\n\n if (rpc.method === 'message/send' || rpc.method === 'message/stream') {\n const params = rpc.params as { message?: { parts?: unknown } } | undefined;\n const parts = params?.message?.parts;\n if (!Array.isArray(parts)) return undefined;\n for (const part of parts) {\n if (part && typeof part === 'object') {\n const p = part as { kind?: unknown; data?: { skill?: unknown } };\n if (p.kind === 'data' && typeof p.data?.skill === 'string') {\n return p.data.skill;\n }\n }\n }\n }\n\n return undefined;\n}\n\n/**\n * Detect webhook receiver credentials in the outbound JSON-RPC payload. The\n * verifier rejects unsigned requests carrying these credentials regardless of\n * the seller's operation-level capability advertisement, so the client must\n * sign them even while the capability cache is cold or silent for that op.\n * The scan is bounded; if the body exceeds the inspection budget, sign it.\n */\nfunction carriesWebhookAuthentication(body: unknown): boolean {\n const text = bodyToUtf8(body);\n if (!text) return false;\n if (text.length > MAX_WEBHOOK_AUTH_INSPECTION_BYTES) return true;\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n return false;\n }\n return containsWebhookAuthentication(parsed);\n}\n\n/**\n * Decide whether an outbound AdCP call should be signed given the seller's\n * advertised capability block and the buyer's override list.\n *\n * Precedence matches the AdCP spec (`required_for` > `warn_for` >\n * `supported_for`):\n * 1. `always_sign` on the buyer config — pilot-time override, signs even\n * if the seller hasn't listed the op.\n * 2. Seller `required_for` — seller rejects unsigned requests, MUST sign.\n * 3. Seller `warn_for` — shadow mode. Seller verifies when present and\n * logs failures without rejecting; counterparties SHOULD sign so the\n * seller can surface failure rates before flipping to `required_for`.\n * 4. Seller `supported_for` — sign only if the buyer opted in via\n * `sign_supported: true` (defaults off).\n *\n * Returns false when the capability is unknown (cold cache) except for ops\n * in `always_sign`, so the priming `get_adcp_capabilities` call itself is\n * never signed.\n */\nexport function shouldSignOperation(\n operation: string | undefined,\n capability: VerifierCapability | undefined,\n config: AgentRequestSigningConfig\n): boolean {\n if (!operation) return false;\n if (config.always_sign?.includes(operation)) return true;\n if (!capability?.supported) return false;\n if (capability.required_for?.includes(operation)) return true;\n if (capability.warn_for?.includes(operation)) return true;\n if (config.sign_supported && capability.supported_for?.includes(operation)) return true;\n return false;\n}\n\n/**\n * Resolve the seller's content-digest policy into a concrete per-request\n * coverage decision.\n *\n * - `required` → must cover content-digest.\n * - `forbidden` → must NOT cover content-digest.\n * - `either` / absent → default to covering (body-binding is the safer\n * choice; the seller has explicitly allowed both forms).\n */\nexport function resolveCoverContentDigest(policy: ContentDigestPolicy | undefined): boolean {\n if (policy === 'forbidden') return false;\n return true;\n}\n\n/**\n * Convert an inline `AgentRequestSigningConfig` into the `SignerKey` shape\n * expected by `signRequest` / `createSigningFetch`. Provider-backed configs\n * have no in-process key material — callers must route through\n * `createSigningFetchAsync(upstream, config.provider, ...)` instead.\n */\nexport function toSignerKey(config: AgentRequestSigningConfigInline): SignerKey {\n return {\n keyid: config.kid,\n alg: config.alg,\n privateKey: config.private_key as SignerKey['privateKey'],\n };\n}\n\n/** Narrow predicate for the inline shape (kind absent or `'inline'`). */\nexport function isInlineSigningConfig(config: AgentRequestSigningConfig): config is AgentRequestSigningConfigInline {\n return config.kind !== 'provider';\n}\n\n/** Narrow predicate for the provider shape. */\nexport function isProviderSigningConfig(\n config: AgentRequestSigningConfig\n): config is AgentRequestSigningConfigProvider {\n return config.kind === 'provider';\n}\n\nexport interface BuildAgentSigningFetchOptions {\n /**\n * Upstream fetch to wrap. Defaults to `globalThis.fetch` when omitted —\n * use a Node 18+ / browser / worker global, or pass a polyfill / a\n * decorated fetch (retries, telemetry) to compose.\n */\n upstream?: FetchLike;\n signing: AgentRequestSigningConfig;\n /** Lazy accessor for the current cached capability — re-read on every call. */\n getCapability: () => CachedCapability | undefined;\n}\n\n/**\n * Build a fetch wrapper suitable for injection into MCP/A2A transports. On\n * every outbound request:\n * 1. Sign immediately when the payload carries webhook authentication.\n * 2. Extract the AdCP operation name from the JSON-RPC body (MCP tool-call\n * or A2A message/send). Non-AdCP JSON-RPC methods (e.g., `initialize`)\n * pass through unsigned.\n * 3. Consult the cached seller capability to decide whether to sign.\n * 4. Resolve the seller's content-digest policy into a per-request toggle.\n * 5. Delegate to `createSigningFetch` with the decision baked in.\n */\nexport function buildAgentSigningFetch(options: BuildAgentSigningFetchOptions): FetchLike {\n const { signing, getCapability } = options;\n // Resolve `globalThis.fetch` per-call when no explicit upstream was passed\n // so a polyfill installed between factory creation and first request still\n // takes effect. Eager resolution would freeze whatever was global at\n // factory-call time.\n const explicitUpstream = options.upstream;\n const upstream: FetchLike = explicitUpstream ?? ((input, init) => defaultUpstream()(input, init));\n\n const shouldSign = (_url: string, init: RequestInit | undefined): boolean => {\n if (carriesWebhookAuthentication(init?.body)) return true;\n const operation = extractAdcpOperation(init?.body);\n const entry = getCapability();\n return shouldSignOperation(operation, entry?.requestSigning, signing);\n };\n\n const coverContentDigest: CoverContentDigestPredicate = (_url, _init) => {\n const entry = getCapability();\n return resolveCoverContentDigest(entry?.requestSigning?.covers_content_digest);\n };\n\n if (isProviderSigningConfig(signing)) {\n // Freeze the provider's identity fields at factory time and pass a\n // snapshot view down to the async signer. TypeScript `readonly` on the\n // `SigningProvider` interface is compile-time only — if a downstream\n // adapter mutates `keyid`/`algorithm`/`fingerprint` between context\n // build and outbound request, the wire `keyid` would drift away from\n // the cache key the connection was bound to. The frozen view binds the\n // wire identity to the snapshot already used for cache routing.\n const frozen = freezeProviderIdentity(signing.provider);\n return createSigningFetchAsync(upstream, frozen, { shouldSign, coverContentDigest });\n }\n return createSigningFetch(upstream, toSignerKey(signing), { shouldSign, coverContentDigest });\n}\n\nfunction freezeProviderIdentity(provider: AgentRequestSigningConfigProvider['provider']) {\n const keyid = provider.keyid;\n const algorithm = provider.algorithm;\n const fingerprint = provider.fingerprint;\n return {\n keyid,\n algorithm,\n fingerprint,\n sign: (payload: Uint8Array) => provider.sign(payload),\n };\n}\n\nexport interface CreateAgentSignedFetchOptions {\n /** This agent's RFC 9421 signing identity (kid, alg, private_key, agent_url). */\n signing: AgentRequestSigningConfig;\n /**\n * Target seller's `agent_uri` — the base URL whose `get_adcp_capabilities`\n * response gates whether each operation gets signed. The preset keys a\n * capability-cache entry off this URL so repeat calls reuse the same\n * advertisement.\n *\n * For multi-seller buyers, build one signed fetch per seller (or use\n * {@link buildAgentSigningFetch} directly and supply your own\n * `getCapability` that dispatches on the target URL).\n */\n sellerAgentUri: string;\n /**\n * Optional auth token the seller expects alongside the signing headers.\n * Included in the capability cache key so a token rotation naturally\n * invalidates the cached advertisement.\n */\n sellerAuthToken?: string;\n /**\n * Capability cache. Defaults to the shared {@link defaultCapabilityCache}.\n *\n * **The shared default is load-bearing.** `ProtocolClient` and\n * `buildAgentSigningContext` write the seller's `get_adcp_capabilities`\n * response into `defaultCapabilityCache`; this preset reads from the same\n * instance so a single priming call serves every subsequent signing\n * decision. Passing a fresh `new CapabilityCache()` here without also\n * priming it will silently disable `required_for` enforcement: cold cache\n * → `shouldSignOperation` returns `false` → every required op ships\n * unsigned → seller rejects. The SDK emits no error on this path because\n * \"decided not to sign\" is the spec-correct behavior on a cold cache.\n *\n * Pass an explicit cache only when you've also primed it — call\n * {@link ensureCapabilityLoaded} against your cache instance after\n * construction, or seed it via `cache.set(buildCapabilityCacheKey(uri,\n * token), entry)` — or when you genuinely want each caller to re-fetch\n * the seller's capability block.\n *\n * Note: the cache stores **public** seller capability advertisements\n * (the contents of `get_adcp_capabilities` responses), not buyer\n * credentials. The shared default is not a multi-tenant security\n * boundary — different sellers, different auth tokens, and different\n * signing keys already get separate entries via\n * {@link buildCapabilityCacheKey}.\n */\n cache?: CapabilityCache;\n /** Upstream fetch. Defaults to `globalThis.fetch`. */\n upstream?: FetchLike;\n}\n\n/**\n * One-call preset for the single-seller case: bundles\n * {@link buildAgentSigningFetch} with a {@link CapabilityCache} lookup keyed\n * by `sellerAgentUri`, so adapter authors don't have to wire the cache and\n * capability accessor themselves.\n *\n * ```ts\n * // fetch.ts\n * import { createAgentSignedFetch } from '@adcp/sdk/signing';\n *\n * export const signedFetch = createAgentSignedFetch({\n * signing: {\n * kid: 'my-agent-2026',\n * alg: 'ed25519',\n * private_key: JSON.parse(process.env.ADCP_PRIV_KEY!),\n * agent_url: 'https://agent.example.com',\n * },\n * sellerAgentUri: 'https://seller.example.com',\n * });\n * ```\n *\n * ```ts\n * // any other module\n * import { signedFetch } from './fetch';\n *\n * await signedFetch('https://seller.example.com/mcp', {\n * method: 'POST',\n * headers: { 'Content-Type': 'application/json' },\n * body: JSON.stringify(payload),\n * });\n * ```\n *\n * Signing only happens on operations the seller advertises as\n * `required_for` / `warn_for` (or `supported_for` when the buyer config\n * opts in) — so the `get_adcp_capabilities` priming call itself is always\n * unsigned, as the spec requires.\n *\n * For multi-seller adapters, construct one preset per seller, or use\n * {@link buildAgentSigningFetch} directly with a request-dispatching\n * `getCapability` callback.\n */\nexport function createAgentSignedFetch(options: CreateAgentSignedFetchOptions): FetchLike {\n const cache = options.cache ?? defaultCapabilityCache;\n const cacheKey = buildCapabilityCacheKey(options.sellerAgentUri, options.sellerAuthToken);\n return buildAgentSigningFetch({\n signing: options.signing,\n upstream: options.upstream,\n getCapability: () => cache.get(cacheKey),\n });\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/signing/agent-fetch.ts"],"sourcesContent":["import type {\n AgentRequestSigningConfig,\n AgentRequestSigningConfigInline,\n AgentRequestSigningConfigProvider,\n} from '../types/adcp';\nimport { createSigningFetch, type CoverContentDigestPredicate } from './fetch';\nimport { createSigningFetchAsync } from './fetch-async';\nimport {\n buildCapabilityCacheKey,\n defaultCapabilityCache,\n type CachedCapability,\n type CapabilityCache,\n} from './capability-cache';\nimport type { ContentDigestPolicy, VerifierCapability } from './types';\nimport type { SignerKey } from './signer';\nimport { containsWebhookAuthentication } from './webhook-auth-detection';\n\ntype FetchLike = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;\nconst MAX_WEBHOOK_AUTH_INSPECTION_BYTES = 1_048_576;\n\n/**\n * Resolve `globalThis.fetch` at the moment of each outbound request — not at\n * module import, and not at factory call. Late resolution lets a polyfill\n * installed after `buildAgentSigningFetch` returns (but before first request)\n * still take effect. The helper throws a clear error on environments lacking\n * global `fetch` instead of binding `undefined` and failing cryptically inside\n * the signing pipeline.\n */\nfunction defaultUpstream(): FetchLike {\n const f = (globalThis as { fetch?: FetchLike }).fetch;\n if (typeof f !== 'function') {\n throw new TypeError(\n 'buildAgentSigningFetch: no upstream fetch provided and globalThis.fetch is unavailable. Pass `upstream: yourFetch` explicitly.'\n );\n }\n return f;\n}\n\nfunction bodyToUtf8(body: unknown): string | undefined {\n if (body === undefined || body === null) return undefined;\n if (typeof body === 'string') return body.length ? body : undefined;\n if (body instanceof Uint8Array) return Buffer.from(body).toString('utf8');\n if (body instanceof ArrayBuffer) return Buffer.from(body).toString('utf8');\n // FormData / Blob / ReadableStream / async iterables fall through. Throw\n // rather than return `undefined` — a silent pass-through would ship the\n // request unsigned with no hint to the caller, defeating the seller's\n // `required_for` contract. Matches the strict posture of `createSigningFetch`\n // which also refuses unsupported body shapes.\n throw new TypeError(\n `buildAgentSigningFetch cannot extract an AdCP operation name from a body of type ${describeBody(body)}. The signer only supports string, Uint8Array, and ArrayBuffer bodies because the signature must cover the exact wire bytes.`\n );\n}\n\nfunction describeBody(body: unknown): string {\n if (body && typeof body === 'object') {\n const ctor = (body as { constructor?: { name?: string } }).constructor?.name;\n if (ctor) return ctor;\n }\n return typeof body;\n}\n\n/**\n * Extract the AdCP operation name from a JSON-RPC request body, if any.\n *\n * - MCP tool calls: `method === \"tools/call\"` → `params.name` is the op name.\n * - A2A `message/send` / `message/stream`: the op name lives on the first\n * data-kind part as `data.skill`.\n * - All other JSON-RPC methods (`initialize`, `tools/list`, notifications)\n * return `undefined` — those are protocol-layer housekeeping, not AdCP\n * operations subject to request-signing policy.\n *\n * Throws if the body is of a shape the signer can't read (Blob, FormData,\n * ReadableStream). The MCP / A2A SDKs both emit JSON strings today; a future\n * SDK version switching to streams would silently break signing otherwise.\n */\nexport function extractAdcpOperation(body: unknown): string | undefined {\n const text = bodyToUtf8(body);\n if (!text) return undefined;\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n return undefined;\n }\n if (!parsed || typeof parsed !== 'object') return undefined;\n const rpc = parsed as { method?: unknown; params?: unknown };\n\n if (rpc.method === 'tools/call') {\n const params = rpc.params as { name?: unknown } | undefined;\n return typeof params?.name === 'string' ? params.name : undefined;\n }\n\n if (rpc.method === 'message/send' || rpc.method === 'message/stream') {\n const params = rpc.params as { message?: { parts?: unknown } } | undefined;\n const parts = params?.message?.parts;\n if (!Array.isArray(parts)) return undefined;\n for (const part of parts) {\n if (part && typeof part === 'object') {\n const p = part as { kind?: unknown; data?: { skill?: unknown } };\n if (p.kind === 'data' && typeof p.data?.skill === 'string') {\n return p.data.skill;\n }\n }\n }\n }\n\n return undefined;\n}\n\n/**\n * Detect webhook receiver credentials in the outbound JSON-RPC payload. The\n * verifier rejects unsigned requests carrying these credentials regardless of\n * the seller's operation-level capability advertisement, so the client must\n * sign them even while the capability cache is cold or silent for that op.\n * The scan is bounded; if the body exceeds the inspection budget, sign it.\n */\nfunction carriesWebhookAuthentication(body: unknown): boolean {\n const text = bodyToUtf8(body);\n if (!text) return false;\n if (text.length > MAX_WEBHOOK_AUTH_INSPECTION_BYTES) return true;\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n return false;\n }\n return containsWebhookAuthentication(parsed);\n}\n\n/**\n * Decide whether an outbound AdCP call should be signed given the seller's\n * advertised capability block and the buyer's override list.\n *\n * Precedence matches the AdCP spec (`required_for` > `warn_for` >\n * `supported_for`):\n * 1. `always_sign` on the buyer config — pilot-time override, signs even\n * if the seller hasn't listed the op.\n * 2. Seller `required_for` — seller rejects unsigned requests, MUST sign.\n * 3. Seller `warn_for` — shadow mode. Seller verifies when present and\n * logs failures without rejecting; counterparties SHOULD sign so the\n * seller can surface failure rates before flipping to `required_for`.\n * 4. Seller `supported_for` — sign only if the buyer opted in via\n * `sign_supported: true` (defaults off).\n *\n * Returns false when the capability is unknown (cold cache) except for ops\n * in `always_sign`, so the priming `get_adcp_capabilities` call itself is\n * never signed.\n */\nexport function shouldSignOperation(\n operation: string | undefined,\n capability: VerifierCapability | undefined,\n config: AgentRequestSigningConfig\n): boolean {\n if (!operation) return false;\n if (config.always_sign?.includes(operation)) return true;\n if (!capability?.supported) return false;\n if (capability.required_for?.includes(operation)) return true;\n if (capability.warn_for?.includes(operation)) return true;\n if (config.sign_supported && capability.supported_for?.includes(operation)) return true;\n return false;\n}\n\n/**\n * Resolve the seller's content-digest policy into a concrete per-request\n * coverage decision.\n *\n * - `required` → must cover content-digest.\n * - `forbidden` → must NOT cover content-digest.\n * - `either` / absent → default to covering (body-binding is the safer\n * choice; the seller has explicitly allowed both forms).\n */\nexport function resolveCoverContentDigest(policy: ContentDigestPolicy | undefined): boolean {\n if (policy === 'forbidden') return false;\n return true;\n}\n\n/**\n * Convert an inline `AgentRequestSigningConfig` into the `SignerKey` shape\n * expected by `signRequest` / `createSigningFetch`. Provider-backed configs\n * have no in-process key material — callers must route through\n * `createSigningFetchAsync(upstream, config.provider, ...)` instead.\n */\nexport function toSignerKey(config: AgentRequestSigningConfigInline): SignerKey {\n return {\n keyid: config.kid,\n alg: config.alg,\n privateKey: config.private_key as SignerKey['privateKey'],\n };\n}\n\n/** Narrow predicate for the inline shape (kind absent or `'inline'`). */\nexport function isInlineSigningConfig(config: AgentRequestSigningConfig): config is AgentRequestSigningConfigInline {\n return config.kind !== 'provider';\n}\n\n/** Narrow predicate for the provider shape. */\nexport function isProviderSigningConfig(\n config: AgentRequestSigningConfig\n): config is AgentRequestSigningConfigProvider {\n return config.kind === 'provider';\n}\n\nexport interface BuildAgentSigningFetchOptions {\n /**\n * Upstream fetch to wrap. Defaults to `globalThis.fetch` when omitted —\n * use a Node 18+ / browser / worker global, or pass a polyfill / a\n * decorated fetch (retries, telemetry) to compose.\n */\n upstream?: FetchLike;\n signing: AgentRequestSigningConfig;\n /** Lazy accessor for the current cached capability — re-read on every call. */\n getCapability: () => CachedCapability | undefined;\n}\n\n/**\n * Build a fetch wrapper suitable for injection into MCP/A2A transports. On\n * every outbound request:\n * 1. Sign immediately when the payload carries webhook authentication.\n * 2. Extract the AdCP operation name from the JSON-RPC body (MCP tool-call\n * or A2A message/send). Non-AdCP JSON-RPC methods (e.g., `initialize`)\n * pass through unsigned.\n * 3. Consult the cached seller capability to decide whether to sign.\n * 4. Resolve the seller's content-digest policy into a per-request toggle.\n * 5. Delegate to `createSigningFetch` with the decision baked in.\n */\nexport function buildAgentSigningFetch(options: BuildAgentSigningFetchOptions): FetchLike {\n const { signing, getCapability } = options;\n // Resolve `globalThis.fetch` per-call when no explicit upstream was passed\n // so a polyfill installed between factory creation and first request still\n // takes effect. Eager resolution would freeze whatever was global at\n // factory-call time.\n const explicitUpstream = options.upstream;\n const upstream: FetchLike = explicitUpstream ?? ((input, init) => defaultUpstream()(input, init));\n\n const shouldSign = (_url: string, init: RequestInit | undefined): boolean => {\n if (carriesWebhookAuthentication(init?.body)) return true;\n const operation = extractAdcpOperation(init?.body);\n const entry = getCapability();\n return shouldSignOperation(operation, entry?.requestSigning, signing);\n };\n\n const coverContentDigest: CoverContentDigestPredicate = (_url, _init) => {\n const entry = getCapability();\n return resolveCoverContentDigest(entry?.requestSigning?.covers_content_digest);\n };\n\n if (isProviderSigningConfig(signing)) {\n // Freeze the provider's identity fields at factory time and pass a\n // snapshot view down to the async signer. TypeScript `readonly` on the\n // `SigningProvider` interface is compile-time only — if a downstream\n // adapter mutates `keyid`/`algorithm`/`fingerprint` between context\n // build and outbound request, the wire `keyid` would drift away from\n // the cache key the connection was bound to. The frozen view binds the\n // wire identity to the snapshot already used for cache routing.\n const frozen = freezeProviderIdentity(signing.provider);\n return createSigningFetchAsync(upstream, frozen, { shouldSign, coverContentDigest });\n }\n return createSigningFetch(upstream, toSignerKey(signing), { shouldSign, coverContentDigest });\n}\n\nfunction freezeProviderIdentity(provider: AgentRequestSigningConfigProvider['provider']) {\n const keyid = provider.keyid;\n const algorithm = provider.algorithm;\n const fingerprint = provider.fingerprint;\n return {\n keyid,\n algorithm,\n fingerprint,\n sign: (payload: Uint8Array) => provider.sign(payload),\n };\n}\n\nexport interface CreateAgentSignedFetchOptions {\n /** This agent's RFC 9421 signing identity (kid, alg, private_key, agent_url). */\n signing: AgentRequestSigningConfig;\n /**\n * Target seller's `agent_uri` — the base URL whose `get_adcp_capabilities`\n * response gates whether each operation gets signed. The preset keys a\n * capability-cache entry off this URL so repeat calls reuse the same\n * advertisement.\n *\n * For multi-seller buyers, build one signed fetch per seller (or use\n * {@link buildAgentSigningFetch} directly and supply your own\n * `getCapability` that dispatches on the target URL).\n */\n sellerAgentUri: string;\n /**\n * Optional auth token the seller expects alongside the signing headers.\n * Included in the capability cache key so a token rotation naturally\n * invalidates the cached advertisement.\n */\n sellerAuthToken?: string;\n /**\n * Capability cache. Defaults to the shared {@link defaultCapabilityCache}.\n *\n * **The shared default is load-bearing.** `ProtocolClient` and\n * `buildAgentSigningContext` write the seller's `get_adcp_capabilities`\n * response into `defaultCapabilityCache`; this preset reads from the same\n * instance so a single priming call serves every subsequent signing\n * decision. Passing a fresh `new CapabilityCache()` here without also\n * priming it will silently disable `required_for` enforcement: cold cache\n * → `shouldSignOperation` returns `false` → every required op ships\n * unsigned → seller rejects. The SDK emits no error on this path because\n * \"decided not to sign\" is the spec-correct behavior on a cold cache.\n *\n * Pass an explicit cache only when you've also primed it — call\n * {@link ensureCapabilityLoaded} against your cache instance after\n * construction, or seed it via `cache.set(buildCapabilityCacheKey(uri,\n * token), entry)` — or when you genuinely want each caller to re-fetch\n * the seller's capability block.\n *\n * Note: the cache stores **public** seller capability advertisements\n * (the contents of `get_adcp_capabilities` responses), not buyer\n * credentials. The shared default is not a multi-tenant security\n * boundary — different sellers, different auth tokens, and different\n * signing keys already get separate entries via\n * {@link buildCapabilityCacheKey}.\n */\n cache?: CapabilityCache;\n /** Upstream fetch. Defaults to `globalThis.fetch`. */\n upstream?: FetchLike;\n}\n\n/**\n * One-call preset for the single-seller case: bundles\n * {@link buildAgentSigningFetch} with a {@link CapabilityCache} lookup keyed\n * by `sellerAgentUri`, so adapter authors don't have to wire the cache and\n * capability accessor themselves.\n *\n * ```ts\n * // fetch.ts\n * import { createAgentSignedFetch } from '@adcp/sdk/signing';\n *\n * export const signedFetch = createAgentSignedFetch({\n * signing: {\n * kid: 'my-agent-2026',\n * alg: 'ed25519',\n * private_key: JSON.parse(process.env.ADCP_PRIV_KEY!),\n * agent_url: 'https://agent.example.com',\n * },\n * sellerAgentUri: 'https://seller.example.com',\n * });\n * ```\n *\n * ```ts\n * // any other module\n * import { signedFetch } from './fetch';\n *\n * await signedFetch('https://seller.example.com/mcp', {\n * method: 'POST',\n * headers: { 'Content-Type': 'application/json' },\n * body: JSON.stringify(payload),\n * });\n * ```\n *\n * Signing only happens on operations the seller advertises as\n * `required_for` / `warn_for` (or `supported_for` when the buyer config\n * opts in) — so the `get_adcp_capabilities` priming call itself is always\n * unsigned, as the spec requires.\n *\n * For multi-seller adapters, construct one preset per seller, or use\n * {@link buildAgentSigningFetch} directly with a request-dispatching\n * `getCapability` callback.\n */\nexport function createAgentSignedFetch(options: CreateAgentSignedFetchOptions): FetchLike {\n const cache = options.cache ?? defaultCapabilityCache;\n const cacheKey = buildCapabilityCacheKey(options.sellerAgentUri, options.sellerAuthToken);\n return buildAgentSigningFetch({\n signing: options.signing,\n upstream: options.upstream,\n getCapability: () => cache.get(cacheKey),\n });\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import { fileURLToPath as __adcpFileURLToPath } from 'node:url';
|
|
2
|
-
import { dirname as __adcpDirname } from 'node:path';
|
|
3
|
-
import { createRequire as __adcpCreateRequire } from 'node:module';
|
|
4
|
-
const __filename = __adcpFileURLToPath(import.meta.url);
|
|
5
|
-
const __dirname = __adcpDirname(__filename);
|
|
6
|
-
const require = __adcpCreateRequire(import.meta.url);
|
|
7
1
|
class CanonicalizeError extends Error {
|
|
8
2
|
constructor(message, meta) {
|
|
9
3
|
super(message);
|