内容
# 本地优先工具包
一个本地优先的Web应用参考栈。待办事项列表原型是架构决策的试验场 —— 目标是为尊重隐私、支持AI的本地优先应用提供可重用的基础。
## 技术栈
| 层 | 选择 |
|-------|--------|
| UI | SvelteKit(Svelte 5 符文) |
| 样式 | UnoCSS + BitsUI |
| 数据 | TinyBase 8 `MergeableStore` |
| 身份验证 | 通过 `@oslojs/*` 原语的Passkeys(无身份验证框架) |
| 基础设施 | Cloudflare Workers + Durable Objects 通过Alchemy |
## 架构
数据存储在设备上的TinyBase `MergeableStore`中,持久化到OPFS(本地存储备用)。每个空间的Durable Object作为同步对等体 —— 不是守卫。CRDT在重新连接时合并来自所有源(浏览器、API、代理)的更改。
身份验证数据(用户、刷新令牌、API令牌)存储在D1中。应用数据存储在TinyBase中。短时JWT连接它们。
```
浏览器(TinyBase MergeableStore + OPFS)
│ WebSocket同步(CRDT)
▼
Durable Object(每个空间 — TinyBase SQLite)
│ 内部获取
▼
SvelteKit Worker
├── /sync/[spaceId] WebSocket代理
├── /api/todos REST API(会话cookie或Bearer令牌)
├── /mcp AI代理的MCP服务器
├── /settings API令牌管理
└── /auth/* Passkey身份验证 + JWT颁发
```
## 软件包
```
packages/auth/ 无状态加密原语(JWT、Passkeys、密码哈希)
packages/config/ APP_ID 和派生的存储密钥常量
packages/data/ TinyBase存储工厂、浏览器持久性、同步Durable Object
packages/domain/ DEMO DOMAIN:待办事项模式、操作、访问控制 — 替换此
packages/infra/ Alchemy IaC — D1、DO、机密、迁移
apps/web/ SvelteKit应用
```
## 入门指南
```bash
npm install
```
复制 `packages/infra/.env.example` 到 `packages/infra/.env`:
```bash
cp packages/infra/.env.example packages/infra/.env
```
- `JWT_SECRET` — 如果留空,则在第一次 `npm run dev` 时自动生成。对于预发布/生产环境,请将其设置为Cloudflare机密。
- `ALCHEMY_PASSWORD` — 加密Alchemy的本地状态文件。任何值都适用于本地开发。
身份验证开箱即用,支持Passkeys。
```bash
npm run dev # Alchemy + Vite — 热模块替换,测试阶段绑定(无本地Durable Object)
npm run dev:workers # 完整应用 + Durable Object在workerd运行时(同步有效;无HMR)
npm run check-types # TypeScript检查(所有软件包)
npm test --workspace @local-first-kit/domain # 领域单元测试
```
开发服务器URL在启动时打印到终端。
### 两种开发模式
由于SvelteKit尚不能在 `vite dev` 中运行Durable Object(`@cloudflare/vite-plugin` 无SvelteKit支持 — 参见 [sveltejs/kit#15627](https://github.com/sveltejs/kit/pull/15627),受阻于SvelteKit 3):
- **`npm run dev`** — Alchemy + Vite,带有热模块替换。适用于UI工作。`SYNC_DO` 命名空间已绑定,但DO **不**在此运行(同步保持断开)。
- **`npm run dev:workers`** — 构建SvelteKit worker,并在真实的 `workerd` 运行时通过 `wrangler dev` 运行(处理程序 **+** `WsServerDurableObject`),在 `http://localhost:8787` 提供服务。TinyBase同步、DO的WebSocket和本地DO RPC(`/api/*`、`/mcp`)在本地运行,与生产环境完全相同。无Vite HMR — 应用代码更改后重新运行(或在第二个终端中运行 `vite build --watch`)。
`dev:workers` 的首次设置:
```bash
npm run dev # 一次:生成.alchemy/local/wrangler.jsonc(启动后Ctrl-C)
npm run db:local --workspace web # 一次:将D1迁移应用于本地状态,以便登录有效
```
(`dev:workers`内部的 `vite build` 使用SvelteKit适配器的Alchemy配置,该配置由 `npm run dev` 或任何部署创建 — 如果缺少该配置,您将得到一个清晰的“运行alchemy dev或alchemy deploy”的错误提示。)
此模式的本地配置位于 `apps/web/wrangler.dev.jsonc`;它仅镜像由 `packages/infra/alchemy.run.ts` 拥有的绑定,并在本地状态(`.wrangler/state`)上运行,而不运行真正的Cloudflare资源。
## 适应您的领域
`packages/domain/` 是演示(待办事项);其他部分是您保留的基础设施。要使其成为您的应用:
1. **设置 `APP_ID`** 在 `packages/config/src/index.ts` 中 — **在首次部署之前**。存储密钥、OPFS文件前缀和JWT颁发者都源自它,并且在用户拥有数据后不得更改。
2. **替换 `packages/domain/`** — 您的表模式(`schema/`)、操作(`operations/`)和访问控制(`access.ts`:`WRITE_TABLES_BY_ROLE`、`ENCRYPTED_FIELDS_BY_TABLE`)。
3. **将应用指向您的模式** — 在 `apps/web/src/lib/local/appStore.ts` 中将其传递给 `createAppStore()`。
4. **更新代理接口** — `apps/web/src/routes/api/todos/`(REST)和 `apps/web/src/routes/mcp/+server.ts` 中的工具。
协作层(`lib/server/collab/`、`routes/api/spaces/`、`routes/api/invites/`)与领域无关 — 保留它以支持多用户空间,或删除它以支持单用户应用。`AGENTS.md` 提供了完整的逐步指南。
## 部署
```bash
npm run deploy:staging # 部署到预发布环境(单独的D1/DO实例)
npm run deploy:prod # 部署到生产环境
npm run destroy:staging # 拆除预发布环境资源
npm run destroy:prod # 拆除生产环境资源(需要CONFIRM_DESTROY=production)
```
## AI代理访问
应用在 `/mcp` 处公开MCP服务器,在 `/api/todos` 处公开REST API。 在 `/settings` 处生成API令牌,然后:
```bash
# REST
curl -H "Authorization: Bearer <token>" <dev-url>/api/todos
# MCP
curl -X POST -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"tools/list","id":1}' <dev-url>/mcp
```