ytracking-web 0.2.0 → 0.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.
package/README.md CHANGED
@@ -28,7 +28,7 @@ npm install ytracking-web
28
28
 
29
29
  ## 使用(IIFE)
30
30
 
31
- **最少程式(腳本與 API 同源、後台未強制 collect 金鑰時):** 只需 `siteId`;SDK 會從 `<script src="…ytracking-web.js">` 推斷 ingest origin,並在啟動後自動送一次 `page_view`。
31
+ **最少程式(腳本與 API 同源、後台未強制 collect 金鑰時):** 只需 `siteId`;SDK 會從 `<script src="…ytracking-web.js">` 推斷 ingest origin,並在啟動後自動送一次 `pageview`。
32
32
 
33
33
  ```html
34
34
  <script src="https://你的網域/sdk/ytracking-web.js"></script>
@@ -52,29 +52,35 @@ npm install ytracking-web
52
52
  ```
53
53
 
54
54
  若腳本託管在 CDN 而 API 在另一網域,**必須**設定 `baseUrl` 或 `baseUrls`(不可依賴推斷)。
55
- 僅追蹤 SPA 路由、不要自動首屏 `page_view` 時:加 **`autoTrackPageView: false`**,再自行呼叫 `trackPageView()`。
55
+ 僅追蹤 SPA 路由、不要自動首屏 `pageview` 時:加 **`autoTrackPageView: false`**,再自行呼叫 `pageView()`(舊名 `trackPageView()`)。
56
56
 
57
57
  ### 0.2.0 行為變更(升級自 0.1.x)
58
58
 
59
- - 預設 **`autoTrackPageView: true`**:若你原本在 `init` 後又手動呼叫一次 `trackPageView()`,首屏可能重複計數 — 請刪除多餘呼叫,或設 `autoTrackPageView: false`。
59
+ - 預設 **`autoTrackPageView: true`**:若你原本在 `init` 後又手動呼叫一次 `pageView()`(或 `trackPageView()`),首屏可能重複計數 — 請刪除多餘呼叫,或設 `autoTrackPageView: false`。
60
60
  - `baseUrls` 改為可選:可只用 **`baseUrl`**(字串),或与 `baseUrls` 合併(`baseUrl` 優先)。
61
61
 
62
+ ### 0.3.0
63
+
64
+ - **套件/API 行為**:與 0.2.0 相同(minor 發佈:對外文件、`external-demo.html` 的 **`tracker=web`** 驗收路徑與靜態 bundle 對齊)。
65
+ - **整合測試**:Production/本機可於 **`/external-demo.html?siteId=…&tracker=web`** 驗證 Web SDK(見 repo **`docs/SITE_AND_EMBED.md`**)。
66
+
62
67
  ## API(`YTrackingWeb`)
63
68
 
64
69
  - `init(config)` — 必填 **`siteId`**
65
70
  - **`baseUrl`**:單一 ingest origin(無尾階 `/`);與 **`baseUrls`** 可併用(順序:先 `baseUrl` 再 `baseUrls`)
66
71
  - **`baseUrls`**:ingest origins 陣列;若與 `baseUrl` 皆省略,會嘗試從頁面上最後一個 **`…ytracking-web(.min).js`** 的 `<script src>` 推斷 origin(腳本須與 API 同源才正確)
67
72
  - **`sdkKey`**:若設定,所有 POST(collect、install、attribution token、domain-health report)會帶 **`X-YT-Sdk-Key`**
68
- - **`autoTrackPageView`**:預設 **`true`**,`start()` 後自動 enqueue 一次 `page_view`;設 `false` 則完全自行呼叫 `trackPageView`/`track`
73
+ - **`autoTrackPageView`**:預設 **`true`**,`start()` 後自動 enqueue 一次 `pageview`;設 `false` 則完全自行呼叫 `pageView`/`event`
69
74
  - `enableVisitorFallbackFingerprint`:可選(預設 `false`),僅在無 cookie + 無 localStorage visitor 時以低熵指紋產生暫時 visitor seed
70
75
  - `maxQueueSize`:本地 queue 容量上限(預設 `500`),超量時淘汰低優先且最舊事件
71
76
  - `batchSize`:單次 flush 最多取件數(預設 `8`)
72
77
  - `flushConcurrency`:單次 flush 內平行送件 worker 數(預設 `2`,最小 `1`)
73
78
  - `endpointListTtlSec`:成功送達後重新拉取 `/v1/sdk/ingestion-hosts` 的最短間隔(秒,預設 300)
74
79
  - `domainHealthReportMinIntervalMs`:同一 domain 不可達回報最短間隔(毫秒,預設 300000)
75
- - `track(type, { props })` — 對應 `POST /v1/collect` envelope
76
- - `trackPageView()` — `page_view`
77
- - `install({ clickId? })` — 內嵌 **`POST /v1/install`**(無 `appId` 之 embed 形態)
80
+ - `event(type, { props })` — 對應 `POST /v1/collect` envelope
81
+ - `pageView()` — `pageview`(`trackPageView()` 仍可用)
82
+ - `paid({ props? })` — `paid`
83
+ - `install({ clickId? })` — 經佇列送出 **`POST /v1/install`**(與 `yt-embed` 之 `sendInstall` 一致)
78
84
  - `issueAttributionToken({ clickId?, ttlSeconds?, campaignId?, appId?, deferredContext? })` — 同步 **`POST /v1/attribution/token`**(不入佇列);沿用 `setContext` 之 visitor/session/click 等
79
85
  - `flush()` — 手動送出佇列
80
86
  - `setContext({ visitorId, sessionId, clickId, campaignId, appId })` — 後續事件帶入
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ytracking-web",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Browser SDK for YTracking collect/install (IndexedDB queue, retry, multi-host)",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
package/src/client.ts CHANGED
@@ -78,8 +78,10 @@ function collectUrl(origin: string): string {
78
78
  return `${origin}/v1/collect`;
79
79
  }
80
80
 
81
- function installUrl(origin: string): string {
82
- return `${origin}/v1/install`;
81
+ function randomUuid(): string {
82
+ return typeof crypto !== "undefined" && crypto.randomUUID
83
+ ? crypto.randomUUID()
84
+ : `id_${Date.now()}_${Math.random().toString(36).slice(2)}`;
83
85
  }
84
86
 
85
87
  function attributionTokenUrl(origin: string): string {
@@ -200,7 +202,7 @@ export class YTrackingWebClient {
200
202
  }
201
203
  void this.flush();
202
204
  if (this.cfg.autoTrackPageView) {
203
- void this.trackPageView();
205
+ void this.pageView();
204
206
  }
205
207
  }
206
208
 
@@ -219,7 +221,7 @@ export class YTrackingWebClient {
219
221
  this.visibilityHandler = null;
220
222
  }
221
223
 
222
- async track(
224
+ async event(
223
225
  eventType: string,
224
226
  options?: { props?: Record<string, unknown> },
225
227
  ): Promise<void> {
@@ -240,14 +242,41 @@ export class YTrackingWebClient {
240
242
  });
241
243
  }
242
244
 
243
- async trackPageView(): Promise<void> {
244
- return this.track("page_view", {});
245
+ async pageView(): Promise<void> {
246
+ return this.event("pageview", {});
245
247
  }
246
248
 
247
- async install(opts?: { clickId?: string }): Promise<void> {
248
- const body: Record<string, string> = { siteId: this.cfg.siteId };
249
+ async paid(options?: { props?: Record<string, unknown> }): Promise<void> {
250
+ return this.event("paid", options);
251
+ }
252
+
253
+ async install(opts?: {
254
+ clickId?: string;
255
+ installId?: string;
256
+ status?: "first_open" | "install_complete";
257
+ occurredAt?: string;
258
+ props?: Record<string, unknown>;
259
+ }): Promise<void> {
260
+ const occurredAt = opts?.occurredAt?.trim() || new Date().toISOString();
261
+ const installId = opts?.installId?.trim() || randomUuid();
262
+ const status = opts?.status?.trim() || "first_open";
263
+ const event: Record<string, unknown> = {
264
+ type: "install",
265
+ timestamp: occurredAt,
266
+ occurredAt,
267
+ installId,
268
+ status,
269
+ };
249
270
  const cid = opts?.clickId?.trim() || this.context.clickId?.trim();
250
- if (cid) body.clickId = cid;
271
+ if (cid) event.clickId = cid;
272
+ const visitorId = this.context.visitorId?.trim();
273
+ if (visitorId) event.visitorId = visitorId;
274
+ const appId = this.context.appId?.trim();
275
+ if (appId) event.appId = appId;
276
+ if (opts?.props && typeof opts.props === "object") {
277
+ event.props = opts.props;
278
+ }
279
+ const body = { siteId: this.cfg.siteId, event };
251
280
  await this.enqueue({
252
281
  kind: "install",
253
282
  payload: JSON.stringify(body),
@@ -495,7 +524,7 @@ export class YTrackingWebClient {
495
524
  }
496
525
 
497
526
  private async sendOne(rec: OutboxRecord): Promise<void> {
498
- const pathFn = rec.kind === "install" ? installUrl : collectUrl;
527
+ const pathFn = collectUrl;
499
528
  let lastRetryAfter: number | undefined;
500
529
 
501
530
  for (const origin of this.originsInOrder()) {
@@ -704,12 +733,12 @@ export async function track(
704
733
  options?: { props?: Record<string, unknown> },
705
734
  ): Promise<void> {
706
735
  if (!singleton) throw new Error("YTrackingWeb: call init() first");
707
- return singleton.track(eventType, options);
736
+ return singleton.event(eventType, options);
708
737
  }
709
738
 
710
739
  export async function trackPageView(): Promise<void> {
711
740
  if (!singleton) throw new Error("YTrackingWeb: call init() first");
712
- return singleton.trackPageView();
741
+ return singleton.pageView();
713
742
  }
714
743
 
715
744
  export async function flush(): Promise<void> {
@@ -721,11 +750,37 @@ export function shutdown(): void {
721
750
  singleton = null;
722
751
  }
723
752
 
724
- export async function install(opts?: { clickId?: string }): Promise<void> {
753
+ export async function install(opts?: {
754
+ clickId?: string;
755
+ installId?: string;
756
+ status?: "first_open" | "install_complete";
757
+ occurredAt?: string;
758
+ props?: Record<string, unknown>;
759
+ }): Promise<void> {
725
760
  if (!singleton) throw new Error("YTrackingWeb: call init() first");
726
761
  return singleton.install(opts);
727
762
  }
728
763
 
764
+ export async function event(
765
+ eventType: string,
766
+ options?: { props?: Record<string, unknown> },
767
+ ): Promise<void> {
768
+ if (!singleton) throw new Error("YTrackingWeb: call init() first");
769
+ return singleton.event(eventType, options);
770
+ }
771
+
772
+ export async function pageView(): Promise<void> {
773
+ if (!singleton) throw new Error("YTrackingWeb: call init() first");
774
+ return singleton.pageView();
775
+ }
776
+
777
+ export async function paid(options?: {
778
+ props?: Record<string, unknown>;
779
+ }): Promise<void> {
780
+ if (!singleton) throw new Error("YTrackingWeb: call init() first");
781
+ return singleton.paid(options);
782
+ }
783
+
729
784
  export async function issueAttributionToken(opts?: {
730
785
  clickId?: string;
731
786
  ttlSeconds?: number;
package/src/index.ts CHANGED
@@ -8,6 +8,9 @@ export {
8
8
  export { buildCollectEnvelope, getOrCreateSessionId } from "./payload";
9
9
  export {
10
10
  init,
11
+ event,
12
+ pageView,
13
+ paid,
11
14
  track,
12
15
  trackPageView,
13
16
  flush,
package/src/retry.ts CHANGED
@@ -20,8 +20,14 @@ export function backoffMs(attemptIndexZeroBased: number): number {
20
20
  return Math.min(Math.max(0, Math.floor(base + jitter)), 600000);
21
21
  }
22
22
 
23
- const HIGH = new Set(["download_click", "registration", "payment", "app_open"]);
24
- const LOW = new Set(["page_view", "scroll", "heartbeat"]);
23
+ const HIGH = new Set([
24
+ "download_click",
25
+ "registration",
26
+ "paid",
27
+ "payment",
28
+ "app_open",
29
+ ]);
30
+ const LOW = new Set(["pageview", "page_view", "scroll", "heartbeat"]);
25
31
 
26
32
  export function eventPriority(eventType: string): number {
27
33
  if (HIGH.has(eventType)) return 10;
package/src/types.ts CHANGED
@@ -28,8 +28,8 @@ export type InitConfig = {
28
28
  */
29
29
  sdkKey?: string;
30
30
  /**
31
- * After IndexedDB/outbox starts, enqueue one `page_view` (default **true**).
32
- * Set `false` if you only track SPA navigations or call `trackPageView()` yourself.
31
+ * After IndexedDB/outbox starts, enqueue one `pageview` (default **true**).
32
+ * Set `false` if you only track SPA navigations or call `pageView()` yourself.
33
33
  */
34
34
  autoTrackPageView?: boolean;
35
35
  debug?: boolean;