@adcp/sdk 6.11.0 → 6.12.0

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.
Files changed (95) hide show
  1. package/bin/adcp-config.js +7 -1
  2. package/bin/adcp.js +191 -4
  3. package/dist/lib/adapters/index.d.ts +0 -2
  4. package/dist/lib/adapters/index.d.ts.map +1 -1
  5. package/dist/lib/adapters/index.js +1 -8
  6. package/dist/lib/adapters/index.js.map +1 -1
  7. package/dist/lib/index.d.ts +1 -1
  8. package/dist/lib/index.d.ts.map +1 -1
  9. package/dist/lib/index.js +2 -7
  10. package/dist/lib/index.js.map +1 -1
  11. package/dist/lib/mock-server/index.d.ts +2 -0
  12. package/dist/lib/mock-server/index.d.ts.map +1 -1
  13. package/dist/lib/mock-server/index.js +17 -0
  14. package/dist/lib/mock-server/index.js.map +1 -1
  15. package/dist/lib/mock-server/sales-guaranteed/recipe.d.ts +155 -0
  16. package/dist/lib/mock-server/sales-guaranteed/recipe.d.ts.map +1 -0
  17. package/dist/lib/mock-server/sales-guaranteed/recipe.js +107 -0
  18. package/dist/lib/mock-server/sales-guaranteed/recipe.js.map +1 -0
  19. package/dist/lib/mock-server/sales-guaranteed/server.d.ts.map +1 -1
  20. package/dist/lib/mock-server/sales-guaranteed/server.js +212 -0
  21. package/dist/lib/mock-server/sales-guaranteed/server.js.map +1 -1
  22. package/dist/lib/mock-server/sales-non-guaranteed/recipe.d.ts +123 -0
  23. package/dist/lib/mock-server/sales-non-guaranteed/recipe.d.ts.map +1 -0
  24. package/dist/lib/mock-server/sales-non-guaranteed/recipe.js +81 -0
  25. package/dist/lib/mock-server/sales-non-guaranteed/recipe.js.map +1 -0
  26. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  27. package/dist/lib/server/ctx-metadata/index.d.ts +1 -1
  28. package/dist/lib/server/ctx-metadata/index.d.ts.map +1 -1
  29. package/dist/lib/server/ctx-metadata/index.js +3 -1
  30. package/dist/lib/server/ctx-metadata/index.js.map +1 -1
  31. package/dist/lib/server/ctx-metadata/wire-shape.d.ts +21 -0
  32. package/dist/lib/server/ctx-metadata/wire-shape.d.ts.map +1 -1
  33. package/dist/lib/server/ctx-metadata/wire-shape.js +111 -0
  34. package/dist/lib/server/ctx-metadata/wire-shape.js.map +1 -1
  35. package/dist/lib/server/decisioning/context.d.ts +19 -0
  36. package/dist/lib/server/decisioning/context.d.ts.map +1 -1
  37. package/dist/lib/server/decisioning/index.d.ts +3 -0
  38. package/dist/lib/server/decisioning/index.d.ts.map +1 -1
  39. package/dist/lib/server/decisioning/index.js +16 -1
  40. package/dist/lib/server/decisioning/index.js.map +1 -1
  41. package/dist/lib/server/decisioning/platform.d.ts +17 -0
  42. package/dist/lib/server/decisioning/platform.d.ts.map +1 -1
  43. package/dist/lib/server/decisioning/platform.js.map +1 -1
  44. package/dist/lib/server/decisioning/proposal/dispatch.d.ts +203 -0
  45. package/dist/lib/server/decisioning/proposal/dispatch.d.ts.map +1 -0
  46. package/dist/lib/server/decisioning/proposal/dispatch.js +395 -0
  47. package/dist/lib/server/decisioning/proposal/dispatch.js.map +1 -0
  48. package/dist/lib/server/decisioning/proposal/index.d.ts +21 -0
  49. package/dist/lib/server/decisioning/proposal/index.d.ts.map +1 -0
  50. package/dist/lib/server/decisioning/proposal/index.js +37 -0
  51. package/dist/lib/server/decisioning/proposal/index.js.map +1 -0
  52. package/dist/lib/server/decisioning/proposal/lifecycle.d.ts +195 -0
  53. package/dist/lib/server/decisioning/proposal/lifecycle.d.ts.map +1 -0
  54. package/dist/lib/server/decisioning/proposal/lifecycle.js +366 -0
  55. package/dist/lib/server/decisioning/proposal/lifecycle.js.map +1 -0
  56. package/dist/lib/server/decisioning/proposal/mock-manager.d.ts +93 -0
  57. package/dist/lib/server/decisioning/proposal/mock-manager.d.ts.map +1 -0
  58. package/dist/lib/server/decisioning/proposal/mock-manager.js +109 -0
  59. package/dist/lib/server/decisioning/proposal/mock-manager.js.map +1 -0
  60. package/dist/lib/server/decisioning/proposal/store.d.ts +279 -0
  61. package/dist/lib/server/decisioning/proposal/store.d.ts.map +1 -0
  62. package/dist/lib/server/decisioning/proposal/store.js +291 -0
  63. package/dist/lib/server/decisioning/proposal/store.js.map +1 -0
  64. package/dist/lib/server/decisioning/proposal/types.d.ts +394 -0
  65. package/dist/lib/server/decisioning/proposal/types.d.ts.map +1 -0
  66. package/dist/lib/server/decisioning/proposal/types.js +58 -0
  67. package/dist/lib/server/decisioning/proposal/types.js.map +1 -0
  68. package/dist/lib/server/decisioning/runtime/from-platform.d.ts +25 -0
  69. package/dist/lib/server/decisioning/runtime/from-platform.d.ts.map +1 -1
  70. package/dist/lib/server/decisioning/runtime/from-platform.js +198 -15
  71. package/dist/lib/server/decisioning/runtime/from-platform.js.map +1 -1
  72. package/dist/lib/server/index.d.ts +1 -1
  73. package/dist/lib/server/index.d.ts.map +1 -1
  74. package/dist/lib/server/index.js +3 -1
  75. package/dist/lib/server/index.js.map +1 -1
  76. package/dist/lib/testing/client.d.ts.map +1 -1
  77. package/dist/lib/testing/client.js +7 -1
  78. package/dist/lib/testing/client.js.map +1 -1
  79. package/dist/lib/testing/storyboard/task-map.d.ts.map +1 -1
  80. package/dist/lib/testing/storyboard/task-map.js +1 -0
  81. package/dist/lib/testing/storyboard/task-map.js.map +1 -1
  82. package/dist/lib/testing/storyboard/test-kit.d.ts.map +1 -1
  83. package/dist/lib/testing/storyboard/test-kit.js +4 -0
  84. package/dist/lib/testing/storyboard/test-kit.js.map +1 -1
  85. package/dist/lib/testing/types.d.ts +10 -0
  86. package/dist/lib/testing/types.d.ts.map +1 -1
  87. package/dist/lib/version.d.ts +3 -3
  88. package/dist/lib/version.js +3 -3
  89. package/examples/hello_seller_adapter_guaranteed.ts +29 -2
  90. package/examples/hello_seller_adapter_proposal_mode.ts +575 -0
  91. package/package.json +1 -1
  92. package/dist/lib/adapters/proposal-manager.d.ts +0 -142
  93. package/dist/lib/adapters/proposal-manager.d.ts.map +0 -1
  94. package/dist/lib/adapters/proposal-manager.js +0 -184
  95. package/dist/lib/adapters/proposal-manager.js.map +0 -1
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Proposal-lifecycle framework helpers — the v1.5 intercept seam.
3
+ *
4
+ * Sits parallel to existing dispatch helpers in `runtime/from-platform.ts`.
5
+ * Framework intercepts at a seam, does its work, dispatches.
6
+ *
7
+ * Public surface (the dispatch path imports these):
8
+ *
9
+ * - {@link enforceProposalExpiry} — D7. Look up the committed proposal,
10
+ * validate `state === 'committed'` and `now <= expires_at + grace`,
11
+ * return the record.
12
+ * - {@link validateCapabilityOverlap} — D4. Walk a buyer's
13
+ * `create_media_buy` / `update_media_buy` request packages against
14
+ * each recipe's `capability_overlap` and reject mismatches with
15
+ * `INVALID_REQUEST`.
16
+ * - {@link validateOverlapSubsetOfWire} — D4 round-4. Validate at
17
+ * `putDraft` time that each recipe's `capability_overlap` axis is a
18
+ * subset of the corresponding wire-declared product capabilities.
19
+ * Mismatches throw `INTERNAL_ERROR` (adopter bug, not buyer bug).
20
+ * - {@link detectFinalizeAction} — pull out the first finalize-action
21
+ * refine entry from a `GetProductsRequest`.
22
+ * - structured-log helpers per § Observability.
23
+ *
24
+ * Ports `adcp-client-python.src/adcp/decisioning/proposal_lifecycle.py`.
25
+ *
26
+ * @public
27
+ * @packageDocumentation
28
+ */
29
+ import type { GetProductsRequest, Product } from '../../../types/tools.generated';
30
+ import type { ProposalRecord, ProposalStore } from './store';
31
+ import type { Recipe } from './types';
32
+ /**
33
+ * Validate a proposal is committed and within its hold window.
34
+ *
35
+ * Three failure modes mapped to spec error codes:
36
+ *
37
+ * - Record not found OR cross-tenant → `PROPOSAL_NOT_FOUND` (terminal).
38
+ * Cross-tenant probes return the same error as missing IDs (no
39
+ * principal-enumeration via id probing).
40
+ * - State !== `'committed'` → `PROPOSAL_NOT_COMMITTED` (correctable).
41
+ * The buyer needs to call `getProducts({ buying_mode: 'refine',
42
+ * refine: [{ action: 'finalize' }] })` first.
43
+ * - Committed but `now > expires_at + grace` → `PROPOSAL_EXPIRED`
44
+ * (correctable per AdCP 3.0.6 — the buyer re-discovers via
45
+ * `get_products` to obtain a fresh proposal).
46
+ *
47
+ * @public
48
+ */
49
+ export declare function enforceProposalExpiry<TRecipe extends Recipe>(proposalId: string, args: {
50
+ proposalStore: ProposalStore<TRecipe>;
51
+ expectedAccountId: string;
52
+ graceSeconds?: number;
53
+ now?: Date;
54
+ }): Promise<ProposalRecord<TRecipe>>;
55
+ interface PackageLike {
56
+ product_id?: string;
57
+ pricing_option_id?: string;
58
+ pricing_model?: string;
59
+ delivery_type?: string;
60
+ signal_type?: string;
61
+ targeting_overlay?: Record<string, unknown>;
62
+ /**
63
+ * Adopter-resolved pricing model — the validation reads this when
64
+ * present. Set by the framework when it has resolved
65
+ * `pricing_option_id` against the product's pricing options.
66
+ */
67
+ _resolved_pricing_model?: string;
68
+ _resolved_delivery_type?: string;
69
+ [k: string]: unknown;
70
+ }
71
+ /**
72
+ * Pre-adapter validation seam: walk buyer's packages against each recipe's
73
+ * `capability_overlap` and reject mismatches.
74
+ *
75
+ * Called from the framework's `create_media_buy` and `update_media_buy`
76
+ * dispatch paths after recipes are hydrated from the {@link ProposalStore}.
77
+ * Per D4, the framework owns this gate so every adopter doesn't write the
78
+ * same intersection logic.
79
+ *
80
+ * Validation axes (per {@link CapabilityOverlap}):
81
+ *
82
+ * - `pricingModels` — checked against `package._resolved_pricing_model`
83
+ * or `package.pricing_model`.
84
+ * - `targetingDimensions` — checked against `package.targeting_overlay` keys.
85
+ * - `deliveryTypes` — checked against `package._resolved_delivery_type`
86
+ * or `package.delivery_type`.
87
+ * - `signalTypes` — checked against `package.signal_type`.
88
+ *
89
+ * Per the design's undefined-vs-empty-set semantics: `undefined` skips the
90
+ * gate; an explicit `Set` (including the empty set) is enforced.
91
+ *
92
+ * @public
93
+ */
94
+ export declare function validateCapabilityOverlap<TRecipe extends Recipe>(args: {
95
+ packages: readonly PackageLike[];
96
+ recipes: ReadonlyMap<string, TRecipe>;
97
+ fieldPathPrefix?: string;
98
+ }): void;
99
+ /**
100
+ * Validate `recipe.capability_overlap` is a subset of the matching product's
101
+ * wire-declared capabilities.
102
+ *
103
+ * Called at `putDraft` time. Mismatches throw `INTERNAL_ERROR` — this is
104
+ * an adopter bug (the manager declared an overlap claiming capabilities the
105
+ * wire shape doesn't advertise), not a buyer bug.
106
+ *
107
+ * @public
108
+ */
109
+ export declare function validateOverlapSubsetOfWire<TRecipe extends Recipe>(args: {
110
+ recipes: ReadonlyMap<string, TRecipe>;
111
+ products: readonly Product[];
112
+ }): void;
113
+ /**
114
+ * Result of {@link detectFinalizeAction}: the index, proposal_id, and
115
+ * optional ask of the first finalize-action refine entry.
116
+ *
117
+ * @public
118
+ */
119
+ export interface FinalizeActionRef {
120
+ index: number;
121
+ proposalId: string;
122
+ ask?: string;
123
+ }
124
+ /**
125
+ * Return the first finalize-action refine entry from a
126
+ * `GetProductsRequest`, or `null` if no finalize entry exists.
127
+ *
128
+ * The index points at the entry's position in `refine[]` so the framework
129
+ * can produce indexed wire field paths (`refine[3].proposal_id`) on
130
+ * rejection — buyers parsing the error get a precise pointer.
131
+ *
132
+ * Per the spec, `buying_mode: 'refine'` carries a `refine[]` array of
133
+ * entries. Each entry has a `scope` (`request` / `product` / `proposal`)
134
+ * and an optional `action` (`include` / `omit` / `finalize`). v1.5 only
135
+ * intercepts `proposal`-scoped entries with `action: 'finalize'`.
136
+ *
137
+ * The framework processes ONE finalize entry per request; if the buyer
138
+ * sends multiple finalize entries, only the first is processed (rest fall
139
+ * through to the standard refine path).
140
+ *
141
+ * @public
142
+ */
143
+ export declare function detectFinalizeAction(req: GetProductsRequest): FinalizeActionRef | null;
144
+ /**
145
+ * Logger-shaped sink for structured proposal-lifecycle events. Defaults to
146
+ * `console.info`-style emission; the dispatch path can pass a typed logger
147
+ * to route through the rest of the framework's logging.
148
+ *
149
+ * @public
150
+ */
151
+ export interface ProposalLifecycleLogger {
152
+ info(message: string, fields?: Record<string, unknown>): void;
153
+ }
154
+ /**
155
+ * Replace the module-level logger that proposal-lifecycle structured
156
+ * events (`proposal.draft_persisted`, `proposal.finalized`, `proposal.expired`,
157
+ * `proposal.consumed`) emit through. Adopters wire this to their existing
158
+ * logger (pino, bunyan, etc.) so lifecycle events route through the same
159
+ * pipeline as the rest of their server logs. Tests use it to capture
160
+ * structured emissions for assertion.
161
+ *
162
+ * @public
163
+ */
164
+ export declare function setProposalLifecycleLogger(next: ProposalLifecycleLogger): void;
165
+ /** `proposal.draft_persisted` event. */
166
+ export declare function logDraftPersisted(args: {
167
+ proposalId: string;
168
+ accountId: string;
169
+ recipesCount: number;
170
+ }): void;
171
+ /**
172
+ * `proposal.finalized` event. `path` is `'inline'` or `'handoff'`.
173
+ */
174
+ export declare function logFinalizeSucceeded(args: {
175
+ proposalId: string;
176
+ accountId: string;
177
+ expiresAt: Date;
178
+ path: 'inline' | 'handoff';
179
+ }): void;
180
+ /** `proposal.expired` event. */
181
+ export declare function logExpired(args: {
182
+ proposalId: string;
183
+ accountId: string;
184
+ now: Date;
185
+ expiresAt: Date;
186
+ graceSeconds: number;
187
+ }): void;
188
+ /** `proposal.consumed` event. */
189
+ export declare function logConsumed(args: {
190
+ proposalId: string;
191
+ accountId: string;
192
+ mediaBuyId: string;
193
+ }): void;
194
+ export {};
195
+ //# sourceMappingURL=lifecycle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../../../src/lib/server/decisioning/proposal/lifecycle.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,366 @@
1
+ "use strict";
2
+ /**
3
+ * Proposal-lifecycle framework helpers — the v1.5 intercept seam.
4
+ *
5
+ * Sits parallel to existing dispatch helpers in `runtime/from-platform.ts`.
6
+ * Framework intercepts at a seam, does its work, dispatches.
7
+ *
8
+ * Public surface (the dispatch path imports these):
9
+ *
10
+ * - {@link enforceProposalExpiry} — D7. Look up the committed proposal,
11
+ * validate `state === 'committed'` and `now <= expires_at + grace`,
12
+ * return the record.
13
+ * - {@link validateCapabilityOverlap} — D4. Walk a buyer's
14
+ * `create_media_buy` / `update_media_buy` request packages against
15
+ * each recipe's `capability_overlap` and reject mismatches with
16
+ * `INVALID_REQUEST`.
17
+ * - {@link validateOverlapSubsetOfWire} — D4 round-4. Validate at
18
+ * `putDraft` time that each recipe's `capability_overlap` axis is a
19
+ * subset of the corresponding wire-declared product capabilities.
20
+ * Mismatches throw `INTERNAL_ERROR` (adopter bug, not buyer bug).
21
+ * - {@link detectFinalizeAction} — pull out the first finalize-action
22
+ * refine entry from a `GetProductsRequest`.
23
+ * - structured-log helpers per § Observability.
24
+ *
25
+ * Ports `adcp-client-python.src/adcp/decisioning/proposal_lifecycle.py`.
26
+ *
27
+ * @public
28
+ * @packageDocumentation
29
+ */
30
+ Object.defineProperty(exports, "__esModule", { value: true });
31
+ exports.enforceProposalExpiry = enforceProposalExpiry;
32
+ exports.validateCapabilityOverlap = validateCapabilityOverlap;
33
+ exports.validateOverlapSubsetOfWire = validateOverlapSubsetOfWire;
34
+ exports.detectFinalizeAction = detectFinalizeAction;
35
+ exports.setProposalLifecycleLogger = setProposalLifecycleLogger;
36
+ exports.logDraftPersisted = logDraftPersisted;
37
+ exports.logFinalizeSucceeded = logFinalizeSucceeded;
38
+ exports.logExpired = logExpired;
39
+ exports.logConsumed = logConsumed;
40
+ const async_outcome_1 = require("../async-outcome");
41
+ // ---------------------------------------------------------------------------
42
+ // D7 — expires_at enforcement
43
+ // ---------------------------------------------------------------------------
44
+ /**
45
+ * Validate a proposal is committed and within its hold window.
46
+ *
47
+ * Three failure modes mapped to spec error codes:
48
+ *
49
+ * - Record not found OR cross-tenant → `PROPOSAL_NOT_FOUND` (terminal).
50
+ * Cross-tenant probes return the same error as missing IDs (no
51
+ * principal-enumeration via id probing).
52
+ * - State !== `'committed'` → `PROPOSAL_NOT_COMMITTED` (correctable).
53
+ * The buyer needs to call `getProducts({ buying_mode: 'refine',
54
+ * refine: [{ action: 'finalize' }] })` first.
55
+ * - Committed but `now > expires_at + grace` → `PROPOSAL_EXPIRED`
56
+ * (correctable per AdCP 3.0.6 — the buyer re-discovers via
57
+ * `get_products` to obtain a fresh proposal).
58
+ *
59
+ * @public
60
+ */
61
+ async function enforceProposalExpiry(proposalId, args) {
62
+ const { proposalStore, expectedAccountId, graceSeconds = 0, now } = args;
63
+ const record = await proposalStore.get(proposalId, { expectedAccountId });
64
+ if (!record) {
65
+ throw new async_outcome_1.AdcpError('PROPOSAL_NOT_FOUND', {
66
+ recovery: 'terminal',
67
+ message: `Proposal ${JSON.stringify(proposalId)} not found. The buyer must call get_products ` +
68
+ `with buying_mode='refine' and refine=[{action:'finalize',...}] to obtain a ` +
69
+ `committed proposal_id before referencing it on create_media_buy.`,
70
+ field: 'proposal_id',
71
+ });
72
+ }
73
+ if (record.state !== 'committed') {
74
+ throw new async_outcome_1.AdcpError('PROPOSAL_NOT_COMMITTED', {
75
+ recovery: 'correctable',
76
+ message: `Proposal ${JSON.stringify(proposalId)} is in state ${JSON.stringify(record.state)}; ` +
77
+ `only committed proposals can be accepted via create_media_buy. Call get_products ` +
78
+ `with buying_mode='refine' and action='finalize' first.`,
79
+ field: 'proposal_id',
80
+ });
81
+ }
82
+ if (record.expiresAt) {
83
+ const current = now ?? new Date();
84
+ const deadline = record.expiresAt.getTime() + graceSeconds * 1000;
85
+ if (current.getTime() > deadline) {
86
+ logExpired({
87
+ proposalId,
88
+ accountId: record.accountId,
89
+ now: current,
90
+ expiresAt: record.expiresAt,
91
+ graceSeconds,
92
+ });
93
+ throw new async_outcome_1.AdcpError('PROPOSAL_EXPIRED', {
94
+ // Per AdCP 3.0.6 schemas/cache/3.0.6/enums/error-code.json,
95
+ // PROPOSAL_EXPIRED is `correctable` — the buyer re-discovers
96
+ // via `get_products` to obtain a fresh proposal (the hold
97
+ // window has lapsed but the buyer can re-request).
98
+ recovery: 'correctable',
99
+ message: `Proposal ${JSON.stringify(proposalId)} expired at ${record.expiresAt.toISOString()}; ` +
100
+ `create_media_buy must be called within the inventory hold window. Call get_products ` +
101
+ `with buying_mode='refine' and action='finalize' to request a fresh hold.`,
102
+ field: 'proposal_id',
103
+ });
104
+ }
105
+ }
106
+ return record;
107
+ }
108
+ /**
109
+ * Pre-adapter validation seam: walk buyer's packages against each recipe's
110
+ * `capability_overlap` and reject mismatches.
111
+ *
112
+ * Called from the framework's `create_media_buy` and `update_media_buy`
113
+ * dispatch paths after recipes are hydrated from the {@link ProposalStore}.
114
+ * Per D4, the framework owns this gate so every adopter doesn't write the
115
+ * same intersection logic.
116
+ *
117
+ * Validation axes (per {@link CapabilityOverlap}):
118
+ *
119
+ * - `pricingModels` — checked against `package._resolved_pricing_model`
120
+ * or `package.pricing_model`.
121
+ * - `targetingDimensions` — checked against `package.targeting_overlay` keys.
122
+ * - `deliveryTypes` — checked against `package._resolved_delivery_type`
123
+ * or `package.delivery_type`.
124
+ * - `signalTypes` — checked against `package.signal_type`.
125
+ *
126
+ * Per the design's undefined-vs-empty-set semantics: `undefined` skips the
127
+ * gate; an explicit `Set` (including the empty set) is enforced.
128
+ *
129
+ * @public
130
+ */
131
+ function validateCapabilityOverlap(args) {
132
+ const { packages, recipes, fieldPathPrefix = 'packages' } = args;
133
+ for (let i = 0; i < packages.length; i++) {
134
+ const pkg = packages[i];
135
+ const productId = pkg.product_id;
136
+ if (!productId)
137
+ continue;
138
+ const recipe = recipes.get(productId);
139
+ if (!recipe || !recipe.capability_overlap)
140
+ continue;
141
+ const overlap = recipe.capability_overlap;
142
+ if (overlap.pricingModels) {
143
+ const requested = pkg._resolved_pricing_model ?? pkg.pricing_model;
144
+ if (requested !== undefined && !overlap.pricingModels.has(String(requested))) {
145
+ throw new async_outcome_1.AdcpError('INVALID_REQUEST', {
146
+ recovery: 'terminal',
147
+ message: `Buyer requested pricing_model=${JSON.stringify(requested)} on package ` +
148
+ `${JSON.stringify(productId)}, but this product's recipe declares ` +
149
+ `capability_overlap.pricingModels=${JSON.stringify([...overlap.pricingModels].sort())}. ` +
150
+ `The seller did not enable that pricing model for this product.`,
151
+ field: `${fieldPathPrefix}[${i}].pricing_option_id`,
152
+ });
153
+ }
154
+ }
155
+ if (overlap.targetingDimensions) {
156
+ const overlay = pkg.targeting_overlay;
157
+ const keys = overlay && typeof overlay === 'object' ? Object.keys(overlay) : [];
158
+ const disallowed = keys.filter(k => !overlap.targetingDimensions.has(k));
159
+ if (disallowed.length > 0) {
160
+ throw new async_outcome_1.AdcpError('INVALID_REQUEST', {
161
+ recovery: 'terminal',
162
+ message: `Buyer requested targeting dimensions ${JSON.stringify(disallowed.sort())} on ` +
163
+ `package ${JSON.stringify(productId)}, but this product's recipe declares ` +
164
+ `capability_overlap.targetingDimensions=` +
165
+ `${JSON.stringify([...overlap.targetingDimensions].sort())}. The seller did not ` +
166
+ `enable those targeting dimensions for this product.`,
167
+ field: `${fieldPathPrefix}[${i}].targeting_overlay`,
168
+ });
169
+ }
170
+ }
171
+ if (overlap.deliveryTypes) {
172
+ const delivery = pkg._resolved_delivery_type ?? pkg.delivery_type;
173
+ if (delivery !== undefined && !overlap.deliveryTypes.has(String(delivery))) {
174
+ throw new async_outcome_1.AdcpError('INVALID_REQUEST', {
175
+ recovery: 'terminal',
176
+ message: `Buyer requested delivery_type=${JSON.stringify(delivery)} on package ` +
177
+ `${JSON.stringify(productId)}, but this product's recipe declares ` +
178
+ `capability_overlap.deliveryTypes=${JSON.stringify([...overlap.deliveryTypes].sort())}.`,
179
+ field: `${fieldPathPrefix}[${i}].delivery_type`,
180
+ });
181
+ }
182
+ }
183
+ if (overlap.signalTypes) {
184
+ const signalType = pkg.signal_type;
185
+ if (signalType !== undefined && !overlap.signalTypes.has(String(signalType))) {
186
+ throw new async_outcome_1.AdcpError('INVALID_REQUEST', {
187
+ recovery: 'terminal',
188
+ message: `Buyer requested signal_type=${JSON.stringify(signalType)} on package ` +
189
+ `${JSON.stringify(productId)}, but this product's recipe declares ` +
190
+ `capability_overlap.signalTypes=${JSON.stringify([...overlap.signalTypes].sort())}.`,
191
+ field: `${fieldPathPrefix}[${i}].signal_type`,
192
+ });
193
+ }
194
+ }
195
+ }
196
+ }
197
+ /**
198
+ * Validate `recipe.capability_overlap` is a subset of the matching product's
199
+ * wire-declared capabilities.
200
+ *
201
+ * Called at `putDraft` time. Mismatches throw `INTERNAL_ERROR` — this is
202
+ * an adopter bug (the manager declared an overlap claiming capabilities the
203
+ * wire shape doesn't advertise), not a buyer bug.
204
+ *
205
+ * @public
206
+ */
207
+ function validateOverlapSubsetOfWire(args) {
208
+ const productsById = new Map();
209
+ for (const p of args.products) {
210
+ if (p.product_id)
211
+ productsById.set(p.product_id, p);
212
+ }
213
+ for (const [productId, recipe] of args.recipes) {
214
+ if (!recipe.capability_overlap)
215
+ continue;
216
+ const product = productsById.get(productId);
217
+ if (!product)
218
+ continue; // missing-product is caught elsewhere
219
+ const overlap = recipe.capability_overlap;
220
+ if (overlap.pricingModels) {
221
+ const wirePricing = wirePricingModels(product);
222
+ const extras = [...overlap.pricingModels].filter(p => !wirePricing.has(p));
223
+ if (extras.length > 0) {
224
+ throw new async_outcome_1.AdcpError('INTERNAL_ERROR', {
225
+ recovery: 'terminal',
226
+ message: `Recipe for product ${JSON.stringify(productId)} declares ` +
227
+ `capability_overlap.pricingModels=${JSON.stringify([...overlap.pricingModels].sort())} ` +
228
+ `including ${JSON.stringify(extras.sort())}, but the wire product only advertises ` +
229
+ `${JSON.stringify([...wirePricing].sort())}. The recipe's overlap must be a subset ` +
230
+ `of the wire-declared capabilities; adopter declaration is inconsistent with the ` +
231
+ `product shape.`,
232
+ });
233
+ }
234
+ }
235
+ if (overlap.deliveryTypes) {
236
+ const wireDelivery = wireDeliveryTypes(product);
237
+ const extras = [...overlap.deliveryTypes].filter(p => !wireDelivery.has(p));
238
+ // Only enforce when the wire declares something — products lacking a
239
+ // delivery_type field shouldn't trip the gate.
240
+ if (extras.length > 0 && wireDelivery.size > 0) {
241
+ throw new async_outcome_1.AdcpError('INTERNAL_ERROR', {
242
+ recovery: 'terminal',
243
+ message: `Recipe for product ${JSON.stringify(productId)} declares ` +
244
+ `capability_overlap.deliveryTypes=${JSON.stringify([...overlap.deliveryTypes].sort())} ` +
245
+ `including ${JSON.stringify(extras.sort())}, but the wire product only advertises ` +
246
+ `${JSON.stringify([...wireDelivery].sort())}.`,
247
+ });
248
+ }
249
+ }
250
+ }
251
+ }
252
+ function wirePricingModels(product) {
253
+ const out = new Set();
254
+ const pricing = product.pricing_options;
255
+ if (!pricing)
256
+ return out;
257
+ for (const opt of pricing) {
258
+ if (opt.pricing_model)
259
+ out.add(String(opt.pricing_model));
260
+ }
261
+ return out;
262
+ }
263
+ function wireDeliveryTypes(product) {
264
+ const dt = product.delivery_type;
265
+ return dt ? new Set([String(dt)]) : new Set();
266
+ }
267
+ /**
268
+ * Return the first finalize-action refine entry from a
269
+ * `GetProductsRequest`, or `null` if no finalize entry exists.
270
+ *
271
+ * The index points at the entry's position in `refine[]` so the framework
272
+ * can produce indexed wire field paths (`refine[3].proposal_id`) on
273
+ * rejection — buyers parsing the error get a precise pointer.
274
+ *
275
+ * Per the spec, `buying_mode: 'refine'` carries a `refine[]` array of
276
+ * entries. Each entry has a `scope` (`request` / `product` / `proposal`)
277
+ * and an optional `action` (`include` / `omit` / `finalize`). v1.5 only
278
+ * intercepts `proposal`-scoped entries with `action: 'finalize'`.
279
+ *
280
+ * The framework processes ONE finalize entry per request; if the buyer
281
+ * sends multiple finalize entries, only the first is processed (rest fall
282
+ * through to the standard refine path).
283
+ *
284
+ * @public
285
+ */
286
+ function detectFinalizeAction(req) {
287
+ const refine = req.refine;
288
+ if (!refine || refine.length === 0)
289
+ return null;
290
+ for (let index = 0; index < refine.length; index++) {
291
+ const entry = refine[index];
292
+ if (entry.scope === 'proposal' && entry.action === 'finalize') {
293
+ const proposalId = entry.proposal_id;
294
+ if (typeof proposalId === 'string' && proposalId.length > 0) {
295
+ const ask = typeof entry.ask === 'string' ? entry.ask : undefined;
296
+ return ask !== undefined ? { index, proposalId, ask } : { index, proposalId };
297
+ }
298
+ }
299
+ }
300
+ return null;
301
+ }
302
+ let logger = {
303
+ info: (message, fields) => {
304
+ if (fields) {
305
+ console.log(JSON.stringify({ message, ...fields }));
306
+ }
307
+ else {
308
+ console.log(message);
309
+ }
310
+ },
311
+ };
312
+ /**
313
+ * Replace the module-level logger that proposal-lifecycle structured
314
+ * events (`proposal.draft_persisted`, `proposal.finalized`, `proposal.expired`,
315
+ * `proposal.consumed`) emit through. Adopters wire this to their existing
316
+ * logger (pino, bunyan, etc.) so lifecycle events route through the same
317
+ * pipeline as the rest of their server logs. Tests use it to capture
318
+ * structured emissions for assertion.
319
+ *
320
+ * @public
321
+ */
322
+ function setProposalLifecycleLogger(next) {
323
+ logger = next;
324
+ }
325
+ /** `proposal.draft_persisted` event. */
326
+ function logDraftPersisted(args) {
327
+ logger.info('proposal.draft_persisted', {
328
+ event: 'proposal.draft_persisted',
329
+ proposal_id: args.proposalId,
330
+ account_id: args.accountId,
331
+ recipes_count: args.recipesCount,
332
+ });
333
+ }
334
+ /**
335
+ * `proposal.finalized` event. `path` is `'inline'` or `'handoff'`.
336
+ */
337
+ function logFinalizeSucceeded(args) {
338
+ logger.info('proposal.finalized', {
339
+ event: 'proposal.finalized',
340
+ proposal_id: args.proposalId,
341
+ account_id: args.accountId,
342
+ expires_at: args.expiresAt.toISOString(),
343
+ path: args.path,
344
+ });
345
+ }
346
+ /** `proposal.expired` event. */
347
+ function logExpired(args) {
348
+ logger.info('proposal.expired', {
349
+ event: 'proposal.expired',
350
+ proposal_id: args.proposalId,
351
+ account_id: args.accountId,
352
+ now: args.now.toISOString(),
353
+ expires_at: args.expiresAt.toISOString(),
354
+ grace_seconds: args.graceSeconds,
355
+ });
356
+ }
357
+ /** `proposal.consumed` event. */
358
+ function logConsumed(args) {
359
+ logger.info('proposal.consumed', {
360
+ event: 'proposal.consumed',
361
+ proposal_id: args.proposalId,
362
+ account_id: args.accountId,
363
+ media_buy_id: args.mediaBuyId,
364
+ });
365
+ }
366
+ //# sourceMappingURL=lifecycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../../../../../src/lib/server/decisioning/proposal/lifecycle.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * MockProposalManager — v1 default forwarder.
3
+ *
4
+ * Symmetric with the mock-mode dispatch pattern adopters use to point
5
+ * `DecisioningPlatform` upstreams at a running `bin/adcp.js mock-server`.
6
+ * Adopters who don't yet have proposal logic of their own start with this
7
+ * class pointed at the appropriate mock-server specialism; their first
8
+ * working seller agent runs against the mock fixtures with zero adopter
9
+ * code on the proposal side. They implement their own
10
+ * {@link ProposalManager} subtype incrementally as they replace
11
+ * mock-served slices with real assembly logic.
12
+ *
13
+ * The mock-server lifecycle is **not** managed by the SDK. Adopters or CI
14
+ * start it as needed (`bin/adcp.js mock-server sales-non-guaranteed`) and
15
+ * pass the resulting URL to this class's constructor.
16
+ *
17
+ * Ports `adcp-client-python.src/adcp/decisioning/proposal_manager.py`'s
18
+ * `MockProposalManager` (PR #504).
19
+ *
20
+ * **Runtime requirement**: uses `globalThis.fetch`, which lands as a built-in
21
+ * on Node 18+ (the package declares `"engines": { "node": ">=18.0.0" }`).
22
+ * Adopters running on older Node, on Bun without the global, or in any
23
+ * environment without a global `fetch` should pass an explicit `fetch`
24
+ * implementation via the `fetch` constructor option.
25
+ *
26
+ * @public
27
+ * @packageDocumentation
28
+ */
29
+ import type { Account } from '../account';
30
+ import type { RequestContext } from '../context';
31
+ import type { GetProductsRequest, GetProductsResponse } from '../../../types/tools.generated';
32
+ import type { ProposalCapabilities, ProposalManager, ProposalSalesSpecialism, Recipe } from './types';
33
+ /**
34
+ * Construction options for {@link MockProposalManager}.
35
+ *
36
+ * @public
37
+ */
38
+ export interface MockProposalManagerOptions {
39
+ /**
40
+ * URL of the running mock-server. The forwarder POSTs
41
+ * `GetProductsRequest` payloads to `${mockUpstreamUrl}/get_products`
42
+ * and (when refine is enabled) `${mockUpstreamUrl}/refine_products`.
43
+ *
44
+ * Required and non-empty.
45
+ */
46
+ mockUpstreamUrl: string;
47
+ /**
48
+ * Which sales specialism this mock manager serves. Defaults to
49
+ * `sales-non-guaranteed` (the catalog-style mock-server fixture).
50
+ * Adopters wiring a guaranteed mock pass `sales-guaranteed` so the
51
+ * framework's capability projection matches the fixtures.
52
+ */
53
+ salesSpecialism?: ProposalSalesSpecialism;
54
+ /**
55
+ * When true, the manager declares `refine` capability and forwards
56
+ * `buying_mode: 'refine'` requests to `/refine_products`. Default
57
+ * false — the framework falls through to `getProducts` for refine.
58
+ */
59
+ refine?: boolean;
60
+ /**
61
+ * Headers forwarded on every mock-server request (e.g. `X-Tenant-Id`).
62
+ */
63
+ defaultHeaders?: Readonly<Record<string, string>>;
64
+ /**
65
+ * Per-request timeout in milliseconds. Default 30 seconds.
66
+ */
67
+ timeoutMs?: number;
68
+ /**
69
+ * Optional `fetch` override for testing. Defaults to the global
70
+ * `fetch` (Node 18+).
71
+ */
72
+ fetch?: typeof globalThis.fetch;
73
+ }
74
+ /**
75
+ * v1 default forwarder. Dispatches `getProducts` / `refineProducts` to
76
+ * a running mock-server.
77
+ *
78
+ * @public
79
+ */
80
+ export declare class MockProposalManager<TRecipe extends Recipe = Recipe, TCtxMeta = unknown> implements ProposalManager<TRecipe, TCtxMeta> {
81
+ readonly capabilities: ProposalCapabilities;
82
+ private readonly url;
83
+ private readonly headers;
84
+ private readonly timeoutMs;
85
+ private readonly fetchImpl;
86
+ constructor(options: MockProposalManagerOptions);
87
+ /** The configured mock-server URL — useful for diagnostics. */
88
+ get mockUpstreamUrl(): string;
89
+ getProducts(req: GetProductsRequest, _ctx: RequestContext<Account<TCtxMeta>>): Promise<GetProductsResponse>;
90
+ refineProducts(req: GetProductsRequest, _ctx: RequestContext<Account<TCtxMeta>>): Promise<GetProductsResponse>;
91
+ private forward;
92
+ }
93
+ //# sourceMappingURL=mock-manager.d.ts.map