@rei-standard/amsg-server 2.6.0-next.2 → 2.6.0-next.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/LICENSE +21 -0
- package/README.md +599 -24
- package/dist/adapters/d1.d.ts +416 -0
- package/dist/adapters/factory.d.ts +39 -0
- package/dist/adapters/interface.d.ts +306 -0
- package/dist/adapters/neon.d.ts +88 -0
- package/dist/adapters/pg-shared.d.ts +161 -0
- package/dist/adapters/pg.d.ts +84 -0
- package/dist/adapters/schema.d.ts +57 -0
- package/dist/adapters/schema.sqlite.d.ts +49 -0
- package/dist/chunk-7NMQFTDJ.cjs +148 -0
- package/dist/chunk-E7OWP3VL.cjs +135 -0
- package/dist/chunk-GN44PST5.mjs +148 -0
- package/dist/chunk-KOJQVOYS.mjs +5847 -0
- package/dist/chunk-QHVID3V2.cjs +5847 -0
- package/dist/chunk-ZGGF4GMA.mjs +135 -0
- package/dist/cloudflare/single-user-worker.d.ts +37 -0
- package/dist/cloudflare.cjs +39 -2
- package/dist/cloudflare.d.ts +10 -2
- package/dist/cloudflare.mjs +40 -3
- package/dist/handlers/cancel-message.d.ts +3 -0
- package/dist/handlers/capabilities.d.ts +4 -0
- package/dist/handlers/client-state.d.ts +7 -0
- package/dist/handlers/get-message.d.ts +3 -0
- package/dist/handlers/get-user-key.d.ts +3 -0
- package/dist/handlers/init-tenant.d.ts +23 -0
- package/dist/handlers/llm-credentials.d.ts +5 -0
- package/dist/handlers/messages.d.ts +3 -0
- package/dist/handlers/outbox.d.ts +7 -0
- package/dist/handlers/push-subscription.d.ts +5 -0
- package/dist/handlers/schedule-message.d.ts +3 -0
- package/dist/handlers/send-notifications.d.ts +3 -0
- package/dist/handlers/single-user-init.d.ts +13 -0
- package/dist/handlers/update-message.d.ts +3 -0
- package/dist/handlers/vapid-public-key.d.ts +19 -0
- package/dist/index.cjs +133 -44
- package/dist/index.d.cts +131 -766
- package/dist/index.d.ts +131 -766
- package/dist/index.mjs +125 -36
- package/dist/lib/agentic-fire.d.ts +108 -0
- package/dist/lib/client-state-store.d.ts +80 -0
- package/dist/lib/constant-time.d.ts +14 -0
- package/dist/lib/db-errors.d.ts +10 -0
- package/dist/lib/encryption.d.ts +48 -0
- package/dist/lib/errors.d.ts +215 -0
- package/dist/lib/llm-credentials-store.d.ts +147 -0
- package/dist/lib/llm.d.ts +1 -0
- package/dist/lib/message-processor.d.ts +73 -0
- package/dist/lib/outbox-store.d.ts +95 -0
- package/dist/lib/push-subscription-store.d.ts +94 -0
- package/dist/lib/recurrence.d.ts +51 -0
- package/dist/lib/request.d.ts +182 -0
- package/dist/lib/result-emitter.d.ts +54 -0
- package/dist/lib/run-tick.d.ts +115 -0
- package/dist/lib/schema-version.d.ts +50 -0
- package/dist/lib/state-accessors.d.ts +70 -0
- package/dist/lib/state-chunks.d.ts +55 -0
- package/dist/lib/task-projection.d.ts +92 -0
- package/dist/lib/validation.d.ts +97 -0
- package/dist/lib/version.d.ts +7 -0
- package/dist/lib/webcrypto-utils.d.ts +1 -0
- package/dist/lib/webpush-webcrypto.d.ts +78 -0
- package/dist/{neon-BFUS25UX.cjs → neon-KP2CPA57.cjs} +110 -10
- package/dist/{neon-CU5N3CSW.mjs → neon-ZIMHALYI.mjs} +106 -6
- package/dist/{pg-QO6NKTGL.mjs → pg-QGC2XHUT.mjs} +94 -5
- package/dist/{pg-IIH3M4OM.cjs → pg-RZPQGXR3.cjs} +98 -9
- package/dist/single-user.d.ts +95 -0
- package/dist/tenant/blob-store.d.ts +30 -0
- package/dist/tenant/context.d.ts +47 -0
- package/dist/tenant/single-user-context.d.ts +29 -0
- package/dist/tenant/token.d.ts +26 -0
- package/package.json +3 -3
- package/dist/chunk-5ENJGVZX.mjs +0 -72
- package/dist/chunk-GN5QTMCL.cjs +0 -1870
- package/dist/chunk-PVNLB6TF.mjs +0 -1870
- package/dist/chunk-RGECD4OH.cjs +0 -72
- package/dist/cloudflare-B-E51VjP.d.cts +0 -2848
- package/dist/cloudflare-B-E51VjP.d.ts +0 -2848
- package/dist/cloudflare.d.cts +0 -2
- package/dist/neon-CJl66EGy.d.cts +0 -251
- package/dist/neon-DYvGnCzx.d.ts +0 -251
- package/dist/pg-07S-u_H4.d.cts +0 -246
- package/dist/pg-Du-pN_UT.d.ts +0 -246
- package/dist/schema-C8OnYk6j.d.cts +0 -74
- package/dist/schema-C8OnYk6j.d.ts +0 -74
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create a D1 adapter from a Cloudflare D1 binding (env.DB).
|
|
3
|
+
* @param {{ prepare: (sql: string) => any }} db
|
|
4
|
+
* @returns {import('./interface.js').DbAdapter}
|
|
5
|
+
*/
|
|
6
|
+
export function createD1Adapter(db: {
|
|
7
|
+
prepare: (sql: string) => any;
|
|
8
|
+
}): import("./interface.js").DbAdapter;
|
|
9
|
+
export class D1Adapter {
|
|
10
|
+
/** @param {{ prepare: (sql: string) => any }} db - Cloudflare D1 binding */
|
|
11
|
+
constructor(db: {
|
|
12
|
+
prepare: (sql: string) => any;
|
|
13
|
+
});
|
|
14
|
+
/** @private */
|
|
15
|
+
private _db;
|
|
16
|
+
/** @private */
|
|
17
|
+
private _now;
|
|
18
|
+
/** @private */
|
|
19
|
+
private _iso;
|
|
20
|
+
/**
|
|
21
|
+
* 组一组带 `IN (...)` 的语句:值多到一条塞不下时,按 D1 的绑定参数上限切成
|
|
22
|
+
* 几条(见 chunkForBoundParams)。
|
|
23
|
+
*
|
|
24
|
+
* 只有拿得到事务的绑定才切。没有 batch() 的绑定(测试 shim、自定义适配器,
|
|
25
|
+
* 都不是真实 D1)原样发一条不切的语句:切批是为 D1 那条上限服务的,这类绑
|
|
26
|
+
* 定不受它约束,切了反倒会因为没有事务而多出一个「写一半」的中间态。
|
|
27
|
+
*
|
|
28
|
+
* @private
|
|
29
|
+
* @param {(placeholders: string) => string} buildSql - 拿占位符串(`?, ?, ?`)组 SQL
|
|
30
|
+
* @param {unknown[]} leadingParams - IN 列表之前的固定参数,按出现顺序
|
|
31
|
+
* @param {unknown[]} values - IN 列表里的全部值
|
|
32
|
+
* @returns {any[]} 已经 bind 好的语句
|
|
33
|
+
*/
|
|
34
|
+
private _prepareInClauseStatements;
|
|
35
|
+
/**
|
|
36
|
+
* 带 `IN (...)` 的批量写,切成几条也仍然是一次原子操作。
|
|
37
|
+
*
|
|
38
|
+
* 一条语句本来天然原子,拆开之后这份保证得自己补回来,否则就会多出「只删掉
|
|
39
|
+
* 前 99 个」「只 ack 了前 98 条」这类比原问题更难查的中间态。D1 的 batch()
|
|
40
|
+
* 是隐式事务——其中一条失败,整批回滚——正好接住这件事。
|
|
41
|
+
*
|
|
42
|
+
* 一条就够时照旧单发,跟切批以前走的是同一条路,常见调用(一次三五个 id)
|
|
43
|
+
* 的行为一个字节都没变。
|
|
44
|
+
*
|
|
45
|
+
* @private
|
|
46
|
+
* @param {(placeholders: string) => string} buildSql
|
|
47
|
+
* @param {unknown[]} leadingParams
|
|
48
|
+
* @param {unknown[]} values
|
|
49
|
+
* @returns {Promise<number>} 各批影响行数之和
|
|
50
|
+
*/
|
|
51
|
+
private _runInClauseWrite;
|
|
52
|
+
initSchema(): Promise<{
|
|
53
|
+
columnsCreated: number;
|
|
54
|
+
indexesCreated: number;
|
|
55
|
+
indexesFailed: number;
|
|
56
|
+
columns: any[];
|
|
57
|
+
indexes: ({
|
|
58
|
+
name: string;
|
|
59
|
+
status: string;
|
|
60
|
+
description: string;
|
|
61
|
+
critical: boolean;
|
|
62
|
+
error?: undefined;
|
|
63
|
+
} | {
|
|
64
|
+
name: string;
|
|
65
|
+
status: string;
|
|
66
|
+
description: string;
|
|
67
|
+
critical: boolean;
|
|
68
|
+
error: any;
|
|
69
|
+
})[];
|
|
70
|
+
}>;
|
|
71
|
+
/**
|
|
72
|
+
* 活库里现在实际有哪些表 / 列 / 索引。
|
|
73
|
+
*
|
|
74
|
+
* 只读不写,纯粹如实回报:拿它跟这一版需要的清单对照的活儿在
|
|
75
|
+
* lib/schema-version.js(`getSchemaVersion` / `ensureSchema`)。库升级后表结
|
|
76
|
+
* 构变了而老部署没跑过 initSchema 时,cron 会每分钟静默挂在缺的那一列上,
|
|
77
|
+
* 界面上一切正常——这个方法就是让宿主查得出来。
|
|
78
|
+
*
|
|
79
|
+
* @returns {Promise<{ tables: Record<string, string[]>, indexes: string[] }>}
|
|
80
|
+
*/
|
|
81
|
+
describeSchema(): Promise<{
|
|
82
|
+
tables: Record<string, string[]>;
|
|
83
|
+
indexes: string[];
|
|
84
|
+
}>;
|
|
85
|
+
dropSchema(): Promise<void>;
|
|
86
|
+
createTask(params: any): Promise<any>;
|
|
87
|
+
/**
|
|
88
|
+
* 建新任务的同时取消旧的那条(`POST /schedule-message` 的 supersedesUuid)。
|
|
89
|
+
*
|
|
90
|
+
* 两条语句走一次 batch(D1 的隐式事务 + 单次网络往返):不会出现「旧的删了、
|
|
91
|
+
* 新的没建成」的中间态——INSERT 撞 uuid 唯一索引时整个 batch 回滚,旧行原样
|
|
92
|
+
* 留着,调用方按既有的 409 冲突路径处理。
|
|
93
|
+
*
|
|
94
|
+
* @param {import('./interface.js').InsertTaskParams} params
|
|
95
|
+
* @param {string} supersedesUuid - 要取消的旧任务 uuid(同一 user_id 下)
|
|
96
|
+
* @returns {Promise<Object>} createTask 的返回行 + `superseded`(旧行是否
|
|
97
|
+
* 真的被删掉;false = 旧行本就不存在)
|
|
98
|
+
*/
|
|
99
|
+
createTaskSuperseding(params: import("./interface.js").InsertTaskParams, supersedesUuid: string): Promise<any>;
|
|
100
|
+
getTaskByUuid(uuid: any, userId: any): Promise<any>;
|
|
101
|
+
getTaskByUuidOnly(uuid: any): Promise<any>;
|
|
102
|
+
/**
|
|
103
|
+
* 这条 uuid 现在是什么状态——不限用户,也不限状态(getTaskByUuidOnly 只看
|
|
104
|
+
* pending 行)。`runTask` 用它把「这条已经跑完了」和「压根没这条」分开回报。
|
|
105
|
+
*
|
|
106
|
+
* @param {string} uuid
|
|
107
|
+
* @returns {Promise<{ status: string }|null>}
|
|
108
|
+
*/
|
|
109
|
+
getTaskStatusByUuidOnly(uuid: string): Promise<{
|
|
110
|
+
status: string;
|
|
111
|
+
} | null>;
|
|
112
|
+
updateTaskById(taskId: any, updates: any): Promise<any>;
|
|
113
|
+
updateTaskByUuid(uuid: any, userId: any, encryptedPayload: any, extraFields: any): Promise<{
|
|
114
|
+
uuid: any;
|
|
115
|
+
updated_at: string;
|
|
116
|
+
}>;
|
|
117
|
+
deleteTaskById(taskId: any): Promise<boolean>;
|
|
118
|
+
deleteTaskByUuid(uuid: any, userId: any): Promise<boolean>;
|
|
119
|
+
getPendingTasks(limit?: number): Promise<any>;
|
|
120
|
+
/**
|
|
121
|
+
* 领取一条到点的任务:在 lease_until 上写下「这条归我管到什么时候」,
|
|
122
|
+
* 本次投递期间别的 tick 领不走它。
|
|
123
|
+
*
|
|
124
|
+
* 租约写在自己的列上,next_send_at 全程不动——那一列是用户设的触发时刻,
|
|
125
|
+
* 任务列表要读它、循环任务推进下一次也要拿它当基准。
|
|
126
|
+
*
|
|
127
|
+
* 两个 tick 抢同一行时只有一个能改到行,另一个拿到 changes = 0,据此跳过。
|
|
128
|
+
* WHERE 里的两个条件各管一件事:
|
|
129
|
+
* - lease_until 为空或已过期:没人正在跑这条。领了任务的 tick 中途没了
|
|
130
|
+
* 也不会把行焊死,租约到期后自然可以被接手。
|
|
131
|
+
* - next_send_at 等于读这行时看到的值:读出来之后用户又改了排期的话,
|
|
132
|
+
* 这一跳就不该再按旧时刻发。
|
|
133
|
+
*
|
|
134
|
+
* 不加一个 'sending' 状态来表达「正在跑」:建表语句里 status 有
|
|
135
|
+
* CHECK (status IN ('pending','sent','failed')),加值要重建表。
|
|
136
|
+
*
|
|
137
|
+
* expectedNextSendAt 按读到的原样比对,不做时区归一化——老部署里可能还留
|
|
138
|
+
* 着非归一化写法的行(如 +08:00 结尾),归一化后反而对不上,那条任务会永
|
|
139
|
+
* 远领不到。
|
|
140
|
+
*
|
|
141
|
+
* 带 serializeGroup 时多一道分组门:同一分组里已经有别的行拿着未到期的租
|
|
142
|
+
* 约,这条就领不走(同一分组同时只跑一条)。判定和写租约在同一条 UPDATE
|
|
143
|
+
* 里完成,「先查再占」的空档天然不存在——两个 tick 同时来,只有一个改得动
|
|
144
|
+
* 行。分组门只看租约,不看 `retry_after`:等着重试的任务其实闲着,不该把
|
|
145
|
+
* 同分组的其他任务一起堵住。
|
|
146
|
+
*
|
|
147
|
+
* @param {number} taskId
|
|
148
|
+
* @param {string} expectedNextSendAt - 读这行时拿到的 next_send_at 原值
|
|
149
|
+
* @param {string|Date} leaseUntil - 租期末尾
|
|
150
|
+
* @param {string|null} [serializeGroup] - 串行分组标识;空表示不参与分组串行
|
|
151
|
+
* @returns {Promise<boolean>} true = 领到了;false = 别人正拿着租约、同分组
|
|
152
|
+
* 有任务正在跑、排期被改过、或行已不是 pending
|
|
153
|
+
*/
|
|
154
|
+
claimTask(taskId: number, expectedNextSendAt: string, leaseUntil: string | Date, serializeGroup?: string | null): Promise<boolean>;
|
|
155
|
+
/**
|
|
156
|
+
* 投递期间的租约续期(run-tick 的心跳用)。只在行仍是 pending 且确实持有
|
|
157
|
+
* 租约(lease_until 非空)时生效——收尾把租约放掉之后,迟到的心跳不会把
|
|
158
|
+
* 租约复活。
|
|
159
|
+
*
|
|
160
|
+
* @param {number} taskId
|
|
161
|
+
* @param {string|Date} leaseUntil - 新的租期末尾
|
|
162
|
+
* @returns {Promise<boolean>} true = 续上了
|
|
163
|
+
*/
|
|
164
|
+
renewTaskLease(taskId: number, leaseUntil: string | Date): Promise<boolean>;
|
|
165
|
+
listTasks(userId: any, opts?: {}): Promise<{
|
|
166
|
+
tasks: any;
|
|
167
|
+
total: number;
|
|
168
|
+
}>;
|
|
169
|
+
cleanupOldTasks(days?: number): Promise<any>;
|
|
170
|
+
getTaskStatus(uuid: any, userId: any): Promise<any>;
|
|
171
|
+
/**
|
|
172
|
+
* 状态 + 失败摘要(GET /message 用它把「为什么失败」透给已失败的行——那些
|
|
173
|
+
* 行 getTaskByUuid 读不到,payload 里的 lastError 也就够不着了)。
|
|
174
|
+
*
|
|
175
|
+
* @param {string} uuid
|
|
176
|
+
* @param {string} userId
|
|
177
|
+
* @returns {Promise<{ status: string, last_error: string|null }|null>}
|
|
178
|
+
*/
|
|
179
|
+
getTaskStatusInfo(uuid: string, userId: string): Promise<{
|
|
180
|
+
status: string;
|
|
181
|
+
last_error: string | null;
|
|
182
|
+
} | null>;
|
|
183
|
+
/**
|
|
184
|
+
* Batch upsert. Last-write-wins per (namespace, key): an entry older
|
|
185
|
+
* than the stored row (updatedAt strictly lower) is skipped; equal or
|
|
186
|
+
* newer overwrites. Values arrive pre-encrypted (the handler encrypts).
|
|
187
|
+
*
|
|
188
|
+
* `cleanups` 是删除项:在同一 batch 里先于 upsert 执行,`updated_at <= ?`
|
|
189
|
+
* 条件保证陈旧批次删不动更新写入的行。两种形态——
|
|
190
|
+
* - `{ namespace, keyPrefix, updatedAt }` 删 key 前缀下的所有行,用来清掉
|
|
191
|
+
* 大值旧写入留下的切片行(见 lib/state-chunks.js);
|
|
192
|
+
* - `{ namespace, key, updatedAt }` 删这一个 key,用来删整条状态(前缀会
|
|
193
|
+
* 连带删掉同前缀的兄弟 key,删单条必须走精确匹配)。
|
|
194
|
+
*
|
|
195
|
+
* 删掉一整条状态就是这两种各来一条(切片行走前缀、根行走精确 key),
|
|
196
|
+
* `entries` 传空数组即可——见 lib/client-state-store.js。
|
|
197
|
+
*
|
|
198
|
+
* Uses D1's batch() — one network round trip for the whole set (implicit
|
|
199
|
+
* transaction). The client calls this endpoint inside its few-seconds
|
|
200
|
+
* background window, so N sequential round trips could eat the whole
|
|
201
|
+
* window. Bindings without batch() (e.g. the sqlite test shim, custom
|
|
202
|
+
* adapters) fall back to a sequential loop.
|
|
203
|
+
*
|
|
204
|
+
* @param {string} userId
|
|
205
|
+
* @param {Array<{ namespace: string, key: string, value: string, updatedAt: number }>} entries
|
|
206
|
+
* @param {Array<{ namespace: string, key?: string, keyPrefix?: string, updatedAt: number }>} [cleanups]
|
|
207
|
+
* @returns {Promise<{ upserted: number, skipped: number, outcomes: boolean[] }>}
|
|
208
|
+
* `outcomes[i]` 对应 entries[i] 是否真的写入(changes > 0)。
|
|
209
|
+
*/
|
|
210
|
+
upsertClientState(userId: string, entries: Array<{
|
|
211
|
+
namespace: string;
|
|
212
|
+
key: string;
|
|
213
|
+
value: string;
|
|
214
|
+
updatedAt: number;
|
|
215
|
+
}>, cleanups?: Array<{
|
|
216
|
+
namespace: string;
|
|
217
|
+
key?: string;
|
|
218
|
+
keyPrefix?: string;
|
|
219
|
+
updatedAt: number;
|
|
220
|
+
}>): Promise<{
|
|
221
|
+
upserted: number;
|
|
222
|
+
skipped: number;
|
|
223
|
+
outcomes: boolean[];
|
|
224
|
+
}>;
|
|
225
|
+
/**
|
|
226
|
+
* All entries of one namespace (values still encrypted).
|
|
227
|
+
*
|
|
228
|
+
* @param {string} userId
|
|
229
|
+
* @param {string} namespace
|
|
230
|
+
* @returns {Promise<Array<{ namespace: string, key: string, value: string, updated_at: number }>>}
|
|
231
|
+
*/
|
|
232
|
+
getClientState(userId: string, namespace: string): Promise<Array<{
|
|
233
|
+
namespace: string;
|
|
234
|
+
key: string;
|
|
235
|
+
value: string;
|
|
236
|
+
updated_at: number;
|
|
237
|
+
}>>;
|
|
238
|
+
/**
|
|
239
|
+
* 例行清理:把指定命名空间下太久没更新的条目删掉(run-tick 每跳顺手调,
|
|
240
|
+
* 宿主配了 `clientStateTtl` 才会调)。
|
|
241
|
+
*
|
|
242
|
+
* 不限用户——「这个命名空间只留最近 N 天」是命名空间级的约定,单用户部署
|
|
243
|
+
* 下也就是这一个用户的行。指令由 lib/client-state-store.js 的
|
|
244
|
+
* `planClientStateCleanup` 算好(含大值切片所在的保留命名空间),这里只负
|
|
245
|
+
* 责照着删。
|
|
246
|
+
*
|
|
247
|
+
* @param {Array<{ namespace: string, updatedBefore: number }>} targets
|
|
248
|
+
* `updatedBefore` 是 epoch 毫秒,与 `updated_at` 列同一把尺子。
|
|
249
|
+
* @returns {Promise<number>} 删掉的行数
|
|
250
|
+
*/
|
|
251
|
+
cleanupClientState(targets?: Array<{
|
|
252
|
+
namespace: string;
|
|
253
|
+
updatedBefore: number;
|
|
254
|
+
}>): Promise<number>;
|
|
255
|
+
/**
|
|
256
|
+
* Wipe every entry of this user.
|
|
257
|
+
*
|
|
258
|
+
* @param {string} userId
|
|
259
|
+
* @returns {Promise<number>} rows deleted
|
|
260
|
+
*/
|
|
261
|
+
clearClientState(userId: string): Promise<number>;
|
|
262
|
+
/**
|
|
263
|
+
* 这个用户当前登记的推送订阅(密文原样返回,解密在上层)。
|
|
264
|
+
*
|
|
265
|
+
* @param {string} userId
|
|
266
|
+
* @returns {Promise<{ subscription: string, updated_at: number }|null>}
|
|
267
|
+
*/
|
|
268
|
+
getPushSubscription(userId: string): Promise<{
|
|
269
|
+
subscription: string;
|
|
270
|
+
updated_at: number;
|
|
271
|
+
} | null>;
|
|
272
|
+
/**
|
|
273
|
+
* 覆盖写这个用户的订阅。一个用户一行,没有 last-write-wins 之类的比较——
|
|
274
|
+
* 客户端拿到的新订阅永远比旧的有效,旧的那份只会 410。
|
|
275
|
+
*
|
|
276
|
+
* @param {string} userId
|
|
277
|
+
* @param {string} encryptedSubscription
|
|
278
|
+
* @param {number} updatedAt - epoch 毫秒
|
|
279
|
+
* @returns {Promise<boolean>}
|
|
280
|
+
*/
|
|
281
|
+
upsertPushSubscription(userId: string, encryptedSubscription: string, updatedAt: number): Promise<boolean>;
|
|
282
|
+
/**
|
|
283
|
+
* 删掉这个用户的订阅(设置页「停止接收推送」)。
|
|
284
|
+
*
|
|
285
|
+
* @param {string} userId
|
|
286
|
+
* @returns {Promise<boolean>} true = 确实删掉了一行
|
|
287
|
+
*/
|
|
288
|
+
deletePushSubscription(userId: string): Promise<boolean>;
|
|
289
|
+
/**
|
|
290
|
+
* 批量 upsert 这个用户的凭据(value 已是密文,加密在上层)。已存在的行
|
|
291
|
+
* 覆盖 encrypted_value 并刷 updated_at,created_at 保留首次写入的时刻。
|
|
292
|
+
*
|
|
293
|
+
* @param {string} userId
|
|
294
|
+
* @param {Array<{ credId: string, encryptedValue: string }>} entries
|
|
295
|
+
* @returns {Promise<number>} 实际写入/覆盖的行数
|
|
296
|
+
*/
|
|
297
|
+
upsertLlmCredentials(userId: string, entries: Array<{
|
|
298
|
+
credId: string;
|
|
299
|
+
encryptedValue: string;
|
|
300
|
+
}>): Promise<number>;
|
|
301
|
+
/**
|
|
302
|
+
* 按 cred_id 批量读这个用户的凭据行(`encrypted_value` 是密文,解密在上
|
|
303
|
+
* 层)。排程时的存在性检查和 fire 时的解析共用这一个口。
|
|
304
|
+
*
|
|
305
|
+
* @param {string} userId
|
|
306
|
+
* @param {string[]} credIds
|
|
307
|
+
* @returns {Promise<Array<{ cred_id: string, encrypted_value: string, updated_at: string }>>}
|
|
308
|
+
*/
|
|
309
|
+
getLlmCredentials(userId: string, credIds: string[]): Promise<Array<{
|
|
310
|
+
cred_id: string;
|
|
311
|
+
encrypted_value: string;
|
|
312
|
+
updated_at: string;
|
|
313
|
+
}>>;
|
|
314
|
+
/**
|
|
315
|
+
* 这个用户名下所有凭据的对账清单(只有 cred_id 和 updated_at,密文不出
|
|
316
|
+
* 这个方法)。按 cred_id 排序,输出稳定。
|
|
317
|
+
*
|
|
318
|
+
* @param {string} userId
|
|
319
|
+
* @returns {Promise<Array<{ cred_id: string, updated_at: string }>>}
|
|
320
|
+
*/
|
|
321
|
+
listLlmCredentials(userId: string): Promise<Array<{
|
|
322
|
+
cred_id: string;
|
|
323
|
+
updated_at: string;
|
|
324
|
+
}>>;
|
|
325
|
+
/**
|
|
326
|
+
* 删凭据。`credIds` 传数组删指定那几行;传 null 删这个用户的全部
|
|
327
|
+
* (「清空云端数据」用)。
|
|
328
|
+
*
|
|
329
|
+
* @param {string} userId
|
|
330
|
+
* @param {string[]|null} credIds
|
|
331
|
+
* @returns {Promise<number>} 删掉的行数
|
|
332
|
+
*/
|
|
333
|
+
deleteLlmCredentials(userId: string, credIds?: string[] | null): Promise<number>;
|
|
334
|
+
/**
|
|
335
|
+
* 发送前把这一批 push 落进 outbox(一次 batch)。(user_id, message_id)
|
|
336
|
+
* 唯一:重试同一 occurrence 带着同一批 messageId 再来时更新 payload、不加
|
|
337
|
+
* 第二行;客户端已经 ack 过的行不动(ack 是终态,重试不该把它拉回未读)。
|
|
338
|
+
*
|
|
339
|
+
* @param {string} userId
|
|
340
|
+
* @param {Array<{ message_id: string, task_uuid?: string|null, session_id?: string|null,
|
|
341
|
+
* message_index?: number|null, total_messages?: number|null, payload: string, created_at: number }>} rows
|
|
342
|
+
* `payload` 是整条 push JSON 的 encryptForStorage 密文。
|
|
343
|
+
* @returns {Promise<number>} 实际写入/更新的行数
|
|
344
|
+
*/
|
|
345
|
+
appendOutboxMessages(userId: string, rows: Array<{
|
|
346
|
+
message_id: string;
|
|
347
|
+
task_uuid?: string | null;
|
|
348
|
+
session_id?: string | null;
|
|
349
|
+
message_index?: number | null;
|
|
350
|
+
total_messages?: number | null;
|
|
351
|
+
payload: string;
|
|
352
|
+
created_at: number;
|
|
353
|
+
}>): Promise<number>;
|
|
354
|
+
/**
|
|
355
|
+
* 把这一批标记为「Web Push 已发出」。发送失败的段不标——delivered_at 为
|
|
356
|
+
* null 的行正是客户端最需要拉的那部分。
|
|
357
|
+
*
|
|
358
|
+
* @param {string} userId
|
|
359
|
+
* @param {string[]} messageIds
|
|
360
|
+
* @param {number} deliveredAt - epoch 毫秒
|
|
361
|
+
* @returns {Promise<number>}
|
|
362
|
+
*/
|
|
363
|
+
markOutboxDelivered(userId: string, messageIds: string[], deliveredAt: number): Promise<number>;
|
|
364
|
+
/**
|
|
365
|
+
* 把这一批还没发出去的行删掉(任务投递到一半被取消 / 顶替时用)。
|
|
366
|
+
*
|
|
367
|
+
* 只删 delivered_at 仍为 NULL 的行:已经推给设备的那几条撤不回来,行留着让
|
|
368
|
+
* 客户端照常 ack。已 ack 的行更不动。
|
|
369
|
+
*
|
|
370
|
+
* @param {string} userId
|
|
371
|
+
* @param {string[]} messageIds
|
|
372
|
+
* @returns {Promise<number>} 删掉的行数
|
|
373
|
+
*/
|
|
374
|
+
discardOutboxMessages(userId: string, messageIds: string[]): Promise<number>;
|
|
375
|
+
/**
|
|
376
|
+
* 未 ack 的行(id 升序,游标翻页)。payload 仍是密文,解密在 handler。
|
|
377
|
+
*
|
|
378
|
+
* @param {string} userId
|
|
379
|
+
* @param {number} sinceId - 上一页游标(0 = 从头)
|
|
380
|
+
* @param {number} limit
|
|
381
|
+
* @returns {Promise<Array<{ id: number, message_id: string, task_uuid: string|null,
|
|
382
|
+
* session_id: string|null, message_index: number|null, total_messages: number|null,
|
|
383
|
+
* payload: string, created_at: number, delivered_at: number|null }>>}
|
|
384
|
+
*/
|
|
385
|
+
listUnackedOutbox(userId: string, sinceId?: number, limit?: number): Promise<Array<{
|
|
386
|
+
id: number;
|
|
387
|
+
message_id: string;
|
|
388
|
+
task_uuid: string | null;
|
|
389
|
+
session_id: string | null;
|
|
390
|
+
message_index: number | null;
|
|
391
|
+
total_messages: number | null;
|
|
392
|
+
payload: string;
|
|
393
|
+
created_at: number;
|
|
394
|
+
delivered_at: number | null;
|
|
395
|
+
}>>;
|
|
396
|
+
/**
|
|
397
|
+
* 客户端确认收到这一批(幂等:已 ack 的行再 ack 不动)。
|
|
398
|
+
*
|
|
399
|
+
* @param {string} userId
|
|
400
|
+
* @param {string[]} messageIds
|
|
401
|
+
* @param {number} ackedAt - epoch 毫秒
|
|
402
|
+
* @returns {Promise<number>} 本次真正被 ack 的行数
|
|
403
|
+
*/
|
|
404
|
+
ackOutboxMessages(userId: string, messageIds: string[], ackedAt: number): Promise<number>;
|
|
405
|
+
/**
|
|
406
|
+
* outbox 的例行清理(run-tick 每跳顺手调):已 ack 的行留短一些,未 ack 的
|
|
407
|
+
* 也不无限留(Web Push TTL 上限四周,比它更老的推送谁也收不到了)。
|
|
408
|
+
*
|
|
409
|
+
* @param {{ ackedBeforeMs?: number, allBeforeMs?: number }} opts - epoch 毫秒阈值
|
|
410
|
+
* @returns {Promise<number>} 删掉的行数
|
|
411
|
+
*/
|
|
412
|
+
cleanupOutbox({ ackedBeforeMs, allBeforeMs }?: {
|
|
413
|
+
ackedBeforeMs?: number;
|
|
414
|
+
allBeforeMs?: number;
|
|
415
|
+
}): Promise<number>;
|
|
416
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapter Factory
|
|
3
|
+
*
|
|
4
|
+
* Creates a database adapter instance based on the supplied configuration.
|
|
5
|
+
*
|
|
6
|
+
* @typedef {'neon'|'pg'} DriverName
|
|
7
|
+
*
|
|
8
|
+
* @typedef {Object} AdapterConfig
|
|
9
|
+
* @property {DriverName} driver - Which database driver to use.
|
|
10
|
+
* @property {string} connectionString - Database connection URL.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Create a database adapter.
|
|
14
|
+
*
|
|
15
|
+
* @param {AdapterConfig} config
|
|
16
|
+
* @returns {Promise<import('./interface.js').DbAdapter>}
|
|
17
|
+
*/
|
|
18
|
+
export function createAdapter(config: AdapterConfig): Promise<import("./interface.js").DbAdapter>;
|
|
19
|
+
/**
|
|
20
|
+
* Adapter Factory
|
|
21
|
+
*
|
|
22
|
+
* Creates a database adapter instance based on the supplied configuration.
|
|
23
|
+
*/
|
|
24
|
+
export type DriverName = "neon" | "pg";
|
|
25
|
+
/**
|
|
26
|
+
* Adapter Factory
|
|
27
|
+
*
|
|
28
|
+
* Creates a database adapter instance based on the supplied configuration.
|
|
29
|
+
*/
|
|
30
|
+
export type AdapterConfig = {
|
|
31
|
+
/**
|
|
32
|
+
* - Which database driver to use.
|
|
33
|
+
*/
|
|
34
|
+
driver: DriverName;
|
|
35
|
+
/**
|
|
36
|
+
* - Database connection URL.
|
|
37
|
+
*/
|
|
38
|
+
connectionString: string;
|
|
39
|
+
};
|