@adcp/sdk 12.0.0 → 12.0.2
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.d.mts +1 -1
- package/dist/lib/index.d.ts +1 -1
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js.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.d.mts +13 -0
- package/dist/lib/server/create-adcp-server.d.ts +13 -0
- package/dist/lib/server/create-adcp-server.d.ts.map +1 -1
- package/dist/lib/server/create-adcp-server.js +3 -2
- package/dist/lib/server/create-adcp-server.js.map +1 -1
- package/dist/lib/server/create-adcp-server.mjs +3 -8
- 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.d.mts +1 -1
- package/dist/lib/server/index.d.ts +1 -1
- package/dist/lib/server/index.d.ts.map +1 -1
- package/dist/lib/server/index.js.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.d.mts +1 -1
- package/dist/lib/server/legacy/v5/index.d.ts +1 -1
- package/dist/lib/server/legacy/v5/index.d.ts.map +1 -1
- package/dist/lib/server/legacy/v5/index.js.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/socket-mode/conformance-client.ts"],"sourcesContent":["/**\n * Adopter-facing Socket Mode client.\n *\n * Opens an outbound WebSocket from your dev/staging environment to a\n * remote AdCP runner (typically Addie at agenticadvertising.org) and\n * exposes your existing MCP `Server` over it. The remote runner can\n * then drive `tools/list`, `tools/call`, etc. against your server\n * exactly as if it had reached you over HTTP — no public DNS, no\n * inbound exposure.\n *\n * Three-line integration:\n *\n * ```ts\n * import { ConformanceClient } from '@adcp/sdk/server';\n * import { mcpServer } from './my-mcp-server';\n *\n * const client = new ConformanceClient({\n * url: 'wss://addie.agenticadvertising.org/conformance/connect',\n * token: process.env.ADCP_CONFORMANCE_TOKEN!,\n * server: mcpServer,\n * });\n * await client.start();\n * ```\n *\n * Reconnect: exponential backoff capped at 30s. Stop via `close()` to\n * opt out of further reconnect attempts.\n *\n * Dev/staging only by design — production deployments must not expose\n * `comply_test_controller` on any surface, including this channel.\n * See [adcontextprotocol/adcp#3986](https://github.com/adcontextprotocol/adcp/issues/3986).\n */\n\nimport WebSocket from 'ws';\nimport type { Server as MCPServer } from '@modelcontextprotocol/sdk/server/index.js';\nimport { WebSocketTransport } from './ws-transport';\n\nexport type ConformanceStatus = 'idle' | 'connecting' | 'connected' | 'disconnected' | 'error';\n\nexport interface ConformanceClientOptions {\n /**\n * The remote runner's WebSocket URL. For Addie this is\n * `wss://addie.agenticadvertising.org/conformance/connect` in\n * production, `ws://localhost:3000/conformance/connect` in dev.\n */\n url: string;\n\n /**\n * Adopter-account-bound token issued by the runner. For Addie, ask in\n * chat (\"give me a fresh conformance token\") or POST /api/conformance/token.\n * Tokens are short-lived (typically 1h); on expiry, re-request and\n * pass the new token via a fresh `ConformanceClient`.\n */\n token: string;\n\n /**\n * Your existing MCP server. The same instance you'd connect to\n * `StreamableHTTPServerTransport` for normal traffic — this client\n * exposes it bidirectionally over the outbound WebSocket.\n */\n server: MCPServer;\n\n /**\n * If true (default), reconnect on disconnect with exponential backoff\n * up to 30s. Set false for one-shot connections.\n */\n reconnect?: boolean;\n\n /**\n * Status callback. Fires on every state transition. Useful for\n * surfacing connection state in dev tooling or CI logs.\n */\n onStatus?: (status: ConformanceStatus, detail?: { error?: Error; attempt?: number }) => void;\n}\n\nconst RECONNECT_INITIAL_MS = 1000;\nconst RECONNECT_MAX_MS = 30_000;\n\nexport class ConformanceClient {\n private status: ConformanceStatus = 'idle';\n private socket?: WebSocket;\n private transport?: WebSocketTransport;\n private stopped = false;\n private reconnectAttempt = 0;\n private readonly opts: Required<Pick<ConformanceClientOptions, 'reconnect'>> & ConformanceClientOptions;\n\n constructor(opts: ConformanceClientOptions) {\n this.opts = { reconnect: true, ...opts };\n }\n\n async start(): Promise<void> {\n this.stopped = false;\n await this.connect();\n }\n\n async close(): Promise<void> {\n this.stopped = true;\n if (this.transport) {\n await this.transport.close();\n } else if (this.socket) {\n this.socket.close(1000, 'adopter close');\n }\n this.setStatus('idle');\n }\n\n getStatus(): ConformanceStatus {\n return this.status;\n }\n\n private async connect(): Promise<void> {\n this.setStatus('connecting');\n const url = new URL(this.opts.url);\n url.searchParams.set('token', this.opts.token);\n const socket = new WebSocket(url.toString());\n this.socket = socket;\n const transport = new WebSocketTransport(socket);\n this.transport = transport;\n\n socket.once('close', () => {\n this.setStatus('disconnected');\n if (!this.stopped && this.opts.reconnect) {\n void this.scheduleReconnect();\n }\n });\n\n try {\n await this.opts.server.connect(transport);\n this.reconnectAttempt = 0;\n this.setStatus('connected');\n } catch (err) {\n this.setStatus('error', { error: err as Error });\n socket.close(1011, 'connect failed');\n if (!this.stopped && this.opts.reconnect) {\n void this.scheduleReconnect();\n }\n throw err;\n }\n }\n\n private async scheduleReconnect(): Promise<void> {\n this.reconnectAttempt += 1;\n const delay = Math.min(RECONNECT_INITIAL_MS * 2 ** (this.reconnectAttempt - 1), RECONNECT_MAX_MS);\n this.setStatus('connecting', { attempt: this.reconnectAttempt });\n await new Promise(r => setTimeout(r, delay));\n if (this.stopped) return;\n try {\n await this.connect();\n } catch {\n // setStatus already invoked; loop continues via the close handler\n }\n }\n\n private setStatus(status: ConformanceStatus, detail?: { error?: Error; attempt?: number }): void {\n this.status = status;\n this.opts.onStatus?.(status, detail);\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../../../src/lib/server/socket-mode/conformance-client.ts"],"sourcesContent":["/**\n * Adopter-facing Socket Mode client.\n *\n * Opens an outbound WebSocket from your dev/staging environment to a\n * remote AdCP runner (typically Addie at agenticadvertising.org) and\n * exposes your existing MCP `Server` over it. The remote runner can\n * then drive `tools/list`, `tools/call`, etc. against your server\n * exactly as if it had reached you over HTTP — no public DNS, no\n * inbound exposure.\n *\n * Three-line integration:\n *\n * ```ts\n * import { ConformanceClient } from '@adcp/sdk/server';\n * import { mcpServer } from './my-mcp-server';\n *\n * const client = new ConformanceClient({\n * url: 'wss://addie.agenticadvertising.org/conformance/connect',\n * token: process.env.ADCP_CONFORMANCE_TOKEN!,\n * server: mcpServer,\n * });\n * await client.start();\n * ```\n *\n * Reconnect: exponential backoff capped at 30s. Stop via `close()` to\n * opt out of further reconnect attempts.\n *\n * Dev/staging only by design — production deployments must not expose\n * `comply_test_controller` on any surface, including this channel.\n * See [adcontextprotocol/adcp#3986](https://github.com/adcontextprotocol/adcp/issues/3986).\n */\n\nimport WebSocket from 'ws';\nimport type { Server as MCPServer } from '@modelcontextprotocol/sdk/server/index.js';\nimport { WebSocketTransport } from './ws-transport';\n\nexport type ConformanceStatus = 'idle' | 'connecting' | 'connected' | 'disconnected' | 'error';\n\nexport interface ConformanceClientOptions {\n /**\n * The remote runner's WebSocket URL. For Addie this is\n * `wss://addie.agenticadvertising.org/conformance/connect` in\n * production, `ws://localhost:3000/conformance/connect` in dev.\n */\n url: string;\n\n /**\n * Adopter-account-bound token issued by the runner. For Addie, ask in\n * chat (\"give me a fresh conformance token\") or POST /api/conformance/token.\n * Tokens are short-lived (typically 1h); on expiry, re-request and\n * pass the new token via a fresh `ConformanceClient`.\n */\n token: string;\n\n /**\n * Your existing MCP server. The same instance you'd connect to\n * `StreamableHTTPServerTransport` for normal traffic — this client\n * exposes it bidirectionally over the outbound WebSocket.\n */\n server: MCPServer;\n\n /**\n * If true (default), reconnect on disconnect with exponential backoff\n * up to 30s. Set false for one-shot connections.\n */\n reconnect?: boolean;\n\n /**\n * Status callback. Fires on every state transition. Useful for\n * surfacing connection state in dev tooling or CI logs.\n */\n onStatus?: (status: ConformanceStatus, detail?: { error?: Error; attempt?: number }) => void;\n}\n\nconst RECONNECT_INITIAL_MS = 1000;\nconst RECONNECT_MAX_MS = 30_000;\n\nexport class ConformanceClient {\n private status: ConformanceStatus = 'idle';\n private socket?: WebSocket;\n private transport?: WebSocketTransport;\n private stopped = false;\n private reconnectAttempt = 0;\n private readonly opts: Required<Pick<ConformanceClientOptions, 'reconnect'>> & ConformanceClientOptions;\n\n constructor(opts: ConformanceClientOptions) {\n this.opts = { reconnect: true, ...opts };\n }\n\n async start(): Promise<void> {\n this.stopped = false;\n await this.connect();\n }\n\n async close(): Promise<void> {\n this.stopped = true;\n if (this.transport) {\n await this.transport.close();\n } else if (this.socket) {\n this.socket.close(1000, 'adopter close');\n }\n this.setStatus('idle');\n }\n\n getStatus(): ConformanceStatus {\n return this.status;\n }\n\n private async connect(): Promise<void> {\n this.setStatus('connecting');\n const url = new URL(this.opts.url);\n url.searchParams.set('token', this.opts.token);\n const socket = new WebSocket(url.toString());\n this.socket = socket;\n const transport = new WebSocketTransport(socket);\n this.transport = transport;\n\n socket.once('close', () => {\n this.setStatus('disconnected');\n if (!this.stopped && this.opts.reconnect) {\n void this.scheduleReconnect();\n }\n });\n\n try {\n await this.opts.server.connect(transport);\n this.reconnectAttempt = 0;\n this.setStatus('connected');\n } catch (err) {\n this.setStatus('error', { error: err as Error });\n socket.close(1011, 'connect failed');\n if (!this.stopped && this.opts.reconnect) {\n void this.scheduleReconnect();\n }\n throw err;\n }\n }\n\n private async scheduleReconnect(): Promise<void> {\n this.reconnectAttempt += 1;\n const delay = Math.min(RECONNECT_INITIAL_MS * 2 ** (this.reconnectAttempt - 1), RECONNECT_MAX_MS);\n this.setStatus('connecting', { attempt: this.reconnectAttempt });\n await new Promise(r => setTimeout(r, delay));\n if (this.stopped) return;\n try {\n await this.connect();\n } catch {\n // setStatus already invoked; loop continues via the close handler\n }\n }\n\n private setStatus(status: ConformanceStatus, detail?: { error?: Error; attempt?: number }): void {\n this.status = status;\n this.opts.onStatus?.(status, detail);\n }\n}\n"],"mappings":"AAgCA,OAAO,eAAe;AAEtB,SAAS,0BAA0B;AAwCnC,MAAM,uBAAuB;AAC7B,MAAM,mBAAmB;AAElB,MAAM,kBAAkB;AAAA,EACrB,SAA4B;AAAA,EAC5B;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,mBAAmB;AAAA,EACV;AAAA,EAEjB,YAAY,MAAgC;AAC1C,SAAK,OAAO,EAAE,WAAW,MAAM,GAAG,KAAK;AAAA,EACzC;AAAA,EAEA,MAAM,QAAuB;AAC3B,SAAK,UAAU;AACf,UAAM,KAAK,QAAQ;AAAA,EACrB;AAAA,EAEA,MAAM,QAAuB;AAC3B,SAAK,UAAU;AACf,QAAI,KAAK,WAAW;AAClB,YAAM,KAAK,UAAU,MAAM;AAAA,IAC7B,WAAW,KAAK,QAAQ;AACtB,WAAK,OAAO,MAAM,KAAM,eAAe;AAAA,IACzC;AACA,SAAK,UAAU,MAAM;AAAA,EACvB;AAAA,EAEA,YAA+B;AAC7B,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAc,UAAyB;AACrC,SAAK,UAAU,YAAY;AAC3B,UAAM,MAAM,IAAI,IAAI,KAAK,KAAK,GAAG;AACjC,QAAI,aAAa,IAAI,SAAS,KAAK,KAAK,KAAK;AAC7C,UAAM,SAAS,IAAI,UAAU,IAAI,SAAS,CAAC;AAC3C,SAAK,SAAS;AACd,UAAM,YAAY,IAAI,mBAAmB,MAAM;AAC/C,SAAK,YAAY;AAEjB,WAAO,KAAK,SAAS,MAAM;AACzB,WAAK,UAAU,cAAc;AAC7B,UAAI,CAAC,KAAK,WAAW,KAAK,KAAK,WAAW;AACxC,aAAK,KAAK,kBAAkB;AAAA,MAC9B;AAAA,IACF,CAAC;AAED,QAAI;AACF,YAAM,KAAK,KAAK,OAAO,QAAQ,SAAS;AACxC,WAAK,mBAAmB;AACxB,WAAK,UAAU,WAAW;AAAA,IAC5B,SAAS,KAAK;AACZ,WAAK,UAAU,SAAS,EAAE,OAAO,IAAa,CAAC;AAC/C,aAAO,MAAM,MAAM,gBAAgB;AACnC,UAAI,CAAC,KAAK,WAAW,KAAK,KAAK,WAAW;AACxC,aAAK,KAAK,kBAAkB;AAAA,MAC9B;AACA,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,MAAc,oBAAmC;AAC/C,SAAK,oBAAoB;AACzB,UAAM,QAAQ,KAAK,IAAI,uBAAuB,MAAM,KAAK,mBAAmB,IAAI,gBAAgB;AAChG,SAAK,UAAU,cAAc,EAAE,SAAS,KAAK,iBAAiB,CAAC;AAC/D,UAAM,IAAI,QAAQ,OAAK,WAAW,GAAG,KAAK,CAAC;AAC3C,QAAI,KAAK,QAAS;AAClB,QAAI;AACF,YAAM,KAAK,QAAQ;AAAA,IACrB,QAAQ;AAAA,IAER;AAAA,EACF;AAAA,EAEQ,UAAU,QAA2B,QAAoD;AAC/F,SAAK,SAAS;AACd,SAAK,KAAK,WAAW,QAAQ,MAAM;AAAA,EACrC;AACF;","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 { ConformanceClient } from "./conformance-client.mjs";
|
|
8
2
|
import { WebSocketTransport } from "./ws-transport.mjs";
|
|
9
3
|
export {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/lib/server/socket-mode/index.ts"],"sourcesContent":["/**\n * Socket Mode — outbound-WebSocket bridge so an adopter's MCP server\n * can be reached by a remote runner without a public DNS / firewall\n * hole. Slack Socket Mode pattern.\n *\n * Today the canonical consumer is Addie at agenticadvertising.org for\n * conformance assistance during agent development. The transport is\n * neutral — any third-party orchestrator that speaks the same handshake\n * could terminate the other end.\n *\n * Dev/staging only — see `ConformanceClient` JSDoc for the policy.\n */\n\nexport { ConformanceClient, type ConformanceClientOptions, type ConformanceStatus } from './conformance-client';\nexport { WebSocketTransport } from './ws-transport';\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../../../src/lib/server/socket-mode/index.ts"],"sourcesContent":["/**\n * Socket Mode — outbound-WebSocket bridge so an adopter's MCP server\n * can be reached by a remote runner without a public DNS / firewall\n * hole. Slack Socket Mode pattern.\n *\n * Today the canonical consumer is Addie at agenticadvertising.org for\n * conformance assistance during agent development. The transport is\n * neutral — any third-party orchestrator that speaks the same handshake\n * could terminate the other end.\n *\n * Dev/staging only — see `ConformanceClient` JSDoc for the policy.\n */\n\nexport { ConformanceClient, type ConformanceClientOptions, type ConformanceStatus } from './conformance-client';\nexport { WebSocketTransport } from './ws-transport';\n"],"mappings":"AAaA,SAAS,yBAAgF;AACzF,SAAS,0BAA0B;","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 WebSocket from "ws";
|
|
8
2
|
import { JSONRPCMessageSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
9
3
|
class WebSocketTransport {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/lib/server/socket-mode/ws-transport.ts"],"sourcesContent":["/**\n * Adopter-side MCP `Transport` over an outbound WebSocket.\n *\n * Wraps a `ws` Node WebSocket as the MCP SDK's `Transport` interface\n * so an existing `Server` instance can be exposed bidirectionally over\n * a single outbound connection.\n *\n * This is the building block for \"Socket Mode\" — see `ConformanceClient`\n * for the high-level adopter-facing class.\n */\n\nimport WebSocket from 'ws';\nimport type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport { JSONRPCMessageSchema, type JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js';\n\nexport class WebSocketTransport implements Transport {\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n sessionId?: string;\n\n private closed = false;\n\n constructor(private readonly socket: WebSocket) {}\n\n async start(): Promise<void> {\n this.socket.on('message', (data, isBinary) => {\n if (isBinary) {\n this.onerror?.(new Error('binary frames are not supported'));\n return;\n }\n const text = data.toString('utf-8');\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch (err) {\n this.onerror?.(new Error(`malformed JSON frame: ${(err as Error).message}`));\n return;\n }\n const result = JSONRPCMessageSchema.safeParse(parsed);\n if (!result.success) {\n this.onerror?.(new Error(`malformed JSON-RPC message: ${result.error.message}`));\n return;\n }\n this.onmessage?.(result.data);\n });\n\n this.socket.on('close', () => {\n if (this.closed) return;\n this.closed = true;\n this.onclose?.();\n });\n\n this.socket.on('error', err => {\n this.onerror?.(err);\n });\n\n if (this.socket.readyState === WebSocket.CONNECTING) {\n await new Promise<void>((resolve, reject) => {\n this.socket.once('open', () => resolve());\n this.socket.once('error', reject);\n });\n }\n }\n\n async send(message: JSONRPCMessage): Promise<void> {\n if (this.closed) throw new Error('transport closed');\n return new Promise((resolve, reject) => {\n this.socket.send(JSON.stringify(message), err => {\n if (err) reject(err);\n else resolve();\n });\n });\n }\n\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n try {\n this.socket.close(1000, 'transport closed');\n } catch {\n // ignore\n }\n this.onclose?.();\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../../../src/lib/server/socket-mode/ws-transport.ts"],"sourcesContent":["/**\n * Adopter-side MCP `Transport` over an outbound WebSocket.\n *\n * Wraps a `ws` Node WebSocket as the MCP SDK's `Transport` interface\n * so an existing `Server` instance can be exposed bidirectionally over\n * a single outbound connection.\n *\n * This is the building block for \"Socket Mode\" — see `ConformanceClient`\n * for the high-level adopter-facing class.\n */\n\nimport WebSocket from 'ws';\nimport type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';\nimport { JSONRPCMessageSchema, type JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js';\n\nexport class WebSocketTransport implements Transport {\n onclose?: () => void;\n onerror?: (error: Error) => void;\n onmessage?: (message: JSONRPCMessage) => void;\n sessionId?: string;\n\n private closed = false;\n\n constructor(private readonly socket: WebSocket) {}\n\n async start(): Promise<void> {\n this.socket.on('message', (data, isBinary) => {\n if (isBinary) {\n this.onerror?.(new Error('binary frames are not supported'));\n return;\n }\n const text = data.toString('utf-8');\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch (err) {\n this.onerror?.(new Error(`malformed JSON frame: ${(err as Error).message}`));\n return;\n }\n const result = JSONRPCMessageSchema.safeParse(parsed);\n if (!result.success) {\n this.onerror?.(new Error(`malformed JSON-RPC message: ${result.error.message}`));\n return;\n }\n this.onmessage?.(result.data);\n });\n\n this.socket.on('close', () => {\n if (this.closed) return;\n this.closed = true;\n this.onclose?.();\n });\n\n this.socket.on('error', err => {\n this.onerror?.(err);\n });\n\n if (this.socket.readyState === WebSocket.CONNECTING) {\n await new Promise<void>((resolve, reject) => {\n this.socket.once('open', () => resolve());\n this.socket.once('error', reject);\n });\n }\n }\n\n async send(message: JSONRPCMessage): Promise<void> {\n if (this.closed) throw new Error('transport closed');\n return new Promise((resolve, reject) => {\n this.socket.send(JSON.stringify(message), err => {\n if (err) reject(err);\n else resolve();\n });\n });\n }\n\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n try {\n this.socket.close(1000, 'transport closed');\n } catch {\n // ignore\n }\n this.onclose?.();\n }\n}\n"],"mappings":"AAWA,OAAO,eAAe;AAEtB,SAAS,4BAAiD;AAEnD,MAAM,mBAAwC;AAAA,EAQnD,YAA6B,QAAmB;AAAnB;AAAA,EAAoB;AAAA,EAApB;AAAA,EAP7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEQ,SAAS;AAAA,EAIjB,MAAM,QAAuB;AAC3B,SAAK,OAAO,GAAG,WAAW,CAAC,MAAM,aAAa;AAC5C,UAAI,UAAU;AACZ,aAAK,UAAU,IAAI,MAAM,iCAAiC,CAAC;AAC3D;AAAA,MACF;AACA,YAAM,OAAO,KAAK,SAAS,OAAO;AAClC,UAAI;AACJ,UAAI;AACF,iBAAS,KAAK,MAAM,IAAI;AAAA,MAC1B,SAAS,KAAK;AACZ,aAAK,UAAU,IAAI,MAAM,yBAA0B,IAAc,OAAO,EAAE,CAAC;AAC3E;AAAA,MACF;AACA,YAAM,SAAS,qBAAqB,UAAU,MAAM;AACpD,UAAI,CAAC,OAAO,SAAS;AACnB,aAAK,UAAU,IAAI,MAAM,+BAA+B,OAAO,MAAM,OAAO,EAAE,CAAC;AAC/E;AAAA,MACF;AACA,WAAK,YAAY,OAAO,IAAI;AAAA,IAC9B,CAAC;AAED,SAAK,OAAO,GAAG,SAAS,MAAM;AAC5B,UAAI,KAAK,OAAQ;AACjB,WAAK,SAAS;AACd,WAAK,UAAU;AAAA,IACjB,CAAC;AAED,SAAK,OAAO,GAAG,SAAS,SAAO;AAC7B,WAAK,UAAU,GAAG;AAAA,IACpB,CAAC;AAED,QAAI,KAAK,OAAO,eAAe,UAAU,YAAY;AACnD,YAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,aAAK,OAAO,KAAK,QAAQ,MAAM,QAAQ,CAAC;AACxC,aAAK,OAAO,KAAK,SAAS,MAAM;AAAA,MAClC,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,MAAM,KAAK,SAAwC;AACjD,QAAI,KAAK,OAAQ,OAAM,IAAI,MAAM,kBAAkB;AACnD,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,WAAK,OAAO,KAAK,KAAK,UAAU,OAAO,GAAG,SAAO;AAC/C,YAAI,IAAK,QAAO,GAAG;AAAA,YACd,SAAQ;AAAA,MACf,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,QAAuB;AAC3B,QAAI,KAAK,OAAQ;AACjB,SAAK,SAAS;AACd,QAAI;AACF,WAAK,OAAO,MAAM,KAAM,kBAAkB;AAAA,IAC5C,QAAQ;AAAA,IAER;AACA,SAAK,UAAU;AAAA,EACjB;AACF;","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 { AdcpError } from "./decisioning/async-outcome.mjs";
|
|
8
2
|
const MEDIA_BUY_TRANSITIONS = /* @__PURE__ */ new Map([
|
|
9
3
|
["pending_creatives", /* @__PURE__ */ new Set(["pending_start", "active", "paused", "canceled", "rejected"])],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/lib/server/state-machine.ts"],"sourcesContent":["/**\n * Canonical lifecycle graphs for resources whose status enums the spec\n * publishes. Single source of truth — the storyboard runner's\n * `status.monotonic` invariant in `testing/storyboard/default-invariants.ts`\n * imports the same maps, so a seller that enforces transitions with these\n * helpers cannot drift from the conformance-runner enforcement.\n *\n * Edges mirror the prose in `static/schemas/source/enums/<resource>-status.json`.\n * `active ↔ paused` (media buy), `approved ↔ archived` (creative), and the\n * `rejected → processing | pending_review` re-sync path are all explicit per\n * the spec; everything else is derived from the published lifecycle.\n *\n * Two surfaces per resource:\n * - `isLegalXTransition(from, to)` — boolean predicate. Use when you\n * want to wrap the failure in your own error type (test-controller\n * code, custom audit shapes).\n * - `assertXTransition(from, to)` — throws an `AdcpError` with the\n * spec-correct `code`. For media buys this is `NOT_CANCELLABLE` on\n * the re-cancel idempotency path and `INVALID_STATE` everywhere else.\n * Production seller code that handles the wire protocol directly\n * should use this — the framework projects `AdcpError` onto the\n * wire `adcp_error` envelope automatically.\n *\n * @public\n */\n\nimport type { CreativeStatus, MediaBuyStatus } from '../types/core.generated';\nimport { AdcpError } from './decisioning/async-outcome';\n\n/**\n * Legal `MediaBuyStatus` transitions per `media-buy-status.json`.\n *\n * `active ↔ paused` is reversible (buyer pauses, seller resumes).\n * `completed | rejected | canceled` are terminal — no outbound edges.\n *\n * `pending_start → rejected` is supported because the schema's prose\n * (`\"declined by the seller after creation\"`) does not exclude post-start\n * rejection. Storyboards do not exercise that edge today; sellers MAY\n * narrow it in their own enforcement if their upstream forbids it.\n */\nexport const MEDIA_BUY_TRANSITIONS: ReadonlyMap<MediaBuyStatus, ReadonlySet<MediaBuyStatus>> = new Map<\n MediaBuyStatus,\n ReadonlySet<MediaBuyStatus>\n>([\n ['pending_creatives', new Set(['pending_start', 'active', 'paused', 'canceled', 'rejected'])],\n ['pending_start', new Set(['active', 'paused', 'canceled', 'rejected'])],\n ['active', new Set(['paused', 'completed', 'canceled'])],\n ['paused', new Set(['active', 'completed', 'canceled'])],\n ['completed', new Set()],\n ['rejected', new Set()],\n ['canceled', new Set()],\n]);\n\n/**\n * Legal `CreativeStatus` transitions per `creative-status.json`.\n *\n * `processing → approved` is NOT a direct edge — the spec defines\n * `processing → pending_review | rejected`, and review is what produces\n * the `approved` state. `rejected → processing | pending_review` covers\n * the buyer's re-sync path (re-submit via `sync_creatives`). No states\n * are terminal — every state can recover via re-sync.\n */\nexport const CREATIVE_ASSET_TRANSITIONS: ReadonlyMap<CreativeStatus, ReadonlySet<CreativeStatus>> = new Map<\n CreativeStatus,\n ReadonlySet<CreativeStatus>\n>([\n ['processing', new Set(['pending_review', 'rejected'])],\n ['pending_review', new Set(['approved', 'rejected'])],\n ['approved', new Set(['archived', 'rejected'])],\n ['archived', new Set(['approved'])],\n ['rejected', new Set(['processing', 'pending_review'])],\n]);\n\n/** Boolean predicate for `MediaBuyStatus` transitions. Self-edges are illegal. */\nexport function isLegalMediaBuyTransition(from: MediaBuyStatus, to: MediaBuyStatus): boolean {\n return MEDIA_BUY_TRANSITIONS.get(from)?.has(to) ?? false;\n}\n\n/** Boolean predicate for `CreativeStatus` transitions. Self-edges are illegal. */\nexport function isLegalCreativeTransition(from: CreativeStatus, to: CreativeStatus): boolean {\n return CREATIVE_ASSET_TRANSITIONS.get(from)?.has(to) ?? false;\n}\n\n/**\n * Throws an `AdcpError` with the spec-correct error code for an illegal\n * `MediaBuyStatus` transition.\n *\n * Code selection per `compliance/cache/<version>/protocols/media-buy/state-machine.yaml`:\n * _\"The cancellation-specific code takes precedence over the generic\n * INVALID_STATE when the attempted action is cancel; non-cancel actions\n * targeting or out of terminal states still return INVALID_STATE.\"_\n *\n * - Any illegal transition where `to === 'canceled'` (i.e. the buyer was\n * attempting a cancel that the current state forbids — `completed →\n * canceled`, `rejected → canceled`, the double-cancel idempotency\n * `canceled → canceled`): `NOT_CANCELLABLE`.\n * - Any other illegal edge (terminal-state escapes like `canceled →\n * paused`, `completed → active`, lifecycle skips): `INVALID_STATE`.\n *\n * Both codes carry `recovery: 'correctable'` per the manifest in\n * `STANDARD_ERROR_CODES` — the buyer can adjust their request (re-fetch\n * current state, choose a different action) rather than escalating.\n */\nexport function assertMediaBuyTransition(from: MediaBuyStatus, to: MediaBuyStatus): void {\n if (isLegalMediaBuyTransition(from, to)) return;\n if (to === 'canceled') {\n throw new AdcpError('NOT_CANCELLABLE', {\n message: `Media buy in ${from} state cannot be canceled.`,\n field: 'status',\n });\n }\n throw new AdcpError('INVALID_STATE', {\n message: `Illegal MediaBuy transition: ${from} → ${to}.`,\n field: 'status',\n });\n}\n\n/**\n * Throws an `AdcpError('INVALID_STATE')` for an illegal `CreativeStatus`\n * transition. Creatives have no terminals — every state can recover via\n * re-sync — so there is no creative analogue to `NOT_CANCELLABLE`.\n */\nexport function assertCreativeTransition(from: CreativeStatus, to: CreativeStatus): void {\n if (isLegalCreativeTransition(from, to)) return;\n throw new AdcpError('INVALID_STATE', {\n message: `Illegal Creative transition: ${from} → ${to}.`,\n field: 'status',\n });\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/server/state-machine.ts"],"sourcesContent":["/**\n * Canonical lifecycle graphs for resources whose status enums the spec\n * publishes. Single source of truth — the storyboard runner's\n * `status.monotonic` invariant in `testing/storyboard/default-invariants.ts`\n * imports the same maps, so a seller that enforces transitions with these\n * helpers cannot drift from the conformance-runner enforcement.\n *\n * Edges mirror the prose in `static/schemas/source/enums/<resource>-status.json`.\n * `active ↔ paused` (media buy), `approved ↔ archived` (creative), and the\n * `rejected → processing | pending_review` re-sync path are all explicit per\n * the spec; everything else is derived from the published lifecycle.\n *\n * Two surfaces per resource:\n * - `isLegalXTransition(from, to)` — boolean predicate. Use when you\n * want to wrap the failure in your own error type (test-controller\n * code, custom audit shapes).\n * - `assertXTransition(from, to)` — throws an `AdcpError` with the\n * spec-correct `code`. For media buys this is `NOT_CANCELLABLE` on\n * the re-cancel idempotency path and `INVALID_STATE` everywhere else.\n * Production seller code that handles the wire protocol directly\n * should use this — the framework projects `AdcpError` onto the\n * wire `adcp_error` envelope automatically.\n *\n * @public\n */\n\nimport type { CreativeStatus, MediaBuyStatus } from '../types/core.generated';\nimport { AdcpError } from './decisioning/async-outcome';\n\n/**\n * Legal `MediaBuyStatus` transitions per `media-buy-status.json`.\n *\n * `active ↔ paused` is reversible (buyer pauses, seller resumes).\n * `completed | rejected | canceled` are terminal — no outbound edges.\n *\n * `pending_start → rejected` is supported because the schema's prose\n * (`\"declined by the seller after creation\"`) does not exclude post-start\n * rejection. Storyboards do not exercise that edge today; sellers MAY\n * narrow it in their own enforcement if their upstream forbids it.\n */\nexport const MEDIA_BUY_TRANSITIONS: ReadonlyMap<MediaBuyStatus, ReadonlySet<MediaBuyStatus>> = new Map<\n MediaBuyStatus,\n ReadonlySet<MediaBuyStatus>\n>([\n ['pending_creatives', new Set(['pending_start', 'active', 'paused', 'canceled', 'rejected'])],\n ['pending_start', new Set(['active', 'paused', 'canceled', 'rejected'])],\n ['active', new Set(['paused', 'completed', 'canceled'])],\n ['paused', new Set(['active', 'completed', 'canceled'])],\n ['completed', new Set()],\n ['rejected', new Set()],\n ['canceled', new Set()],\n]);\n\n/**\n * Legal `CreativeStatus` transitions per `creative-status.json`.\n *\n * `processing → approved` is NOT a direct edge — the spec defines\n * `processing → pending_review | rejected`, and review is what produces\n * the `approved` state. `rejected → processing | pending_review` covers\n * the buyer's re-sync path (re-submit via `sync_creatives`). No states\n * are terminal — every state can recover via re-sync.\n */\nexport const CREATIVE_ASSET_TRANSITIONS: ReadonlyMap<CreativeStatus, ReadonlySet<CreativeStatus>> = new Map<\n CreativeStatus,\n ReadonlySet<CreativeStatus>\n>([\n ['processing', new Set(['pending_review', 'rejected'])],\n ['pending_review', new Set(['approved', 'rejected'])],\n ['approved', new Set(['archived', 'rejected'])],\n ['archived', new Set(['approved'])],\n ['rejected', new Set(['processing', 'pending_review'])],\n]);\n\n/** Boolean predicate for `MediaBuyStatus` transitions. Self-edges are illegal. */\nexport function isLegalMediaBuyTransition(from: MediaBuyStatus, to: MediaBuyStatus): boolean {\n return MEDIA_BUY_TRANSITIONS.get(from)?.has(to) ?? false;\n}\n\n/** Boolean predicate for `CreativeStatus` transitions. Self-edges are illegal. */\nexport function isLegalCreativeTransition(from: CreativeStatus, to: CreativeStatus): boolean {\n return CREATIVE_ASSET_TRANSITIONS.get(from)?.has(to) ?? false;\n}\n\n/**\n * Throws an `AdcpError` with the spec-correct error code for an illegal\n * `MediaBuyStatus` transition.\n *\n * Code selection per `compliance/cache/<version>/protocols/media-buy/state-machine.yaml`:\n * _\"The cancellation-specific code takes precedence over the generic\n * INVALID_STATE when the attempted action is cancel; non-cancel actions\n * targeting or out of terminal states still return INVALID_STATE.\"_\n *\n * - Any illegal transition where `to === 'canceled'` (i.e. the buyer was\n * attempting a cancel that the current state forbids — `completed →\n * canceled`, `rejected → canceled`, the double-cancel idempotency\n * `canceled → canceled`): `NOT_CANCELLABLE`.\n * - Any other illegal edge (terminal-state escapes like `canceled →\n * paused`, `completed → active`, lifecycle skips): `INVALID_STATE`.\n *\n * Both codes carry `recovery: 'correctable'` per the manifest in\n * `STANDARD_ERROR_CODES` — the buyer can adjust their request (re-fetch\n * current state, choose a different action) rather than escalating.\n */\nexport function assertMediaBuyTransition(from: MediaBuyStatus, to: MediaBuyStatus): void {\n if (isLegalMediaBuyTransition(from, to)) return;\n if (to === 'canceled') {\n throw new AdcpError('NOT_CANCELLABLE', {\n message: `Media buy in ${from} state cannot be canceled.`,\n field: 'status',\n });\n }\n throw new AdcpError('INVALID_STATE', {\n message: `Illegal MediaBuy transition: ${from} → ${to}.`,\n field: 'status',\n });\n}\n\n/**\n * Throws an `AdcpError('INVALID_STATE')` for an illegal `CreativeStatus`\n * transition. Creatives have no terminals — every state can recover via\n * re-sync — so there is no creative analogue to `NOT_CANCELLABLE`.\n */\nexport function assertCreativeTransition(from: CreativeStatus, to: CreativeStatus): void {\n if (isLegalCreativeTransition(from, to)) return;\n throw new AdcpError('INVALID_STATE', {\n message: `Illegal Creative transition: ${from} → ${to}.`,\n field: 'status',\n });\n}\n"],"mappings":"AA2BA,SAAS,iBAAiB;AAanB,MAAM,wBAAkF,oBAAI,IAGjG;AAAA,EACA,CAAC,qBAAqB,oBAAI,IAAI,CAAC,iBAAiB,UAAU,UAAU,YAAY,UAAU,CAAC,CAAC;AAAA,EAC5F,CAAC,iBAAiB,oBAAI,IAAI,CAAC,UAAU,UAAU,YAAY,UAAU,CAAC,CAAC;AAAA,EACvE,CAAC,UAAU,oBAAI,IAAI,CAAC,UAAU,aAAa,UAAU,CAAC,CAAC;AAAA,EACvD,CAAC,UAAU,oBAAI,IAAI,CAAC,UAAU,aAAa,UAAU,CAAC,CAAC;AAAA,EACvD,CAAC,aAAa,oBAAI,IAAI,CAAC;AAAA,EACvB,CAAC,YAAY,oBAAI,IAAI,CAAC;AAAA,EACtB,CAAC,YAAY,oBAAI,IAAI,CAAC;AACxB,CAAC;AAWM,MAAM,6BAAuF,oBAAI,IAGtG;AAAA,EACA,CAAC,cAAc,oBAAI,IAAI,CAAC,kBAAkB,UAAU,CAAC,CAAC;AAAA,EACtD,CAAC,kBAAkB,oBAAI,IAAI,CAAC,YAAY,UAAU,CAAC,CAAC;AAAA,EACpD,CAAC,YAAY,oBAAI,IAAI,CAAC,YAAY,UAAU,CAAC,CAAC;AAAA,EAC9C,CAAC,YAAY,oBAAI,IAAI,CAAC,UAAU,CAAC,CAAC;AAAA,EAClC,CAAC,YAAY,oBAAI,IAAI,CAAC,cAAc,gBAAgB,CAAC,CAAC;AACxD,CAAC;AAGM,SAAS,0BAA0B,MAAsB,IAA6B;AAC3F,SAAO,sBAAsB,IAAI,IAAI,GAAG,IAAI,EAAE,KAAK;AACrD;AAGO,SAAS,0BAA0B,MAAsB,IAA6B;AAC3F,SAAO,2BAA2B,IAAI,IAAI,GAAG,IAAI,EAAE,KAAK;AAC1D;AAsBO,SAAS,yBAAyB,MAAsB,IAA0B;AACvF,MAAI,0BAA0B,MAAM,EAAE,EAAG;AACzC,MAAI,OAAO,YAAY;AACrB,UAAM,IAAI,UAAU,mBAAmB;AAAA,MACrC,SAAS,gBAAgB,IAAI;AAAA,MAC7B,OAAO;AAAA,IACT,CAAC;AAAA,EACH;AACA,QAAM,IAAI,UAAU,iBAAiB;AAAA,IACnC,SAAS,gCAAgC,IAAI,WAAM,EAAE;AAAA,IACrD,OAAO;AAAA,EACT,CAAC;AACH;AAOO,SAAS,yBAAyB,MAAsB,IAA0B;AACvF,MAAI,0BAA0B,MAAM,EAAE,EAAG;AACzC,QAAM,IAAI,UAAU,iBAAiB;AAAA,IACnC,SAAS,gCAAgC,IAAI,WAAM,EAAE;AAAA,IACrD,OAAO;AAAA,EACT,CAAC;AACH;","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 { ADCPError } from "../errors/index.mjs";
|
|
8
2
|
class StateError extends ADCPError {
|
|
9
3
|
code;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/lib/server/state-store.ts"],"sourcesContent":["/**\n * Pluggable state store for AdCP server domain objects.\n *\n * Sellers use this to persist media buys, accounts, creatives, and other\n * domain state across stateless HTTP requests. The store is a generic\n * document store keyed by collection + id.\n *\n * `createAdcpServer` passes the store to handlers via `ctx.store`.\n * The in-memory implementation works for development; use PostgresStateStore\n * for production.\n *\n * @example\n * ```typescript\n * import { createAdcpServer, InMemoryStateStore } from '@adcp/sdk/server/legacy/v5';\n *\n * const store = new InMemoryStateStore();\n *\n * const server = createAdcpServer({\n * name: 'My Publisher', version: '1.0.0',\n * stateStore: store,\n * mediaBuy: {\n * createMediaBuy: async (params, ctx) => {\n * const buy = { media_buy_id: `mb_${Date.now()}`, status: 'active', packages: [] };\n * await ctx.store.put('media_buys', buy.media_buy_id, buy);\n * return buy;\n * },\n * getMediaBuys: async (params, ctx) => {\n * const buys = await ctx.store.list('media_buys');\n * return { media_buys: buys.items };\n * },\n * },\n * });\n * ```\n */\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\nimport { ADCPError } from '../errors';\n\nexport type StateErrorCode = 'INVALID_COLLECTION' | 'INVALID_ID' | 'PAYLOAD_TOO_LARGE' | 'NOT_FOUND' | 'BACKEND_ERROR';\n\n/**\n * Typed error thrown by state-store implementations for validation, size-limit,\n * and backend failures. Extends {@link ADCPError} so callers can use the\n * standard `isADCPError` / `extractErrorInfo` helpers.\n */\nexport class StateError extends ADCPError {\n readonly code: StateErrorCode;\n\n constructor(code: StateErrorCode, message: string, details?: unknown) {\n super(message, details);\n this.code = code;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Validation\n// ---------------------------------------------------------------------------\n\n/** Max bytes (UTF-8) allowed per document. Overridable per-store via options. */\nexport const DEFAULT_MAX_DOCUMENT_BYTES = 5 * 1024 * 1024;\n\n/** 1–256 ASCII letters, digits, or any of `_ - . :`. Prose description is in error messages. */\nconst KEY_PATTERN = /^[A-Za-z0-9_.\\-:]{1,256}$/;\nconst KEY_DESCRIPTION = 'letters, digits, or `_ . - :` (1–256 chars)';\n\n/**\n * Stricter pattern for sessioned-store sessionKeys and ids — excludes `:` so\n * that `sessionKey + \"::\" + id` is unambiguous. Without this, `\"alice:\" + \"::\" + \"x\"`\n * would collide with `\"alice\" + \"::\" + \":x\"` and let tenants read each other's rows.\n */\nconst SESSION_KEY_PATTERN = /^[A-Za-z0-9_.\\-]{1,256}$/;\nconst SESSION_KEY_DESCRIPTION = 'letters, digits, or `_ . -` (1–256 chars; `:` is reserved for scope paths)';\n\n/** Clamp attacker-controlled input before interpolating into error messages or logs. */\nfunction safeForMessage(value: unknown): string {\n const raw = typeof value === 'string' ? value : String(value);\n return raw.slice(0, 64).replace(/[^\\x20-\\x7E]/g, '?');\n}\n\nfunction validateKey(kind: 'collection' | 'id', value: string): void {\n if (typeof value !== 'string' || !KEY_PATTERN.test(value)) {\n const code: StateErrorCode = kind === 'collection' ? 'INVALID_COLLECTION' : 'INVALID_ID';\n throw new StateError(code, `${kind} \"${safeForMessage(value)}\" is invalid. Use ${KEY_DESCRIPTION}.`);\n }\n}\n\nexport function validateCollection(collection: string): void {\n validateKey('collection', collection);\n}\n\nexport function validateId(id: string): void {\n validateKey('id', id);\n}\n\nexport function validatePayloadSize(data: unknown, maxBytes: number): void {\n measureAndCheckSize(data, maxBytes);\n}\n\n/**\n * Measure the serialized size of `data` and throw if it exceeds `maxBytes`.\n * Returns the serialized form so callers can pass it to `db.query` without re-stringifying.\n */\nfunction measureAndCheckSize(data: unknown, maxBytes: number): string {\n const serialized = JSON.stringify(data) ?? '';\n const bytes = Buffer.byteLength(serialized, 'utf8');\n if (bytes > maxBytes) {\n throw new StateError(\n 'PAYLOAD_TOO_LARGE',\n `Document is ${bytes} bytes; exceeds limit of ${maxBytes} bytes. Split the document or raise maxDocumentBytes.`\n );\n }\n return serialized;\n}\n\n/**\n * Validate collection + id + payload size. Returns the serialized payload so\n * callers (e.g., PostgresStateStore) can reuse it without re-stringifying.\n */\nexport function validateWrite(collection: string, id: string, data: unknown, maxBytes: number): string {\n validateKey('collection', collection);\n validateKey('id', id);\n return measureAndCheckSize(data, maxBytes);\n}\n\n// ---------------------------------------------------------------------------\n// Interface\n// ---------------------------------------------------------------------------\n\nexport interface ListOptions {\n /** Filter by field values (exact match). */\n filter?: Record<string, unknown>;\n /** Maximum items to return. */\n limit?: number;\n /** Cursor from a previous list call. */\n cursor?: string;\n}\n\nexport interface ListResult<T = Record<string, unknown>> {\n items: T[];\n nextCursor?: string;\n}\n\n/**\n * Document and its row version, returned by {@link AdcpStateStore.getWithVersion}.\n *\n * `version` is a monotonically increasing integer that starts at `1` on first\n * write and increments by `1` on every `put`/`patch`/`putIfMatch`. Use it with\n * `putIfMatch` to implement optimistic concurrency.\n */\nexport interface VersionedDocument<T = Record<string, unknown>> {\n data: T;\n version: number;\n}\n\n/**\n * Result of an optimistic-concurrency write. Split on `ok` — success gives you\n * the new row version; failure gives you the current version the store has on\n * disk (or `null` if the row doesn't exist) so you can re-read, re-compute, and retry.\n *\n * **Conflict semantics**: `currentVersion` is a best-effort snapshot of the row\n * at conflict-report time, which may be later than the exact moment the CAS\n * failed — another writer can slip in between the failed write and the follow-up\n * read. This is fine for the retry loop in {@link patchWithRetry} (which just\n * re-reads anyway). Direct callers using `currentVersion` for anything beyond\n * \"retry if it changed\" should account for that race.\n */\nexport type PutIfMatchResult = { ok: true; version: number } | { ok: false; currentVersion: number | null };\n\n/**\n * Generic document store for AdCP domain objects.\n *\n * Collections are logical groupings (e.g., 'media_buys', 'accounts', 'creatives').\n * Each document is identified by collection + id.\n */\nexport interface AdcpStateStore {\n /** Get a document by collection and id. Returns null if not found. */\n get<T extends Record<string, unknown> = Record<string, unknown>>(collection: string, id: string): Promise<T | null>;\n\n /** Create or replace a document (upsert semantics). */\n put(collection: string, id: string, data: Record<string, unknown>): Promise<void>;\n\n /**\n * Merge fields into an existing document. Creates the document if it doesn't exist.\n * Only top-level fields are merged — nested objects are replaced, not deep-merged.\n */\n patch(collection: string, id: string, partial: Record<string, unknown>): Promise<void>;\n\n /** Delete a document. Returns true if it existed. */\n delete(collection: string, id: string): Promise<boolean>;\n\n /** List documents in a collection with optional filtering and pagination. */\n list<T extends Record<string, unknown> = Record<string, unknown>>(\n collection: string,\n options?: ListOptions\n ): Promise<ListResult<T>>;\n\n /**\n * Convenience method for built-in stores: returns a session-scoped view.\n * Optional on the interface so custom `AdcpStateStore` implementations\n * aren't forced to implement it — callers who need the wrapper on any\n * store should use {@link scopedStore} instead, which handles the fallback.\n */\n scoped?(sessionKey: string): AdcpStateStore;\n\n /**\n * Read a document with its current row version. Returns `null` if the row\n * doesn't exist. Optional on the interface — custom stores that don't\n * track versions don't have to implement it. Use {@link patchWithRetry} to\n * guard against read-modify-write races without calling this directly.\n */\n getWithVersion?<T extends Record<string, unknown> = Record<string, unknown>>(\n collection: string,\n id: string\n ): Promise<VersionedDocument<T> | null>;\n\n /**\n * Atomic compare-and-swap write. Succeeds only if the row's current version\n * equals `expectedVersion` (or if the row doesn't exist and `expectedVersion`\n * is `null`). On conflict, returns the version the store actually has so the\n * caller can re-read and retry.\n *\n * Optional on the interface — custom stores that can't implement CAS don't\n * have to. Use {@link patchWithRetry} for the common \"update a field with\n * retry on conflict\" case.\n */\n putIfMatch?(\n collection: string,\n id: string,\n data: Record<string, unknown>,\n expectedVersion: number | null\n ): Promise<PutIfMatchResult>;\n}\n\n/**\n * Return a session-scoped view of any `AdcpStateStore`.\n *\n * Prefers `store.scoped(sessionKey)` when the store defines it; otherwise\n * wraps with {@link createSessionedStore}. Use this in SDK code and skills\n * so custom store implementations work without requiring them to implement\n * `scoped` themselves.\n *\n * ```ts\n * import { scopedStore } from '@adcp/sdk/server';\n *\n * const sessionStore = scopedStore(ctx.store, ctx.sessionKey!);\n * await sessionStore.put('media_buys', buyId, buy);\n * const { items } = await sessionStore.list('media_buys');\n * ```\n */\nexport function scopedStore(store: AdcpStateStore, sessionKey: string): AdcpStateStore {\n return store.scoped ? store.scoped(sessionKey) : createSessionedStore(store, sessionKey);\n}\n\n// ---------------------------------------------------------------------------\n// InMemoryStateStore\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_PAGE_SIZE = 50;\nconst MAX_PAGE_SIZE = 500;\n\nexport interface InMemoryStateStoreOptions {\n /** Max bytes per document. Defaults to {@link DEFAULT_MAX_DOCUMENT_BYTES}. */\n maxDocumentBytes?: number;\n}\n\ninterface VersionedRow {\n data: Record<string, unknown>;\n version: number;\n}\n\n/**\n * In-memory state store for development and testing.\n *\n * State is lost when the process restarts. Use PostgresStateStore for production.\n */\nexport class InMemoryStateStore implements AdcpStateStore {\n private collections = new Map<string, Map<string, VersionedRow>>();\n private readonly maxDocumentBytes: number;\n\n constructor(options?: InMemoryStateStoreOptions) {\n this.maxDocumentBytes = options?.maxDocumentBytes ?? DEFAULT_MAX_DOCUMENT_BYTES;\n }\n\n private getCollection(collection: string): Map<string, VersionedRow> {\n let col = this.collections.get(collection);\n if (!col) {\n col = new Map();\n this.collections.set(collection, col);\n }\n return col;\n }\n\n async get<T extends Record<string, unknown> = Record<string, unknown>>(\n collection: string,\n id: string\n ): Promise<T | null> {\n validateCollection(collection);\n validateId(id);\n const row = this.getCollection(collection).get(id);\n return row ? ({ ...row.data } as T) : null;\n }\n\n async getWithVersion<T extends Record<string, unknown> = Record<string, unknown>>(\n collection: string,\n id: string\n ): Promise<VersionedDocument<T> | null> {\n validateCollection(collection);\n validateId(id);\n const row = this.getCollection(collection).get(id);\n return row ? { data: { ...row.data } as T, version: row.version } : null;\n }\n\n async put(collection: string, id: string, data: Record<string, unknown>): Promise<void> {\n validateWrite(collection, id, data, this.maxDocumentBytes);\n const col = this.getCollection(collection);\n const existing = col.get(id);\n col.set(id, { data: { ...data }, version: (existing?.version ?? 0) + 1 });\n }\n\n async putIfMatch(\n collection: string,\n id: string,\n data: Record<string, unknown>,\n expectedVersion: number | null\n ): Promise<PutIfMatchResult> {\n validateWrite(collection, id, data, this.maxDocumentBytes);\n const col = this.getCollection(collection);\n const existing = col.get(id);\n const currentVersion = existing?.version ?? null;\n if (currentVersion !== expectedVersion) {\n return { ok: false, currentVersion };\n }\n const nextVersion = (existing?.version ?? 0) + 1;\n col.set(id, { data: { ...data }, version: nextVersion });\n return { ok: true, version: nextVersion };\n }\n\n async patch(collection: string, id: string, partial: Record<string, unknown>): Promise<void> {\n validateWrite(collection, id, partial, this.maxDocumentBytes);\n const col = this.getCollection(collection);\n const existing = col.get(id);\n col.set(id, {\n data: { ...(existing?.data ?? {}), ...partial },\n version: (existing?.version ?? 0) + 1,\n });\n }\n\n async delete(collection: string, id: string): Promise<boolean> {\n validateCollection(collection);\n validateId(id);\n return this.getCollection(collection).delete(id);\n }\n\n async list<T extends Record<string, unknown> = Record<string, unknown>>(\n collection: string,\n options?: ListOptions\n ): Promise<ListResult<T>> {\n validateCollection(collection);\n const col = this.getCollection(collection);\n\n let entries = [...col.entries()].map(([id, row]) => ({ id, data: row.data as T }));\n\n if (options?.filter) {\n for (const [key, value] of Object.entries(options.filter)) {\n entries = entries.filter(e => e.data[key] === value);\n }\n }\n\n if (options?.cursor) {\n const idx = entries.findIndex(e => e.id === options.cursor);\n if (idx >= 0) {\n entries = entries.slice(idx + 1);\n }\n }\n\n const limit = Math.min(options?.limit ?? DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE);\n const hasMore = entries.length > limit;\n entries = entries.slice(0, limit);\n\n const nextCursor = hasMore && entries.length > 0 ? entries[entries.length - 1]!.id : undefined;\n\n return { items: entries.map(e => ({ ...e.data })), nextCursor };\n }\n\n /** Clear all data. Useful in tests. */\n clear(): void {\n this.collections.clear();\n }\n\n /** Get the number of documents in a collection. */\n size(collection: string): number {\n return this.getCollection(collection).size;\n }\n\n scoped(sessionKey: string): AdcpStateStore {\n return createSessionedStore(this, sessionKey);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Sessioned store wrapper\n// ---------------------------------------------------------------------------\n\n/**\n * Marker field injected by {@link createSessionedStore} into every document\n * it writes. Sellers reading the underlying store directly will see it.\n */\nexport const SESSION_KEY_FIELD = '_session_key';\n\n/** Reserved scope-path separator. `:` is forbidden in sessionKeys and ids so `::` is unambiguous. */\nconst SESSION_ID_SEPARATOR = '::';\n\nfunction validateSessionPart(kind: 'sessionKey' | 'id', value: string): void {\n if (typeof value !== 'string' || !SESSION_KEY_PATTERN.test(value)) {\n throw new StateError(\n 'INVALID_ID',\n `${kind} \"${safeForMessage(value)}\" is invalid. Use ${SESSION_KEY_DESCRIPTION}.`\n );\n }\n}\n\nfunction stripSessionKey<T extends Record<string, unknown>>(doc: T | null): T | null {\n if (doc == null) return null;\n if (!(SESSION_KEY_FIELD in doc)) return doc;\n const { [SESSION_KEY_FIELD]: _ignored, ...rest } = doc as Record<string, unknown>;\n return rest as T;\n}\n\nfunction rejectReservedField(data: Record<string, unknown>): void {\n if (SESSION_KEY_FIELD in data) {\n throw new StateError(\n 'INVALID_ID',\n `Payload field \"${SESSION_KEY_FIELD}\" is reserved by the sessioned store. Rename it in your document.`\n );\n }\n}\n\n/**\n * Wrap an AdcpStateStore so every operation is isolated to `sessionKey`.\n *\n * - **ids** are prefixed with `${sessionKey}::` before hitting the underlying store.\n * - **writes** inject `_session_key: sessionKey` into the document so `list()` can filter on it.\n * - **reads/lists** strip `_session_key` before returning documents.\n * - **list filters** always AND-in `_session_key = sessionKey`.\n *\n * `sessionKey` and caller `id`s must match {@link SESSION_KEY_PATTERN} — no `:`\n * characters — so `${sessionKey}::${id}` is unambiguous. Payloads may not include\n * the reserved `_session_key` field.\n *\n * Use this when every handler's state is scoped to a tenant/brand/session,\n * so you don't have to thread `sessionKey` through every `put`/`list` call.\n */\nexport function createSessionedStore(inner: AdcpStateStore, sessionKey: string): AdcpStateStore {\n validateSessionPart('sessionKey', sessionKey);\n\n const prefix = (id: string) => {\n validateSessionPart('id', id);\n return `${sessionKey}${SESSION_ID_SEPARATOR}${id}`;\n };\n\n return {\n async get<T extends Record<string, unknown> = Record<string, unknown>>(\n collection: string,\n id: string\n ): Promise<T | null> {\n const doc = await inner.get<T>(collection, prefix(id));\n return stripSessionKey(doc);\n },\n\n async put(collection, id, data) {\n rejectReservedField(data);\n await inner.put(collection, prefix(id), { ...data, [SESSION_KEY_FIELD]: sessionKey });\n },\n\n async patch(collection, id, partial) {\n rejectReservedField(partial);\n await inner.patch(collection, prefix(id), { ...partial, [SESSION_KEY_FIELD]: sessionKey });\n },\n\n async delete(collection, id) {\n return inner.delete(collection, prefix(id));\n },\n\n async list<T extends Record<string, unknown> = Record<string, unknown>>(\n collection: string,\n options?: ListOptions\n ): Promise<ListResult<T>> {\n const mergedFilter = {\n ...(options?.filter ?? {}),\n [SESSION_KEY_FIELD]: sessionKey,\n };\n const { items, nextCursor } = await inner.list<T>(collection, {\n ...options,\n filter: mergedFilter,\n });\n const stripped = items.map(item => stripSessionKey(item) as T);\n return nextCursor !== undefined ? { items: stripped, nextCursor } : { items: stripped };\n },\n\n // Proxy optimistic-concurrency methods when the inner store supports them.\n // Capture method references at wrap time (bound to `inner`) so runtime\n // reassignment or `this`-sensitive implementations like PostgresStateStore\n // can't desync.\n ...(() => {\n const innerGetWithVersion = inner.getWithVersion?.bind(inner);\n const innerPutIfMatch = inner.putIfMatch?.bind(inner);\n return {\n ...(innerGetWithVersion && {\n async getWithVersion<T extends Record<string, unknown> = Record<string, unknown>>(\n collection: string,\n id: string\n ): Promise<VersionedDocument<T> | null> {\n const result = await innerGetWithVersion<T>(collection, prefix(id));\n if (result == null) return null;\n const stripped = stripSessionKey(result.data);\n return stripped == null ? null : { data: stripped as T, version: result.version };\n },\n }),\n ...(innerPutIfMatch && {\n async putIfMatch(\n collection: string,\n id: string,\n data: Record<string, unknown>,\n expectedVersion: number | null\n ): Promise<PutIfMatchResult> {\n rejectReservedField(data);\n return innerPutIfMatch(\n collection,\n prefix(id),\n { ...data, [SESSION_KEY_FIELD]: sessionKey },\n expectedVersion\n );\n },\n }),\n };\n })(),\n };\n}\n\n// ---------------------------------------------------------------------------\n// patchWithRetry helper\n// ---------------------------------------------------------------------------\n\nexport interface PatchWithRetryOptions {\n /** Max retry attempts on version conflict. Defaults to 5. */\n maxAttempts?: number;\n\n /**\n * Called to compute a millisecond delay before the next attempt. Receives the\n * attempt number that just failed (1-indexed). Defaults to jittered exponential\n * backoff: `Math.random() * (1 << attempt)` (0–2 ms after 1, 0–4 ms after 2, …).\n * Return `0` (or pass a function that does) to disable backoff.\n */\n backoffMs?: (attempt: number) => number;\n\n /**\n * When the row is deleted between the initial read and the write, `patchWithRetry`\n * normally aborts with `PatchConflictError` to avoid silently resurrecting a\n * deleted document. Set to `true` to treat the post-delete state as a fresh\n * insert (re-running `update(null)` and inserting the result).\n *\n * Default: `false`.\n */\n allowResurrection?: boolean;\n}\n\n// Cap the shift so `1 << attempt` can't overflow to 0 or flip sign at attempt >= 31.\n// 20 is plenty: 2^20 = ~1 second, and maxAttempts would be absurd long before then.\nconst defaultBackoffMs = (attempt: number) => Math.random() * Math.pow(2, Math.min(attempt, 20));\n\n/**\n * Error thrown when `patchWithRetry` exceeds `maxAttempts` consecutive version conflicts,\n * or when the row was deleted between the initial read and the write and\n * `allowResurrection` is not set.\n */\nexport class PatchConflictError extends ADCPError {\n readonly code = 'PATCH_CONFLICT';\n constructor(\n readonly collection: string,\n readonly id: string,\n readonly attempts: number,\n readonly reason: 'max_attempts_exceeded' | 'deleted_during_retry',\n readonly lastObservedVersion: number | null\n ) {\n super(\n reason === 'deleted_during_retry'\n ? `patchWithRetry(${collection}, ${id}): row was deleted between read and write. ` +\n `Pass {allowResurrection: true} if you want to re-insert it.`\n : `patchWithRetry(${collection}, ${id}) exhausted ${attempts} attempts after version conflicts ` +\n `(last observed version: ${lastObservedVersion ?? 'none'}). ` +\n `Either the row has too many concurrent writers (split it), or raise {maxAttempts: N}.`\n );\n }\n}\n\n/**\n * Type guard for the conflict arm of {@link PutIfMatchResult}. Useful for\n * narrowing in hand-rolled retry loops.\n *\n * ```ts\n * const result = await store.putIfMatch('col', 'x', data, version);\n * if (isPutIfMatchConflict(result)) {\n * // result.currentVersion is number | null here\n * }\n * ```\n */\nexport function isPutIfMatchConflict(result: PutIfMatchResult): result is { ok: false; currentVersion: number | null } {\n return result.ok === false;\n}\n\n/**\n * Read-compute-write loop that retries on version conflict, using `getWithVersion`\n * + `putIfMatch` under the hood. The right primitive for counter-like updates\n * where two handlers might read the same pre-state and otherwise lose one write.\n *\n * `update` receives the current document (or `null` if the row doesn't exist)\n * and returns the next document. Returning `null` aborts without writing.\n *\n * ```ts\n * await patchWithRetry(ctx.store, 'media_buys', 'mb_1', (current) => ({\n * ...(current ?? {}),\n * budget_spent: (current?.budget_spent ?? 0) + cost,\n * }));\n * ```\n *\n * Throws {@link PatchConflictError} after `maxAttempts` consecutive conflicts.\n * Throws {@link PatchConflictError} with `reason: 'deleted_during_retry'` if\n * the row is deleted between the initial read and the write (opt into\n * resurrection via `allowResurrection: true`).\n *\n * Requires the store to implement both `getWithVersion` and `putIfMatch`.\n *\n * **Mutation caveat**: always build a fresh object in `update` — never mutate\n * the `current` argument in place. In-memory stores hand out a shallow copy\n * (nested references shared across retries) while Postgres deserializes JSONB\n * fresh each time; the safe pattern (`{ ...current, field: newValue }`) works\n * on both.\n *\n * **Error propagation**: if `update` throws, the exception propagates\n * immediately and no retry happens.\n *\n * **Backoff**: default is jittered exponential (low single-digit ms). Override\n * with `options.backoffMs`. Return `0` to disable.\n *\n * **Size**: each retry re-reads the full document. Avoid on rows close to\n * `maxDocumentBytes` — split into per-entity rows instead.\n */\nexport async function patchWithRetry<T extends Record<string, unknown> = Record<string, unknown>>(\n store: AdcpStateStore,\n collection: string,\n id: string,\n update: (current: T | null) => T | null,\n options: PatchWithRetryOptions = {}\n): Promise<T | null> {\n if (!store.getWithVersion || !store.putIfMatch) {\n throw new StateError(\n 'BACKEND_ERROR',\n 'patchWithRetry requires a store with getWithVersion and putIfMatch. Use InMemoryStateStore or PostgresStateStore, or pass a custom store that implements both.'\n );\n }\n\n const maxAttempts = options.maxAttempts ?? 5;\n const backoffMs = options.backoffMs ?? defaultBackoffMs;\n const allowResurrection = options.allowResurrection ?? false;\n\n let sawExistingRow = false;\n let lastObservedVersion: number | null = null;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n const current = await store.getWithVersion<T>(collection, id);\n if (current) {\n sawExistingRow = true;\n lastObservedVersion = current.version;\n } else if (sawExistingRow && !allowResurrection) {\n throw new PatchConflictError(collection, id, attempt, 'deleted_during_retry', lastObservedVersion);\n }\n\n const nextData = update(current ? current.data : null);\n if (nextData === null) return null;\n\n const result = await store.putIfMatch(collection, id, nextData, current ? current.version : null);\n if (result.ok) return nextData;\n\n if (result.currentVersion !== null) lastObservedVersion = result.currentVersion;\n\n if (attempt < maxAttempts) {\n const delay = backoffMs(attempt);\n if (delay > 0) await new Promise(r => setTimeout(r, delay));\n }\n }\n throw new PatchConflictError(collection, id, maxAttempts, 'max_attempts_exceeded', lastObservedVersion);\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/server/state-store.ts"],"sourcesContent":["/**\n * Pluggable state store for AdCP server domain objects.\n *\n * Sellers use this to persist media buys, accounts, creatives, and other\n * domain state across stateless HTTP requests. The store is a generic\n * document store keyed by collection + id.\n *\n * `createAdcpServer` passes the store to handlers via `ctx.store`.\n * The in-memory implementation works for development; use PostgresStateStore\n * for production.\n *\n * @example\n * ```typescript\n * import { createAdcpServer, InMemoryStateStore } from '@adcp/sdk/server/legacy/v5';\n *\n * const store = new InMemoryStateStore();\n *\n * const server = createAdcpServer({\n * name: 'My Publisher', version: '1.0.0',\n * stateStore: store,\n * mediaBuy: {\n * createMediaBuy: async (params, ctx) => {\n * const buy = { media_buy_id: `mb_${Date.now()}`, status: 'active', packages: [] };\n * await ctx.store.put('media_buys', buy.media_buy_id, buy);\n * return buy;\n * },\n * getMediaBuys: async (params, ctx) => {\n * const buys = await ctx.store.list('media_buys');\n * return { media_buys: buys.items };\n * },\n * },\n * });\n * ```\n */\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\nimport { ADCPError } from '../errors';\n\nexport type StateErrorCode = 'INVALID_COLLECTION' | 'INVALID_ID' | 'PAYLOAD_TOO_LARGE' | 'NOT_FOUND' | 'BACKEND_ERROR';\n\n/**\n * Typed error thrown by state-store implementations for validation, size-limit,\n * and backend failures. Extends {@link ADCPError} so callers can use the\n * standard `isADCPError` / `extractErrorInfo` helpers.\n */\nexport class StateError extends ADCPError {\n readonly code: StateErrorCode;\n\n constructor(code: StateErrorCode, message: string, details?: unknown) {\n super(message, details);\n this.code = code;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Validation\n// ---------------------------------------------------------------------------\n\n/** Max bytes (UTF-8) allowed per document. Overridable per-store via options. */\nexport const DEFAULT_MAX_DOCUMENT_BYTES = 5 * 1024 * 1024;\n\n/** 1–256 ASCII letters, digits, or any of `_ - . :`. Prose description is in error messages. */\nconst KEY_PATTERN = /^[A-Za-z0-9_.\\-:]{1,256}$/;\nconst KEY_DESCRIPTION = 'letters, digits, or `_ . - :` (1–256 chars)';\n\n/**\n * Stricter pattern for sessioned-store sessionKeys and ids — excludes `:` so\n * that `sessionKey + \"::\" + id` is unambiguous. Without this, `\"alice:\" + \"::\" + \"x\"`\n * would collide with `\"alice\" + \"::\" + \":x\"` and let tenants read each other's rows.\n */\nconst SESSION_KEY_PATTERN = /^[A-Za-z0-9_.\\-]{1,256}$/;\nconst SESSION_KEY_DESCRIPTION = 'letters, digits, or `_ . -` (1–256 chars; `:` is reserved for scope paths)';\n\n/** Clamp attacker-controlled input before interpolating into error messages or logs. */\nfunction safeForMessage(value: unknown): string {\n const raw = typeof value === 'string' ? value : String(value);\n return raw.slice(0, 64).replace(/[^\\x20-\\x7E]/g, '?');\n}\n\nfunction validateKey(kind: 'collection' | 'id', value: string): void {\n if (typeof value !== 'string' || !KEY_PATTERN.test(value)) {\n const code: StateErrorCode = kind === 'collection' ? 'INVALID_COLLECTION' : 'INVALID_ID';\n throw new StateError(code, `${kind} \"${safeForMessage(value)}\" is invalid. Use ${KEY_DESCRIPTION}.`);\n }\n}\n\nexport function validateCollection(collection: string): void {\n validateKey('collection', collection);\n}\n\nexport function validateId(id: string): void {\n validateKey('id', id);\n}\n\nexport function validatePayloadSize(data: unknown, maxBytes: number): void {\n measureAndCheckSize(data, maxBytes);\n}\n\n/**\n * Measure the serialized size of `data` and throw if it exceeds `maxBytes`.\n * Returns the serialized form so callers can pass it to `db.query` without re-stringifying.\n */\nfunction measureAndCheckSize(data: unknown, maxBytes: number): string {\n const serialized = JSON.stringify(data) ?? '';\n const bytes = Buffer.byteLength(serialized, 'utf8');\n if (bytes > maxBytes) {\n throw new StateError(\n 'PAYLOAD_TOO_LARGE',\n `Document is ${bytes} bytes; exceeds limit of ${maxBytes} bytes. Split the document or raise maxDocumentBytes.`\n );\n }\n return serialized;\n}\n\n/**\n * Validate collection + id + payload size. Returns the serialized payload so\n * callers (e.g., PostgresStateStore) can reuse it without re-stringifying.\n */\nexport function validateWrite(collection: string, id: string, data: unknown, maxBytes: number): string {\n validateKey('collection', collection);\n validateKey('id', id);\n return measureAndCheckSize(data, maxBytes);\n}\n\n// ---------------------------------------------------------------------------\n// Interface\n// ---------------------------------------------------------------------------\n\nexport interface ListOptions {\n /** Filter by field values (exact match). */\n filter?: Record<string, unknown>;\n /** Maximum items to return. */\n limit?: number;\n /** Cursor from a previous list call. */\n cursor?: string;\n}\n\nexport interface ListResult<T = Record<string, unknown>> {\n items: T[];\n nextCursor?: string;\n}\n\n/**\n * Document and its row version, returned by {@link AdcpStateStore.getWithVersion}.\n *\n * `version` is a monotonically increasing integer that starts at `1` on first\n * write and increments by `1` on every `put`/`patch`/`putIfMatch`. Use it with\n * `putIfMatch` to implement optimistic concurrency.\n */\nexport interface VersionedDocument<T = Record<string, unknown>> {\n data: T;\n version: number;\n}\n\n/**\n * Result of an optimistic-concurrency write. Split on `ok` — success gives you\n * the new row version; failure gives you the current version the store has on\n * disk (or `null` if the row doesn't exist) so you can re-read, re-compute, and retry.\n *\n * **Conflict semantics**: `currentVersion` is a best-effort snapshot of the row\n * at conflict-report time, which may be later than the exact moment the CAS\n * failed — another writer can slip in between the failed write and the follow-up\n * read. This is fine for the retry loop in {@link patchWithRetry} (which just\n * re-reads anyway). Direct callers using `currentVersion` for anything beyond\n * \"retry if it changed\" should account for that race.\n */\nexport type PutIfMatchResult = { ok: true; version: number } | { ok: false; currentVersion: number | null };\n\n/**\n * Generic document store for AdCP domain objects.\n *\n * Collections are logical groupings (e.g., 'media_buys', 'accounts', 'creatives').\n * Each document is identified by collection + id.\n */\nexport interface AdcpStateStore {\n /** Get a document by collection and id. Returns null if not found. */\n get<T extends Record<string, unknown> = Record<string, unknown>>(collection: string, id: string): Promise<T | null>;\n\n /** Create or replace a document (upsert semantics). */\n put(collection: string, id: string, data: Record<string, unknown>): Promise<void>;\n\n /**\n * Merge fields into an existing document. Creates the document if it doesn't exist.\n * Only top-level fields are merged — nested objects are replaced, not deep-merged.\n */\n patch(collection: string, id: string, partial: Record<string, unknown>): Promise<void>;\n\n /** Delete a document. Returns true if it existed. */\n delete(collection: string, id: string): Promise<boolean>;\n\n /** List documents in a collection with optional filtering and pagination. */\n list<T extends Record<string, unknown> = Record<string, unknown>>(\n collection: string,\n options?: ListOptions\n ): Promise<ListResult<T>>;\n\n /**\n * Convenience method for built-in stores: returns a session-scoped view.\n * Optional on the interface so custom `AdcpStateStore` implementations\n * aren't forced to implement it — callers who need the wrapper on any\n * store should use {@link scopedStore} instead, which handles the fallback.\n */\n scoped?(sessionKey: string): AdcpStateStore;\n\n /**\n * Read a document with its current row version. Returns `null` if the row\n * doesn't exist. Optional on the interface — custom stores that don't\n * track versions don't have to implement it. Use {@link patchWithRetry} to\n * guard against read-modify-write races without calling this directly.\n */\n getWithVersion?<T extends Record<string, unknown> = Record<string, unknown>>(\n collection: string,\n id: string\n ): Promise<VersionedDocument<T> | null>;\n\n /**\n * Atomic compare-and-swap write. Succeeds only if the row's current version\n * equals `expectedVersion` (or if the row doesn't exist and `expectedVersion`\n * is `null`). On conflict, returns the version the store actually has so the\n * caller can re-read and retry.\n *\n * Optional on the interface — custom stores that can't implement CAS don't\n * have to. Use {@link patchWithRetry} for the common \"update a field with\n * retry on conflict\" case.\n */\n putIfMatch?(\n collection: string,\n id: string,\n data: Record<string, unknown>,\n expectedVersion: number | null\n ): Promise<PutIfMatchResult>;\n}\n\n/**\n * Return a session-scoped view of any `AdcpStateStore`.\n *\n * Prefers `store.scoped(sessionKey)` when the store defines it; otherwise\n * wraps with {@link createSessionedStore}. Use this in SDK code and skills\n * so custom store implementations work without requiring them to implement\n * `scoped` themselves.\n *\n * ```ts\n * import { scopedStore } from '@adcp/sdk/server';\n *\n * const sessionStore = scopedStore(ctx.store, ctx.sessionKey!);\n * await sessionStore.put('media_buys', buyId, buy);\n * const { items } = await sessionStore.list('media_buys');\n * ```\n */\nexport function scopedStore(store: AdcpStateStore, sessionKey: string): AdcpStateStore {\n return store.scoped ? store.scoped(sessionKey) : createSessionedStore(store, sessionKey);\n}\n\n// ---------------------------------------------------------------------------\n// InMemoryStateStore\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_PAGE_SIZE = 50;\nconst MAX_PAGE_SIZE = 500;\n\nexport interface InMemoryStateStoreOptions {\n /** Max bytes per document. Defaults to {@link DEFAULT_MAX_DOCUMENT_BYTES}. */\n maxDocumentBytes?: number;\n}\n\ninterface VersionedRow {\n data: Record<string, unknown>;\n version: number;\n}\n\n/**\n * In-memory state store for development and testing.\n *\n * State is lost when the process restarts. Use PostgresStateStore for production.\n */\nexport class InMemoryStateStore implements AdcpStateStore {\n private collections = new Map<string, Map<string, VersionedRow>>();\n private readonly maxDocumentBytes: number;\n\n constructor(options?: InMemoryStateStoreOptions) {\n this.maxDocumentBytes = options?.maxDocumentBytes ?? DEFAULT_MAX_DOCUMENT_BYTES;\n }\n\n private getCollection(collection: string): Map<string, VersionedRow> {\n let col = this.collections.get(collection);\n if (!col) {\n col = new Map();\n this.collections.set(collection, col);\n }\n return col;\n }\n\n async get<T extends Record<string, unknown> = Record<string, unknown>>(\n collection: string,\n id: string\n ): Promise<T | null> {\n validateCollection(collection);\n validateId(id);\n const row = this.getCollection(collection).get(id);\n return row ? ({ ...row.data } as T) : null;\n }\n\n async getWithVersion<T extends Record<string, unknown> = Record<string, unknown>>(\n collection: string,\n id: string\n ): Promise<VersionedDocument<T> | null> {\n validateCollection(collection);\n validateId(id);\n const row = this.getCollection(collection).get(id);\n return row ? { data: { ...row.data } as T, version: row.version } : null;\n }\n\n async put(collection: string, id: string, data: Record<string, unknown>): Promise<void> {\n validateWrite(collection, id, data, this.maxDocumentBytes);\n const col = this.getCollection(collection);\n const existing = col.get(id);\n col.set(id, { data: { ...data }, version: (existing?.version ?? 0) + 1 });\n }\n\n async putIfMatch(\n collection: string,\n id: string,\n data: Record<string, unknown>,\n expectedVersion: number | null\n ): Promise<PutIfMatchResult> {\n validateWrite(collection, id, data, this.maxDocumentBytes);\n const col = this.getCollection(collection);\n const existing = col.get(id);\n const currentVersion = existing?.version ?? null;\n if (currentVersion !== expectedVersion) {\n return { ok: false, currentVersion };\n }\n const nextVersion = (existing?.version ?? 0) + 1;\n col.set(id, { data: { ...data }, version: nextVersion });\n return { ok: true, version: nextVersion };\n }\n\n async patch(collection: string, id: string, partial: Record<string, unknown>): Promise<void> {\n validateWrite(collection, id, partial, this.maxDocumentBytes);\n const col = this.getCollection(collection);\n const existing = col.get(id);\n col.set(id, {\n data: { ...(existing?.data ?? {}), ...partial },\n version: (existing?.version ?? 0) + 1,\n });\n }\n\n async delete(collection: string, id: string): Promise<boolean> {\n validateCollection(collection);\n validateId(id);\n return this.getCollection(collection).delete(id);\n }\n\n async list<T extends Record<string, unknown> = Record<string, unknown>>(\n collection: string,\n options?: ListOptions\n ): Promise<ListResult<T>> {\n validateCollection(collection);\n const col = this.getCollection(collection);\n\n let entries = [...col.entries()].map(([id, row]) => ({ id, data: row.data as T }));\n\n if (options?.filter) {\n for (const [key, value] of Object.entries(options.filter)) {\n entries = entries.filter(e => e.data[key] === value);\n }\n }\n\n if (options?.cursor) {\n const idx = entries.findIndex(e => e.id === options.cursor);\n if (idx >= 0) {\n entries = entries.slice(idx + 1);\n }\n }\n\n const limit = Math.min(options?.limit ?? DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE);\n const hasMore = entries.length > limit;\n entries = entries.slice(0, limit);\n\n const nextCursor = hasMore && entries.length > 0 ? entries[entries.length - 1]!.id : undefined;\n\n return { items: entries.map(e => ({ ...e.data })), nextCursor };\n }\n\n /** Clear all data. Useful in tests. */\n clear(): void {\n this.collections.clear();\n }\n\n /** Get the number of documents in a collection. */\n size(collection: string): number {\n return this.getCollection(collection).size;\n }\n\n scoped(sessionKey: string): AdcpStateStore {\n return createSessionedStore(this, sessionKey);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Sessioned store wrapper\n// ---------------------------------------------------------------------------\n\n/**\n * Marker field injected by {@link createSessionedStore} into every document\n * it writes. Sellers reading the underlying store directly will see it.\n */\nexport const SESSION_KEY_FIELD = '_session_key';\n\n/** Reserved scope-path separator. `:` is forbidden in sessionKeys and ids so `::` is unambiguous. */\nconst SESSION_ID_SEPARATOR = '::';\n\nfunction validateSessionPart(kind: 'sessionKey' | 'id', value: string): void {\n if (typeof value !== 'string' || !SESSION_KEY_PATTERN.test(value)) {\n throw new StateError(\n 'INVALID_ID',\n `${kind} \"${safeForMessage(value)}\" is invalid. Use ${SESSION_KEY_DESCRIPTION}.`\n );\n }\n}\n\nfunction stripSessionKey<T extends Record<string, unknown>>(doc: T | null): T | null {\n if (doc == null) return null;\n if (!(SESSION_KEY_FIELD in doc)) return doc;\n const { [SESSION_KEY_FIELD]: _ignored, ...rest } = doc as Record<string, unknown>;\n return rest as T;\n}\n\nfunction rejectReservedField(data: Record<string, unknown>): void {\n if (SESSION_KEY_FIELD in data) {\n throw new StateError(\n 'INVALID_ID',\n `Payload field \"${SESSION_KEY_FIELD}\" is reserved by the sessioned store. Rename it in your document.`\n );\n }\n}\n\n/**\n * Wrap an AdcpStateStore so every operation is isolated to `sessionKey`.\n *\n * - **ids** are prefixed with `${sessionKey}::` before hitting the underlying store.\n * - **writes** inject `_session_key: sessionKey` into the document so `list()` can filter on it.\n * - **reads/lists** strip `_session_key` before returning documents.\n * - **list filters** always AND-in `_session_key = sessionKey`.\n *\n * `sessionKey` and caller `id`s must match {@link SESSION_KEY_PATTERN} — no `:`\n * characters — so `${sessionKey}::${id}` is unambiguous. Payloads may not include\n * the reserved `_session_key` field.\n *\n * Use this when every handler's state is scoped to a tenant/brand/session,\n * so you don't have to thread `sessionKey` through every `put`/`list` call.\n */\nexport function createSessionedStore(inner: AdcpStateStore, sessionKey: string): AdcpStateStore {\n validateSessionPart('sessionKey', sessionKey);\n\n const prefix = (id: string) => {\n validateSessionPart('id', id);\n return `${sessionKey}${SESSION_ID_SEPARATOR}${id}`;\n };\n\n return {\n async get<T extends Record<string, unknown> = Record<string, unknown>>(\n collection: string,\n id: string\n ): Promise<T | null> {\n const doc = await inner.get<T>(collection, prefix(id));\n return stripSessionKey(doc);\n },\n\n async put(collection, id, data) {\n rejectReservedField(data);\n await inner.put(collection, prefix(id), { ...data, [SESSION_KEY_FIELD]: sessionKey });\n },\n\n async patch(collection, id, partial) {\n rejectReservedField(partial);\n await inner.patch(collection, prefix(id), { ...partial, [SESSION_KEY_FIELD]: sessionKey });\n },\n\n async delete(collection, id) {\n return inner.delete(collection, prefix(id));\n },\n\n async list<T extends Record<string, unknown> = Record<string, unknown>>(\n collection: string,\n options?: ListOptions\n ): Promise<ListResult<T>> {\n const mergedFilter = {\n ...(options?.filter ?? {}),\n [SESSION_KEY_FIELD]: sessionKey,\n };\n const { items, nextCursor } = await inner.list<T>(collection, {\n ...options,\n filter: mergedFilter,\n });\n const stripped = items.map(item => stripSessionKey(item) as T);\n return nextCursor !== undefined ? { items: stripped, nextCursor } : { items: stripped };\n },\n\n // Proxy optimistic-concurrency methods when the inner store supports them.\n // Capture method references at wrap time (bound to `inner`) so runtime\n // reassignment or `this`-sensitive implementations like PostgresStateStore\n // can't desync.\n ...(() => {\n const innerGetWithVersion = inner.getWithVersion?.bind(inner);\n const innerPutIfMatch = inner.putIfMatch?.bind(inner);\n return {\n ...(innerGetWithVersion && {\n async getWithVersion<T extends Record<string, unknown> = Record<string, unknown>>(\n collection: string,\n id: string\n ): Promise<VersionedDocument<T> | null> {\n const result = await innerGetWithVersion<T>(collection, prefix(id));\n if (result == null) return null;\n const stripped = stripSessionKey(result.data);\n return stripped == null ? null : { data: stripped as T, version: result.version };\n },\n }),\n ...(innerPutIfMatch && {\n async putIfMatch(\n collection: string,\n id: string,\n data: Record<string, unknown>,\n expectedVersion: number | null\n ): Promise<PutIfMatchResult> {\n rejectReservedField(data);\n return innerPutIfMatch(\n collection,\n prefix(id),\n { ...data, [SESSION_KEY_FIELD]: sessionKey },\n expectedVersion\n );\n },\n }),\n };\n })(),\n };\n}\n\n// ---------------------------------------------------------------------------\n// patchWithRetry helper\n// ---------------------------------------------------------------------------\n\nexport interface PatchWithRetryOptions {\n /** Max retry attempts on version conflict. Defaults to 5. */\n maxAttempts?: number;\n\n /**\n * Called to compute a millisecond delay before the next attempt. Receives the\n * attempt number that just failed (1-indexed). Defaults to jittered exponential\n * backoff: `Math.random() * (1 << attempt)` (0–2 ms after 1, 0–4 ms after 2, …).\n * Return `0` (or pass a function that does) to disable backoff.\n */\n backoffMs?: (attempt: number) => number;\n\n /**\n * When the row is deleted between the initial read and the write, `patchWithRetry`\n * normally aborts with `PatchConflictError` to avoid silently resurrecting a\n * deleted document. Set to `true` to treat the post-delete state as a fresh\n * insert (re-running `update(null)` and inserting the result).\n *\n * Default: `false`.\n */\n allowResurrection?: boolean;\n}\n\n// Cap the shift so `1 << attempt` can't overflow to 0 or flip sign at attempt >= 31.\n// 20 is plenty: 2^20 = ~1 second, and maxAttempts would be absurd long before then.\nconst defaultBackoffMs = (attempt: number) => Math.random() * Math.pow(2, Math.min(attempt, 20));\n\n/**\n * Error thrown when `patchWithRetry` exceeds `maxAttempts` consecutive version conflicts,\n * or when the row was deleted between the initial read and the write and\n * `allowResurrection` is not set.\n */\nexport class PatchConflictError extends ADCPError {\n readonly code = 'PATCH_CONFLICT';\n constructor(\n readonly collection: string,\n readonly id: string,\n readonly attempts: number,\n readonly reason: 'max_attempts_exceeded' | 'deleted_during_retry',\n readonly lastObservedVersion: number | null\n ) {\n super(\n reason === 'deleted_during_retry'\n ? `patchWithRetry(${collection}, ${id}): row was deleted between read and write. ` +\n `Pass {allowResurrection: true} if you want to re-insert it.`\n : `patchWithRetry(${collection}, ${id}) exhausted ${attempts} attempts after version conflicts ` +\n `(last observed version: ${lastObservedVersion ?? 'none'}). ` +\n `Either the row has too many concurrent writers (split it), or raise {maxAttempts: N}.`\n );\n }\n}\n\n/**\n * Type guard for the conflict arm of {@link PutIfMatchResult}. Useful for\n * narrowing in hand-rolled retry loops.\n *\n * ```ts\n * const result = await store.putIfMatch('col', 'x', data, version);\n * if (isPutIfMatchConflict(result)) {\n * // result.currentVersion is number | null here\n * }\n * ```\n */\nexport function isPutIfMatchConflict(result: PutIfMatchResult): result is { ok: false; currentVersion: number | null } {\n return result.ok === false;\n}\n\n/**\n * Read-compute-write loop that retries on version conflict, using `getWithVersion`\n * + `putIfMatch` under the hood. The right primitive for counter-like updates\n * where two handlers might read the same pre-state and otherwise lose one write.\n *\n * `update` receives the current document (or `null` if the row doesn't exist)\n * and returns the next document. Returning `null` aborts without writing.\n *\n * ```ts\n * await patchWithRetry(ctx.store, 'media_buys', 'mb_1', (current) => ({\n * ...(current ?? {}),\n * budget_spent: (current?.budget_spent ?? 0) + cost,\n * }));\n * ```\n *\n * Throws {@link PatchConflictError} after `maxAttempts` consecutive conflicts.\n * Throws {@link PatchConflictError} with `reason: 'deleted_during_retry'` if\n * the row is deleted between the initial read and the write (opt into\n * resurrection via `allowResurrection: true`).\n *\n * Requires the store to implement both `getWithVersion` and `putIfMatch`.\n *\n * **Mutation caveat**: always build a fresh object in `update` — never mutate\n * the `current` argument in place. In-memory stores hand out a shallow copy\n * (nested references shared across retries) while Postgres deserializes JSONB\n * fresh each time; the safe pattern (`{ ...current, field: newValue }`) works\n * on both.\n *\n * **Error propagation**: if `update` throws, the exception propagates\n * immediately and no retry happens.\n *\n * **Backoff**: default is jittered exponential (low single-digit ms). Override\n * with `options.backoffMs`. Return `0` to disable.\n *\n * **Size**: each retry re-reads the full document. Avoid on rows close to\n * `maxDocumentBytes` — split into per-entity rows instead.\n */\nexport async function patchWithRetry<T extends Record<string, unknown> = Record<string, unknown>>(\n store: AdcpStateStore,\n collection: string,\n id: string,\n update: (current: T | null) => T | null,\n options: PatchWithRetryOptions = {}\n): Promise<T | null> {\n if (!store.getWithVersion || !store.putIfMatch) {\n throw new StateError(\n 'BACKEND_ERROR',\n 'patchWithRetry requires a store with getWithVersion and putIfMatch. Use InMemoryStateStore or PostgresStateStore, or pass a custom store that implements both.'\n );\n }\n\n const maxAttempts = options.maxAttempts ?? 5;\n const backoffMs = options.backoffMs ?? defaultBackoffMs;\n const allowResurrection = options.allowResurrection ?? false;\n\n let sawExistingRow = false;\n let lastObservedVersion: number | null = null;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n const current = await store.getWithVersion<T>(collection, id);\n if (current) {\n sawExistingRow = true;\n lastObservedVersion = current.version;\n } else if (sawExistingRow && !allowResurrection) {\n throw new PatchConflictError(collection, id, attempt, 'deleted_during_retry', lastObservedVersion);\n }\n\n const nextData = update(current ? current.data : null);\n if (nextData === null) return null;\n\n const result = await store.putIfMatch(collection, id, nextData, current ? current.version : null);\n if (result.ok) return nextData;\n\n if (result.currentVersion !== null) lastObservedVersion = result.currentVersion;\n\n if (attempt < maxAttempts) {\n const delay = backoffMs(attempt);\n if (delay > 0) await new Promise(r => setTimeout(r, delay));\n }\n }\n throw new PatchConflictError(collection, id, maxAttempts, 'max_attempts_exceeded', lastObservedVersion);\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
|
function isEnvelope(value) {
|
|
8
2
|
if (typeof value !== "object" || value === null) return false;
|
|
9
3
|
const o = value;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/lib/server/structured-serialize.ts"],"sourcesContent":["/**\n * Lossless serialization of rich JS types through {@link AdcpStateStore}.\n *\n * AdcpStateStore accepts `Record<string, unknown>` and round-trips through\n * JSON, which silently drops `Map`, `Set`, and converts `Date` to string.\n * Handlers that keep those types in memory can wrap reads and writes:\n *\n * ```ts\n * import { structuredSerialize, structuredDeserialize } from '@adcp/sdk/server';\n *\n * await ctx.store.put('sessions', id, structuredSerialize(session));\n * const session = structuredDeserialize(await ctx.store.get('sessions', id));\n * ```\n *\n * Tagged envelopes use the namespaced field `__adcpType` to avoid collision\n * with domain data. Unknown tag values pass through unchanged on deserialize,\n * so a future tag addition won't corrupt caller data that already uses\n * `__adcpType` for something else.\n */\n\ntype Primitive = string | number | boolean | null;\n\ninterface DateEnvelope {\n __adcpType: 'Date';\n value: string;\n}\n\ninterface MapEnvelope {\n __adcpType: 'Map';\n entries: [unknown, unknown][];\n}\n\ninterface SetEnvelope {\n __adcpType: 'Set';\n values: unknown[];\n}\n\ntype Envelope = DateEnvelope | MapEnvelope | SetEnvelope;\n\nfunction isEnvelope(value: unknown): value is Envelope {\n if (typeof value !== 'object' || value === null) return false;\n const o = value as Record<string, unknown>;\n switch (o.__adcpType) {\n case 'Date':\n return typeof o.value === 'string';\n case 'Map':\n return Array.isArray(o.entries);\n case 'Set':\n return Array.isArray(o.values);\n default:\n return false;\n }\n}\n\n/**\n * Walk `value` replacing `Map`, `Set`, and `Date` instances with JSON-safe\n * tagged envelopes. Plain objects and arrays recurse.\n */\nexport function structuredSerialize(value: unknown): unknown {\n if (value === null || value === undefined) return value;\n const t = typeof value;\n if (t === 'string' || t === 'number' || t === 'boolean') return value as Primitive;\n\n if (value instanceof Date) {\n return { __adcpType: 'Date', value: value.toISOString() } satisfies DateEnvelope;\n }\n\n if (value instanceof Map) {\n return {\n __adcpType: 'Map',\n entries: [...value.entries()].map(([k, v]) => [structuredSerialize(k), structuredSerialize(v)]),\n } satisfies MapEnvelope;\n }\n\n if (value instanceof Set) {\n return {\n __adcpType: 'Set',\n values: [...value.values()].map(v => structuredSerialize(v)),\n } satisfies SetEnvelope;\n }\n\n if (Array.isArray(value)) {\n return value.map(v => structuredSerialize(v));\n }\n\n if (t === 'object') {\n const out: Record<string, unknown> = {};\n for (const [key, v] of Object.entries(value as Record<string, unknown>)) {\n out[key] = structuredSerialize(v);\n }\n return out;\n }\n\n // Functions, symbols, bigints — drop silently (consistent with JSON.stringify).\n return undefined;\n}\n\n/**\n * Inverse of {@link structuredSerialize}. Walks `value` converting tagged\n * envelopes back into native `Map`, `Set`, and `Date` instances.\n */\nexport function structuredDeserialize(value: unknown): unknown {\n if (value === null || value === undefined) return value;\n const t = typeof value;\n if (t === 'string' || t === 'number' || t === 'boolean') return value;\n\n if (Array.isArray(value)) {\n return value.map(v => structuredDeserialize(v));\n }\n\n if (isEnvelope(value)) {\n switch (value.__adcpType) {\n case 'Date':\n return new Date(value.value);\n case 'Map':\n return new Map(value.entries.map(([k, v]) => [structuredDeserialize(k), structuredDeserialize(v)]));\n case 'Set':\n return new Set(value.values.map(v => structuredDeserialize(v)));\n }\n }\n\n if (t === 'object') {\n const out: Record<string, unknown> = {};\n for (const [key, v] of Object.entries(value as Record<string, unknown>)) {\n out[key] = structuredDeserialize(v);\n }\n return out;\n }\n\n return value;\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/server/structured-serialize.ts"],"sourcesContent":["/**\n * Lossless serialization of rich JS types through {@link AdcpStateStore}.\n *\n * AdcpStateStore accepts `Record<string, unknown>` and round-trips through\n * JSON, which silently drops `Map`, `Set`, and converts `Date` to string.\n * Handlers that keep those types in memory can wrap reads and writes:\n *\n * ```ts\n * import { structuredSerialize, structuredDeserialize } from '@adcp/sdk/server';\n *\n * await ctx.store.put('sessions', id, structuredSerialize(session));\n * const session = structuredDeserialize(await ctx.store.get('sessions', id));\n * ```\n *\n * Tagged envelopes use the namespaced field `__adcpType` to avoid collision\n * with domain data. Unknown tag values pass through unchanged on deserialize,\n * so a future tag addition won't corrupt caller data that already uses\n * `__adcpType` for something else.\n */\n\ntype Primitive = string | number | boolean | null;\n\ninterface DateEnvelope {\n __adcpType: 'Date';\n value: string;\n}\n\ninterface MapEnvelope {\n __adcpType: 'Map';\n entries: [unknown, unknown][];\n}\n\ninterface SetEnvelope {\n __adcpType: 'Set';\n values: unknown[];\n}\n\ntype Envelope = DateEnvelope | MapEnvelope | SetEnvelope;\n\nfunction isEnvelope(value: unknown): value is Envelope {\n if (typeof value !== 'object' || value === null) return false;\n const o = value as Record<string, unknown>;\n switch (o.__adcpType) {\n case 'Date':\n return typeof o.value === 'string';\n case 'Map':\n return Array.isArray(o.entries);\n case 'Set':\n return Array.isArray(o.values);\n default:\n return false;\n }\n}\n\n/**\n * Walk `value` replacing `Map`, `Set`, and `Date` instances with JSON-safe\n * tagged envelopes. Plain objects and arrays recurse.\n */\nexport function structuredSerialize(value: unknown): unknown {\n if (value === null || value === undefined) return value;\n const t = typeof value;\n if (t === 'string' || t === 'number' || t === 'boolean') return value as Primitive;\n\n if (value instanceof Date) {\n return { __adcpType: 'Date', value: value.toISOString() } satisfies DateEnvelope;\n }\n\n if (value instanceof Map) {\n return {\n __adcpType: 'Map',\n entries: [...value.entries()].map(([k, v]) => [structuredSerialize(k), structuredSerialize(v)]),\n } satisfies MapEnvelope;\n }\n\n if (value instanceof Set) {\n return {\n __adcpType: 'Set',\n values: [...value.values()].map(v => structuredSerialize(v)),\n } satisfies SetEnvelope;\n }\n\n if (Array.isArray(value)) {\n return value.map(v => structuredSerialize(v));\n }\n\n if (t === 'object') {\n const out: Record<string, unknown> = {};\n for (const [key, v] of Object.entries(value as Record<string, unknown>)) {\n out[key] = structuredSerialize(v);\n }\n return out;\n }\n\n // Functions, symbols, bigints — drop silently (consistent with JSON.stringify).\n return undefined;\n}\n\n/**\n * Inverse of {@link structuredSerialize}. Walks `value` converting tagged\n * envelopes back into native `Map`, `Set`, and `Date` instances.\n */\nexport function structuredDeserialize(value: unknown): unknown {\n if (value === null || value === undefined) return value;\n const t = typeof value;\n if (t === 'string' || t === 'number' || t === 'boolean') return value;\n\n if (Array.isArray(value)) {\n return value.map(v => structuredDeserialize(v));\n }\n\n if (isEnvelope(value)) {\n switch (value.__adcpType) {\n case 'Date':\n return new Date(value.value);\n case 'Map':\n return new Map(value.entries.map(([k, v]) => [structuredDeserialize(k), structuredDeserialize(v)]));\n case 'Set':\n return new Set(value.values.map(v => structuredDeserialize(v)));\n }\n }\n\n if (t === 'object') {\n const out: Record<string, unknown> = {};\n for (const [key, v] of Object.entries(value as Record<string, unknown>)) {\n out[key] = structuredDeserialize(v);\n }\n return out;\n }\n\n return value;\n}\n"],"mappings":"AAuCA,SAAS,WAAW,OAAmC;AACrD,MAAI,OAAO,UAAU,YAAY,UAAU,KAAM,QAAO;AACxD,QAAM,IAAI;AACV,UAAQ,EAAE,YAAY;AAAA,IACpB,KAAK;AACH,aAAO,OAAO,EAAE,UAAU;AAAA,IAC5B,KAAK;AACH,aAAO,MAAM,QAAQ,EAAE,OAAO;AAAA,IAChC,KAAK;AACH,aAAO,MAAM,QAAQ,EAAE,MAAM;AAAA,IAC/B;AACE,aAAO;AAAA,EACX;AACF;AAMO,SAAS,oBAAoB,OAAyB;AAC3D,MAAI,UAAU,QAAQ,UAAU,OAAW,QAAO;AAClD,QAAM,IAAI,OAAO;AACjB,MAAI,MAAM,YAAY,MAAM,YAAY,MAAM,UAAW,QAAO;AAEhE,MAAI,iBAAiB,MAAM;AACzB,WAAO,EAAE,YAAY,QAAQ,OAAO,MAAM,YAAY,EAAE;AAAA,EAC1D;AAEA,MAAI,iBAAiB,KAAK;AACxB,WAAO;AAAA,MACL,YAAY;AAAA,MACZ,SAAS,CAAC,GAAG,MAAM,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,oBAAoB,CAAC,GAAG,oBAAoB,CAAC,CAAC,CAAC;AAAA,IAChG;AAAA,EACF;AAEA,MAAI,iBAAiB,KAAK;AACxB,WAAO;AAAA,MACL,YAAY;AAAA,MACZ,QAAQ,CAAC,GAAG,MAAM,OAAO,CAAC,EAAE,IAAI,OAAK,oBAAoB,CAAC,CAAC;AAAA,IAC7D;AAAA,EACF;AAEA,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,WAAO,MAAM,IAAI,OAAK,oBAAoB,CAAC,CAAC;AAAA,EAC9C;AAEA,MAAI,MAAM,UAAU;AAClB,UAAM,MAA+B,CAAC;AACtC,eAAW,CAAC,KAAK,CAAC,KAAK,OAAO,QAAQ,KAAgC,GAAG;AACvE,UAAI,GAAG,IAAI,oBAAoB,CAAC;AAAA,IAClC;AACA,WAAO;AAAA,EACT;AAGA,SAAO;AACT;AAMO,SAAS,sBAAsB,OAAyB;AAC7D,MAAI,UAAU,QAAQ,UAAU,OAAW,QAAO;AAClD,QAAM,IAAI,OAAO;AACjB,MAAI,MAAM,YAAY,MAAM,YAAY,MAAM,UAAW,QAAO;AAEhE,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,WAAO,MAAM,IAAI,OAAK,sBAAsB,CAAC,CAAC;AAAA,EAChD;AAEA,MAAI,WAAW,KAAK,GAAG;AACrB,YAAQ,MAAM,YAAY;AAAA,MACxB,KAAK;AACH,eAAO,IAAI,KAAK,MAAM,KAAK;AAAA,MAC7B,KAAK;AACH,eAAO,IAAI,IAAI,MAAM,QAAQ,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,sBAAsB,CAAC,GAAG,sBAAsB,CAAC,CAAC,CAAC,CAAC;AAAA,MACpG,KAAK;AACH,eAAO,IAAI,IAAI,MAAM,OAAO,IAAI,OAAK,sBAAsB,CAAC,CAAC,CAAC;AAAA,IAClE;AAAA,EACF;AAEA,MAAI,MAAM,UAAU;AAClB,UAAM,MAA+B,CAAC;AACtC,eAAW,CAAC,KAAK,CAAC,KAAK,OAAO,QAAQ,KAAgC,GAAG;AACvE,UAAI,GAAG,IAAI,sBAAsB,CAAC;AAAA,IACpC;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;","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 { lookup as dnsLookup } from "node:dns/promises";
|
|
9
3
|
import { callMCPTool } from "../protocols/mcp.mjs";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/lib/server/targeting-helpers.ts"],"sourcesContent":["/**\n * Inventory list targeting helpers for AdCP sellers.\n *\n * AdCP 3.0 carries property and collection targeting as references to\n * externally-managed lists (`PropertyListReference`, `CollectionListReference`).\n * The seller resolves those references at serve time and filters its own\n * inventory against the resolved contents.\n *\n * These helpers let a seller handler do that in one line:\n *\n * @example\n * ```typescript\n * import { resolvePropertyList, matchesPropertyList } from '@adcp/sdk/server';\n *\n * if (targeting.property_list) {\n * const list = await resolvePropertyList(targeting.property_list);\n * inventory = inventory.filter(p => matchesPropertyList(p.identifier, list));\n * }\n * ```\n *\n * Resolution goes through MCP (`get_property_list` / `get_collection_list`) on\n * the referenced agent — the same contract a buyer agent uses to fetch the list.\n * Matching follows the identifier and distribution-id semantics defined by the\n * AdCP property and collection specs.\n */\n\nimport { createHash } from 'node:crypto';\nimport { lookup as dnsLookup } from 'node:dns/promises';\nimport { callMCPTool } from '../protocols/mcp';\nimport { isAlwaysBlocked, isPrivateIp } from '../net';\nimport { isInternalProbesAllowed } from '../utils/probe-policy';\nimport { createPinAndBindFetch } from './pin-and-bind-fetch';\nimport type {\n PropertyListReference,\n CollectionListReference,\n GetPropertyListResponse,\n GetCollectionListResponse,\n Identifier,\n} from '../types/tools.generated';\n\n// ---------------------------------------------------------------------------\n// Resolved list shapes\n// ---------------------------------------------------------------------------\n\nexport interface ResolvedPropertyList {\n listId: string;\n agentUrl: string;\n identifiers: Identifier[];\n cacheValidUntil?: string;\n}\n\n/** A collection entry as returned by get_collection_list. */\nexport type ResolvedCollection = NonNullable<GetCollectionListResponse['collections']>[number];\n\nexport interface ResolvedCollectionList {\n listId: string;\n agentUrl: string;\n collections: ResolvedCollection[];\n cacheValidUntil?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Resolved list cache\n// ---------------------------------------------------------------------------\n\nexport type ResolvedListKind = 'property' | 'collection';\nexport type ResolvedListCacheValue = ResolvedPropertyList | ResolvedCollectionList;\nexport const DEFAULT_RESOLVE_LIST_PAGE_SIZE = 1000;\n\nexport interface ResolvedListCacheEntry<TValue extends ResolvedListCacheValue = ResolvedListCacheValue> {\n value: TValue;\n expiresAtMs: number;\n}\n\nexport interface ResolvedListCache {\n get<TValue extends ResolvedListCacheValue>(key: string, nowMs?: number): TValue | undefined;\n set<TValue extends ResolvedListCacheValue>(key: string, entry: ResolvedListCacheEntry<TValue>): void;\n delete(key: string): boolean;\n clear(): void;\n}\n\nclass MemoryResolvedListCache implements ResolvedListCache {\n private readonly entries = new Map<string, ResolvedListCacheEntry>();\n\n get<TValue extends ResolvedListCacheValue>(key: string, nowMs = Date.now()): TValue | undefined {\n const entry = this.entries.get(key);\n if (!entry) return undefined;\n if (entry.expiresAtMs <= nowMs) {\n this.entries.delete(key);\n return undefined;\n }\n return cloneResolvedList(entry.value) as TValue;\n }\n\n set<TValue extends ResolvedListCacheValue>(key: string, entry: ResolvedListCacheEntry<TValue>): void {\n this.entries.set(key, {\n expiresAtMs: entry.expiresAtMs,\n value: cloneResolvedList(entry.value),\n });\n }\n\n delete(key: string): boolean {\n return this.entries.delete(key);\n }\n\n clear(): void {\n this.entries.clear();\n }\n}\n\nexport function createResolvedListCache(): ResolvedListCache {\n return new MemoryResolvedListCache();\n}\n\nexport const defaultResolvedListCache = createResolvedListCache();\n\nexport function clearDefaultResolvedListCache(): void {\n defaultResolvedListCache.clear();\n}\n\n// ---------------------------------------------------------------------------\n// resolvePropertyList / resolveCollectionList\n// ---------------------------------------------------------------------------\n\nexport type ResolveListCallTool = (\n agentUrl: string,\n toolName: 'get_property_list' | 'get_collection_list',\n args: Record<string, unknown>,\n authToken?: string\n) => Promise<unknown>;\n\nconst defaultResolveListFetchBase = createPinAndBindFetch();\nconst defaultResolveListFetch: typeof fetch = (input, init) =>\n defaultResolveListFetchBase(input, { ...init, redirect: 'manual' });\n\nconst defaultResolveListCallTool: ResolveListCallTool = (agentUrl, toolName, args, authToken) =>\n callMCPTool(agentUrl, toolName, args, authToken, [], undefined, undefined, defaultResolveListFetch);\n\nexport interface ResolveListOptions {\n /** Maximum page size when calling get_property_list / get_collection_list. Defaults to 1000. */\n pageSize?: number;\n /** Maximum number of pages to walk before treating pagination as unsafe. Defaults to 100. */\n maxPages?: number;\n /** Cache for resolved list contents. Defaults to the process-local SDK cache. Pass false to disable caching. */\n cache?: ResolvedListCache | false;\n /**\n * Tenant/principal/auth-context fingerprint for cache scoping when the list\n * reference does not carry an auth_token. Shared caching is disabled without\n * either auth_token or this explicit scope.\n */\n cacheScopeKey?: string;\n /** Clock hook for testing cache expiry. */\n now?: () => Date | number;\n /** MCP call hook for testing/custom transports. */\n callTool?: ResolveListCallTool;\n}\n\n/**\n * Resolve a PropertyListReference to the concrete identifiers it contains.\n *\n * Calls `get_property_list` on the referenced agent with `resolve: true`.\n * Honors `auth_token` on the reference if present.\n */\nexport async function resolvePropertyList(\n ref: PropertyListReference,\n options: ResolveListOptions = {}\n): Promise<ResolvedPropertyList> {\n const structurallyNormalizedRef = await normalizePropertyListReference(ref, { resolveDns: false });\n const cache = cacheForOptions(options);\n const nowMs = nowMsForOptions(options);\n const cacheKey = resolvedListCacheKey('property', structurallyNormalizedRef, options);\n const cached = cacheKey ? cache?.get<ResolvedPropertyList>(cacheKey, nowMs) : undefined;\n if (cached) return cached;\n\n const normalizedRef = options.callTool\n ? structurallyNormalizedRef\n : await normalizePropertyListReference(structurallyNormalizedRef, { resolveDns: false });\n const result = await fetchResolvedPropertyList(normalizedRef, options);\n cacheResolvedList(cache, cacheKey, result, nowMs);\n return result;\n}\n\nasync function fetchResolvedPropertyList(\n ref: PropertyListReference,\n options: ResolveListOptions\n): Promise<ResolvedPropertyList> {\n const callTool = options.callTool ?? defaultResolveListCallTool;\n const identifiers: Identifier[] = [];\n let listId: string;\n let cacheValidUntil: string | undefined;\n let missingCacheValidUntil = false;\n let cursor: string | undefined;\n\n for (let pageIndex = 0; pageIndex < maxPagesForOptions(options); pageIndex += 1) {\n const args = buildResolveListArgs(ref.list_id, pageSizeForOptions(options), cursor);\n\n const raw = (await callTool(ref.agent_url, 'get_property_list', args, ref.auth_token)) as Record<string, unknown>;\n\n if (!raw || typeof raw !== 'object' || !('list' in raw)) {\n throw new Error('property_list_invalid_response');\n }\n const response = raw as unknown as GetPropertyListResponse;\n listId = response.list.list_id;\n identifiers.push(...(response.identifiers ?? []));\n if (typeof response.cache_valid_until === 'string') {\n cacheValidUntil = earliestIsoTimestamp(cacheValidUntil, response.cache_valid_until);\n } else {\n missingCacheValidUntil = true;\n }\n\n const pagination = readPagination(response.pagination);\n if (pagination.invalid) {\n throw new Error('property_list_invalid_pagination');\n }\n if (!pagination.hasMore) {\n return {\n listId,\n agentUrl: ref.agent_url,\n identifiers,\n cacheValidUntil: missingCacheValidUntil ? undefined : cacheValidUntil,\n };\n }\n if (!pagination.cursor) {\n throw new Error('property_list_invalid_pagination');\n }\n cursor = pagination.cursor;\n }\n\n throw new Error('property_list_pagination_limit_exceeded');\n}\n\n/**\n * Resolve a CollectionListReference to the concrete collections it contains.\n *\n * Calls `get_collection_list` on the referenced agent with `resolve: true`.\n * Honors `auth_token` on the reference if present.\n */\nexport async function resolveCollectionList(\n ref: CollectionListReference,\n options: ResolveListOptions = {}\n): Promise<ResolvedCollectionList> {\n const structurallyNormalizedRef = await normalizeCollectionListReference(ref, { resolveDns: false });\n const cache = cacheForOptions(options);\n const nowMs = nowMsForOptions(options);\n const cacheKey = resolvedListCacheKey('collection', structurallyNormalizedRef, options);\n const cached = cacheKey ? cache?.get<ResolvedCollectionList>(cacheKey, nowMs) : undefined;\n if (cached) return cached;\n\n const normalizedRef = options.callTool\n ? structurallyNormalizedRef\n : await normalizeCollectionListReference(structurallyNormalizedRef, { resolveDns: false });\n const result = await fetchResolvedCollectionList(normalizedRef, options);\n cacheResolvedList(cache, cacheKey, result, nowMs);\n return result;\n}\n\nasync function fetchResolvedCollectionList(\n ref: CollectionListReference,\n options: ResolveListOptions\n): Promise<ResolvedCollectionList> {\n const callTool = options.callTool ?? defaultResolveListCallTool;\n const collections: ResolvedCollection[] = [];\n let listId: string;\n let cacheValidUntil: string | undefined;\n let missingCacheValidUntil = false;\n let cursor: string | undefined;\n\n for (let pageIndex = 0; pageIndex < maxPagesForOptions(options); pageIndex += 1) {\n const args = buildResolveListArgs(ref.list_id, pageSizeForOptions(options), cursor);\n\n const raw = (await callTool(ref.agent_url, 'get_collection_list', args, ref.auth_token)) as Record<string, unknown>;\n\n if (!raw || typeof raw !== 'object' || !('list' in raw)) {\n throw new Error('collection_list_invalid_response');\n }\n const response = raw as unknown as GetCollectionListResponse;\n listId = response.list.list_id;\n collections.push(...(response.collections ?? []));\n if (typeof response.cache_valid_until === 'string') {\n cacheValidUntil = earliestIsoTimestamp(cacheValidUntil, response.cache_valid_until);\n } else {\n missingCacheValidUntil = true;\n }\n\n const pagination = readPagination(response.pagination);\n if (pagination.invalid) {\n throw new Error('collection_list_invalid_pagination');\n }\n if (!pagination.hasMore) {\n return {\n listId,\n agentUrl: ref.agent_url,\n collections,\n cacheValidUntil: missingCacheValidUntil ? undefined : cacheValidUntil,\n };\n }\n if (!pagination.cursor) {\n throw new Error('collection_list_invalid_pagination');\n }\n cursor = pagination.cursor;\n }\n\n throw new Error('collection_list_pagination_limit_exceeded');\n}\n\nexport function resolvedListCacheKey(\n kind: ResolvedListKind,\n ref: Pick<PropertyListReference | CollectionListReference, 'agent_url' | 'list_id' | 'auth_token'>,\n options: Pick<ResolveListOptions, 'pageSize' | 'cacheScopeKey'> = {}\n): string | undefined {\n const scope = ref.auth_token ?? options.cacheScopeKey;\n if (!scope) return undefined;\n const material = JSON.stringify({\n kind,\n agent_url: ref.agent_url,\n list_id: ref.list_id,\n scope_sha256: sha256(scope),\n page_size: options.pageSize ?? DEFAULT_RESOLVE_LIST_PAGE_SIZE,\n resolve: true,\n });\n return `adcp-resolved-list:${sha256(material)}`;\n}\n\nasync function normalizePropertyListReference(\n ref: PropertyListReference,\n options: { resolveDns: boolean }\n): Promise<PropertyListReference> {\n return {\n ...ref,\n agent_url: await normalizeListAgentUrl(ref.agent_url, options),\n };\n}\n\nasync function normalizeCollectionListReference(\n ref: CollectionListReference,\n options: { resolveDns: boolean }\n): Promise<CollectionListReference> {\n return {\n ...ref,\n agent_url: await normalizeListAgentUrl(ref.agent_url, options),\n };\n}\n\nasync function normalizeListAgentUrl(raw: string, options: { resolveDns: boolean }): Promise<string> {\n let url: URL;\n try {\n url = new URL(raw);\n } catch {\n throw new Error('list_agent_url_invalid');\n }\n\n if (url.username || url.password || url.search || url.hash) {\n throw new Error('list_agent_url_malformed');\n }\n\n const allowInternal = isInternalProbesAllowed();\n if (url.protocol !== 'https:' && !(allowInternal && url.protocol === 'http:')) {\n throw new Error('list_agent_url_insecure');\n }\n\n const hostname = url.hostname.replace(/^\\[|\\]$/g, '');\n if (isAlwaysBlocked(hostname)) {\n throw new Error('list_agent_url_always_blocked');\n }\n if (!allowInternal && isPrivateIp(hostname)) {\n throw new Error('list_agent_url_private_address');\n }\n\n if (options.resolveDns) {\n let addresses: { address: string }[];\n try {\n addresses = await dnsLookup(hostname, { all: true });\n } catch {\n throw new Error('list_agent_url_dns_failed');\n }\n if (addresses.length === 0) {\n throw new Error('list_agent_url_dns_empty');\n }\n for (const { address } of addresses) {\n if (isAlwaysBlocked(address)) {\n throw new Error('list_agent_url_always_blocked');\n }\n if (!allowInternal && isPrivateIp(address)) {\n throw new Error('list_agent_url_private_address');\n }\n }\n }\n\n return url.toString();\n}\n\nfunction buildResolveListArgs(listId: string, pageSize: number, cursor: string | undefined) {\n const args: Record<string, unknown> = {\n list_id: listId,\n resolve: true,\n };\n const pagination: Record<string, unknown> = {};\n pagination.max_results = pageSize;\n if (cursor) pagination.cursor = cursor;\n if (Object.keys(pagination).length > 0) args.pagination = pagination;\n return args;\n}\n\nfunction cacheForOptions(options: ResolveListOptions): ResolvedListCache | undefined {\n if (options.callTool && options.cache === undefined) return undefined;\n return options.cache === false ? undefined : (options.cache ?? defaultResolvedListCache);\n}\n\nfunction cacheResolvedList(\n cache: ResolvedListCache | undefined,\n cacheKey: string | undefined,\n value: ResolvedListCacheValue,\n nowMs: number\n): void {\n if (!cache || !cacheKey || !value.cacheValidUntil) return;\n const expiresAtMs = Date.parse(value.cacheValidUntil);\n if (!Number.isFinite(expiresAtMs) || expiresAtMs <= nowMs) return;\n cache.set(cacheKey, { value, expiresAtMs });\n}\n\nfunction nowMsForOptions(options: ResolveListOptions): number {\n const now = options.now?.();\n if (now instanceof Date) return now.getTime();\n if (typeof now === 'number') return now;\n return Date.now();\n}\n\nfunction maxPagesForOptions(options: ResolveListOptions): number {\n return options.maxPages ?? 100;\n}\n\nfunction pageSizeForOptions(options: Pick<ResolveListOptions, 'pageSize'>): number {\n return options.pageSize ?? DEFAULT_RESOLVE_LIST_PAGE_SIZE;\n}\n\nfunction readPagination(value: unknown): { hasMore: boolean; cursor?: string; invalid: boolean } {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return { hasMore: false, invalid: false };\n const pagination = value as { has_more?: unknown; cursor?: unknown };\n const cursor = typeof pagination.cursor === 'string' && pagination.cursor.length > 0 ? pagination.cursor : undefined;\n const invalid =\n (pagination.has_more !== undefined && typeof pagination.has_more !== 'boolean') ||\n (cursor !== undefined && pagination.has_more !== true);\n return {\n hasMore: pagination.has_more === true,\n cursor,\n invalid,\n };\n}\n\nfunction earliestIsoTimestamp(current: string | undefined, next: string): string {\n if (!current) return next;\n const currentMs = Date.parse(current);\n const nextMs = Date.parse(next);\n if (!Number.isFinite(currentMs)) return next;\n if (!Number.isFinite(nextMs)) return current;\n return nextMs < currentMs ? next : current;\n}\n\nfunction cloneResolvedList<TValue extends ResolvedListCacheValue>(value: TValue): TValue {\n return structuredClone(value);\n}\n\nfunction sha256(value: string): string {\n return createHash('sha256').update(value, 'utf8').digest('hex');\n}\n\n// ---------------------------------------------------------------------------\n// matchesPropertyList / matchesCollectionList\n// ---------------------------------------------------------------------------\n\n/**\n * Return true if `property` matches any identifier in the resolved list.\n *\n * Domain matching rules (from the AdCP property spec):\n * - Exact match on the identifier value is always accepted.\n * - A `domain` entry of `example.com` also matches `www.example.com` and\n * `m.example.com` — the two canonical aliases for a base domain.\n * - A `domain` entry of `*.example.com` matches every subdomain but NOT the\n * base domain itself.\n * - Non-domain identifier types (ios_bundle, network_id, etc.) use exact\n * string match on both `type` and `value`.\n */\nexport function matchesPropertyList(property: Identifier, list: ResolvedPropertyList): boolean {\n return list.identifiers.some(entry => identifiersMatch(property, entry));\n}\n\nfunction identifiersMatch(property: Identifier, entry: Identifier): boolean {\n if (property.type !== entry.type) return false;\n if (property.type !== 'domain') {\n return property.value === entry.value;\n }\n return domainMatches(property.value, entry.value);\n}\n\nfunction domainMatches(candidate: string, pattern: string): boolean {\n const c = candidate.toLowerCase();\n const p = pattern.toLowerCase();\n if (c === p) return true;\n if (p.startsWith('*.')) {\n const suffix = p.slice(1); // \".example.com\"\n return c.endsWith(suffix) && c !== p.slice(2);\n }\n return c === `www.${p}` || c === `m.${p}`;\n}\n\n/**\n * Return true if `collection` matches any entry in the resolved list.\n *\n * Matching modes:\n * - Collections match when `collection_rid` is set on both sides and equal.\n * - Otherwise, they match when they share at least one `distribution_ids`\n * entry with the same `type` and `value`.\n *\n * A collection with no collection_rid and no distribution_ids cannot match —\n * the seller has nothing to match against.\n */\nexport function matchesCollectionList(\n collection: { collection_rid?: string; distribution_ids?: { type: string; value: string }[] },\n list: ResolvedCollectionList\n): boolean {\n return list.collections.some(entry => collectionsMatch(collection, entry));\n}\n\nfunction collectionsMatch(\n a: { collection_rid?: string; distribution_ids?: { type: string; value: string }[] },\n b: ResolvedCollection\n): boolean {\n if (a.collection_rid && b.collection_rid && a.collection_rid === b.collection_rid) {\n return true;\n }\n if (!a.distribution_ids?.length || !b.distribution_ids?.length) return false;\n return a.distribution_ids.some(aid =>\n b.distribution_ids!.some(bid => aid.type === bid.type && aid.value === bid.value)\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/server/targeting-helpers.ts"],"sourcesContent":["/**\n * Inventory list targeting helpers for AdCP sellers.\n *\n * AdCP 3.0 carries property and collection targeting as references to\n * externally-managed lists (`PropertyListReference`, `CollectionListReference`).\n * The seller resolves those references at serve time and filters its own\n * inventory against the resolved contents.\n *\n * These helpers let a seller handler do that in one line:\n *\n * @example\n * ```typescript\n * import { resolvePropertyList, matchesPropertyList } from '@adcp/sdk/server';\n *\n * if (targeting.property_list) {\n * const list = await resolvePropertyList(targeting.property_list);\n * inventory = inventory.filter(p => matchesPropertyList(p.identifier, list));\n * }\n * ```\n *\n * Resolution goes through MCP (`get_property_list` / `get_collection_list`) on\n * the referenced agent — the same contract a buyer agent uses to fetch the list.\n * Matching follows the identifier and distribution-id semantics defined by the\n * AdCP property and collection specs.\n */\n\nimport { createHash } from 'node:crypto';\nimport { lookup as dnsLookup } from 'node:dns/promises';\nimport { callMCPTool } from '../protocols/mcp';\nimport { isAlwaysBlocked, isPrivateIp } from '../net';\nimport { isInternalProbesAllowed } from '../utils/probe-policy';\nimport { createPinAndBindFetch } from './pin-and-bind-fetch';\nimport type {\n PropertyListReference,\n CollectionListReference,\n GetPropertyListResponse,\n GetCollectionListResponse,\n Identifier,\n} from '../types/tools.generated';\n\n// ---------------------------------------------------------------------------\n// Resolved list shapes\n// ---------------------------------------------------------------------------\n\nexport interface ResolvedPropertyList {\n listId: string;\n agentUrl: string;\n identifiers: Identifier[];\n cacheValidUntil?: string;\n}\n\n/** A collection entry as returned by get_collection_list. */\nexport type ResolvedCollection = NonNullable<GetCollectionListResponse['collections']>[number];\n\nexport interface ResolvedCollectionList {\n listId: string;\n agentUrl: string;\n collections: ResolvedCollection[];\n cacheValidUntil?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Resolved list cache\n// ---------------------------------------------------------------------------\n\nexport type ResolvedListKind = 'property' | 'collection';\nexport type ResolvedListCacheValue = ResolvedPropertyList | ResolvedCollectionList;\nexport const DEFAULT_RESOLVE_LIST_PAGE_SIZE = 1000;\n\nexport interface ResolvedListCacheEntry<TValue extends ResolvedListCacheValue = ResolvedListCacheValue> {\n value: TValue;\n expiresAtMs: number;\n}\n\nexport interface ResolvedListCache {\n get<TValue extends ResolvedListCacheValue>(key: string, nowMs?: number): TValue | undefined;\n set<TValue extends ResolvedListCacheValue>(key: string, entry: ResolvedListCacheEntry<TValue>): void;\n delete(key: string): boolean;\n clear(): void;\n}\n\nclass MemoryResolvedListCache implements ResolvedListCache {\n private readonly entries = new Map<string, ResolvedListCacheEntry>();\n\n get<TValue extends ResolvedListCacheValue>(key: string, nowMs = Date.now()): TValue | undefined {\n const entry = this.entries.get(key);\n if (!entry) return undefined;\n if (entry.expiresAtMs <= nowMs) {\n this.entries.delete(key);\n return undefined;\n }\n return cloneResolvedList(entry.value) as TValue;\n }\n\n set<TValue extends ResolvedListCacheValue>(key: string, entry: ResolvedListCacheEntry<TValue>): void {\n this.entries.set(key, {\n expiresAtMs: entry.expiresAtMs,\n value: cloneResolvedList(entry.value),\n });\n }\n\n delete(key: string): boolean {\n return this.entries.delete(key);\n }\n\n clear(): void {\n this.entries.clear();\n }\n}\n\nexport function createResolvedListCache(): ResolvedListCache {\n return new MemoryResolvedListCache();\n}\n\nexport const defaultResolvedListCache = createResolvedListCache();\n\nexport function clearDefaultResolvedListCache(): void {\n defaultResolvedListCache.clear();\n}\n\n// ---------------------------------------------------------------------------\n// resolvePropertyList / resolveCollectionList\n// ---------------------------------------------------------------------------\n\nexport type ResolveListCallTool = (\n agentUrl: string,\n toolName: 'get_property_list' | 'get_collection_list',\n args: Record<string, unknown>,\n authToken?: string\n) => Promise<unknown>;\n\nconst defaultResolveListFetchBase = createPinAndBindFetch();\nconst defaultResolveListFetch: typeof fetch = (input, init) =>\n defaultResolveListFetchBase(input, { ...init, redirect: 'manual' });\n\nconst defaultResolveListCallTool: ResolveListCallTool = (agentUrl, toolName, args, authToken) =>\n callMCPTool(agentUrl, toolName, args, authToken, [], undefined, undefined, defaultResolveListFetch);\n\nexport interface ResolveListOptions {\n /** Maximum page size when calling get_property_list / get_collection_list. Defaults to 1000. */\n pageSize?: number;\n /** Maximum number of pages to walk before treating pagination as unsafe. Defaults to 100. */\n maxPages?: number;\n /** Cache for resolved list contents. Defaults to the process-local SDK cache. Pass false to disable caching. */\n cache?: ResolvedListCache | false;\n /**\n * Tenant/principal/auth-context fingerprint for cache scoping when the list\n * reference does not carry an auth_token. Shared caching is disabled without\n * either auth_token or this explicit scope.\n */\n cacheScopeKey?: string;\n /** Clock hook for testing cache expiry. */\n now?: () => Date | number;\n /** MCP call hook for testing/custom transports. */\n callTool?: ResolveListCallTool;\n}\n\n/**\n * Resolve a PropertyListReference to the concrete identifiers it contains.\n *\n * Calls `get_property_list` on the referenced agent with `resolve: true`.\n * Honors `auth_token` on the reference if present.\n */\nexport async function resolvePropertyList(\n ref: PropertyListReference,\n options: ResolveListOptions = {}\n): Promise<ResolvedPropertyList> {\n const structurallyNormalizedRef = await normalizePropertyListReference(ref, { resolveDns: false });\n const cache = cacheForOptions(options);\n const nowMs = nowMsForOptions(options);\n const cacheKey = resolvedListCacheKey('property', structurallyNormalizedRef, options);\n const cached = cacheKey ? cache?.get<ResolvedPropertyList>(cacheKey, nowMs) : undefined;\n if (cached) return cached;\n\n const normalizedRef = options.callTool\n ? structurallyNormalizedRef\n : await normalizePropertyListReference(structurallyNormalizedRef, { resolveDns: false });\n const result = await fetchResolvedPropertyList(normalizedRef, options);\n cacheResolvedList(cache, cacheKey, result, nowMs);\n return result;\n}\n\nasync function fetchResolvedPropertyList(\n ref: PropertyListReference,\n options: ResolveListOptions\n): Promise<ResolvedPropertyList> {\n const callTool = options.callTool ?? defaultResolveListCallTool;\n const identifiers: Identifier[] = [];\n let listId: string;\n let cacheValidUntil: string | undefined;\n let missingCacheValidUntil = false;\n let cursor: string | undefined;\n\n for (let pageIndex = 0; pageIndex < maxPagesForOptions(options); pageIndex += 1) {\n const args = buildResolveListArgs(ref.list_id, pageSizeForOptions(options), cursor);\n\n const raw = (await callTool(ref.agent_url, 'get_property_list', args, ref.auth_token)) as Record<string, unknown>;\n\n if (!raw || typeof raw !== 'object' || !('list' in raw)) {\n throw new Error('property_list_invalid_response');\n }\n const response = raw as unknown as GetPropertyListResponse;\n listId = response.list.list_id;\n identifiers.push(...(response.identifiers ?? []));\n if (typeof response.cache_valid_until === 'string') {\n cacheValidUntil = earliestIsoTimestamp(cacheValidUntil, response.cache_valid_until);\n } else {\n missingCacheValidUntil = true;\n }\n\n const pagination = readPagination(response.pagination);\n if (pagination.invalid) {\n throw new Error('property_list_invalid_pagination');\n }\n if (!pagination.hasMore) {\n return {\n listId,\n agentUrl: ref.agent_url,\n identifiers,\n cacheValidUntil: missingCacheValidUntil ? undefined : cacheValidUntil,\n };\n }\n if (!pagination.cursor) {\n throw new Error('property_list_invalid_pagination');\n }\n cursor = pagination.cursor;\n }\n\n throw new Error('property_list_pagination_limit_exceeded');\n}\n\n/**\n * Resolve a CollectionListReference to the concrete collections it contains.\n *\n * Calls `get_collection_list` on the referenced agent with `resolve: true`.\n * Honors `auth_token` on the reference if present.\n */\nexport async function resolveCollectionList(\n ref: CollectionListReference,\n options: ResolveListOptions = {}\n): Promise<ResolvedCollectionList> {\n const structurallyNormalizedRef = await normalizeCollectionListReference(ref, { resolveDns: false });\n const cache = cacheForOptions(options);\n const nowMs = nowMsForOptions(options);\n const cacheKey = resolvedListCacheKey('collection', structurallyNormalizedRef, options);\n const cached = cacheKey ? cache?.get<ResolvedCollectionList>(cacheKey, nowMs) : undefined;\n if (cached) return cached;\n\n const normalizedRef = options.callTool\n ? structurallyNormalizedRef\n : await normalizeCollectionListReference(structurallyNormalizedRef, { resolveDns: false });\n const result = await fetchResolvedCollectionList(normalizedRef, options);\n cacheResolvedList(cache, cacheKey, result, nowMs);\n return result;\n}\n\nasync function fetchResolvedCollectionList(\n ref: CollectionListReference,\n options: ResolveListOptions\n): Promise<ResolvedCollectionList> {\n const callTool = options.callTool ?? defaultResolveListCallTool;\n const collections: ResolvedCollection[] = [];\n let listId: string;\n let cacheValidUntil: string | undefined;\n let missingCacheValidUntil = false;\n let cursor: string | undefined;\n\n for (let pageIndex = 0; pageIndex < maxPagesForOptions(options); pageIndex += 1) {\n const args = buildResolveListArgs(ref.list_id, pageSizeForOptions(options), cursor);\n\n const raw = (await callTool(ref.agent_url, 'get_collection_list', args, ref.auth_token)) as Record<string, unknown>;\n\n if (!raw || typeof raw !== 'object' || !('list' in raw)) {\n throw new Error('collection_list_invalid_response');\n }\n const response = raw as unknown as GetCollectionListResponse;\n listId = response.list.list_id;\n collections.push(...(response.collections ?? []));\n if (typeof response.cache_valid_until === 'string') {\n cacheValidUntil = earliestIsoTimestamp(cacheValidUntil, response.cache_valid_until);\n } else {\n missingCacheValidUntil = true;\n }\n\n const pagination = readPagination(response.pagination);\n if (pagination.invalid) {\n throw new Error('collection_list_invalid_pagination');\n }\n if (!pagination.hasMore) {\n return {\n listId,\n agentUrl: ref.agent_url,\n collections,\n cacheValidUntil: missingCacheValidUntil ? undefined : cacheValidUntil,\n };\n }\n if (!pagination.cursor) {\n throw new Error('collection_list_invalid_pagination');\n }\n cursor = pagination.cursor;\n }\n\n throw new Error('collection_list_pagination_limit_exceeded');\n}\n\nexport function resolvedListCacheKey(\n kind: ResolvedListKind,\n ref: Pick<PropertyListReference | CollectionListReference, 'agent_url' | 'list_id' | 'auth_token'>,\n options: Pick<ResolveListOptions, 'pageSize' | 'cacheScopeKey'> = {}\n): string | undefined {\n const scope = ref.auth_token ?? options.cacheScopeKey;\n if (!scope) return undefined;\n const material = JSON.stringify({\n kind,\n agent_url: ref.agent_url,\n list_id: ref.list_id,\n scope_sha256: sha256(scope),\n page_size: options.pageSize ?? DEFAULT_RESOLVE_LIST_PAGE_SIZE,\n resolve: true,\n });\n return `adcp-resolved-list:${sha256(material)}`;\n}\n\nasync function normalizePropertyListReference(\n ref: PropertyListReference,\n options: { resolveDns: boolean }\n): Promise<PropertyListReference> {\n return {\n ...ref,\n agent_url: await normalizeListAgentUrl(ref.agent_url, options),\n };\n}\n\nasync function normalizeCollectionListReference(\n ref: CollectionListReference,\n options: { resolveDns: boolean }\n): Promise<CollectionListReference> {\n return {\n ...ref,\n agent_url: await normalizeListAgentUrl(ref.agent_url, options),\n };\n}\n\nasync function normalizeListAgentUrl(raw: string, options: { resolveDns: boolean }): Promise<string> {\n let url: URL;\n try {\n url = new URL(raw);\n } catch {\n throw new Error('list_agent_url_invalid');\n }\n\n if (url.username || url.password || url.search || url.hash) {\n throw new Error('list_agent_url_malformed');\n }\n\n const allowInternal = isInternalProbesAllowed();\n if (url.protocol !== 'https:' && !(allowInternal && url.protocol === 'http:')) {\n throw new Error('list_agent_url_insecure');\n }\n\n const hostname = url.hostname.replace(/^\\[|\\]$/g, '');\n if (isAlwaysBlocked(hostname)) {\n throw new Error('list_agent_url_always_blocked');\n }\n if (!allowInternal && isPrivateIp(hostname)) {\n throw new Error('list_agent_url_private_address');\n }\n\n if (options.resolveDns) {\n let addresses: { address: string }[];\n try {\n addresses = await dnsLookup(hostname, { all: true });\n } catch {\n throw new Error('list_agent_url_dns_failed');\n }\n if (addresses.length === 0) {\n throw new Error('list_agent_url_dns_empty');\n }\n for (const { address } of addresses) {\n if (isAlwaysBlocked(address)) {\n throw new Error('list_agent_url_always_blocked');\n }\n if (!allowInternal && isPrivateIp(address)) {\n throw new Error('list_agent_url_private_address');\n }\n }\n }\n\n return url.toString();\n}\n\nfunction buildResolveListArgs(listId: string, pageSize: number, cursor: string | undefined) {\n const args: Record<string, unknown> = {\n list_id: listId,\n resolve: true,\n };\n const pagination: Record<string, unknown> = {};\n pagination.max_results = pageSize;\n if (cursor) pagination.cursor = cursor;\n if (Object.keys(pagination).length > 0) args.pagination = pagination;\n return args;\n}\n\nfunction cacheForOptions(options: ResolveListOptions): ResolvedListCache | undefined {\n if (options.callTool && options.cache === undefined) return undefined;\n return options.cache === false ? undefined : (options.cache ?? defaultResolvedListCache);\n}\n\nfunction cacheResolvedList(\n cache: ResolvedListCache | undefined,\n cacheKey: string | undefined,\n value: ResolvedListCacheValue,\n nowMs: number\n): void {\n if (!cache || !cacheKey || !value.cacheValidUntil) return;\n const expiresAtMs = Date.parse(value.cacheValidUntil);\n if (!Number.isFinite(expiresAtMs) || expiresAtMs <= nowMs) return;\n cache.set(cacheKey, { value, expiresAtMs });\n}\n\nfunction nowMsForOptions(options: ResolveListOptions): number {\n const now = options.now?.();\n if (now instanceof Date) return now.getTime();\n if (typeof now === 'number') return now;\n return Date.now();\n}\n\nfunction maxPagesForOptions(options: ResolveListOptions): number {\n return options.maxPages ?? 100;\n}\n\nfunction pageSizeForOptions(options: Pick<ResolveListOptions, 'pageSize'>): number {\n return options.pageSize ?? DEFAULT_RESOLVE_LIST_PAGE_SIZE;\n}\n\nfunction readPagination(value: unknown): { hasMore: boolean; cursor?: string; invalid: boolean } {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return { hasMore: false, invalid: false };\n const pagination = value as { has_more?: unknown; cursor?: unknown };\n const cursor = typeof pagination.cursor === 'string' && pagination.cursor.length > 0 ? pagination.cursor : undefined;\n const invalid =\n (pagination.has_more !== undefined && typeof pagination.has_more !== 'boolean') ||\n (cursor !== undefined && pagination.has_more !== true);\n return {\n hasMore: pagination.has_more === true,\n cursor,\n invalid,\n };\n}\n\nfunction earliestIsoTimestamp(current: string | undefined, next: string): string {\n if (!current) return next;\n const currentMs = Date.parse(current);\n const nextMs = Date.parse(next);\n if (!Number.isFinite(currentMs)) return next;\n if (!Number.isFinite(nextMs)) return current;\n return nextMs < currentMs ? next : current;\n}\n\nfunction cloneResolvedList<TValue extends ResolvedListCacheValue>(value: TValue): TValue {\n return structuredClone(value);\n}\n\nfunction sha256(value: string): string {\n return createHash('sha256').update(value, 'utf8').digest('hex');\n}\n\n// ---------------------------------------------------------------------------\n// matchesPropertyList / matchesCollectionList\n// ---------------------------------------------------------------------------\n\n/**\n * Return true if `property` matches any identifier in the resolved list.\n *\n * Domain matching rules (from the AdCP property spec):\n * - Exact match on the identifier value is always accepted.\n * - A `domain` entry of `example.com` also matches `www.example.com` and\n * `m.example.com` — the two canonical aliases for a base domain.\n * - A `domain` entry of `*.example.com` matches every subdomain but NOT the\n * base domain itself.\n * - Non-domain identifier types (ios_bundle, network_id, etc.) use exact\n * string match on both `type` and `value`.\n */\nexport function matchesPropertyList(property: Identifier, list: ResolvedPropertyList): boolean {\n return list.identifiers.some(entry => identifiersMatch(property, entry));\n}\n\nfunction identifiersMatch(property: Identifier, entry: Identifier): boolean {\n if (property.type !== entry.type) return false;\n if (property.type !== 'domain') {\n return property.value === entry.value;\n }\n return domainMatches(property.value, entry.value);\n}\n\nfunction domainMatches(candidate: string, pattern: string): boolean {\n const c = candidate.toLowerCase();\n const p = pattern.toLowerCase();\n if (c === p) return true;\n if (p.startsWith('*.')) {\n const suffix = p.slice(1); // \".example.com\"\n return c.endsWith(suffix) && c !== p.slice(2);\n }\n return c === `www.${p}` || c === `m.${p}`;\n}\n\n/**\n * Return true if `collection` matches any entry in the resolved list.\n *\n * Matching modes:\n * - Collections match when `collection_rid` is set on both sides and equal.\n * - Otherwise, they match when they share at least one `distribution_ids`\n * entry with the same `type` and `value`.\n *\n * A collection with no collection_rid and no distribution_ids cannot match —\n * the seller has nothing to match against.\n */\nexport function matchesCollectionList(\n collection: { collection_rid?: string; distribution_ids?: { type: string; value: string }[] },\n list: ResolvedCollectionList\n): boolean {\n return list.collections.some(entry => collectionsMatch(collection, entry));\n}\n\nfunction collectionsMatch(\n a: { collection_rid?: string; distribution_ids?: { type: string; value: string }[] },\n b: ResolvedCollection\n): boolean {\n if (a.collection_rid && b.collection_rid && a.collection_rid === b.collection_rid) {\n return true;\n }\n if (!a.distribution_ids?.length || !b.distribution_ids?.length) return false;\n return a.distribution_ids.some(aid =>\n b.distribution_ids!.some(bid => aid.type === bid.type && aid.value === bid.value)\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
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8
2
|
import { getSdkServer } from "./adcp-server.mjs";
|
|
9
3
|
import { InMemoryTaskStore as _InMemoryTaskStore } from "@modelcontextprotocol/sdk/experimental/tasks/stores/in-memory.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/lib/server/tasks.ts"],"sourcesContent":["/**\n * Server-side helpers for MCP Tasks protocol support.\n *\n * Publishers can use these to add async task support to their AdCP MCP servers.\n * The MCP SDK handles all protocol plumbing (tasks/get, tasks/result, tasks/cancel,\n * tasks/list, TTL, _meta injection) — publishers only implement task creation and\n * result storage logic.\n *\n * @example\n * ```typescript\n * import { createTaskCapableServer, registerAdcpTaskTool, InMemoryTaskStore } from '@adcp/sdk';\n *\n * const server = createTaskCapableServer('My Publisher', '1.0.0');\n *\n * registerAdcpTaskTool(server, 'create_media_buy', {\n * description: 'Create a media buy (async)',\n * inputSchema: { campaign_name: z.string() },\n * taskSupport: 'required',\n * }, {\n * createTask: async (args, extra) => {\n * const task = await extra.taskStore.createTask({ ttl: 300_000 });\n * startBackgroundWork(task.taskId, args);\n * return { task };\n * },\n * getTask: async (_args, extra) => {\n * return extra.taskStore.getTask(extra.taskId);\n * },\n * getTaskResult: async (_args, extra) => {\n * return extra.taskStore.getTaskResult(extra.taskId);\n * },\n * });\n * ```\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { AdcpServer } from './adcp-server';\nimport { getSdkServer } from './adcp-server';\nimport type { RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ZodRawShapeCompat, AnySchema } from '@modelcontextprotocol/sdk/server/zod-compat.js';\nimport type { ToolAnnotations, CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport type { ToolTaskHandler, TaskToolExecution } from '@modelcontextprotocol/sdk/experimental/tasks/interfaces.js';\n\n// Re-export SDK task primitives so publishers don't import from experimental paths\nimport { InMemoryTaskStore as _InMemoryTaskStore } from '@modelcontextprotocol/sdk/experimental/tasks/stores/in-memory.js';\nexport { _InMemoryTaskStore as InMemoryTaskStore };\nexport { isTerminal } from '@modelcontextprotocol/sdk/experimental/tasks/interfaces.js';\nexport type {\n TaskStore,\n TaskMessageQueue,\n CreateTaskOptions,\n ToolTaskHandler,\n CreateTaskRequestHandlerExtra,\n TaskRequestHandlerExtra,\n} from '@modelcontextprotocol/sdk/experimental/tasks/interfaces.js';\nexport type { CreateTaskResult, GetTaskResult, Task } from '@modelcontextprotocol/sdk/experimental/tasks/types.js';\n\n/**\n * Build a task tool result response.\n *\n * Same pattern as `productsResponse()` / `mediaBuyResponse()` but designed for\n * use in `getTaskResult` handlers where you return the final async result.\n */\nexport function taskToolResponse(data: Record<string, unknown>, summary?: string): CallToolResult {\n return {\n content: [{ type: 'text', text: summary ?? 'Task completed' }],\n structuredContent: data,\n };\n}\n\n/**\n * Configuration for registering an AdCP task tool.\n */\nexport interface AdcpTaskToolConfig {\n description: string;\n title?: string;\n inputSchema?: ZodRawShapeCompat | AnySchema;\n outputSchema?: ZodRawShapeCompat | AnySchema;\n annotations?: ToolAnnotations;\n taskSupport?: 'optional' | 'required';\n}\n\n/**\n * Register an AdCP task-based tool on an MCP server.\n *\n * Wraps `server.experimental.tasks.registerToolTask()` with AdCP defaults.\n * Sets `execution.taskSupport` (default: 'optional') and passes through to the SDK.\n *\n * Accepts either an `AdcpServer` from `createAdcpServer()` or a raw SDK\n * `McpServer` from `createTaskCapableServer()` — the helper unwraps the\n * opaque handle when needed so registration reaches the SDK server.\n */\nexport function registerAdcpTaskTool(\n server: AdcpServer | McpServer,\n name: string,\n config: AdcpTaskToolConfig,\n handler: ToolTaskHandler<any>\n): RegisteredTool {\n const mcp = getSdkServer(server as AdcpServer) ?? (server as McpServer);\n const { description, title, inputSchema, outputSchema, annotations, taskSupport = 'optional' } = config;\n\n const execution: TaskToolExecution = { taskSupport };\n\n const sdkConfig = {\n description,\n execution,\n ...(title != null && { title }),\n ...(outputSchema != null && { outputSchema }),\n ...(annotations != null && { annotations }),\n };\n\n if (inputSchema != null) {\n return mcp.experimental.tasks.registerToolTask(name, { ...sdkConfig, inputSchema }, handler);\n }\n\n return mcp.experimental.tasks.registerToolTask(\n name,\n sdkConfig as Parameters<typeof mcp.experimental.tasks.registerToolTask>[1],\n handler as ToolTaskHandler<undefined>\n );\n}\n\n/**\n * Create an MCP server with task support pre-configured.\n *\n * Sets up the server with a `taskStore` (defaults to `InMemoryTaskStore`) and\n * declares tasks capability. Publishers can then use `registerAdcpTaskTool()`\n * or `server.experimental.tasks.registerToolTask()` directly.\n *\n * This uses the higher-level `McpServer` API. Publishers with complex dispatch\n * patterns (e.g., custom request routing) can use the lower-level `Server` class\n * directly by passing `taskStore` in `ServerOptions` and using `setRequestHandler`\n * for task lifecycle methods. See the MCP SDK docs for that approach.\n */\nexport function createTaskCapableServer(\n name: string,\n version: string,\n options?: {\n taskStore?: import('@modelcontextprotocol/sdk/experimental/tasks/interfaces.js').TaskStore;\n taskMessageQueue?: import('@modelcontextprotocol/sdk/experimental/tasks/interfaces.js').TaskMessageQueue;\n instructions?: string;\n }\n): McpServer {\n const taskStore = options?.taskStore ?? new _InMemoryTaskStore();\n\n return new McpServer({ name, version }, {\n capabilities: {\n tasks: {\n list: {},\n cancel: {},\n requests: {\n tools: {\n call: {},\n },\n },\n },\n },\n taskStore,\n taskMessageQueue: options?.taskMessageQueue,\n instructions: options?.instructions,\n } as ConstructorParameters<typeof McpServer>[1]);\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/server/tasks.ts"],"sourcesContent":["/**\n * Server-side helpers for MCP Tasks protocol support.\n *\n * Publishers can use these to add async task support to their AdCP MCP servers.\n * The MCP SDK handles all protocol plumbing (tasks/get, tasks/result, tasks/cancel,\n * tasks/list, TTL, _meta injection) — publishers only implement task creation and\n * result storage logic.\n *\n * @example\n * ```typescript\n * import { createTaskCapableServer, registerAdcpTaskTool, InMemoryTaskStore } from '@adcp/sdk';\n *\n * const server = createTaskCapableServer('My Publisher', '1.0.0');\n *\n * registerAdcpTaskTool(server, 'create_media_buy', {\n * description: 'Create a media buy (async)',\n * inputSchema: { campaign_name: z.string() },\n * taskSupport: 'required',\n * }, {\n * createTask: async (args, extra) => {\n * const task = await extra.taskStore.createTask({ ttl: 300_000 });\n * startBackgroundWork(task.taskId, args);\n * return { task };\n * },\n * getTask: async (_args, extra) => {\n * return extra.taskStore.getTask(extra.taskId);\n * },\n * getTaskResult: async (_args, extra) => {\n * return extra.taskStore.getTaskResult(extra.taskId);\n * },\n * });\n * ```\n */\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { AdcpServer } from './adcp-server';\nimport { getSdkServer } from './adcp-server';\nimport type { RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ZodRawShapeCompat, AnySchema } from '@modelcontextprotocol/sdk/server/zod-compat.js';\nimport type { ToolAnnotations, CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport type { ToolTaskHandler, TaskToolExecution } from '@modelcontextprotocol/sdk/experimental/tasks/interfaces.js';\n\n// Re-export SDK task primitives so publishers don't import from experimental paths\nimport { InMemoryTaskStore as _InMemoryTaskStore } from '@modelcontextprotocol/sdk/experimental/tasks/stores/in-memory.js';\nexport { _InMemoryTaskStore as InMemoryTaskStore };\nexport { isTerminal } from '@modelcontextprotocol/sdk/experimental/tasks/interfaces.js';\nexport type {\n TaskStore,\n TaskMessageQueue,\n CreateTaskOptions,\n ToolTaskHandler,\n CreateTaskRequestHandlerExtra,\n TaskRequestHandlerExtra,\n} from '@modelcontextprotocol/sdk/experimental/tasks/interfaces.js';\nexport type { CreateTaskResult, GetTaskResult, Task } from '@modelcontextprotocol/sdk/experimental/tasks/types.js';\n\n/**\n * Build a task tool result response.\n *\n * Same pattern as `productsResponse()` / `mediaBuyResponse()` but designed for\n * use in `getTaskResult` handlers where you return the final async result.\n */\nexport function taskToolResponse(data: Record<string, unknown>, summary?: string): CallToolResult {\n return {\n content: [{ type: 'text', text: summary ?? 'Task completed' }],\n structuredContent: data,\n };\n}\n\n/**\n * Configuration for registering an AdCP task tool.\n */\nexport interface AdcpTaskToolConfig {\n description: string;\n title?: string;\n inputSchema?: ZodRawShapeCompat | AnySchema;\n outputSchema?: ZodRawShapeCompat | AnySchema;\n annotations?: ToolAnnotations;\n taskSupport?: 'optional' | 'required';\n}\n\n/**\n * Register an AdCP task-based tool on an MCP server.\n *\n * Wraps `server.experimental.tasks.registerToolTask()` with AdCP defaults.\n * Sets `execution.taskSupport` (default: 'optional') and passes through to the SDK.\n *\n * Accepts either an `AdcpServer` from `createAdcpServer()` or a raw SDK\n * `McpServer` from `createTaskCapableServer()` — the helper unwraps the\n * opaque handle when needed so registration reaches the SDK server.\n */\nexport function registerAdcpTaskTool(\n server: AdcpServer | McpServer,\n name: string,\n config: AdcpTaskToolConfig,\n handler: ToolTaskHandler<any>\n): RegisteredTool {\n const mcp = getSdkServer(server as AdcpServer) ?? (server as McpServer);\n const { description, title, inputSchema, outputSchema, annotations, taskSupport = 'optional' } = config;\n\n const execution: TaskToolExecution = { taskSupport };\n\n const sdkConfig = {\n description,\n execution,\n ...(title != null && { title }),\n ...(outputSchema != null && { outputSchema }),\n ...(annotations != null && { annotations }),\n };\n\n if (inputSchema != null) {\n return mcp.experimental.tasks.registerToolTask(name, { ...sdkConfig, inputSchema }, handler);\n }\n\n return mcp.experimental.tasks.registerToolTask(\n name,\n sdkConfig as Parameters<typeof mcp.experimental.tasks.registerToolTask>[1],\n handler as ToolTaskHandler<undefined>\n );\n}\n\n/**\n * Create an MCP server with task support pre-configured.\n *\n * Sets up the server with a `taskStore` (defaults to `InMemoryTaskStore`) and\n * declares tasks capability. Publishers can then use `registerAdcpTaskTool()`\n * or `server.experimental.tasks.registerToolTask()` directly.\n *\n * This uses the higher-level `McpServer` API. Publishers with complex dispatch\n * patterns (e.g., custom request routing) can use the lower-level `Server` class\n * directly by passing `taskStore` in `ServerOptions` and using `setRequestHandler`\n * for task lifecycle methods. See the MCP SDK docs for that approach.\n */\nexport function createTaskCapableServer(\n name: string,\n version: string,\n options?: {\n taskStore?: import('@modelcontextprotocol/sdk/experimental/tasks/interfaces.js').TaskStore;\n taskMessageQueue?: import('@modelcontextprotocol/sdk/experimental/tasks/interfaces.js').TaskMessageQueue;\n instructions?: string;\n }\n): McpServer {\n const taskStore = options?.taskStore ?? new _InMemoryTaskStore();\n\n return new McpServer({ name, version }, {\n capabilities: {\n tasks: {\n list: {},\n cancel: {},\n requests: {\n tools: {\n call: {},\n },\n },\n },\n },\n taskStore,\n taskMessageQueue: options?.taskMessageQueue,\n instructions: options?.instructions,\n } as ConstructorParameters<typeof McpServer>[1]);\n}\n"],"mappings":"AAkCA,SAAS,iBAAiB;AAE1B,SAAS,oBAAoB;AAO7B,SAAS,qBAAqB,0BAA0B;AAExD,SAAS,kBAAkB;AAiBpB,SAAS,iBAAiB,MAA+B,SAAkC;AAChG,SAAO;AAAA,IACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,WAAW,iBAAiB,CAAC;AAAA,IAC7D,mBAAmB;AAAA,EACrB;AACF;AAwBO,SAAS,qBACd,QACA,MACA,QACA,SACgB;AAChB,QAAM,MAAM,aAAa,MAAoB,KAAM;AACnD,QAAM,EAAE,aAAa,OAAO,aAAa,cAAc,aAAa,cAAc,WAAW,IAAI;AAEjG,QAAM,YAA+B,EAAE,YAAY;AAEnD,QAAM,YAAY;AAAA,IAChB;AAAA,IACA;AAAA,IACA,GAAI,SAAS,QAAQ,EAAE,MAAM;AAAA,IAC7B,GAAI,gBAAgB,QAAQ,EAAE,aAAa;AAAA,IAC3C,GAAI,eAAe,QAAQ,EAAE,YAAY;AAAA,EAC3C;AAEA,MAAI,eAAe,MAAM;AACvB,WAAO,IAAI,aAAa,MAAM,iBAAiB,MAAM,EAAE,GAAG,WAAW,YAAY,GAAG,OAAO;AAAA,EAC7F;AAEA,SAAO,IAAI,aAAa,MAAM;AAAA,IAC5B;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAcO,SAAS,wBACd,MACA,SACA,SAKW;AACX,QAAM,YAAY,SAAS,aAAa,IAAI,mBAAmB;AAE/D,SAAO,IAAI,UAAU,EAAE,MAAM,QAAQ,GAAG;AAAA,IACtC,cAAc;AAAA,MACZ,OAAO;AAAA,QACL,MAAM,CAAC;AAAA,QACP,QAAQ,CAAC;AAAA,QACT,UAAU;AAAA,UACR,OAAO;AAAA,YACL,MAAM,CAAC;AAAA,UACT;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IACA;AAAA,IACA,kBAAkB,SAAS;AAAA,IAC3B,cAAc,SAAS;AAAA,EACzB,CAA+C;AACjD;","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 { mergeSeedProduct } from "../testing/seed-merge.mjs";
|
|
8
2
|
function isSandboxRequest(input) {
|
|
9
3
|
const account = input.account;
|