From 77ca0ea1101b97391d551d2ac8e695c15230ce03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B5=85=E5=BF=B5?= Date: Fri, 18 Sep 2026 12:22:05 +0800 Subject: [PATCH] =?UTF-8?q?feat(sdk):=20=E7=89=88=E6=9C=AC=E6=B3=A8?= =?UTF-8?q?=E5=85=A5=E4=B8=8E=E4=BA=8B=E4=BB=B6=E5=BD=92=E5=B1=9E=E9=AA=A8?= =?UTF-8?q?=E6=9E=B6=EF=BC=880.2.0/build=5Fid/schema=5Fversion=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Code --- sdk/build.mjs | 15 +++++++++++++-- sdk/package.json | 2 +- sdk/src/index.ts | 3 ++- sdk/src/types.ts | 23 +++++++++++++++++++++++ sdk/test/index.test.ts | 8 ++++++-- sdk/vitest.config.ts | 2 ++ 6 files changed, 47 insertions(+), 6 deletions(-) create mode 100644 sdk/src/types.ts diff --git a/sdk/build.mjs b/sdk/build.mjs index 3583922..dec0140 100644 --- a/sdk/build.mjs +++ b/sdk/build.mjs @@ -1,6 +1,17 @@ import { build } from "esbuild"; +import { execSync } from "node:child_process"; + +let buildId; +try { + buildId = execSync("git log -1 --format=%h-%cd --date=format:%Y%m%d%H%M").toString().trim(); +} catch { + buildId = "dev-" + Date.now(); +} + await build({ - entryPoints: ["src/index.ts"], - bundle: true, minify: true, format: "iife", + entryPoints: ["src/index.ts"], bundle: true, minify: true, format: "iife", outfile: "dist/v1.js", target: ["es2020"], // 代码依赖 globalThis(es2018 目标名不副实) + // define 键必须用点链:esbuild 只做精确表达式匹配,裸键不会替换 + // globalThis.__BUILD_ID__ 成员访问(实测 0.24.2),裸键将恒为 "unknown" + define: { "globalThis.__BUILD_ID__": JSON.stringify(buildId) }, }); diff --git a/sdk/package.json b/sdk/package.json index 86057d7..f4a1568 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,6 +1,6 @@ { "name": "ctk-sdk", - "version": "0.1.0", + "version": "0.2.0", "private": true, "type": "module", "scripts": { diff --git a/sdk/src/index.ts b/sdk/src/index.ts index 613802f..8e5c22a 100644 --- a/sdk/src/index.ts +++ b/sdk/src/index.ts @@ -3,6 +3,7 @@ import { mergeConfig } from "./config"; import { Transport } from "./transport"; import { startPageTracking } from "./events"; import { buildEnvContext, collectHighEntropyHints, pageContext } from "./context"; +import { SDK_VERSION, BUILD_ID, SCHEMA_VERSION, CONFIG_VERSION } from "./types"; import type { CtkConfig } from "./config"; // IIFE 打包入口(build.mjs):模块加载即接管全局 ctk 命令队列并重放。 @@ -49,7 +50,7 @@ function dispatch(cmd: string, arg?: unknown) { occurred_at: new Date().toISOString(), path: pc.path, title: pc.title, - properties: { sdk_version: "0.1.0" }, + properties: { sdk_version: SDK_VERSION, build_id: BUILD_ID, schema_version: SCHEMA_VERSION, config_version: CONFIG_VERSION }, }); startPageTracking(transport); } else if (cmd === "track" && transport) { diff --git a/sdk/src/types.ts b/sdk/src/types.ts new file mode 100644 index 0000000..4b14c47 --- /dev/null +++ b/sdk/src/types.ts @@ -0,0 +1,23 @@ +/** 事件归属四元组:入队时冻结,flush 不再重读(spec §1.1) */ +export interface Attribution { + site_id: string; + visitor_id: string; + session_id: string; + page_id: string; + seq: number; + purpose?: null; // V1.1 预留 + epoch?: null; // V1.1 预留 + grant_ref?: null; // V1.1 预留 +} +export const SDK_VERSION = "0.2.0"; // 与 package.json 同步手维护 +export const SCHEMA_VERSION = 2; +export const CONFIG_VERSION = "1"; + +// build_id 由 esbuild/vitest define 注入的全局常量(ambient 声明, +// 不能用模块级 export declare——esbuild 打包时会被擦除导致引用悬空) +declare global { + // eslint-disable-next-line no-var + var __BUILD_ID__: string | undefined; +} +/** 运行时兜底读取(build 注入缺失时降级 "unknown",绝不抛) */ +export const BUILD_ID: string = globalThis.__BUILD_ID__ ?? "unknown"; diff --git a/sdk/test/index.test.ts b/sdk/test/index.test.ts index 69406e5..ccc7479 100644 --- a/sdk/test/index.test.ts +++ b/sdk/test/index.test.ts @@ -54,7 +54,7 @@ describe("index 入口", () => { delete (document as { referrer?: string }).referrer; }); - it("init 后 sdk_loaded 入队(含 sdk_version)", async () => { + it("init 后 sdk_loaded 入队(含版本组:sdk_version/build_id/schema_version/config_version)", async () => { await loadSdk([["init", { siteId: "site-1", endpoint: "/e", flushMs: 60000 }]]); g.ctk!("flush"); @@ -62,7 +62,11 @@ describe("index 入口", () => { expect(bodies).toHaveLength(1); const loaded = bodies[0].events.find((e: { event_name: string }) => e.event_name === "sdk_loaded"); expect(loaded).toBeTruthy(); - expect(loaded.properties.sdk_version).toBe("0.1.0"); + expect(loaded.properties.sdk_version).toBe("0.2.0"); + // build_id 由 vitest.config define 注入 "vitest";无 define 环境兜底 "unknown" + expect(loaded.properties.build_id).toBe("vitest"); + expect(loaded.properties.schema_version).toBe(2); + expect(loaded.properties.config_version).toBe("1"); expect(loaded.event_id).toMatch(UUID_RE); }); diff --git a/sdk/vitest.config.ts b/sdk/vitest.config.ts index 3e29a97..d80da8e 100644 --- a/sdk/vitest.config.ts +++ b/sdk/vitest.config.ts @@ -1,4 +1,6 @@ import { defineConfig } from "vitest/config"; export default defineConfig({ test: { environment: "happy-dom", setupFiles: [] }, + // 与 build.mjs 同构的 define 注入(点链键):测试内 BUILD_ID 恒为 "vitest" + define: { "globalThis.__BUILD_ID__": JSON.stringify("vitest") }, });