@flatbiz/device 2.0.0 → 2.0.1

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
@@ -1,6 +1,6 @@
1
1
  ## @flatbiz/device
2
2
 
3
- 增强 H5 设备信息采集能力:`deviceInfo` 支持自定义/自动获取 IP、屏幕分辨率字符串,并合并 UA 解析结果;补充 Vitest 单测与 Vite 本地演示。
3
+ 增强 H5 设备信息采集能力:`deviceInfo` 支持自定义/自动获取 IP、屏幕分辨率字符串,并合并 UA 解析结果;`deviceId` 通过 cookie + localStorage 持久化,并支持主域与子域共享。
4
4
 
5
5
  ### Breaking Changes
6
6
 
@@ -16,7 +16,8 @@
16
16
  - 新增返回字段 **`ip`**、**`screenSize`**(格式 `{width}x{height}`,如 `390x844`)
17
17
  - 非浏览器环境(`typeof window === 'undefined'`)安全返回 `{}`,避免 SSR/Node 直接调用抛错
18
18
  - 新增 helpers:`getIp`、`getWindowSize`;`getDeviceId` 独立为 `helper-get-deviceId`
19
- - 开发体验:补充 Vitest 单元测试;`yarn demo`(Vite)可本地预览 `public/index.html`
19
+ - **`deviceId` 持久化**:基于 `js-cookie` 写入可跨子域共享的 cookie,并与 localStorage 双向同步
20
+ - 开发体验:补充 Vitest 单元测试(含 jsdom 主域/子域 cookie 共享用例);`yarn demo`(Vite)可本地预览 `public/index.html`
20
21
 
21
22
  ### `deviceInfo()` 返回字段
22
23
 
@@ -34,6 +35,55 @@
34
35
  - `osName`、`osVersion`、`cpuModel`
35
36
  - `deviceType`、`deviceVendor`、`deviceModel`
36
37
 
38
+ ### deviceId 与 Cookie
39
+
40
+ `deviceInfo()` 内部会调用 `getDeviceId()` 生成/读取设备 ID。存储与同步规则如下。
41
+
42
+ #### 存储键
43
+
44
+ | 介质 | Key | 说明 |
45
+ | --- | --- | --- |
46
+ | Cookie | `flatbiz_device_ftbzdid` | 优先读取;用于跨子域共享 |
47
+ | localStorage | `flatbiz_device_ftbzdid` | Cookie 缺失时的回退;Cookie 有值时会被覆盖 |
48
+
49
+ #### 读取优先级
50
+
51
+ 1. **Cookie 有值**:返回该 ID,并写入/覆盖 localStorage
52
+ 2. **Cookie 无、localStorage 有**:返回 localStorage 中的 ID,并回写 Cookie
53
+ 3. **都没有**:生成新 ID(16 位类 UUID),同时写入 Cookie 与 localStorage
54
+
55
+ #### Cookie 属性
56
+
57
+ 通过 `js-cookie` 写入,默认属性:
58
+
59
+ | 属性 | 值 | 说明 |
60
+ | --- | --- | --- |
61
+ | `expires` | `1`(约 1 天) | 过期后续访问会重新走生成/回写逻辑 |
62
+ | `path` | `/` | 整站路径可访问 |
63
+ | `sameSite` | `Lax` | 常规同站导航可携带 |
64
+ | `domain` | 见下 | 主域与子域共享 |
65
+
66
+ #### 跨子域共享(`domain`)
67
+
68
+ 使用 [psl](https://www.npmjs.com/package/psl) 解析当前 `location.hostname` 的注册域,并设置为带前缀点的根域,例如:
69
+
70
+ - `example.com` → `domain=.example.com`
71
+ - `www.example.com` → `domain=.example.com`
72
+ - `api.example.com` → `domain=.example.com`
73
+
74
+ 因此在同一注册域下,主域与各子域可共享同一个 `deviceId`(localStorage 仍按源隔离,跨域依赖 Cookie)。
75
+
76
+ 以下场景**不设置** `domain`(仅当前 host 生效):
77
+
78
+ - `localhost`
79
+ - IP 地址(如 `127.0.0.1`)
80
+ - 无法解析出注册域的 hostname
81
+
82
+ #### 注意事项
83
+
84
+ - 浏览器禁用 Cookie 时,仍可能依赖 localStorage;返回字段 `cookiesEnable` 可反映 Cookie 是否启用
85
+ - Cookie 约 24 小时过期后,若 localStorage 仍有值,会按优先级回写新 Cookie,**ID 保持不变**
86
+
37
87
  ### Usage
38
88
 
39
89
  ```ts
@@ -47,4 +97,7 @@ const info2 = await deviceInfo({ autoGetIp: true });
47
97
 
48
98
  // 调试模式
49
99
  const info3 = await deviceInfo({ isDebug: true });
100
+
101
+ // deviceId 已持久化在 cookie / localStorage
102
+ console.log(info.deviceId);
50
103
  ```
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * 获取设备 ID
3
- * @returns 设备 ID
4
- * - 如果设备 ID 不存在,则生成一个设备 ID 并返回
5
- * - 如果设备 ID 存在,则返回设备 ID
3
+ * 优先级:cookie > localStorage > 新生成
4
+ * - cookie 有值:返回并覆盖 localStorage
5
+ * - cookie 无、localStorage 有:返回并回写 cookie(含子域名可共享的 domain)
6
+ * - 都没有:生成新 ID,同时写入 cookie 与 localStorage
6
7
  */
7
8
  export declare const getDeviceId: () => string;
@@ -1,4 +1,8 @@
1
+ import Cookies from 'js-cookie';
2
+ import psl from 'psl';
1
3
  const storageKey = 'flatbiz_device';
4
+ const cacheKey = 'ftbzdid';
5
+ const cookieName = `${storageKey}_${cacheKey}`;
2
6
  const localStorageCache = {
3
7
  set: (key, value) => {
4
8
  localStorage.setItem(`${storageKey}_${key}`, value);
@@ -16,19 +20,46 @@ const getUuid = () => {
16
20
  return v.toString(16);
17
21
  });
18
22
  };
23
+ /** 获取可用于子域名共享的 cookie domain(如 .example.com) */
24
+ const getCookieDomain = () => {
25
+ const hostname = location.hostname;
26
+ if (!hostname ||
27
+ hostname === 'localhost' ||
28
+ /^\d{1,3}(\.\d{1,3}){3}$/.test(hostname)) {
29
+ return undefined;
30
+ }
31
+ const domain = psl.get(hostname);
32
+ return domain ? `.${domain}` : undefined;
33
+ };
34
+ const setCookie = (name, value) => {
35
+ Cookies.set(name, value, {
36
+ // 约 24 小时过期
37
+ expires: 1,
38
+ path: '/',
39
+ sameSite: 'Lax',
40
+ domain: getCookieDomain(),
41
+ });
42
+ };
19
43
  /**
20
44
  * 获取设备 ID
21
- * @returns 设备 ID
22
- * - 如果设备 ID 不存在,则生成一个设备 ID 并返回
23
- * - 如果设备 ID 存在,则返回设备 ID
45
+ * 优先级:cookie > localStorage > 新生成
46
+ * - cookie 有值:返回并覆盖 localStorage
47
+ * - cookie 无、localStorage 有:返回并回写 cookie(含子域名可共享的 domain)
48
+ * - 都没有:生成新 ID,同时写入 cookie 与 localStorage
24
49
  */
25
50
  export const getDeviceId = () => {
26
- const cacheKey = 'ftbzdid';
27
- const deviceId = localStorageCache.get(cacheKey);
28
- if (!deviceId) {
29
- const uuid = getUuid();
30
- localStorageCache.set(cacheKey, uuid);
31
- return uuid;
51
+ const cookieDeviceId = Cookies.get(cookieName);
52
+ if (cookieDeviceId) {
53
+ localStorageCache.set(cacheKey, cookieDeviceId);
54
+ return cookieDeviceId;
55
+ }
56
+ const storageDeviceId = localStorageCache.get(cacheKey);
57
+ if (storageDeviceId) {
58
+ setCookie(cookieName, storageDeviceId);
59
+ return storageDeviceId;
32
60
  }
33
- return deviceId;
61
+ const uuid = getUuid();
62
+ setCookie(cookieName, uuid);
63
+ localStorageCache.set(cacheKey, uuid);
64
+ return uuid;
34
65
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flatbiz/device",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "homepage": "https://gitlab.itcjf.com/flatjs/flat-biz-devkit",
5
5
  "repository": {
6
6
  "type": "git",
@@ -37,13 +37,17 @@
37
37
  "dev": "vite"
38
38
  },
39
39
  "dependencies": {
40
+ "js-cookie": "3.0.8",
41
+ "psl": "1.15.0",
40
42
  "ua-parser-js": "2.0.9"
41
43
  },
42
44
  "devDependencies": {
43
45
  "@hyperse/eslint-config-hyperse": "^1.7.3",
46
+ "@types/js-cookie": "3.0.6",
44
47
  "@types/node": "25.8.0",
45
48
  "eslint": "10.4.0",
46
49
  "happy-dom": "17.4.6",
50
+ "jsdom": "26.1.0",
47
51
  "npm-run-all": "4.1.5",
48
52
  "typescript": "6.0.3",
49
53
  "vite": "6.3.5",