@rei-standard/amsg-server 2.6.0-next.9 → 3.0.0
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 +822 -41
- package/dist/adapters/d1.d.ts +693 -0
- package/dist/adapters/factory.d.ts +39 -0
- package/dist/adapters/interface.d.ts +384 -0
- package/dist/adapters/neon.d.ts +105 -0
- package/dist/adapters/pg-shared.d.ts +194 -0
- package/dist/adapters/pg.d.ts +101 -0
- package/dist/adapters/schema.d.ts +57 -0
- package/dist/adapters/schema.sqlite.d.ts +80 -0
- package/dist/chunk-7NMQFTDJ.cjs +148 -0
- package/dist/{chunk-GOBCWCJP.mjs → chunk-GN44PST5.mjs} +68 -1
- package/dist/chunk-RL7V7VSS.mjs +8784 -0
- package/dist/chunk-TKDISKCF.cjs +163 -0
- package/dist/chunk-XKUWMYVF.mjs +163 -0
- package/dist/chunk-YBDQJVFQ.cjs +8784 -0
- package/dist/cloudflare/single-user-worker.d.ts +39 -0
- package/dist/cloudflare.cjs +35 -2
- package/dist/cloudflare.d.ts +11 -2
- package/dist/cloudflare.mjs +36 -3
- package/dist/handlers/cancel-message.d.ts +3 -0
- package/dist/handlers/capabilities.d.ts +4 -0
- package/dist/handlers/client-state-namespaces.d.ts +5 -0
- package/dist/handlers/client-state.d.ts +7 -0
- package/dist/handlers/cloud-data.d.ts +4 -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 +9 -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 +145 -766
- package/dist/index.d.ts +145 -766
- package/dist/index.mjs +125 -36
- package/dist/lib/agentic-fire.d.ts +177 -0
- package/dist/lib/client-state-store.d.ts +88 -0
- package/dist/lib/cloud-data-cleanup.d.ts +36 -0
- package/dist/lib/cloud-data-guard.d.ts +14 -0
- package/dist/lib/cloud-data-inventory.d.ts +15 -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 +273 -0
- package/dist/lib/llm-credentials-store.d.ts +147 -0
- package/dist/lib/llm.d.ts +12 -0
- package/dist/lib/message-processor.d.ts +82 -0
- package/dist/lib/outbox-store.d.ts +162 -0
- package/dist/lib/push-policy.d.ts +11 -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 +60 -0
- package/dist/lib/retry-policy.d.ts +11 -0
- package/dist/lib/run-tick.d.ts +121 -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 +65 -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-QJ7KYHEX.mjs → neon-GKLT4UY3.mjs} +116 -44
- package/dist/{neon-V4VOH55N.cjs → neon-XHPJZESV.cjs} +121 -49
- package/dist/{pg-6X74HKMX.cjs → pg-FHXQ6BJP.cjs} +109 -48
- package/dist/{pg-DFJSFOJQ.mjs → pg-J4YXRQP5.mjs} +104 -43
- package/dist/single-user.d.ts +106 -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-3QRVP4JE.cjs +0 -2834
- package/dist/chunk-DCKCAGFV.cjs +0 -81
- package/dist/chunk-S6O3MXQO.mjs +0 -2834
- package/dist/cloudflare-kUY6z-k5.d.cts +0 -4460
- package/dist/cloudflare-kUY6z-k5.d.ts +0 -4460
- package/dist/cloudflare.d.cts +0 -2
- package/dist/neon-D8Z2Nn97.d.cts +0 -295
- package/dist/neon-PqNFJIz2.d.ts +0 -295
- package/dist/pg-C48FwCWW.d.cts +0 -289
- package/dist/pg-C5gtWUfU.d.ts +0 -289
- package/dist/schema-BHuyH_6Z.d.cts +0 -88
- package/dist/schema-BHuyH_6Z.d.ts +0 -88
|
@@ -0,0 +1,693 @@
|
|
|
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
|
+
/** A complete management implementation needs real transactional batches. */
|
|
53
|
+
get cloudDataManagement(): boolean;
|
|
54
|
+
_cloudGuardPredicate(userId: any, guard: any): {
|
|
55
|
+
sql: string;
|
|
56
|
+
args: any[];
|
|
57
|
+
};
|
|
58
|
+
_cloudBatch(statements: any, userId: any, guards?: any[], metadata?: Map<any, any>): Promise<any[]>;
|
|
59
|
+
getCloudResourceMetadata(userId: any, resourceKey: any): Promise<any>;
|
|
60
|
+
listCloudResourceRows(userId: any): Promise<{
|
|
61
|
+
gaps: any[];
|
|
62
|
+
}>;
|
|
63
|
+
_cloudRecord(row: any): Promise<{
|
|
64
|
+
id: any;
|
|
65
|
+
userId: any;
|
|
66
|
+
kind: any;
|
|
67
|
+
data: any;
|
|
68
|
+
updatedAt: any;
|
|
69
|
+
}>;
|
|
70
|
+
putCloudDataRecord(userId: any, kind: any, id: any, data: any, { idempotencyKey, createOnly, leaseToken, work }?: {
|
|
71
|
+
idempotencyKey?: any;
|
|
72
|
+
createOnly?: boolean;
|
|
73
|
+
}): Promise<{
|
|
74
|
+
id: any;
|
|
75
|
+
userId: any;
|
|
76
|
+
kind: any;
|
|
77
|
+
data: any;
|
|
78
|
+
updatedAt: any;
|
|
79
|
+
}>;
|
|
80
|
+
getCloudDataRecord(userId: any, kind: any, id: any): Promise<{
|
|
81
|
+
id: any;
|
|
82
|
+
userId: any;
|
|
83
|
+
kind: any;
|
|
84
|
+
data: any;
|
|
85
|
+
updatedAt: any;
|
|
86
|
+
}>;
|
|
87
|
+
getCloudDataRecordByIdempotency(userId: any, kind: any, idempotencyKey: any): Promise<{
|
|
88
|
+
id: any;
|
|
89
|
+
userId: any;
|
|
90
|
+
kind: any;
|
|
91
|
+
data: any;
|
|
92
|
+
updatedAt: any;
|
|
93
|
+
}>;
|
|
94
|
+
_cloudRecordForList(row: any): Promise<{
|
|
95
|
+
id: any;
|
|
96
|
+
userId: any;
|
|
97
|
+
kind: any;
|
|
98
|
+
data: any;
|
|
99
|
+
updatedAt: any;
|
|
100
|
+
}>;
|
|
101
|
+
listCloudDataRecords(userId: any, kind: any): Promise<any[]>;
|
|
102
|
+
listCloudDataRecordsAcrossUsers(kind: any): Promise<any[]>;
|
|
103
|
+
claimCloudDataRecord(userId: any, kind: any, id: any, leaseMs: any, leaseToken?: any): Promise<boolean>;
|
|
104
|
+
releaseCloudDataRecord(userId: any, kind: any, id: any, leaseToken: any): Promise<void>;
|
|
105
|
+
renewCloudDataRecordLease(userId: any, kind: any, id: any, leaseMs: any, leaseToken: any): Promise<boolean>;
|
|
106
|
+
deleteCloudDataRecord(userId: any, kind: any, id: any): Promise<boolean>;
|
|
107
|
+
cleanupCloudDataRecords(kind: any, beforeMs: any): Promise<any>;
|
|
108
|
+
listDueCloudDataOperations(now?: number, limit?: number): Promise<any[]>;
|
|
109
|
+
cleanupExpiredCloudDataRecords(now?: number, limit?: number): Promise<any>;
|
|
110
|
+
getCloudMaintenanceBatch(name: any, table: any, limit?: number): Promise<{
|
|
111
|
+
rows: any[];
|
|
112
|
+
completed: boolean;
|
|
113
|
+
upperBound?: undefined;
|
|
114
|
+
cursor?: undefined;
|
|
115
|
+
} | {
|
|
116
|
+
rows: any;
|
|
117
|
+
upperBound: any;
|
|
118
|
+
cursor: any;
|
|
119
|
+
completed: boolean;
|
|
120
|
+
}>;
|
|
121
|
+
finishCloudMaintenanceBatch(name: any, cursor: any, completed: any): Promise<void>;
|
|
122
|
+
/**
|
|
123
|
+
* Index a whole repair batch in one statement. Never overwrites an existing schedule, and
|
|
124
|
+
* skips a record that was rewritten after the batch was read.
|
|
125
|
+
*/
|
|
126
|
+
indexCloudDataRecordWorkBatch(entries: any): Promise<void>;
|
|
127
|
+
/** Move an indexed record's schedule, unless the record was rewritten since the caller read it. */
|
|
128
|
+
rescheduleCloudDataRecordWork(userId: any, kind: any, id: any, work: any, expectedWriteToken?: any): Promise<boolean>;
|
|
129
|
+
readCloudMaintenanceRecord(row: any): Promise<{
|
|
130
|
+
id: any;
|
|
131
|
+
userId: any;
|
|
132
|
+
kind: any;
|
|
133
|
+
data: any;
|
|
134
|
+
updatedAt: any;
|
|
135
|
+
}>;
|
|
136
|
+
repairCloudResourceMetadata(limit?: number): Promise<void>;
|
|
137
|
+
/** One explicit repair, or a bounded upgrade batch; never called as a recurring sweep. */
|
|
138
|
+
cleanupCloudResourceMetadata(userId?: any, range?: any): Promise<any>;
|
|
139
|
+
listCloudOwners(userId: any): Promise<any>;
|
|
140
|
+
getCloudOwner(userId: any, owner: any): Promise<{
|
|
141
|
+
active: boolean;
|
|
142
|
+
generation: any;
|
|
143
|
+
updatedAt: any;
|
|
144
|
+
}>;
|
|
145
|
+
setCloudOwnerActive(userId: any, owner: any, active: any, expectedGeneration: any): Promise<{
|
|
146
|
+
active: boolean;
|
|
147
|
+
generation: any;
|
|
148
|
+
updatedAt: any;
|
|
149
|
+
}>;
|
|
150
|
+
isCloudOwnerGuardValid(userId: any, guard: any): Promise<boolean>;
|
|
151
|
+
/** Delete only the exact physical snapshot; no untrusted SQL identifiers. */
|
|
152
|
+
deleteCloudResourceRows(userId: any, locators: any, { operationId, leaseToken }?: {}): Promise<{
|
|
153
|
+
deleted: number;
|
|
154
|
+
changed: number;
|
|
155
|
+
}>;
|
|
156
|
+
initSchema(): Promise<{
|
|
157
|
+
columnsCreated: number;
|
|
158
|
+
indexesCreated: number;
|
|
159
|
+
indexesFailed: number;
|
|
160
|
+
columns: any[];
|
|
161
|
+
indexes: ({
|
|
162
|
+
name: string;
|
|
163
|
+
status: string;
|
|
164
|
+
description: string;
|
|
165
|
+
critical: boolean;
|
|
166
|
+
error?: undefined;
|
|
167
|
+
} | {
|
|
168
|
+
name: string;
|
|
169
|
+
status: string;
|
|
170
|
+
description: string;
|
|
171
|
+
critical: boolean;
|
|
172
|
+
error: any;
|
|
173
|
+
})[];
|
|
174
|
+
}>;
|
|
175
|
+
/**
|
|
176
|
+
* 活库里现在实际有哪些表 / 列 / 索引。
|
|
177
|
+
*
|
|
178
|
+
* 只读不写,纯粹如实回报:拿它跟这一版需要的清单对照的活儿在
|
|
179
|
+
* lib/schema-version.js(`getSchemaVersion` / `ensureSchema`)。库升级后表结
|
|
180
|
+
* 构变了而老部署没跑过 initSchema 时,cron 会每分钟静默挂在缺的那一列上,
|
|
181
|
+
* 界面上一切正常——这个方法就是让宿主查得出来。
|
|
182
|
+
*
|
|
183
|
+
* @returns {Promise<{ tables: Record<string, string[]>, indexes: string[], triggers: string[] }>}
|
|
184
|
+
*/
|
|
185
|
+
describeSchema(): Promise<{
|
|
186
|
+
tables: Record<string, string[]>;
|
|
187
|
+
indexes: string[];
|
|
188
|
+
triggers: string[];
|
|
189
|
+
}>;
|
|
190
|
+
dropSchema(): Promise<void>;
|
|
191
|
+
createTask(params: any, cloudGuard?: any): Promise<any>;
|
|
192
|
+
/**
|
|
193
|
+
* 建新任务的同时取消旧的那条(`POST /schedule-message` 的 supersedesUuid)。
|
|
194
|
+
*
|
|
195
|
+
* 两条语句走一次 batch(D1 的隐式事务 + 单次网络往返):不会出现「旧的删了、
|
|
196
|
+
* 新的没建成」的中间态——INSERT 撞 uuid 唯一索引时整个 batch 回滚,旧行原样
|
|
197
|
+
* 留着,调用方按既有的 409 冲突路径处理。
|
|
198
|
+
*
|
|
199
|
+
* @param {import('./interface.js').InsertTaskParams} params
|
|
200
|
+
* @param {string} supersedesUuid - 要取消的旧任务 uuid(同一 user_id 下)
|
|
201
|
+
* @returns {Promise<Object>} createTask 的返回行 + `superseded`(旧行是否
|
|
202
|
+
* 真的被删掉;false = 旧行本就不存在)
|
|
203
|
+
*/
|
|
204
|
+
createTaskSuperseding(params: import("./interface.js").InsertTaskParams, supersedesUuid: string, cloudGuard?: any): Promise<any>;
|
|
205
|
+
getTaskByUuid(uuid: any, userId: any): Promise<any>;
|
|
206
|
+
getTaskByUuidOnly(uuid: any): Promise<any>;
|
|
207
|
+
/**
|
|
208
|
+
* 这条 uuid 现在是什么状态——不限用户,也不限状态(getTaskByUuidOnly 只看
|
|
209
|
+
* pending 行)。`runTask` 用它把「这条已经跑完了」和「压根没这条」分开回报。
|
|
210
|
+
*
|
|
211
|
+
* @param {string} uuid
|
|
212
|
+
* @returns {Promise<{ status: string }|null>}
|
|
213
|
+
*/
|
|
214
|
+
getTaskStatusByUuidOnly(uuid: string): Promise<{
|
|
215
|
+
status: string;
|
|
216
|
+
} | null>;
|
|
217
|
+
updateTaskById(taskId: any, updates: any, cloudGuard?: any): Promise<any>;
|
|
218
|
+
/**
|
|
219
|
+
* 按 uuid 改一条 pending 任务(PUT /update-message、fire hook 的 renewTask)。
|
|
220
|
+
*
|
|
221
|
+
* 改排期(extraFields 里带 next_send_at)时多一道租约门:这条任务正被一次投递
|
|
222
|
+
* 占着的话不改,返回 null。投递收尾会按自己领取时看到的排期推进下一次(一次性
|
|
223
|
+
* 任务干脆标成已发送),这期间写进去的新时刻随后就被盖掉,接口却已经回了成功
|
|
224
|
+
* ——用户以为改期生效了,实际什么都没留下。正文这类字段不受这道门约束:它们
|
|
225
|
+
* 只影响以后的触发,收尾那边本来就不会覆盖(见 run-tick 的收尾守卫)。
|
|
226
|
+
*/
|
|
227
|
+
updateTaskByUuid(uuid: any, userId: any, encryptedPayload: any, extraFields: any, cloudGuard?: any): Promise<{
|
|
228
|
+
uuid: any;
|
|
229
|
+
updated_at: string;
|
|
230
|
+
}>;
|
|
231
|
+
deleteTaskById(taskId: any): Promise<boolean>;
|
|
232
|
+
deleteTaskByUuid(uuid: any, userId: any): Promise<boolean>;
|
|
233
|
+
getPendingTasks(limit?: number): Promise<any>;
|
|
234
|
+
/**
|
|
235
|
+
* 领取一条到点的任务:在 lease_until 上写下「这条归我管到什么时候」,
|
|
236
|
+
* 本次投递期间别的 tick 领不走它。
|
|
237
|
+
*
|
|
238
|
+
* 租约写在自己的列上,next_send_at 全程不动——那一列是用户设的触发时刻,
|
|
239
|
+
* 任务列表要读它、循环任务推进下一次也要拿它当基准。
|
|
240
|
+
*
|
|
241
|
+
* 两个 tick 抢同一行时只有一个能改到行,另一个拿到 changes = 0,据此跳过。
|
|
242
|
+
* WHERE 里的两个条件各管一件事:
|
|
243
|
+
* - lease_until 为空或已过期:没人正在跑这条。领了任务的 tick 中途没了
|
|
244
|
+
* 也不会把行焊死,租约到期后自然可以被接手。
|
|
245
|
+
* - next_send_at 等于读这行时看到的值:读出来之后用户又改了排期的话,
|
|
246
|
+
* 这一跳就不该再按旧时刻发。
|
|
247
|
+
*
|
|
248
|
+
* 不加一个 'sending' 状态来表达「正在跑」:建表语句里 status 有
|
|
249
|
+
* CHECK (status IN ('pending','sent','failed')),加值要重建表。
|
|
250
|
+
*
|
|
251
|
+
* expectedNextSendAt 按读到的原样比对,不做时区归一化——老部署里可能还留
|
|
252
|
+
* 着非归一化写法的行(如 +08:00 结尾),归一化后反而对不上,那条任务会永
|
|
253
|
+
* 远领不到。
|
|
254
|
+
*
|
|
255
|
+
* 带 serializeGroup 时多一道分组门:同一分组里已经有别的行拿着未到期的租
|
|
256
|
+
* 约,这条就领不走(同一分组同时只跑一条)。判定和写租约在同一条 UPDATE
|
|
257
|
+
* 里完成,「先查再占」的空档天然不存在——两个 tick 同时来,只有一个改得动
|
|
258
|
+
* 行。分组门只看租约,不看 `retry_after`:等着重试的任务其实闲着,不该把
|
|
259
|
+
* 同分组的其他任务一起堵住。
|
|
260
|
+
*
|
|
261
|
+
* @param {number} taskId
|
|
262
|
+
* @param {string} expectedNextSendAt - 读这行时拿到的 next_send_at 原值
|
|
263
|
+
* @param {string|Date} leaseUntil - 租期末尾
|
|
264
|
+
* @param {string|null} [serializeGroup] - 串行分组标识;空表示不参与分组串行
|
|
265
|
+
* @returns {Promise<boolean>} true = 领到了;false = 别人正拿着租约、同分组
|
|
266
|
+
* 有任务正在跑、排期被改过、或行已不是 pending
|
|
267
|
+
*/
|
|
268
|
+
claimTask(taskId: number, expectedNextSendAt: string, leaseUntil: string | Date, serializeGroup?: string | null, cloudGuard?: any): Promise<boolean>;
|
|
269
|
+
/**
|
|
270
|
+
* 投递期间的租约续期(run-tick 的心跳用)。只在行仍是 pending 且确实持有
|
|
271
|
+
* 租约(lease_until 非空)时生效——收尾把租约放掉之后,迟到的心跳不会把
|
|
272
|
+
* 租约复活。
|
|
273
|
+
*
|
|
274
|
+
* @param {number} taskId
|
|
275
|
+
* @param {string|Date} leaseUntil - 新的租期末尾
|
|
276
|
+
* @returns {Promise<boolean>} true = 续上了
|
|
277
|
+
*/
|
|
278
|
+
renewTaskLease(taskId: number, leaseUntil: string | Date, cloudGuard?: any): Promise<boolean>;
|
|
279
|
+
listTasks(userId: any, opts?: {}): Promise<{
|
|
280
|
+
tasks: any;
|
|
281
|
+
total: number;
|
|
282
|
+
}>;
|
|
283
|
+
cleanupOldTasks(days?: number): Promise<any>;
|
|
284
|
+
getTaskStatus(uuid: any, userId: any): Promise<any>;
|
|
285
|
+
/**
|
|
286
|
+
* 状态 + 失败摘要(GET /message 用它把「为什么失败」透给已失败的行——那些
|
|
287
|
+
* 行 getTaskByUuid 读不到,payload 里的 lastError 也就够不着了)。
|
|
288
|
+
*
|
|
289
|
+
* @param {string} uuid
|
|
290
|
+
* @param {string} userId
|
|
291
|
+
* @returns {Promise<{ status: string, last_error: string|null }|null>}
|
|
292
|
+
*/
|
|
293
|
+
getTaskStatusInfo(uuid: string, userId: string): Promise<{
|
|
294
|
+
status: string;
|
|
295
|
+
last_error: string | null;
|
|
296
|
+
} | null>;
|
|
297
|
+
/**
|
|
298
|
+
* Batch upsert. Last-write-wins per (namespace, key): an entry older
|
|
299
|
+
* than the stored row (updatedAt strictly lower) is skipped; equal or
|
|
300
|
+
* newer overwrites. Values arrive pre-encrypted (the handler encrypts).
|
|
301
|
+
*
|
|
302
|
+
* 例外是「来自未来」的行:`updated_at` 晚于服务端当前时间的行一律放行覆盖。
|
|
303
|
+
* 比较值是客户端自己报的时间戳,设备时钟只要领先过真实时间(用户改过系统
|
|
304
|
+
* 时间、时区或日期误操作),那一刻同步上来的行就带着一个还没到的时刻;之后
|
|
305
|
+
* 这台设备发什么都比它「旧」,条件写全被无声跳过,云端那行要等真实时间追上
|
|
306
|
+
* 去才解得开——客户端删本地数据、重装都碰不到它。合法写入不可能来自未来,
|
|
307
|
+
* 所以这种行按脏数据处理:服务端的钟是可信的那一个,拿它当判据放行。一次
|
|
308
|
+
* 正常写入就把 `updated_at` 拉回现实,之后旧不盖新照常生效。
|
|
309
|
+
*
|
|
310
|
+
* `cleanups` 是删除项:在同一 batch 里先于 upsert 执行,`updated_at <= ?`
|
|
311
|
+
* 条件保证陈旧批次删不动更新写入的行(同样对未来时间戳的行放行,否则删一条
|
|
312
|
+
* 状态时切片行留在库里成孤儿)。两种形态——
|
|
313
|
+
* - `{ namespace, keyPrefix, updatedAt }` 删 key 前缀下的所有行,用来清掉
|
|
314
|
+
* 大值旧写入留下的切片行(见 lib/state-chunks.js);
|
|
315
|
+
* - `{ namespace, key, updatedAt }` 删这一个 key,用来删整条状态(前缀会
|
|
316
|
+
* 连带删掉同前缀的兄弟 key,删单条必须走精确匹配)。
|
|
317
|
+
*
|
|
318
|
+
* 删掉一整条状态就是这两种各来一条(切片行走前缀、根行走精确 key),
|
|
319
|
+
* `entries` 传空数组即可——见 lib/client-state-store.js。
|
|
320
|
+
*
|
|
321
|
+
* Uses D1's batch() — one network round trip for the whole set (implicit
|
|
322
|
+
* transaction). The client calls this endpoint inside its few-seconds
|
|
323
|
+
* background window, so N sequential round trips could eat the whole
|
|
324
|
+
* window. Bindings without batch() (e.g. the sqlite test shim, custom
|
|
325
|
+
* adapters) fall back to a sequential loop.
|
|
326
|
+
*
|
|
327
|
+
* @param {string} userId
|
|
328
|
+
* @param {Array<{ namespace: string, key: string, value: string, updatedAt: number }>} entries
|
|
329
|
+
* @param {Array<{ namespace: string, key?: string, keyPrefix?: string, updatedAt: number }>} [cleanups]
|
|
330
|
+
* @param {number} [now] - 服务端当前时刻(epoch 毫秒),判定「这行来自未来」用的
|
|
331
|
+
* 就是它。调用方(lib/client-state-store.js)传下来,测试可以钉住一个假时钟;
|
|
332
|
+
* 自定义调用方不传时退回本机时钟。
|
|
333
|
+
* @returns {Promise<{ upserted: number, skipped: number, outcomes: boolean[], cleanupOutcomes?: Array<boolean|null> }>}
|
|
334
|
+
* `outcomes[i]` 对应 entries[i] 是否真的写入(changes > 0)。
|
|
335
|
+
* `cleanupOutcomes[i]` 对应 cleanups[i](传了 cleanups 才有):精确 key 形态
|
|
336
|
+
* 回 `true` = 这个 key 的行已经不在(删掉了,或本来就没有)、`false` = 行还在
|
|
337
|
+
* (库里那行更新,删除被条件写拦下);前缀形态不探测,回 `null`。
|
|
338
|
+
*/
|
|
339
|
+
upsertClientState(userId: string, entries: Array<{
|
|
340
|
+
namespace: string;
|
|
341
|
+
key: string;
|
|
342
|
+
value: string;
|
|
343
|
+
updatedAt: number;
|
|
344
|
+
}>, cleanups?: Array<{
|
|
345
|
+
namespace: string;
|
|
346
|
+
key?: string;
|
|
347
|
+
keyPrefix?: string;
|
|
348
|
+
updatedAt: number;
|
|
349
|
+
}>, now?: number, cloudGuard?: any): Promise<{
|
|
350
|
+
upserted: number;
|
|
351
|
+
skipped: number;
|
|
352
|
+
outcomes: boolean[];
|
|
353
|
+
cleanupOutcomes?: Array<boolean | null>;
|
|
354
|
+
}>;
|
|
355
|
+
/**
|
|
356
|
+
* All entries of one namespace (values still encrypted).
|
|
357
|
+
*
|
|
358
|
+
* @param {string} userId
|
|
359
|
+
* @param {string} namespace
|
|
360
|
+
* @returns {Promise<Array<{ namespace: string, key: string, value: string, updated_at: number }>>}
|
|
361
|
+
*/
|
|
362
|
+
getClientState(userId: string, namespace: string): Promise<Array<{
|
|
363
|
+
namespace: string;
|
|
364
|
+
key: string;
|
|
365
|
+
value: string;
|
|
366
|
+
updated_at: number;
|
|
367
|
+
}>>;
|
|
368
|
+
/**
|
|
369
|
+
* 例行清理:把指定命名空间下太久没更新的条目删掉(run-tick 每跳顺手调,
|
|
370
|
+
* 宿主配了 `clientStateTtl` 才会调)。
|
|
371
|
+
*
|
|
372
|
+
* 不限用户——「这个命名空间只留最近 N 天」是命名空间级的约定,单用户部署
|
|
373
|
+
* 下也就是这一个用户的行。指令由 lib/client-state-store.js 的
|
|
374
|
+
* `planClientStateCleanup` 算好(含大值切片所在的保留命名空间),这里只负
|
|
375
|
+
* 责照着删。
|
|
376
|
+
*
|
|
377
|
+
* @param {Array<{ namespace: string, updatedBefore: number }>} targets
|
|
378
|
+
* `updatedBefore` 是 epoch 毫秒,与 `updated_at` 列同一把尺子。
|
|
379
|
+
* @returns {Promise<number>} 删掉的行数
|
|
380
|
+
*/
|
|
381
|
+
cleanupClientState(targets?: Array<{
|
|
382
|
+
namespace: string;
|
|
383
|
+
updatedBefore: number;
|
|
384
|
+
}>): Promise<number>;
|
|
385
|
+
/**
|
|
386
|
+
* Wipe every entry of this user.
|
|
387
|
+
*
|
|
388
|
+
* @param {string} userId
|
|
389
|
+
* @returns {Promise<number>} rows deleted
|
|
390
|
+
*/
|
|
391
|
+
clearClientState(userId: string): Promise<number>;
|
|
392
|
+
/**
|
|
393
|
+
* 这个用户名下有哪些命名空间,各自几条、占多少字节、最后更新是什么时候
|
|
394
|
+
* (宿主对账「云端到底存了什么」用)。
|
|
395
|
+
*
|
|
396
|
+
* `foldPrefix` 传进来的是大值分块那个保留命名空间的前缀(见
|
|
397
|
+
* lib/state-chunks.js):以它开头的行不单独成一个命名空间,而是折算进
|
|
398
|
+
* 去掉前缀之后的那个原命名空间——保留命名空间是库的存储实现细节,宿主眼里
|
|
399
|
+
* 那些切片行就是原命名空间占掉的地方。折算口径:
|
|
400
|
+
* - `byte_size` / `updated_at` 算进去(存储确实占着、写入确实发生过);
|
|
401
|
+
* - `entry_count` 不算(切片是一个逻辑条目的几段,不是几个条目)。
|
|
402
|
+
* 不传 `foldPrefix` = 不折算,保留命名空间按普通命名空间原样列出来。
|
|
403
|
+
*
|
|
404
|
+
* 前缀由调用方传、不由适配器自己知道:分块是 lib 层的约定,适配器只照着折。
|
|
405
|
+
*
|
|
406
|
+
* 折算写在 SQL 里而不是取回来在 JS 里合,是因为有 `limit`:保留命名空间以
|
|
407
|
+
* \u001f 开头,BINARY 排序下排在所有正常命名空间前面,先取 limit 条再折算
|
|
408
|
+
* 的话额度会被切片命名空间吃光,正常命名空间一条都露不出来。
|
|
409
|
+
*
|
|
410
|
+
* `LENGTH(CAST(value AS BLOB))` 数的是字节不是字符——TEXT 上的 `LENGTH()`
|
|
411
|
+
* 按字符算,密文虽然是 ASCII 十六进制、两者相同,但换个存法就悄悄差一截。
|
|
412
|
+
*
|
|
413
|
+
* 这条语句要把该用户的 client_state 全扫一遍(GROUP BY 本来就得看每一行),
|
|
414
|
+
* 所以没为它单独加索引:它是宿主按需点开的对账口,不在 cron 路径上。别把它
|
|
415
|
+
* 塞进每分钟跑的东西里(理由见 adapters/schema.sqlite.js 的 CLIENT_STATE_INDEXES)。
|
|
416
|
+
*
|
|
417
|
+
* @param {string} userId
|
|
418
|
+
* @param {{ limit?: number, foldPrefix?: string|null }} [opts]
|
|
419
|
+
* @returns {Promise<Array<{ namespace: string, entry_count: number, byte_size: number, updated_at: number }>>}
|
|
420
|
+
* 按 namespace 升序,最多 `limit` 条。
|
|
421
|
+
*/
|
|
422
|
+
listClientStateNamespaces(userId: string, { limit, foldPrefix }?: {
|
|
423
|
+
limit?: number;
|
|
424
|
+
foldPrefix?: string | null;
|
|
425
|
+
}): Promise<Array<{
|
|
426
|
+
namespace: string;
|
|
427
|
+
entry_count: number;
|
|
428
|
+
byte_size: number;
|
|
429
|
+
updated_at: number;
|
|
430
|
+
}>>;
|
|
431
|
+
/**
|
|
432
|
+
* 把这几个命名空间下这个用户的行一次删光(一次 batch = 一次事务)。
|
|
433
|
+
*
|
|
434
|
+
* 调用方传的是「原命名空间 + 它的切片保留命名空间」两个(见
|
|
435
|
+
* lib/state-chunks.js 的 chunkNamespaceFor):只删前者的话,大值那几行切片
|
|
436
|
+
* 留在库里成孤儿——读不出来、也不会被别的路径清掉。哪些命名空间算一组由调
|
|
437
|
+
* 用方决定,适配器只负责它们在同一个事务里删完。
|
|
438
|
+
*
|
|
439
|
+
* 条件是 `user_id = ? AND namespace = ?`,吃的是主键
|
|
440
|
+
* (user_id, namespace, key) 的前两列,不扫表。
|
|
441
|
+
*
|
|
442
|
+
* @param {string} userId
|
|
443
|
+
* @param {string[]} namespaces
|
|
444
|
+
* @returns {Promise<number>} 删掉的行数合计(含切片行)
|
|
445
|
+
*/
|
|
446
|
+
deleteClientStateNamespaces(userId: string, namespaces: string[]): Promise<number>;
|
|
447
|
+
/**
|
|
448
|
+
* 这个用户当前登记的推送订阅(密文原样返回,解密在上层)。
|
|
449
|
+
*
|
|
450
|
+
* @param {string} userId
|
|
451
|
+
* @returns {Promise<{ subscription: string, updated_at: number }|null>}
|
|
452
|
+
*/
|
|
453
|
+
getPushSubscription(userId: string): Promise<{
|
|
454
|
+
subscription: string;
|
|
455
|
+
updated_at: number;
|
|
456
|
+
} | null>;
|
|
457
|
+
/**
|
|
458
|
+
* 覆盖写这个用户的订阅。一个用户一行,没有 last-write-wins 之类的比较——
|
|
459
|
+
* 客户端拿到的新订阅永远比旧的有效,旧的那份只会 410。
|
|
460
|
+
*
|
|
461
|
+
* @param {string} userId
|
|
462
|
+
* @param {string} encryptedSubscription
|
|
463
|
+
* @param {number} updatedAt - epoch 毫秒
|
|
464
|
+
* @returns {Promise<boolean>}
|
|
465
|
+
*/
|
|
466
|
+
upsertPushSubscription(userId: string, encryptedSubscription: string, updatedAt: number): Promise<boolean>;
|
|
467
|
+
/**
|
|
468
|
+
* 删掉这个用户的订阅(设置页「停止接收推送」)。
|
|
469
|
+
*
|
|
470
|
+
* @param {string} userId
|
|
471
|
+
* @returns {Promise<boolean>} true = 确实删掉了一行
|
|
472
|
+
*/
|
|
473
|
+
deletePushSubscription(userId: string): Promise<boolean>;
|
|
474
|
+
/**
|
|
475
|
+
* 批量 upsert 这个用户的凭据(value 已是密文,加密在上层)。已存在的行
|
|
476
|
+
* 覆盖 encrypted_value 并刷 updated_at,created_at 保留首次写入的时刻。
|
|
477
|
+
*
|
|
478
|
+
* @param {string} userId
|
|
479
|
+
* @param {Array<{ credId: string, encryptedValue: string }>} entries
|
|
480
|
+
* @returns {Promise<number>} 实际写入/覆盖的行数
|
|
481
|
+
*/
|
|
482
|
+
upsertLlmCredentials(userId: string, entries: Array<{
|
|
483
|
+
credId: string;
|
|
484
|
+
encryptedValue: string;
|
|
485
|
+
}>, cloudGuard?: any): Promise<number>;
|
|
486
|
+
/**
|
|
487
|
+
* 按 cred_id 批量读这个用户的凭据行(`encrypted_value` 是密文,解密在上
|
|
488
|
+
* 层)。排程时的存在性检查和 fire 时的解析共用这一个口。
|
|
489
|
+
*
|
|
490
|
+
* @param {string} userId
|
|
491
|
+
* @param {string[]} credIds
|
|
492
|
+
* @returns {Promise<Array<{ cred_id: string, encrypted_value: string, updated_at: string }>>}
|
|
493
|
+
*/
|
|
494
|
+
getLlmCredentials(userId: string, credIds: string[]): Promise<Array<{
|
|
495
|
+
cred_id: string;
|
|
496
|
+
encrypted_value: string;
|
|
497
|
+
updated_at: string;
|
|
498
|
+
}>>;
|
|
499
|
+
/**
|
|
500
|
+
* 这个用户名下所有凭据的对账清单(只有 cred_id 和 updated_at,密文不出
|
|
501
|
+
* 这个方法)。按 cred_id 排序,输出稳定。
|
|
502
|
+
*
|
|
503
|
+
* @param {string} userId
|
|
504
|
+
* @returns {Promise<Array<{ cred_id: string, updated_at: string }>>}
|
|
505
|
+
*/
|
|
506
|
+
listLlmCredentials(userId: string): Promise<Array<{
|
|
507
|
+
cred_id: string;
|
|
508
|
+
updated_at: string;
|
|
509
|
+
}>>;
|
|
510
|
+
/**
|
|
511
|
+
* 删凭据。`credIds` 传数组删指定那几行;传 null 删这个用户的全部
|
|
512
|
+
* (「清空云端数据」用)。
|
|
513
|
+
*
|
|
514
|
+
* @param {string} userId
|
|
515
|
+
* @param {string[]|null} credIds
|
|
516
|
+
* @returns {Promise<number>} 删掉的行数
|
|
517
|
+
*/
|
|
518
|
+
deleteLlmCredentials(userId: string, credIds?: string[] | null): Promise<number>;
|
|
519
|
+
/**
|
|
520
|
+
* 按 cred_id 前缀删这个用户的凭据(宿主按角色清理:`char:<charId>/` 一把清
|
|
521
|
+
* 掉该角色名下的 chat / instant / emotion 几行)。
|
|
522
|
+
*
|
|
523
|
+
* **不用 LIKE。** 两条 D1 的限制在这儿各埋一个雷:
|
|
524
|
+
* - LIKE / GLOB 的 pattern 在 D1 上最长 50 字节(SQLite 默认 50000,官方文
|
|
525
|
+
* 档没写这一条)。`char:<uuid>/` 就是 42 字节,前缀里再多点东西、或者
|
|
526
|
+
* cred_id 用上契约允许的 128 字符,pattern 当场超限,整条语句报
|
|
527
|
+
* `LIKE or GLOB pattern too complex`——本地 better-sqlite3 上永远复现不
|
|
528
|
+
* 了,只有真实 D1 才炸。
|
|
529
|
+
* - 退一步「先 SELECT 出匹配的 cred_id 再按 id 批量删」也不是好路:单条语
|
|
530
|
+
* 句最多 100 个绑定参数,得自己切批,还平白多一个来回和一个「查完到删完
|
|
531
|
+
* 之间又写进来一行」的窗口。
|
|
532
|
+
* 走字典序范围(`cred_id >= 前缀 AND cred_id < 上界`)两条都绕开了:没有长度
|
|
533
|
+
* 上限,前缀里的 `%` `_` `\` 只是普通字符,一条语句三个绑定参数,而且直接吃
|
|
534
|
+
* (user_id, cred_id) 主键索引。上界算法见本文件顶部的 prefixRangeEnd。
|
|
535
|
+
*
|
|
536
|
+
* 前缀没有字典序上界时(prefixRangeEnd 返回空串,实际用不到——见那个函数的
|
|
537
|
+
* 说明)范围条件一行都匹配不上,删 0 行。宁可少删,也不能把别人的行带走。
|
|
538
|
+
*
|
|
539
|
+
* @param {string} userId
|
|
540
|
+
* @param {string} credIdPrefix - 非空前缀,空串由上层拒掉(空前缀 = 删全部,
|
|
541
|
+
* 那是 `deleteLlmCredentials(userId, null)` 的活儿,不能从这个口误伤进来)
|
|
542
|
+
* @returns {Promise<number>} 删掉的行数
|
|
543
|
+
*/
|
|
544
|
+
deleteLlmCredentialsByPrefix(userId: string, credIdPrefix: string): Promise<number>;
|
|
545
|
+
/**
|
|
546
|
+
* 发送前把这一批 push 落进 outbox(一次 batch)。(user_id, message_id)
|
|
547
|
+
* 唯一:重试同一 occurrence 带着同一批 messageId 再来时更新 payload、不加
|
|
548
|
+
* 第二行;客户端已经 ack 过的行不动(ack 是终态,重试不该把它拉回未读)。
|
|
549
|
+
*
|
|
550
|
+
* @param {string} userId
|
|
551
|
+
* @param {Array<{ message_id: string, task_uuid?: string|null, session_id?: string|null,
|
|
552
|
+
* message_index?: number|null, total_messages?: number|null, payload: string, created_at: number }>} rows
|
|
553
|
+
* `payload` 是整条 push JSON 的 encryptForStorage 密文。
|
|
554
|
+
* @returns {Promise<number>} 实际写入/更新的行数
|
|
555
|
+
*/
|
|
556
|
+
appendOutboxMessages(userId: string, rows: Array<{
|
|
557
|
+
message_id: string;
|
|
558
|
+
task_uuid?: string | null;
|
|
559
|
+
session_id?: string | null;
|
|
560
|
+
message_index?: number | null;
|
|
561
|
+
total_messages?: number | null;
|
|
562
|
+
payload: string;
|
|
563
|
+
created_at: number;
|
|
564
|
+
}>, cloudGuard?: any): Promise<number>;
|
|
565
|
+
/**
|
|
566
|
+
* 把这一批标记为「Web Push 已发出」。发送失败的段不标——delivered_at 为
|
|
567
|
+
* null 的行正是客户端最需要拉的那部分。
|
|
568
|
+
*
|
|
569
|
+
* @param {string} userId
|
|
570
|
+
* @param {string[]} messageIds
|
|
571
|
+
* @param {number} deliveredAt - epoch 毫秒
|
|
572
|
+
* @returns {Promise<number>}
|
|
573
|
+
*/
|
|
574
|
+
markOutboxDelivered(userId: string, messageIds: string[], deliveredAt: number): Promise<number>;
|
|
575
|
+
/**
|
|
576
|
+
* 把这一批还没发出去的行删掉(任务投递到一半被取消 / 顶替时用)。
|
|
577
|
+
*
|
|
578
|
+
* 只删 delivered_at 仍为 NULL 的行:已经推给设备的那几条撤不回来,行留着让
|
|
579
|
+
* 客户端照常 ack。已 ack 的行更不动。
|
|
580
|
+
*
|
|
581
|
+
* @param {string} userId
|
|
582
|
+
* @param {string[]} messageIds
|
|
583
|
+
* @returns {Promise<number>} 删掉的行数
|
|
584
|
+
*/
|
|
585
|
+
discardOutboxMessages(userId: string, messageIds: string[]): Promise<number>;
|
|
586
|
+
/**
|
|
587
|
+
* 把某条任务名下还没发出去的行一次删掉(取消 / 顶替时的 outbox 清理)。
|
|
588
|
+
*
|
|
589
|
+
* 判据与 discardOutboxMessages 相同:只删 delivered_at 与 acked_at 均为
|
|
590
|
+
* NULL 的行——已经推给设备 / 已 ack 的照旧留着。按 task_uuid 直删是为了不
|
|
591
|
+
* 受未 ack 积压量的影响:靠翻页扫描挑行的话,积压一大这条任务的行就落在
|
|
592
|
+
* 扫描上限之外(见 lib/outbox-store.js 的 discardUndeliveredPushesForTask)。
|
|
593
|
+
*
|
|
594
|
+
* @param {string} userId
|
|
595
|
+
* @param {string} taskUuid
|
|
596
|
+
* @returns {Promise<number>} 删掉的行数
|
|
597
|
+
*/
|
|
598
|
+
discardUndeliveredOutboxForTask(userId: string, taskUuid: string): Promise<number>;
|
|
599
|
+
/**
|
|
600
|
+
* 某条任务名下、某个时刻之后落的行,**不论投递 / ack 状态**(payload 仍是密文)。
|
|
601
|
+
*
|
|
602
|
+
* 给「生成成功之后推送失败、重试只补推送」那条路用(见 lib/outbox-store.js 的
|
|
603
|
+
* findCommittedBatch):重试那一跳先来这里看这次触发的整批是不是已经落定了。
|
|
604
|
+
* 已 ack 的行也要读——客户端在两次重试之间把整批补收并 ack 了,同样说明这次
|
|
605
|
+
* 触发的内容已经生成过,不该再生成一份。
|
|
606
|
+
*
|
|
607
|
+
* `+user_id` 的一元加号是故意的:它让这一项不参与选索引,查询改走
|
|
608
|
+
* idx_outbox_created 按 created_at 收窄。否则 SQLite 会挑 (user_id, message_id)
|
|
609
|
+
* 的唯一约束索引,单用户部署下 user_id 对每一行都成立,等于把整个收件箱扫一遍
|
|
610
|
+
* (D1 按扫过的行数计费)。idx_outbox_created 不在时照样查得出来,只是退回扫表。
|
|
611
|
+
*
|
|
612
|
+
* @param {string} userId
|
|
613
|
+
* @param {string} taskUuid
|
|
614
|
+
* @param {{ sinceMs?: number, limit?: number }} [options] - sinceMs:只要
|
|
615
|
+
* created_at ≥ 它的行(epoch 毫秒);limit:最多读几行(默认 500)
|
|
616
|
+
* @returns {Promise<Array<{ id: number, message_id: string, task_uuid: string|null,
|
|
617
|
+
* session_id: string|null, message_index: number|null, total_messages: number|null,
|
|
618
|
+
* payload: string, created_at: number, delivered_at: number|null, acked_at: number|null }>>}
|
|
619
|
+
*/
|
|
620
|
+
listOutboxForTask(userId: string, taskUuid: string, { sinceMs, limit }?: {
|
|
621
|
+
sinceMs?: number;
|
|
622
|
+
limit?: number;
|
|
623
|
+
}): Promise<Array<{
|
|
624
|
+
id: number;
|
|
625
|
+
message_id: string;
|
|
626
|
+
task_uuid: string | null;
|
|
627
|
+
session_id: string | null;
|
|
628
|
+
message_index: number | null;
|
|
629
|
+
total_messages: number | null;
|
|
630
|
+
payload: string;
|
|
631
|
+
created_at: number;
|
|
632
|
+
delivered_at: number | null;
|
|
633
|
+
acked_at: number | null;
|
|
634
|
+
}>>;
|
|
635
|
+
/**
|
|
636
|
+
* 未 ack 的行(id 升序,游标翻页)。payload 仍是密文,解密在 handler。
|
|
637
|
+
*
|
|
638
|
+
* @param {string} userId
|
|
639
|
+
* @param {number} sinceId - 上一页游标(0 = 从头)
|
|
640
|
+
* @param {number} limit
|
|
641
|
+
* @returns {Promise<Array<{ id: number, message_id: string, task_uuid: string|null,
|
|
642
|
+
* session_id: string|null, message_index: number|null, total_messages: number|null,
|
|
643
|
+
* payload: string, created_at: number, delivered_at: number|null }>>}
|
|
644
|
+
*/
|
|
645
|
+
listUnackedOutbox(userId: string, sinceId?: number, limit?: number): Promise<Array<{
|
|
646
|
+
id: number;
|
|
647
|
+
message_id: string;
|
|
648
|
+
task_uuid: string | null;
|
|
649
|
+
session_id: string | null;
|
|
650
|
+
message_index: number | null;
|
|
651
|
+
total_messages: number | null;
|
|
652
|
+
payload: string;
|
|
653
|
+
created_at: number;
|
|
654
|
+
delivered_at: number | null;
|
|
655
|
+
}>>;
|
|
656
|
+
/**
|
|
657
|
+
* 客户端确认收到这一批(幂等:已 ack 的行再 ack 不动)。
|
|
658
|
+
*
|
|
659
|
+
* @param {string} userId
|
|
660
|
+
* @param {string[]} messageIds
|
|
661
|
+
* @param {number} ackedAt - epoch 毫秒
|
|
662
|
+
* @returns {Promise<number>} 本次真正被 ack 的行数
|
|
663
|
+
*/
|
|
664
|
+
ackOutboxMessages(userId: string, messageIds: string[], ackedAt: number): Promise<number>;
|
|
665
|
+
/**
|
|
666
|
+
* 主动删 outbox 的行:数组删指定那几条,传 null 删这个用户的全部。
|
|
667
|
+
*
|
|
668
|
+
* 跟 `discardOutboxMessages` 是两件事:那个只撤「还没发出去的」,是取消 / 顶
|
|
669
|
+
* 替时的收尾;这个不看 delivered_at / acked_at,是宿主的清理口(对完账之后
|
|
670
|
+
* 把某几条、或者整个收件箱清掉)。在此之前 message_outbox 只能等 cron 的
|
|
671
|
+
* TTL(已签收 7 天 / 任何行 28 天)自己老化。
|
|
672
|
+
*
|
|
673
|
+
* 数组形态走 `_runInClauseWrite`:D1 单条语句最多 100 个绑定参数,`user_id`
|
|
674
|
+
* 占掉 1 个,所以一批最多 99 个 id,多了自动切批、整组仍在一个事务里(切开
|
|
675
|
+
* 之后「只删掉前 99 个」那种中间态比原问题更难查)。
|
|
676
|
+
*
|
|
677
|
+
* @param {string} userId
|
|
678
|
+
* @param {string[]|null} messageIds - null = 这个用户的全部
|
|
679
|
+
* @returns {Promise<number>} 删掉的行数
|
|
680
|
+
*/
|
|
681
|
+
deleteOutboxMessages(userId: string, messageIds?: string[] | null): Promise<number>;
|
|
682
|
+
/**
|
|
683
|
+
* outbox 的例行清理(run-tick 每跳顺手调):已 ack 的行留短一些,未 ack 的
|
|
684
|
+
* 也不无限留(Web Push TTL 上限四周,比它更老的推送谁也收不到了)。
|
|
685
|
+
*
|
|
686
|
+
* @param {{ ackedBeforeMs?: number, allBeforeMs?: number }} opts - epoch 毫秒阈值
|
|
687
|
+
* @returns {Promise<number>} 删掉的行数
|
|
688
|
+
*/
|
|
689
|
+
cleanupOutbox({ ackedBeforeMs, allBeforeMs }?: {
|
|
690
|
+
ackedBeforeMs?: number;
|
|
691
|
+
allBeforeMs?: number;
|
|
692
|
+
}): Promise<number>;
|
|
693
|
+
}
|