@shopbb/helium 0.7.7 → 0.9.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 (76) hide show
  1. package/dist/analytics/index.d.ts +18 -0
  2. package/dist/analytics/index.d.ts.map +1 -0
  3. package/dist/analytics/index.js +16 -0
  4. package/dist/analytics/index.js.map +1 -0
  5. package/dist/analytics/queue.d.ts +31 -0
  6. package/dist/analytics/queue.d.ts.map +1 -0
  7. package/dist/analytics/queue.js +203 -0
  8. package/dist/analytics/queue.js.map +1 -0
  9. package/dist/analytics/react.d.ts +44 -0
  10. package/dist/analytics/react.d.ts.map +1 -0
  11. package/dist/analytics/react.js +114 -0
  12. package/dist/analytics/react.js.map +1 -0
  13. package/dist/analytics/types.d.ts +68 -0
  14. package/dist/analytics/types.d.ts.map +1 -0
  15. package/dist/analytics/types.js +7 -0
  16. package/dist/analytics/types.js.map +1 -0
  17. package/dist/components/AddToCartButton.d.ts +7 -0
  18. package/dist/components/AddToCartButton.d.ts.map +1 -1
  19. package/dist/components/AddToCartButton.js +13 -2
  20. package/dist/components/AddToCartButton.js.map +1 -1
  21. package/dist/page-schema/PageRenderer.d.ts +29 -0
  22. package/dist/page-schema/PageRenderer.d.ts.map +1 -0
  23. package/dist/page-schema/PageRenderer.js +73 -0
  24. package/dist/page-schema/PageRenderer.js.map +1 -0
  25. package/dist/page-schema/index.d.ts +20 -0
  26. package/dist/page-schema/index.d.ts.map +1 -0
  27. package/dist/page-schema/index.js +18 -0
  28. package/dist/page-schema/index.js.map +1 -0
  29. package/dist/page-schema/sections/Banner.d.ts +10 -0
  30. package/dist/page-schema/sections/Banner.d.ts.map +1 -0
  31. package/dist/page-schema/sections/Banner.js +35 -0
  32. package/dist/page-schema/sections/Banner.js.map +1 -0
  33. package/dist/page-schema/sections/Hero.d.ts +15 -0
  34. package/dist/page-schema/sections/Hero.d.ts.map +1 -0
  35. package/dist/page-schema/sections/Hero.js +44 -0
  36. package/dist/page-schema/sections/Hero.js.map +1 -0
  37. package/dist/page-schema/sections/Image.d.ts +10 -0
  38. package/dist/page-schema/sections/Image.d.ts.map +1 -0
  39. package/dist/page-schema/sections/Image.js +12 -0
  40. package/dist/page-schema/sections/Image.js.map +1 -0
  41. package/dist/page-schema/sections/ProductGrid.d.ts +29 -0
  42. package/dist/page-schema/sections/ProductGrid.d.ts.map +1 -0
  43. package/dist/page-schema/sections/ProductGrid.js +28 -0
  44. package/dist/page-schema/sections/ProductGrid.js.map +1 -0
  45. package/dist/page-schema/sections/RichText.d.ts +13 -0
  46. package/dist/page-schema/sections/RichText.d.ts.map +1 -0
  47. package/dist/page-schema/sections/RichText.js +16 -0
  48. package/dist/page-schema/sections/RichText.js.map +1 -0
  49. package/dist/page-schema/sections/Spacer.d.ts +10 -0
  50. package/dist/page-schema/sections/Spacer.d.ts.map +1 -0
  51. package/dist/page-schema/sections/Spacer.js +6 -0
  52. package/dist/page-schema/sections/Spacer.js.map +1 -0
  53. package/dist/page-schema/types.d.ts +138 -0
  54. package/dist/page-schema/types.d.ts.map +1 -0
  55. package/dist/page-schema/types.js +129 -0
  56. package/dist/page-schema/types.js.map +1 -0
  57. package/dist/react.d.ts +7 -0
  58. package/dist/react.d.ts.map +1 -1
  59. package/dist/react.js +15 -0
  60. package/dist/react.js.map +1 -1
  61. package/package.json +1 -1
  62. package/src/analytics/index.ts +27 -0
  63. package/src/analytics/queue.ts +224 -0
  64. package/src/analytics/react.tsx +146 -0
  65. package/src/analytics/types.ts +81 -0
  66. package/src/components/AddToCartButton.tsx +18 -0
  67. package/src/page-schema/PageRenderer.tsx +147 -0
  68. package/src/page-schema/index.ts +48 -0
  69. package/src/page-schema/sections/Banner.tsx +63 -0
  70. package/src/page-schema/sections/Hero.tsx +92 -0
  71. package/src/page-schema/sections/Image.tsx +42 -0
  72. package/src/page-schema/sections/ProductGrid.tsx +96 -0
  73. package/src/page-schema/sections/RichText.tsx +49 -0
  74. package/src/page-schema/sections/Spacer.tsx +15 -0
  75. package/src/page-schema/types.ts +286 -0
  76. package/src/react.tsx +59 -0
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @shopbb/helium/analytics — 埋点 SDK 公开 API
3
+ *
4
+ * 浏览器侧用法:
5
+ * - <TrackingProvider>:包在 root,初始化 session、起 flush 定时器、自动 page_view
6
+ * - useTracking():在组件内拿到 track() 函数
7
+ *
8
+ * 服务端不要用本模块——服务端事件(checkout_paid)由 platform-api 直接 INSERT。
9
+ *
10
+ * 注意:本模块与 components/AnalyticsProvider 是两个独立产品:
11
+ * - AnalyticsProvider(components):客户端事件总线,挂 GA / 自定义 reporter
12
+ * - TrackingProvider(本模块):把 5 个核心事件批量发到 /api/events,给 agent 用
13
+ */
14
+ export { TrackingProvider, useTracking } from './react';
15
+ export type { TrackingProviderProps } from './react';
16
+ export { AnalyticsQueue } from './queue';
17
+ export type { AnalyticsEvent, AnalyticsConfig, EventType, PageViewProps, ProductViewProps, AddToCartProps, CheckoutStartProps, TrackFn, } from './types';
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/analytics/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACxD,YAAY,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACzC,YAAY,EACV,cAAc,EACd,eAAe,EACf,SAAS,EACT,aAAa,EACb,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,OAAO,GACR,MAAM,SAAS,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @shopbb/helium/analytics — 埋点 SDK 公开 API
3
+ *
4
+ * 浏览器侧用法:
5
+ * - <TrackingProvider>:包在 root,初始化 session、起 flush 定时器、自动 page_view
6
+ * - useTracking():在组件内拿到 track() 函数
7
+ *
8
+ * 服务端不要用本模块——服务端事件(checkout_paid)由 platform-api 直接 INSERT。
9
+ *
10
+ * 注意:本模块与 components/AnalyticsProvider 是两个独立产品:
11
+ * - AnalyticsProvider(components):客户端事件总线,挂 GA / 自定义 reporter
12
+ * - TrackingProvider(本模块):把 5 个核心事件批量发到 /api/events,给 agent 用
13
+ */
14
+ export { TrackingProvider, useTracking } from './react';
15
+ export { AnalyticsQueue } from './queue';
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/analytics/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAExD,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * AnalyticsQueue — 浏览器侧事件缓冲队列
3
+ *
4
+ * 职责:
5
+ * 1. 把 track() 调用收集到内存队列
6
+ * 2. 定时(3s)或满批(20)触发 flush
7
+ * 3. flush 调 POST /api/events,失败重试 1 次
8
+ * 4. beforeunload 时用 navigator.sendBeacon 兜底刷出
9
+ *
10
+ * 注意:这是浏览器侧逻辑。SSR 阶段调 track() 会被丢弃(typeof window === 'undefined' 检查)。
11
+ */
12
+ import type { AnalyticsConfig, AnalyticsEvent } from './types';
13
+ export declare class AnalyticsQueue {
14
+ private readonly config;
15
+ private queue;
16
+ private timer;
17
+ private sessionId;
18
+ private flushing;
19
+ constructor(config: AnalyticsConfig);
20
+ start(): void;
21
+ stop(): void;
22
+ setBuyerId(id: string | null): void;
23
+ track(event: AnalyticsEvent): void;
24
+ /** 主动 flush。返回是否实际发送了请求 */
25
+ flush(): Promise<boolean>;
26
+ /** 用 sendBeacon 兜底,页面卸载场景 */
27
+ private flushBeacon;
28
+ private headers;
29
+ private log;
30
+ }
31
+ //# sourceMappingURL=queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../../src/analytics/queue.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAe,MAAM,SAAS,CAAC;AAgC5E,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAGrB;IACF,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,KAAK,CAA+C;IAC5D,OAAO,CAAC,SAAS,CAAM;IACvB,OAAO,CAAC,QAAQ,CAAS;gBAEb,MAAM,EAAE,eAAe;IAWnC,KAAK,IAAI,IAAI;IAqBb,IAAI,IAAI,IAAI;IAOZ,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAInC,KAAK,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI;IAoClC,2BAA2B;IACrB,KAAK,IAAI,OAAO,CAAC,OAAO,CAAC;IA0C/B,6BAA6B;IAC7B,OAAO,CAAC,WAAW;IA4BnB,OAAO,CAAC,OAAO;IAYf,OAAO,CAAC,GAAG;CAMZ"}
@@ -0,0 +1,203 @@
1
+ /**
2
+ * AnalyticsQueue — 浏览器侧事件缓冲队列
3
+ *
4
+ * 职责:
5
+ * 1. 把 track() 调用收集到内存队列
6
+ * 2. 定时(3s)或满批(20)触发 flush
7
+ * 3. flush 调 POST /api/events,失败重试 1 次
8
+ * 4. beforeunload 时用 navigator.sendBeacon 兜底刷出
9
+ *
10
+ * 注意:这是浏览器侧逻辑。SSR 阶段调 track() 会被丢弃(typeof window === 'undefined' 检查)。
11
+ */
12
+ const SESSION_COOKIE = 'sbb_sid';
13
+ const SESSION_TTL_DAYS = 365;
14
+ /**
15
+ * 读 sbb_sid cookie,没有则生成一个新的并 set。
16
+ * 仅浏览器环境调用。
17
+ */
18
+ function getOrCreateSessionId() {
19
+ if (typeof document === 'undefined')
20
+ return '';
21
+ const match = document.cookie.match(/(?:^|;\s*)sbb_sid=([^;]+)/);
22
+ if (match)
23
+ return match[1];
24
+ // 生成 UUID(不依赖 crypto.randomUUID 以兼容老浏览器)
25
+ const sid = crypto?.randomUUID?.() ?? generateFallbackUuid();
26
+ const maxAge = SESSION_TTL_DAYS * 24 * 60 * 60;
27
+ document.cookie = `${SESSION_COOKIE}=${sid}; max-age=${maxAge}; path=/; samesite=lax`;
28
+ return sid;
29
+ }
30
+ function generateFallbackUuid() {
31
+ // RFC4122 v4 简化版
32
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
33
+ const r = (Math.random() * 16) | 0;
34
+ const v = c === 'x' ? r : (r & 0x3) | 0x8;
35
+ return v.toString(16);
36
+ });
37
+ }
38
+ export class AnalyticsQueue {
39
+ config;
40
+ queue = [];
41
+ timer = null;
42
+ sessionId = '';
43
+ flushing = false;
44
+ constructor(config) {
45
+ this.config = {
46
+ apiBase: config.apiBase.replace(/\/+$/, ''),
47
+ publicAccessToken: config.publicAccessToken,
48
+ buyerId: config.buyerId ?? null,
49
+ debug: !!config.debug,
50
+ flushIntervalMs: config.flushIntervalMs ?? 3000,
51
+ flushBatchSize: config.flushBatchSize ?? 20,
52
+ };
53
+ }
54
+ start() {
55
+ if (typeof window === 'undefined')
56
+ return;
57
+ this.sessionId = getOrCreateSessionId();
58
+ if (this.timer)
59
+ return;
60
+ this.timer = setInterval(() => {
61
+ void this.flush();
62
+ }, this.config.flushIntervalMs);
63
+ // 页面卸载兜底
64
+ window.addEventListener('pagehide', () => this.flushBeacon());
65
+ window.addEventListener('beforeunload', () => this.flushBeacon());
66
+ if (typeof document !== 'undefined') {
67
+ document.addEventListener('visibilitychange', () => {
68
+ if (document.visibilityState === 'hidden') {
69
+ this.flushBeacon();
70
+ }
71
+ });
72
+ }
73
+ }
74
+ stop() {
75
+ if (this.timer) {
76
+ clearInterval(this.timer);
77
+ this.timer = null;
78
+ }
79
+ }
80
+ setBuyerId(id) {
81
+ this.config.buyerId = id;
82
+ }
83
+ track(event) {
84
+ if (typeof window === 'undefined') {
85
+ this.log('skip-ssr', event.type);
86
+ return;
87
+ }
88
+ if (!this.sessionId) {
89
+ this.sessionId = getOrCreateSessionId();
90
+ }
91
+ const queued = {
92
+ type: event.type,
93
+ occurredAt: event.occurredAt ?? Math.floor(Date.now() / 1000),
94
+ path: event.path !== undefined
95
+ ? event.path
96
+ : typeof location !== 'undefined'
97
+ ? location.pathname + location.search
98
+ : null,
99
+ referrer: event.referrer !== undefined
100
+ ? event.referrer
101
+ : typeof document !== 'undefined'
102
+ ? document.referrer || null
103
+ : null,
104
+ props: event.props ?? {},
105
+ };
106
+ this.queue.push(queued);
107
+ this.log('queued', queued.type, this.queue.length);
108
+ if (this.queue.length >= this.config.flushBatchSize) {
109
+ void this.flush();
110
+ }
111
+ }
112
+ /** 主动 flush。返回是否实际发送了请求 */
113
+ async flush() {
114
+ if (this.flushing)
115
+ return false;
116
+ if (this.queue.length === 0)
117
+ return false;
118
+ if (!this.config.publicAccessToken) {
119
+ this.log('skip-no-token');
120
+ return false;
121
+ }
122
+ this.flushing = true;
123
+ const batch = this.queue.splice(0, this.config.flushBatchSize);
124
+ const body = JSON.stringify({
125
+ events: batch.map((e) => ({
126
+ type: e.type,
127
+ occurredAt: e.occurredAt,
128
+ path: e.path ?? undefined,
129
+ referrer: e.referrer ?? undefined,
130
+ props: e.props,
131
+ })),
132
+ });
133
+ try {
134
+ const res = await fetch(`${this.config.apiBase}/api/events`, {
135
+ method: 'POST',
136
+ headers: this.headers(),
137
+ body,
138
+ keepalive: true,
139
+ });
140
+ if (!res.ok) {
141
+ this.log('flush-failed-status', res.status);
142
+ // 失败不重试入队(避免堆积),demo 阶段简化
143
+ return false;
144
+ }
145
+ this.log('flushed', batch.length);
146
+ return true;
147
+ }
148
+ catch (err) {
149
+ this.log('flush-error', err);
150
+ return false;
151
+ }
152
+ finally {
153
+ this.flushing = false;
154
+ }
155
+ }
156
+ /** 用 sendBeacon 兜底,页面卸载场景 */
157
+ flushBeacon() {
158
+ if (this.queue.length === 0)
159
+ return;
160
+ if (typeof navigator === 'undefined' || !navigator.sendBeacon)
161
+ return;
162
+ if (!this.config.publicAccessToken)
163
+ return;
164
+ const batch = this.queue.splice(0, this.config.flushBatchSize);
165
+ const body = JSON.stringify({
166
+ events: batch.map((e) => ({
167
+ type: e.type,
168
+ occurredAt: e.occurredAt,
169
+ path: e.path ?? undefined,
170
+ referrer: e.referrer ?? undefined,
171
+ props: e.props,
172
+ })),
173
+ // sendBeacon 无法设 header,所以把 token / session 塞进 body 作为兜底
174
+ // platform-api handler 优先认 header;body 字段用于 beacon 场景
175
+ _auth: {
176
+ token: this.config.publicAccessToken,
177
+ sessionId: this.sessionId,
178
+ buyerId: this.config.buyerId,
179
+ },
180
+ });
181
+ const blob = new Blob([body], { type: 'application/json' });
182
+ const ok = navigator.sendBeacon(`${this.config.apiBase}/api/events?beacon=1`, blob);
183
+ this.log('beacon', ok, batch.length);
184
+ }
185
+ headers() {
186
+ const h = {
187
+ 'Content-Type': 'application/json',
188
+ 'X-Storefront-Access-Token': this.config.publicAccessToken,
189
+ 'X-Session-Id': this.sessionId,
190
+ };
191
+ if (this.config.buyerId) {
192
+ h['X-Buyer-Id'] = this.config.buyerId;
193
+ }
194
+ return h;
195
+ }
196
+ log(...args) {
197
+ if (this.config.debug) {
198
+ // eslint-disable-next-line no-console
199
+ console.log('[helium-analytics]', ...args);
200
+ }
201
+ }
202
+ }
203
+ //# sourceMappingURL=queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue.js","sourceRoot":"","sources":["../../src/analytics/queue.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * TrackingProvider + useTracking — React 绑定(后端事件埋点)
3
+ *
4
+ * 与 components/AnalyticsProvider 不同:
5
+ * - AnalyticsProvider(旧):客户端事件总线,商家挂 GA / Plausible 等 reporter
6
+ * - TrackingProvider(新):把 5 个核心事件发到 platform-api /api/events
7
+ *
8
+ * 用法:
9
+ * <TrackingProvider
10
+ * apiBase={store.apiBase}
11
+ * publicAccessToken={store.publicAccessToken}
12
+ * buyerId={buyer?.id}
13
+ * debug
14
+ * >
15
+ * <App />
16
+ * </TrackingProvider>
17
+ *
18
+ * const { track } = useTracking();
19
+ * track({ type: 'add_to_cart', props: { productId, quantity: 1 } });
20
+ *
21
+ * 自动事件:
22
+ * - 首次挂载发一次 page_view
23
+ * - 监听 history pushState/replaceState/popstate,每次 URL 变化补发 page_view
24
+ */
25
+ import * as React from 'react';
26
+ import type { AnalyticsConfig, TrackFn } from './types';
27
+ interface ContextValue {
28
+ track: TrackFn;
29
+ setBuyerId: (id: string | null) => void;
30
+ }
31
+ export interface TrackingProviderProps extends AnalyticsConfig {
32
+ children: React.ReactNode;
33
+ /** 是否自动发 page_view(默认 true) */
34
+ autoPageview?: boolean;
35
+ }
36
+ export declare function TrackingProvider(props: TrackingProviderProps): React.ReactElement;
37
+ /**
38
+ * 在组件里拿到 track 函数。
39
+ *
40
+ * 在 TrackingProvider 之外调用会得到一个 no-op,不报错(方便在测试/SSR 中使用)。
41
+ */
42
+ export declare function useTracking(): ContextValue;
43
+ export {};
44
+ //# sourceMappingURL=react.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../../src/analytics/react.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAkB,eAAe,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAExE,UAAU,YAAY;IACpB,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,EAAE,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;CACzC;AAID,MAAM,WAAW,qBAAsB,SAAQ,eAAe;IAC5D,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,+BAA+B;IAC/B,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,GAAG,KAAK,CAAC,YAAY,CAuFjF;AAED;;;;GAIG;AACH,wBAAgB,WAAW,IAAI,YAAY,CAI1C"}
@@ -0,0 +1,114 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * TrackingProvider + useTracking — React 绑定(后端事件埋点)
4
+ *
5
+ * 与 components/AnalyticsProvider 不同:
6
+ * - AnalyticsProvider(旧):客户端事件总线,商家挂 GA / Plausible 等 reporter
7
+ * - TrackingProvider(新):把 5 个核心事件发到 platform-api /api/events
8
+ *
9
+ * 用法:
10
+ * <TrackingProvider
11
+ * apiBase={store.apiBase}
12
+ * publicAccessToken={store.publicAccessToken}
13
+ * buyerId={buyer?.id}
14
+ * debug
15
+ * >
16
+ * <App />
17
+ * </TrackingProvider>
18
+ *
19
+ * const { track } = useTracking();
20
+ * track({ type: 'add_to_cart', props: { productId, quantity: 1 } });
21
+ *
22
+ * 自动事件:
23
+ * - 首次挂载发一次 page_view
24
+ * - 监听 history pushState/replaceState/popstate,每次 URL 变化补发 page_view
25
+ */
26
+ import * as React from 'react';
27
+ import { AnalyticsQueue } from './queue';
28
+ const TrackingCtx = React.createContext(null);
29
+ export function TrackingProvider(props) {
30
+ const { children, autoPageview = true, apiBase, publicAccessToken, buyerId, debug, flushIntervalMs, flushBatchSize, } = props;
31
+ // 用 ref 持有 queue,避免每次 re-render 重建
32
+ const queueRef = React.useRef(null);
33
+ if (queueRef.current === null) {
34
+ queueRef.current = new AnalyticsQueue({
35
+ apiBase,
36
+ publicAccessToken,
37
+ buyerId,
38
+ debug,
39
+ flushIntervalMs,
40
+ flushBatchSize,
41
+ });
42
+ }
43
+ // buyerId 变化时同步到 queue
44
+ React.useEffect(() => {
45
+ queueRef.current?.setBuyerId(buyerId ?? null);
46
+ }, [buyerId]);
47
+ // 启动 queue(只在浏览器侧)
48
+ React.useEffect(() => {
49
+ if (typeof window === 'undefined')
50
+ return;
51
+ const q = queueRef.current;
52
+ if (!q)
53
+ return;
54
+ q.start();
55
+ return () => q.stop();
56
+ }, []);
57
+ // 自动 page_view
58
+ React.useEffect(() => {
59
+ if (!autoPageview)
60
+ return;
61
+ if (typeof window === 'undefined')
62
+ return;
63
+ const q = queueRef.current;
64
+ if (!q)
65
+ return;
66
+ const send = () => {
67
+ q.track({
68
+ type: 'page_view',
69
+ props: { title: typeof document !== 'undefined' ? document.title : undefined },
70
+ });
71
+ };
72
+ // 首次
73
+ send();
74
+ // patch history methods 以捕获 SPA 导航
75
+ const origPush = history.pushState;
76
+ const origReplace = history.replaceState;
77
+ history.pushState = function (...args) {
78
+ origPush.apply(this, args);
79
+ send();
80
+ };
81
+ history.replaceState = function (...args) {
82
+ origReplace.apply(this, args);
83
+ send();
84
+ };
85
+ const onPop = () => send();
86
+ window.addEventListener('popstate', onPop);
87
+ return () => {
88
+ history.pushState = origPush;
89
+ history.replaceState = origReplace;
90
+ window.removeEventListener('popstate', onPop);
91
+ };
92
+ }, [autoPageview]);
93
+ const value = React.useMemo(() => ({
94
+ track: (e) => queueRef.current?.track(e),
95
+ setBuyerId: (id) => queueRef.current?.setBuyerId(id),
96
+ }), []);
97
+ return _jsx(TrackingCtx.Provider, { value: value, children: children });
98
+ }
99
+ /**
100
+ * 在组件里拿到 track 函数。
101
+ *
102
+ * 在 TrackingProvider 之外调用会得到一个 no-op,不报错(方便在测试/SSR 中使用)。
103
+ */
104
+ export function useTracking() {
105
+ const ctx = React.useContext(TrackingCtx);
106
+ if (ctx)
107
+ return ctx;
108
+ return NOOP;
109
+ }
110
+ const NOOP = {
111
+ track: () => { },
112
+ setBuyerId: () => { },
113
+ };
114
+ //# sourceMappingURL=react.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react.js","sourceRoot":"","sources":["../../src/analytics/react.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Analytics SDK 类型
3
+ *
4
+ * Helium 的埋点接口。与 platform-api 的事件 schema 保持一致。
5
+ */
6
+ export type EventType = 'page_view' | 'product_view' | 'add_to_cart' | 'checkout_start' | 'checkout_paid';
7
+ /** 单条事件输入 */
8
+ export interface AnalyticsEvent {
9
+ /** 事件类型 */
10
+ type: EventType;
11
+ /** 客户端发生时间(秒,Unix)。默认 Date.now()/1000 */
12
+ occurredAt?: number;
13
+ /** 页面 path(默认 window.location.pathname) */
14
+ path?: string;
15
+ /** referrer(默认 document.referrer) */
16
+ referrer?: string;
17
+ /** 事件附加数据 */
18
+ props?: Record<string, unknown>;
19
+ }
20
+ /** 各事件的 props 形状 */
21
+ export interface PageViewProps {
22
+ title?: string;
23
+ }
24
+ export interface ProductViewProps {
25
+ productId: string;
26
+ variantId?: string;
27
+ priceCents?: number;
28
+ currency?: string;
29
+ }
30
+ export interface AddToCartProps {
31
+ productId: string;
32
+ variantId?: string;
33
+ quantity: number;
34
+ priceCents?: number;
35
+ currency?: string;
36
+ }
37
+ export interface CheckoutStartProps {
38
+ cartId?: string;
39
+ itemCount: number;
40
+ subtotalCents: number;
41
+ currency: string;
42
+ }
43
+ /** AnalyticsProvider 初始化配置 */
44
+ export interface AnalyticsConfig {
45
+ /** 平台 API base URL,例如 https://api.oxygen-demo.cloudc.top */
46
+ apiBase: string;
47
+ /** Storefront public access token */
48
+ publicAccessToken: string;
49
+ /** 当前买家 ID(已登录时传入) */
50
+ buyerId?: string | null;
51
+ /** 是否开启 debug 日志 */
52
+ debug?: boolean;
53
+ /** flush 间隔(毫秒),默认 3000 */
54
+ flushIntervalMs?: number;
55
+ /** 批量上限(条),默认 20 */
56
+ flushBatchSize?: number;
57
+ }
58
+ /** track 函数签名 */
59
+ export type TrackFn = (event: AnalyticsEvent) => void;
60
+ /** 内部队列条目 */
61
+ export interface QueuedEvent {
62
+ type: EventType;
63
+ occurredAt: number;
64
+ path: string | null;
65
+ referrer: string | null;
66
+ props: Record<string, unknown>;
67
+ }
68
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/analytics/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,MAAM,SAAS,GACjB,WAAW,GACX,cAAc,GACd,aAAa,GACb,gBAAgB,GAChB,eAAe,CAAC;AAEpB,aAAa;AACb,MAAM,WAAW,cAAc;IAC7B,WAAW;IACX,IAAI,EAAE,SAAS,CAAC;IAChB,yCAAyC;IACzC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2CAA2C;IAC3C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qCAAqC;IACrC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa;IACb,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED,oBAAoB;AACpB,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,8BAA8B;AAC9B,MAAM,WAAW,eAAe;IAC9B,4DAA4D;IAC5D,OAAO,EAAE,MAAM,CAAC;IAChB,qCAAqC;IACrC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,sBAAsB;IACtB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,oBAAoB;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,2BAA2B;IAC3B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oBAAoB;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,iBAAiB;AACjB,MAAM,MAAM,OAAO,GAAG,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;AAEtD,aAAa;AACb,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,SAAS,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Analytics SDK 类型
3
+ *
4
+ * Helium 的埋点接口。与 platform-api 的事件 schema 保持一致。
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/analytics/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
@@ -17,6 +17,7 @@
17
17
  * 监听全局 fetcher 状态。Hydrogen demo store 用 `useAside().open()` 模式。
18
18
  */
19
19
  import * as React from 'react';
20
+ import { type CartLineInput } from './CartForm';
20
21
  export interface AddToCartButtonProps {
21
22
  /** variant GID */
22
23
  variantId: string;
@@ -39,6 +40,12 @@ export interface AddToCartButtonProps {
39
40
  route?: string;
40
41
  /** 按钮 children */
41
42
  children?: React.ReactNode;
43
+ /**
44
+ * 按钮被点击时触发(form 提交之前)。
45
+ * 用于埋点 / dataLayer / 自定义分析回调。
46
+ * 不影响表单提交流程。
47
+ */
48
+ onAdd?: (line: CartLineInput) => void;
42
49
  }
43
50
  export declare function AddToCartButton(props: AddToCartButtonProps): import("react/jsx-runtime").JSX.Element;
44
51
  //# sourceMappingURL=AddToCartButton.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AddToCartButton.d.ts","sourceRoot":"","sources":["../../src/components/AddToCartButton.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,MAAM,WAAW,oBAAoB;IACnC,kBAAkB;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oBAAoB;IACpB,UAAU,CAAC,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnD,kDAAkD;IAClD,eAAe,CAAC,EAAE,GAAG,CAAC;IACtB,kBAAkB;IAClB,eAAe,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAClC,sBAAsB;IACtB,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wBAAwB;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kBAAkB;IAClB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,2CAwC1D"}
1
+ {"version":3,"file":"AddToCartButton.d.ts","sourceRoot":"","sources":["../../src/components/AddToCartButton.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAY,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAE1D,MAAM,WAAW,oBAAoB;IACnC,kBAAkB;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oBAAoB;IACpB,UAAU,CAAC,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnD,kDAAkD;IAClD,eAAe,CAAC,EAAE,GAAG,CAAC;IACtB,kBAAkB;IAClB,eAAe,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAClC,sBAAsB;IACtB,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wBAAwB;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kBAAkB;IAClB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B;;;;OAIG;IACH,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC;CACvC;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,2CAoD1D"}
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { CartForm } from './CartForm';
3
3
  export function AddToCartButton(props) {
4
- const { variantId, quantity = 1, attributes, selectedVariant, unavailableText = '缺货', loadingText = '加入中...', disabled = false, className, route = '/cart', children = '加入购物车', } = props;
4
+ const { variantId, quantity = 1, attributes, selectedVariant, unavailableText = '缺货', loadingText = '加入中...', disabled = false, className, route = '/cart', children = '加入购物车', onAdd, } = props;
5
5
  const line = { merchandiseId: variantId, quantity };
6
6
  if (attributes && attributes.length > 0)
7
7
  line.attributes = attributes;
@@ -9,7 +9,18 @@ export function AddToCartButton(props) {
9
9
  line.selectedVariant = selectedVariant;
10
10
  return (_jsx(CartForm, { route: route, action: CartForm.ACTIONS.LinesAdd, inputs: { lines: [line] }, children: (fetcher) => {
11
11
  const submitting = fetcher.state !== 'idle';
12
- return (_jsx("button", { type: "submit", className: className, disabled: disabled || submitting, "data-add-to-cart": true, "data-loading": submitting ? '' : undefined, children: submitting ? loadingText : disabled ? unavailableText : children }));
12
+ return (_jsx("button", { type: "submit", className: className, disabled: disabled || submitting, "data-add-to-cart": true, "data-loading": submitting ? '' : undefined, onClick: () => {
13
+ if (onAdd && !submitting && !disabled) {
14
+ try {
15
+ onAdd(line);
16
+ }
17
+ catch (err) {
18
+ if (typeof console !== 'undefined') {
19
+ console.warn('[AddToCartButton] onAdd threw', err);
20
+ }
21
+ }
22
+ }
23
+ }, children: submitting ? loadingText : disabled ? unavailableText : children }));
13
24
  } }));
14
25
  }
15
26
  //# sourceMappingURL=AddToCartButton.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AddToCartButton.js","sourceRoot":"","sources":["../../src/components/AddToCartButton.tsx"],"names":[],"mappings":";AAoBA,OAAO,EAAE,QAAQ,EAAsB,MAAM,YAAY,CAAC;AAuB1D,MAAM,UAAU,eAAe,CAAC,KAA2B;IACzD,MAAM,EACJ,SAAS,EACT,QAAQ,GAAG,CAAC,EACZ,UAAU,EACV,eAAe,EACf,eAAe,GAAG,IAAI,EACtB,WAAW,GAAG,QAAQ,EACtB,QAAQ,GAAG,KAAK,EAChB,SAAS,EACT,KAAK,GAAG,OAAO,EACf,QAAQ,GAAG,OAAO,GACnB,GAAG,KAAK,CAAC;IAEV,MAAM,IAAI,GAAkB,EAAE,aAAa,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC;IACnE,IAAI,UAAU,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC;QAAE,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IACtE,IAAI,eAAe;QAAE,IAAI,CAAC,eAAe,GAAG,eAAe,CAAC;IAE5D,OAAO,CACL,KAAC,QAAQ,IACP,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,QAAQ,EACjC,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE,YAExB,CAAC,OAAO,EAAE,EAAE;YACX,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,KAAK,MAAM,CAAC;YAC5C,OAAO,CACL,iBACE,IAAI,EAAC,QAAQ,EACb,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,QAAQ,IAAI,UAAU,4CAElB,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,YAExC,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,QAAQ,GAC1D,CACV,CAAC;QACJ,CAAC,GACQ,CACZ,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"AddToCartButton.js","sourceRoot":"","sources":["../../src/components/AddToCartButton.tsx"],"names":[],"mappings":";AAoBA,OAAO,EAAE,QAAQ,EAAsB,MAAM,YAAY,CAAC;AA6B1D,MAAM,UAAU,eAAe,CAAC,KAA2B;IACzD,MAAM,EACJ,SAAS,EACT,QAAQ,GAAG,CAAC,EACZ,UAAU,EACV,eAAe,EACf,eAAe,GAAG,IAAI,EACtB,WAAW,GAAG,QAAQ,EACtB,QAAQ,GAAG,KAAK,EAChB,SAAS,EACT,KAAK,GAAG,OAAO,EACf,QAAQ,GAAG,OAAO,EAClB,KAAK,GACN,GAAG,KAAK,CAAC;IAEV,MAAM,IAAI,GAAkB,EAAE,aAAa,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC;IACnE,IAAI,UAAU,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC;QAAE,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IACtE,IAAI,eAAe;QAAE,IAAI,CAAC,eAAe,GAAG,eAAe,CAAC;IAE5D,OAAO,CACL,KAAC,QAAQ,IACP,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,QAAQ,EACjC,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE,YAExB,CAAC,OAAO,EAAE,EAAE;YACX,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,KAAK,MAAM,CAAC;YAC5C,OAAO,CACL,iBACE,IAAI,EAAC,QAAQ,EACb,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,QAAQ,IAAI,UAAU,4CAElB,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,EACzC,OAAO,EAAE,GAAG,EAAE;oBACZ,IAAI,KAAK,IAAI,CAAC,UAAU,IAAI,CAAC,QAAQ,EAAE,CAAC;wBACtC,IAAI,CAAC;4BACH,KAAK,CAAC,IAAI,CAAC,CAAC;wBACd,CAAC;wBAAC,OAAO,GAAG,EAAE,CAAC;4BACb,IAAI,OAAO,OAAO,KAAK,WAAW,EAAE,CAAC;gCACnC,OAAO,CAAC,IAAI,CAAC,+BAA+B,EAAE,GAAG,CAAC,CAAC;4BACrD,CAAC;wBACH,CAAC;oBACH,CAAC;gBACH,CAAC,YAEA,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,QAAQ,GAC1D,CACV,CAAC;QACJ,CAAC,GACQ,CACZ,CAAC;AACJ,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * <PageRenderer> — 按 Page Schema 渲染一整页
3
+ *
4
+ * 同一份组件在 production storefront 与 admin 编辑器画布共用。
5
+ *
6
+ * 商品网格的 selection_mode 自动模式(newest / top-selling / collection)
7
+ * 由父组件预先拉数据通过 productsBySection prop 注入,组件本身不发请求。
8
+ * 这让 renderer 保持纯组件、可在 SSR / CSR / 编辑器画布任何环境用。
9
+ */
10
+ import * as React from 'react';
11
+ import type { Page } from './types';
12
+ export interface PageRendererProps {
13
+ page: Page;
14
+ /**
15
+ * 按 section.id 提供该 section 用到的商品数据(仅 product-grid 用到)。
16
+ * Page renderer 不发请求,请父组件按 schema 计算后传入。
17
+ */
18
+ productsBySection?: Record<string, any[]>;
19
+ /**
20
+ * 编辑器模式下传入 onSectionClick / selectedSectionId,渲染时会给每个 section
21
+ * 加 hover / 选中外框,便于点选定位。
22
+ */
23
+ editorMode?: {
24
+ selectedSectionId?: string | null;
25
+ onSectionClick?: (sectionId: string) => void;
26
+ };
27
+ }
28
+ export declare function PageRenderer({ page, productsBySection, editorMode, }: PageRendererProps): React.ReactElement;
29
+ //# sourceMappingURL=PageRenderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PageRenderer.d.ts","sourceRoot":"","sources":["../../src/page-schema/PageRenderer.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAc,IAAI,EAAE,MAAM,SAAS,CAAC;AAQhD,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,IAAI,CAAC;IACX;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IAC1C;;;OAGG;IACH,UAAU,CAAC,EAAE;QACX,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAClC,cAAc,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;KAC9C,CAAC;CACH;AAED,wBAAgB,YAAY,CAAC,EAC3B,IAAI,EACJ,iBAAiB,EACjB,UAAU,GACX,EAAE,iBAAiB,GAAG,KAAK,CAAC,YAAY,CA0BxC"}