bff-store 0.1.0 → 0.1.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/.claude/settings.local.json +10 -1
- package/README.md +37 -8
- package/dist/index.d.mts +42 -1
- package/dist/index.d.ts +42 -1
- package/dist/index.mjs +43 -0
- package/dist/package.json +2 -5
- package/dist/server/entry.js +31970 -3
- package/dist/server/entry.mjs +32005 -4
- package/dist/storage/mongodb-entry.js +31978 -1
- package/dist/storage/mongodb-entry.mjs +32003 -2
- package/docs/BUG_FIX_MONGODB_CHILD_PROCESS.md +129 -0
- package/docs/FRONTEND_BACKEND_ISOLATION.md +150 -0
- package/docs/LOCAL_LINK_STRATEGIES.md +117 -0
- package/package.json +6 -6
- package/src/environment.ts +11 -0
- package/src/index.ts +7 -0
- package/src/nodeStore.ts +79 -0
- package/tsconfig.json +1 -0
- package/docs/BUG_DIAGNOSIS_REMOTE_STORAGE_OPTIONS.md +0 -104
- package/docs/BUG_FIX_REMOTE_STORAGE_OPTIONS.md +0 -63
- package/docs/BUG_FIX_SESSION_2026-06-03.md +0 -171
- /package/docs/{SIDECAR_SERVER.md → BFF_SIDECAR_SERVER.md} +0 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# BUG: child_process / mongodb 错误
|
|
2
|
+
|
|
3
|
+
**日期**: 2026-06-03
|
|
4
|
+
|
|
5
|
+
## 问题现象
|
|
6
|
+
|
|
7
|
+
Next.js 构建时报错:
|
|
8
|
+
```
|
|
9
|
+
Module not found: Can't resolve 'child_process'
|
|
10
|
+
./tests/next_test/node_modules/mongodb/lib/client-side-encryption/mongocryptd_manager.js:38
|
|
11
|
+
const { spawn } = require('child_process');
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## 根因分析
|
|
15
|
+
|
|
16
|
+
**不是本次改动引起的,是之前就存在的依赖架构问题。**
|
|
17
|
+
|
|
18
|
+
### 1. bff-store 的 `package.json` 把 mongodb 放在 `dependencies`
|
|
19
|
+
|
|
20
|
+
```json
|
|
21
|
+
// bff-store/package.json
|
|
22
|
+
{
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"mongodb": "^6.21.0"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### 2. next_test 通过 `file:` 路径安装 bff-store
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
// next_test/package.json
|
|
33
|
+
{
|
|
34
|
+
"bff-store": "file:../../dist"
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### 3. npm install 时把 mongodb 也装进了 next_test/node_modules
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
next_test/node_modules/mongodb/ ← 被安装了
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 4. Next.js/Webpack 在静态分析时解析到了 mongodb
|
|
45
|
+
|
|
46
|
+
即使 `dist/index.mjs` 实际不引用 mongodb(`tsup external: ['mongodb']`),但 Next.js 的 bundler 在分析依赖时看到了 `node_modules/mongodb`,尝试解析它,然后触发了 `child_process` 的引用。
|
|
47
|
+
|
|
48
|
+
## 验证
|
|
49
|
+
|
|
50
|
+
- `dist/index.mjs` 实际 bundle:干净(0 个 mongodb 引用)
|
|
51
|
+
- tsup 的 external 配置:正确(mongodb 被 external)
|
|
52
|
+
- 清理 next_test 的 `node_modules` 和 `package-lock.json` 后重装,mongodb 没再被安装,构建成功
|
|
53
|
+
|
|
54
|
+
## 结论
|
|
55
|
+
|
|
56
|
+
| 环节 | 状态 |
|
|
57
|
+
|------|------|
|
|
58
|
+
| 本次新增代码(environment.ts、nodeStore.ts)| 无问题 |
|
|
59
|
+
| `dist/index.mjs` 实际 bundle | 干净(0 个 mongodb 引用)|
|
|
60
|
+
| tsup 的 external 配置 | 正确(mongodb 被 external)|
|
|
61
|
+
| **根本原因** | `package.json` 的 `dependencies` 里有 mongodb |
|
|
62
|
+
|
|
63
|
+
这是之前就存在的问题,只是以前 next_test 可能没走到触发这个解析的代码路径。
|
|
64
|
+
|
|
65
|
+
## 修复(2026-06-03)
|
|
66
|
+
|
|
67
|
+
已将 `mongodb` 从 `package.json` 的 `dependencies` 移到 `devDependencies`。
|
|
68
|
+
|
|
69
|
+
**原因**:
|
|
70
|
+
- `tsup` 构建 `mongodb-entry.ts` 时会将 mongodb 打包进 `dist/storage/mongodb-entry.js`
|
|
71
|
+
- 根 `package.json` 的 `dependencies` 不需要 mongodb
|
|
72
|
+
- npm install 时不会为消费者安装 devDependencies,所以 next_test 不再被强制安装 mongodb
|
|
73
|
+
|
|
74
|
+
**修复后验证**:
|
|
75
|
+
- next_test 重新 `npm install` 后,`node_modules/mongodb` 不存在
|
|
76
|
+
- Next.js 构建成功,无 `child_process` 错误
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 问题二:Can't resolve 'bff-store'
|
|
81
|
+
|
|
82
|
+
**日期**: 2026-06-03
|
|
83
|
+
|
|
84
|
+
### 问题现象
|
|
85
|
+
|
|
86
|
+
Next.js dev 时报错:
|
|
87
|
+
```
|
|
88
|
+
Module not found: Can't resolve 'bff-store'
|
|
89
|
+
import { createStore, useStore, remoteStorage } from 'bff-store';
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 根因分析
|
|
93
|
+
|
|
94
|
+
`package.json` exports 的 `require` 字段指向不存在的文件:
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
"exports": {
|
|
98
|
+
".": {
|
|
99
|
+
"types": "./dist/index.d.ts",
|
|
100
|
+
"import": "./dist/index.mjs",
|
|
101
|
+
"require": "./dist/index.js" // ← 这个文件不存在!
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
tsup 只生成 ESM(`.mjs`),没有生成 CJS(`.js`)。但 exports 里写了 `require` 字段指向 `index.js`,Node.js CJS resolver 尝试解析时找不到文件就报错了。
|
|
107
|
+
|
|
108
|
+
### 修复
|
|
109
|
+
|
|
110
|
+
移除 exports 中的 `require` 字段(ESM-only 库):
|
|
111
|
+
|
|
112
|
+
```json
|
|
113
|
+
"exports": {
|
|
114
|
+
".": {
|
|
115
|
+
"types": "./dist/index.d.ts",
|
|
116
|
+
"import": "./dist/index.mjs"
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### ESM-only 的影响
|
|
122
|
+
|
|
123
|
+
| 方式 | 支持 |
|
|
124
|
+
|------|------|
|
|
125
|
+
| `import from 'bff-store'` | ✅ 正常 |
|
|
126
|
+
| `node --input-type=module` + import | ✅ 正常 |
|
|
127
|
+
| `const x = require('bff-store')` | ❌ 报错 |
|
|
128
|
+
|
|
129
|
+
bff-store 主打 React + 新项目,用 ESM 语法不受影响。老项目用 `require` 的场景暂不支持。
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# 前端/后端隔离机制
|
|
2
|
+
|
|
3
|
+
## 概述
|
|
4
|
+
|
|
5
|
+
bff-store 通过构建配置和 package.json exports 条件导出实现前端/后端隔离,确保 Node.js 特有模块(fs、mongodb)不会进入前端 bundle。
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. 构建层面隔离(tsup)
|
|
10
|
+
|
|
11
|
+
tsup.config.ts 定义了 5 个独立的构建入口:
|
|
12
|
+
|
|
13
|
+
| 入口 | 输出 | external |
|
|
14
|
+
|------|------|----------|
|
|
15
|
+
| `src/index.ts` | `dist/index.mjs` | `react, jotai, mongodb` |
|
|
16
|
+
| `src/storage/jsonl-entry.ts` | `dist/storage/jsonl-entry.mjs` | `react, jotai` |
|
|
17
|
+
| `src/storage/mongodb-entry.ts` | `dist/storage/mongodb-entry.mjs` | `react, jotai` |
|
|
18
|
+
| `src/server/entry.ts` | `dist/server/entry.mjs` | `react, jotai` |
|
|
19
|
+
| `src/server/cli.ts` | `dist/cli.js` | `react, jotai` + noExternal: mongodb |
|
|
20
|
+
|
|
21
|
+
**关键**:主入口的 tsup external 里标了 `mongodb`,所以 `dist/index.mjs` 不会打包 mongodb。fs 同理。
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 2. package.json 条件导出
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"import": "./dist/index.mjs",
|
|
32
|
+
"require": "./dist/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./jsonl": {
|
|
35
|
+
"types": "./dist/storage/jsonl-entry.d.ts",
|
|
36
|
+
"import": "./dist/storage/jsonl-entry.mjs",
|
|
37
|
+
"require": "./dist/storage/jsonl-entry.js"
|
|
38
|
+
},
|
|
39
|
+
"./mongodb": {
|
|
40
|
+
"types": "./dist/storage/mongodb-entry.d.ts",
|
|
41
|
+
"import": "./dist/storage/mongodb-entry.mjs",
|
|
42
|
+
"require": "./dist/storage/mongodb-entry.js"
|
|
43
|
+
},
|
|
44
|
+
"./server": {
|
|
45
|
+
"types": "./dist/server/entry.d.ts",
|
|
46
|
+
"import": "./dist/server/entry.mjs",
|
|
47
|
+
"require": "./dist/server/entry.js"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
用户不主动 `import from 'bff-store/jsonl'` 或 `bff-store/mongodb'`,就拿不到包含 fs/mongodb 的模块。
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## 3. 主入口 index.ts 干净
|
|
57
|
+
|
|
58
|
+
`src/index.ts` 只导出前端安全的模块:
|
|
59
|
+
|
|
60
|
+
- `createStore`, `useStore`, `createPersistedAtom`
|
|
61
|
+
- `memoryStorage`, `remoteStorage`
|
|
62
|
+
- `isNode`, `isBrowser`, `createNodeStore`
|
|
63
|
+
|
|
64
|
+
**没有**引用 `jsonl.ts` 或 `mongodb.ts`。
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## 4. 引用 fs/mongodb 的文件
|
|
69
|
+
|
|
70
|
+
| 文件 | 引用 |
|
|
71
|
+
|------|------|
|
|
72
|
+
| `src/storage/jsonl.ts` | `import * as fs from 'fs'` |
|
|
73
|
+
| `src/storage/mongodb.ts` | `import { MongoClient } from 'mongodb'` |
|
|
74
|
+
| `src/server/index.ts` | `import { mongodbStorage } from '../storage/mongodb'` |
|
|
75
|
+
|
|
76
|
+
这些文件**只会被** `jsonl-entry.ts`、`mongodb-entry.ts`、`server/entry.ts` 引用,不会被主入口 `index.ts` 引用。
|
|
77
|
+
|
|
78
|
+
### 为什么 index.ts 不引用 fs/mongodb 很重要?
|
|
79
|
+
|
|
80
|
+
**不是"空间换时间",是避免运行时错误。**
|
|
81
|
+
|
|
82
|
+
tsup 的 `external` 配置是保险,但 `import` 语句本身就会触发模块解析:
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
// index.ts 如果这样写
|
|
86
|
+
import * as fs from 'fs'; // ← 加了这行
|
|
87
|
+
export { createStore } from './createStore';
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
1. tsup external 会让 fs 代码不打包进 bundle
|
|
91
|
+
2. 但 bundler(webpack/rollup)看到 `import` 语句仍会尝试解析
|
|
92
|
+
3. Next.js 环境有 node_modules/fs → 解析成功,但运行时会出问题
|
|
93
|
+
4. 纯浏览器环境没有 fs → 运行时直接报错
|
|
94
|
+
|
|
95
|
+
**不让源头存在,比靠 external 兜底更安全。**
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## 5. createNodeStore 的安全性
|
|
100
|
+
|
|
101
|
+
`createNodeStore` 在 `src/nodeStore.ts` 中实现,它的 import:
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
import { getDefaultStore } from 'jotai';
|
|
105
|
+
import { createStore } from './createStore';
|
|
106
|
+
import type { AtomConfigs, StoreAtoms, StoreLoadingAtoms } from './types';
|
|
107
|
+
import type { StorageAdapter } from './storage/base';
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**没有**引用 fs 或 mongodb,所以即使在 index.ts 中导出,前端也不会被打包进去。
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## 6. 之前 mongodb 进 next_test 的原因
|
|
115
|
+
|
|
116
|
+
不是代码问题,是 **package.json dependencies** 的问题。
|
|
117
|
+
|
|
118
|
+
```json
|
|
119
|
+
// 原先的 package.json
|
|
120
|
+
{
|
|
121
|
+
"dependencies": {
|
|
122
|
+
"mongodb": "^6.21.0" // ← 问题在这里
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
next_test 通过 `file:../../dist` 安装 bff-store 时,npm install 会安装 `dependencies` 中的包,导致 mongodb 被装进 next_test/node_modules,即使用不到也会被 Next.js bundler 解析到。
|
|
128
|
+
|
|
129
|
+
**修复**:将 mongodb 移到 `devDependencies`,因为 tsup 构建时会把它打包进 `dist/storage/mongodb-entry.js`,不需要作为独立依赖安装。
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## 验证方法
|
|
134
|
+
|
|
135
|
+
### 检查主入口是否干净
|
|
136
|
+
```bash
|
|
137
|
+
grep -c "mongodb" dist/index.mjs # 应为 0
|
|
138
|
+
grep -c "fs" dist/index.mjs # 应为 0
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### 检查子入口是否包含目标模块
|
|
142
|
+
```bash
|
|
143
|
+
grep -c "mongodb" dist/storage/mongodb-entry.mjs # 应 > 0
|
|
144
|
+
grep -c "fs" dist/storage/jsonl-entry.mjs # 应 > 0
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### 检查 next_test 是否被安装 mongodb
|
|
148
|
+
```bash
|
|
149
|
+
ls node_modules/mongodb # 应不存在
|
|
150
|
+
```
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# 本地包链接策略
|
|
2
|
+
|
|
3
|
+
## 背景
|
|
4
|
+
|
|
5
|
+
开发 bff-store 时,需要在项目中测试自己的包。常见的链接方式有三种。
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 方式一:`file:` 路径(当前使用)
|
|
10
|
+
|
|
11
|
+
### 配置
|
|
12
|
+
|
|
13
|
+
```json
|
|
14
|
+
// tests/next_test/package.json
|
|
15
|
+
{
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"bff-store": "file:../../dist"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
npm install 后会创建 symlink:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
tests/next_test/node_modules/bff-store -> bff-store/dist
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### 优点
|
|
29
|
+
- 简单直接,改完 `npm run build` 就生效
|
|
30
|
+
- 无需额外命令
|
|
31
|
+
|
|
32
|
+
### 缺点
|
|
33
|
+
- 不是真实 npm 安装行为,可能有边界情况
|
|
34
|
+
- 路径固定,不够灵活
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## 方式二:`npm link`(推荐本地开发)
|
|
39
|
+
|
|
40
|
+
### 配置
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# 1. 在 bff-store 目录注册全局链接
|
|
44
|
+
cd bff-store
|
|
45
|
+
npm link
|
|
46
|
+
|
|
47
|
+
# 2. 在目标项目链接全局包
|
|
48
|
+
cd tests/next_test
|
|
49
|
+
npm link bff-store
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 原理
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
bff-store/ (全局注册的包)
|
|
56
|
+
↓
|
|
57
|
+
tests/next_test/node_modules/bff-store -> 全局包的 symlink
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### 优点
|
|
61
|
+
- 更接近真实 npm 安装行为
|
|
62
|
+
- 全局一份,多个项目共用
|
|
63
|
+
- 可同时开发多个相互依赖的包
|
|
64
|
+
|
|
65
|
+
### 缺点
|
|
66
|
+
- 需要两条命令
|
|
67
|
+
- 全局状态管理可能混乱
|
|
68
|
+
|
|
69
|
+
### 清理
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
cd tests/next_test
|
|
73
|
+
npm unlink bff-store
|
|
74
|
+
|
|
75
|
+
cd bff-store
|
|
76
|
+
npm link remove bff-store
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## 方式三:发布到 npm(正式发布)
|
|
82
|
+
|
|
83
|
+
### 配置
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
# 1. 登录 npm
|
|
87
|
+
npm login
|
|
88
|
+
|
|
89
|
+
# 2. 发布(需要 package.json 的 name 是唯一的)
|
|
90
|
+
npm publish
|
|
91
|
+
|
|
92
|
+
# 3. 在项目中使用
|
|
93
|
+
npm install bff-store
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### 优点
|
|
97
|
+
- 真实用户安装方式
|
|
98
|
+
- 版本管理规范
|
|
99
|
+
|
|
100
|
+
### 缺点
|
|
101
|
+
- 需要 npm 账号
|
|
102
|
+
- 版本管理,每次改动都要发版
|
|
103
|
+
- 发布后不能即时生效
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 总结
|
|
108
|
+
|
|
109
|
+
| 方式 | 即时生效 | 配置复杂度 | 接近真实安装 |
|
|
110
|
+
|------|---------|-----------|------------|
|
|
111
|
+
| `file:` | ✅ | 简单 | 一般 |
|
|
112
|
+
| `npm link` | ✅ | 中等 | ✅ |
|
|
113
|
+
| `npm publish` | ❌ | 需要版本管理 | ✅ |
|
|
114
|
+
|
|
115
|
+
**本地开发推荐**:`npm link` 或保持 `file:`
|
|
116
|
+
|
|
117
|
+
**正式发布**:用 `npm publish`
|
package/package.json
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bff-store",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "A jotai-based state management library with pluggable storage adapters",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
5
8
|
"main": "dist/index.js",
|
|
6
9
|
"module": "dist/index.mjs",
|
|
7
10
|
"types": "dist/index.d.ts",
|
|
8
11
|
"exports": {
|
|
9
12
|
".": {
|
|
10
13
|
"types": "./dist/index.d.ts",
|
|
11
|
-
"import": "./dist/index.mjs"
|
|
12
|
-
"require": "./dist/index.js"
|
|
14
|
+
"import": "./dist/index.mjs"
|
|
13
15
|
},
|
|
14
16
|
"./jsonl": {
|
|
15
17
|
"types": "./dist/storage/jsonl-entry.d.ts",
|
|
@@ -50,13 +52,11 @@
|
|
|
50
52
|
"@types/react": "^19.2.16",
|
|
51
53
|
"jotai": "^2.6.0",
|
|
52
54
|
"jsdom": "^29.1.1",
|
|
55
|
+
"mongodb": "^6.21.0",
|
|
53
56
|
"react": "^18.2.0",
|
|
54
57
|
"react-dom": "^18.2.0",
|
|
55
58
|
"tsup": "^8.0.0",
|
|
56
59
|
"typescript": "^5.0.0",
|
|
57
60
|
"vitest": "^1.0.0"
|
|
58
|
-
},
|
|
59
|
-
"dependencies": {
|
|
60
|
-
"mongodb": "^6.21.0"
|
|
61
61
|
}
|
|
62
62
|
}
|
package/src/index.ts
CHANGED
|
@@ -33,3 +33,10 @@ export { HttpTransport, createStorageFromTransport } from './storage/transport';
|
|
|
33
33
|
export { RestStorageProtocol, createStorageWithProtocol } from './storage/protocol';
|
|
34
34
|
export type { TransportAdapter } from './storage/transport';
|
|
35
35
|
export type { StorageHttpProtocol } from './storage/protocol';
|
|
36
|
+
|
|
37
|
+
// ========================================
|
|
38
|
+
// Node.js Utilities
|
|
39
|
+
// ========================================
|
|
40
|
+
|
|
41
|
+
export { isNode, isBrowser } from './environment';
|
|
42
|
+
export { createNodeStore } from './nodeStore';
|
package/src/nodeStore.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { getDefaultStore } from 'jotai';
|
|
2
|
+
import { createStore } from './createStore';
|
|
3
|
+
import type { AtomConfigs, StoreAtoms, StoreLoadingAtoms } from './types';
|
|
4
|
+
import type { StorageAdapter } from './storage/base';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Create a store for Node.js environments.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { jsonlStorage } from 'bff-store/jsonl';
|
|
12
|
+
* import { createNodeStore } from 'bff-store';
|
|
13
|
+
*
|
|
14
|
+
* const store = createNodeStore('entity-123', [
|
|
15
|
+
* { key: 'theme', defaultValue: 'dark' },
|
|
16
|
+
* ], {
|
|
17
|
+
* storage: jsonlStorage({ dir: './sessions' }),
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* await store.waitForLoad();
|
|
21
|
+
*
|
|
22
|
+
* const jotai = getDefaultStore();
|
|
23
|
+
* jotai.set(store.atoms.theme, 'light');
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export function createNodeStore(
|
|
27
|
+
entityId: string,
|
|
28
|
+
config: AtomConfigs,
|
|
29
|
+
options: {
|
|
30
|
+
storage: StorageAdapter;
|
|
31
|
+
debounceMs?: number;
|
|
32
|
+
}
|
|
33
|
+
): {
|
|
34
|
+
/** Atoms to use with getDefaultStore().get/set */
|
|
35
|
+
atoms: StoreAtoms;
|
|
36
|
+
/** Loading state atoms */
|
|
37
|
+
loadingAtoms: StoreLoadingAtoms;
|
|
38
|
+
/**
|
|
39
|
+
* Wait for all atoms to finish loading from storage.
|
|
40
|
+
* Call this after creating the store to ensure data is ready.
|
|
41
|
+
*/
|
|
42
|
+
waitForLoad(): Promise<void>;
|
|
43
|
+
} {
|
|
44
|
+
const store = createStore(entityId, config, options);
|
|
45
|
+
const jotaiStore = getDefaultStore();
|
|
46
|
+
|
|
47
|
+
async function waitForLoad(): Promise<void> {
|
|
48
|
+
// Trigger onMount for all atoms by subscribing to them
|
|
49
|
+
// In Node.js, sub() triggers onMount (unlike get() which doesn't)
|
|
50
|
+
for (const atom of Object.values(store.atoms)) {
|
|
51
|
+
jotaiStore.sub(atom, () => {});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Now wait for all loading atoms to become false
|
|
55
|
+
return new Promise((resolve) => {
|
|
56
|
+
const loadingAtoms = Object.values(store.loadingAtoms);
|
|
57
|
+
const stillLoading = () => loadingAtoms.some((atom) => jotaiStore.get(atom));
|
|
58
|
+
|
|
59
|
+
if (!stillLoading()) {
|
|
60
|
+
resolve();
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Poll until all are loaded
|
|
65
|
+
const interval = setInterval(() => {
|
|
66
|
+
if (!stillLoading()) {
|
|
67
|
+
clearInterval(interval);
|
|
68
|
+
resolve();
|
|
69
|
+
}
|
|
70
|
+
}, 10);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
atoms: store.atoms,
|
|
76
|
+
loadingAtoms: store.loadingAtoms,
|
|
77
|
+
waitForLoad,
|
|
78
|
+
};
|
|
79
|
+
}
|
package/tsconfig.json
CHANGED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
# Bug 诊断:`'backend' does not exist in type 'RemoteStorageOptions'`
|
|
2
|
-
|
|
3
|
-
## 现象
|
|
4
|
-
|
|
5
|
-
在 `tests/next_test` 中使用 `bff-store`(通过 `"file:../../dist"` 安装)时,TypeScript 报错:
|
|
6
|
-
|
|
7
|
-
```
|
|
8
|
-
Object literal may only specify known properties, and 'backend' does not exist in type 'RemoteStorageOptions'.ts(2353)
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
触发代码:
|
|
12
|
-
|
|
13
|
-
```typescript
|
|
14
|
-
import { remoteStorage } from 'bff-store';
|
|
15
|
-
|
|
16
|
-
const adapter = remoteStorage({
|
|
17
|
-
backend: 'jsonl',
|
|
18
|
-
jsonlDir: '/tmp/test',
|
|
19
|
-
});
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
## 根因分析
|
|
23
|
-
|
|
24
|
-
问题由**两个因素叠加**导致:
|
|
25
|
-
|
|
26
|
-
### 因素一(主因):`dist/package.json` 路径错误
|
|
27
|
-
|
|
28
|
-
构建脚本 `npm run build` 直接将项目根目录的 `package.json` 复制到 `dist/`。根目录的 `package.json` 中,`exports`、`main`、`module`、`types` 字段的路径都带有 `./dist/` 前缀:
|
|
29
|
-
|
|
30
|
-
```json
|
|
31
|
-
{
|
|
32
|
-
"main": "dist/index.js",
|
|
33
|
-
"module": "dist/index.mjs",
|
|
34
|
-
"types": "dist/index.d.ts",
|
|
35
|
-
"exports": {
|
|
36
|
-
".": {
|
|
37
|
-
"types": "./dist/index.d.ts",
|
|
38
|
-
"import": "./dist/index.mjs",
|
|
39
|
-
"require": "./dist/index.js"
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
这些路径在 npm 发布时是正确的(因为 `dist/` 是项目根目录的子目录)。但当通过 `"file:../../dist"` 本地安装时,**包根目录就是 `dist/` 本身**,`./dist/index.d.ts` 就会解析为 `dist/dist/index.d.ts` —— 该文件不存在。
|
|
46
|
-
|
|
47
|
-
| 场景 | 包根目录 | `./dist/index.d.ts` 解析结果 | 存在? |
|
|
48
|
-
|------|----------|---------------------------|------|
|
|
49
|
-
| npm 发布 | 项目根目录 | `项目根/dist/index.d.ts` | 是 |
|
|
50
|
-
| `file:../../dist` | `dist/` | `dist/dist/index.d.ts` | **否** |
|
|
51
|
-
|
|
52
|
-
因此 TypeScript 无法通过本地 symlink 解析到正确的类型声明文件。
|
|
53
|
-
|
|
54
|
-
### 因素二(助推):全局安装了旧版本
|
|
55
|
-
|
|
56
|
-
由于本地 symlink 解析失败,TypeScript 沿目录树向上查找,最终在 `/Users/Admin/node_modules/bff-store/` 找到了全局安装的 **v0.1.1**(当前项目版本为 v0.1.3)。
|
|
57
|
-
|
|
58
|
-
该旧版本的 `RemoteStorageOptions` 接口**没有** `backend` 等新增字段:
|
|
59
|
-
|
|
60
|
-
- v0.1.1(旧):
|
|
61
|
-
```typescript
|
|
62
|
-
interface RemoteStorageOptions {
|
|
63
|
-
baseUrl?: string;
|
|
64
|
-
entityId?: string;
|
|
65
|
-
transport?: TransportAdapter;
|
|
66
|
-
protocol?: StorageHttpProtocol;
|
|
67
|
-
}
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
- v0.1.3(当前源码/dist):
|
|
71
|
-
```typescript
|
|
72
|
-
interface RemoteStorageOptions {
|
|
73
|
-
baseUrl?: string;
|
|
74
|
-
entityId?: string;
|
|
75
|
-
transport?: TransportAdapter;
|
|
76
|
-
protocol?: StorageHttpProtocol;
|
|
77
|
-
backend?: 'mongodb' | 'jsonl'; // 新增
|
|
78
|
-
mongoUrl?: string; // 新增
|
|
79
|
-
mongoDb?: string; // 新增
|
|
80
|
-
jsonlDir?: string; // 新增
|
|
81
|
-
}
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
## 修复方案
|
|
85
|
-
|
|
86
|
-
新增 `scripts/adapt-dist-package.js`,在构建时将 `dist/package.json` 中的路径从"相对于项目根目录"改写为"相对于 dist/":
|
|
87
|
-
|
|
88
|
-
- `./dist/index.d.ts` → `./index.d.ts`
|
|
89
|
-
- `./dist/index.mjs` → `./index.mjs`
|
|
90
|
-
- `./dist/index.js` → `./index.js`
|
|
91
|
-
- `./dist/storage/...` → `./storage/...`
|
|
92
|
-
- `./dist/server/...` → `./server/...`
|
|
93
|
-
|
|
94
|
-
同时更新 `package.json` 的 `build` 脚本,在 `tsup` 和 `cp` 之间插入该脚本。
|
|
95
|
-
|
|
96
|
-
## 修复文件
|
|
97
|
-
|
|
98
|
-
- **新增**: `scripts/adapt-dist-package.js` — 路径改写脚本
|
|
99
|
-
- **修改**: `package.json` — build 脚本中调用 `node scripts/adapt-dist-package.js`
|
|
100
|
-
|
|
101
|
-
## 验证
|
|
102
|
-
|
|
103
|
-
- `npx tsc --noEmit` in `tests/next_test`:**0 errors**(修复前 4 errors)
|
|
104
|
-
- `npx vitest run`:68/69 通过(1 个 pre-existing failure,与本 bug 无关)
|