@adcp/sdk 14.1.0 → 14.3.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 (139) hide show
  1. package/dist/lib/core/SingleAgentClient.d.mts +4 -1
  2. package/dist/lib/core/SingleAgentClient.d.ts +4 -1
  3. package/dist/lib/core/SingleAgentClient.js +25 -4
  4. package/dist/lib/core/SingleAgentClient.mjs +25 -4
  5. package/dist/lib/core/TaskExecutor.d.mts +3 -1
  6. package/dist/lib/core/TaskExecutor.d.ts +3 -1
  7. package/dist/lib/core/TaskExecutor.js +15 -11
  8. package/dist/lib/core/TaskExecutor.mjs +15 -11
  9. package/dist/lib/core/buyer-account-registry.d.mts +30 -2
  10. package/dist/lib/core/buyer-account-registry.d.ts +30 -2
  11. package/dist/lib/core/buyer-account-registry.js +126 -42
  12. package/dist/lib/core/buyer-account-registry.mjs +127 -43
  13. package/dist/lib/index.d.mts +3 -2
  14. package/dist/lib/index.d.ts +3 -2
  15. package/dist/lib/index.js +9 -0
  16. package/dist/lib/index.mjs +12 -1
  17. package/dist/lib/net/agent-transport-fetch.d.mts +4 -0
  18. package/dist/lib/net/agent-transport-fetch.d.ts +4 -0
  19. package/dist/lib/net/agent-transport-fetch.js +18 -5
  20. package/dist/lib/net/agent-transport-fetch.mjs +17 -5
  21. package/dist/lib/reporting/inspection.d.mts +3 -3
  22. package/dist/lib/reporting/inspection.d.ts +3 -3
  23. package/dist/lib/reporting/inspection.js +229 -84
  24. package/dist/lib/reporting/inspection.mjs +229 -84
  25. package/dist/lib/reporting/ledger/status-ingest.js +3 -19
  26. package/dist/lib/reporting/ledger/status-ingest.mjs +2 -18
  27. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  28. package/dist/lib/server/create-adcp-server.js +5 -2
  29. package/dist/lib/server/create-adcp-server.mjs +5 -2
  30. package/dist/lib/signing/agent-resolver/errors.d.mts +2 -0
  31. package/dist/lib/signing/agent-resolver/errors.d.ts +2 -0
  32. package/dist/lib/signing/agent-resolver/errors.js +6 -0
  33. package/dist/lib/signing/agent-resolver/errors.mjs +6 -0
  34. package/dist/lib/signing/agent-resolver/fetch-helpers.d.mts +9 -0
  35. package/dist/lib/signing/agent-resolver/fetch-helpers.d.ts +9 -0
  36. package/dist/lib/signing/agent-resolver/fetch-helpers.js +35 -1
  37. package/dist/lib/signing/agent-resolver/fetch-helpers.mjs +38 -2
  38. package/dist/lib/signing/agent-resolver/legacy-brand.js +1 -1
  39. package/dist/lib/signing/agent-resolver/legacy-brand.mjs +2 -2
  40. package/dist/lib/signing/agent-resolver/resolve-agent.d.mts +1 -1
  41. package/dist/lib/signing/agent-resolver/resolve-agent.d.ts +1 -1
  42. package/dist/lib/signing/agent-resolver/resolve-agent.js +47 -5
  43. package/dist/lib/signing/agent-resolver/resolve-agent.mjs +55 -6
  44. package/dist/lib/signing/brand-jwks.d.mts +2 -0
  45. package/dist/lib/signing/brand-jwks.d.ts +2 -0
  46. package/dist/lib/signing/brand-jwks.js +10 -2
  47. package/dist/lib/signing/brand-jwks.mjs +10 -2
  48. package/dist/lib/signing/canonicalize.d.mts +2 -0
  49. package/dist/lib/signing/canonicalize.d.ts +2 -0
  50. package/dist/lib/signing/canonicalize.js +1 -1
  51. package/dist/lib/signing/canonicalize.mjs +1 -1
  52. package/dist/lib/signing/errors.d.mts +4 -2
  53. package/dist/lib/signing/errors.d.ts +4 -2
  54. package/dist/lib/signing/errors.js +3 -1
  55. package/dist/lib/signing/errors.mjs +3 -1
  56. package/dist/lib/signing/verifier.js +2 -2
  57. package/dist/lib/signing/verifier.mjs +2 -2
  58. package/dist/lib/signing/webhook-verifier.js +2 -2
  59. package/dist/lib/signing/webhook-verifier.mjs +3 -3
  60. package/dist/lib/testing/storyboard/request-signing/builder.js +13 -1
  61. package/dist/lib/testing/storyboard/request-signing/builder.mjs +13 -1
  62. package/dist/lib/testing/storyboard/request-signing/grader.js +1 -0
  63. package/dist/lib/testing/storyboard/request-signing/grader.mjs +1 -0
  64. package/dist/lib/testing/storyboard/request-signing/vector-loader.js +1 -1
  65. package/dist/lib/testing/storyboard/request-signing/vector-loader.mjs +1 -1
  66. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  67. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  68. package/dist/lib/types/accept-proposal.d.ts +19 -1
  69. package/dist/lib/types/buy-products.d.ts +19 -1
  70. package/dist/lib/types/check-governance.d.ts +19 -1
  71. package/dist/lib/types/comply-test-controller.d.ts +19 -1
  72. package/dist/lib/types/control-media-buy.d.ts +19 -1
  73. package/dist/lib/types/core.generated.d.mts +14 -1
  74. package/dist/lib/types/core.generated.d.ts +14 -1
  75. package/dist/lib/types/create-media-buy.d.ts +14 -1
  76. package/dist/lib/types/get-media-buys.d.ts +14 -1
  77. package/dist/lib/types/get-products.d.ts +19 -1
  78. package/dist/lib/types/list-products.d.ts +19 -1
  79. package/dist/lib/types/refine-proposals.d.ts +19 -1
  80. package/dist/lib/types/request-proposals.d.ts +19 -1
  81. package/dist/lib/types/schemas.generated.d.ts +12 -3
  82. package/dist/lib/types/schemas.generated.js +4 -1
  83. package/dist/lib/types/schemas.generated.mjs +4 -1
  84. package/dist/lib/types/tools.generated.d.mts +14 -1
  85. package/dist/lib/types/tools.generated.d.ts +14 -1
  86. package/dist/lib/types/update-media-buy.d.ts +14 -1
  87. package/dist/lib/utils/pointer-utils.d.mts +2 -0
  88. package/dist/lib/utils/pointer-utils.d.ts +2 -0
  89. package/dist/lib/utils/pointer-utils.js +43 -0
  90. package/dist/lib/utils/pointer-utils.mjs +19 -0
  91. package/dist/lib/validation/diagnostic-limits.d.mts +4 -0
  92. package/dist/lib/validation/diagnostic-limits.d.ts +4 -0
  93. package/dist/lib/validation/diagnostic-limits.js +57 -0
  94. package/dist/lib/validation/diagnostic-limits.mjs +32 -0
  95. package/dist/lib/validation/schema-errors.d.mts +3 -1
  96. package/dist/lib/validation/schema-errors.d.ts +3 -1
  97. package/dist/lib/validation/schema-errors.js +18 -3
  98. package/dist/lib/validation/schema-errors.mjs +18 -3
  99. package/dist/lib/validation/schema-validator.js +56 -4
  100. package/dist/lib/validation/schema-validator.mjs +56 -4
  101. package/dist/lib/version.d.mts +3 -3
  102. package/dist/lib/version.d.ts +3 -3
  103. package/dist/lib/version.js +3 -3
  104. package/dist/lib/version.mjs +3 -3
  105. package/dist/lib/webhooks/index.d.mts +24 -0
  106. package/dist/lib/webhooks/index.d.ts +24 -0
  107. package/dist/lib/webhooks/index.js +50 -24
  108. package/dist/lib/webhooks/index.mjs +49 -24
  109. package/dist/lib/wholesale-feed-sync/index.d.mts +2 -0
  110. package/dist/lib/wholesale-feed-sync/index.d.ts +2 -0
  111. package/dist/lib/wholesale-feed-sync/index.js +7 -0
  112. package/dist/lib/wholesale-feed-sync/index.mjs +4 -0
  113. package/dist/lib/wholesale-feed-sync/mirror.d.mts +97 -0
  114. package/dist/lib/wholesale-feed-sync/mirror.d.ts +97 -0
  115. package/dist/lib/wholesale-feed-sync/mirror.js +350 -0
  116. package/dist/lib/wholesale-feed-sync/mirror.mjs +322 -0
  117. package/dist/lib/wholesale-feed-sync/sync.d.mts +12 -29
  118. package/dist/lib/wholesale-feed-sync/sync.d.ts +12 -29
  119. package/dist/lib/wholesale-feed-sync/sync.js +153 -297
  120. package/dist/lib/wholesale-feed-sync/sync.mjs +158 -297
  121. package/docs/README.md +6 -0
  122. package/docs/TYPE-SUMMARY.md +2 -2
  123. package/docs/guides/BUYER-STORAGE.md +3 -0
  124. package/docs/guides/FIRST-CALL-TO-A-SELLER.md +3 -1
  125. package/docs/guides/account-resolution.md +46 -10
  126. package/docs/llms.txt +2 -2
  127. package/docs/migration-14.0-to-14.1.md +85 -0
  128. package/docs/migration-14.x-rc-worksheet.md +4 -4
  129. package/docs/migration-agent-resolution-3.3.md +5 -3
  130. package/docs/recipes/verifying-inbound-webhooks.md +4 -0
  131. package/package.json +3 -2
  132. package/skills/call-adcp-agent/SKILL.md +4 -1
  133. package/skills/adcp-brand.previous/SKILL.md +0 -200
  134. package/skills/adcp-creative.previous/SKILL.md +0 -305
  135. package/skills/adcp-governance.previous/SKILL.md +0 -566
  136. package/skills/adcp-measurement.previous/SKILL.md +0 -136
  137. package/skills/adcp-media-buy.previous/SKILL.md +0 -556
  138. package/skills/adcp-si.previous/SKILL.md +0 -206
  139. package/skills/adcp-signals.previous/SKILL.md +0 -204
@@ -0,0 +1,97 @@
1
+ import type { AccountReference } from '../types/index.mjs';
2
+ import type { AdcpErrorInfo } from '../core/ConversationTypes.mjs';
3
+ import type { WholesaleFeedSyncClient } from './types.mjs';
4
+ import type { LegacyWholesaleFeedEvent, LegacyWholesaleFeedWebhook, LegacyWholesaleProduct, LegacyWholesaleSignal } from './protocol-types.mjs';
5
+ export type WholesaleFeedMirrorItem = LegacyWholesaleProduct | LegacyWholesaleSignal;
6
+ /** accountKey must include the caller/principal and account overlay, even for public catalogs. */
7
+ export interface WholesaleFeedMirrorScope {
8
+ agentUrl: string;
9
+ accountKey: string;
10
+ entity: 'product' | 'signal';
11
+ }
12
+ export interface WholesaleFeedMirrorSnapshot {
13
+ revision: number;
14
+ items: WholesaleFeedMirrorItem[];
15
+ wholesaleFeedVersion?: string;
16
+ pricingVersion?: string;
17
+ cacheScope: 'public' | 'account';
18
+ lastSyncedAt?: string;
19
+ lastWebhookEventId?: string;
20
+ lastWebhookIdempotencyKey?: string;
21
+ error?: AdcpErrorInfo;
22
+ }
23
+ export interface WholesaleFeedMirrorCommit {
24
+ upserts: WholesaleFeedMirrorItem[];
25
+ deletes: string[];
26
+ wholesaleFeedVersion?: string;
27
+ pricingVersion?: string;
28
+ cacheScope: 'public' | 'account';
29
+ lastSyncedAt: string;
30
+ lastWebhookEventId?: string;
31
+ lastWebhookIdempotencyKey?: string;
32
+ /** Persist this delivery receipt atomically with the row/token commit. */
33
+ webhookReceipt?: {
34
+ eventId: string;
35
+ idempotencyKey: string;
36
+ };
37
+ }
38
+ /** Consistent reads; atomically check/increment revisions with rows, tokens, and receipts. */
39
+ export interface WholesaleFeedMirrorStore {
40
+ read(scope: WholesaleFeedMirrorScope): Promise<WholesaleFeedMirrorSnapshot>;
41
+ hasWebhookReceipt(scope: WholesaleFeedMirrorScope, idempotencyKey: string): Promise<boolean>;
42
+ commit(scope: WholesaleFeedMirrorScope, expectedRevision: number, change: WholesaleFeedMirrorCommit): Promise<boolean>;
43
+ recordError(scope: WholesaleFeedMirrorScope, expectedRevision: number, error: AdcpErrorInfo): Promise<boolean>;
44
+ }
45
+ export type WholesaleFeedRefreshOutcome = {
46
+ outcome: 'unchanged';
47
+ snapshot: WholesaleFeedMirrorSnapshot;
48
+ } | {
49
+ outcome: 'applied';
50
+ snapshot: WholesaleFeedMirrorSnapshot;
51
+ diff: LegacyWholesaleFeedEvent[];
52
+ } | {
53
+ outcome: 'degraded';
54
+ error: AdcpErrorInfo;
55
+ cause: Error;
56
+ } | {
57
+ outcome: 'superseded';
58
+ };
59
+ export interface RefreshWholesaleFeedOptions {
60
+ client: WholesaleFeedSyncClient;
61
+ store: WholesaleFeedMirrorStore;
62
+ scope: WholesaleFeedMirrorScope;
63
+ account?: AccountReference;
64
+ /** Disable against sellers that do not advertise wholesale-feed versioning. Defaults to true. */
65
+ conditional?: boolean;
66
+ /** Local cancellation fence; no timer is started by the core. */
67
+ isCurrent?: () => boolean;
68
+ }
69
+ export declare class InMemoryWholesaleFeedMirrorStore implements WholesaleFeedMirrorStore {
70
+ private readonly maxWebhookReceipts;
71
+ private readonly records;
72
+ private readonly receipts;
73
+ private receiptCount;
74
+ /** Fail closed at capacity rather than evicting receipts inside the delivery retry window. */
75
+ constructor(maxWebhookReceipts?: number);
76
+ hasWebhookReceipt(scope: WholesaleFeedMirrorScope, idempotencyKey: string): Promise<boolean>;
77
+ private key;
78
+ read(scope: WholesaleFeedMirrorScope): Promise<WholesaleFeedMirrorSnapshot>;
79
+ /** Seed a detached record when restoring a shell snapshot. */
80
+ restore(scope: WholesaleFeedMirrorScope, snapshot: WholesaleFeedMirrorSnapshot): void;
81
+ commit(scope: WholesaleFeedMirrorScope, expectedRevision: number, change: WholesaleFeedMirrorCommit): Promise<boolean>;
82
+ recordError(scope: WholesaleFeedMirrorScope, expectedRevision: number, error: AdcpErrorInfo): Promise<boolean>;
83
+ }
84
+ /** Refresh one legacy get_products/get_signals feed without owning scheduling or storage. */
85
+ export declare function refreshWholesaleFeed(options: RefreshWholesaleFeedOptions): Promise<WholesaleFeedRefreshOutcome>;
86
+ export interface ApplyWholesaleFeedWebhookOptions extends RefreshWholesaleFeedOptions {
87
+ webhook: LegacyWholesaleFeedWebhook;
88
+ /** Expected subscription route, supplied after authentication by the receiver. */
89
+ webhookScope?: {
90
+ accountId?: string;
91
+ subscriberId?: string;
92
+ };
93
+ }
94
+ /** Apply an authenticated legacy delta, or repair from the seller on bulk change/order/version mismatch. */
95
+ export declare function applyWholesaleFeedWebhook(options: ApplyWholesaleFeedWebhookOptions): Promise<WholesaleFeedRefreshOutcome>;
96
+ export declare function applyWholesaleFeedEvent(entity: WholesaleFeedMirrorScope['entity'], previous: WholesaleFeedMirrorItem[], event: LegacyWholesaleFeedEvent): WholesaleFeedMirrorItem[] | undefined;
97
+ export declare function diffWholesaleFeed(entity: WholesaleFeedMirrorScope['entity'], previous: WholesaleFeedMirrorItem[], next: WholesaleFeedMirrorItem[], cacheScope: 'public' | 'account', includeAllChanges?: boolean): LegacyWholesaleFeedEvent[];
@@ -0,0 +1,97 @@
1
+ import type { AccountReference } from '../types';
2
+ import type { AdcpErrorInfo } from '../core/ConversationTypes';
3
+ import type { WholesaleFeedSyncClient } from './types';
4
+ import type { LegacyWholesaleFeedEvent, LegacyWholesaleFeedWebhook, LegacyWholesaleProduct, LegacyWholesaleSignal } from './protocol-types';
5
+ export type WholesaleFeedMirrorItem = LegacyWholesaleProduct | LegacyWholesaleSignal;
6
+ /** accountKey must include the caller/principal and account overlay, even for public catalogs. */
7
+ export interface WholesaleFeedMirrorScope {
8
+ agentUrl: string;
9
+ accountKey: string;
10
+ entity: 'product' | 'signal';
11
+ }
12
+ export interface WholesaleFeedMirrorSnapshot {
13
+ revision: number;
14
+ items: WholesaleFeedMirrorItem[];
15
+ wholesaleFeedVersion?: string;
16
+ pricingVersion?: string;
17
+ cacheScope: 'public' | 'account';
18
+ lastSyncedAt?: string;
19
+ lastWebhookEventId?: string;
20
+ lastWebhookIdempotencyKey?: string;
21
+ error?: AdcpErrorInfo;
22
+ }
23
+ export interface WholesaleFeedMirrorCommit {
24
+ upserts: WholesaleFeedMirrorItem[];
25
+ deletes: string[];
26
+ wholesaleFeedVersion?: string;
27
+ pricingVersion?: string;
28
+ cacheScope: 'public' | 'account';
29
+ lastSyncedAt: string;
30
+ lastWebhookEventId?: string;
31
+ lastWebhookIdempotencyKey?: string;
32
+ /** Persist this delivery receipt atomically with the row/token commit. */
33
+ webhookReceipt?: {
34
+ eventId: string;
35
+ idempotencyKey: string;
36
+ };
37
+ }
38
+ /** Consistent reads; atomically check/increment revisions with rows, tokens, and receipts. */
39
+ export interface WholesaleFeedMirrorStore {
40
+ read(scope: WholesaleFeedMirrorScope): Promise<WholesaleFeedMirrorSnapshot>;
41
+ hasWebhookReceipt(scope: WholesaleFeedMirrorScope, idempotencyKey: string): Promise<boolean>;
42
+ commit(scope: WholesaleFeedMirrorScope, expectedRevision: number, change: WholesaleFeedMirrorCommit): Promise<boolean>;
43
+ recordError(scope: WholesaleFeedMirrorScope, expectedRevision: number, error: AdcpErrorInfo): Promise<boolean>;
44
+ }
45
+ export type WholesaleFeedRefreshOutcome = {
46
+ outcome: 'unchanged';
47
+ snapshot: WholesaleFeedMirrorSnapshot;
48
+ } | {
49
+ outcome: 'applied';
50
+ snapshot: WholesaleFeedMirrorSnapshot;
51
+ diff: LegacyWholesaleFeedEvent[];
52
+ } | {
53
+ outcome: 'degraded';
54
+ error: AdcpErrorInfo;
55
+ cause: Error;
56
+ } | {
57
+ outcome: 'superseded';
58
+ };
59
+ export interface RefreshWholesaleFeedOptions {
60
+ client: WholesaleFeedSyncClient;
61
+ store: WholesaleFeedMirrorStore;
62
+ scope: WholesaleFeedMirrorScope;
63
+ account?: AccountReference;
64
+ /** Disable against sellers that do not advertise wholesale-feed versioning. Defaults to true. */
65
+ conditional?: boolean;
66
+ /** Local cancellation fence; no timer is started by the core. */
67
+ isCurrent?: () => boolean;
68
+ }
69
+ export declare class InMemoryWholesaleFeedMirrorStore implements WholesaleFeedMirrorStore {
70
+ private readonly maxWebhookReceipts;
71
+ private readonly records;
72
+ private readonly receipts;
73
+ private receiptCount;
74
+ /** Fail closed at capacity rather than evicting receipts inside the delivery retry window. */
75
+ constructor(maxWebhookReceipts?: number);
76
+ hasWebhookReceipt(scope: WholesaleFeedMirrorScope, idempotencyKey: string): Promise<boolean>;
77
+ private key;
78
+ read(scope: WholesaleFeedMirrorScope): Promise<WholesaleFeedMirrorSnapshot>;
79
+ /** Seed a detached record when restoring a shell snapshot. */
80
+ restore(scope: WholesaleFeedMirrorScope, snapshot: WholesaleFeedMirrorSnapshot): void;
81
+ commit(scope: WholesaleFeedMirrorScope, expectedRevision: number, change: WholesaleFeedMirrorCommit): Promise<boolean>;
82
+ recordError(scope: WholesaleFeedMirrorScope, expectedRevision: number, error: AdcpErrorInfo): Promise<boolean>;
83
+ }
84
+ /** Refresh one legacy get_products/get_signals feed without owning scheduling or storage. */
85
+ export declare function refreshWholesaleFeed(options: RefreshWholesaleFeedOptions): Promise<WholesaleFeedRefreshOutcome>;
86
+ export interface ApplyWholesaleFeedWebhookOptions extends RefreshWholesaleFeedOptions {
87
+ webhook: LegacyWholesaleFeedWebhook;
88
+ /** Expected subscription route, supplied after authentication by the receiver. */
89
+ webhookScope?: {
90
+ accountId?: string;
91
+ subscriberId?: string;
92
+ };
93
+ }
94
+ /** Apply an authenticated legacy delta, or repair from the seller on bulk change/order/version mismatch. */
95
+ export declare function applyWholesaleFeedWebhook(options: ApplyWholesaleFeedWebhookOptions): Promise<WholesaleFeedRefreshOutcome>;
96
+ export declare function applyWholesaleFeedEvent(entity: WholesaleFeedMirrorScope['entity'], previous: WholesaleFeedMirrorItem[], event: LegacyWholesaleFeedEvent): WholesaleFeedMirrorItem[] | undefined;
97
+ export declare function diffWholesaleFeed(entity: WholesaleFeedMirrorScope['entity'], previous: WholesaleFeedMirrorItem[], next: WholesaleFeedMirrorItem[], cacheScope: 'public' | 'account', includeAllChanges?: boolean): LegacyWholesaleFeedEvent[];
@@ -0,0 +1,350 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var mirror_exports = {};
20
+ __export(mirror_exports, {
21
+ InMemoryWholesaleFeedMirrorStore: () => InMemoryWholesaleFeedMirrorStore,
22
+ applyWholesaleFeedEvent: () => applyWholesaleFeedEvent,
23
+ applyWholesaleFeedWebhook: () => applyWholesaleFeedWebhook,
24
+ diffWholesaleFeed: () => diffWholesaleFeed,
25
+ refreshWholesaleFeed: () => refreshWholesaleFeed
26
+ });
27
+ module.exports = __toCommonJS(mirror_exports);
28
+ var import_node_util = require("node:util");
29
+ var import_node_crypto = require("node:crypto");
30
+ var import_webhook_notification = require('./webhook-notification.js');
31
+ class InMemoryWholesaleFeedMirrorStore {
32
+ /** Fail closed at capacity rather than evicting receipts inside the delivery retry window. */
33
+ constructor(maxWebhookReceipts = 1e4) {
34
+ this.maxWebhookReceipts = maxWebhookReceipts;
35
+ if (!Number.isSafeInteger(maxWebhookReceipts) || maxWebhookReceipts < 1)
36
+ throw new Error("maxWebhookReceipts must be a positive integer.");
37
+ }
38
+ maxWebhookReceipts;
39
+ records = /* @__PURE__ */ new Map();
40
+ receipts = /* @__PURE__ */ new Map();
41
+ receiptCount = 0;
42
+ async hasWebhookReceipt(scope, idempotencyKey) {
43
+ return this.receipts.get(this.key(scope))?.has(idempotencyKey) ?? false;
44
+ }
45
+ key(scope) {
46
+ return JSON.stringify([scope.agentUrl, scope.accountKey, scope.entity]);
47
+ }
48
+ async read(scope) {
49
+ return structuredClone(this.records.get(this.key(scope)) ?? { revision: 0, items: [], cacheScope: "public" });
50
+ }
51
+ /** Seed a detached record when restoring a shell snapshot. */
52
+ restore(scope, snapshot) {
53
+ this.records.set(this.key(scope), structuredClone(snapshot));
54
+ }
55
+ async commit(scope, expectedRevision, change) {
56
+ const key = this.key(scope);
57
+ const prior = this.records.get(key) ?? { revision: 0, items: [], cacheScope: "public" };
58
+ if (prior.revision !== expectedRevision) return false;
59
+ const items = new Map(prior.items.map((item) => [itemId(scope.entity, item), item]));
60
+ for (const id of change.deletes) items.delete(id);
61
+ for (const item of change.upserts) items.set(itemId(scope.entity, item), structuredClone(item));
62
+ const { upserts: _upserts, deletes: _deletes, webhookReceipt, ...metadata } = change;
63
+ const next = {
64
+ ...prior,
65
+ ...structuredClone(metadata),
66
+ revision: prior.revision + 1,
67
+ items: [...items.values()],
68
+ error: void 0
69
+ };
70
+ if (webhookReceipt) {
71
+ const receipts = this.receipts.get(key) ?? /* @__PURE__ */ new Set();
72
+ if (!receipts.has(webhookReceipt.idempotencyKey)) {
73
+ if (this.receiptCount >= this.maxWebhookReceipts)
74
+ throw new Error("Webhook receipt capacity reached; use durable storage or increase maxWebhookReceipts.");
75
+ receipts.add(webhookReceipt.idempotencyKey);
76
+ ++this.receiptCount;
77
+ }
78
+ this.receipts.set(key, receipts);
79
+ }
80
+ this.records.set(key, next);
81
+ return true;
82
+ }
83
+ async recordError(scope, expectedRevision, error) {
84
+ const key = this.key(scope);
85
+ const prior = this.records.get(key) ?? { revision: 0, items: [], cacheScope: "public" };
86
+ if (prior.revision !== expectedRevision) return false;
87
+ this.records.set(key, { ...prior, revision: prior.revision + 1, error: structuredClone(error) });
88
+ return true;
89
+ }
90
+ }
91
+ async function refreshWholesaleFeed(options) {
92
+ return refreshMirror(options);
93
+ }
94
+ async function refreshMirror(options, receipt, expectedWebhookVersion) {
95
+ options = { ...options, scope: structuredClone(options.scope), account: structuredClone(options.account) };
96
+ const { store, scope, client } = options;
97
+ const previous = await store.read(scope);
98
+ if (options.isCurrent && !options.isCurrent()) return { outcome: "superseded" };
99
+ let cursor;
100
+ const cursors = /* @__PURE__ */ new Set();
101
+ const items = /* @__PURE__ */ new Map();
102
+ let wholesaleFeedVersion;
103
+ let pricingVersion;
104
+ let cacheScope = previous.cacheScope;
105
+ let unchanged = false;
106
+ let page = 0;
107
+ try {
108
+ do {
109
+ if (++page > 1e4) throw new Error("Wholesale feed pagination exceeded its page limit.");
110
+ const params = {
111
+ ...scope.entity === "product" ? { buying_mode: "wholesale" } : { discovery_mode: "wholesale" },
112
+ pagination: { max_results: 100, ...cursor && { cursor } },
113
+ ...options.account && { account: options.account },
114
+ ...!cursor && options.conditional !== false && previous.wholesaleFeedVersion && {
115
+ if_wholesale_feed_version: previous.wholesaleFeedVersion,
116
+ ...previous.pricingVersion && { if_pricing_version: previous.pricingVersion }
117
+ }
118
+ };
119
+ const result = await (scope.entity === "product" ? client.getProducts(params) : client.getSignals(params));
120
+ if (options.isCurrent && !options.isCurrent()) return { outcome: "superseded" };
121
+ if (result.success === false || result.status !== void 0 && !["completed", "success"].includes(result.status)) {
122
+ throw Object.assign(new Error(result.error ?? "Wholesale feed refresh failed."), {
123
+ adcpError: result.adcpError
124
+ });
125
+ }
126
+ const body = result.data;
127
+ if (!body) throw new Error("Wholesale feed did not return a completed catalog.");
128
+ if (body.unchanged) {
129
+ if (cursor || !("if_wholesale_feed_version" in params) || body.wholesale_feed_version !== previous.wholesaleFeedVersion || "if_pricing_version" in params && body.pricing_version !== previous.pricingVersion || body.cache_scope !== void 0 && body.cache_scope !== previous.cacheScope) {
130
+ throw new Error("Wholesale feed unchanged response must match both requested version tokens.");
131
+ }
132
+ unchanged = true;
133
+ }
134
+ if (page > 1 && (body.wholesale_feed_version !== void 0 && body.wholesale_feed_version !== wholesaleFeedVersion || body.pricing_version !== void 0 && body.pricing_version !== pricingVersion || (body.cache_scope ?? cacheScope) !== cacheScope)) {
135
+ throw new Error("Wholesale feed versions changed during pagination.");
136
+ }
137
+ wholesaleFeedVersion = body.wholesale_feed_version ?? wholesaleFeedVersion;
138
+ pricingVersion = body.pricing_version ?? pricingVersion;
139
+ cacheScope = body.cache_scope ?? cacheScope;
140
+ if (unchanged) break;
141
+ const rows = scope.entity === "product" ? body.products : body.signals;
142
+ if (!Array.isArray(rows)) throw new Error("Wholesale feed response is missing its catalog rows.");
143
+ for (const item of rows) {
144
+ const id = itemId(scope.entity, item);
145
+ if (!id) throw new Error("Wholesale feed row is missing its entity id.");
146
+ items.set(id, item);
147
+ }
148
+ cursor = body.pagination?.has_more ? body.pagination.cursor : void 0;
149
+ if (body.pagination?.has_more && (!cursor || cursors.has(cursor)))
150
+ throw new Error("Wholesale feed pagination did not advance.");
151
+ if (cursor) cursors.add(cursor);
152
+ } while (cursor);
153
+ if (receipt && expectedWebhookVersion !== void 0 && wholesaleFeedVersion === previous.wholesaleFeedVersion && wholesaleFeedVersion !== expectedWebhookVersion)
154
+ throw new Error("Wholesale repair has not observed the webhook version; retry delivery.");
155
+ } catch (cause) {
156
+ if (options.isCurrent && !options.isCurrent()) return { outcome: "superseded" };
157
+ const errorCause = cause instanceof Error ? cause : new Error(String(cause));
158
+ const error = cause?.adcpError ?? {
159
+ code: "SERVICE_UNAVAILABLE",
160
+ recovery: "transient",
161
+ message: "Wholesale feed refresh failed.",
162
+ synthetic: true
163
+ };
164
+ if (!await store.recordError(scope, previous.revision, error)) return { outcome: "superseded" };
165
+ return { outcome: "degraded", error, cause: errorCause };
166
+ }
167
+ const nextItems = unchanged ? previous.items : [...items.values()];
168
+ const next = {
169
+ ...previous,
170
+ items: nextItems,
171
+ wholesaleFeedVersion,
172
+ pricingVersion,
173
+ cacheScope,
174
+ lastSyncedAt: (/* @__PURE__ */ new Date()).toISOString(),
175
+ revision: previous.revision + 1,
176
+ error: void 0,
177
+ ...receipt && {
178
+ lastWebhookEventId: latestEventId(previous.lastWebhookEventId, receipt.eventId),
179
+ lastWebhookIdempotencyKey: receipt.idempotencyKey
180
+ }
181
+ };
182
+ const change = rowDiff(scope.entity, previous.items, nextItems);
183
+ if (options.isCurrent && !options.isCurrent()) return { outcome: "superseded" };
184
+ if (!await store.commit(scope, previous.revision, {
185
+ ...change,
186
+ wholesaleFeedVersion,
187
+ pricingVersion,
188
+ cacheScope,
189
+ lastSyncedAt: next.lastSyncedAt,
190
+ ...receipt && {
191
+ webhookReceipt: receipt,
192
+ lastWebhookEventId: next.lastWebhookEventId,
193
+ lastWebhookIdempotencyKey: receipt.idempotencyKey
194
+ }
195
+ }))
196
+ return { outcome: "superseded" };
197
+ return unchanged ? { outcome: "unchanged", snapshot: next } : {
198
+ outcome: "applied",
199
+ snapshot: next,
200
+ diff: diffWholesaleFeed(scope.entity, previous.items, nextItems, cacheScope, true)
201
+ };
202
+ }
203
+ async function applyWholesaleFeedWebhook(options) {
204
+ options = {
205
+ ...options,
206
+ scope: structuredClone(options.scope),
207
+ account: structuredClone(options.account),
208
+ webhookScope: structuredClone(options.webhookScope),
209
+ webhook: structuredClone(options.webhook)
210
+ };
211
+ for (let attempt = 0; attempt < 3; attempt++) {
212
+ const outcome = await applyWebhookOnce(options);
213
+ if (outcome.outcome !== "superseded" || options.isCurrent && !options.isCurrent()) return outcome;
214
+ }
215
+ return { outcome: "superseded" };
216
+ }
217
+ async function applyWebhookOnce(options) {
218
+ const { webhook, scope, store } = options;
219
+ (0, import_webhook_notification.parseWholesaleFeedWebhookNotification)(webhook);
220
+ const event = webhook.event;
221
+ if (!event || webhook.notification_type !== event.event_type || webhook.notification_id !== event.event_id)
222
+ throw new Error("Wholesale webhook event identity does not match its envelope.");
223
+ const entity = event.event_type === "wholesale_feed.bulk_change" ? event.payload.affected_entity_type : event.event_type.startsWith("product.") ? "product" : event.event_type.startsWith("signal.") ? "signal" : void 0;
224
+ if (entity !== scope.entity) throw new Error("Wholesale webhook entity does not match this mirror.");
225
+ const accountId = options.webhookScope?.accountId ?? (options.account && "account_id" in options.account ? options.account.account_id : void 0);
226
+ if (!accountId) throw new Error("Wholesale webhook application requires an expected account_id.");
227
+ if (webhook.account_id !== accountId || options.webhookScope?.subscriberId && options.webhookScope.subscriberId !== webhook.subscriber_id)
228
+ throw new Error("Wholesale webhook subscription does not match this mirror.");
229
+ const applies = event.payload.applies_to;
230
+ if (applies?.scope && applies.scope !== webhook.cache_scope || webhook.cache_scope === "account" && applies?.account_ids && !applies.account_ids.includes(accountId))
231
+ throw new Error("Wholesale webhook cache scope does not match this mirror.");
232
+ const previous = await store.read(scope);
233
+ if (await store.hasWebhookReceipt(scope, webhook.idempotency_key) || previous.lastWebhookEventId === event.event_id)
234
+ return { outcome: "unchanged", snapshot: previous };
235
+ if (previous.cacheScope !== webhook.cache_scope)
236
+ return refreshMirror(
237
+ { ...options, conditional: false },
238
+ { eventId: event.event_id, idempotencyKey: webhook.idempotency_key },
239
+ webhook.wholesale_feed_version
240
+ );
241
+ const stale = previous.lastWebhookEventId && isUuidV7(event.event_id) && isUuidV7(previous.lastWebhookEventId) && event.event_id.toLowerCase() <= previous.lastWebhookEventId.toLowerCase();
242
+ if (event.event_type === "wholesale_feed.bulk_change" || stale || !previous.wholesaleFeedVersion || webhook.previous_wholesale_feed_version !== previous.wholesaleFeedVersion) {
243
+ return refreshMirror(
244
+ options,
245
+ { eventId: event.event_id, idempotencyKey: webhook.idempotency_key },
246
+ stale ? void 0 : webhook.wholesale_feed_version
247
+ );
248
+ }
249
+ const items = applyWholesaleFeedEvent(scope.entity, previous.items, event);
250
+ if (!items)
251
+ return refreshMirror(
252
+ options,
253
+ { eventId: event.event_id, idempotencyKey: webhook.idempotency_key },
254
+ webhook.wholesale_feed_version
255
+ );
256
+ const next = {
257
+ ...previous,
258
+ items,
259
+ revision: previous.revision + 1,
260
+ wholesaleFeedVersion: webhook.wholesale_feed_version,
261
+ cacheScope: webhook.cache_scope,
262
+ lastSyncedAt: (/* @__PURE__ */ new Date()).toISOString(),
263
+ lastWebhookEventId: event.event_id,
264
+ lastWebhookIdempotencyKey: webhook.idempotency_key,
265
+ error: void 0
266
+ };
267
+ if (options.isCurrent && !options.isCurrent()) return { outcome: "superseded" };
268
+ if (!await store.commit(scope, previous.revision, {
269
+ ...rowDiff(scope.entity, previous.items, items),
270
+ wholesaleFeedVersion: next.wholesaleFeedVersion,
271
+ pricingVersion: next.pricingVersion,
272
+ cacheScope: next.cacheScope,
273
+ lastSyncedAt: next.lastSyncedAt,
274
+ lastWebhookEventId: event.event_id,
275
+ lastWebhookIdempotencyKey: webhook.idempotency_key,
276
+ webhookReceipt: { eventId: event.event_id, idempotencyKey: webhook.idempotency_key }
277
+ }))
278
+ return { outcome: "superseded" };
279
+ return { outcome: "applied", snapshot: next, diff: [event] };
280
+ }
281
+ function applyWholesaleFeedEvent(entity, previous, event) {
282
+ const items = new Map(previous.map((item) => [itemId(entity, item), item]));
283
+ const payload = event.payload;
284
+ const id = entity === "product" ? payload.product_id : payload.signal_agent_segment_id;
285
+ if (!id) return void 0;
286
+ if (event.event_type.endsWith(".removed")) items.delete(id);
287
+ else if (event.event_type.endsWith(".priced")) {
288
+ const prior = items.get(id);
289
+ if (!prior || !Array.isArray(payload.pricing_options)) return void 0;
290
+ items.set(id, { ...prior, pricing_options: payload.pricing_options });
291
+ } else {
292
+ const item = entity === "product" ? payload.product : payload.signal;
293
+ if (!item || itemId(entity, item) !== id) return void 0;
294
+ items.set(id, item);
295
+ }
296
+ return [...items.values()];
297
+ }
298
+ function diffWholesaleFeed(entity, previous, next, cacheScope, includeAllChanges = false) {
299
+ const before = new Map(previous.map((item) => [itemId(entity, item), item]));
300
+ const after = new Map(next.map((item) => [itemId(entity, item), item]));
301
+ const events = [];
302
+ const emit = (id, action, data) => events.push({
303
+ event_id: (0, import_node_crypto.randomUUID)(),
304
+ event_type: `${entity}.${action}`,
305
+ entity_type: entity,
306
+ entity_id: id,
307
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
308
+ payload: {
309
+ [entity === "product" ? "product_id" : "signal_agent_segment_id"]: id,
310
+ ...data,
311
+ applies_to: { scope: cacheScope }
312
+ }
313
+ });
314
+ for (const [id, item] of after) {
315
+ const prior = before.get(id);
316
+ if (!prior) emit(id, "created", { [entity]: item });
317
+ else if (!(0, import_node_util.isDeepStrictEqual)(prior.pricing_options, item.pricing_options)) {
318
+ emit(id, "priced", { pricing_options: item.pricing_options ?? [] });
319
+ if (includeAllChanges && !(0, import_node_util.isDeepStrictEqual)({ ...prior, pricing_options: item.pricing_options }, item))
320
+ emit(id, "updated", { [entity]: item });
321
+ } else if (!(0, import_node_util.isDeepStrictEqual)(prior, item)) emit(id, "updated", { [entity]: item });
322
+ }
323
+ for (const id of before.keys()) if (!after.has(id)) emit(id, "removed", {});
324
+ return events;
325
+ }
326
+ function itemId(entity, item) {
327
+ return entity === "product" ? item.product_id : item.signal_agent_segment_id;
328
+ }
329
+ function rowDiff(entity, previous, next) {
330
+ const before = new Map(previous.map((item) => [itemId(entity, item), item]));
331
+ const ids = new Set(next.map((item) => itemId(entity, item)));
332
+ return {
333
+ upserts: next.filter((item) => !(0, import_node_util.isDeepStrictEqual)(before.get(itemId(entity, item)), item)),
334
+ deletes: [...before.keys()].filter((id) => !ids.has(id))
335
+ };
336
+ }
337
+ function isUuidV7(value) {
338
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
339
+ }
340
+ function latestEventId(previous, next) {
341
+ return previous && isUuidV7(previous) && (!isUuidV7(next) || previous.toLowerCase() >= next.toLowerCase()) ? previous : next;
342
+ }
343
+ // Annotate the CommonJS export names for ESM import in node:
344
+ 0 && (module.exports = {
345
+ InMemoryWholesaleFeedMirrorStore,
346
+ applyWholesaleFeedEvent,
347
+ applyWholesaleFeedWebhook,
348
+ diffWholesaleFeed,
349
+ refreshWholesaleFeed
350
+ });