rsclick-log-sdk-web 0.1.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 +181 -0
- package/dist/browser.d.ts +9 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.global.js +359 -0
- package/dist/index.js +320 -0
- package/dist/sdk.d.ts +41 -0
- package/dist/types.d.ts +56 -0
- package/package.json +36 -0
package/README.md
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# `rsclick-log-sdk-web`
|
|
2
|
+
|
|
3
|
+
用于 `rs-click-log` MVP 的轻量 Web 埋点 SDK。
|
|
4
|
+
|
|
5
|
+
目标:
|
|
6
|
+
|
|
7
|
+
- 在浏览器端生成并持久化 `anonymous_id`
|
|
8
|
+
- 自动维护 30 分钟 `session_id`
|
|
9
|
+
- 调用后端 `POST /track/collect` 上报事件
|
|
10
|
+
- 默认初始化后自动发送 `$page_view`
|
|
11
|
+
- 登录态识别仅影响后续事件
|
|
12
|
+
|
|
13
|
+
## 目录
|
|
14
|
+
|
|
15
|
+
```text
|
|
16
|
+
fronts/sdk-web
|
|
17
|
+
├── src
|
|
18
|
+
├── examples
|
|
19
|
+
├── tests
|
|
20
|
+
└── package.json
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## 开发命令
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
cd fronts/sdk-web
|
|
27
|
+
bun test
|
|
28
|
+
npx -p typescript tsc --noEmit -p tsconfig.json
|
|
29
|
+
bun run build
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Vue 示例:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
cd fronts/sdk-web/examples/vue
|
|
36
|
+
npm install
|
|
37
|
+
npm run dev
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## 输出产物
|
|
41
|
+
|
|
42
|
+
- `dist/index.js`: ESM 构建产物
|
|
43
|
+
- `dist/index.global.js`: 浏览器全局 bundle,暴露 `window.TrackingAnalyticsSDK`
|
|
44
|
+
- `dist/index.d.ts`: ESM 入口类型声明
|
|
45
|
+
|
|
46
|
+
## 初始化示例
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import { init } from "rsclick-log-sdk-web";
|
|
50
|
+
|
|
51
|
+
await init({
|
|
52
|
+
writeKey: "trk_live_xxx",
|
|
53
|
+
endpoint: "https://your-api.example.com/track/collect",
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
关闭默认 `$page_view`:
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
import { init } from "rsclick-log-sdk-web";
|
|
61
|
+
|
|
62
|
+
await init({
|
|
63
|
+
writeKey: "trk_live_xxx",
|
|
64
|
+
endpoint: "https://your-api.example.com/track/collect",
|
|
65
|
+
autoPageview: false,
|
|
66
|
+
});
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## 事件示例
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
import { track } from "rsclick-log-sdk-web";
|
|
73
|
+
|
|
74
|
+
await track("click_signup_button", {
|
|
75
|
+
properties: {
|
|
76
|
+
button_name: "立即注册",
|
|
77
|
+
page_section: "hero",
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
主动发送页面浏览事件:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
import { page } from "rsclick-log-sdk-web";
|
|
86
|
+
|
|
87
|
+
await page({
|
|
88
|
+
properties: {
|
|
89
|
+
from: "manual-refresh",
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Identify 示例
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
import { identify, track } from "rsclick-log-sdk-web";
|
|
98
|
+
|
|
99
|
+
identify("user_123");
|
|
100
|
+
|
|
101
|
+
await track("submit_signup_form", {
|
|
102
|
+
properties: {
|
|
103
|
+
plan_type: "pro",
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`identify` 只影响后续事件,不会回溯历史匿名事件。
|
|
109
|
+
|
|
110
|
+
## 浏览器直接接入
|
|
111
|
+
|
|
112
|
+
```html
|
|
113
|
+
<script src="/sdk/index.global.js"></script>
|
|
114
|
+
<script>
|
|
115
|
+
TrackingAnalyticsSDK.init({
|
|
116
|
+
writeKey: "trk_live_xxx",
|
|
117
|
+
endpoint: "https://your-api.example.com/track/collect"
|
|
118
|
+
}).then(function () {
|
|
119
|
+
return TrackingAnalyticsSDK.track("landing_cta_click", {
|
|
120
|
+
properties: {
|
|
121
|
+
section: "hero"
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
</script>
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## 采集字段
|
|
129
|
+
|
|
130
|
+
SDK 默认上报以下字段:
|
|
131
|
+
|
|
132
|
+
- `anonymous_id`
|
|
133
|
+
- `session_id`
|
|
134
|
+
- `user_id`
|
|
135
|
+
- `page_url`
|
|
136
|
+
- `page_path`
|
|
137
|
+
- `page_title`
|
|
138
|
+
- `referrer`
|
|
139
|
+
- `utm_source`
|
|
140
|
+
- `utm_medium`
|
|
141
|
+
- `utm_campaign`
|
|
142
|
+
- `properties`
|
|
143
|
+
|
|
144
|
+
传输策略:
|
|
145
|
+
|
|
146
|
+
1. 优先调用 `navigator.sendBeacon`
|
|
147
|
+
2. 如果 `sendBeacon` 返回 `false` 或抛错,则回退到 `fetch(..., { keepalive: true })`
|
|
148
|
+
|
|
149
|
+
## 发布说明
|
|
150
|
+
|
|
151
|
+
当前 npm 包名为 `rsclick-log-sdk-web`。如果要发布到 npm,按下面步骤操作:
|
|
152
|
+
|
|
153
|
+
1. 进入 SDK 目录并执行发布前检查。
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
cd fronts/sdk-web
|
|
157
|
+
bun test
|
|
158
|
+
bun run build
|
|
159
|
+
npx -p typescript tsc --noEmit -p tsconfig.json
|
|
160
|
+
npm pack --dry-run
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
2. 登录 npm 并发布:
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
npm login
|
|
167
|
+
npm publish --access public
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
如果本机 `~/.npmrc` 残留旧 token,优先清理后再登录,否则 `npm publish` 可能不会使用当前登录态。
|
|
171
|
+
|
|
172
|
+
当前包通过 `files` 字段控制发布内容,默认只会带上:
|
|
173
|
+
|
|
174
|
+
- `dist`
|
|
175
|
+
- `README.md`
|
|
176
|
+
|
|
177
|
+
如果只想内部使用,不需要发布到 npm,可以直接分发构建产物:
|
|
178
|
+
|
|
179
|
+
- `dist/index.js`
|
|
180
|
+
- `dist/index.global.js`
|
|
181
|
+
- `dist/index.d.ts`
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
declare const TrackingAnalyticsSDK: {
|
|
2
|
+
init: (options: import("./types").TrackingInitOptions) => Promise<void>;
|
|
3
|
+
track: (eventName: string, options?: import("./types").TrackOptions) => Promise<import("./types").TrackingDispatchResult>;
|
|
4
|
+
identify: (userId: string | null) => void;
|
|
5
|
+
page: (options?: import("./types").PageOptions) => Promise<import("./types").TrackingDispatchResult>;
|
|
6
|
+
reset: () => import("./types").TrackingStateSnapshot;
|
|
7
|
+
getDebugState: () => import("./types").TrackingStateSnapshot;
|
|
8
|
+
};
|
|
9
|
+
export { TrackingAnalyticsSDK };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { getDebugState, identify, init, page, reset, track } from "./sdk";
|
|
2
|
+
export type { PageOptions, TrackOptions, TrackingCollectPayload, TrackingDispatchResult, TrackingEventPayload, TrackingInitOptions, TrackingProperties, TrackingResponse, TrackingScalar, TrackingStateSnapshot, } from "./types";
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
(() => {
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __moduleCache = /* @__PURE__ */ new WeakMap;
|
|
7
|
+
var __toCommonJS = (from) => {
|
|
8
|
+
var entry = __moduleCache.get(from), desc;
|
|
9
|
+
if (entry)
|
|
10
|
+
return entry;
|
|
11
|
+
entry = __defProp({}, "__esModule", { value: true });
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function")
|
|
13
|
+
__getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
|
|
14
|
+
get: () => from[key],
|
|
15
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
16
|
+
}));
|
|
17
|
+
__moduleCache.set(from, entry);
|
|
18
|
+
return entry;
|
|
19
|
+
};
|
|
20
|
+
var __export = (target, all) => {
|
|
21
|
+
for (var name in all)
|
|
22
|
+
__defProp(target, name, {
|
|
23
|
+
get: all[name],
|
|
24
|
+
enumerable: true,
|
|
25
|
+
configurable: true,
|
|
26
|
+
set: (newValue) => all[name] = () => newValue
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// src/browser.ts
|
|
31
|
+
var exports_browser = {};
|
|
32
|
+
__export(exports_browser, {
|
|
33
|
+
TrackingAnalyticsSDK: () => TrackingAnalyticsSDK
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// src/sdk.ts
|
|
37
|
+
var DEFAULT_ENDPOINT = "/track/collect";
|
|
38
|
+
var DEFAULT_SESSION_TIMEOUT_MINUTES = 30;
|
|
39
|
+
var DEFAULT_STORAGE_PREFIX = "rs_tracking_sdk";
|
|
40
|
+
var STORAGE_KEYS = {
|
|
41
|
+
anonymousId: "anonymous_id",
|
|
42
|
+
sessionId: "session_id",
|
|
43
|
+
lastActivityAt: "last_activity_at",
|
|
44
|
+
userId: "user_id"
|
|
45
|
+
};
|
|
46
|
+
var trackingClient = null;
|
|
47
|
+
var init = async (options) => {
|
|
48
|
+
trackingClient = createTrackingClient(options);
|
|
49
|
+
await trackingClient.init();
|
|
50
|
+
};
|
|
51
|
+
var track = async (eventName, options = {}) => getClient().track(eventName, options);
|
|
52
|
+
var identify = (userId) => {
|
|
53
|
+
getClient().identify(userId);
|
|
54
|
+
};
|
|
55
|
+
var page = async (options = {}) => getClient().page(options);
|
|
56
|
+
var reset = () => getClient().reset();
|
|
57
|
+
var getDebugState = () => getClient().getState();
|
|
58
|
+
var createTrackingClient = (options) => {
|
|
59
|
+
const browser = resolveBrowser();
|
|
60
|
+
const config = normalizeConfig(options);
|
|
61
|
+
const storage = createScopedStorage(browser, config.storagePrefix);
|
|
62
|
+
let state = loadState(browser, storage);
|
|
63
|
+
const touchSession = (now) => {
|
|
64
|
+
const timeoutExceeded = now - state.lastActivityAt >= config.sessionTimeoutMs / 1000;
|
|
65
|
+
if (timeoutExceeded) {
|
|
66
|
+
state = {
|
|
67
|
+
...state,
|
|
68
|
+
sessionId: generateId(browser)
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
state = {
|
|
72
|
+
...state,
|
|
73
|
+
lastActivityAt: now
|
|
74
|
+
};
|
|
75
|
+
persistState(storage, state);
|
|
76
|
+
return state;
|
|
77
|
+
};
|
|
78
|
+
const buildEventPayload = (eventName, options2 = {}) => {
|
|
79
|
+
const now = currentUnixTime(browser);
|
|
80
|
+
const nextState = touchSession(now);
|
|
81
|
+
const pageContext = resolvePageContext(browser, options2.page);
|
|
82
|
+
const utm = extractUtm(pageContext.url);
|
|
83
|
+
return {
|
|
84
|
+
event_name: eventName.trim(),
|
|
85
|
+
event_time: options2.eventTime,
|
|
86
|
+
anonymous_id: nextState.anonymousId,
|
|
87
|
+
session_id: nextState.sessionId,
|
|
88
|
+
user_id: nextState.userId ?? undefined,
|
|
89
|
+
page_url: pageContext.url,
|
|
90
|
+
page_path: pageContext.path,
|
|
91
|
+
page_title: pageContext.title,
|
|
92
|
+
referrer: pageContext.referrer,
|
|
93
|
+
utm_source: utm.utm_source,
|
|
94
|
+
utm_medium: utm.utm_medium,
|
|
95
|
+
utm_campaign: utm.utm_campaign,
|
|
96
|
+
properties: sanitizeProperties(options2.properties)
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
const dispatch = async (payload) => {
|
|
100
|
+
const body = JSON.stringify(payload);
|
|
101
|
+
if (canUseBeacon(browser)) {
|
|
102
|
+
try {
|
|
103
|
+
const blob = createJsonBlob(browser, body);
|
|
104
|
+
const sent = browser.navigator?.sendBeacon?.(config.endpoint, blob);
|
|
105
|
+
if (sent) {
|
|
106
|
+
return { transport: "beacon" };
|
|
107
|
+
}
|
|
108
|
+
} catch {
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const response = await postWithFetch(browser, config.endpoint, body);
|
|
112
|
+
return {
|
|
113
|
+
transport: "fetch",
|
|
114
|
+
response
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
return {
|
|
118
|
+
init: async () => {
|
|
119
|
+
if (config.autoPageview) {
|
|
120
|
+
await dispatch({
|
|
121
|
+
write_key: config.writeKey,
|
|
122
|
+
events: [buildEventPayload("$page_view")]
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
track: async (eventName, options2 = {}) => {
|
|
127
|
+
const payload = {
|
|
128
|
+
write_key: config.writeKey,
|
|
129
|
+
events: [buildEventPayload(eventName, options2)]
|
|
130
|
+
};
|
|
131
|
+
return dispatch(payload);
|
|
132
|
+
},
|
|
133
|
+
identify: (userId) => {
|
|
134
|
+
state = {
|
|
135
|
+
...state,
|
|
136
|
+
userId: normalizeOptionalString(userId)
|
|
137
|
+
};
|
|
138
|
+
persistState(storage, state);
|
|
139
|
+
},
|
|
140
|
+
page: async (options2 = {}) => dispatch({
|
|
141
|
+
write_key: config.writeKey,
|
|
142
|
+
events: [
|
|
143
|
+
buildEventPayload("$page_view", {
|
|
144
|
+
properties: options2.properties,
|
|
145
|
+
page: options2
|
|
146
|
+
})
|
|
147
|
+
]
|
|
148
|
+
}),
|
|
149
|
+
reset: () => {
|
|
150
|
+
clearState(storage);
|
|
151
|
+
state = createFreshState(browser);
|
|
152
|
+
persistState(storage, state);
|
|
153
|
+
return { ...state };
|
|
154
|
+
},
|
|
155
|
+
getState: () => ({ ...state })
|
|
156
|
+
};
|
|
157
|
+
};
|
|
158
|
+
var getClient = () => {
|
|
159
|
+
if (!trackingClient) {
|
|
160
|
+
throw new Error("Tracking SDK has not been initialized");
|
|
161
|
+
}
|
|
162
|
+
return trackingClient;
|
|
163
|
+
};
|
|
164
|
+
var resolveBrowser = () => {
|
|
165
|
+
const candidate = globalThis;
|
|
166
|
+
return {
|
|
167
|
+
document: candidate.document,
|
|
168
|
+
location: candidate.location,
|
|
169
|
+
navigator: candidate.navigator,
|
|
170
|
+
screen: candidate.screen,
|
|
171
|
+
localStorage: candidate.localStorage,
|
|
172
|
+
fetch: candidate.fetch?.bind(globalThis),
|
|
173
|
+
crypto: candidate.crypto,
|
|
174
|
+
Blob: candidate.Blob,
|
|
175
|
+
Date
|
|
176
|
+
};
|
|
177
|
+
};
|
|
178
|
+
var normalizeConfig = (options) => {
|
|
179
|
+
const writeKey = options.writeKey.trim();
|
|
180
|
+
if (!writeKey) {
|
|
181
|
+
throw new Error("Tracking writeKey is required");
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
writeKey,
|
|
185
|
+
endpoint: normalizeOptionalString(options.endpoint) ?? DEFAULT_ENDPOINT,
|
|
186
|
+
autoPageview: options.autoPageview ?? true,
|
|
187
|
+
sessionTimeoutMs: Math.max(options.sessionTimeoutMinutes ?? DEFAULT_SESSION_TIMEOUT_MINUTES, 1) * 60000,
|
|
188
|
+
storagePrefix: normalizeOptionalString(options.storagePrefix) ?? DEFAULT_STORAGE_PREFIX
|
|
189
|
+
};
|
|
190
|
+
};
|
|
191
|
+
var createScopedStorage = (browser, prefix) => {
|
|
192
|
+
const nativeStorage = browser.localStorage;
|
|
193
|
+
if (!nativeStorage) {
|
|
194
|
+
const memoryStore = new Map;
|
|
195
|
+
return {
|
|
196
|
+
getItem: (key) => memoryStore.get(key) ?? null,
|
|
197
|
+
setItem: (key, value) => {
|
|
198
|
+
memoryStore.set(key, value);
|
|
199
|
+
},
|
|
200
|
+
removeItem: (key) => {
|
|
201
|
+
memoryStore.delete(key);
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
return {
|
|
206
|
+
getItem: (key) => nativeStorage.getItem(`${prefix}:${key}`),
|
|
207
|
+
setItem: (key, value) => {
|
|
208
|
+
nativeStorage.setItem(`${prefix}:${key}`, value);
|
|
209
|
+
},
|
|
210
|
+
removeItem: (key) => {
|
|
211
|
+
nativeStorage.removeItem(`${prefix}:${key}`);
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
};
|
|
215
|
+
var loadState = (browser, storage) => {
|
|
216
|
+
const anonymousId = normalizeOptionalString(storage.getItem(STORAGE_KEYS.anonymousId));
|
|
217
|
+
const sessionId = normalizeOptionalString(storage.getItem(STORAGE_KEYS.sessionId));
|
|
218
|
+
const userId = normalizeOptionalString(storage.getItem(STORAGE_KEYS.userId));
|
|
219
|
+
const lastActivityAt = parseInteger(storage.getItem(STORAGE_KEYS.lastActivityAt));
|
|
220
|
+
if (anonymousId && sessionId && lastActivityAt) {
|
|
221
|
+
return {
|
|
222
|
+
anonymousId,
|
|
223
|
+
sessionId,
|
|
224
|
+
userId,
|
|
225
|
+
lastActivityAt
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
const freshState = createFreshState(browser);
|
|
229
|
+
persistState(storage, freshState);
|
|
230
|
+
return freshState;
|
|
231
|
+
};
|
|
232
|
+
var createFreshState = (browser) => {
|
|
233
|
+
const now = currentUnixTime(browser);
|
|
234
|
+
return {
|
|
235
|
+
anonymousId: generateId(browser),
|
|
236
|
+
sessionId: generateId(browser),
|
|
237
|
+
userId: null,
|
|
238
|
+
lastActivityAt: now
|
|
239
|
+
};
|
|
240
|
+
};
|
|
241
|
+
var persistState = (storage, state) => {
|
|
242
|
+
storage.setItem(STORAGE_KEYS.anonymousId, state.anonymousId);
|
|
243
|
+
storage.setItem(STORAGE_KEYS.sessionId, state.sessionId);
|
|
244
|
+
storage.setItem(STORAGE_KEYS.lastActivityAt, String(state.lastActivityAt));
|
|
245
|
+
if (state.userId) {
|
|
246
|
+
storage.setItem(STORAGE_KEYS.userId, state.userId);
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
storage.removeItem(STORAGE_KEYS.userId);
|
|
250
|
+
};
|
|
251
|
+
var clearState = (storage) => {
|
|
252
|
+
storage.removeItem(STORAGE_KEYS.anonymousId);
|
|
253
|
+
storage.removeItem(STORAGE_KEYS.sessionId);
|
|
254
|
+
storage.removeItem(STORAGE_KEYS.lastActivityAt);
|
|
255
|
+
storage.removeItem(STORAGE_KEYS.userId);
|
|
256
|
+
};
|
|
257
|
+
var currentUnixTime = (browser) => Math.floor((browser.Date ?? Date).now() / 1000);
|
|
258
|
+
var generateId = (browser) => {
|
|
259
|
+
if (browser.crypto?.randomUUID) {
|
|
260
|
+
return browser.crypto.randomUUID();
|
|
261
|
+
}
|
|
262
|
+
const bytes = new Uint8Array(16);
|
|
263
|
+
if (browser.crypto?.getRandomValues) {
|
|
264
|
+
browser.crypto.getRandomValues(bytes);
|
|
265
|
+
} else {
|
|
266
|
+
for (let index = 0;index < bytes.length; index += 1) {
|
|
267
|
+
bytes[index] = Math.floor(Math.random() * 256);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
271
|
+
};
|
|
272
|
+
var resolvePageContext = (browser, page2 = {}) => ({
|
|
273
|
+
url: normalizeOptionalString(page2.url) ?? undefinedIfNull(normalizeOptionalString(browser.location?.href)),
|
|
274
|
+
path: normalizeOptionalString(page2.path) ?? undefinedIfNull(normalizeOptionalString(browser.location?.pathname)),
|
|
275
|
+
title: normalizeOptionalString(page2.title) ?? undefinedIfNull(normalizeOptionalString(browser.document?.title)),
|
|
276
|
+
referrer: normalizeOptionalString(page2.referrer) ?? undefinedIfNull(normalizeOptionalString(browser.document?.referrer))
|
|
277
|
+
});
|
|
278
|
+
var extractUtm = (url) => {
|
|
279
|
+
if (!url) {
|
|
280
|
+
return {};
|
|
281
|
+
}
|
|
282
|
+
try {
|
|
283
|
+
const parsed = new URL(url, "https://sdk.local");
|
|
284
|
+
return {
|
|
285
|
+
utm_source: undefinedIfNull(normalizeOptionalString(parsed.searchParams.get("utm_source"))),
|
|
286
|
+
utm_medium: undefinedIfNull(normalizeOptionalString(parsed.searchParams.get("utm_medium"))),
|
|
287
|
+
utm_campaign: undefinedIfNull(normalizeOptionalString(parsed.searchParams.get("utm_campaign")))
|
|
288
|
+
};
|
|
289
|
+
} catch {
|
|
290
|
+
return {};
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
var sanitizeProperties = (properties) => {
|
|
294
|
+
if (!properties) {
|
|
295
|
+
return {};
|
|
296
|
+
}
|
|
297
|
+
return Object.entries(properties).reduce((accumulator, [key, value]) => {
|
|
298
|
+
const normalizedKey = key.trim();
|
|
299
|
+
if (!normalizedKey) {
|
|
300
|
+
return accumulator;
|
|
301
|
+
}
|
|
302
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value === null) {
|
|
303
|
+
accumulator[normalizedKey] = value;
|
|
304
|
+
}
|
|
305
|
+
return accumulator;
|
|
306
|
+
}, {});
|
|
307
|
+
};
|
|
308
|
+
var normalizeOptionalString = (value) => {
|
|
309
|
+
const trimmed = value?.trim();
|
|
310
|
+
return trimmed ? trimmed : null;
|
|
311
|
+
};
|
|
312
|
+
var undefinedIfNull = (value) => value ?? undefined;
|
|
313
|
+
var parseInteger = (value) => {
|
|
314
|
+
if (!value) {
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
const parsed = Number.parseInt(value, 10);
|
|
318
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
319
|
+
};
|
|
320
|
+
var canUseBeacon = (browser) => typeof browser.navigator?.sendBeacon === "function";
|
|
321
|
+
var createJsonBlob = (browser, body) => {
|
|
322
|
+
if (browser.Blob) {
|
|
323
|
+
return new browser.Blob([body], { type: "application/json" });
|
|
324
|
+
}
|
|
325
|
+
return body;
|
|
326
|
+
};
|
|
327
|
+
var postWithFetch = async (browser, endpoint, body) => {
|
|
328
|
+
if (!browser.fetch) {
|
|
329
|
+
throw new Error("Tracking SDK requires fetch when sendBeacon is unavailable");
|
|
330
|
+
}
|
|
331
|
+
const response = await browser.fetch(endpoint, {
|
|
332
|
+
method: "POST",
|
|
333
|
+
keepalive: true,
|
|
334
|
+
headers: {
|
|
335
|
+
"content-type": "application/json"
|
|
336
|
+
},
|
|
337
|
+
body
|
|
338
|
+
});
|
|
339
|
+
if (!response.ok) {
|
|
340
|
+
throw new Error(`Tracking collect request failed with status ${response.status}`);
|
|
341
|
+
}
|
|
342
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
343
|
+
if (!contentType.includes("application/json")) {
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
return await response.json();
|
|
347
|
+
};
|
|
348
|
+
// src/browser.ts
|
|
349
|
+
var TrackingAnalyticsSDK = {
|
|
350
|
+
init,
|
|
351
|
+
track,
|
|
352
|
+
identify,
|
|
353
|
+
page,
|
|
354
|
+
reset,
|
|
355
|
+
getDebugState
|
|
356
|
+
};
|
|
357
|
+
var browserGlobal = globalThis;
|
|
358
|
+
browserGlobal.TrackingAnalyticsSDK = TrackingAnalyticsSDK;
|
|
359
|
+
})();
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
// src/sdk.ts
|
|
2
|
+
var DEFAULT_ENDPOINT = "/track/collect";
|
|
3
|
+
var DEFAULT_SESSION_TIMEOUT_MINUTES = 30;
|
|
4
|
+
var DEFAULT_STORAGE_PREFIX = "rs_tracking_sdk";
|
|
5
|
+
var STORAGE_KEYS = {
|
|
6
|
+
anonymousId: "anonymous_id",
|
|
7
|
+
sessionId: "session_id",
|
|
8
|
+
lastActivityAt: "last_activity_at",
|
|
9
|
+
userId: "user_id"
|
|
10
|
+
};
|
|
11
|
+
var trackingClient = null;
|
|
12
|
+
var init = async (options) => {
|
|
13
|
+
trackingClient = createTrackingClient(options);
|
|
14
|
+
await trackingClient.init();
|
|
15
|
+
};
|
|
16
|
+
var track = async (eventName, options = {}) => getClient().track(eventName, options);
|
|
17
|
+
var identify = (userId) => {
|
|
18
|
+
getClient().identify(userId);
|
|
19
|
+
};
|
|
20
|
+
var page = async (options = {}) => getClient().page(options);
|
|
21
|
+
var reset = () => getClient().reset();
|
|
22
|
+
var getDebugState = () => getClient().getState();
|
|
23
|
+
var createTrackingClient = (options) => {
|
|
24
|
+
const browser = resolveBrowser();
|
|
25
|
+
const config = normalizeConfig(options);
|
|
26
|
+
const storage = createScopedStorage(browser, config.storagePrefix);
|
|
27
|
+
let state = loadState(browser, storage);
|
|
28
|
+
const touchSession = (now) => {
|
|
29
|
+
const timeoutExceeded = now - state.lastActivityAt >= config.sessionTimeoutMs / 1000;
|
|
30
|
+
if (timeoutExceeded) {
|
|
31
|
+
state = {
|
|
32
|
+
...state,
|
|
33
|
+
sessionId: generateId(browser)
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
state = {
|
|
37
|
+
...state,
|
|
38
|
+
lastActivityAt: now
|
|
39
|
+
};
|
|
40
|
+
persistState(storage, state);
|
|
41
|
+
return state;
|
|
42
|
+
};
|
|
43
|
+
const buildEventPayload = (eventName, options2 = {}) => {
|
|
44
|
+
const now = currentUnixTime(browser);
|
|
45
|
+
const nextState = touchSession(now);
|
|
46
|
+
const pageContext = resolvePageContext(browser, options2.page);
|
|
47
|
+
const utm = extractUtm(pageContext.url);
|
|
48
|
+
return {
|
|
49
|
+
event_name: eventName.trim(),
|
|
50
|
+
event_time: options2.eventTime,
|
|
51
|
+
anonymous_id: nextState.anonymousId,
|
|
52
|
+
session_id: nextState.sessionId,
|
|
53
|
+
user_id: nextState.userId ?? undefined,
|
|
54
|
+
page_url: pageContext.url,
|
|
55
|
+
page_path: pageContext.path,
|
|
56
|
+
page_title: pageContext.title,
|
|
57
|
+
referrer: pageContext.referrer,
|
|
58
|
+
utm_source: utm.utm_source,
|
|
59
|
+
utm_medium: utm.utm_medium,
|
|
60
|
+
utm_campaign: utm.utm_campaign,
|
|
61
|
+
properties: sanitizeProperties(options2.properties)
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
const dispatch = async (payload) => {
|
|
65
|
+
const body = JSON.stringify(payload);
|
|
66
|
+
if (canUseBeacon(browser)) {
|
|
67
|
+
try {
|
|
68
|
+
const blob = createJsonBlob(browser, body);
|
|
69
|
+
const sent = browser.navigator?.sendBeacon?.(config.endpoint, blob);
|
|
70
|
+
if (sent) {
|
|
71
|
+
return { transport: "beacon" };
|
|
72
|
+
}
|
|
73
|
+
} catch {
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const response = await postWithFetch(browser, config.endpoint, body);
|
|
77
|
+
return {
|
|
78
|
+
transport: "fetch",
|
|
79
|
+
response
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
return {
|
|
83
|
+
init: async () => {
|
|
84
|
+
if (config.autoPageview) {
|
|
85
|
+
await dispatch({
|
|
86
|
+
write_key: config.writeKey,
|
|
87
|
+
events: [buildEventPayload("$page_view")]
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
track: async (eventName, options2 = {}) => {
|
|
92
|
+
const payload = {
|
|
93
|
+
write_key: config.writeKey,
|
|
94
|
+
events: [buildEventPayload(eventName, options2)]
|
|
95
|
+
};
|
|
96
|
+
return dispatch(payload);
|
|
97
|
+
},
|
|
98
|
+
identify: (userId) => {
|
|
99
|
+
state = {
|
|
100
|
+
...state,
|
|
101
|
+
userId: normalizeOptionalString(userId)
|
|
102
|
+
};
|
|
103
|
+
persistState(storage, state);
|
|
104
|
+
},
|
|
105
|
+
page: async (options2 = {}) => dispatch({
|
|
106
|
+
write_key: config.writeKey,
|
|
107
|
+
events: [
|
|
108
|
+
buildEventPayload("$page_view", {
|
|
109
|
+
properties: options2.properties,
|
|
110
|
+
page: options2
|
|
111
|
+
})
|
|
112
|
+
]
|
|
113
|
+
}),
|
|
114
|
+
reset: () => {
|
|
115
|
+
clearState(storage);
|
|
116
|
+
state = createFreshState(browser);
|
|
117
|
+
persistState(storage, state);
|
|
118
|
+
return { ...state };
|
|
119
|
+
},
|
|
120
|
+
getState: () => ({ ...state })
|
|
121
|
+
};
|
|
122
|
+
};
|
|
123
|
+
var getClient = () => {
|
|
124
|
+
if (!trackingClient) {
|
|
125
|
+
throw new Error("Tracking SDK has not been initialized");
|
|
126
|
+
}
|
|
127
|
+
return trackingClient;
|
|
128
|
+
};
|
|
129
|
+
var resolveBrowser = () => {
|
|
130
|
+
const candidate = globalThis;
|
|
131
|
+
return {
|
|
132
|
+
document: candidate.document,
|
|
133
|
+
location: candidate.location,
|
|
134
|
+
navigator: candidate.navigator,
|
|
135
|
+
screen: candidate.screen,
|
|
136
|
+
localStorage: candidate.localStorage,
|
|
137
|
+
fetch: candidate.fetch?.bind(globalThis),
|
|
138
|
+
crypto: candidate.crypto,
|
|
139
|
+
Blob: candidate.Blob,
|
|
140
|
+
Date
|
|
141
|
+
};
|
|
142
|
+
};
|
|
143
|
+
var normalizeConfig = (options) => {
|
|
144
|
+
const writeKey = options.writeKey.trim();
|
|
145
|
+
if (!writeKey) {
|
|
146
|
+
throw new Error("Tracking writeKey is required");
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
writeKey,
|
|
150
|
+
endpoint: normalizeOptionalString(options.endpoint) ?? DEFAULT_ENDPOINT,
|
|
151
|
+
autoPageview: options.autoPageview ?? true,
|
|
152
|
+
sessionTimeoutMs: Math.max(options.sessionTimeoutMinutes ?? DEFAULT_SESSION_TIMEOUT_MINUTES, 1) * 60000,
|
|
153
|
+
storagePrefix: normalizeOptionalString(options.storagePrefix) ?? DEFAULT_STORAGE_PREFIX
|
|
154
|
+
};
|
|
155
|
+
};
|
|
156
|
+
var createScopedStorage = (browser, prefix) => {
|
|
157
|
+
const nativeStorage = browser.localStorage;
|
|
158
|
+
if (!nativeStorage) {
|
|
159
|
+
const memoryStore = new Map;
|
|
160
|
+
return {
|
|
161
|
+
getItem: (key) => memoryStore.get(key) ?? null,
|
|
162
|
+
setItem: (key, value) => {
|
|
163
|
+
memoryStore.set(key, value);
|
|
164
|
+
},
|
|
165
|
+
removeItem: (key) => {
|
|
166
|
+
memoryStore.delete(key);
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
getItem: (key) => nativeStorage.getItem(`${prefix}:${key}`),
|
|
172
|
+
setItem: (key, value) => {
|
|
173
|
+
nativeStorage.setItem(`${prefix}:${key}`, value);
|
|
174
|
+
},
|
|
175
|
+
removeItem: (key) => {
|
|
176
|
+
nativeStorage.removeItem(`${prefix}:${key}`);
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
};
|
|
180
|
+
var loadState = (browser, storage) => {
|
|
181
|
+
const anonymousId = normalizeOptionalString(storage.getItem(STORAGE_KEYS.anonymousId));
|
|
182
|
+
const sessionId = normalizeOptionalString(storage.getItem(STORAGE_KEYS.sessionId));
|
|
183
|
+
const userId = normalizeOptionalString(storage.getItem(STORAGE_KEYS.userId));
|
|
184
|
+
const lastActivityAt = parseInteger(storage.getItem(STORAGE_KEYS.lastActivityAt));
|
|
185
|
+
if (anonymousId && sessionId && lastActivityAt) {
|
|
186
|
+
return {
|
|
187
|
+
anonymousId,
|
|
188
|
+
sessionId,
|
|
189
|
+
userId,
|
|
190
|
+
lastActivityAt
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
const freshState = createFreshState(browser);
|
|
194
|
+
persistState(storage, freshState);
|
|
195
|
+
return freshState;
|
|
196
|
+
};
|
|
197
|
+
var createFreshState = (browser) => {
|
|
198
|
+
const now = currentUnixTime(browser);
|
|
199
|
+
return {
|
|
200
|
+
anonymousId: generateId(browser),
|
|
201
|
+
sessionId: generateId(browser),
|
|
202
|
+
userId: null,
|
|
203
|
+
lastActivityAt: now
|
|
204
|
+
};
|
|
205
|
+
};
|
|
206
|
+
var persistState = (storage, state) => {
|
|
207
|
+
storage.setItem(STORAGE_KEYS.anonymousId, state.anonymousId);
|
|
208
|
+
storage.setItem(STORAGE_KEYS.sessionId, state.sessionId);
|
|
209
|
+
storage.setItem(STORAGE_KEYS.lastActivityAt, String(state.lastActivityAt));
|
|
210
|
+
if (state.userId) {
|
|
211
|
+
storage.setItem(STORAGE_KEYS.userId, state.userId);
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
storage.removeItem(STORAGE_KEYS.userId);
|
|
215
|
+
};
|
|
216
|
+
var clearState = (storage) => {
|
|
217
|
+
storage.removeItem(STORAGE_KEYS.anonymousId);
|
|
218
|
+
storage.removeItem(STORAGE_KEYS.sessionId);
|
|
219
|
+
storage.removeItem(STORAGE_KEYS.lastActivityAt);
|
|
220
|
+
storage.removeItem(STORAGE_KEYS.userId);
|
|
221
|
+
};
|
|
222
|
+
var currentUnixTime = (browser) => Math.floor((browser.Date ?? Date).now() / 1000);
|
|
223
|
+
var generateId = (browser) => {
|
|
224
|
+
if (browser.crypto?.randomUUID) {
|
|
225
|
+
return browser.crypto.randomUUID();
|
|
226
|
+
}
|
|
227
|
+
const bytes = new Uint8Array(16);
|
|
228
|
+
if (browser.crypto?.getRandomValues) {
|
|
229
|
+
browser.crypto.getRandomValues(bytes);
|
|
230
|
+
} else {
|
|
231
|
+
for (let index = 0;index < bytes.length; index += 1) {
|
|
232
|
+
bytes[index] = Math.floor(Math.random() * 256);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
236
|
+
};
|
|
237
|
+
var resolvePageContext = (browser, page2 = {}) => ({
|
|
238
|
+
url: normalizeOptionalString(page2.url) ?? undefinedIfNull(normalizeOptionalString(browser.location?.href)),
|
|
239
|
+
path: normalizeOptionalString(page2.path) ?? undefinedIfNull(normalizeOptionalString(browser.location?.pathname)),
|
|
240
|
+
title: normalizeOptionalString(page2.title) ?? undefinedIfNull(normalizeOptionalString(browser.document?.title)),
|
|
241
|
+
referrer: normalizeOptionalString(page2.referrer) ?? undefinedIfNull(normalizeOptionalString(browser.document?.referrer))
|
|
242
|
+
});
|
|
243
|
+
var extractUtm = (url) => {
|
|
244
|
+
if (!url) {
|
|
245
|
+
return {};
|
|
246
|
+
}
|
|
247
|
+
try {
|
|
248
|
+
const parsed = new URL(url, "https://sdk.local");
|
|
249
|
+
return {
|
|
250
|
+
utm_source: undefinedIfNull(normalizeOptionalString(parsed.searchParams.get("utm_source"))),
|
|
251
|
+
utm_medium: undefinedIfNull(normalizeOptionalString(parsed.searchParams.get("utm_medium"))),
|
|
252
|
+
utm_campaign: undefinedIfNull(normalizeOptionalString(parsed.searchParams.get("utm_campaign")))
|
|
253
|
+
};
|
|
254
|
+
} catch {
|
|
255
|
+
return {};
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
var sanitizeProperties = (properties) => {
|
|
259
|
+
if (!properties) {
|
|
260
|
+
return {};
|
|
261
|
+
}
|
|
262
|
+
return Object.entries(properties).reduce((accumulator, [key, value]) => {
|
|
263
|
+
const normalizedKey = key.trim();
|
|
264
|
+
if (!normalizedKey) {
|
|
265
|
+
return accumulator;
|
|
266
|
+
}
|
|
267
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value === null) {
|
|
268
|
+
accumulator[normalizedKey] = value;
|
|
269
|
+
}
|
|
270
|
+
return accumulator;
|
|
271
|
+
}, {});
|
|
272
|
+
};
|
|
273
|
+
var normalizeOptionalString = (value) => {
|
|
274
|
+
const trimmed = value?.trim();
|
|
275
|
+
return trimmed ? trimmed : null;
|
|
276
|
+
};
|
|
277
|
+
var undefinedIfNull = (value) => value ?? undefined;
|
|
278
|
+
var parseInteger = (value) => {
|
|
279
|
+
if (!value) {
|
|
280
|
+
return null;
|
|
281
|
+
}
|
|
282
|
+
const parsed = Number.parseInt(value, 10);
|
|
283
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
284
|
+
};
|
|
285
|
+
var canUseBeacon = (browser) => typeof browser.navigator?.sendBeacon === "function";
|
|
286
|
+
var createJsonBlob = (browser, body) => {
|
|
287
|
+
if (browser.Blob) {
|
|
288
|
+
return new browser.Blob([body], { type: "application/json" });
|
|
289
|
+
}
|
|
290
|
+
return body;
|
|
291
|
+
};
|
|
292
|
+
var postWithFetch = async (browser, endpoint, body) => {
|
|
293
|
+
if (!browser.fetch) {
|
|
294
|
+
throw new Error("Tracking SDK requires fetch when sendBeacon is unavailable");
|
|
295
|
+
}
|
|
296
|
+
const response = await browser.fetch(endpoint, {
|
|
297
|
+
method: "POST",
|
|
298
|
+
keepalive: true,
|
|
299
|
+
headers: {
|
|
300
|
+
"content-type": "application/json"
|
|
301
|
+
},
|
|
302
|
+
body
|
|
303
|
+
});
|
|
304
|
+
if (!response.ok) {
|
|
305
|
+
throw new Error(`Tracking collect request failed with status ${response.status}`);
|
|
306
|
+
}
|
|
307
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
308
|
+
if (!contentType.includes("application/json")) {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
return await response.json();
|
|
312
|
+
};
|
|
313
|
+
export {
|
|
314
|
+
track,
|
|
315
|
+
reset,
|
|
316
|
+
page,
|
|
317
|
+
init,
|
|
318
|
+
identify,
|
|
319
|
+
getDebugState
|
|
320
|
+
};
|
package/dist/sdk.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { PageOptions, TrackOptions, TrackingDispatchResult, TrackingInitOptions, TrackingStateSnapshot } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* 初始化 SDK,并按默认配置发送一次 `$page_view`。
|
|
4
|
+
*
|
|
5
|
+
* @param options - SDK 初始化配置
|
|
6
|
+
* @returns 初始化完成后的 Promise
|
|
7
|
+
*/
|
|
8
|
+
export declare const init: (options: TrackingInitOptions) => Promise<void>;
|
|
9
|
+
/**
|
|
10
|
+
* 上报自定义事件。
|
|
11
|
+
*
|
|
12
|
+
* @param eventName - 事件名
|
|
13
|
+
* @param options - 事件上下文与业务属性
|
|
14
|
+
* @returns 返回本次上报的传输结果
|
|
15
|
+
*/
|
|
16
|
+
export declare const track: (eventName: string, options?: TrackOptions) => Promise<TrackingDispatchResult>;
|
|
17
|
+
/**
|
|
18
|
+
* 识别当前登录用户,仅影响后续事件。
|
|
19
|
+
*
|
|
20
|
+
* @param userId - 业务系统用户标识,传 `null` 可清除当前识别结果
|
|
21
|
+
*/
|
|
22
|
+
export declare const identify: (userId: string | null) => void;
|
|
23
|
+
/**
|
|
24
|
+
* 主动发送 `$page_view` 事件。
|
|
25
|
+
*
|
|
26
|
+
* @param options - 页面上下文与附加属性
|
|
27
|
+
* @returns 返回本次上报的传输结果
|
|
28
|
+
*/
|
|
29
|
+
export declare const page: (options?: PageOptions) => Promise<TrackingDispatchResult>;
|
|
30
|
+
/**
|
|
31
|
+
* 清空本地身份状态并生成新的匿名访客与会话。
|
|
32
|
+
*
|
|
33
|
+
* @returns 返回重置后的身份快照
|
|
34
|
+
*/
|
|
35
|
+
export declare const reset: () => TrackingStateSnapshot;
|
|
36
|
+
/**
|
|
37
|
+
* 仅用于调试或测试,返回当前内存态。
|
|
38
|
+
*
|
|
39
|
+
* @returns 返回 SDK 当前状态快照
|
|
40
|
+
*/
|
|
41
|
+
export declare const getDebugState: () => TrackingStateSnapshot;
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export type TrackingScalar = string | number | boolean | null;
|
|
2
|
+
export type TrackingProperties = Record<string, TrackingScalar>;
|
|
3
|
+
export interface TrackingInitOptions {
|
|
4
|
+
writeKey: string;
|
|
5
|
+
endpoint: string;
|
|
6
|
+
autoPageview?: boolean;
|
|
7
|
+
sessionTimeoutMinutes?: number;
|
|
8
|
+
storagePrefix?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface PageOptions {
|
|
11
|
+
path?: string;
|
|
12
|
+
url?: string;
|
|
13
|
+
title?: string;
|
|
14
|
+
referrer?: string;
|
|
15
|
+
properties?: TrackingProperties;
|
|
16
|
+
}
|
|
17
|
+
export interface TrackOptions {
|
|
18
|
+
eventTime?: number;
|
|
19
|
+
properties?: TrackingProperties;
|
|
20
|
+
page?: PageOptions;
|
|
21
|
+
}
|
|
22
|
+
export interface TrackingEventPayload {
|
|
23
|
+
event_name: string;
|
|
24
|
+
event_time?: number;
|
|
25
|
+
anonymous_id: string;
|
|
26
|
+
session_id: string;
|
|
27
|
+
user_id?: string;
|
|
28
|
+
page_url?: string;
|
|
29
|
+
page_path?: string;
|
|
30
|
+
page_title?: string;
|
|
31
|
+
referrer?: string;
|
|
32
|
+
utm_source?: string;
|
|
33
|
+
utm_medium?: string;
|
|
34
|
+
utm_campaign?: string;
|
|
35
|
+
properties: TrackingProperties;
|
|
36
|
+
}
|
|
37
|
+
export interface TrackingCollectPayload {
|
|
38
|
+
write_key: string;
|
|
39
|
+
events: TrackingEventPayload[];
|
|
40
|
+
}
|
|
41
|
+
export interface TrackingResponse {
|
|
42
|
+
request_id?: string;
|
|
43
|
+
accepted?: number;
|
|
44
|
+
rejected?: number;
|
|
45
|
+
server_time?: number;
|
|
46
|
+
}
|
|
47
|
+
export interface TrackingDispatchResult {
|
|
48
|
+
transport: "beacon" | "fetch";
|
|
49
|
+
response?: TrackingResponse;
|
|
50
|
+
}
|
|
51
|
+
export interface TrackingStateSnapshot {
|
|
52
|
+
anonymousId: string;
|
|
53
|
+
sessionId: string;
|
|
54
|
+
userId: string | null;
|
|
55
|
+
lastActivityAt: number;
|
|
56
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "rsclick-log-sdk-web",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "Lightweight Web tracking SDK for rs-click-log.",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"module": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./browser": {
|
|
17
|
+
"default": "./dist/index.global.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"access": "public",
|
|
22
|
+
"registry": "https://registry.npmjs.org/"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist",
|
|
26
|
+
"README.md"
|
|
27
|
+
],
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "bun run build:esm && bun run build:browser && bun run build:types",
|
|
30
|
+
"build:esm": "bun build --outdir=./dist --format=esm --target=browser ./src/index.ts",
|
|
31
|
+
"build:browser": "bun build --outfile=./dist/index.global.js --format=iife --target=browser ./src/browser.ts",
|
|
32
|
+
"build:types": "npx -p typescript tsc -p tsconfig.build.json",
|
|
33
|
+
"test": "bun test",
|
|
34
|
+
"typecheck": "npx -p typescript tsc --noEmit -p tsconfig.json"
|
|
35
|
+
}
|
|
36
|
+
}
|