@adcp/sdk 14.0.0 → 14.2.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.
- package/dist/lib/adapters/implicit-account-store.d.mts +12 -7
- package/dist/lib/adapters/implicit-account-store.d.ts +12 -7
- package/dist/lib/adapters/implicit-account-store.js +69 -15
- package/dist/lib/adapters/implicit-account-store.mjs +69 -15
- package/dist/lib/core/AgentClient.d.mts +1 -0
- package/dist/lib/core/AgentClient.d.ts +1 -0
- package/dist/lib/core/AgentClient.js +3 -0
- package/dist/lib/core/AgentClient.mjs +3 -0
- package/dist/lib/core/SingleAgentClient.d.mts +35 -2
- package/dist/lib/core/SingleAgentClient.d.ts +35 -2
- package/dist/lib/core/SingleAgentClient.js +377 -36
- package/dist/lib/core/SingleAgentClient.mjs +387 -38
- package/dist/lib/core/TaskExecutor.d.mts +3 -1
- package/dist/lib/core/TaskExecutor.d.ts +3 -1
- package/dist/lib/core/TaskExecutor.js +17 -12
- package/dist/lib/core/TaskExecutor.mjs +17 -12
- package/dist/lib/core/account-key.d.mts +3 -0
- package/dist/lib/core/account-key.d.ts +3 -0
- package/dist/lib/core/account-key.js +41 -0
- package/dist/lib/core/account-key.mjs +17 -0
- package/dist/lib/core/account-resolution.d.mts +2 -0
- package/dist/lib/core/account-resolution.d.ts +2 -0
- package/dist/lib/core/buyer-account-registry.d.mts +93 -0
- package/dist/lib/core/buyer-account-registry.d.ts +93 -0
- package/dist/lib/core/buyer-account-registry.js +602 -0
- package/dist/lib/core/buyer-account-registry.mjs +578 -0
- package/dist/lib/core/product-cache.d.mts +18 -0
- package/dist/lib/core/product-cache.d.ts +18 -0
- package/dist/lib/core/product-cache.js +137 -0
- package/dist/lib/core/product-cache.mjs +112 -0
- package/dist/lib/errors/index.d.mts +40 -1
- package/dist/lib/errors/index.d.ts +40 -1
- package/dist/lib/errors/index.js +69 -3
- package/dist/lib/errors/index.mjs +64 -3
- package/dist/lib/governance/authorization.d.mts +17 -1
- package/dist/lib/governance/authorization.d.ts +17 -1
- package/dist/lib/governance/authorization.js +55 -7
- package/dist/lib/governance/authorization.mjs +59 -7
- package/dist/lib/governance/index.d.mts +2 -2
- package/dist/lib/governance/index.d.ts +2 -2
- package/dist/lib/governance/index.js +2 -0
- package/dist/lib/governance/index.mjs +3 -1
- package/dist/lib/index.d.mts +7 -4
- package/dist/lib/index.d.ts +7 -4
- package/dist/lib/index.js +29 -0
- package/dist/lib/index.mjs +31 -1
- package/dist/lib/net/agent-transport-fetch.d.mts +4 -0
- package/dist/lib/net/agent-transport-fetch.d.ts +4 -0
- package/dist/lib/net/agent-transport-fetch.js +18 -5
- package/dist/lib/net/agent-transport-fetch.mjs +17 -5
- package/dist/lib/protocols/a2a.js +9 -1
- package/dist/lib/protocols/a2a.mjs +9 -1
- package/dist/lib/protocols/index.js +9 -2
- package/dist/lib/protocols/index.mjs +9 -2
- package/dist/lib/protocols/mcp-modern.js +2 -1
- package/dist/lib/protocols/mcp-modern.mjs +2 -1
- package/dist/lib/protocols/mcp.js +5 -2
- package/dist/lib/protocols/mcp.mjs +5 -2
- package/dist/lib/protocols/rawResponseCapture.d.mts +6 -0
- package/dist/lib/protocols/rawResponseCapture.d.ts +6 -0
- package/dist/lib/protocols/rawResponseCapture.js +41 -29
- package/dist/lib/protocols/rawResponseCapture.mjs +40 -29
- package/dist/lib/protocols/signedRequestRejection.d.mts +9 -0
- package/dist/lib/protocols/signedRequestRejection.d.ts +9 -0
- package/dist/lib/protocols/signedRequestRejection.js +209 -0
- package/dist/lib/protocols/signedRequestRejection.mjs +189 -0
- package/dist/lib/protocols/transportDiagnostics.d.mts +1 -0
- package/dist/lib/protocols/transportDiagnostics.d.ts +1 -0
- package/dist/lib/protocols/transportDiagnostics.js +2 -0
- package/dist/lib/protocols/transportDiagnostics.mjs +1 -0
- package/dist/lib/registry/types.generated.d.mts +112 -45
- package/dist/lib/registry/types.generated.d.ts +112 -45
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/server/account-provisioning.d.mts +2 -0
- package/dist/lib/server/account-provisioning.d.ts +2 -0
- package/dist/lib/server/account-provisioning.js +30 -0
- package/dist/lib/server/account-provisioning.mjs +6 -0
- package/dist/lib/server/account-reference-warnings.d.mts +12 -0
- package/dist/lib/server/account-reference-warnings.d.ts +12 -0
- package/dist/lib/server/account-reference-warnings.js +48 -0
- package/dist/lib/server/account-reference-warnings.mjs +23 -0
- package/dist/lib/server/auth-signature.js +1 -0
- package/dist/lib/server/auth-signature.mjs +1 -0
- package/dist/lib/server/create-adcp-server.d.mts +34 -0
- package/dist/lib/server/create-adcp-server.d.ts +34 -0
- package/dist/lib/server/create-adcp-server.js +225 -14
- package/dist/lib/server/create-adcp-server.mjs +225 -14
- package/dist/lib/server/decisioning/account.d.mts +2 -0
- package/dist/lib/server/decisioning/account.d.ts +2 -0
- package/dist/lib/server/decisioning/runtime/from-platform.js +49 -10
- package/dist/lib/server/decisioning/runtime/from-platform.mjs +49 -10
- package/dist/lib/server/index.d.mts +2 -2
- package/dist/lib/server/index.d.ts +2 -2
- package/dist/lib/server/index.js +2 -0
- package/dist/lib/server/index.mjs +3 -1
- package/dist/lib/signing/agent-resolver/consistency.d.mts +6 -13
- package/dist/lib/signing/agent-resolver/consistency.d.ts +6 -13
- package/dist/lib/signing/agent-resolver/consistency.js +0 -1
- package/dist/lib/signing/agent-resolver/consistency.mjs +0 -1
- package/dist/lib/signing/agent-resolver/errors.d.mts +3 -1
- package/dist/lib/signing/agent-resolver/errors.d.ts +3 -1
- package/dist/lib/signing/agent-resolver/errors.js +6 -0
- package/dist/lib/signing/agent-resolver/errors.mjs +6 -0
- package/dist/lib/signing/agent-resolver/fetch-helpers.d.mts +11 -0
- package/dist/lib/signing/agent-resolver/fetch-helpers.d.ts +11 -0
- package/dist/lib/signing/agent-resolver/fetch-helpers.js +37 -2
- package/dist/lib/signing/agent-resolver/fetch-helpers.mjs +40 -3
- package/dist/lib/signing/agent-resolver/jwks-set.js +24 -3
- package/dist/lib/signing/agent-resolver/jwks-set.mjs +24 -3
- package/dist/lib/signing/agent-resolver/legacy-brand.d.mts +15 -0
- package/dist/lib/signing/agent-resolver/legacy-brand.d.ts +15 -0
- package/dist/lib/signing/agent-resolver/legacy-brand.js +60 -0
- package/dist/lib/signing/agent-resolver/legacy-brand.mjs +36 -0
- package/dist/lib/signing/agent-resolver/operator-authorization.d.mts +13 -0
- package/dist/lib/signing/agent-resolver/operator-authorization.d.ts +13 -0
- package/dist/lib/signing/agent-resolver/operator-authorization.js +108 -0
- package/dist/lib/signing/agent-resolver/operator-authorization.mjs +84 -0
- package/dist/lib/signing/agent-resolver/resolve-agent.d.mts +14 -4
- package/dist/lib/signing/agent-resolver/resolve-agent.d.ts +14 -4
- package/dist/lib/signing/agent-resolver/resolve-agent.js +148 -137
- package/dist/lib/signing/agent-resolver/resolve-agent.mjs +157 -139
- package/dist/lib/signing/agent-resolver/resolved-agent-jwks.d.mts +7 -1
- package/dist/lib/signing/agent-resolver/resolved-agent-jwks.d.ts +7 -1
- package/dist/lib/signing/agent-resolver/resolved-agent-jwks.js +50 -17
- package/dist/lib/signing/agent-resolver/resolved-agent-jwks.mjs +50 -17
- package/dist/lib/signing/agent-resolver/select-agent.d.mts +14 -15
- package/dist/lib/signing/agent-resolver/select-agent.d.ts +14 -15
- package/dist/lib/signing/agent-resolver/select-agent.js +98 -13
- package/dist/lib/signing/agent-resolver/select-agent.mjs +95 -13
- package/dist/lib/signing/brand-jwks.d.mts +29 -75
- package/dist/lib/signing/brand-jwks.d.ts +29 -75
- package/dist/lib/signing/brand-jwks.js +120 -182
- package/dist/lib/signing/brand-jwks.mjs +120 -182
- package/dist/lib/signing/errors.d.mts +7 -3
- package/dist/lib/signing/errors.d.ts +7 -3
- package/dist/lib/signing/errors.js +7 -2
- package/dist/lib/signing/errors.mjs +7 -2
- package/dist/lib/signing/jwks-https.d.mts +7 -0
- package/dist/lib/signing/jwks-https.d.ts +7 -0
- package/dist/lib/signing/jwks-https.js +31 -8
- package/dist/lib/signing/jwks-https.mjs +31 -8
- package/dist/lib/signing/jwks.d.mts +8 -0
- package/dist/lib/signing/jwks.d.ts +8 -0
- package/dist/lib/signing/middleware.js +2 -1
- package/dist/lib/signing/middleware.mjs +2 -1
- package/dist/lib/signing/publisher-pins.d.mts +11 -0
- package/dist/lib/signing/publisher-pins.d.ts +11 -0
- package/dist/lib/signing/publisher-pins.js +125 -0
- package/dist/lib/signing/publisher-pins.mjs +101 -0
- package/dist/lib/signing/server.d.mts +1 -0
- package/dist/lib/signing/server.d.ts +1 -0
- package/dist/lib/signing/types.d.mts +5 -0
- package/dist/lib/signing/types.d.ts +5 -0
- package/dist/lib/signing/verifier.js +50 -5
- package/dist/lib/signing/verifier.mjs +50 -5
- package/dist/lib/signing/webhook-verifier.d.mts +7 -2
- package/dist/lib/signing/webhook-verifier.d.ts +7 -2
- package/dist/lib/signing/webhook-verifier.js +42 -2
- package/dist/lib/signing/webhook-verifier.mjs +42 -2
- package/dist/lib/testing/storyboard/account-policy.d.mts +2 -0
- package/dist/lib/testing/storyboard/account-policy.d.ts +2 -0
- package/dist/lib/testing/storyboard/account-policy.js +35 -0
- package/dist/lib/testing/storyboard/account-policy.mjs +11 -0
- package/dist/lib/testing/storyboard/context.js +6 -0
- package/dist/lib/testing/storyboard/context.mjs +6 -0
- package/dist/lib/testing/storyboard/request-builder.js +12 -2
- package/dist/lib/testing/storyboard/request-builder.mjs +12 -2
- package/dist/lib/testing/storyboard/runner.js +3 -2
- package/dist/lib/testing/storyboard/runner.mjs +3 -2
- package/dist/lib/testing/storyboard/validations.d.mts +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts +1 -1
- package/dist/lib/types/accept-proposal.d.ts +19 -1
- package/dist/lib/types/buy-products.d.ts +19 -1
- package/dist/lib/types/check-governance.d.ts +19 -1
- package/dist/lib/types/comply-test-controller.d.ts +19 -1
- package/dist/lib/types/control-media-buy.d.ts +19 -1
- package/dist/lib/types/core.generated.d.mts +14 -1
- package/dist/lib/types/core.generated.d.ts +14 -1
- package/dist/lib/types/create-media-buy.d.ts +14 -1
- package/dist/lib/types/get-media-buys.d.ts +14 -1
- package/dist/lib/types/get-products.d.ts +19 -1
- package/dist/lib/types/list-products.d.ts +19 -1
- package/dist/lib/types/refine-proposals.d.ts +19 -1
- package/dist/lib/types/request-proposals.d.ts +19 -1
- package/dist/lib/types/schemas.generated.d.ts +12 -3
- package/dist/lib/types/schemas.generated.js +4 -1
- package/dist/lib/types/schemas.generated.mjs +4 -1
- package/dist/lib/types/tools.generated.d.mts +14 -1
- package/dist/lib/types/tools.generated.d.ts +14 -1
- package/dist/lib/types/update-media-buy.d.ts +14 -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.mjs +3 -3
- package/dist/lib/webhooks/index.d.mts +24 -0
- package/dist/lib/webhooks/index.d.ts +24 -0
- package/dist/lib/webhooks/index.js +50 -24
- package/dist/lib/webhooks/index.mjs +49 -24
- package/dist/lib/wholesale-feed-sync/index.d.mts +2 -0
- package/dist/lib/wholesale-feed-sync/index.d.ts +2 -0
- package/dist/lib/wholesale-feed-sync/index.js +7 -0
- package/dist/lib/wholesale-feed-sync/index.mjs +4 -0
- package/dist/lib/wholesale-feed-sync/mirror.d.mts +97 -0
- package/dist/lib/wholesale-feed-sync/mirror.d.ts +97 -0
- package/dist/lib/wholesale-feed-sync/mirror.js +350 -0
- package/dist/lib/wholesale-feed-sync/mirror.mjs +322 -0
- package/dist/lib/wholesale-feed-sync/sync.d.mts +13 -29
- package/dist/lib/wholesale-feed-sync/sync.d.ts +13 -29
- package/dist/lib/wholesale-feed-sync/sync.js +208 -281
- package/dist/lib/wholesale-feed-sync/sync.mjs +213 -281
- package/dist/lib/wholesale-feed-sync/types.d.mts +4 -3
- package/dist/lib/wholesale-feed-sync/types.d.ts +4 -3
- package/docs/README.md +6 -0
- package/docs/TYPE-SUMMARY.md +2 -2
- package/docs/guides/BUILD-AN-AGENT.md +2 -2
- package/docs/guides/BUYER-QUICKSTART-3.2.md +2 -0
- package/docs/guides/BUYER-STORAGE.md +3 -0
- package/docs/guides/FIRST-CALL-TO-A-SELLER.md +106 -0
- package/docs/guides/SIGNING-GUIDE.md +16 -7
- package/docs/guides/account-resolution.md +132 -10
- package/docs/llms.txt +3 -2
- package/docs/migration-14.0-to-14.1.md +85 -0
- package/docs/migration-14.x-rc-worksheet.md +4 -4
- package/docs/migration-4.x-to-5.x.md +1 -0
- package/docs/migration-agent-resolution-3.3.md +125 -0
- package/docs/recipes/verifying-inbound-webhooks.md +60 -15
- package/package.json +3 -2
- package/skills/adcp-brand.previous/SKILL.md +0 -200
- package/skills/adcp-creative.previous/SKILL.md +0 -305
- package/skills/adcp-governance.previous/SKILL.md +0 -566
- package/skills/adcp-measurement.previous/SKILL.md +0 -136
- package/skills/adcp-media-buy.previous/SKILL.md +0 -556
- package/skills/adcp-si.previous/SKILL.md +0 -206
- package/skills/adcp-signals.previous/SKILL.md +0 -204
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
import { EventEmitter } from "node:events";
|
|
2
2
|
import { isDeepStrictEqual } from "node:util";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
refreshWholesaleFeed,
|
|
5
|
+
InMemoryWholesaleFeedMirrorStore,
|
|
6
|
+
applyWholesaleFeedEvent,
|
|
7
|
+
diffWholesaleFeed
|
|
8
|
+
} from "./mirror.mjs";
|
|
4
9
|
import { assertLegacyWholesaleFeedRepresentation } from "./webhook-notification.mjs";
|
|
10
|
+
const bootstrapReadFailures = /* @__PURE__ */ new WeakSet();
|
|
11
|
+
function supersededRepairError() {
|
|
12
|
+
const error = new Error("WholesaleFeedSync: repair superseded repeatedly; retry webhook delivery.");
|
|
13
|
+
bootstrapReadFailures.add(error);
|
|
14
|
+
return error;
|
|
15
|
+
}
|
|
5
16
|
const DEFAULT_PROBE_INTERVAL_MS = 6e5;
|
|
6
17
|
const DEFAULT_CAPABILITY_REFRESH_INTERVAL_MS = 864e5;
|
|
7
18
|
const DEFAULT_PERSISTENCE_TIMEOUT_MS = 3e4;
|
|
8
|
-
const DEFAULT_BOOTSTRAP_PAGE_LIMIT = 100;
|
|
9
19
|
const VERSION_MISMATCH_RECOVERY_ATTEMPTS = 3;
|
|
10
20
|
const VERSION_MISMATCH_RECOVERY_BACKOFF_MS = 5;
|
|
11
21
|
class WholesaleFeedSync extends EventEmitter {
|
|
@@ -46,11 +56,10 @@ class WholesaleFeedSync extends EventEmitter {
|
|
|
46
56
|
probeTimer = null;
|
|
47
57
|
capabilityTimer = null;
|
|
48
58
|
lifecycleEpoch = 0;
|
|
59
|
+
mirrorRevision = 0;
|
|
49
60
|
/**
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* agents (products-feed + signals-wholesale-only, or vice versa) this
|
|
53
|
-
* may differ from `signals.mode`.
|
|
61
|
+
* Product index view. `mode` identifies the product sync strategy and may
|
|
62
|
+
* differ from `signals.mode` for agents with mixed feed capabilities.
|
|
54
63
|
*/
|
|
55
64
|
products = {
|
|
56
65
|
list: () => [...this.productIndex.values()],
|
|
@@ -85,19 +94,12 @@ class WholesaleFeedSync extends EventEmitter {
|
|
|
85
94
|
},
|
|
86
95
|
_mode: "manual",
|
|
87
96
|
/**
|
|
88
|
-
*
|
|
89
|
-
* `
|
|
90
|
-
* agent only supports brief-mode discovery — `signals.list()` will
|
|
91
|
-
* be empty until adopters call into the agent with their own briefs.
|
|
97
|
+
* True when `get_signals` supports `discovery_mode: 'wholesale'`.
|
|
98
|
+
* Otherwise `list()` is empty; use client brief discovery instead.
|
|
92
99
|
*/
|
|
93
100
|
queryable: true
|
|
94
101
|
};
|
|
95
|
-
|
|
96
|
-
* One-shot console warning when adopters call `signals.list()` or
|
|
97
|
-
* `signals.search()` against an agent that doesn't support wholesale
|
|
98
|
-
* signal enumeration. Without this, empty results read as "no signals
|
|
99
|
-
* match" rather than "the agent doesn't browse, only briefs."
|
|
100
|
-
*/
|
|
102
|
+
// Warn once when empty signal results reflect missing wholesale support.
|
|
101
103
|
warnIfSignalsNotQueryable() {
|
|
102
104
|
if (this.signals.queryable || this.signalsQueryableWarned) return;
|
|
103
105
|
if (this._state === "idle" || this._state === "bootstrapping") return;
|
|
@@ -123,14 +125,9 @@ class WholesaleFeedSync extends EventEmitter {
|
|
|
123
125
|
}
|
|
124
126
|
// ====== Lifecycle ======
|
|
125
127
|
/**
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
* Safe to call repeatedly — concurrent calls await the in-flight
|
|
131
|
-
* bootstrap and return when it completes (no duplicate bootstrap, no
|
|
132
|
-
* silent drop). Sequential calls re-probe capabilities and re-bootstrap,
|
|
133
|
-
* equivalent to calling `refresh()` after a mode upgrade.
|
|
128
|
+
* Discover capabilities, bootstrap the mirror, and schedule auto-poll probes
|
|
129
|
+
* for versioned sellers. Concurrent calls share the bootstrap; sequential
|
|
130
|
+
* calls re-probe capabilities and refresh the mirror.
|
|
134
131
|
*/
|
|
135
132
|
async start() {
|
|
136
133
|
if (this.startPromise && this.startPromiseEpoch === this.lifecycleEpoch) return this.startPromise;
|
|
@@ -149,12 +146,13 @@ class WholesaleFeedSync extends EventEmitter {
|
|
|
149
146
|
const epoch = this.lifecycleEpoch;
|
|
150
147
|
if (!await this.restorePersistedState(epoch)) return;
|
|
151
148
|
if (!await this.resolveMode(epoch)) return;
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
149
|
+
try {
|
|
150
|
+
await this.bootstrapWithRetries({ epoch });
|
|
151
|
+
} finally {
|
|
152
|
+
if (this.isLifecycleCurrent(epoch)) {
|
|
153
|
+
if (this._mode === "auto-poll") this.scheduleProbe(epoch);
|
|
154
|
+
if (this.capabilityRefreshIntervalMs > 0) this.scheduleCapabilityRefresh(epoch);
|
|
155
|
+
}
|
|
158
156
|
}
|
|
159
157
|
}
|
|
160
158
|
/** Stop all background activity. Preserves in-memory state and version tokens. */
|
|
@@ -164,7 +162,8 @@ class WholesaleFeedSync extends EventEmitter {
|
|
|
164
162
|
if (this.capabilityTimer) clearTimeout(this.capabilityTimer);
|
|
165
163
|
this.probeTimer = null;
|
|
166
164
|
this.capabilityTimer = null;
|
|
167
|
-
if (this._state === "syncing" || this._state === "bootstrapping"
|
|
165
|
+
if (this._state === "syncing" || this._state === "bootstrapping" || this._state === "degraded")
|
|
166
|
+
this.setState("idle");
|
|
168
167
|
}
|
|
169
168
|
/**
|
|
170
169
|
* Stop, clear all indexes and version tokens. Call `start()` again to
|
|
@@ -172,6 +171,7 @@ class WholesaleFeedSync extends EventEmitter {
|
|
|
172
171
|
*/
|
|
173
172
|
async reset() {
|
|
174
173
|
this.stop();
|
|
174
|
+
++this.mirrorRevision;
|
|
175
175
|
this.productIndex.clear();
|
|
176
176
|
this.signalIndex.clear();
|
|
177
177
|
this.productWholesaleFeedVersion = void 0;
|
|
@@ -197,7 +197,7 @@ class WholesaleFeedSync extends EventEmitter {
|
|
|
197
197
|
async refresh() {
|
|
198
198
|
const epoch = this.lifecycleEpoch;
|
|
199
199
|
this.emit("resyncing", { reason: "manual" });
|
|
200
|
-
await this.
|
|
200
|
+
await this.bootstrapWithRetries({ emitDiffs: true, epoch, propagateFailure: true });
|
|
201
201
|
}
|
|
202
202
|
/**
|
|
203
203
|
* Apply one legacy-view account-level wholesale feed webhook to the local mirror.
|
|
@@ -246,6 +246,11 @@ class WholesaleFeedSync extends EventEmitter {
|
|
|
246
246
|
this.rememberLastWebhookEventId(event.event_id);
|
|
247
247
|
throw err;
|
|
248
248
|
}
|
|
249
|
+
if (!this._lastSyncedAt && event.event_type !== "wholesale_feed.bulk_change") {
|
|
250
|
+
if (!await this.recoverFromVersionMismatch(event, epoch)) return;
|
|
251
|
+
await this.markWebhookProcessed(dedupeKey, eventDedupeKey);
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
249
254
|
if (webhook.previous_wholesale_feed_version && currentVersion && webhook.previous_wholesale_feed_version !== currentVersion) {
|
|
250
255
|
if (!await this.recoverFromVersionMismatch(event, epoch)) return;
|
|
251
256
|
await this.markWebhookProcessed(dedupeKey, eventDedupeKey);
|
|
@@ -256,8 +261,10 @@ class WholesaleFeedSync extends EventEmitter {
|
|
|
256
261
|
try {
|
|
257
262
|
if (!await this.recoverFromBulkChange(event, epoch)) return;
|
|
258
263
|
} catch (err) {
|
|
259
|
-
|
|
260
|
-
|
|
264
|
+
if (!(err instanceof Error && bootstrapReadFailures.has(err))) {
|
|
265
|
+
await this.markWebhookProcessed(dedupeKey, eventDedupeKey);
|
|
266
|
+
this.rememberLastWebhookEventId(event.event_id);
|
|
267
|
+
}
|
|
261
268
|
throw err;
|
|
262
269
|
}
|
|
263
270
|
await this.markWebhookProcessed(dedupeKey, eventDedupeKey);
|
|
@@ -324,10 +331,24 @@ class WholesaleFeedSync extends EventEmitter {
|
|
|
324
331
|
return true;
|
|
325
332
|
}
|
|
326
333
|
// ====== Private: bootstrap (wholesale enumeration) ======
|
|
334
|
+
async bootstrapWithRetries(options) {
|
|
335
|
+
for (let attempt = 0; attempt < VERSION_MISMATCH_RECOVERY_ATTEMPTS; attempt++) {
|
|
336
|
+
const revision = this.mirrorRevision;
|
|
337
|
+
if (await this.bootstrap({ ...options, conditional: attempt === 0 ? void 0 : false })) return;
|
|
338
|
+
if (!this.isLifecycleCurrent(options.epoch) || revision === this.mirrorRevision) return;
|
|
339
|
+
}
|
|
340
|
+
const error = new Error("WholesaleFeedSync: refresh superseded repeatedly; retry start or refresh.");
|
|
341
|
+
this.handleBootstrapFailure({ error });
|
|
342
|
+
throw error;
|
|
343
|
+
}
|
|
327
344
|
async bootstrap(options = {}) {
|
|
328
345
|
const epoch = options.epoch ?? this.lifecycleEpoch;
|
|
329
346
|
if (!this.isLifecycleCurrent(epoch)) return false;
|
|
330
347
|
this.setState("bootstrapping");
|
|
348
|
+
const previousLastSyncedAt = this._lastSyncedAt;
|
|
349
|
+
const expectedRevision = this.mirrorRevision;
|
|
350
|
+
let reportedFailure;
|
|
351
|
+
let committed = false;
|
|
331
352
|
try {
|
|
332
353
|
const previousProducts = new Map(this.productIndex);
|
|
333
354
|
const previousSignals = new Map(this.signalIndex);
|
|
@@ -339,14 +360,50 @@ class WholesaleFeedSync extends EventEmitter {
|
|
|
339
360
|
const refreshProducts = entities !== "signals";
|
|
340
361
|
const refreshSignals = entities !== "products" && this.signals.queryable;
|
|
341
362
|
if (refreshProducts) {
|
|
342
|
-
productResult = await this.bootstrapProducts(epoch);
|
|
363
|
+
productResult = await this.bootstrapProducts(epoch, options.conditional);
|
|
343
364
|
if (productResult.cancelled) return false;
|
|
365
|
+
if (this.mirrorRevision !== expectedRevision) {
|
|
366
|
+
this.setState(this._lastSyncedAt ? "syncing" : "idle");
|
|
367
|
+
return false;
|
|
368
|
+
}
|
|
369
|
+
if (productResult.failure) {
|
|
370
|
+
const failure = productResult.failure;
|
|
371
|
+
reportedFailure = failure.error;
|
|
372
|
+
this.handleBootstrapFailure(failure);
|
|
373
|
+
if (options.propagateFailure) {
|
|
374
|
+
bootstrapReadFailures.add(failure.error);
|
|
375
|
+
if (failure.adcpError) Object.assign(failure.error, { adcpError: failure.adcpError });
|
|
376
|
+
throw failure.error;
|
|
377
|
+
}
|
|
378
|
+
return false;
|
|
379
|
+
}
|
|
344
380
|
}
|
|
345
381
|
if (refreshSignals) {
|
|
346
|
-
signalResult = await this.bootstrapSignals(epoch);
|
|
382
|
+
signalResult = await this.bootstrapSignals(epoch, options.conditional);
|
|
347
383
|
if (signalResult.cancelled) return false;
|
|
384
|
+
if (this.mirrorRevision !== expectedRevision) {
|
|
385
|
+
this.setState(this._lastSyncedAt ? "syncing" : "idle");
|
|
386
|
+
return false;
|
|
387
|
+
}
|
|
388
|
+
if (signalResult.failure) {
|
|
389
|
+
const failure = signalResult.failure;
|
|
390
|
+
reportedFailure = failure.error;
|
|
391
|
+
this.handleBootstrapFailure(failure);
|
|
392
|
+
if (options.propagateFailure) {
|
|
393
|
+
bootstrapReadFailures.add(failure.error);
|
|
394
|
+
if (failure.adcpError) Object.assign(failure.error, { adcpError: failure.adcpError });
|
|
395
|
+
throw failure.error;
|
|
396
|
+
}
|
|
397
|
+
return false;
|
|
398
|
+
}
|
|
348
399
|
}
|
|
349
400
|
if (!this.isLifecycleCurrent(epoch)) return false;
|
|
401
|
+
if (this.mirrorRevision !== expectedRevision) {
|
|
402
|
+
this.setState(this._lastSyncedAt ? "syncing" : "idle");
|
|
403
|
+
return false;
|
|
404
|
+
}
|
|
405
|
+
++this.mirrorRevision;
|
|
406
|
+
committed = true;
|
|
350
407
|
if (refreshProducts && productResult) {
|
|
351
408
|
this.commitProductMetadata(productResult.metadata);
|
|
352
409
|
if (!productResult.unchanged) {
|
|
@@ -378,77 +435,91 @@ class WholesaleFeedSync extends EventEmitter {
|
|
|
378
435
|
return true;
|
|
379
436
|
} catch (err) {
|
|
380
437
|
if (!this.isLifecycleCurrent(epoch)) return false;
|
|
381
|
-
this.
|
|
438
|
+
if (!committed && this.mirrorRevision !== expectedRevision && err !== reportedFailure) return false;
|
|
439
|
+
if (err === reportedFailure) throw err;
|
|
440
|
+
this._lastSyncedAt = previousLastSyncedAt;
|
|
441
|
+
this.setState(this._lastSyncedAt ? "degraded" : "error");
|
|
382
442
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
383
443
|
this.errorHandler?.(error);
|
|
384
|
-
this.emit("error", { error });
|
|
444
|
+
if (this.listenerCount("error") > 0) this.emit("error", { error });
|
|
445
|
+
if (options.propagateFailure) bootstrapReadFailures.add(error);
|
|
385
446
|
throw error;
|
|
386
447
|
}
|
|
387
448
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
do {
|
|
394
|
-
const params = {
|
|
395
|
-
buying_mode: "wholesale",
|
|
396
|
-
pagination: { max_results: DEFAULT_BOOTSTRAP_PAGE_LIMIT, ...cursor && { cursor } },
|
|
397
|
-
...this.account && { account: this.account }
|
|
398
|
-
};
|
|
399
|
-
if (!cursor && metadata.wholesaleFeedVersion && this._capabilities.wholesaleFeedVersioning) {
|
|
400
|
-
params.if_wholesale_feed_version = metadata.wholesaleFeedVersion;
|
|
401
|
-
if (metadata.pricingVersion) params.if_pricing_version = metadata.pricingVersion;
|
|
402
|
-
}
|
|
403
|
-
const result = await this.client.getProducts(params);
|
|
404
|
-
if (!this.isLifecycleCurrent(epoch)) return { cancelled: true, unchanged: false, items: into, metadata };
|
|
405
|
-
const body = result.data;
|
|
406
|
-
if (!body) return { cancelled: false, unchanged: false, items: into, metadata };
|
|
407
|
-
if (body.unchanged) {
|
|
408
|
-
metadata = mergeFeedMetadata(metadata, body);
|
|
409
|
-
return { cancelled: false, unchanged: true, items: into, metadata };
|
|
410
|
-
}
|
|
411
|
-
const products = Array.isArray(body.products) ? body.products : [];
|
|
412
|
-
for (const product of products) {
|
|
413
|
-
const id = product.product_id;
|
|
414
|
-
if (typeof id === "string") into.set(id, product);
|
|
415
|
-
}
|
|
416
|
-
metadata = mergeFeedMetadata(metadata, body);
|
|
417
|
-
cursor = body.pagination?.has_more ? body.pagination?.cursor : void 0;
|
|
418
|
-
} while (cursor);
|
|
419
|
-
return { cancelled: false, unchanged: false, items: into, metadata };
|
|
449
|
+
handleBootstrapFailure(failure) {
|
|
450
|
+
this.setState(this._lastSyncedAt ? "degraded" : "error");
|
|
451
|
+
this.errorHandler?.(failure.error);
|
|
452
|
+
if (this.listenerCount("error") > 0) this.emit("error", failure);
|
|
453
|
+
return false;
|
|
420
454
|
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
metadata = mergeFeedMetadata(metadata, body);
|
|
441
|
-
return { cancelled: false, unchanged: true, items: into, metadata };
|
|
455
|
+
bootstrapProducts(epoch, conditional) {
|
|
456
|
+
return this.bootstrapFeed("product", epoch, conditional);
|
|
457
|
+
}
|
|
458
|
+
bootstrapSignals(epoch, conditional) {
|
|
459
|
+
return this.bootstrapFeed("signal", epoch, conditional);
|
|
460
|
+
}
|
|
461
|
+
async bootstrapFeed(entity, epoch, conditional) {
|
|
462
|
+
const store = new InMemoryWholesaleFeedMirrorStore();
|
|
463
|
+
const scope = { agentUrl: "shell", accountKey: "shell", entity };
|
|
464
|
+
const metadata = entity === "product" ? this.currentProductMetadata() : this.currentSignalMetadata();
|
|
465
|
+
const previous = entity === "product" ? [...this.productIndex.values()] : [...this.signalIndex.values()];
|
|
466
|
+
store.restore(scope, { revision: 0, items: previous, ...metadata });
|
|
467
|
+
let readException;
|
|
468
|
+
const read = async (call) => {
|
|
469
|
+
try {
|
|
470
|
+
return await call();
|
|
471
|
+
} catch (cause) {
|
|
472
|
+
readException = cause instanceof Error ? cause : new Error(String(cause));
|
|
473
|
+
throw cause;
|
|
442
474
|
}
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
475
|
+
};
|
|
476
|
+
const outcome = await refreshWholesaleFeed({
|
|
477
|
+
client: {
|
|
478
|
+
getAdcpCapabilities: this.client.getAdcpCapabilities.bind(this.client),
|
|
479
|
+
getProducts: (params) => read(() => this.client.getProducts(params)),
|
|
480
|
+
getSignals: (params) => read(() => this.client.getSignals(params))
|
|
481
|
+
},
|
|
482
|
+
store,
|
|
483
|
+
scope,
|
|
484
|
+
account: this.account,
|
|
485
|
+
conditional: conditional ?? this._capabilities.wholesaleFeedVersioning,
|
|
486
|
+
isCurrent: () => this.isLifecycleCurrent(epoch)
|
|
487
|
+
});
|
|
488
|
+
if (outcome.outcome === "superseded") return { cancelled: true, unchanged: false, items: /* @__PURE__ */ new Map(), metadata };
|
|
489
|
+
if (outcome.outcome === "degraded") {
|
|
490
|
+
if (readException) throw readException;
|
|
491
|
+
return {
|
|
492
|
+
cancelled: false,
|
|
493
|
+
unchanged: false,
|
|
494
|
+
items: /* @__PURE__ */ new Map(),
|
|
495
|
+
metadata,
|
|
496
|
+
failure: {
|
|
497
|
+
error: new Error(
|
|
498
|
+
`${entity === "product" ? "Product" : "Signal"} bootstrap failed: ${outcome.cause.message}`,
|
|
499
|
+
{
|
|
500
|
+
cause: outcome.cause
|
|
501
|
+
}
|
|
502
|
+
),
|
|
503
|
+
adcpError: outcome.error
|
|
504
|
+
}
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
const snapshot = outcome.snapshot;
|
|
508
|
+
return {
|
|
509
|
+
cancelled: false,
|
|
510
|
+
unchanged: outcome.outcome === "unchanged",
|
|
511
|
+
items: new Map(
|
|
512
|
+
snapshot.items.map((item) => [
|
|
513
|
+
entity === "product" ? item.product_id : item.signal_agent_segment_id,
|
|
514
|
+
item
|
|
515
|
+
])
|
|
516
|
+
),
|
|
517
|
+
metadata: {
|
|
518
|
+
wholesaleFeedVersion: snapshot.wholesaleFeedVersion,
|
|
519
|
+
pricingVersion: snapshot.pricingVersion,
|
|
520
|
+
cacheScope: snapshot.cacheScope
|
|
447
521
|
}
|
|
448
|
-
|
|
449
|
-
cursor = body.pagination?.has_more ? body.pagination?.cursor : void 0;
|
|
450
|
-
} while (cursor);
|
|
451
|
-
return { cancelled: false, unchanged: false, items: into, metadata };
|
|
522
|
+
};
|
|
452
523
|
}
|
|
453
524
|
async recoverFromBulkChange(event, epoch = this.lifecycleEpoch) {
|
|
454
525
|
this.emit("resyncing", { reason: "bulk_change" });
|
|
@@ -459,14 +530,22 @@ class WholesaleFeedSync extends EventEmitter {
|
|
|
459
530
|
);
|
|
460
531
|
}
|
|
461
532
|
const entities = affected === "product" ? "products" : "signals";
|
|
462
|
-
|
|
533
|
+
for (let attempt = 0; attempt < VERSION_MISMATCH_RECOVERY_ATTEMPTS; attempt++) {
|
|
534
|
+
if (await this.bootstrap({ emitDiffs: true, entities, epoch, propagateFailure: true })) return true;
|
|
535
|
+
if (!this.isLifecycleCurrent(epoch)) return false;
|
|
536
|
+
}
|
|
537
|
+
throw supersededRepairError();
|
|
463
538
|
}
|
|
464
539
|
async recoverFromVersionMismatch(event, epoch = this.lifecycleEpoch) {
|
|
465
540
|
this.emit("resyncing", { reason: "version_mismatch" });
|
|
466
541
|
const beforeVersion = this.currentWholesaleFeedVersionForEvent(event);
|
|
467
542
|
for (let attempt = 1; attempt <= VERSION_MISMATCH_RECOVERY_ATTEMPTS; attempt++) {
|
|
468
|
-
const recovered = await this.bootstrap({ emitDiffs: true, epoch });
|
|
469
|
-
if (!recovered)
|
|
543
|
+
const recovered = await this.bootstrap({ emitDiffs: true, epoch, propagateFailure: true });
|
|
544
|
+
if (!recovered) {
|
|
545
|
+
if (!this.isLifecycleCurrent(epoch)) return false;
|
|
546
|
+
if (attempt === VERSION_MISMATCH_RECOVERY_ATTEMPTS) throw supersededRepairError();
|
|
547
|
+
continue;
|
|
548
|
+
}
|
|
470
549
|
const afterVersion = this.currentWholesaleFeedVersionForEvent(event);
|
|
471
550
|
if (afterVersion !== beforeVersion) return true;
|
|
472
551
|
if (attempt < VERSION_MISMATCH_RECOVERY_ATTEMPTS) {
|
|
@@ -487,11 +566,12 @@ class WholesaleFeedSync extends EventEmitter {
|
|
|
487
566
|
await this.probeVersion(epoch);
|
|
488
567
|
} catch (err) {
|
|
489
568
|
if (!this.isLifecycleCurrent(epoch)) return;
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
569
|
+
if (this._state !== "degraded" && this._state !== "error") {
|
|
570
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
571
|
+
this.handleBootstrapFailure({ error });
|
|
572
|
+
}
|
|
493
573
|
}
|
|
494
|
-
if (this.isLifecycleCurrent(epoch) && this._state === "syncing" && this._mode === "auto-poll") {
|
|
574
|
+
if (this.isLifecycleCurrent(epoch) && (this._state === "syncing" || this._state === "degraded" || this._state === "error") && this._mode === "auto-poll") {
|
|
495
575
|
this.scheduleProbe(epoch);
|
|
496
576
|
}
|
|
497
577
|
}
|
|
@@ -518,7 +598,7 @@ class WholesaleFeedSync extends EventEmitter {
|
|
|
518
598
|
this.emit("error", { error });
|
|
519
599
|
this.errorHandler?.(error);
|
|
520
600
|
}
|
|
521
|
-
if (this.isLifecycleCurrent(epoch) && this._state === "syncing" && this.capabilityRefreshIntervalMs > 0) {
|
|
601
|
+
if (this.isLifecycleCurrent(epoch) && (this._state === "syncing" || this._state === "degraded" || this._state === "error") && this.capabilityRefreshIntervalMs > 0) {
|
|
522
602
|
this.scheduleCapabilityRefresh(epoch);
|
|
523
603
|
}
|
|
524
604
|
}
|
|
@@ -619,63 +699,16 @@ class WholesaleFeedSync extends EventEmitter {
|
|
|
619
699
|
}
|
|
620
700
|
}
|
|
621
701
|
applyEvent(event) {
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
}
|
|
633
|
-
case "product.priced": {
|
|
634
|
-
const payload = event.payload;
|
|
635
|
-
const existing = this.productIndex.get(payload.product_id);
|
|
636
|
-
if (existing && Array.isArray(payload.pricing_options)) {
|
|
637
|
-
this.productIndex.set(payload.product_id, {
|
|
638
|
-
...existing,
|
|
639
|
-
pricing_options: payload.pricing_options
|
|
640
|
-
});
|
|
641
|
-
}
|
|
642
|
-
return;
|
|
643
|
-
}
|
|
644
|
-
case "product.removed": {
|
|
645
|
-
const payload = event.payload;
|
|
646
|
-
if (typeof payload.product_id === "string") this.productIndex.delete(payload.product_id);
|
|
647
|
-
return;
|
|
648
|
-
}
|
|
649
|
-
case "signal.created":
|
|
650
|
-
case "signal.updated": {
|
|
651
|
-
const payload = event.payload;
|
|
652
|
-
if (typeof payload.signal_agent_segment_id !== "string") return;
|
|
653
|
-
if (payload.signal) {
|
|
654
|
-
this.signalIndex.set(payload.signal_agent_segment_id, payload.signal);
|
|
655
|
-
}
|
|
656
|
-
return;
|
|
657
|
-
}
|
|
658
|
-
case "signal.priced": {
|
|
659
|
-
const payload = event.payload;
|
|
660
|
-
const existing = this.signalIndex.get(payload.signal_agent_segment_id);
|
|
661
|
-
if (existing && Array.isArray(payload.pricing_options)) {
|
|
662
|
-
this.signalIndex.set(payload.signal_agent_segment_id, {
|
|
663
|
-
...existing,
|
|
664
|
-
pricing_options: payload.pricing_options
|
|
665
|
-
});
|
|
666
|
-
}
|
|
667
|
-
return;
|
|
668
|
-
}
|
|
669
|
-
case "signal.removed": {
|
|
670
|
-
const payload = event.payload;
|
|
671
|
-
if (typeof payload.signal_agent_segment_id === "string") {
|
|
672
|
-
this.signalIndex.delete(payload.signal_agent_segment_id);
|
|
673
|
-
}
|
|
674
|
-
return;
|
|
675
|
-
}
|
|
676
|
-
case "wholesale_feed.bulk_change":
|
|
677
|
-
return;
|
|
678
|
-
}
|
|
702
|
+
++this.mirrorRevision;
|
|
703
|
+
const entity = event.event_type.startsWith("product.") ? "product" : "signal";
|
|
704
|
+
const items = applyWholesaleFeedEvent(
|
|
705
|
+
entity,
|
|
706
|
+
entity === "product" ? [...this.productIndex.values()] : [...this.signalIndex.values()],
|
|
707
|
+
event
|
|
708
|
+
);
|
|
709
|
+
if (!items) return;
|
|
710
|
+
if (entity === "product") this.productIndex = new Map(items.map((item) => [item.product_id, item]));
|
|
711
|
+
else this.signalIndex = new Map(items.map((item) => [item.signal_agent_segment_id, item]));
|
|
679
712
|
}
|
|
680
713
|
emitTypedEvent(event) {
|
|
681
714
|
switch (event.event_type) {
|
|
@@ -709,107 +742,23 @@ class WholesaleFeedSync extends EventEmitter {
|
|
|
709
742
|
}
|
|
710
743
|
// ====== Private: diff emission (auto-poll / manual refresh) ======
|
|
711
744
|
emitDiffs(previousProducts, previousSignals) {
|
|
712
|
-
const
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
const
|
|
745
|
+
const events = [
|
|
746
|
+
...diffWholesaleFeed(
|
|
747
|
+
"product",
|
|
748
|
+
[...previousProducts.values()],
|
|
749
|
+
[...this.productIndex.values()],
|
|
750
|
+
this.productCacheScope
|
|
751
|
+
),
|
|
752
|
+
...diffWholesaleFeed(
|
|
753
|
+
"signal",
|
|
754
|
+
[...previousSignals.values()],
|
|
755
|
+
[...this.signalIndex.values()],
|
|
756
|
+
this.signalCacheScope
|
|
757
|
+
)
|
|
758
|
+
];
|
|
759
|
+
for (const event of events) {
|
|
727
760
|
this.emit("event", { event, synthetic: true });
|
|
728
|
-
this.emit(
|
|
729
|
-
};
|
|
730
|
-
for (const [id, product] of this.productIndex) {
|
|
731
|
-
const prev = previousProducts.get(id);
|
|
732
|
-
if (!prev) {
|
|
733
|
-
emit(
|
|
734
|
-
"product.created",
|
|
735
|
-
makeEvent("product.created", "product", id, {
|
|
736
|
-
product_id: id,
|
|
737
|
-
product,
|
|
738
|
-
applies_to: { scope: this.productCacheScope }
|
|
739
|
-
})
|
|
740
|
-
);
|
|
741
|
-
} else if (priceChanged(prev, product)) {
|
|
742
|
-
emit(
|
|
743
|
-
"product.priced",
|
|
744
|
-
makeEvent("product.priced", "product", id, {
|
|
745
|
-
product_id: id,
|
|
746
|
-
pricing_options: product.pricing_options ?? [],
|
|
747
|
-
applies_to: { scope: this.productCacheScope }
|
|
748
|
-
})
|
|
749
|
-
);
|
|
750
|
-
} else if (!isDeepStrictEqual(prev, product)) {
|
|
751
|
-
emit(
|
|
752
|
-
"product.updated",
|
|
753
|
-
makeEvent("product.updated", "product", id, {
|
|
754
|
-
product_id: id,
|
|
755
|
-
product,
|
|
756
|
-
applies_to: { scope: this.productCacheScope }
|
|
757
|
-
})
|
|
758
|
-
);
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
for (const [id] of previousProducts) {
|
|
762
|
-
if (!this.productIndex.has(id)) {
|
|
763
|
-
emit(
|
|
764
|
-
"product.removed",
|
|
765
|
-
makeEvent("product.removed", "product", id, {
|
|
766
|
-
product_id: id,
|
|
767
|
-
applies_to: { scope: this.productCacheScope }
|
|
768
|
-
})
|
|
769
|
-
);
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
for (const [id, signal] of this.signalIndex) {
|
|
773
|
-
const prev = previousSignals.get(id);
|
|
774
|
-
if (!prev) {
|
|
775
|
-
emit(
|
|
776
|
-
"signal.created",
|
|
777
|
-
makeEvent("signal.created", "signal", id, {
|
|
778
|
-
signal_agent_segment_id: id,
|
|
779
|
-
signal,
|
|
780
|
-
applies_to: { scope: this.signalCacheScope }
|
|
781
|
-
})
|
|
782
|
-
);
|
|
783
|
-
} else if (signalPriceChanged(prev, signal)) {
|
|
784
|
-
emit(
|
|
785
|
-
"signal.priced",
|
|
786
|
-
makeEvent("signal.priced", "signal", id, {
|
|
787
|
-
signal_agent_segment_id: id,
|
|
788
|
-
pricing_options: signal.pricing_options ?? [],
|
|
789
|
-
applies_to: { scope: this.signalCacheScope }
|
|
790
|
-
})
|
|
791
|
-
);
|
|
792
|
-
} else if (!isDeepStrictEqual(prev, signal)) {
|
|
793
|
-
emit(
|
|
794
|
-
"signal.updated",
|
|
795
|
-
makeEvent("signal.updated", "signal", id, {
|
|
796
|
-
signal_agent_segment_id: id,
|
|
797
|
-
signal,
|
|
798
|
-
applies_to: { scope: this.signalCacheScope }
|
|
799
|
-
})
|
|
800
|
-
);
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
for (const [id] of previousSignals) {
|
|
804
|
-
if (!this.signalIndex.has(id)) {
|
|
805
|
-
emit(
|
|
806
|
-
"signal.removed",
|
|
807
|
-
makeEvent("signal.removed", "signal", id, {
|
|
808
|
-
signal_agent_segment_id: id,
|
|
809
|
-
applies_to: { scope: this.signalCacheScope }
|
|
810
|
-
})
|
|
811
|
-
);
|
|
812
|
-
}
|
|
761
|
+
this.emit(event.event_type, { event, synthetic: true });
|
|
813
762
|
}
|
|
814
763
|
}
|
|
815
764
|
// ====== Private: search ======
|
|
@@ -986,16 +935,6 @@ function assertOptionalDate(value, field, invalid) {
|
|
|
986
935
|
function parsePersistedDate(value) {
|
|
987
936
|
return value === void 0 ? void 0 : new Date(value);
|
|
988
937
|
}
|
|
989
|
-
function priceChanged(prev, next) {
|
|
990
|
-
const a = prev.pricing_options;
|
|
991
|
-
const b = next.pricing_options;
|
|
992
|
-
return !isDeepStrictEqual(a, b);
|
|
993
|
-
}
|
|
994
|
-
function signalPriceChanged(prev, next) {
|
|
995
|
-
const a = prev.pricing_options;
|
|
996
|
-
const b = next.pricing_options;
|
|
997
|
-
return !isDeepStrictEqual(a, b);
|
|
998
|
-
}
|
|
999
938
|
function isUuidV7(value) {
|
|
1000
939
|
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);
|
|
1001
940
|
}
|
|
@@ -1005,13 +944,6 @@ function compareUuidV7(a, b) {
|
|
|
1005
944
|
const normalizedB = b.toLowerCase();
|
|
1006
945
|
return normalizedA === normalizedB ? 0 : normalizedA > normalizedB ? 1 : -1;
|
|
1007
946
|
}
|
|
1008
|
-
function mergeFeedMetadata(current, body) {
|
|
1009
|
-
return {
|
|
1010
|
-
wholesaleFeedVersion: typeof body.wholesale_feed_version === "string" ? body.wholesale_feed_version : current.wholesaleFeedVersion,
|
|
1011
|
-
pricingVersion: typeof body.pricing_version === "string" ? body.pricing_version : current.pricingVersion,
|
|
1012
|
-
cacheScope: body.cache_scope === "public" || body.cache_scope === "account" ? body.cache_scope : current.cacheScope
|
|
1013
|
-
};
|
|
1014
|
-
}
|
|
1015
947
|
async function sleep(ms) {
|
|
1016
948
|
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
1017
949
|
}
|