@rei-standard/amsg-server 2.6.0-next.24 → 2.6.0-next.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.
package/README.md CHANGED
@@ -578,6 +578,8 @@ clientStateTtl: {
578
578
 
579
579
  一个坑说在前头:`PUT /client-state` 和 `writeState()` 的条件写护栏(entry 上的 `version`)落的就是 `updated_at` 这一列。护栏值传的是自增计数器之类的小整数时,这行的 `updated_at` 看起来就像 1970 年,第一次清理就会被扫走。要给某个命名空间配 TTL,就让它的写入方把 `version` 传成毫秒时间戳。
580
580
 
581
+ 反过来,`updated_at` 大幅领先真实时间的行(设备时钟跑偏时同步上来的)要等真实时间追过去才轮得到清理。这种行不会卡住写入——条件写认得出它来自未来,照样覆盖。
582
+
581
583
  `GET /capabilities` 的 features 里有 `client-state-ttl`。
582
584
 
583
585
  ## 循环任务的时区(`tzId`)
@@ -185,8 +185,17 @@ export class D1Adapter {
185
185
  * than the stored row (updatedAt strictly lower) is skipped; equal or
186
186
  * newer overwrites. Values arrive pre-encrypted (the handler encrypts).
187
187
  *
188
+ * 例外是「来自未来」的行:`updated_at` 晚于服务端当前时间的行一律放行覆盖。
189
+ * 比较值是客户端自己报的时间戳,设备时钟只要领先过真实时间(用户改过系统
190
+ * 时间、时区或日期误操作),那一刻同步上来的行就带着一个还没到的时刻;之后
191
+ * 这台设备发什么都比它「旧」,条件写全被无声跳过,云端那行要等真实时间追上
192
+ * 去才解得开——客户端删本地数据、重装都碰不到它。合法写入不可能来自未来,
193
+ * 所以这种行按脏数据处理:服务端的钟是可信的那一个,拿它当判据放行。一次
194
+ * 正常写入就把 `updated_at` 拉回现实,之后旧不盖新照常生效。
195
+ *
188
196
  * `cleanups` 是删除项:在同一 batch 里先于 upsert 执行,`updated_at <= ?`
189
- * 条件保证陈旧批次删不动更新写入的行。两种形态——
197
+ * 条件保证陈旧批次删不动更新写入的行(同样对未来时间戳的行放行,否则删一条
198
+ * 状态时切片行留在库里成孤儿)。两种形态——
190
199
  * - `{ namespace, keyPrefix, updatedAt }` 删 key 前缀下的所有行,用来清掉
191
200
  * 大值旧写入留下的切片行(见 lib/state-chunks.js);
192
201
  * - `{ namespace, key, updatedAt }` 删这一个 key,用来删整条状态(前缀会
@@ -204,6 +213,9 @@ export class D1Adapter {
204
213
  * @param {string} userId
205
214
  * @param {Array<{ namespace: string, key: string, value: string, updatedAt: number }>} entries
206
215
  * @param {Array<{ namespace: string, key?: string, keyPrefix?: string, updatedAt: number }>} [cleanups]
216
+ * @param {number} [now] - 服务端当前时刻(epoch 毫秒),判定「这行来自未来」用的
217
+ * 就是它。调用方(lib/client-state-store.js)传下来,测试可以钉住一个假时钟;
218
+ * 自定义调用方不传时退回本机时钟。
207
219
  * @returns {Promise<{ upserted: number, skipped: number, outcomes: boolean[] }>}
208
220
  * `outcomes[i]` 对应 entries[i] 是否真的写入(changes > 0)。
209
221
  */
@@ -217,7 +229,7 @@ export class D1Adapter {
217
229
  key?: string;
218
230
  keyPrefix?: string;
219
231
  updatedAt: number;
220
- }>): Promise<{
232
+ }>, now?: number): Promise<{
221
233
  upserted: number;
222
234
  skipped: number;
223
235
  outcomes: boolean[];
@@ -143,6 +143,9 @@ export type DbAdapter = {
143
143
  * 旧写入留下的切片行,见 lib/state-chunks.js),带 `key` 的删这一个 key(删整条状态;前缀会连带
144
144
  * 删掉同前缀的兄弟 key)。两种都只删 `updated_at <= updatedAt` 的行。自定义 adapter 忽略前缀形态
145
145
  * 只损失存储卫生;忽略精确 key 形态则删不掉状态,`ctx.writeState()` 的删除会失效。
146
+ * `now` 是服务端当前时刻(epoch 毫秒):库里 `updated_at` 晚于它的行只可能来自跑偏的设备时钟,
147
+ * 写入与删除都对这种行放行,否则那一行会一直卡到真实时间追上去为止。忽略这个参数的自定义
148
+ * adapter 功能不变,只是没有这道解锁。
146
149
  * `outcomes` 逐条报告 entries[i] 是否真的写入(缺席时调用方按物理行计数兜底)。
147
150
  */
148
151
  upsertClientState?: (userId: string, entries: Array<{
@@ -155,7 +158,7 @@ export type DbAdapter = {
155
158
  key?: string;
156
159
  keyPrefix?: string;
157
160
  updatedAt: number;
158
- }>) => Promise<{
161
+ }>, now?: number) => Promise<{
159
162
  upserted: number;
160
163
  skipped: number;
161
164
  outcomes?: boolean[];
@@ -1058,14 +1058,17 @@ function planClientStateCleanup(ttl, now) {
1058
1058
  }
1059
1059
  return targets;
1060
1060
  }
1061
- async function writeClientStateEntries({ db, userId, userKey, entries }) {
1061
+ async function writeClientStateEntries({ db, userId, userKey, entries, now }) {
1062
+ const nowFn = typeof now === "function" ? now : Date.now;
1063
+ const at = nowFn();
1062
1064
  const physicalRows = [];
1063
1065
  const cleanups = [];
1064
1066
  const rootRowIndexes = [];
1065
1067
  const rootRowEntries = [];
1066
1068
  let deleted = 0;
1067
1069
  for (const entry of entries) {
1068
- const guardAt = Number.isInteger(entry.version) && entry.version > 0 ? entry.version : entry.updatedAt;
1070
+ const rawGuardAt = Number.isInteger(entry.version) && entry.version > 0 ? entry.version : entry.updatedAt;
1071
+ const guardAt = Math.min(rawGuardAt, at);
1069
1072
  cleanups.push({
1070
1073
  namespace: chunkNamespaceFor(entry.namespace),
1071
1074
  keyPrefix: chunkKeyPrefixFor(entry.key),
@@ -1111,7 +1114,7 @@ async function writeClientStateEntries({ db, userId, userKey, entries }) {
1111
1114
  if (physicalRows.length === 0 && cleanups.length === 0) {
1112
1115
  return { upserted: 0, skipped: 0, deleted: 0, skippedEntries: [] };
1113
1116
  }
1114
- const result = await db.upsertClientState(userId, physicalRows, cleanups);
1117
+ const result = await db.upsertClientState(userId, physicalRows, cleanups, at);
1115
1118
  let upserted = 0;
1116
1119
  let skipped = 0;
1117
1120
  const skippedEntries = [];
@@ -1204,7 +1207,7 @@ function createStateAccessors({ db, userId, userKey, maxStateValueBytes, now })
1204
1207
  ...entry.version !== void 0 ? { version: entry.version } : {}
1205
1208
  };
1206
1209
  });
1207
- return writeClientStateEntries({ db, userId, userKey, entries: normalized });
1210
+ return writeClientStateEntries({ db, userId, userKey, entries: normalized, now: nowFn });
1208
1211
  };
1209
1212
  return { readState, writeState };
1210
1213
  }
@@ -4649,8 +4652,17 @@ var D1Adapter = class {
4649
4652
  * than the stored row (updatedAt strictly lower) is skipped; equal or
4650
4653
  * newer overwrites. Values arrive pre-encrypted (the handler encrypts).
4651
4654
  *
4655
+ * 例外是「来自未来」的行:`updated_at` 晚于服务端当前时间的行一律放行覆盖。
4656
+ * 比较值是客户端自己报的时间戳,设备时钟只要领先过真实时间(用户改过系统
4657
+ * 时间、时区或日期误操作),那一刻同步上来的行就带着一个还没到的时刻;之后
4658
+ * 这台设备发什么都比它「旧」,条件写全被无声跳过,云端那行要等真实时间追上
4659
+ * 去才解得开——客户端删本地数据、重装都碰不到它。合法写入不可能来自未来,
4660
+ * 所以这种行按脏数据处理:服务端的钟是可信的那一个,拿它当判据放行。一次
4661
+ * 正常写入就把 `updated_at` 拉回现实,之后旧不盖新照常生效。
4662
+ *
4652
4663
  * `cleanups` 是删除项:在同一 batch 里先于 upsert 执行,`updated_at <= ?`
4653
- * 条件保证陈旧批次删不动更新写入的行。两种形态——
4664
+ * 条件保证陈旧批次删不动更新写入的行(同样对未来时间戳的行放行,否则删一条
4665
+ * 状态时切片行留在库里成孤儿)。两种形态——
4654
4666
  * - `{ namespace, keyPrefix, updatedAt }` 删 key 前缀下的所有行,用来清掉
4655
4667
  * 大值旧写入留下的切片行(见 lib/state-chunks.js);
4656
4668
  * - `{ namespace, key, updatedAt }` 删这一个 key,用来删整条状态(前缀会
@@ -4668,24 +4680,30 @@ var D1Adapter = class {
4668
4680
  * @param {string} userId
4669
4681
  * @param {Array<{ namespace: string, key: string, value: string, updatedAt: number }>} entries
4670
4682
  * @param {Array<{ namespace: string, key?: string, keyPrefix?: string, updatedAt: number }>} [cleanups]
4683
+ * @param {number} [now] - 服务端当前时刻(epoch 毫秒),判定「这行来自未来」用的
4684
+ * 就是它。调用方(lib/client-state-store.js)传下来,测试可以钉住一个假时钟;
4685
+ * 自定义调用方不传时退回本机时钟。
4671
4686
  * @returns {Promise<{ upserted: number, skipped: number, outcomes: boolean[] }>}
4672
4687
  * `outcomes[i]` 对应 entries[i] 是否真的写入(changes > 0)。
4673
4688
  */
4674
- async upsertClientState(userId, entries, cleanups = []) {
4689
+ async upsertClientState(userId, entries, cleanups = [], now = Date.now()) {
4675
4690
  const UPSERT_SQL = `INSERT INTO client_state (user_id, namespace, key, value, updated_at)
4676
4691
  VALUES (?, ?, ?, ?, ?)
4677
4692
  ON CONFLICT (user_id, namespace, key) DO UPDATE SET
4678
4693
  value = excluded.value,
4679
4694
  updated_at = excluded.updated_at
4680
- WHERE excluded.updated_at >= client_state.updated_at`;
4695
+ WHERE excluded.updated_at >= client_state.updated_at
4696
+ OR client_state.updated_at > ?`;
4681
4697
  const CLEANUP_PREFIX_SQL = `DELETE FROM client_state
4682
- WHERE user_id = ? AND namespace = ? AND key >= ? AND key < ? AND updated_at <= ?`;
4698
+ WHERE user_id = ? AND namespace = ? AND key >= ? AND key < ?
4699
+ AND (updated_at <= ? OR updated_at > ?)`;
4683
4700
  const CLEANUP_KEY_SQL = `DELETE FROM client_state
4684
- WHERE user_id = ? AND namespace = ? AND key = ? AND updated_at <= ?`;
4701
+ WHERE user_id = ? AND namespace = ? AND key = ?
4702
+ AND (updated_at <= ? OR updated_at > ?)`;
4685
4703
  const buildStatements = () => [
4686
- ...cleanups.map((c) => typeof c.key === "string" ? this._db.prepare(CLEANUP_KEY_SQL).bind(userId, c.namespace, c.key, c.updatedAt) : this._db.prepare(CLEANUP_PREFIX_SQL).bind(userId, c.namespace, c.keyPrefix, prefixRangeEnd(c.keyPrefix), c.updatedAt)),
4704
+ ...cleanups.map((c) => typeof c.key === "string" ? this._db.prepare(CLEANUP_KEY_SQL).bind(userId, c.namespace, c.key, c.updatedAt, now) : this._db.prepare(CLEANUP_PREFIX_SQL).bind(userId, c.namespace, c.keyPrefix, prefixRangeEnd(c.keyPrefix), c.updatedAt, now)),
4687
4705
  ...entries.map(
4688
- (entry) => this._db.prepare(UPSERT_SQL).bind(userId, entry.namespace, entry.key, entry.value, entry.updatedAt)
4706
+ (entry) => this._db.prepare(UPSERT_SQL).bind(userId, entry.namespace, entry.key, entry.value, entry.updatedAt, now)
4689
4707
  )
4690
4708
  ];
4691
4709
  let results;
@@ -5307,7 +5325,7 @@ function createClientStateHandler(ctx) {
5307
5325
  }
5308
5326
 
5309
5327
  // src/server/lib/version.js
5310
- var SERVER_VERSION = true ? "2.6.0-next.24" : "0.0.0-dev";
5328
+ var SERVER_VERSION = true ? "2.6.0-next.26" : "0.0.0-dev";
5311
5329
 
5312
5330
  // src/server/handlers/capabilities.js
5313
5331
  var SERVER_FEATURES = Object.freeze([
@@ -1058,14 +1058,17 @@ function planClientStateCleanup(ttl, now) {
1058
1058
  }
1059
1059
  return targets;
1060
1060
  }
1061
- async function writeClientStateEntries({ db, userId, userKey, entries }) {
1061
+ async function writeClientStateEntries({ db, userId, userKey, entries, now }) {
1062
+ const nowFn = typeof now === "function" ? now : Date.now;
1063
+ const at = nowFn();
1062
1064
  const physicalRows = [];
1063
1065
  const cleanups = [];
1064
1066
  const rootRowIndexes = [];
1065
1067
  const rootRowEntries = [];
1066
1068
  let deleted = 0;
1067
1069
  for (const entry of entries) {
1068
- const guardAt = Number.isInteger(entry.version) && entry.version > 0 ? entry.version : entry.updatedAt;
1070
+ const rawGuardAt = Number.isInteger(entry.version) && entry.version > 0 ? entry.version : entry.updatedAt;
1071
+ const guardAt = Math.min(rawGuardAt, at);
1069
1072
  cleanups.push({
1070
1073
  namespace: chunkNamespaceFor(entry.namespace),
1071
1074
  keyPrefix: chunkKeyPrefixFor(entry.key),
@@ -1111,7 +1114,7 @@ async function writeClientStateEntries({ db, userId, userKey, entries }) {
1111
1114
  if (physicalRows.length === 0 && cleanups.length === 0) {
1112
1115
  return { upserted: 0, skipped: 0, deleted: 0, skippedEntries: [] };
1113
1116
  }
1114
- const result = await db.upsertClientState(userId, physicalRows, cleanups);
1117
+ const result = await db.upsertClientState(userId, physicalRows, cleanups, at);
1115
1118
  let upserted = 0;
1116
1119
  let skipped = 0;
1117
1120
  const skippedEntries = [];
@@ -1204,7 +1207,7 @@ function createStateAccessors({ db, userId, userKey, maxStateValueBytes, now })
1204
1207
  ...entry.version !== void 0 ? { version: entry.version } : {}
1205
1208
  };
1206
1209
  });
1207
- return writeClientStateEntries({ db, userId, userKey, entries: normalized });
1210
+ return writeClientStateEntries({ db, userId, userKey, entries: normalized, now: nowFn });
1208
1211
  };
1209
1212
  return { readState, writeState };
1210
1213
  }
@@ -4649,8 +4652,17 @@ var D1Adapter = class {
4649
4652
  * than the stored row (updatedAt strictly lower) is skipped; equal or
4650
4653
  * newer overwrites. Values arrive pre-encrypted (the handler encrypts).
4651
4654
  *
4655
+ * 例外是「来自未来」的行:`updated_at` 晚于服务端当前时间的行一律放行覆盖。
4656
+ * 比较值是客户端自己报的时间戳,设备时钟只要领先过真实时间(用户改过系统
4657
+ * 时间、时区或日期误操作),那一刻同步上来的行就带着一个还没到的时刻;之后
4658
+ * 这台设备发什么都比它「旧」,条件写全被无声跳过,云端那行要等真实时间追上
4659
+ * 去才解得开——客户端删本地数据、重装都碰不到它。合法写入不可能来自未来,
4660
+ * 所以这种行按脏数据处理:服务端的钟是可信的那一个,拿它当判据放行。一次
4661
+ * 正常写入就把 `updated_at` 拉回现实,之后旧不盖新照常生效。
4662
+ *
4652
4663
  * `cleanups` 是删除项:在同一 batch 里先于 upsert 执行,`updated_at <= ?`
4653
- * 条件保证陈旧批次删不动更新写入的行。两种形态——
4664
+ * 条件保证陈旧批次删不动更新写入的行(同样对未来时间戳的行放行,否则删一条
4665
+ * 状态时切片行留在库里成孤儿)。两种形态——
4654
4666
  * - `{ namespace, keyPrefix, updatedAt }` 删 key 前缀下的所有行,用来清掉
4655
4667
  * 大值旧写入留下的切片行(见 lib/state-chunks.js);
4656
4668
  * - `{ namespace, key, updatedAt }` 删这一个 key,用来删整条状态(前缀会
@@ -4668,24 +4680,30 @@ var D1Adapter = class {
4668
4680
  * @param {string} userId
4669
4681
  * @param {Array<{ namespace: string, key: string, value: string, updatedAt: number }>} entries
4670
4682
  * @param {Array<{ namespace: string, key?: string, keyPrefix?: string, updatedAt: number }>} [cleanups]
4683
+ * @param {number} [now] - 服务端当前时刻(epoch 毫秒),判定「这行来自未来」用的
4684
+ * 就是它。调用方(lib/client-state-store.js)传下来,测试可以钉住一个假时钟;
4685
+ * 自定义调用方不传时退回本机时钟。
4671
4686
  * @returns {Promise<{ upserted: number, skipped: number, outcomes: boolean[] }>}
4672
4687
  * `outcomes[i]` 对应 entries[i] 是否真的写入(changes > 0)。
4673
4688
  */
4674
- async upsertClientState(userId, entries, cleanups = []) {
4689
+ async upsertClientState(userId, entries, cleanups = [], now = Date.now()) {
4675
4690
  const UPSERT_SQL = `INSERT INTO client_state (user_id, namespace, key, value, updated_at)
4676
4691
  VALUES (?, ?, ?, ?, ?)
4677
4692
  ON CONFLICT (user_id, namespace, key) DO UPDATE SET
4678
4693
  value = excluded.value,
4679
4694
  updated_at = excluded.updated_at
4680
- WHERE excluded.updated_at >= client_state.updated_at`;
4695
+ WHERE excluded.updated_at >= client_state.updated_at
4696
+ OR client_state.updated_at > ?`;
4681
4697
  const CLEANUP_PREFIX_SQL = `DELETE FROM client_state
4682
- WHERE user_id = ? AND namespace = ? AND key >= ? AND key < ? AND updated_at <= ?`;
4698
+ WHERE user_id = ? AND namespace = ? AND key >= ? AND key < ?
4699
+ AND (updated_at <= ? OR updated_at > ?)`;
4683
4700
  const CLEANUP_KEY_SQL = `DELETE FROM client_state
4684
- WHERE user_id = ? AND namespace = ? AND key = ? AND updated_at <= ?`;
4701
+ WHERE user_id = ? AND namespace = ? AND key = ?
4702
+ AND (updated_at <= ? OR updated_at > ?)`;
4685
4703
  const buildStatements = () => [
4686
- ...cleanups.map((c) => typeof c.key === "string" ? this._db.prepare(CLEANUP_KEY_SQL).bind(userId, c.namespace, c.key, c.updatedAt) : this._db.prepare(CLEANUP_PREFIX_SQL).bind(userId, c.namespace, c.keyPrefix, prefixRangeEnd(c.keyPrefix), c.updatedAt)),
4704
+ ...cleanups.map((c) => typeof c.key === "string" ? this._db.prepare(CLEANUP_KEY_SQL).bind(userId, c.namespace, c.key, c.updatedAt, now) : this._db.prepare(CLEANUP_PREFIX_SQL).bind(userId, c.namespace, c.keyPrefix, prefixRangeEnd(c.keyPrefix), c.updatedAt, now)),
4687
4705
  ...entries.map(
4688
- (entry) => this._db.prepare(UPSERT_SQL).bind(userId, entry.namespace, entry.key, entry.value, entry.updatedAt)
4706
+ (entry) => this._db.prepare(UPSERT_SQL).bind(userId, entry.namespace, entry.key, entry.value, entry.updatedAt, now)
4689
4707
  )
4690
4708
  ];
4691
4709
  let results;
@@ -5307,7 +5325,7 @@ function createClientStateHandler(ctx) {
5307
5325
  }
5308
5326
 
5309
5327
  // src/server/lib/version.js
5310
- var SERVER_VERSION = true ? "2.6.0-next.24" : "0.0.0-dev";
5328
+ var SERVER_VERSION = true ? "2.6.0-next.26" : "0.0.0-dev";
5311
5329
 
5312
5330
  // src/server/handlers/capabilities.js
5313
5331
  var SERVER_FEATURES = Object.freeze([
@@ -26,7 +26,7 @@
26
26
 
27
27
 
28
28
 
29
- var _chunkXLCBVU37cjs = require('./chunk-XLCBVU37.cjs');
29
+ var _chunkGXYVOEF3cjs = require('./chunk-GXYVOEF3.cjs');
30
30
  require('./chunk-7NMQFTDJ.cjs');
31
31
 
32
32
 
@@ -56,4 +56,4 @@ require('./chunk-7NMQFTDJ.cjs');
56
56
 
57
57
 
58
58
 
59
- exports.DEFAULT_MAX_REQUEST_BODY_BYTES = _chunkXLCBVU37cjs.DEFAULT_MAX_REQUEST_BODY_BYTES; exports.MAX_PUSH_PAYLOAD_BYTES = _chunkXLCBVU37cjs.MAX_PUSH_PAYLOAD_BYTES; exports.NonRetryableError = _chunkXLCBVU37cjs.NonRetryableError; exports.PUSH_ENVELOPE_RESERVED_BYTES = _chunkXLCBVU37cjs.PUSH_ENVELOPE_RESERVED_BYTES; exports.SCHEMA_VERSION = _chunkXLCBVU37cjs.SCHEMA_VERSION; exports.WEB_PUSH_ENCRYPTION_OVERHEAD_BYTES = _chunkXLCBVU37cjs.WEB_PUSH_ENCRYPTION_OVERHEAD_BYTES; exports.WEB_PUSH_MAX_BODY_BYTES = _chunkXLCBVU37cjs.WEB_PUSH_MAX_BODY_BYTES; exports.advanceOccurrence = _chunkXLCBVU37cjs.advanceOccurrence; exports.createD1Adapter = _chunkXLCBVU37cjs.createD1Adapter; exports.createSingleUserCloudflareWorker = _chunkXLCBVU37cjs.createSingleUserCloudflareWorker; exports.createSingleUserServer = _chunkXLCBVU37cjs.createSingleUserServer; exports.createWebCryptoWebPush = _chunkXLCBVU37cjs.createWebCryptoWebPush; exports.decryptFromStorage = _chunkXLCBVU37cjs.decryptFromStorage; exports.decryptPayload = _chunkXLCBVU37cjs.decryptPayload; exports.deriveUserEncryptionKey = _chunkXLCBVU37cjs.deriveUserEncryptionKey; exports.encryptForStorage = _chunkXLCBVU37cjs.encryptForStorage; exports.ensureSchema = _chunkXLCBVU37cjs.ensureSchema; exports.getSchemaVersion = _chunkXLCBVU37cjs.getSchemaVersion; exports.isNonRetryableError = _chunkXLCBVU37cjs.isNonRetryableError; exports.isValidTimeZoneId = _chunkXLCBVU37cjs.isValidTimeZoneId; exports.measurePushPayload = _chunkXLCBVU37cjs.measurePushPayload; exports.nextFutureOccurrence = _chunkXLCBVU37cjs.nextFutureOccurrence; exports.planNextOccurrence = _chunkXLCBVU37cjs.planNextOccurrence; exports.readRequestBody = _chunkXLCBVU37cjs.readRequestBody; exports.runScheduledTick = _chunkXLCBVU37cjs.runScheduledTick; exports.runTask = _chunkXLCBVU37cjs.runTask; exports.summarizeErrorCause = _chunkXLCBVU37cjs.summarizeErrorCause;
59
+ exports.DEFAULT_MAX_REQUEST_BODY_BYTES = _chunkGXYVOEF3cjs.DEFAULT_MAX_REQUEST_BODY_BYTES; exports.MAX_PUSH_PAYLOAD_BYTES = _chunkGXYVOEF3cjs.MAX_PUSH_PAYLOAD_BYTES; exports.NonRetryableError = _chunkGXYVOEF3cjs.NonRetryableError; exports.PUSH_ENVELOPE_RESERVED_BYTES = _chunkGXYVOEF3cjs.PUSH_ENVELOPE_RESERVED_BYTES; exports.SCHEMA_VERSION = _chunkGXYVOEF3cjs.SCHEMA_VERSION; exports.WEB_PUSH_ENCRYPTION_OVERHEAD_BYTES = _chunkGXYVOEF3cjs.WEB_PUSH_ENCRYPTION_OVERHEAD_BYTES; exports.WEB_PUSH_MAX_BODY_BYTES = _chunkGXYVOEF3cjs.WEB_PUSH_MAX_BODY_BYTES; exports.advanceOccurrence = _chunkGXYVOEF3cjs.advanceOccurrence; exports.createD1Adapter = _chunkGXYVOEF3cjs.createD1Adapter; exports.createSingleUserCloudflareWorker = _chunkGXYVOEF3cjs.createSingleUserCloudflareWorker; exports.createSingleUserServer = _chunkGXYVOEF3cjs.createSingleUserServer; exports.createWebCryptoWebPush = _chunkGXYVOEF3cjs.createWebCryptoWebPush; exports.decryptFromStorage = _chunkGXYVOEF3cjs.decryptFromStorage; exports.decryptPayload = _chunkGXYVOEF3cjs.decryptPayload; exports.deriveUserEncryptionKey = _chunkGXYVOEF3cjs.deriveUserEncryptionKey; exports.encryptForStorage = _chunkGXYVOEF3cjs.encryptForStorage; exports.ensureSchema = _chunkGXYVOEF3cjs.ensureSchema; exports.getSchemaVersion = _chunkGXYVOEF3cjs.getSchemaVersion; exports.isNonRetryableError = _chunkGXYVOEF3cjs.isNonRetryableError; exports.isValidTimeZoneId = _chunkGXYVOEF3cjs.isValidTimeZoneId; exports.measurePushPayload = _chunkGXYVOEF3cjs.measurePushPayload; exports.nextFutureOccurrence = _chunkGXYVOEF3cjs.nextFutureOccurrence; exports.planNextOccurrence = _chunkGXYVOEF3cjs.planNextOccurrence; exports.readRequestBody = _chunkGXYVOEF3cjs.readRequestBody; exports.runScheduledTick = _chunkGXYVOEF3cjs.runScheduledTick; exports.runTask = _chunkGXYVOEF3cjs.runTask; exports.summarizeErrorCause = _chunkGXYVOEF3cjs.summarizeErrorCause;
@@ -26,7 +26,7 @@ import {
26
26
  runScheduledTick,
27
27
  runTask,
28
28
  summarizeErrorCause
29
- } from "./chunk-N7CZ3KXB.mjs";
29
+ } from "./chunk-4YH3TS4W.mjs";
30
30
  import "./chunk-GN44PST5.mjs";
31
31
  export {
32
32
  DEFAULT_MAX_REQUEST_BODY_BYTES,
package/dist/index.cjs CHANGED
@@ -71,7 +71,7 @@
71
71
 
72
72
 
73
73
 
74
- var _chunkXLCBVU37cjs = require('./chunk-XLCBVU37.cjs');
74
+ var _chunkGXYVOEF3cjs = require('./chunk-GXYVOEF3.cjs');
75
75
  require('./chunk-7NMQFTDJ.cjs');
76
76
 
77
77
  // src/server/adapters/factory.js
@@ -105,7 +105,7 @@ async function createAdapter(config) {
105
105
  // src/server/handlers/init-tenant.js
106
106
  function createInitTenantHandler(ctx) {
107
107
  async function POST(headers, body) {
108
- const initSecret = _chunkXLCBVU37cjs.getHeader.call(void 0, headers, "x-init-secret");
108
+ const initSecret = _chunkGXYVOEF3cjs.getHeader.call(void 0, headers, "x-init-secret");
109
109
  if (ctx.tenant.initSecret && initSecret !== ctx.tenant.initSecret) {
110
110
  return {
111
111
  status: 401,
@@ -118,7 +118,7 @@ function createInitTenantHandler(ctx) {
118
118
  }
119
119
  };
120
120
  }
121
- const parsed = _chunkXLCBVU37cjs.parseJsonBody.call(void 0, body);
121
+ const parsed = _chunkGXYVOEF3cjs.parseJsonBody.call(void 0, body);
122
122
  if (!parsed.ok) {
123
123
  return {
124
124
  status: 400,
@@ -132,7 +132,7 @@ function createInitTenantHandler(ctx) {
132
132
  const tenantId = payload.tenantId || void 0;
133
133
  const driver = payload.driver;
134
134
  const databaseUrl = payload.databaseUrl || payload.connectionString;
135
- if (tenantId && !_chunkXLCBVU37cjs.isValidUUIDv4.call(void 0, tenantId)) {
135
+ if (tenantId && !_chunkGXYVOEF3cjs.isValidUUIDv4.call(void 0, tenantId)) {
136
136
  return {
137
137
  status: 400,
138
138
  body: {
@@ -252,7 +252,7 @@ function createSendNotificationsHandler(ctx) {
252
252
  }
253
253
  };
254
254
  }
255
- const data = await _chunkXLCBVU37cjs.runScheduledTick.call(void 0, { ...ctx, db, masterKey });
255
+ const data = await _chunkGXYVOEF3cjs.runScheduledTick.call(void 0, { ...ctx, db, masterKey });
256
256
  return { status: 200, body: { success: true, data } };
257
257
  }
258
258
  return { POST };
@@ -274,16 +274,16 @@ function encryptConfig(config, kekBuffer) {
274
274
  const plaintext = Buffer.from(JSON.stringify(config), "utf8");
275
275
  const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
276
276
  const authTag = cipher.getAuthTag();
277
- return `v1.${_chunkXLCBVU37cjs.bytesToBase64Url.call(void 0, iv)}.${_chunkXLCBVU37cjs.bytesToBase64Url.call(void 0, authTag)}.${_chunkXLCBVU37cjs.bytesToBase64Url.call(void 0, ciphertext)}`;
277
+ return `v1.${_chunkGXYVOEF3cjs.bytesToBase64Url.call(void 0, iv)}.${_chunkGXYVOEF3cjs.bytesToBase64Url.call(void 0, authTag)}.${_chunkGXYVOEF3cjs.bytesToBase64Url.call(void 0, ciphertext)}`;
278
278
  }
279
279
  function decryptConfig(encrypted, kekBuffer) {
280
280
  const parts = String(encrypted || "").split(".");
281
281
  if (parts.length !== 4 || parts[0] !== "v1") {
282
282
  throw new Error("INVALID_TENANT_CONFIG");
283
283
  }
284
- const iv = _chunkXLCBVU37cjs.base64UrlToBytes.call(void 0, parts[1]);
285
- const authTag = _chunkXLCBVU37cjs.base64UrlToBytes.call(void 0, parts[2]);
286
- const ciphertext = _chunkXLCBVU37cjs.base64UrlToBytes.call(void 0, parts[3]);
284
+ const iv = _chunkGXYVOEF3cjs.base64UrlToBytes.call(void 0, parts[1]);
285
+ const authTag = _chunkGXYVOEF3cjs.base64UrlToBytes.call(void 0, parts[2]);
286
+ const ciphertext = _chunkGXYVOEF3cjs.base64UrlToBytes.call(void 0, parts[3]);
287
287
  const decipher = _crypto.createDecipheriv.call(void 0, "aes-256-gcm", kekBuffer, iv);
288
288
  decipher.setAuthTag(authTag);
289
289
  const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
@@ -351,7 +351,7 @@ function createTenantBlobStore(options) {
351
351
  // src/server/tenant/token.js
352
352
 
353
353
  function sign(input, secret) {
354
- return _chunkXLCBVU37cjs.bytesToBase64Url.call(void 0, _crypto.createHmac.call(void 0, "sha256", secret).update(input).digest());
354
+ return _chunkGXYVOEF3cjs.bytesToBase64Url.call(void 0, _crypto.createHmac.call(void 0, "sha256", secret).update(input).digest());
355
355
  }
356
356
  function nowEpochSeconds() {
357
357
  return Math.floor(Date.now() / 1e3);
@@ -367,8 +367,8 @@ function createTenantToken(params, secret) {
367
367
  v: 1
368
368
  };
369
369
  const header = { alg: "HS256", typ: "JWT" };
370
- const encodedHeader = _chunkXLCBVU37cjs.bytesToBase64Url.call(void 0, _chunkXLCBVU37cjs.utf8.call(void 0, JSON.stringify(header)));
371
- const encodedPayload = _chunkXLCBVU37cjs.bytesToBase64Url.call(void 0, _chunkXLCBVU37cjs.utf8.call(void 0, JSON.stringify(payload)));
370
+ const encodedHeader = _chunkGXYVOEF3cjs.bytesToBase64Url.call(void 0, _chunkGXYVOEF3cjs.utf8.call(void 0, JSON.stringify(header)));
371
+ const encodedPayload = _chunkGXYVOEF3cjs.bytesToBase64Url.call(void 0, _chunkGXYVOEF3cjs.utf8.call(void 0, JSON.stringify(payload)));
372
372
  const signingInput = `${encodedHeader}.${encodedPayload}`;
373
373
  const signature = sign(signingInput, secret);
374
374
  return `${signingInput}.${signature}`;
@@ -387,17 +387,17 @@ function verifyTenantToken(token, secret, options = {}) {
387
387
  let receivedBytes;
388
388
  let expectedBytes;
389
389
  try {
390
- receivedBytes = _chunkXLCBVU37cjs.base64UrlToBytes.call(void 0, receivedSignature);
391
- expectedBytes = _chunkXLCBVU37cjs.base64UrlToBytes.call(void 0, expectedSignature);
390
+ receivedBytes = _chunkGXYVOEF3cjs.base64UrlToBytes.call(void 0, receivedSignature);
391
+ expectedBytes = _chunkGXYVOEF3cjs.base64UrlToBytes.call(void 0, expectedSignature);
392
392
  } catch (e) {
393
393
  throw new Error("INVALID_TENANT_AUTH");
394
394
  }
395
- if (!_chunkXLCBVU37cjs.timingSafeEqualBytes.call(void 0, receivedBytes, expectedBytes)) {
395
+ if (!_chunkGXYVOEF3cjs.timingSafeEqualBytes.call(void 0, receivedBytes, expectedBytes)) {
396
396
  throw new Error("INVALID_TENANT_AUTH");
397
397
  }
398
398
  let payload;
399
399
  try {
400
- payload = JSON.parse(_chunkXLCBVU37cjs.utf8Decode.call(void 0, _chunkXLCBVU37cjs.base64UrlToBytes.call(void 0, encodedPayload)));
400
+ payload = JSON.parse(_chunkGXYVOEF3cjs.utf8Decode.call(void 0, _chunkGXYVOEF3cjs.base64UrlToBytes.call(void 0, encodedPayload)));
401
401
  } catch (e2) {
402
402
  throw new Error("INVALID_TENANT_AUTH");
403
403
  }
@@ -621,15 +621,15 @@ async function createReiServer(config) {
621
621
  return {
622
622
  handlers: {
623
623
  initTenant: createInitTenantHandler(ctx),
624
- getUserKey: _chunkXLCBVU37cjs.createGetUserKeyHandler.call(void 0, ctx),
625
- scheduleMessage: _chunkXLCBVU37cjs.createScheduleMessageHandler.call(void 0, ctx),
624
+ getUserKey: _chunkGXYVOEF3cjs.createGetUserKeyHandler.call(void 0, ctx),
625
+ scheduleMessage: _chunkGXYVOEF3cjs.createScheduleMessageHandler.call(void 0, ctx),
626
626
  sendNotifications: createSendNotificationsHandler(ctx),
627
- updateMessage: _chunkXLCBVU37cjs.createUpdateMessageHandler.call(void 0, ctx),
628
- cancelMessage: _chunkXLCBVU37cjs.createCancelMessageHandler.call(void 0, ctx),
629
- messages: _chunkXLCBVU37cjs.createMessagesHandler.call(void 0, ctx),
630
- getMessage: _chunkXLCBVU37cjs.createGetMessageHandler.call(void 0, ctx),
631
- pushSubscription: _chunkXLCBVU37cjs.createPushSubscriptionHandler.call(void 0, ctx),
632
- llmCredentials: _chunkXLCBVU37cjs.createLlmCredentialsHandler.call(void 0, ctx)
627
+ updateMessage: _chunkGXYVOEF3cjs.createUpdateMessageHandler.call(void 0, ctx),
628
+ cancelMessage: _chunkGXYVOEF3cjs.createCancelMessageHandler.call(void 0, ctx),
629
+ messages: _chunkGXYVOEF3cjs.createMessagesHandler.call(void 0, ctx),
630
+ getMessage: _chunkGXYVOEF3cjs.createGetMessageHandler.call(void 0, ctx),
631
+ pushSubscription: _chunkGXYVOEF3cjs.createPushSubscriptionHandler.call(void 0, ctx),
632
+ llmCredentials: _chunkGXYVOEF3cjs.createLlmCredentialsHandler.call(void 0, ctx)
633
633
  }
634
634
  };
635
635
  }
@@ -695,4 +695,4 @@ async function createReiServer(config) {
695
695
 
696
696
 
697
697
 
698
- exports.CORS_ALLOW_HEADERS = _chunkXLCBVU37cjs.CORS_ALLOW_HEADERS; exports.CORS_ALLOW_METHODS = _chunkXLCBVU37cjs.CORS_ALLOW_METHODS; exports.CRED_ID_MAX_LENGTH = _chunkXLCBVU37cjs.CRED_ID_MAX_LENGTH; exports.CRED_PUT_BATCH_MAX = _chunkXLCBVU37cjs.CRED_PUT_BATCH_MAX; exports.CRED_REFS_MAX_ENTRIES = _chunkXLCBVU37cjs.CRED_REFS_MAX_ENTRIES; exports.CRED_ROWS_PER_USER_MAX = _chunkXLCBVU37cjs.CRED_ROWS_PER_USER_MAX; exports.DEFAULT_CLAIM_LEASE_MS = _chunkXLCBVU37cjs.DEFAULT_CLAIM_LEASE_MS; exports.DEFAULT_HEARTBEAT_LEASE_TTL_MS = _chunkXLCBVU37cjs.DEFAULT_HEARTBEAT_LEASE_TTL_MS; exports.DEFAULT_LEASE_HEARTBEAT_MS = _chunkXLCBVU37cjs.DEFAULT_LEASE_HEARTBEAT_MS; exports.DEFAULT_MAX_REQUEST_BODY_BYTES = _chunkXLCBVU37cjs.DEFAULT_MAX_REQUEST_BODY_BYTES; exports.DEFAULT_MAX_SCHEDULED_TASKS_PER_FIRE = _chunkXLCBVU37cjs.DEFAULT_MAX_SCHEDULED_TASKS_PER_FIRE; exports.DEFAULT_MAX_TOOL_ITERATIONS = _chunkXLCBVU37cjs.DEFAULT_MAX_TOOL_ITERATIONS; exports.DEFAULT_TOTAL_TIMEOUT_MS = _chunkXLCBVU37cjs.DEFAULT_TOTAL_TIMEOUT_MS; exports.DeploymentConfigError = _chunkXLCBVU37cjs.DeploymentConfigError; exports.MAX_PUSH_PAYLOAD_BYTES = _chunkXLCBVU37cjs.MAX_PUSH_PAYLOAD_BYTES; exports.MAX_TASK_PAYLOAD_BYTES = _chunkXLCBVU37cjs.MAX_TASK_PAYLOAD_BYTES; exports.MIN_SCHEDULE_LEAD_MS = _chunkXLCBVU37cjs.MIN_SCHEDULE_LEAD_MS; exports.NonRetryableError = _chunkXLCBVU37cjs.NonRetryableError; exports.PUSH_ENVELOPE_RESERVED_BYTES = _chunkXLCBVU37cjs.PUSH_ENVELOPE_RESERVED_BYTES; exports.SCHEMA_VERSION = _chunkXLCBVU37cjs.SCHEMA_VERSION; exports.WEB_PUSH_ENCRYPTION_OVERHEAD_BYTES = _chunkXLCBVU37cjs.WEB_PUSH_ENCRYPTION_OVERHEAD_BYTES; exports.WEB_PUSH_MAX_BODY_BYTES = _chunkXLCBVU37cjs.WEB_PUSH_MAX_BODY_BYTES; exports.advanceOccurrence = _chunkXLCBVU37cjs.advanceOccurrence; exports.createAdapter = createAdapter; exports.createD1Adapter = _chunkXLCBVU37cjs.createD1Adapter; exports.createReiServer = createReiServer; exports.createSingleUserCloudflareWorker = _chunkXLCBVU37cjs.createSingleUserCloudflareWorker; exports.createSingleUserServer = _chunkXLCBVU37cjs.createSingleUserServer; exports.createTenantToken = createTenantToken; exports.createWebCryptoWebPush = _chunkXLCBVU37cjs.createWebCryptoWebPush; exports.decryptFromStorage = _chunkXLCBVU37cjs.decryptFromStorage; exports.decryptPayload = _chunkXLCBVU37cjs.decryptPayload; exports.deriveUserEncryptionKey = _chunkXLCBVU37cjs.deriveUserEncryptionKey; exports.encryptForStorage = _chunkXLCBVU37cjs.encryptForStorage; exports.ensureSchema = _chunkXLCBVU37cjs.ensureSchema; exports.getSchemaVersion = _chunkXLCBVU37cjs.getSchemaVersion; exports.hasChatCredRef = _chunkXLCBVU37cjs.hasChatCredRef; exports.isNonRetryableError = _chunkXLCBVU37cjs.isNonRetryableError; exports.isValidCredId = _chunkXLCBVU37cjs.isValidCredId; exports.isValidISO8601 = _chunkXLCBVU37cjs.isValidISO8601; exports.isValidTimeZoneId = _chunkXLCBVU37cjs.isValidTimeZoneId; exports.isValidUUID = _chunkXLCBVU37cjs.isValidUUID; exports.isValidUUIDv4 = _chunkXLCBVU37cjs.isValidUUIDv4; exports.isValidUrl = _chunkXLCBVU37cjs.isValidUrl; exports.measurePushPayload = _chunkXLCBVU37cjs.measurePushPayload; exports.nextFutureOccurrence = _chunkXLCBVU37cjs.nextFutureOccurrence; exports.planNextOccurrence = _chunkXLCBVU37cjs.planNextOccurrence; exports.readRequestBody = _chunkXLCBVU37cjs.readRequestBody; exports.resolveLlmCredential = _chunkXLCBVU37cjs.resolveLlmCredential; exports.runScheduledTick = _chunkXLCBVU37cjs.runScheduledTick; exports.runTask = _chunkXLCBVU37cjs.runTask; exports.sanitizeErrorSummary = _chunkXLCBVU37cjs.sanitizeErrorSummary; exports.summarizeErrorCause = _chunkXLCBVU37cjs.summarizeErrorCause; exports.supportsLlmCredentialsStore = _chunkXLCBVU37cjs.supportsLlmCredentialsStore; exports.validateAvatarUrl = _chunkXLCBVU37cjs.validateAvatarUrl; exports.validateCredRefs = _chunkXLCBVU37cjs.validateCredRefs; exports.validateCredValue = _chunkXLCBVU37cjs.validateCredValue; exports.validateLlmMessagesArray = _chunkXLCBVU37cjs.validateLlmMessagesArray; exports.validateScheduleMessagePayload = _chunkXLCBVU37cjs.validateScheduleMessagePayload; exports.validateSplitPattern = _chunkXLCBVU37cjs.validateSplitPattern; exports.verifyTenantToken = verifyTenantToken;
698
+ exports.CORS_ALLOW_HEADERS = _chunkGXYVOEF3cjs.CORS_ALLOW_HEADERS; exports.CORS_ALLOW_METHODS = _chunkGXYVOEF3cjs.CORS_ALLOW_METHODS; exports.CRED_ID_MAX_LENGTH = _chunkGXYVOEF3cjs.CRED_ID_MAX_LENGTH; exports.CRED_PUT_BATCH_MAX = _chunkGXYVOEF3cjs.CRED_PUT_BATCH_MAX; exports.CRED_REFS_MAX_ENTRIES = _chunkGXYVOEF3cjs.CRED_REFS_MAX_ENTRIES; exports.CRED_ROWS_PER_USER_MAX = _chunkGXYVOEF3cjs.CRED_ROWS_PER_USER_MAX; exports.DEFAULT_CLAIM_LEASE_MS = _chunkGXYVOEF3cjs.DEFAULT_CLAIM_LEASE_MS; exports.DEFAULT_HEARTBEAT_LEASE_TTL_MS = _chunkGXYVOEF3cjs.DEFAULT_HEARTBEAT_LEASE_TTL_MS; exports.DEFAULT_LEASE_HEARTBEAT_MS = _chunkGXYVOEF3cjs.DEFAULT_LEASE_HEARTBEAT_MS; exports.DEFAULT_MAX_REQUEST_BODY_BYTES = _chunkGXYVOEF3cjs.DEFAULT_MAX_REQUEST_BODY_BYTES; exports.DEFAULT_MAX_SCHEDULED_TASKS_PER_FIRE = _chunkGXYVOEF3cjs.DEFAULT_MAX_SCHEDULED_TASKS_PER_FIRE; exports.DEFAULT_MAX_TOOL_ITERATIONS = _chunkGXYVOEF3cjs.DEFAULT_MAX_TOOL_ITERATIONS; exports.DEFAULT_TOTAL_TIMEOUT_MS = _chunkGXYVOEF3cjs.DEFAULT_TOTAL_TIMEOUT_MS; exports.DeploymentConfigError = _chunkGXYVOEF3cjs.DeploymentConfigError; exports.MAX_PUSH_PAYLOAD_BYTES = _chunkGXYVOEF3cjs.MAX_PUSH_PAYLOAD_BYTES; exports.MAX_TASK_PAYLOAD_BYTES = _chunkGXYVOEF3cjs.MAX_TASK_PAYLOAD_BYTES; exports.MIN_SCHEDULE_LEAD_MS = _chunkGXYVOEF3cjs.MIN_SCHEDULE_LEAD_MS; exports.NonRetryableError = _chunkGXYVOEF3cjs.NonRetryableError; exports.PUSH_ENVELOPE_RESERVED_BYTES = _chunkGXYVOEF3cjs.PUSH_ENVELOPE_RESERVED_BYTES; exports.SCHEMA_VERSION = _chunkGXYVOEF3cjs.SCHEMA_VERSION; exports.WEB_PUSH_ENCRYPTION_OVERHEAD_BYTES = _chunkGXYVOEF3cjs.WEB_PUSH_ENCRYPTION_OVERHEAD_BYTES; exports.WEB_PUSH_MAX_BODY_BYTES = _chunkGXYVOEF3cjs.WEB_PUSH_MAX_BODY_BYTES; exports.advanceOccurrence = _chunkGXYVOEF3cjs.advanceOccurrence; exports.createAdapter = createAdapter; exports.createD1Adapter = _chunkGXYVOEF3cjs.createD1Adapter; exports.createReiServer = createReiServer; exports.createSingleUserCloudflareWorker = _chunkGXYVOEF3cjs.createSingleUserCloudflareWorker; exports.createSingleUserServer = _chunkGXYVOEF3cjs.createSingleUserServer; exports.createTenantToken = createTenantToken; exports.createWebCryptoWebPush = _chunkGXYVOEF3cjs.createWebCryptoWebPush; exports.decryptFromStorage = _chunkGXYVOEF3cjs.decryptFromStorage; exports.decryptPayload = _chunkGXYVOEF3cjs.decryptPayload; exports.deriveUserEncryptionKey = _chunkGXYVOEF3cjs.deriveUserEncryptionKey; exports.encryptForStorage = _chunkGXYVOEF3cjs.encryptForStorage; exports.ensureSchema = _chunkGXYVOEF3cjs.ensureSchema; exports.getSchemaVersion = _chunkGXYVOEF3cjs.getSchemaVersion; exports.hasChatCredRef = _chunkGXYVOEF3cjs.hasChatCredRef; exports.isNonRetryableError = _chunkGXYVOEF3cjs.isNonRetryableError; exports.isValidCredId = _chunkGXYVOEF3cjs.isValidCredId; exports.isValidISO8601 = _chunkGXYVOEF3cjs.isValidISO8601; exports.isValidTimeZoneId = _chunkGXYVOEF3cjs.isValidTimeZoneId; exports.isValidUUID = _chunkGXYVOEF3cjs.isValidUUID; exports.isValidUUIDv4 = _chunkGXYVOEF3cjs.isValidUUIDv4; exports.isValidUrl = _chunkGXYVOEF3cjs.isValidUrl; exports.measurePushPayload = _chunkGXYVOEF3cjs.measurePushPayload; exports.nextFutureOccurrence = _chunkGXYVOEF3cjs.nextFutureOccurrence; exports.planNextOccurrence = _chunkGXYVOEF3cjs.planNextOccurrence; exports.readRequestBody = _chunkGXYVOEF3cjs.readRequestBody; exports.resolveLlmCredential = _chunkGXYVOEF3cjs.resolveLlmCredential; exports.runScheduledTick = _chunkGXYVOEF3cjs.runScheduledTick; exports.runTask = _chunkGXYVOEF3cjs.runTask; exports.sanitizeErrorSummary = _chunkGXYVOEF3cjs.sanitizeErrorSummary; exports.summarizeErrorCause = _chunkGXYVOEF3cjs.summarizeErrorCause; exports.supportsLlmCredentialsStore = _chunkGXYVOEF3cjs.supportsLlmCredentialsStore; exports.validateAvatarUrl = _chunkGXYVOEF3cjs.validateAvatarUrl; exports.validateCredRefs = _chunkGXYVOEF3cjs.validateCredRefs; exports.validateCredValue = _chunkGXYVOEF3cjs.validateCredValue; exports.validateLlmMessagesArray = _chunkGXYVOEF3cjs.validateLlmMessagesArray; exports.validateScheduleMessagePayload = _chunkGXYVOEF3cjs.validateScheduleMessagePayload; exports.validateSplitPattern = _chunkGXYVOEF3cjs.validateSplitPattern; exports.verifyTenantToken = verifyTenantToken;
package/dist/index.mjs CHANGED
@@ -71,7 +71,7 @@ import {
71
71
  validateLlmMessagesArray,
72
72
  validateScheduleMessagePayload,
73
73
  validateSplitPattern
74
- } from "./chunk-N7CZ3KXB.mjs";
74
+ } from "./chunk-4YH3TS4W.mjs";
75
75
  import "./chunk-GN44PST5.mjs";
76
76
 
77
77
  // src/server/adapters/factory.js
@@ -29,7 +29,9 @@ export function planClientStateCleanup(ttl: Record<string, number> | null | unde
29
29
  * - 删除:根行按精确 key 删,切片行按 key 前缀删,一个 key 的数据清干净。
30
30
  *
31
31
  * 两者都受同一套 last-write-wins 约束:`updatedAt` 比库里已有值旧的写入
32
- * (或删除)不生效,陈旧批次盖不掉新数据。
32
+ * (或删除)不生效,陈旧批次盖不掉新数据。唯一的例外是库里那行「来自未来」
33
+ * (`updated_at` 晚于服务端当前时刻)——那种行只可能出自跑偏的设备时钟,不当
34
+ * 比较基准,写入放行,详见 adapters/d1.js 的 `upsertClientState`。
33
35
  *
34
36
  * 条件写护栏:条目可带可选的 `version`(毫秒时间戳或单调递增整数)。带了它,
35
37
  * 比较用的就是这个值而不是 `updatedAt`——同一个 key 有多个写入方时(例如
@@ -48,12 +50,14 @@ export function planClientStateCleanup(ttl: Record<string, number> | null | unde
48
50
  * @param {Array<{ namespace: string, key: string, value: string|null, updatedAt: number, version?: number }>} args.entries
49
51
  * `version`(可选):条件写护栏值,见文件头。带了它,这条的 last-write-wins
50
52
  * 比较用它(写进行的 updated_at 列);没带照旧用 `updatedAt`。
53
+ * @param {() => number} [args.now] - 取服务端当前时刻(测试可注入假时钟)。适配器
54
+ * 拿它认出库里「来自未来」的脏行。
51
55
  * @returns {Promise<{ upserted: number, skipped: number, deleted: number, skippedEntries: Array<{ namespace: string, key: string }> }>}
52
56
  * `upserted` / `skipped` 按逻辑条目计(切片行不计);`deleted` 是请求删除的
53
57
  * key 数,不代表这些 key 原本一定存在。`skippedEntries` 逐条列出被
54
58
  * last-write-wins 拦下的 key(适配器不回 outcomes 时为空数组——分不清是哪条)。
55
59
  */
56
- export function writeClientStateEntries({ db, userId, userKey, entries }: {
60
+ export function writeClientStateEntries({ db, userId, userKey, entries, now }: {
57
61
  db: {
58
62
  upsertClientState: Function;
59
63
  };
@@ -66,6 +70,7 @@ export function writeClientStateEntries({ db, userId, userKey, entries }: {
66
70
  updatedAt: number;
67
71
  version?: number;
68
72
  }>;
73
+ now?: () => number;
69
74
  }): Promise<{
70
75
  upserted: number;
71
76
  skipped: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rei-standard/amsg-server",
3
- "version": "2.6.0-next.24",
3
+ "version": "2.6.0-next.26",
4
4
  "description": "ReiStandard Active Messaging server SDK with pluggable database adapters. Three-axis push schema (messageKind / messageType / messageSubtype) from @rei-standard/amsg-shared. Auto-emits ReasoningPush when the LLM response carries reasoning_content.",
5
5
  "repository": {
6
6
  "type": "git",