@aiao/rxdb-adapter-supabase 0.0.20 → 0.0.21

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,311 +1,297 @@
1
1
  # @aiao/rxdb-adapter-supabase
2
2
 
3
- RxDB Supabase 适配器 - 基于 PostgreSQL 的远程同步实现。
3
+ RxDB Supabase 适配器 基于 PostgreSQL 的远程同步实现。
4
+
5
+ 作为 RxDB 的 **remote** 适配器使用:本地用 SQLite(wa-sqlite / PGlite)离线读写,远程用 Supabase 做多端同步、实时推送与云端备份。
4
6
 
5
7
  ## 特性
6
8
 
7
- - ✅ **完整的 CRUD 操作** - 通过 Repository 模式封装 Supabase API
8
- - ✅ **批量操作** - `saveMany` / `removeMany` / `mutations` 高性能批处理
9
- - ✅ **事务支持** - 基于 PostgreSQL RPC 的原子性操作
10
- - ✅ **实时订阅** - Supabase Realtime 自动推送远程变更
11
- - ✅ **变更追踪** - 自动记录所有数据变更到 `RxDBChange`
12
- - ✅ **双向同步** - Push/Pull 机制确保本地与远程数据一致
13
- - ✅ **树形结构** - `SupabaseTreeRepository` 支持树形数据操作
9
+ - ✅ **完整 CRUD** 通过 `SupabaseRepository` 封装 PostgREST
10
+ - ✅ **批量操作** `saveMany` / `removeMany`(非事务)、`mutations`(RPC 事务)
11
+ - ✅ **事务支持** 基于 PostgreSQL RPC `rxdb_mutations` 的原子写入
12
+ - ✅ **实时订阅** 监听 `rxdb_change` 表的 INSERT,自动派发远程事件(带指数退避重连)
13
+ - ✅ **双向同步** `pullChanges` / `mergeChanges` / `pullChangesBatch` 配合 RxDB VersionManager
14
+ - ✅ **树形结构** `SupabaseTreeRepository`(递归 CTE,缺函数时降级到内存遍历)
15
+ - ✅ **RLS 自检** — 连接时校验目标表是否开启行级安全(可配 `warn` / `throw`)
16
+ - ✅ **瞬时错误重试** — 写入/事务路径对网关超时等瞬时错误自动重试
17
+
18
+ ## 何时使用
19
+
20
+ - 需要多端数据同步(Web / 移动端 / 桌面端)
21
+ - 需要实时协作
22
+ - 需要云端备份与恢复
23
+ - 需要 PostgreSQL 高级特性(JSONB、全文搜索、RLS)
24
+ - 已有 Supabase 基础设施
25
+
26
+ ## 与其他适配器对比
27
+
28
+ | 特性 | Supabase | PGlite | wa-sqlite |
29
+ | -------- | ----------- | ---------- | --------- |
30
+ | 部署模式 | 云端/自托管 | 本地 | 本地 |
31
+ | 多端同步 | ✅ | ❌ | ❌ |
32
+ | 实时订阅 | ✅ | ❌ | ❌ |
33
+ | 离线优先 | ✅(混合) | ✅ | ✅ |
34
+ | 数据库 | PostgreSQL | PostgreSQL | SQLite |
35
+ | 认证集成 | ✅ | ❌ | ❌ |
14
36
 
15
37
  ## 安装
16
38
 
17
39
  ```bash
18
- pnpm add @aiao/rxdb-adapter-supabase @supabase/supabase-js
40
+ pnpm add @aiao/rxdb @aiao/rxdb-adapter-supabase @supabase/supabase-js
41
+ # 本地适配器(二选一或按需)
42
+ pnpm add @aiao/rxdb-adapter-wa-sqlite
19
43
  ```
20
44
 
45
+ > peerDependencies:`@aiao/rxdb`、`@supabase/supabase-js` ^2.88、`rxjs` ^7.8。
46
+
47
+ ## 导出
48
+
49
+ - `RxDBAdapterSupabase` — 远程适配器主类
50
+ - `SupabaseRepository` — 表级 CRUD / 查询 Repository
51
+ - `SupabaseTreeRepository` — 树形(邻接表)Repository
52
+ - `SupabaseAdapterOptions` — 配置类型
53
+ - `SupabaseSyncError` / `SupabaseConfigError` / `SupabaseNetworkError` / `SupabaseDataError` — 错误类型
54
+ - `ADAPTER_NAME` — 常量 `'supabase'`
55
+
56
+ > ⚠️ 不存在 `createSupabaseAdapter()` / `createRxdb()` 这类工厂函数,请用下方的类 + `rxdb.adapter()` 注册方式。
57
+
21
58
  ## 快速开始
22
59
 
23
- ### 1. 配置 RxDB
60
+ ### 1. 配置 RxDB(本地 wa-sqlite + 远程 supabase)
24
61
 
25
62
  ```typescript
26
- import { RxDB } from '@aiao/rxdb';
63
+ import { RxDB, SyncType } from '@aiao/rxdb';
27
64
  import { RxDBAdapterSupabase } from '@aiao/rxdb-adapter-supabase';
28
- import { RxDBAdapterSqlite } from '@aiao/rxdb-adapter-wa-sqlite';
65
+ import { RxDBAdapterWaSqlite } from '@aiao/rxdb-adapter-wa-sqlite';
66
+ import { Todo } from './entities/Todo';
29
67
 
30
68
  const rxdb = new RxDB({
31
69
  dbName: 'my-app',
32
- context: { userId: 'user-123' },
33
- entities: [Todo, User],
70
+ context: { userId: 'current-user-id' },
71
+ entities: [Todo],
34
72
  sync: {
35
- local: { adapter: 'sqlite' },
73
+ local: { adapter: 'wa-sqlite' },
36
74
  remote: { adapter: 'supabase' },
37
- type: SyncType.PullPush
75
+ type: SyncType.Full
38
76
  }
39
77
  });
40
78
 
41
- // 注册适配器
42
- rxdb.adapter('sqlite', db => new RxDBAdapterSqlite(db, { vfs: 'MemoryAsyncVFS' }));
79
+ // 注册本地适配器
80
+ rxdb.adapter('wa-sqlite', db => new RxDBAdapterWaSqlite(db, { vfs: 'IDBBatchAtomicVFS', async: true }));
43
81
 
82
+ // 注册远程适配器
44
83
  rxdb.adapter(
45
84
  'supabase',
46
- db =>
85
+ async db =>
47
86
  new RxDBAdapterSupabase(db, {
48
87
  supabaseUrl: 'https://your-project.supabase.co',
49
88
  supabaseKey: 'your-anon-key'
89
+ // 或复用已有客户端:client: existingSupabaseClient
50
90
  })
51
91
  );
52
92
 
53
- // 连接
54
- await rxdb.connect('sqlite');
55
- await rxdb.connect('supabase');
93
+ // 连接本地(自动建表)
94
+ await rxdb.connect('wa-sqlite');
95
+
96
+ // 连接远程(启动 Realtime 订阅 + RLS 自检)
97
+ const supabase = await rxdb.getAdapter('supabase');
98
+ await supabase.connect();
56
99
  ```
57
100
 
58
- ### 2. 数据操作
101
+ ### 2. 数据操作(active-record 风格)
59
102
 
60
103
  ```typescript
61
104
  // 创建
62
- const todo = rxdb.repository(Todo).create();
105
+ const todo = new Todo();
63
106
  todo.title = 'Learn RxDB';
107
+ todo.completed = false;
64
108
  await todo.save();
65
109
 
66
- // 查询
110
+ // 查询(本地 SQLite,响应式)
67
111
  const todos = await rxdb.repository(Todo).find({
68
- where: {
69
- combinator: 'and',
70
- rules: [{ field: 'completed', operator: '=', value: false }]
71
- }
112
+ where: { combinator: 'and', rules: [{ field: 'completed', operator: '=', value: false }] }
72
113
  });
73
-
74
- // 同步
75
- await rxdb.versionManager.push(); // 推送本地变更到远程
76
- await rxdb.versionManager.pull(); // 拉取远程变更到本地
77
- await rxdb.versionManager.sync(); // 双向同步 (pull + push)
78
114
  ```
79
115
 
80
- ## 测试
81
-
82
- ### 前置条件
83
-
84
- 测试依赖本地 Supabase 环境,确保:
116
+ ### 3. 同步
85
117
 
86
- 1. **Docker 已安装并运行**
87
- 2. **启动 Supabase 测试容器**:
88
-
89
- ```bash
90
- cd docker
91
- bash start.sh
92
- ```
93
-
94
- 这会启动并初始化:
95
- - PostgreSQL (端口 5432)
96
- - Kong API Gateway (端口 8000)
97
- - 创建必要的表和函数
98
-
99
- 3. **环境变量配置**:
100
- 测试自动使用以下配置:
101
- - `VITE_SUPABASE_URL=http://localhost:8000`
102
- - `VITE_SUPABASE_KEY=<anon-key>` (见 `docker/.env`)
118
+ ```typescript
119
+ await rxdb.versionManager.push(); // 推送本地变更到远程
120
+ await rxdb.versionManager.pull(); // 拉取远程变更到本地
121
+ await rxdb.versionManager.sync(); // 双向(pull + push)
103
122
 
104
- ### 运行测试
123
+ // 也可按实体粒度同步
124
+ await rxdb.versionManager.pushRepository('public', 'Todo');
125
+ await rxdb.versionManager.pullRepository('public', 'Todo', { limit: 200 });
126
+ await rxdb.versionManager.syncRepository('public', 'Todo');
127
+ ```
105
128
 
106
- ```bash
107
- # 确保 Supabase 已启动
108
- cd docker && bash start.sh
129
+ ## 配置项(`SupabaseAdapterOptions`)
109
130
 
110
- # 运行测试
111
- nx test rxdb-adapter-supabase
131
+ - `supabaseUrl?: string` — 项目 URL(与 `supabaseKey` 配合)
132
+ - `supabaseKey?: string` — API Key(与 `supabaseUrl` 配合)
133
+ - `client?: SupabaseClient` — 复用已有客户端,优先级高于 URL+Key
134
+ - `rlsCheck?: boolean | SupabaseRlsCheckOptions` — 连接时 RLS 自检;`false` 关闭,对象可配 `rpcName` / `failureMode` / `tables`
112
135
 
113
- # 带覆盖率
114
- nx test rxdb-adapter-supabase --coverage
136
+ `rlsCheck` 默认行为:调用 RPC `rxdb_check_rls` 检查实体表是否启用 RLS,未启用时 **告警但不阻断**(`failureMode: 'warn'`)。设为 `'throw'` 可在生产环境强制阻断未开启 RLS 的连接。
115
137
 
116
- # 停止 Supabase
117
- cd docker && bash stop.sh
138
+ ```typescript
139
+ const adapter = new RxDBAdapterSupabase(rxdb, {
140
+ client,
141
+ rlsCheck: { failureMode: 'throw' }
142
+ });
118
143
  ```
119
144
 
120
- **常见问题**:
121
-
122
- - ❌ `Could not find the table 'public.RxDBChange' in the schema cache`
123
- - **原因**:Supabase 容器未启动或数据库未初始化
124
- - **解决**:`cd docker && bash start.sh`
125
-
126
- - ❌ `Connection refused at localhost:8000`
127
- - **原因**:Kong API Gateway 未就绪
128
- - **解决**:等待 `start.sh` 完成健康检查
145
+ 严格模式下,目标表不存在、RLS 未启用、RPC 响应遗漏目标表、检查 RPC 缺失或检查请求失败都会拒绝 `connect()`,且不会启动 Realtime;后续重试会重新执行检查。该检查只确认目标表已启用 RLS,不验证 policy 内容、认证 claims、tenant ownership 或 RPC grants,这些仍需在服务端独立审计。
129
146
 
130
147
  ## 数据库 Schema
131
148
 
132
- Supabase 适配器需要以下表结构:
149
+ 适配器依赖两张系统表(**snake_case 表名,camelCase 列名加引号**),完整脚本见 [docker/sql/](https://github.com/aiao-io/aiao/tree/main/docker/sql)。
133
150
 
134
- ### RxDBChange (变更追踪表)
151
+ ### `rxdb_change`(变更记录)
135
152
 
136
153
  ```sql
137
- CREATE TABLE "RxDBChange" (
138
- "id" BIGSERIAL PRIMARY KEY,
139
- "branchId" TEXT NOT NULL,
140
- "entityType" TEXT NOT NULL,
141
- "entityId" TEXT NOT NULL,
142
- "type" TEXT NOT NULL, -- INSERT/UPDATE/DELETE
143
- "patch" JSONB,
144
- "remoteId" BIGINT,
145
- "revertChangeId" BIGINT,
146
- "createdAt" TIMESTAMPTZ DEFAULT NOW(),
147
- "updatedAt" TIMESTAMPTZ DEFAULT NOW()
154
+ CREATE TABLE public.rxdb_change (
155
+ id serial PRIMARY KEY,
156
+ namespace varchar NOT NULL DEFAULT 'public',
157
+ entity varchar NOT NULL,
158
+ "entityId" varchar NOT NULL,
159
+ "branchId" varchar DEFAULT 'main',
160
+ type varchar NOT NULL CHECK (type IN ('INSERT','UPDATE','DELETE')),
161
+ patch jsonb,
162
+ "inversePatch" jsonb,
163
+ "transactionId" uuid,
164
+ "localId" integer,
165
+ "clientId" varchar,
166
+ "createdAt" timestamptz(3) NOT NULL DEFAULT now(),
167
+ "updatedAt" timestamptz(3) NOT NULL DEFAULT now()
148
168
  );
149
169
  ```
150
170
 
151
- ### RxDBBranch (分支元数据)
171
+ ### `rxdb_branch`(分支元数据)
152
172
 
153
173
  ```sql
154
- CREATE TABLE "RxDBBranch" (
155
- "id" TEXT PRIMARY KEY,
156
- "dbName" TEXT NOT NULL,
157
- "context" JSONB,
158
- "lastPushedChangeId" BIGINT,
159
- "lastPullRemoteChangeId" BIGINT,
160
- "createdAt" TIMESTAMPTZ DEFAULT NOW(),
161
- "updatedAt" TIMESTAMPTZ DEFAULT NOW()
174
+ CREATE TABLE public.rxdb_branch (
175
+ id varchar PRIMARY KEY,
176
+ activated boolean DEFAULT false,
177
+ "fromChangeId" integer,
178
+ "lastPushedChangeId" integer,
179
+ "lastPushedAt" timestamptz(3),
180
+ "lastPulledAt" timestamptz(3),
181
+ "parentId" varchar,
182
+ "createdAt" timestamptz(3) NOT NULL DEFAULT now(),
183
+ "updatedAt" timestamptz(3) NOT NULL DEFAULT now()
162
184
  );
163
185
  ```
164
186
 
165
- ### 业务表示例
166
-
167
- ```sql
168
- CREATE TABLE "Todo" (
169
- "id" TEXT PRIMARY KEY,
170
- "title" TEXT NOT NULL,
171
- "completed" BOOLEAN DEFAULT FALSE,
172
- "createdAt" TIMESTAMPTZ DEFAULT NOW(),
173
- "updatedAt" TIMESTAMPTZ DEFAULT NOW()
174
- );
175
-
176
- -- 自动创建变更记录的触发器
177
- CREATE TRIGGER rxdb_sync_trigger
178
- AFTER INSERT OR UPDATE OR DELETE ON "Todo"
179
- FOR EACH ROW EXECUTE FUNCTION rxdb_sync_change();
180
- ```
181
-
182
- 完整 SQL 脚本见 [docker/sql/](../../docker/sql/) 目录。
187
+ > Realtime 需把 `rxdb_change` 加入 `supabase_realtime` publication;系统表初始化脚本已处理。
188
+ > 同步事务依赖 RPC:`rxdb_mutations`、`rxdb_check_rls`、`rxdb_enable_sync_for_branch`,树查询依赖 `get_descendants` / `get_root_descendants` / `get_ancestors`(缺失时自动降级为内存遍历)。
183
189
 
184
190
  ## API
185
191
 
186
192
  ### RxDBAdapterSupabase
187
193
 
188
194
  ```typescript
189
- class RxDBAdapterSupabase {
195
+ class RxDBAdapterSupabase extends RxDBAdapterRemoteBase {
190
196
  constructor(rxdb: RxDB, options: SupabaseAdapterOptions);
197
+ readonly client: SupabaseClient;
191
198
 
192
- // 连接管理
193
- connect(): Promise<IRxDBAdapter>;
199
+ // 连接 / 生命周期
200
+ connect(): Promise<IRxDBAdapter>; // RLS 自检 + 启动 Realtime
194
201
  disconnect(): Promise<void>;
202
+ version(): Promise<string>;
195
203
 
196
- // 同步操作
197
- pullChanges(options: PullOptions): Promise<RemoteChange[]>;
198
- pushChanges(changes: LocalChange[]): Promise<PushResult>;
199
-
200
- // 批量操作
201
- saveMany<T>(entities: T[]): Promise<T[]>;
202
- removeMany<T>(entities: T[]): Promise<void>;
203
- mutations(map: RxDBMutationsMap): Promise<void>;
204
+ // 批量
205
+ saveMany<T>(entities): Promise<T[]>; // upsert,非事务
206
+ removeMany<T>(entities): Promise<T[]>; // 非事务
207
+ mutations<T>(map): Promise<T[]>; // RPC 事务
208
+
209
+ // 同步引擎
210
+ pullChanges(sinceId, limit?, repositoryFilter?, filter?, branchId?): Promise<RemoteChange[]>;
211
+ pullChangesBatch(requests, limit?, branchIds?): Promise<RemoteChange[]>;
212
+ getChangeCount(sinceId, repositoryFilter?, branchId?): Promise<{ count; latestChangeId }>;
213
+ mergeChanges(actions, branchId?, changes?): Promise<RemoteMergeResult>;
214
+
215
+ // 分支
216
+ pushBranches(branches): Promise<{ synced; skipped }>;
217
+ branchExists(branchId): Promise<boolean>;
218
+ pullBranches(): Promise<RemoteBranchInfo[]>;
219
+
220
+ // QueryCache
221
+ fetchMetadata(entityName, queryFilter): Observable<QueryCacheEntityMetadata[]>;
222
+ findByIds<T>(entityName, ids): Observable<T[]>;
223
+
224
+ isTableExisted(EntityType): Promise<boolean>;
204
225
  }
205
226
  ```
206
227
 
207
228
  ### SupabaseRepository
208
229
 
209
230
  ```typescript
210
- class SupabaseRepository<T> {
211
- // CRUD
212
- create(): T;
213
- find(options?: FindOptions): Promise<T[]>;
214
- findOne(options?: FindOptions): Promise<T | null>;
215
- count(options?: FindOptions): Promise<number>;
216
-
217
- // 批量
218
- saveMany(entities: T[]): Promise<T[]>;
219
- removeMany(entities: T[]): Promise<void>;
231
+ class SupabaseRepository<T> extends RepositoryBase<T> {
232
+ find(options): Promise<T[]>; // 自动 1000 行分页;始终以 id 兜底排序
233
+ count(options): Promise<number>; // count=exact + head(groupBy 暂不支持)
234
+ create(entity): Promise<T>;
235
+ update(entity, patch): Promise<T>;
236
+ remove(entity): Promise<T>; // 硬删除
220
237
  }
221
238
  ```
222
239
 
240
+ `find` 的 `where` 支持点分关系路径(`orders.amount`)与 `exists`/`notExists`,会自动生成 PostgREST `!inner` 关联过滤。
241
+
223
242
  ### SupabaseTreeRepository
224
243
 
225
244
  ```typescript
226
245
  class SupabaseTreeRepository<T> extends SupabaseRepository<T> {
227
- // 树形操作
228
- findDescendants(entity: T, depth?: number): Promise<T[]>;
229
- findAncestors(entity: T): Promise<T[]>;
230
- findRoots(): Promise<T[]>;
246
+ findDescendants(options: FindTreeOptions<T>): Promise<T[]>; // 含当前节点
247
+ countDescendants(options: FindTreeOptions<T>): Promise<number>; // 不含当前节点
248
+ findAncestors(options: FindTreeOptions<T>): Promise<T[]>; // 含当前节点
249
+ countAncestors(options: FindTreeOptions<T>): Promise<number>; // 不含当前节点
231
250
  }
232
251
  ```
233
252
 
234
- ## 架构
253
+ ## 同步机制
254
+
255
+ ### Push(`mergeChanges`)
235
256
 
236
- ```
237
- ┌─────────────────────────────────────────────────────────────┐
238
- │ RxDB Core │
239
- │ ┌────────────┐ ┌────────────┐ ┌──────────────────────┐ │
240
- │ │ Repository │ │VersionMgr │ │ SchemaManager │ │
241
- │ └─────┬──────┘ └─────┬──────┘ └──────────────────────┘ │
242
- └────────┼───────────────┼─────────────────────────────────────┘
243
- │ │
244
- ▼ ▼
245
- ┌─────────────────────────────────────────────────────────────┐
246
- │ RxDBAdapterSupabase (Remote) │
247
- │ ┌──────────────────────────────────────────────────────┐ │
248
- │ │ SupabaseRepository │ │
249
- │ │ - CRUD 操作 │ │
250
- │ │ - 查询构建 (apply_rule_group) │ │
251
- │ │ - 批量操作 │ │
252
- │ └──────────────────────────────────────────────────────┘ │
253
- │ ┌──────────────────────────────────────────────────────┐ │
254
- │ │ Sync Engine │ │
255
- │ │ - pullChanges: 拉取远程变更 │ │
256
- │ │ - pushChanges: 推送本地变更 (带压缩) │ │
257
- │ │ - mergeChanges: 通过 RPC 原子性写入 │ │
258
- │ └──────────────────────────────────────────────────────┘ │
259
- │ ┌──────────────────────────────────────────────────────┐ │
260
- │ │ Realtime Subscription │ │
261
- │ │ - 监听 RxDBChange 表 INSERT 事件 │ │
262
- │ │ - 自动触发 pull 同步 │ │
263
- │ └──────────────────────────────────────────────────────┘ │
264
- └─────────────┬───────────────────────────────────────────────┘
265
-
266
-
267
- ┌─────────────────────┐
268
- │ Supabase (PostgreSQL) │
269
- │ - RxDBChange │
270
- │ - RxDBBranch │
271
- │ - Business Tables │
272
- │ - Triggers/RPCs │
273
- └─────────────────────┘
274
- ```
257
+ 1. 收集本地未推送变更(`remoteId = null`)
258
+ 2. 变更压缩:`INSERT→UPDATE*` → `INSERT`;`INSERT→DELETE` → 丢弃;`UPDATE*→DELETE` → `DELETE`
259
+ 3. 调用 RPC `rxdb_mutations`,在单事务内写 `rxdb_change` + 实体表(`p_skip_sync=true` 跳过触发器)
260
+ 4. 返回 `maxChangeId` 与 `changeIdMapping`(localId → remoteId)
275
261
 
276
- ## 同步机制
262
+ ### Pull(`pullChanges` / `pullChangesBatch`)
277
263
 
278
- ### Push 流程
264
+ 1. `id > sinceId` 为游标查询远程变更(用 id 而非时间戳,避免同毫秒重复)
265
+ 2. 按 `INSERT/UPDATE/DELETE` 应用到本地实体表
266
+ 3. 写入本地 `rxdb_change` 并标记 `remoteId`,避免回推
267
+ 4. 更新水位线
279
268
 
280
- 1. **收集本地变更**:从 `RxDBChange` 表查询 `remoteId = null` 的记录
281
- 2. **变更压缩**:
282
- - `INSERT→UPDATE*` → 单个 `INSERT`
283
- - `INSERT→DELETE` → 丢弃
284
- - `UPDATE*→DELETE` → 单个 `DELETE`
285
- 3. **调用 RPC**:通过 `rxdb_mutations()` 原子性写入
286
- 4. **更新元数据**:记录 `lastPushedChangeId`
269
+ ### Realtime
287
270
 
288
- ### Pull 流程
271
+ 监听 `rxdb_change` 的 INSERT,按 `clientId` 过滤掉自身变更,派发 `EntityRemote{Created,Updated,Removed}Event`;通道异常按指数退避(500ms→5s)重连。
289
272
 
290
- 1. **查询远程变更**:`id > lastPullRemoteChangeId` 且 `branchId != current`
291
- 2. **应用到本地实体表**:
292
- - `INSERT` → 插入
293
- - `UPDATE` → 更新
294
- - `DELETE` → 删除
295
- 3. **写入本地 RxDBChange**:标记 `remoteId` 避免重复 push
296
- 4. **更新元数据**:记录 `lastPullRemoteChangeId`
273
+ ## 测试
297
274
 
298
- ## 开发
275
+ 测试在浏览器环境(Vitest + Playwright Chromium)运行,集成用例需要本地 Supabase(Docker)。
299
276
 
300
277
  ```bash
301
- # 构建
302
- nx build rxdb-adapter-supabase
278
+ # 一键:自动起容器 + 初始化 + 跑测试(test 目标 dependsOn: test-env)
279
+ pnpm nx test rxdb-adapter-supabase
303
280
 
304
- # 格式化
305
- nx format:write --projects=rxdb-adapter-supabase
281
+ # 覆盖率
282
+ pnpm nx test rxdb-adapter-supabase --coverage
283
+ ```
284
+
285
+ 测试自动注入环境变量 `VITE_SUPABASE_URL`(`http://localhost:54321`)与 `VITE_SUPABASE_KEY`(取自 `docker/docker-compose.ci.yml`)。容器与初始化脚本见 [docker/](https://github.com/aiao-io/aiao/tree/main/docker)。
286
+
287
+ > 纯单元用例(如 `rule_group_builder` 转义、tree fallback、retry)不依赖网络,可单独运行:
288
+ > `pnpm exec vitest run src/__tests__/review-regressions.spec.ts`(需在包目录下)。
306
289
 
307
- # Lint
308
- nx lint rxdb-adapter-supabase
290
+ ## 开发
291
+
292
+ ```bash
293
+ pnpm nx build rxdb-adapter-supabase
294
+ pnpm nx lint rxdb-adapter-supabase
309
295
  ```
310
296
 
311
297
  ## License
@@ -1,8 +1,10 @@
1
- import { EntityType, IRepository, IRxDBAdapter, IRxDBChange, PullBatchRequest, QueryCacheEntityMetadata, RemoteBranchInfo, RemoteChange, RemoteMergeResult, RuleGroup, RxDB, RxDBMutationsMap, SwitchVersionActions, RxDBAdapterRemoteBase } from '../packages/rxdb/src/index.ts';
1
+ import { EntityType, IRepository, IRxDBAdapter, IRxDBChange, PullBatchRequest, QueryCacheEntityMetadata, RemoteBranchInfo, RemoteChange, RemoteMergeResult, RuleGroup, RxDB, RxDBMutationsMap, SwitchVersionActions, RxDBAdapterRemoteBase } from '@aiao/rxdb';
2
2
  import { SupabaseClient } from '@supabase/supabase-js';
3
3
  import { Observable } from 'rxjs';
4
4
  import { SupabaseAdapterOptions } from './supabase.interface.js';
5
5
  export declare const ADAPTER_NAME = "supabase";
6
+ /** 适配器所基于的 @supabase/supabase-js 最低支持版本(与 package.json peerDependencies 对齐) */
7
+ export declare const SUPABASE_SDK_VERSION = "2.88.0";
6
8
  /** pushChanges 参数结构(已压缩的变更) */
7
9
  interface CompressedChange {
8
10
  entityKey: string;
@@ -1 +1 @@
1
- {"version":3,"file":"RxDBAdapterSupabase.d.ts","sourceRoot":"","sources":["../src/RxDBAdapterSupabase.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"RxDBAdapterSupabase.d.ts","sourceRoot":"","sources":["../src/RxDBAdapterSupabase.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { EntityType } from '../packages/rxdb/src/index.ts';
1
+ import { EntityType } from '@aiao/rxdb';
2
2
  /**
3
3
  * 按实体类型分组
4
4
  */
@@ -1,4 +1,4 @@
1
- import { EntityMetadata, EntityType, IRepository, EntityStaticType, RepositoryBase } from '../packages/rxdb/src/index.ts';
1
+ import { EntityMetadata, EntityType, IRepository, EntityStaticType, RepositoryBase } from '@aiao/rxdb';
2
2
  import { RxDBAdapterSupabase } from './RxDBAdapterSupabase.js';
3
3
  /**
4
4
  * Supabase Repository
@@ -1 +1 @@
1
- {"version":3,"file":"SupabaseRepository.d.ts","sourceRoot":"","sources":["../src/SupabaseRepository.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,WAAW,EAAa,MAAM,YAAY,CAAC;AACrF,OAAO,EAAE,gBAAgB,EAAkC,cAAc,EAAE,MAAM,YAAY,CAAC;AAK9F,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAIpE;;;GAGG;AACH,qBAAa,kBAAkB,CAAC,CAAC,SAAS,UAAU,CAAE,SAAQ,cAAc,CAAC,CAAC,CAAE,YAAW,WAAW,CAAC,CAAC,CAAC;IAIrG,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,mBAAmB;IAHjD,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;gBAGvB,OAAO,EAAE,mBAAmB,EAC/C,UAAU,EAAE,CAAC;IAMf;;;;;;;;;OASG;IACG,IAAI,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC,EAAE,aAAa,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;IA2DnF;;;;;;;;;;OAUG;IACG,KAAK,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC,EAAE,cAAc,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAsB1E;;;;;;;;OAQG;IACG,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAc/D;;;;;;;;;;OAUG;IACG,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAehG;;;;;;;;OAQG;IACG,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAU/D,6CAA6C;IAC7C,OAAO,CAAC,uBAAuB;IAgC/B;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IA0C7B;;;OAGG;IACH,OAAO,CAAC,4BAA4B;IAwBpC,qBAAqB;IACrB,OAAO,CAAC,sBAAsB;IAuB9B,yBAAyB;IACzB,OAAO,CAAC,mBAAmB;IAI3B,0BAA0B;IAC1B,OAAO,CAAC,UAAU;CAMnB"}
1
+ {"version":3,"file":"SupabaseRepository.d.ts","sourceRoot":"","sources":["../src/SupabaseRepository.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,WAAW,EAAa,MAAM,YAAY,CAAC;AACrF,OAAO,EAAE,gBAAgB,EAAkC,cAAc,EAAE,MAAM,YAAY,CAAC;AAI9F,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAYpE;;;GAGG;AACH,qBAAa,kBAAkB,CAAC,CAAC,SAAS,UAAU,CAAE,SAAQ,cAAc,CAAC,CAAC,CAAE,YAAW,WAAW,CAAC,CAAC,CAAC;IAIrG,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,mBAAmB;IAHjD,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;gBAGvB,OAAO,EAAE,mBAAmB,EAC/C,UAAU,EAAE,CAAC;IAMf;;;;;;;;;OASG;IACG,IAAI,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC,EAAE,aAAa,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;IAgEnF;;;;;;;;;;OAUG;IACG,KAAK,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC,EAAE,cAAc,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAuB1E;;;;;;;;OAQG;IACG,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAc/D;;;;;;;;;;OAUG;IACG,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAehG;;;;;;;;OAQG;IACG,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAW/D,6CAA6C;IAC7C,OAAO,CAAC,uBAAuB;IA0C/B;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IA0C7B;;;OAGG;IACH,OAAO,CAAC,4BAA4B;IAyBpC,qBAAqB;IACrB,OAAO,CAAC,sBAAsB;IAuB9B,yBAAyB;IACzB,OAAO,CAAC,mBAAmB;IAI3B,0BAA0B;IAC1B,OAAO,CAAC,UAAU;CAMnB"}
@@ -1,4 +1,4 @@
1
- import { EntityType, FindTreeOptions, ITreeRepository } from '../packages/rxdb/src/index.ts';
1
+ import { EntityType, FindTreeOptions, ITreeRepository } from '@aiao/rxdb';
2
2
  import { RxDBAdapterSupabase } from './RxDBAdapterSupabase.js';
3
3
  import { SupabaseRepository } from './SupabaseRepository.js';
4
4
  /**
@@ -40,17 +40,17 @@ export declare class SupabaseTreeRepository<T extends EntityType> extends Supaba
40
40
  */
41
41
  countAncestors(options: FindTreeOptions<T>): Promise<number>;
42
42
  /**
43
- * 备用的子孙节点查询(不使用存储函数)
43
+ * 基于实体表的子孙节点查询
44
44
  *
45
45
  * 一次查询获取所有节点,在内存中构建树结构,避免 N+1 问题。
46
46
  */
47
- private findDescendantsFallback;
47
+ private findDescendantsFromTable;
48
48
  /**
49
- * 备用的祖先节点查询(不使用存储函数)
49
+ * 基于实体表的祖先节点查询
50
50
  *
51
51
  * 一次查询获取所有节点,在内存中向上遍历,避免 N+1 问题。
52
52
  */
53
- private findAncestorsFallback;
53
+ private findAncestorsFromTable;
54
54
  /**
55
55
  * 将数据库行转换为实体
56
56
  */
@@ -1 +1 @@
1
- {"version":3,"file":"SupabaseTreeRepository.d.ts","sourceRoot":"","sources":["../src/SupabaseTreeRepository.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE/E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAEpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAG7D;;;;;GAKG;AACH,qBAAa,sBAAsB,CAAC,CAAC,SAAS,UAAU,CAAE,SAAQ,kBAAkB,CAAC,CAAC,CAAE,YAAW,eAAe,CAAC,CAAC,CAAC;gBACvG,OAAO,EAAE,mBAAmB,EAAE,UAAU,EAAE,CAAC;IAIvD;;;;;;OAMG;IACG,eAAe,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;IAoB9E;;;;;;OAMG;IACG,gBAAgB,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAcpE;;;;;OAKG;IACG,aAAa,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;IAyB5E;;;;;OAKG;IACG,cAAc,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAOlE;;;;OAIG;YACW,uBAAuB;IA0ErC;;;;OAIG;YACW,qBAAqB;IAuDnC;;OAEG;IACH,OAAO,CAAC,oBAAoB;CAM7B"}
1
+ {"version":3,"file":"SupabaseTreeRepository.d.ts","sourceRoot":"","sources":["../src/SupabaseTreeRepository.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE/E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAEpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAG7D;;;;;GAKG;AACH,qBAAa,sBAAsB,CAAC,CAAC,SAAS,UAAU,CAAE,SAAQ,kBAAkB,CAAC,CAAC,CAAE,YAAW,eAAe,CAAC,CAAC,CAAC;gBACvG,OAAO,EAAE,mBAAmB,EAAE,UAAU,EAAE,CAAC;IAIvD;;;;;;OAMG;IACG,eAAe,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;IAI9E;;;;;;OAMG;IACG,gBAAgB,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAcpE;;;;;OAKG;IACG,aAAa,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;IAI5E;;;;;OAKG;IACG,cAAc,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAOlE;;;;OAIG;YACW,wBAAwB;IA2EtC;;;;OAIG;YACW,sBAAsB;IAuDpC;;OAEG;IACH,OAAO,CAAC,oBAAoB;CAM7B"}
@@ -0,0 +1,19 @@
1
+ import { RxDB } from '@aiao/rxdb';
2
+ /** Supabase 中实体的逻辑作用域与物理表定位。 */
3
+ export interface EntityScope {
4
+ namespace: string;
5
+ entity: string;
6
+ schema: string;
7
+ tableName: string;
8
+ }
9
+ /** 返回当前 RxDB 配置中的唯一实体作用域。 */
10
+ export declare function getConfiguredEntityScopes(rxdb: RxDB): EntityScope[];
11
+ /**
12
+ * 将上游仅含实体名的标识解析为唯一作用域。
13
+ *
14
+ * 显式的 `namespace:entity` 可直接定位;裸实体名只有在配置中唯一时才允许使用。
15
+ */
16
+ export declare function resolveEntityScope(rxdb: RxDB, identifier: string): EntityScope;
17
+ /** 判断逻辑 namespace + entity 是否属于当前配置。 */
18
+ export declare function isConfiguredEntityScope(rxdb: RxDB, namespace: string | undefined, entity: string): boolean;
19
+ //# sourceMappingURL=entity_scope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entity_scope.d.ts","sourceRoot":"","sources":["../src/entity_scope.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,IAAI,EAAE,MAAM,YAAY,CAAC;AAI1D,gCAAgC;AAChC,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,6BAA6B;AAC7B,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,IAAI,GAAG,WAAW,EAAE,CAgBnE;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,WAAW,CAqB9E;AAED,wCAAwC;AACxC,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAG1G"}
@@ -1 +1 @@
1
- {"version":3,"file":"handle_supabase_change.d.ts","sourceRoot":"","sources":["../src/handle_supabase_change.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,uBAAuB,CAAC;AAC5E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAEpE,KAAK,eAAe,GAAG,8BAA8B,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAoB/E;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,mBAAmB,EAAE,OAAO,EAAE,eAAe,GAAG,IAAI,CAoCjG"}
1
+ {"version":3,"file":"handle_supabase_change.d.ts","sourceRoot":"","sources":["../src/handle_supabase_change.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,uBAAuB,CAAC;AAE5E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAEpE,KAAK,eAAe,GAAG,8BAA8B,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAoB/E;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,mBAAmB,EAAE,OAAO,EAAE,eAAe,GAAG,IAAI,CAqCjG"}