@tokenoftrust/storefront-runner 2.2.17 → 2.2.19
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tokenoftrust/storefront-runner",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.19",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE",
|
|
5
5
|
"description": "World-shareable storefront runner: multi-tenant renderer on Astro/Cloudflare. No control plane.",
|
|
6
6
|
"packageManager": "pnpm@11.9.0",
|
|
@@ -6,6 +6,15 @@
|
|
|
6
6
|
* payload to one exact collection endpoint. The fixed device value groups events
|
|
7
7
|
* as non-identifying storefront traffic; it is never derived from a browser,
|
|
8
8
|
* person, account, persisted client value, network address, or device signal.
|
|
9
|
+
*
|
|
10
|
+
* Channel ATTRIBUTION (UTM tokens + the referrer's origin) is allowed through as
|
|
11
|
+
* plain `event_properties`: it is a minimized, campaign-scoped label already
|
|
12
|
+
* validated upstream (`events.ts` `validatePageAttribution`), not a visitor or
|
|
13
|
+
* device signal, and this adapter re-screens each value for PII before sending
|
|
14
|
+
* it regardless. A persistent/session identity is a different thing entirely —
|
|
15
|
+
* it stays out, on purpose: `device_id` remains the fixed, non-identifying value
|
|
16
|
+
* below and there is still no `session_id`, `user_id`, or any persisted client
|
|
17
|
+
* value anywhere in this file.
|
|
9
18
|
*/
|
|
10
19
|
import type { CommerceEventBroker, CommerceEventSubscription } from "./commerce-broker.js";
|
|
11
20
|
import {
|
|
@@ -47,13 +56,23 @@ export const AMPLITUDE_HTTP_EVENT_TYPES = Object.freeze([
|
|
|
47
56
|
"storefront.action.activated.v1",
|
|
48
57
|
] as const);
|
|
49
58
|
|
|
59
|
+
/** The flat channel-attribution keys this adapter will forward, mirroring `events.ts` UTM_FIELDS. */
|
|
60
|
+
export type AmplitudeAttributionProperties = Partial<{
|
|
61
|
+
utm_source: string;
|
|
62
|
+
utm_medium: string;
|
|
63
|
+
utm_campaign: string;
|
|
64
|
+
utm_term: string;
|
|
65
|
+
utm_content: string;
|
|
66
|
+
referrer_origin: string;
|
|
67
|
+
}>;
|
|
68
|
+
|
|
50
69
|
export interface AmplitudeHttpEvent {
|
|
51
70
|
event_type: "page_view" | (typeof APPROVED_MARKETING_ACTION_NAMES)[number];
|
|
52
71
|
device_id: typeof ANONYMOUS_DEVICE_ID;
|
|
53
72
|
insert_id: string;
|
|
54
73
|
/** Occurrence time, epoch milliseconds. */
|
|
55
74
|
time: number;
|
|
56
|
-
event_properties: GovernedEventProperties & { page_path?: string };
|
|
75
|
+
event_properties: GovernedEventProperties & { page_path?: string } & AmplitudeAttributionProperties;
|
|
57
76
|
}
|
|
58
77
|
|
|
59
78
|
export const AMPLITUDE_DEDUPE_CAPACITY = 512 as const;
|
|
@@ -104,6 +123,44 @@ class BoundedInsertIds {
|
|
|
104
123
|
}
|
|
105
124
|
}
|
|
106
125
|
|
|
126
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
127
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Flatten the already-validated `data.attribution` object (`events.ts`
|
|
132
|
+
* `validatePageAttribution` — a minimized referrer origin plus up to five UTM
|
|
133
|
+
* tokens) into Amplitude's flat `event_properties`. Re-screened for PII here
|
|
134
|
+
* rather than trusted from upstream validation alone, matching `isSafePagePath`'s
|
|
135
|
+
* own double-check: a field that resembles PII is dropped individually rather
|
|
136
|
+
* than failing the whole page-view event.
|
|
137
|
+
*/
|
|
138
|
+
function extractAttributionProperties(data: Record<string, unknown>): AmplitudeAttributionProperties {
|
|
139
|
+
const attribution = data["attribution"];
|
|
140
|
+
if (!isPlainObject(attribution)) return {};
|
|
141
|
+
const out: AmplitudeAttributionProperties = {};
|
|
142
|
+
|
|
143
|
+
const utm = attribution["utm"];
|
|
144
|
+
if (isPlainObject(utm)) {
|
|
145
|
+
for (const key of ["source", "medium", "campaign", "term", "content"] as const) {
|
|
146
|
+
const value = utm[key];
|
|
147
|
+
if (typeof value === "string" && value.length > 0 && !resemblesManagedAnalyticsPii(value)) {
|
|
148
|
+
out[`utm_${key}`] = value;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const referrer = attribution["referrer"];
|
|
154
|
+
if (isPlainObject(referrer)) {
|
|
155
|
+
const origin = referrer["origin"];
|
|
156
|
+
if (typeof origin === "string" && origin.length > 0 && !resemblesManagedAnalyticsPii(origin)) {
|
|
157
|
+
out.referrer_origin = origin;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return out;
|
|
162
|
+
}
|
|
163
|
+
|
|
107
164
|
function isSafePagePath(value: unknown): value is string {
|
|
108
165
|
return typeof value === "string" && value.startsWith("/") && !value.startsWith("//") &&
|
|
109
166
|
!value.includes("\\") && !value.includes("?") && !value.includes("#") &&
|
|
@@ -166,7 +223,12 @@ export function mapCommerceEventToAmplitude(
|
|
|
166
223
|
}
|
|
167
224
|
const path = event.data["path"];
|
|
168
225
|
if (!isSafePagePath(path)) return null;
|
|
169
|
-
|
|
226
|
+
const attribution = extractAttributionProperties(event.data);
|
|
227
|
+
return {
|
|
228
|
+
...base,
|
|
229
|
+
event_type: "page_view",
|
|
230
|
+
event_properties: { ...governed, page_path: path, ...attribution },
|
|
231
|
+
};
|
|
170
232
|
}
|
|
171
233
|
|
|
172
234
|
export interface RegisterAmplitudeHttpDestinationInput {
|