@aiao/rxdb-adapter-http 0.0.26

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.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +269 -0
  3. package/dist/RxDBAdapterHttp.d.ts +251 -0
  4. package/dist/RxDBAdapterHttp.d.ts.map +1 -0
  5. package/dist/change-feed.d.ts +66 -0
  6. package/dist/change-feed.d.ts.map +1 -0
  7. package/dist/chunking.d.ts +29 -0
  8. package/dist/chunking.d.ts.map +1 -0
  9. package/dist/conditional-cache.d.ts +175 -0
  10. package/dist/conditional-cache.d.ts.map +1 -0
  11. package/dist/config.d.ts +30 -0
  12. package/dist/config.d.ts.map +1 -0
  13. package/dist/errors.d.ts +215 -0
  14. package/dist/errors.d.ts.map +1 -0
  15. package/dist/handler-contract.d.ts +41 -0
  16. package/dist/handler-contract.d.ts.map +1 -0
  17. package/dist/http.interface.d.ts +519 -0
  18. package/dist/http.interface.d.ts.map +1 -0
  19. package/dist/index.d.ts +25 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +864 -0
  22. package/dist/metadata.d.ts +15 -0
  23. package/dist/metadata.d.ts.map +1 -0
  24. package/dist/pagination.d.ts +30 -0
  25. package/dist/pagination.d.ts.map +1 -0
  26. package/dist/rest.d.ts +94 -0
  27. package/dist/rest.d.ts.map +1 -0
  28. package/dist/transport.d.ts +174 -0
  29. package/dist/transport.d.ts.map +1 -0
  30. package/package.json +64 -0
  31. package/src/RxDBAdapterHttp.ts +786 -0
  32. package/src/change-feed.ts +470 -0
  33. package/src/chunking.ts +68 -0
  34. package/src/conditional-cache.ts +271 -0
  35. package/src/config.ts +83 -0
  36. package/src/errors.ts +295 -0
  37. package/src/handler-contract.ts +77 -0
  38. package/src/http.interface.ts +554 -0
  39. package/src/index.ts +25 -0
  40. package/src/metadata.ts +94 -0
  41. package/src/pagination.ts +184 -0
  42. package/src/rest.ts +476 -0
  43. package/src/transport.ts +699 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Aiao Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,269 @@
1
+ # @aiao/rxdb-adapter-http
2
+
3
+ RxDB HTTP 适配器 — 让**你自己的 REST API** 充当 QueryCache 的远端事实源。
4
+
5
+ 作为 RxDB 的 **remote** 适配器使用:本地 SQLite 只做行缓存,读写都以远端为准。
6
+ 适合已有一套后端接口、不打算把数据库结构暴露给客户端的场景。
7
+
8
+ > ⚠️ **v1 只支持 `SyncType.QueryCache`。** `pullChanges` / `mergeChanges` / `getChangeCount`
9
+ > 一律抛 `HttpChangelogUnsupportedError`,**不会**返回空数组或 `0` 假装「远端无变更」。
10
+ > 需要离线写、undo/redo 与冲突解决的实体请改用本地适配器 + Supabase。
11
+
12
+ ## 特性
13
+
14
+ - ✅ **transport 在包内** — `fetch`、auth 注入、状态码提取、错误分类都由适配器做;handler 只做协议 mapping,拿不到网络
15
+ - ✅ **翻页** — offset(短页终止)与 token(`nextPageToken` 终止)两种形态,中途换形态 / token 不推进 / 连续空页 / 总页数触顶四种退化各自 fail-fast
16
+ - ✅ **分块 `findByIds`** — 按 `idChunkSize` 切块并合并;某块失败即整体失败,不当空块吞掉
17
+ - ✅ **单次发射契约** — `fetchMetadata` / `findByIds` 都是「拼完再发一次」并 `complete`,逐页/逐块发射会让 core 的 `forkJoin` 只留最后一份
18
+ - ✅ **REST 模板工厂** — `createRestHandlers()` 一行替掉样板 handler,模板校验全在**构造期**
19
+ - ✅ **错误分类锚定 core** — 传输失败与超时抛 core 的 `NetworkOfflineError`(`offlineFallback` 可降级),HTTP 响应错误带数字 `status`(不降级)
20
+ - ✅ **单请求超时** — 默认 30s,超时抛 `NetworkOfflineError` 而非裸 `AbortError`
21
+ - ✅ **结构隔离** — 不 `new` 任何本地存储、不实现 `upsertMany` / `deleteByIds` / `rawQuery`
22
+
23
+ ## 何时使用
24
+
25
+ - 已有 REST / JSON API,想让它接进 RxDB 的响应式查询
26
+ - 远端权威、客户端只需要一层结构化行缓存
27
+ - 不想把 SQL 或数据库结构暴露给客户端(请求体是 JSON `RuleGroup`,不是 SQL)
28
+
29
+ 不适用:需要离线写、Full / Filter 双向同步、分支与工作树语义 —— 那些请用 Supabase 适配器。
30
+
31
+ ## 安装
32
+
33
+ ```bash
34
+ pnpm add @aiao/rxdb @aiao/rxdb-adapter-http
35
+ # 行缓存用的本地适配器(按需二选一)
36
+ pnpm add @aiao/rxdb-adapter-wa-sqlite
37
+ # QueryCache 读写整条路径横跨三个插件包,缺一个 connect() 就抛
38
+ pnpm add @aiao/rxdb-plugin-history @aiao/rxdb-plugin-sync @aiao/rxdb-plugin-querycache
39
+ ```
40
+
41
+ > peerDependencies:`@aiao/rxdb`、`rxjs` ^7.8。无运行时依赖,请求走全局 `fetch`。
42
+
43
+ ## 导出
44
+
45
+ - `RxDBAdapterHttp` — 远程适配器主类
46
+ - `ADAPTER_NAME` — 常量 `'http'`
47
+ - `createRestHandlers` — REST 资源 URL 模板工厂,产出的是普通 `HttpHandlers`
48
+ - `HttpAdapterOptions` / `HttpHandlers` / `HttpRequestSpec` / `HttpNumericConfig` / `HttpAuthHook` 等配置与 handler 类型
49
+ - `RestHandlersOptions` / `RestOperation` / `RestOperationTemplate` / `RestPlaceholder` — 模板工厂类型
50
+ - 变更推送类型:`HttpChangeFeedOptions`、`HttpChangeFeedUnavailableReport` / `HttpChangeFeedUnavailableHook`、
51
+ `HttpChangeFeedNotificationReport` / `HttpChangeFeedNotificationHook`
52
+ - 诊断类型:`HttpEtagUnreadableReport` / `HttpEtagUnreadableHook`、`HttpResponseType`
53
+ - `DEFAULT_HTTP_CONFIG` — 六个数值配置的默认值
54
+ - 错误类型:`HttpAdapterError`(基类)、`HttpConfigError`、`HttpResponseError`、`HttpInvalidResponseError`、
55
+ `HttpRequestBuildError`、`HttpPaginationError`、`HttpInvalidMetadataError`、`HttpHandlerContractError`、
56
+ `HttpDisconnectedError`、`HttpChangelogUnsupportedError`、`HttpUnsupportedWireTypeError`、
57
+ `HttpUnsupportedOperationError`
58
+ - 错误成因的判别串:`HttpRequestBuildFailure`(`HttpRequestBuildError.reason`)、
59
+ `HttpPaginationFailure`(`HttpPaginationError.reason`)
60
+
61
+ > ⚠️ 不存在 `createRxDatabase()` / `createHttpAdapter()` 这类函数,注册方式见下方 `rxdb.adapter()`。
62
+
63
+ ## 快速开始
64
+
65
+ ### 1. 两个槽位各归各位
66
+
67
+ QueryCache 需要两个适配器,本包只占 **remote** 那一个:
68
+
69
+ | 槽位 | 谁来做 | 职责 |
70
+ | :------- | :------------------------------- | :---------------------------------------------------------------- |
71
+ | `remote` | `@aiao/rxdb-adapter-http` | `fetchMetadata` / `findByIds` + 可选的 `create`/`update`/`delete` |
72
+ | `local` | 任一 SQLite 适配器(**你注册**) | 行缓存:`getMetadataByIds` / `upsertMany` / `deleteByIds` |
73
+
74
+ 读引擎本身是第三样东西,也要你装,而且不止一个包:`SyncType.QueryCache` 的读路径住在
75
+ `@aiao/rxdb-plugin-querycache`,离线写出站住在 `@aiao/rxdb-plugin-sync`,后者又
76
+ `inject: ['plugin:history']`——三个包缺一个,`connect()` 就以 `RxDBMissingPluginError`
77
+ 拒绝,不会静默退化成本地查询。对照表见
78
+ [历史与同步拆包](../../website/docs/migration/history-sync-plugins.md)。
79
+
80
+ 本包**不持有也不创建**任何本地存储——不 `new` SQLite、不打开 OPFS / IndexedDB。
81
+ `inject: ['adapter:local']` 的插件(搜索、图查询等)因此绑到你注册的 SQLite,不会绑到本包。
82
+
83
+ ```typescript
84
+ import { RxDB, SyncType } from '@aiao/rxdb';
85
+ import { createRestHandlers, RxDBAdapterHttp } from '@aiao/rxdb-adapter-http';
86
+ import { RxDBAdapterWaSqlite } from '@aiao/rxdb-adapter-wa-sqlite';
87
+ import { rxDBPluginHistory } from '@aiao/rxdb-plugin-history';
88
+ import { rxDBPluginQueryCache } from '@aiao/rxdb-plugin-querycache';
89
+ import { rxDBPluginSync } from '@aiao/rxdb-plugin-sync';
90
+
91
+ const rxdb = new RxDB({
92
+ dbName: 'catalog',
93
+ entities: [Product],
94
+ sync: {
95
+ type: SyncType.QueryCache,
96
+ local: { adapter: 'wa-sqlite' },
97
+ remote: { adapter: 'http' }
98
+ }
99
+ });
100
+
101
+ // 传的是插件工厂函数本身,不要调用它。注册顺序随意,先后由 `inject` 保证
102
+ rxdb.use(rxDBPluginHistory);
103
+ rxdb.use(rxDBPluginSync);
104
+ rxdb.use(rxDBPluginQueryCache);
105
+
106
+ rxdb.adapter('wa-sqlite', db => new RxDBAdapterWaSqlite(db));
107
+ rxdb.adapter(
108
+ 'http',
109
+ db =>
110
+ new RxDBAdapterHttp(db, {
111
+ baseUrl: 'https://api.example.com/v1',
112
+ handlers: createRestHandlers({ resources: { Product: 'products' } }),
113
+ auth: () => ({ Authorization: `Bearer ${getToken()}` })
114
+ })
115
+ );
116
+ ```
117
+
118
+ ### 2A. REST 模板(推荐起步)
119
+
120
+ `createRestHandlers()` 把「资源路径 + 固定 body 形状」这份样板收成一个工厂,
121
+ 产出的仍是普通 `HttpHandlers`——**适配器一行没改**,翻页、分块、发射契约、错误分类全部照旧。
122
+
123
+ 默认模板:
124
+
125
+ | 操作 | 方法 | 路径 | 默认产出 |
126
+ | :--------------- | :------ | :----------------- | :----------- |
127
+ | `fetchMetadata` | `POST` | `:entity/metadata` | 是(不可关) |
128
+ | `findByIds` | `POST` | `:entity/by-ids` | 是(不可关) |
129
+ | `create` | `POST` | `:entity` | 是 |
130
+ | `update` | `PATCH` | `:entity/:id` | 是 |
131
+ | `delete` | `POST` | `:entity/delete` | 是 |
132
+ | `version` | `GET` | — | 否 |
133
+ | `isTableExisted` | `HEAD` | `:entity` | 否 |
134
+
135
+ ```typescript
136
+ createRestHandlers({
137
+ resources: { Product: 'products' }, // 未列出的实体直接用实体名作路径片段
138
+ templates: {
139
+ version: { path: 'meta/version' }, // 覆盖:给对象
140
+ delete: null // 关闭:只读后端让 repo.delete() fail-fast
141
+ }
142
+ });
143
+ ```
144
+
145
+ 模板校验**在构造期**:路径为空 / 含空白、`?`、`#` / 方法非法 / 占位符集合不匹配 / 关掉必选 handler,
146
+ 都当场抛 `HttpConfigError`。占位符必须**恰好匹配**——`update` 少 `:id` 会 `PATCH` 整个集合,
147
+ 任意模板少 `:entity` 会让所有实体共用一个 URL,两种退化在网线上都可能是 2xx,等发出去再看响应码是看不出来的。
148
+
149
+ > `delete` 默认走 `POST :entity/delete` 而不是 `DELETE :entity` + body:DELETE 的请求体会被不少代理、
150
+ > 网关和服务端框架直接丢弃,那样一条「删这 3 行」的请求会以「删整个集合」的面目到达服务端。
151
+ > 需要真 `DELETE` 的显式覆盖即可。
152
+
153
+ ### 2B. 手写 handlers
154
+
155
+ 后端形状不规则时手写。handler 只做协议 mapping:给出请求描述、解析响应体,**全程不碰网络**。
156
+
157
+ ```typescript
158
+ import type { HttpHandlers } from '@aiao/rxdb-adapter-http';
159
+
160
+ const handlers: HttpHandlers = {
161
+ onFetchMetadata: {
162
+ request: ctx => ({
163
+ url: `${ctx.entityName}/metadata`,
164
+ method: 'POST',
165
+ body: { where: ctx.where, offset: ctx.offset, limit: ctx.limit }
166
+ }),
167
+ parse: body => body as { id: string; updatedAt: string }[]
168
+ },
169
+ onFindByIds: {
170
+ request: ctx => ({ url: `${ctx.entityName}/rows`, method: 'POST', body: { ids: ctx.ids } }),
171
+ parse: body => body as unknown[]
172
+ }
173
+ };
174
+ ```
175
+
176
+ `where` 以 **JSON `RuleGroup`** 的形态原样交给你——本包不生成 SQL,服务端怎么翻译由你决定。
177
+ `onCreate` / `onUpdate` / `onDelete` 没配,适配器上就**不存在**对应方法,`repo.create(...)` 当场 fail-fast。
178
+
179
+ ## 配置项
180
+
181
+ ```typescript
182
+ new RxDBAdapterHttp(db, {
183
+ baseUrl: 'https://api.example.com',
184
+ handlers,
185
+ auth: () => ({ Authorization: `Bearer ${getToken()}` }),
186
+ headers: { 'X-App': 'catalog' },
187
+ pageSize: 1000,
188
+ idChunkSize: 100,
189
+ maxEmptyPages: 3,
190
+ maxPages: 1000,
191
+ requestTimeoutMs: 30000,
192
+ conditionalRequests: false,
193
+ conditionalCacheSize: 256
194
+ });
195
+ ```
196
+
197
+ | 字段 | 默认 | 含义 |
198
+ | :--------------------- | :------ | :----------------------------------------------- |
199
+ | `pageSize` | `1000` | 单页条数,透传为 handler 的 `ctx.limit` |
200
+ | `idChunkSize` | `100` | `findByIds` 单块 id 数 |
201
+ | `maxEmptyPages` | `3` | 游标形态下连续空页容忍上限;`0` = 不容忍 |
202
+ | `maxPages` | `1000` | 单次 `fetchMetadata` 总页数上限,触顶**抛错** |
203
+ | `requestTimeoutMs` | `30000` | **单个**请求的超时上限 |
204
+ | `conditionalCacheSize` | `256` | 条件请求响应缓存条目上限,仅在下方开关打开时生效 |
205
+
206
+ 六个数值都必须是 finite 正整数(`maxEmptyPages` 可为 `0`),否则**构造期**抛 `HttpConfigError` 并带上字段名与实际值。
207
+
208
+ `changeFeed`(SSE 变更通知)缺省不存在、配了即开;它是**实验性**能力,不在 1.0 兼容承诺内,协议目前只有参考后端一个实现。
209
+ `auth` 在每次请求发出前调用,与 `headers` 冲突时以 `auth` 为准;`auth` 抛错则请求不发出。
210
+
211
+ ### 条件请求(`conditionalRequests`,缺省关闭)
212
+
213
+ 打开后,`fetchMetadata` / `findByIds` 会带上 `If-None-Match` 复用上次 `200` 的解析结果,命中 `304` 时**返回那份结果**——
214
+ 不是空集(把 304 当空集就是本包全篇在防的假孤儿)。同一请求指纹的并发调用 **single-flight** 去重,
215
+ 不会出现「后一个拿到 304 而前一个还没回填」的空洞。
216
+
217
+ ```typescript
218
+ new RxDBAdapterHttp(db, { baseUrl, handlers, conditionalRequests: true });
219
+ ```
220
+
221
+ 四条要点:
222
+
223
+ - **必须显式开启。** 它只在远端真的发 `ETag` 并认 `If-None-Match` 时才有收益,而这一点适配器无从探测。
224
+ 关闭时行为与不带此特性的版本**逐字相同**:不发条件头、不去重、304 照旧当错误。
225
+ - **缓存的是响应不是行。** 行缓存归 core 经本地适配器落盘,本包不碰。响应缓存按适配器实例存活、
226
+ 按 `conditionalCacheSize` 有界(LRU)、`disconnect()` 时清空。翻页 / 分块按**单页 / 单块**各自键控。
227
+ - **三组 header 只有 handler 那组进请求指纹。** `HttpRequestSpec.headers`(handler 逐请求给的)**进**——
228
+ 在同一条 URL 上按租户 / 语言分投影是安全的,两个投影各占各的条目。适配器级的 `headers`(本实例恒定)
229
+ 与 auth hook 产出的**不进**:前者进去只是给每条键加同一段前缀,后者进去会让每次 token 轮换都全量失效。
230
+ - **换用户必须走 `disconnect()` / `connect()`。** 这是 auth header 不进指纹的代价:在同一实例上
231
+ 直接换 token 会读到上一个身份的响应。
232
+
233
+ ## 错误与离线降级
234
+
235
+ 判别位是**类名**(`instanceof`),`code` 只作辅助。
236
+
237
+ | 情形 | 抛出 | `isNetworkError` | `offlineFallback` |
238
+ | :------------------------- | :------------------------------------- | :--------------- | :---------------- |
239
+ | 非 2xx 响应(401/409/…) | `HttpResponseError`(带数字 `status`) | `false` | 不降级,原样上抛 |
240
+ | 2xx 但响应体不是 JSON | `HttpInvalidResponseError` | `false` | 不降级 |
241
+ | 请求发出前就构造失败 | `HttpRequestBuildError`(带 `reason`) | `false` | **不降级** |
242
+ | 连不上远端(fetch reject) | core 的 `NetworkOfflineError` | `true` | 有缓存则降级 |
243
+ | 单请求超时 | core 的 `NetworkOfflineError` | `true` | 有缓存则降级 |
244
+ | `disconnect()` 主动取消 | `HttpDisconnectedError` | `false` | **不降级** |
245
+
246
+ 传输失败刻意**不**包进本包的错误类:`isNetworkError` 的第一条判据是 `instanceof NetworkOfflineError`,
247
+ 包起来会让降级静默失效。
248
+
249
+ 反过来,`HttpRequestBuildError` 存在的理由正是**不**被包成传输失败:body 带 bigint、header 非法、
250
+ `GET` 带 body、URL 拼不出来、主键不是非空字符串——这五种本地问题里,`JSON.stringify` 与 `fetch` 抛的
251
+ 都是裸 `TypeError`,与真的连不上远端**完全同型**。不摘出来的话,一次脏数据会被 `offlineFallback`
252
+ 静默换成一份陈旧缓存,从此再没有报错。`reason` 取 `'body'` / `'headers'` / `'method'` / `'url'` / `'id'`。
253
+
254
+ ## 限制
255
+
256
+ - **bigint / binary 字段**:`connect()` 当场抛 `HttpUnsupportedWireTypeError`——本包没有为这两种类型定义 wire codec
257
+ - **`getRepository` / `saveMany` / `removeMany` / `mutations`**:一律抛 `HttpUnsupportedOperationError`;
258
+ QueryCache 的批量写不经过它们(走 core 的 remote-then-local)
259
+ - **`rawQuery`、分支成员(`pushBranches` / `branchExists` / `pullBranches`)**:不实现
260
+ - **`updatedAt`** 必须是合法 ISO 8601 串;带偏移或缺毫秒的会被 canonicalize 成 UTC + 3 位毫秒,非法串抛 `HttpInvalidMetadataError`
261
+
262
+ ## 文档
263
+
264
+ - [HTTP 适配器完整文档](https://rxdb.netlify.app/docs/adapters/http)
265
+ - [适配器总览](https://rxdb.netlify.app/docs/adapters)
266
+
267
+ ## License
268
+
269
+ MIT
@@ -0,0 +1,251 @@
1
+ import { RxDBAdapterRemoteBase, EntityType, IRepository, IRxDBAdapter, QueryCacheEntityMetadata, RemoteChange, RemoteMergeResult, RuleGroup, RxDB } from '@aiao/rxdb';
2
+ import { Observable } from 'rxjs';
3
+ import { HttpAdapterOptions, HttpNumericConfig } from './http.interface.js';
4
+ /** 适配器注册名 */
5
+ export declare const ADAPTER_NAME = "http";
6
+ /**
7
+ * 基于用户自有 REST API 的 QueryCache 远程适配器。
8
+ *
9
+ * @remarks
10
+ * **transport 归本类,不归 handler。** handler 只做协议映射:`request()` 产出请求描述,
11
+ * `parse()` 解码响应体。发请求、注入 auth、提取状态码、分类错误、翻页、分块全在这一侧。
12
+ * 让 handler 自己发请求的话,AC#12 / #13 / #16 三条契约的担保人就变成接入方,
13
+ * 本包只能退化成文档劝告。
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * const rxdb = new RxDB({
18
+ * dbName: 'app',
19
+ * entities: [Recipe],
20
+ * sync: { type: SyncType.QueryCache, local: { adapter: 'sqlite' }, remote: { adapter: 'http' } }
21
+ * });
22
+ * // 读引擎随插件走(`@aiao/rxdb-plugin-querycache`),漏装时 `connect()` 直接拒绝
23
+ * rxdb.use(rxDBPluginQueryCache);
24
+ * rxdb.adapter('http', db => new RxDBAdapterHttp(db, {
25
+ * baseUrl: 'https://api.example.com',
26
+ * handlers: {
27
+ * onFetchMetadata: {
28
+ * request: ctx => ({ url: `${ctx.entityName}/metadata`, method: 'POST', body: ctx }),
29
+ * parse: body => body as QueryCacheEntityMetadata[]
30
+ * },
31
+ * onFindByIds: {
32
+ * request: ctx => ({ url: `${ctx.entityName}`, method: 'POST', body: { ids: ctx.ids } }),
33
+ * parse: body => body as unknown[]
34
+ * }
35
+ * }
36
+ * }));
37
+ * ```
38
+ */
39
+ export declare class RxDBAdapterHttp extends RxDBAdapterRemoteBase implements IRxDBAdapter {
40
+ #private;
41
+ readonly options: HttpAdapterOptions;
42
+ readonly name = "http";
43
+ /** 已校验的数值配置,构造期定型 */
44
+ readonly config: HttpNumericConfig;
45
+ /**
46
+ * 创建实体。仅在配置了 `onCreate` 时**存在**。
47
+ *
48
+ * @remarks
49
+ * `QueryCacheEngine` 用 `if (!this.remoteAdapter.create)` 做特性探测,所以「不支持」
50
+ * 必须表现为**属性缺席**,不能是一个总在抛错的方法——后者会让探测判 `true`,
51
+ * AC#4 那句清晰的「Remote adapter does not support create」变成运行期意外。
52
+ *
53
+ * 因此这里只 `declare` 类型,实体由构造期的 `#installWriteDucks()` 按需挂上。
54
+ */
55
+ create?: <R>(entityName: string, data: R) => Observable<R>;
56
+ /** 更新实体。仅在配置了 `onUpdate` 时存在,理由同 {@link RxDBAdapterHttp.create}。 */
57
+ update?: <R>(entityName: string, id: string, data: Partial<R>) => Observable<R>;
58
+ /** 删除实体。仅在配置了 `onDelete` 时存在,理由同 {@link RxDBAdapterHttp.create}。 */
59
+ delete?: (entityName: string, ids: string | string[]) => Observable<void>;
60
+ /**
61
+ * 变更通知通道此刻**要不要**跑,由 {@link RxDBAdapterHttp.startChangeFeed} /
62
+ * {@link RxDBAdapterHttp.stopChangeFeed} 翻转。
63
+ *
64
+ * @returns 未配 `changeFeed` 时恒为 `false`
65
+ *
66
+ * @remarks
67
+ * 这是**意图**,不是连接状态:通道正在退避重连、或所在运行时压根没有 `EventSource` 时,
68
+ * 它照样是 `true`。想知道连接本身的死活,看 `changeFeed.onUnavailable` 的诊断上报——
69
+ * `EventSource` 不暴露状态码,一个从这里返回的布尔值答不了「为什么没连上」,
70
+ * 只会把两种问题混成一种。
71
+ *
72
+ * (夹在写 duck 与构造函数之间是 lint `member-ordering` 的要求:访问器排在字段之后、
73
+ * 构造函数之前。语义上它属于下面那组运行时开关。)
74
+ */
75
+ get changeFeedEnabled(): boolean;
76
+ /**
77
+ * @param rxdb - 宿主 RxDB 实例
78
+ * @param options - baseUrl、handlers 与可选的数值/认证配置
79
+ * @throws HttpConfigError baseUrl 为空、缺必选 handler,或任一数值配置不是 finite 正整数
80
+ */
81
+ constructor(rxdb: RxDB, options: HttpAdapterOptions);
82
+ /**
83
+ * 不建立长连接,只做本地校验。
84
+ *
85
+ * @remarks
86
+ * **不发探测请求**:远端此刻不可达不代表配置错,那是查询期的网络错误,
87
+ * 交给 `isNetworkError` 分类。想在启动时确认可达性的应用自己调
88
+ * {@link RxDBAdapterHttp.isTableExisted},那是显式选择而非框架行为。
89
+ *
90
+ * **配了 `changeFeed` 才有长连接**,且它建在最后:校验没过的 `connect()` 不该留下一条
91
+ * 活着的通知连接。通道自身连不上不影响本方法的返回——它按 US-023 AC#17 只诊断不抛错。
92
+ *
93
+ * 通道走的是{@link RxDBAdapterHttp.changeFeedEnabled}这个**意图位**而不是「配了就开」:
94
+ * 调用方手动 {@link RxDBAdapterHttp.stopChangeFeed} 掉的通道,不该被下一次重连
95
+ * (切后端、纪元交替都会走到这里)悄悄复活。
96
+ *
97
+ * @returns 适配器自身
98
+ * @throws HttpUnsupportedWireTypeError 已注册实体声明了 bigint / binary 字段
99
+ */
100
+ connect(): Promise<IRxDBAdapter>;
101
+ /**
102
+ * 取消进行中的请求。
103
+ *
104
+ * @remarks
105
+ * 中止走 **error 通道**:complete 一个没发射过的 Observable 会让 core 的 `forkJoin`
106
+ * 静默产出「远端零条」,整表判成孤儿,比抛错危险得多。
107
+ *
108
+ * **已发出的写请求不回滚**——HTTP 没有事务,假装能回滚比不回滚更危险。
109
+ *
110
+ * 顺带清空条件请求缓存(AC#28):那些响应绑定的是断开前的认证身份与远端状态,
111
+ * 跨越一次断开继续复用,等于让重连后的第一批查询读到上一段连接的世界。
112
+ *
113
+ * 变更通知通道一并停掉,**含待执行的重连**:只关连接不取消定时器的话,退避窗口里的那次
114
+ * 重连会在断开之后把连接重新建起来。
115
+ *
116
+ * 但{@link RxDBAdapterHttp.changeFeedEnabled}**不动**:断开是生命周期事件,不是调用方
117
+ * 改了主意。把它一并清掉的话,一次重连就会把用户「我要收通知」的选择吃掉。
118
+ */
119
+ disconnect(): Promise<void>;
120
+ /**
121
+ * 接通变更通知通道。
122
+ *
123
+ * @remarks
124
+ * 幂等:已经开着时只是重建连接(`start()` 自带「先收口上一条」),不会留下第二条。
125
+ * 连上之后照常触发一次全量失效(D7)——「从这一刻起我能收到变更了」对首次连接与
126
+ * 重新接通是同一句话。
127
+ *
128
+ * **连接状态先判**,与 {@link RxDBAdapterHttp.version} 同一条口径:在一个已经
129
+ * `disconnect()` 的适配器上开出一条活着的 SSE,是实打实的资源泄漏。
130
+ *
131
+ * 这里判的是 `#connected` 而不是 `#assertConnected` 那一位。**「从未 `connect()`」
132
+ * 与「已 `disconnect()`」在这个方法上是同一件事**:两种情况下都还没有一次成功的
133
+ * `connect()`,也就意味着 `#assertConfiguredEntitiesSupported()` 还没跑过——
134
+ * 用 `new` + `startChangeFeed()` 就能绕开线格式校验、在一个连不连得上都不知道的
135
+ * 适配器上开出真连接,还会对着一份尚未确认可用的实体表做 D7 全量失效。
136
+ *
137
+ * @throws HttpDisconnectedError 尚未 `connect()`,或已 `disconnect()`
138
+ * @throws HttpUnsupportedOperationError 未配置 `changeFeed`
139
+ */
140
+ startChangeFeed(): void;
141
+ /**
142
+ * 断开变更通知通道,**含待执行的重连**。
143
+ *
144
+ * @remarks
145
+ * 断开期间远端的变更没有人会补发,重新
146
+ * {@link RxDBAdapterHttp.startChangeFeed} 时的那次全量失效才是补课的地方。
147
+ *
148
+ * 与 {@link RxDBAdapterHttp.startChangeFeed} 不同,本方法**不判断开状态**:已经
149
+ * `disconnect()` 的适配器上通道本来就停着,两条路通向同一个终点,为此抛错只会让
150
+ * 「关掉开关」这种收尾动作平白需要一个 try。幂等的停止不是兜底——它没有掩盖任何差异。
151
+ *
152
+ * @throws HttpUnsupportedOperationError 未配置 `changeFeed`
153
+ */
154
+ stopChangeFeed(): void;
155
+ /**
156
+ * 返回**远端服务端**版本。
157
+ *
158
+ * @remarks
159
+ * 与 sqlite / pglite / supabase 三家口径一致(都返回后端引擎版本)。HTTP 没有内建
160
+ * RPC,因此靠可选的 `onVersion` handler;未配置时抛错而**不**回落到本包
161
+ * `package.json` 的版本号——那是拿适配器版本冒充后端版本。
162
+ *
163
+ * **断开状态先判**:已 `disconnect()` 的适配器上,「未配 `onVersion`」不是调用方
164
+ * 此刻该看见的答案——配置问题下次连上仍在,而生命周期问题是当下这一次调用的实情。
165
+ * 反过来(先判 handler)还会让下面那句 `#assertConnected` 在缺 handler 的路径上
166
+ * 永远走不到,而那条路径正是它存在的两个理由之一(另一个是空 id 列表的 `findByIds`)。
167
+ *
168
+ * @throws HttpDisconnectedError 适配器已断开
169
+ * @throws HttpUnsupportedOperationError 未配置 `onVersion`
170
+ */
171
+ version(): Promise<string>;
172
+ /**
173
+ * 按**远端资源可达性**回答,不恒 `true` 蒙混。
174
+ *
175
+ * @remarks
176
+ * `2xx` → `true`,`404` → `false`,其余状态码与传输失败 → 抛错。
177
+ * 抛错而不是返回 `false` 是刻意的:「不知道」和「不存在」必须区分,
178
+ * 把 500 读成「表不存在」会让调用方基于一个假答案做决定。
179
+ *
180
+ * 未配 `onIsTableExisted` 时复用 `onFetchMetadata` 的 `limit: 1` 探测。
181
+ *
182
+ * @param EntityType - 要检查的实体类
183
+ */
184
+ isTableExisted(EntityType: EntityType): Promise<boolean>;
185
+ /**
186
+ * 拉取满足 `query` 的全部实体元数据。
187
+ *
188
+ * @remarks
189
+ * **恰好发射一次再 complete**(AC#23)。翻页循环整体落在一个 `Promise` 里,
190
+ * `from(promise)` 天然满足这条;逐页发射会让调用方的 `forkJoin` 只留最后一页,
191
+ * 前面各页当场丢失,那些 id 随即被判成「远端已删除」。
192
+ *
193
+ * @param entityName - 实体名
194
+ * @param query - 查询条件,原样透传给 handler
195
+ */
196
+ fetchMetadata(entityName: string, query: RuleGroup<unknown>): Observable<QueryCacheEntityMetadata[]>;
197
+ /**
198
+ * 按 id 列表批量拉取完整行。
199
+ *
200
+ * @remarks
201
+ * 与 {@link RxDBAdapterHttp.fetchMetadata} 同款发射契约(AC#33):所有块合并后
202
+ * 恰好发射一次再 complete。逐块发射丢掉的是要写进本地缓存的完整行。
203
+ *
204
+ * @param entityName - 实体名
205
+ * @param ids - 需要拉取的 id 列表
206
+ */
207
+ findByIds<R>(entityName: string, ids: string[]): Observable<R[]>;
208
+ /**
209
+ * @throws HttpUnsupportedOperationError 总是
210
+ *
211
+ * @remarks
212
+ * QueryCache 的主仓储由 core 用**本地**适配器组装,本类这一侧没有可返回的东西。
213
+ *
214
+ * 这一组连形参都不声明(同 {@link RxDBAdapterHttp.pullChanges}):函数少写形参照样
215
+ * 满足 `IRxDBAdapter`,而留一个只为凑签名的 `_entities` 会让读者以为它被用上了。
216
+ */
217
+ getRepository<E extends EntityType, RT extends IRepository<E> = IRepository<E>>(): RT;
218
+ /** @throws HttpUnsupportedOperationError 总是(Full/Filter 写路径,v1 无 Full-sync) */
219
+ saveMany<E extends EntityType>(): Promise<InstanceType<E>[]>;
220
+ /** @throws HttpUnsupportedOperationError 总是(同 {@link RxDBAdapterHttp.saveMany}) */
221
+ removeMany<E extends EntityType>(): Promise<InstanceType<E>[]>;
222
+ /**
223
+ * @throws HttpUnsupportedOperationError 总是
224
+ *
225
+ * @remarks
226
+ * 不影响 QueryCache 的批量写:`EntityManager.mutations` 判定为 QueryCache 批后走
227
+ * `#mutations_query_cache` 的 remote-then-local,根本不经过这里。
228
+ */
229
+ mutations<E extends EntityType>(): Promise<InstanceType<E>[]>;
230
+ /**
231
+ * @throws HttpChangelogUnsupportedError 总是
232
+ *
233
+ * @remarks
234
+ * 返回空数组**算失败**:Full-sync 会把它读成「远端无变更」并覆盖本地认知。
235
+ * v1 没有 changelog,unsupported throw 是唯一诚实行为。
236
+ */
237
+ pullChanges(): Promise<RemoteChange[]>;
238
+ /** @throws HttpChangelogUnsupportedError 总是(返回 `0` 同样会被读成「远端无变更」) */
239
+ getChangeCount(): Promise<{
240
+ count: number;
241
+ latestChangeId: number;
242
+ }>;
243
+ /** @throws HttpChangelogUnsupportedError 总是 */
244
+ mergeChanges(): Promise<RemoteMergeResult | number | void>;
245
+ }
246
+ declare module '@aiao/rxdb' {
247
+ interface RxDBAdapters {
248
+ [ADAPTER_NAME]: RxDBAdapterHttp;
249
+ }
250
+ }
251
+ //# sourceMappingURL=RxDBAdapterHttp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RxDBAdapterHttp.d.ts","sourceRoot":"","sources":["../src/RxDBAdapterHttp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ import { HttpChangeFeedOptions } from './http.interface.js';
2
+ /** 参与 D7 全量失效的实体标识 */
3
+ export interface ChangeFeedEntity {
4
+ name: string;
5
+ namespace: string;
6
+ }
7
+ /**
8
+ * {@link HttpChangeFeed} 与适配器之间的全部接触面。
9
+ *
10
+ * @remarks
11
+ * 三个函数而不是一个适配器引用:通道拿不到适配器,也就无从调用它的读写口——
12
+ * 结构隔离用类型钉死,比在评审里叮嘱可靠。
13
+ */
14
+ export interface ChangeFeedHost {
15
+ /** 已解析好的绝对 URL */
16
+ url: string;
17
+ options: HttpChangeFeedOptions;
18
+ /** 本机 clientId,用于自回声抑制(D6);每次现读,登录后被替换也跟得上 */
19
+ clientId: () => string | undefined;
20
+ /** 当前走本适配器 remote 槽位的实体(D7 的「已订阅实体」) */
21
+ entities: () => readonly ChangeFeedEntity[];
22
+ /** 失效上报口;签名里没有任何参数能承载行数据(D8) */
23
+ invalidate: (entity: string, namespace: string) => void;
24
+ }
25
+ /**
26
+ * SSE 变更通知通道。
27
+ *
28
+ * @remarks
29
+ * **连接成功 = 全量失效。** `onopen` 不区分首次连接与重连(D7):两者对客户端的含义完全一样
30
+ * ——「从这一刻起我能收到变更了,而这一刻之前的事我一无所知」。
31
+ *
32
+ * **失败绝不外泄到查询路径。** 本类不抛错、不产生 `NetworkOfflineError`:一条断掉的通知连接
33
+ * 不代表离线(可能只是后端没实现该端点),把它算进 `offlineFallback` 的判定,会让一次
34
+ * 查询在网络完好时被降级成读缓存。唯一的出口是
35
+ * {@link HttpChangeFeedOptions.onUnavailable} 这个诊断回调。
36
+ */
37
+ export declare class HttpChangeFeed {
38
+ #private;
39
+ /**
40
+ * @param host - 通道与适配器之间的接触面
41
+ * @throws HttpConfigError 解析后的 URL 解析不出来,或退避配置不是 finite 正整数 / 上限小于起步值
42
+ */
43
+ constructor(host: ChangeFeedHost);
44
+ /**
45
+ * 建立连接。
46
+ *
47
+ * @remarks
48
+ * 可重复调用:先收口上一条连接再新建,与适配器 `connect()` 的「重连隐含断开」同一口径。
49
+ * 少了这一步,重复 `connect()` 会让旧连接失去最后一个引用却仍在收消息。
50
+ */
51
+ start(): void;
52
+ /** 关闭连接并取消待执行的重连。 */
53
+ stop(): void;
54
+ }
55
+ /**
56
+ * 校验通知端点。
57
+ *
58
+ * @param url - 用户配置的 `changeFeed.url`
59
+ * @throws HttpConfigError 不是非空字符串
60
+ *
61
+ * @remarks
62
+ * 空串会被拼成 `baseUrl/`,于是通知连接打到 API 根路径上——那里多半返回 HTML,
63
+ * 表现为一条永远在退避重连的连接,而不是一句配置错误。
64
+ */
65
+ export declare const assertChangeFeedUrl: (url: string) => void;
66
+ //# sourceMappingURL=change-feed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"change-feed.d.ts","sourceRoot":"","sources":["../src/change-feed.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,qBAAqB,EAAmC,MAAM,qBAAqB,CAAC;AAsDlG,sBAAsB;AACtB,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B,kBAAkB;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,qBAAqB,CAAC;IAC/B,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IACnC,wCAAwC;IACxC,QAAQ,EAAE,MAAM,SAAS,gBAAgB,EAAE,CAAC;IAC5C,gCAAgC;IAChC,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CACzD;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,cAAc;;IAkBzB;;;OAGG;gBACS,IAAI,EAAE,cAAc;IAoBhC;;;;;;OAMG;IACH,KAAK,IAAI,IAAI;IAUb,qBAAqB;IACrB,IAAI,IAAI,IAAI;CA4Lb;AA0ED;;;;;;;;;GASG;AACH,eAAO,MAAM,mBAAmB,GAAI,KAAK,MAAM,KAAG,IAIjD,CAAC"}
@@ -0,0 +1,29 @@
1
+ import { FindByIdsHandler, HttpNumericConfig } from './http.interface.js';
2
+ import { HttpTransport } from './transport.js';
3
+ /** 分块所需的协作方 */
4
+ export interface ChunkingDeps {
5
+ transport: HttpTransport;
6
+ handler: FindByIdsHandler;
7
+ config: Pick<HttpNumericConfig, 'idChunkSize'>;
8
+ }
9
+ /**
10
+ * 分块拉取整行并按块序合并。
11
+ *
12
+ * @remarks
13
+ * **串行**,与 supabase 的 `#findByIdsInChunks` 同款:首块出错即停,不再打后续请求。
14
+ * 并发能快一点,但会在远端已经出问题时继续加压,而结果反正是整体 reject。
15
+ *
16
+ * 返回 `Promise` 的理由同 {@link fetchAllMetadataPages}:调用方 `from(promise)` 一层就
17
+ * 天然「恰好发射一次再 complete」(AC#33)。逐块发射会让 `forkJoin` 只留最后一块,
18
+ * 前面各块拉回来的完整行当场丢失——而那些行正是要写进本地缓存的数据。
19
+ *
20
+ * @param deps - transport、handler 与分块尺寸
21
+ * @param ctx - 实体名与完整 id 列表
22
+ * @returns 各块合并后的原始行,顺序即块序
23
+ * @throws 任一块失败即整体抛出该块的错误,**不**降级成空块继续合并
24
+ */
25
+ export declare const findByIdsInChunks: (deps: ChunkingDeps, ctx: {
26
+ entityName: string;
27
+ ids: string[];
28
+ }) => Promise<unknown[]>;
29
+ //# sourceMappingURL=chunking.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunking.d.ts","sourceRoot":"","sources":["../src/chunking.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAoB,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACjG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEpD,eAAe;AACf,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,aAAa,CAAC;IACzB,OAAO,EAAE,gBAAgB,CAAC;IAC1B,MAAM,EAAE,IAAI,CAAC,iBAAiB,EAAE,aAAa,CAAC,CAAC;CAChD;AAWD;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,iBAAiB,GAC5B,MAAM,YAAY,EAClB,KAAK;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,EAAE,CAAA;CAAE,KACzC,OAAO,CAAC,OAAO,EAAE,CAmBnB,CAAC"}