@rei-standard/amsg-server 2.6.0-next.35 → 2.6.0-next.36

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
@@ -902,8 +902,8 @@ export default createSingleUserCloudflareWorker(buildConfig, {
902
902
  });
903
903
  ```
904
904
 
905
- - `onError` 放在工厂的第二个参数上,而不是 `buildConfig` 的返回值里:`buildConfig` 自己抛错时配置里的东西一个都读不到,而那恰恰是最需要被看见的一种故障。`stage` 在 cron 路径上是 `config`(配置构建失败 / VAPID 没配齐)或 `tick`(那一跳抛错)。VAPID 没配齐这一支没有异常对象,`error` 为 `null`、`cause.name` 是 `VapidNotConfigured`。
906
- - `scheduled()` 现在有返回值:`{ ok: true, summary }` 或 `{ ok: false, cause }`。Cloudflare 不看它,是给「自己包一层再转调 `scheduled`」的宿主和测试用的。
905
+ - `onError` 放在工厂的第二个参数上,而不是 `buildConfig` 的返回值里:`buildConfig` 自己抛错时配置里的东西一个都读不到,而那恰恰是最需要被看见的一种故障。`stage` 在 cron 路径上是 `config`(配置构建失败 / VAPID 没配齐)、`tick`(那一跳抛错)或 `cloud-cleanup`(云端数据清理那一步抛错,这一跳的消息照常投递)。VAPID 没配齐这一支没有异常对象,`error` 为 `null`、`cause.name` 是 `VapidNotConfigured`。
906
+ - `scheduled()` 现在有返回值:`{ ok: true, summary }` 或 `{ ok: false, cause }`,`ok` 只看消息投递这一段。云端数据清理那一步出错时,两种返回值上都会多一个 `cloudCleanupCause`。Cloudflare 不看它,是给「自己包一层再转调 `scheduled`」的宿主和测试用的。
907
907
 
908
908
  ## 导出 API(Exports)
909
909
 
@@ -119,7 +119,13 @@ export class D1Adapter {
119
119
  completed: boolean;
120
120
  }>;
121
121
  finishCloudMaintenanceBatch(name: any, cursor: any, completed: any): Promise<void>;
122
- indexCloudDataRecordWork(userId: any, kind: any, id: any, work: any, expectedWriteToken?: 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>;
123
129
  readCloudMaintenanceRecord(row: any): Promise<{
124
130
  id: any;
125
131
  userId: any;
@@ -42,7 +42,8 @@ export type DbAdapter = {
42
42
  */
43
43
  initSchema: () => Promise<InitSchemaResult>;
44
44
  /**
45
- * (可选)活库里现在实际有哪些表 / 列 / 索引,只读不写。
45
+ * (可选)活库里现在实际有哪些表 / 列 / 索引 / 触发器,只读不写。`triggers`
46
+ * 可以不回,不回时触发器这一项不查。
46
47
  * `getSchemaVersion` / `ensureSchema`(lib/schema-version.js)拿它跟这一版
47
48
  * 需要的清单对照;不实现的适配器调那两个函数会抛错。内置只有 D1 实现。
48
49
  */
@@ -5481,7 +5481,7 @@ var D1Adapter = class {
5481
5481
  WHERE w.kind='operation' AND w.next_run_at IS NOT NULL AND w.next_run_at<=?
5482
5482
  AND (r.lease_until IS NULL OR r.lease_until<=?)
5483
5483
  ORDER BY w.next_run_at LIMIT ?`).bind(now, now, limit).all();
5484
- return Promise.all((result.results || []).map((row) => this._cloudRecordForList(row)));
5484
+ return Promise.all((result.results || []).map(async (row) => ({ ...await this._cloudRecordForList(row), writeToken: row.write_token })));
5485
5485
  }
5486
5486
  async cleanupExpiredCloudDataRecords(now = Date.now(), limit = 100) {
5487
5487
  const result = await this._db.prepare(`DELETE FROM cloud_data_records WHERE rowid IN (
@@ -5501,17 +5501,34 @@ var D1Adapter = class {
5501
5501
  marker = await this._db.prepare("SELECT * FROM cloud_data_maintenance WHERE name=?").bind(name).first();
5502
5502
  }
5503
5503
  if (marker.completed) return { rows: [], completed: true };
5504
- const result = await this._db.prepare(`SELECT rowid AS maintenance_rowid,* FROM ${table}
5504
+ const columns = table === "cloud_data_records" ? "rowid AS maintenance_rowid,user_id,kind,id,updated_at,write_token,CASE WHEN kind='operation' THEN data END AS data" : "rowid AS maintenance_rowid";
5505
+ const result = await this._db.prepare(`SELECT ${columns} FROM ${table}
5505
5506
  WHERE rowid>? AND rowid<=? ORDER BY rowid LIMIT ?`).bind(marker.cursor, marker.upper_bound, limit).all();
5506
5507
  return { rows: result.results || [], upperBound: marker.upper_bound, cursor: marker.cursor, completed: false };
5507
5508
  }
5508
5509
  async finishCloudMaintenanceBatch(name, cursor, completed) {
5509
5510
  await this._db.prepare(`UPDATE cloud_data_maintenance SET cursor=MAX(cursor,?),completed=MAX(completed,?) WHERE name=?`).bind(cursor, completed ? 1 : 0, name).run();
5510
5511
  }
5511
- async indexCloudDataRecordWork(userId, kind, id, work, expectedWriteToken = null) {
5512
+ /**
5513
+ * Index a whole repair batch in one statement. Never overwrites an existing schedule, and
5514
+ * skips a record that was rewritten after the batch was read.
5515
+ */
5516
+ async indexCloudDataRecordWorkBatch(entries) {
5517
+ if (!entries.length) return;
5518
+ const rows = entries.map(({ userId, kind, id, work, writeToken }) => [userId, kind, id, work.nextRunAt ?? null, work.expiresAt ?? null, writeToken ?? null]);
5512
5519
  await this._db.prepare(`INSERT INTO cloud_data_work (user_id,kind,id,next_run_at,expires_at)
5513
- SELECT user_id,kind,id,?,? FROM cloud_data_records WHERE user_id=? AND kind=? AND id=? AND write_token IS ?
5514
- ON CONFLICT (user_id,kind,id) DO NOTHING`).bind(work.nextRunAt ?? null, work.expiresAt ?? null, userId, kind, id, expectedWriteToken).run();
5520
+ SELECT r.user_id,r.kind,r.id,json_extract(j.value,'$[3]'),json_extract(j.value,'$[4]')
5521
+ FROM json_each(?) j JOIN cloud_data_records r ON r.user_id=json_extract(j.value,'$[0]')
5522
+ AND r.kind=json_extract(j.value,'$[1]') AND r.id=json_extract(j.value,'$[2]')
5523
+ AND r.write_token IS json_extract(j.value,'$[5]')
5524
+ WHERE 1 ON CONFLICT (user_id,kind,id) DO NOTHING`).bind(JSON.stringify(rows)).run();
5525
+ }
5526
+ /** Move an indexed record's schedule, unless the record was rewritten since the caller read it. */
5527
+ async rescheduleCloudDataRecordWork(userId, kind, id, work, expectedWriteToken = null) {
5528
+ const result = await this._db.prepare(`UPDATE cloud_data_work SET next_run_at=?,expires_at=?
5529
+ WHERE user_id=? AND kind=? AND id=? AND EXISTS (SELECT 1 FROM cloud_data_records r
5530
+ WHERE r.user_id=cloud_data_work.user_id AND r.kind=cloud_data_work.kind AND r.id=cloud_data_work.id AND r.write_token IS ?)`).bind(work.nextRunAt ?? null, work.expiresAt ?? null, userId, kind, id, expectedWriteToken).run();
5531
+ return result.meta.changes > 0;
5515
5532
  }
5516
5533
  async readCloudMaintenanceRecord(row) {
5517
5534
  return this._cloudRecordForList(row);
@@ -7011,7 +7028,7 @@ function createClientStateNamespacesHandler(ctx) {
7011
7028
  }
7012
7029
 
7013
7030
  // src/server/lib/version.js
7014
- var SERVER_VERSION = true ? "2.6.0-next.35" : "0.0.0-dev";
7031
+ var SERVER_VERSION = true ? "2.6.0-next.36" : "0.0.0-dev";
7015
7032
 
7016
7033
  // src/server/handlers/capabilities.js
7017
7034
  var SERVER_FEATURES = Object.freeze([
@@ -7816,12 +7833,23 @@ async function advanceCleanup(ctx, db, userId, key, operation) {
7816
7833
  }
7817
7834
  return operation;
7818
7835
  }
7836
+ var WORK_INDEX_METHODS = [
7837
+ "listDueCloudDataOperations",
7838
+ "cleanupExpiredCloudDataRecords",
7839
+ "rescheduleCloudDataRecordWork",
7840
+ "getCloudMaintenanceBatch",
7841
+ "finishCloudMaintenanceBatch",
7842
+ "readCloudMaintenanceRecord",
7843
+ "indexCloudDataRecordWorkBatch",
7844
+ "repairCloudResourceMetadata"
7845
+ ];
7846
+ var UNREADABLE_RETRY_MS = 15 * 6e4;
7819
7847
  async function repairLegacyWorkIndex(ctx) {
7820
7848
  const db = ctx.db;
7821
- if (typeof db.getCloudMaintenanceBatch !== "function") return;
7822
7849
  const name = "management-work-v1";
7823
7850
  const batch = await db.getCloudMaintenanceBatch(name, "cloud_data_records", 100);
7824
7851
  if (batch.completed) return;
7852
+ const entries = [];
7825
7853
  for (const row of batch.rows) {
7826
7854
  let work;
7827
7855
  if (["inventory", "plan"].includes(row.kind)) {
@@ -7832,31 +7860,59 @@ async function repairLegacyWorkIndex(ctx) {
7832
7860
  const key = await deriveUserEncryptionKey(row.user_id, ctx.masterKey);
7833
7861
  work = operationWork(JSON.parse(await decryptFromStorage(record.data, key)));
7834
7862
  } catch {
7835
- console.warn("[amsg] Legacy cleanup operation could not be indexed");
7836
- continue;
7863
+ console.warn("[amsg] Legacy cleanup operation could not be read; queued for retry");
7864
+ work = { nextRunAt: 0, expiresAt: null };
7837
7865
  }
7838
7866
  } else continue;
7839
- await db.indexCloudDataRecordWork(row.user_id, row.kind, row.id, work, row.write_token);
7867
+ entries.push({ userId: row.user_id, kind: row.kind, id: row.id, work, writeToken: row.write_token });
7840
7868
  }
7869
+ await db.indexCloudDataRecordWorkBatch(entries);
7841
7870
  const cursor = batch.rows.at(-1)?.maintenance_rowid ?? batch.upperBound;
7842
7871
  await db.finishCloudMaintenanceBatch(name, cursor, batch.rows.length < 100 || cursor >= batch.upperBound);
7843
7872
  }
7844
7873
  async function resumeCloudDataCleanups(ctx) {
7845
7874
  const db = ctx.db;
7846
7875
  if (!db?.cloudDataManagement) return;
7847
- await repairLegacyWorkIndex(ctx);
7848
- if (typeof db.repairCloudResourceMetadata === "function") await db.repairCloudResourceMetadata(100);
7849
- if (typeof db.cleanupExpiredCloudDataRecords === "function") await db.cleanupExpiredCloudDataRecords(Date.now(), 100);
7850
- const rows = typeof db.listDueCloudDataOperations === "function" ? await db.listDueCloudDataOperations(Date.now(), 25) : await db.listCloudDataRecordsAcrossUsers("operation");
7876
+ const indexed = WORK_INDEX_METHODS.every((method) => typeof db[method] === "function");
7877
+ let rows;
7878
+ if (indexed) {
7879
+ await repairLegacyWorkIndex(ctx);
7880
+ await db.repairCloudResourceMetadata(100);
7881
+ await db.cleanupExpiredCloudDataRecords(Date.now(), 100);
7882
+ rows = await db.listDueCloudDataOperations(Date.now(), 25);
7883
+ } else {
7884
+ await db.cleanupCloudDataRecords("inventory", Date.now() - 36e5);
7885
+ await db.cleanupCloudDataRecords("plan", Date.now() - 36e5);
7886
+ rows = await db.listCloudDataRecordsAcrossUsers("operation");
7887
+ }
7851
7888
  for (const row of rows) {
7852
7889
  try {
7853
7890
  const key = await deriveUserEncryptionKey(row.userId, ctx.masterKey);
7854
7891
  const operation = JSON.parse(await decryptFromStorage(row.data, key));
7892
+ const work = operationWork(operation);
7893
+ if (indexed) {
7894
+ if (work.nextRunAt === null || work.nextRunAt > Date.now()) {
7895
+ await db.rescheduleCloudDataRecordWork(row.userId, "operation", row.id, work, row.writeToken);
7896
+ continue;
7897
+ }
7898
+ } else if (work.expiresAt !== null && work.expiresAt <= Date.now()) {
7899
+ await db.deleteCloudDataRecord(row.userId, "operation", row.id);
7900
+ continue;
7901
+ }
7855
7902
  if (["pending", "running"].includes(operation.status)) {
7856
7903
  await advanceCleanup(ctx, db, row.userId, key, operation);
7857
7904
  }
7858
- } catch {
7905
+ } catch (error) {
7859
7906
  console.warn("[amsg] A cloud cleanup operation could not be resumed");
7907
+ if (!indexed || error?.code === "CLOUD_LEASE_LOST") continue;
7908
+ await db.rescheduleCloudDataRecordWork(
7909
+ row.userId,
7910
+ "operation",
7911
+ row.id,
7912
+ { nextRunAt: Date.now() + UNREADABLE_RETRY_MS, expiresAt: null },
7913
+ row.writeToken
7914
+ ).catch(() => {
7915
+ });
7860
7916
  }
7861
7917
  }
7862
7918
  }
@@ -8343,9 +8399,11 @@ async function getSchemaVersion(db) {
8343
8399
  for (const index of SQLITE_REQUIRED_SCHEMA.indexes) {
8344
8400
  if (!liveIndexes.has(index)) missing.push(`index:${index}`);
8345
8401
  }
8346
- const liveTriggers = new Set(live?.triggers || []);
8347
- for (const name of SQLITE_REQUIRED_SCHEMA.triggers) {
8348
- if (!liveTriggers.has(name)) missing.push(`trigger:${name}`);
8402
+ if (Array.isArray(live?.triggers)) {
8403
+ const liveTriggers = new Set(live.triggers);
8404
+ for (const name of SQLITE_REQUIRED_SCHEMA.triggers) {
8405
+ if (!liveTriggers.has(name)) missing.push(`trigger:${name}`);
8406
+ }
8349
8407
  }
8350
8408
  const ok = missing.length === 0;
8351
8409
  return { current: ok ? SCHEMA_VERSION : null, required: SCHEMA_VERSION, ok, missing };
@@ -8603,12 +8661,13 @@ function createSingleUserCloudflareWorker(buildConfig, options = {}) {
8603
8661
  await reportError({ stage: "config", error, cause, path: null });
8604
8662
  return { ok: false, cause };
8605
8663
  }
8664
+ const cleanup = {};
8606
8665
  try {
8607
8666
  await resumeCloudDataCleanups(buildTickContext(cfg));
8608
8667
  } catch (error) {
8609
- const cause = summarizeErrorCause(error, "cloud-cleanup");
8610
- await reportError({ stage: "cloud-cleanup", error, cause, path: null });
8611
- return { ok: false, cause };
8668
+ console.error("[amsg single-user] scheduled(): cloud cleanup failed:", error && error.message);
8669
+ cleanup.cloudCleanupCause = summarizeErrorCause(error, "cloud-cleanup");
8670
+ await reportError({ stage: "cloud-cleanup", error, cause: cleanup.cloudCleanupCause, path: null });
8612
8671
  }
8613
8672
  if (!pushConfigured(cfg)) {
8614
8673
  console.error("[amsg single-user] scheduled(): VAPID/webpush not configured; skipping tick");
@@ -8617,16 +8676,16 @@ function createSingleUserCloudflareWorker(buildConfig, options = {}) {
8617
8676
  "config"
8618
8677
  );
8619
8678
  await reportError({ stage: "config", error: null, cause, path: null });
8620
- return { ok: false, cause };
8679
+ return { ok: false, cause, ...cleanup };
8621
8680
  }
8622
8681
  try {
8623
8682
  const summary = await runScheduledTick(buildTickContext(cfg));
8624
- return { ok: true, summary };
8683
+ return { ok: true, summary, ...cleanup };
8625
8684
  } catch (error) {
8626
8685
  console.error("[amsg single-user] scheduled(): tick failed:", error && error.message);
8627
8686
  const cause = summarizeErrorCause(error, "tick");
8628
8687
  await reportError({ stage: "tick", error, cause, path: null });
8629
- return { ok: false, cause };
8688
+ return { ok: false, cause, ...cleanup };
8630
8689
  }
8631
8690
  }
8632
8691
  async function runTask2(uuid, env) {
@@ -5481,7 +5481,7 @@ var D1Adapter = class {
5481
5481
  WHERE w.kind='operation' AND w.next_run_at IS NOT NULL AND w.next_run_at<=?
5482
5482
  AND (r.lease_until IS NULL OR r.lease_until<=?)
5483
5483
  ORDER BY w.next_run_at LIMIT ?`).bind(now, now, limit).all();
5484
- return Promise.all((result.results || []).map((row) => this._cloudRecordForList(row)));
5484
+ return Promise.all((result.results || []).map(async (row) => ({ ...await this._cloudRecordForList(row), writeToken: row.write_token })));
5485
5485
  }
5486
5486
  async cleanupExpiredCloudDataRecords(now = Date.now(), limit = 100) {
5487
5487
  const result = await this._db.prepare(`DELETE FROM cloud_data_records WHERE rowid IN (
@@ -5501,17 +5501,34 @@ var D1Adapter = class {
5501
5501
  marker = await this._db.prepare("SELECT * FROM cloud_data_maintenance WHERE name=?").bind(name).first();
5502
5502
  }
5503
5503
  if (marker.completed) return { rows: [], completed: true };
5504
- const result = await this._db.prepare(`SELECT rowid AS maintenance_rowid,* FROM ${table}
5504
+ const columns = table === "cloud_data_records" ? "rowid AS maintenance_rowid,user_id,kind,id,updated_at,write_token,CASE WHEN kind='operation' THEN data END AS data" : "rowid AS maintenance_rowid";
5505
+ const result = await this._db.prepare(`SELECT ${columns} FROM ${table}
5505
5506
  WHERE rowid>? AND rowid<=? ORDER BY rowid LIMIT ?`).bind(marker.cursor, marker.upper_bound, limit).all();
5506
5507
  return { rows: result.results || [], upperBound: marker.upper_bound, cursor: marker.cursor, completed: false };
5507
5508
  }
5508
5509
  async finishCloudMaintenanceBatch(name, cursor, completed) {
5509
5510
  await this._db.prepare(`UPDATE cloud_data_maintenance SET cursor=MAX(cursor,?),completed=MAX(completed,?) WHERE name=?`).bind(cursor, completed ? 1 : 0, name).run();
5510
5511
  }
5511
- async indexCloudDataRecordWork(userId, kind, id, work, expectedWriteToken = null) {
5512
+ /**
5513
+ * Index a whole repair batch in one statement. Never overwrites an existing schedule, and
5514
+ * skips a record that was rewritten after the batch was read.
5515
+ */
5516
+ async indexCloudDataRecordWorkBatch(entries) {
5517
+ if (!entries.length) return;
5518
+ const rows = entries.map(({ userId, kind, id, work, writeToken }) => [userId, kind, id, _nullishCoalesce(work.nextRunAt, () => ( null)), _nullishCoalesce(work.expiresAt, () => ( null)), _nullishCoalesce(writeToken, () => ( null))]);
5512
5519
  await this._db.prepare(`INSERT INTO cloud_data_work (user_id,kind,id,next_run_at,expires_at)
5513
- SELECT user_id,kind,id,?,? FROM cloud_data_records WHERE user_id=? AND kind=? AND id=? AND write_token IS ?
5514
- ON CONFLICT (user_id,kind,id) DO NOTHING`).bind(_nullishCoalesce(work.nextRunAt, () => ( null)), _nullishCoalesce(work.expiresAt, () => ( null)), userId, kind, id, expectedWriteToken).run();
5520
+ SELECT r.user_id,r.kind,r.id,json_extract(j.value,'$[3]'),json_extract(j.value,'$[4]')
5521
+ FROM json_each(?) j JOIN cloud_data_records r ON r.user_id=json_extract(j.value,'$[0]')
5522
+ AND r.kind=json_extract(j.value,'$[1]') AND r.id=json_extract(j.value,'$[2]')
5523
+ AND r.write_token IS json_extract(j.value,'$[5]')
5524
+ WHERE 1 ON CONFLICT (user_id,kind,id) DO NOTHING`).bind(JSON.stringify(rows)).run();
5525
+ }
5526
+ /** Move an indexed record's schedule, unless the record was rewritten since the caller read it. */
5527
+ async rescheduleCloudDataRecordWork(userId, kind, id, work, expectedWriteToken = null) {
5528
+ const result = await this._db.prepare(`UPDATE cloud_data_work SET next_run_at=?,expires_at=?
5529
+ WHERE user_id=? AND kind=? AND id=? AND EXISTS (SELECT 1 FROM cloud_data_records r
5530
+ WHERE r.user_id=cloud_data_work.user_id AND r.kind=cloud_data_work.kind AND r.id=cloud_data_work.id AND r.write_token IS ?)`).bind(_nullishCoalesce(work.nextRunAt, () => ( null)), _nullishCoalesce(work.expiresAt, () => ( null)), userId, kind, id, expectedWriteToken).run();
5531
+ return result.meta.changes > 0;
5515
5532
  }
5516
5533
  async readCloudMaintenanceRecord(row) {
5517
5534
  return this._cloudRecordForList(row);
@@ -7011,7 +7028,7 @@ function createClientStateNamespacesHandler(ctx) {
7011
7028
  }
7012
7029
 
7013
7030
  // src/server/lib/version.js
7014
- var SERVER_VERSION = true ? "2.6.0-next.35" : "0.0.0-dev";
7031
+ var SERVER_VERSION = true ? "2.6.0-next.36" : "0.0.0-dev";
7015
7032
 
7016
7033
  // src/server/handlers/capabilities.js
7017
7034
  var SERVER_FEATURES = Object.freeze([
@@ -7816,12 +7833,23 @@ async function advanceCleanup(ctx, db, userId, key, operation) {
7816
7833
  }
7817
7834
  return operation;
7818
7835
  }
7836
+ var WORK_INDEX_METHODS = [
7837
+ "listDueCloudDataOperations",
7838
+ "cleanupExpiredCloudDataRecords",
7839
+ "rescheduleCloudDataRecordWork",
7840
+ "getCloudMaintenanceBatch",
7841
+ "finishCloudMaintenanceBatch",
7842
+ "readCloudMaintenanceRecord",
7843
+ "indexCloudDataRecordWorkBatch",
7844
+ "repairCloudResourceMetadata"
7845
+ ];
7846
+ var UNREADABLE_RETRY_MS = 15 * 6e4;
7819
7847
  async function repairLegacyWorkIndex(ctx) {
7820
7848
  const db = ctx.db;
7821
- if (typeof db.getCloudMaintenanceBatch !== "function") return;
7822
7849
  const name = "management-work-v1";
7823
7850
  const batch = await db.getCloudMaintenanceBatch(name, "cloud_data_records", 100);
7824
7851
  if (batch.completed) return;
7852
+ const entries = [];
7825
7853
  for (const row of batch.rows) {
7826
7854
  let work;
7827
7855
  if (["inventory", "plan"].includes(row.kind)) {
@@ -7832,31 +7860,59 @@ async function repairLegacyWorkIndex(ctx) {
7832
7860
  const key = await deriveUserEncryptionKey(row.user_id, ctx.masterKey);
7833
7861
  work = operationWork(JSON.parse(await decryptFromStorage(record.data, key)));
7834
7862
  } catch (e11) {
7835
- console.warn("[amsg] Legacy cleanup operation could not be indexed");
7836
- continue;
7863
+ console.warn("[amsg] Legacy cleanup operation could not be read; queued for retry");
7864
+ work = { nextRunAt: 0, expiresAt: null };
7837
7865
  }
7838
7866
  } else continue;
7839
- await db.indexCloudDataRecordWork(row.user_id, row.kind, row.id, work, row.write_token);
7867
+ entries.push({ userId: row.user_id, kind: row.kind, id: row.id, work, writeToken: row.write_token });
7840
7868
  }
7869
+ await db.indexCloudDataRecordWorkBatch(entries);
7841
7870
  const cursor = _nullishCoalesce(_optionalChain([batch, 'access', _84 => _84.rows, 'access', _85 => _85.at, 'call', _86 => _86(-1), 'optionalAccess', _87 => _87.maintenance_rowid]), () => ( batch.upperBound));
7842
7871
  await db.finishCloudMaintenanceBatch(name, cursor, batch.rows.length < 100 || cursor >= batch.upperBound);
7843
7872
  }
7844
7873
  async function resumeCloudDataCleanups(ctx) {
7845
7874
  const db = ctx.db;
7846
7875
  if (!_optionalChain([db, 'optionalAccess', _88 => _88.cloudDataManagement])) return;
7847
- await repairLegacyWorkIndex(ctx);
7848
- if (typeof db.repairCloudResourceMetadata === "function") await db.repairCloudResourceMetadata(100);
7849
- if (typeof db.cleanupExpiredCloudDataRecords === "function") await db.cleanupExpiredCloudDataRecords(Date.now(), 100);
7850
- const rows = typeof db.listDueCloudDataOperations === "function" ? await db.listDueCloudDataOperations(Date.now(), 25) : await db.listCloudDataRecordsAcrossUsers("operation");
7876
+ const indexed = WORK_INDEX_METHODS.every((method) => typeof db[method] === "function");
7877
+ let rows;
7878
+ if (indexed) {
7879
+ await repairLegacyWorkIndex(ctx);
7880
+ await db.repairCloudResourceMetadata(100);
7881
+ await db.cleanupExpiredCloudDataRecords(Date.now(), 100);
7882
+ rows = await db.listDueCloudDataOperations(Date.now(), 25);
7883
+ } else {
7884
+ await db.cleanupCloudDataRecords("inventory", Date.now() - 36e5);
7885
+ await db.cleanupCloudDataRecords("plan", Date.now() - 36e5);
7886
+ rows = await db.listCloudDataRecordsAcrossUsers("operation");
7887
+ }
7851
7888
  for (const row of rows) {
7852
7889
  try {
7853
7890
  const key = await deriveUserEncryptionKey(row.userId, ctx.masterKey);
7854
7891
  const operation = JSON.parse(await decryptFromStorage(row.data, key));
7892
+ const work = operationWork(operation);
7893
+ if (indexed) {
7894
+ if (work.nextRunAt === null || work.nextRunAt > Date.now()) {
7895
+ await db.rescheduleCloudDataRecordWork(row.userId, "operation", row.id, work, row.writeToken);
7896
+ continue;
7897
+ }
7898
+ } else if (work.expiresAt !== null && work.expiresAt <= Date.now()) {
7899
+ await db.deleteCloudDataRecord(row.userId, "operation", row.id);
7900
+ continue;
7901
+ }
7855
7902
  if (["pending", "running"].includes(operation.status)) {
7856
7903
  await advanceCleanup(ctx, db, row.userId, key, operation);
7857
7904
  }
7858
- } catch (e12) {
7905
+ } catch (error) {
7859
7906
  console.warn("[amsg] A cloud cleanup operation could not be resumed");
7907
+ if (!indexed || _optionalChain([error, 'optionalAccess', _89 => _89.code]) === "CLOUD_LEASE_LOST") continue;
7908
+ await db.rescheduleCloudDataRecordWork(
7909
+ row.userId,
7910
+ "operation",
7911
+ row.id,
7912
+ { nextRunAt: Date.now() + UNREADABLE_RETRY_MS, expiresAt: null },
7913
+ row.writeToken
7914
+ ).catch(() => {
7915
+ });
7860
7916
  }
7861
7917
  }
7862
7918
  }
@@ -7907,7 +7963,7 @@ function createCloudDataHandler(ctx) {
7907
7963
  if (!parsed.ok) throw cloudError("INVALID_PAYLOAD", "\u8BF7\u6C42\u683C\u5F0F\u65E0\u6548");
7908
7964
  try {
7909
7965
  input = await decryptPayload(parsed.data, key);
7910
- } catch (e13) {
7966
+ } catch (e12) {
7911
7967
  throw cloudError("DECRYPTION_FAILED", "\u8BF7\u6C42\u89E3\u5BC6\u5931\u8D25");
7912
7968
  }
7913
7969
  if (!input || typeof input !== "object" || Array.isArray(input))
@@ -7956,7 +8012,7 @@ function createCloudDataHandler(ctx) {
7956
8012
  summary: inventorySummary(inventory),
7957
8013
  expiresAt: Date.now() + 15 * 60 * 1e3,
7958
8014
  resources: inventory.entries.map((e) => e.resource).filter(
7959
- (r) => (!type || r.type === type) && (!ownerId || _optionalChain([r, 'access', _89 => _89.owner, 'optionalAccess', _90 => _90.type]) === ownerType && r.owner.id === ownerId)
8015
+ (r) => (!type || r.type === type) && (!ownerId || _optionalChain([r, 'access', _90 => _90.owner, 'optionalAccess', _91 => _91.type]) === ownerType && r.owner.id === ownerId)
7960
8016
  ),
7961
8017
  complete: inventory.complete,
7962
8018
  gaps: inventory.gaps
@@ -7991,7 +8047,7 @@ function createCloudDataHandler(ctx) {
7991
8047
  JSON.parse(await decryptFromStorage(row.data, key))
7992
8048
  )
7993
8049
  );
7994
- } catch (e14) {
8050
+ } catch (e13) {
7995
8051
  gaps.push({
7996
8052
  source: "operation",
7997
8053
  code: "UNREADABLE_OPERATION",
@@ -8039,7 +8095,7 @@ function createCloudDataHandler(ctx) {
8039
8095
  const rows = await db.listCloudDataRecords(userId, "operation");
8040
8096
  for (const row of rows) {
8041
8097
  const op = JSON.parse(await decryptFromStorage(row.data, key));
8042
- if (op.mode === "retire-owner" && _optionalChain([op, 'access', _91 => _91.owner, 'optionalAccess', _92 => _92.type]) === owner.type && _optionalChain([op, 'access', _93 => _93.owner, 'optionalAccess', _94 => _94.id]) === owner.id && ["pending", "running"].includes(op.status))
8098
+ if (op.mode === "retire-owner" && _optionalChain([op, 'access', _92 => _92.owner, 'optionalAccess', _93 => _93.type]) === owner.type && _optionalChain([op, 'access', _94 => _94.owner, 'optionalAccess', _95 => _95.id]) === owner.id && ["pending", "running"].includes(op.status))
8043
8099
  throw cloudError(
8044
8100
  "CLOUD_CLEANUP_IN_PROGRESS",
8045
8101
  "\u6E05\u7406\u5B8C\u6210\u540E\u624D\u80FD\u91CD\u65B0\u542F\u7528",
@@ -8257,7 +8313,7 @@ function createSingleUserServer(config) {
8257
8313
  if (!config.masterKey) throw new Error("[amsg-server single-user] config.masterKey is required");
8258
8314
  const vapid = config.vapid || {};
8259
8315
  const tenantManager = createSingleUserContextManager({
8260
- db: createCloudGuardedAdapter(config.db, { masterKey: config.masterKey, resolveOwner: _optionalChain([config, 'access', _95 => _95.cloudData, 'optionalAccess', _96 => _96.resolveOwner]) }),
8316
+ db: createCloudGuardedAdapter(config.db, { masterKey: config.masterKey, resolveOwner: _optionalChain([config, 'access', _96 => _96.cloudData, 'optionalAccess', _97 => _97.resolveOwner]) }),
8261
8317
  masterKey: config.masterKey,
8262
8318
  serverToken: config.serverToken
8263
8319
  });
@@ -8343,9 +8399,11 @@ async function getSchemaVersion(db) {
8343
8399
  for (const index of SQLITE_REQUIRED_SCHEMA.indexes) {
8344
8400
  if (!liveIndexes.has(index)) missing.push(`index:${index}`);
8345
8401
  }
8346
- const liveTriggers = new Set(_optionalChain([live, 'optionalAccess', _97 => _97.triggers]) || []);
8347
- for (const name of SQLITE_REQUIRED_SCHEMA.triggers) {
8348
- if (!liveTriggers.has(name)) missing.push(`trigger:${name}`);
8402
+ if (Array.isArray(_optionalChain([live, 'optionalAccess', _98 => _98.triggers]))) {
8403
+ const liveTriggers = new Set(live.triggers);
8404
+ for (const name of SQLITE_REQUIRED_SCHEMA.triggers) {
8405
+ if (!liveTriggers.has(name)) missing.push(`trigger:${name}`);
8406
+ }
8349
8407
  }
8350
8408
  const ok = missing.length === 0;
8351
8409
  return { current: ok ? SCHEMA_VERSION : null, required: SCHEMA_VERSION, ok, missing };
@@ -8603,12 +8661,13 @@ function createSingleUserCloudflareWorker(buildConfig, options = {}) {
8603
8661
  await reportError({ stage: "config", error, cause, path: null });
8604
8662
  return { ok: false, cause };
8605
8663
  }
8664
+ const cleanup = {};
8606
8665
  try {
8607
8666
  await resumeCloudDataCleanups(buildTickContext(cfg));
8608
8667
  } catch (error) {
8609
- const cause = summarizeErrorCause(error, "cloud-cleanup");
8610
- await reportError({ stage: "cloud-cleanup", error, cause, path: null });
8611
- return { ok: false, cause };
8668
+ console.error("[amsg single-user] scheduled(): cloud cleanup failed:", error && error.message);
8669
+ cleanup.cloudCleanupCause = summarizeErrorCause(error, "cloud-cleanup");
8670
+ await reportError({ stage: "cloud-cleanup", error, cause: cleanup.cloudCleanupCause, path: null });
8612
8671
  }
8613
8672
  if (!pushConfigured(cfg)) {
8614
8673
  console.error("[amsg single-user] scheduled(): VAPID/webpush not configured; skipping tick");
@@ -8617,16 +8676,16 @@ function createSingleUserCloudflareWorker(buildConfig, options = {}) {
8617
8676
  "config"
8618
8677
  );
8619
8678
  await reportError({ stage: "config", error: null, cause, path: null });
8620
- return { ok: false, cause };
8679
+ return { ok: false, cause, ...cleanup };
8621
8680
  }
8622
8681
  try {
8623
8682
  const summary = await runScheduledTick(buildTickContext(cfg));
8624
- return { ok: true, summary };
8683
+ return { ok: true, summary, ...cleanup };
8625
8684
  } catch (error) {
8626
8685
  console.error("[amsg single-user] scheduled(): tick failed:", error && error.message);
8627
8686
  const cause = summarizeErrorCause(error, "tick");
8628
8687
  await reportError({ stage: "tick", error, cause, path: null });
8629
- return { ok: false, cause };
8688
+ return { ok: false, cause, ...cleanup };
8630
8689
  }
8631
8690
  }
8632
8691
  async function runTask2(uuid, env) {
@@ -1,12 +1,12 @@
1
1
  /**
2
2
  * @param {(env: Object) => Object|Promise<Object>} buildConfig
3
- * @param {{ onError?: (info: { stage: 'config'|'request'|'tick', error: unknown, cause: import('../lib/errors.js').ErrorCause, path: string|null }) => void|Promise<void> }} [options]
3
+ * @param {{ onError?: (info: { stage: 'config'|'request'|'tick'|'cloud-cleanup', error: unknown, cause: import('../lib/errors.js').ErrorCause, path: string|null }) => void|Promise<void> }} [options]
4
4
  * `onError` 在 fetch 或 cron 出错时调一次(best-effort,自身抛错只记日志)。
5
5
  * cron 那条路上没有调用方能看到错误响应,这个 hook 是宿主唯一的出口。
6
6
  */
7
7
  export function createSingleUserCloudflareWorker(buildConfig: (env: any) => any | Promise<any>, options?: {
8
8
  onError?: (info: {
9
- stage: "config" | "request" | "tick";
9
+ stage: "config" | "request" | "tick" | "cloud-cleanup";
10
10
  error: unknown;
11
11
  cause: import("../lib/errors.js").ErrorCause;
12
12
  path: string | null;
@@ -16,9 +16,11 @@ export function createSingleUserCloudflareWorker(buildConfig: (env: any) => any
16
16
  scheduled: (event: any, env: any) => Promise<{
17
17
  ok: true;
18
18
  summary: any;
19
+ cloudCleanupCause?: import("../lib/errors.js").ErrorCause;
19
20
  } | {
20
21
  ok: false;
21
22
  cause: import("../lib/errors.js").ErrorCause;
23
+ cloudCleanupCause?: import("../lib/errors.js").ErrorCause;
22
24
  }>;
23
25
  runTask: (uuid: string, env: any) => Promise<{
24
26
  ran: false;
@@ -28,7 +28,7 @@
28
28
 
29
29
 
30
30
 
31
- var _chunkUG5ZNM5Jcjs = require('./chunk-UG5ZNM5J.cjs');
31
+ var _chunkI4BGZXQAcjs = require('./chunk-I4BGZXQA.cjs');
32
32
  require('./chunk-7NMQFTDJ.cjs');
33
33
 
34
34
 
@@ -60,4 +60,4 @@ require('./chunk-7NMQFTDJ.cjs');
60
60
 
61
61
 
62
62
 
63
- exports.DEFAULT_MAX_DELIVERY_RETRIES = _chunkUG5ZNM5Jcjs.DEFAULT_MAX_DELIVERY_RETRIES; exports.DEFAULT_MAX_REQUEST_BODY_BYTES = _chunkUG5ZNM5Jcjs.DEFAULT_MAX_REQUEST_BODY_BYTES; exports.MAX_DEFER_AFTER_MS = _chunkUG5ZNM5Jcjs.MAX_DEFER_AFTER_MS; exports.MAX_PUSH_PAYLOAD_BYTES = _chunkUG5ZNM5Jcjs.MAX_PUSH_PAYLOAD_BYTES; exports.NonRetryableError = _chunkUG5ZNM5Jcjs.NonRetryableError; exports.PUSH_ENVELOPE_RESERVED_BYTES = _chunkUG5ZNM5Jcjs.PUSH_ENVELOPE_RESERVED_BYTES; exports.SCHEMA_VERSION = _chunkUG5ZNM5Jcjs.SCHEMA_VERSION; exports.WEB_PUSH_ENCRYPTION_OVERHEAD_BYTES = _chunkUG5ZNM5Jcjs.WEB_PUSH_ENCRYPTION_OVERHEAD_BYTES; exports.WEB_PUSH_MAX_BODY_BYTES = _chunkUG5ZNM5Jcjs.WEB_PUSH_MAX_BODY_BYTES; exports.advanceOccurrence = _chunkUG5ZNM5Jcjs.advanceOccurrence; exports.createD1Adapter = _chunkUG5ZNM5Jcjs.createD1Adapter; exports.createSingleUserCloudflareWorker = _chunkUG5ZNM5Jcjs.createSingleUserCloudflareWorker; exports.createSingleUserServer = _chunkUG5ZNM5Jcjs.createSingleUserServer; exports.createWebCryptoWebPush = _chunkUG5ZNM5Jcjs.createWebCryptoWebPush; exports.decryptFromStorage = _chunkUG5ZNM5Jcjs.decryptFromStorage; exports.decryptPayload = _chunkUG5ZNM5Jcjs.decryptPayload; exports.deriveUserEncryptionKey = _chunkUG5ZNM5Jcjs.deriveUserEncryptionKey; exports.encryptForStorage = _chunkUG5ZNM5Jcjs.encryptForStorage; exports.ensureSchema = _chunkUG5ZNM5Jcjs.ensureSchema; exports.getSchemaVersion = _chunkUG5ZNM5Jcjs.getSchemaVersion; exports.isNonRetryableError = _chunkUG5ZNM5Jcjs.isNonRetryableError; exports.isValidTimeZoneId = _chunkUG5ZNM5Jcjs.isValidTimeZoneId; exports.measurePushPayload = _chunkUG5ZNM5Jcjs.measurePushPayload; exports.nextFutureOccurrence = _chunkUG5ZNM5Jcjs.nextFutureOccurrence; exports.planNextOccurrence = _chunkUG5ZNM5Jcjs.planNextOccurrence; exports.readRequestBody = _chunkUG5ZNM5Jcjs.readRequestBody; exports.runScheduledTick = _chunkUG5ZNM5Jcjs.runScheduledTick; exports.runTask = _chunkUG5ZNM5Jcjs.runTask; exports.summarizeErrorCause = _chunkUG5ZNM5Jcjs.summarizeErrorCause;
63
+ exports.DEFAULT_MAX_DELIVERY_RETRIES = _chunkI4BGZXQAcjs.DEFAULT_MAX_DELIVERY_RETRIES; exports.DEFAULT_MAX_REQUEST_BODY_BYTES = _chunkI4BGZXQAcjs.DEFAULT_MAX_REQUEST_BODY_BYTES; exports.MAX_DEFER_AFTER_MS = _chunkI4BGZXQAcjs.MAX_DEFER_AFTER_MS; exports.MAX_PUSH_PAYLOAD_BYTES = _chunkI4BGZXQAcjs.MAX_PUSH_PAYLOAD_BYTES; exports.NonRetryableError = _chunkI4BGZXQAcjs.NonRetryableError; exports.PUSH_ENVELOPE_RESERVED_BYTES = _chunkI4BGZXQAcjs.PUSH_ENVELOPE_RESERVED_BYTES; exports.SCHEMA_VERSION = _chunkI4BGZXQAcjs.SCHEMA_VERSION; exports.WEB_PUSH_ENCRYPTION_OVERHEAD_BYTES = _chunkI4BGZXQAcjs.WEB_PUSH_ENCRYPTION_OVERHEAD_BYTES; exports.WEB_PUSH_MAX_BODY_BYTES = _chunkI4BGZXQAcjs.WEB_PUSH_MAX_BODY_BYTES; exports.advanceOccurrence = _chunkI4BGZXQAcjs.advanceOccurrence; exports.createD1Adapter = _chunkI4BGZXQAcjs.createD1Adapter; exports.createSingleUserCloudflareWorker = _chunkI4BGZXQAcjs.createSingleUserCloudflareWorker; exports.createSingleUserServer = _chunkI4BGZXQAcjs.createSingleUserServer; exports.createWebCryptoWebPush = _chunkI4BGZXQAcjs.createWebCryptoWebPush; exports.decryptFromStorage = _chunkI4BGZXQAcjs.decryptFromStorage; exports.decryptPayload = _chunkI4BGZXQAcjs.decryptPayload; exports.deriveUserEncryptionKey = _chunkI4BGZXQAcjs.deriveUserEncryptionKey; exports.encryptForStorage = _chunkI4BGZXQAcjs.encryptForStorage; exports.ensureSchema = _chunkI4BGZXQAcjs.ensureSchema; exports.getSchemaVersion = _chunkI4BGZXQAcjs.getSchemaVersion; exports.isNonRetryableError = _chunkI4BGZXQAcjs.isNonRetryableError; exports.isValidTimeZoneId = _chunkI4BGZXQAcjs.isValidTimeZoneId; exports.measurePushPayload = _chunkI4BGZXQAcjs.measurePushPayload; exports.nextFutureOccurrence = _chunkI4BGZXQAcjs.nextFutureOccurrence; exports.planNextOccurrence = _chunkI4BGZXQAcjs.planNextOccurrence; exports.readRequestBody = _chunkI4BGZXQAcjs.readRequestBody; exports.runScheduledTick = _chunkI4BGZXQAcjs.runScheduledTick; exports.runTask = _chunkI4BGZXQAcjs.runTask; exports.summarizeErrorCause = _chunkI4BGZXQAcjs.summarizeErrorCause;
@@ -28,7 +28,7 @@ import {
28
28
  runScheduledTick,
29
29
  runTask,
30
30
  summarizeErrorCause
31
- } from "./chunk-INQD6LRY.mjs";
31
+ } from "./chunk-67RM2E46.mjs";
32
32
  import "./chunk-GN44PST5.mjs";
33
33
  export {
34
34
  DEFAULT_MAX_DELIVERY_RETRIES,
package/dist/index.cjs CHANGED
@@ -73,7 +73,7 @@
73
73
 
74
74
 
75
75
 
76
- var _chunkUG5ZNM5Jcjs = require('./chunk-UG5ZNM5J.cjs');
76
+ var _chunkI4BGZXQAcjs = require('./chunk-I4BGZXQA.cjs');
77
77
  require('./chunk-7NMQFTDJ.cjs');
78
78
 
79
79
  // src/server/adapters/factory.js
@@ -107,7 +107,7 @@ async function createAdapter(config) {
107
107
  // src/server/handlers/init-tenant.js
108
108
  function createInitTenantHandler(ctx) {
109
109
  async function POST(headers, body) {
110
- const initSecret = _chunkUG5ZNM5Jcjs.getHeader.call(void 0, headers, "x-init-secret");
110
+ const initSecret = _chunkI4BGZXQAcjs.getHeader.call(void 0, headers, "x-init-secret");
111
111
  if (ctx.tenant.initSecret && initSecret !== ctx.tenant.initSecret) {
112
112
  return {
113
113
  status: 401,
@@ -120,7 +120,7 @@ function createInitTenantHandler(ctx) {
120
120
  }
121
121
  };
122
122
  }
123
- const parsed = _chunkUG5ZNM5Jcjs.parseJsonBody.call(void 0, body);
123
+ const parsed = _chunkI4BGZXQAcjs.parseJsonBody.call(void 0, body);
124
124
  if (!parsed.ok) {
125
125
  return {
126
126
  status: 400,
@@ -134,7 +134,7 @@ function createInitTenantHandler(ctx) {
134
134
  const tenantId = payload.tenantId || void 0;
135
135
  const driver = payload.driver;
136
136
  const databaseUrl = payload.databaseUrl || payload.connectionString;
137
- if (tenantId && !_chunkUG5ZNM5Jcjs.isValidUUIDv4.call(void 0, tenantId)) {
137
+ if (tenantId && !_chunkI4BGZXQAcjs.isValidUUIDv4.call(void 0, tenantId)) {
138
138
  return {
139
139
  status: 400,
140
140
  body: {
@@ -254,7 +254,7 @@ function createSendNotificationsHandler(ctx) {
254
254
  }
255
255
  };
256
256
  }
257
- const data = await _chunkUG5ZNM5Jcjs.runScheduledTick.call(void 0, { ...ctx, db, masterKey });
257
+ const data = await _chunkI4BGZXQAcjs.runScheduledTick.call(void 0, { ...ctx, db, masterKey });
258
258
  return { status: 200, body: { success: true, data } };
259
259
  }
260
260
  return { POST };
@@ -276,16 +276,16 @@ function encryptConfig(config, kekBuffer) {
276
276
  const plaintext = Buffer.from(JSON.stringify(config), "utf8");
277
277
  const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
278
278
  const authTag = cipher.getAuthTag();
279
- return `v1.${_chunkUG5ZNM5Jcjs.bytesToBase64Url.call(void 0, iv)}.${_chunkUG5ZNM5Jcjs.bytesToBase64Url.call(void 0, authTag)}.${_chunkUG5ZNM5Jcjs.bytesToBase64Url.call(void 0, ciphertext)}`;
279
+ return `v1.${_chunkI4BGZXQAcjs.bytesToBase64Url.call(void 0, iv)}.${_chunkI4BGZXQAcjs.bytesToBase64Url.call(void 0, authTag)}.${_chunkI4BGZXQAcjs.bytesToBase64Url.call(void 0, ciphertext)}`;
280
280
  }
281
281
  function decryptConfig(encrypted, kekBuffer) {
282
282
  const parts = String(encrypted || "").split(".");
283
283
  if (parts.length !== 4 || parts[0] !== "v1") {
284
284
  throw new Error("INVALID_TENANT_CONFIG");
285
285
  }
286
- const iv = _chunkUG5ZNM5Jcjs.base64UrlToBytes.call(void 0, parts[1]);
287
- const authTag = _chunkUG5ZNM5Jcjs.base64UrlToBytes.call(void 0, parts[2]);
288
- const ciphertext = _chunkUG5ZNM5Jcjs.base64UrlToBytes.call(void 0, parts[3]);
286
+ const iv = _chunkI4BGZXQAcjs.base64UrlToBytes.call(void 0, parts[1]);
287
+ const authTag = _chunkI4BGZXQAcjs.base64UrlToBytes.call(void 0, parts[2]);
288
+ const ciphertext = _chunkI4BGZXQAcjs.base64UrlToBytes.call(void 0, parts[3]);
289
289
  const decipher = _crypto.createDecipheriv.call(void 0, "aes-256-gcm", kekBuffer, iv);
290
290
  decipher.setAuthTag(authTag);
291
291
  const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
@@ -353,7 +353,7 @@ function createTenantBlobStore(options) {
353
353
  // src/server/tenant/token.js
354
354
 
355
355
  function sign(input, secret) {
356
- return _chunkUG5ZNM5Jcjs.bytesToBase64Url.call(void 0, _crypto.createHmac.call(void 0, "sha256", secret).update(input).digest());
356
+ return _chunkI4BGZXQAcjs.bytesToBase64Url.call(void 0, _crypto.createHmac.call(void 0, "sha256", secret).update(input).digest());
357
357
  }
358
358
  function nowEpochSeconds() {
359
359
  return Math.floor(Date.now() / 1e3);
@@ -369,8 +369,8 @@ function createTenantToken(params, secret) {
369
369
  v: 1
370
370
  };
371
371
  const header = { alg: "HS256", typ: "JWT" };
372
- const encodedHeader = _chunkUG5ZNM5Jcjs.bytesToBase64Url.call(void 0, _chunkUG5ZNM5Jcjs.utf8.call(void 0, JSON.stringify(header)));
373
- const encodedPayload = _chunkUG5ZNM5Jcjs.bytesToBase64Url.call(void 0, _chunkUG5ZNM5Jcjs.utf8.call(void 0, JSON.stringify(payload)));
372
+ const encodedHeader = _chunkI4BGZXQAcjs.bytesToBase64Url.call(void 0, _chunkI4BGZXQAcjs.utf8.call(void 0, JSON.stringify(header)));
373
+ const encodedPayload = _chunkI4BGZXQAcjs.bytesToBase64Url.call(void 0, _chunkI4BGZXQAcjs.utf8.call(void 0, JSON.stringify(payload)));
374
374
  const signingInput = `${encodedHeader}.${encodedPayload}`;
375
375
  const signature = sign(signingInput, secret);
376
376
  return `${signingInput}.${signature}`;
@@ -389,17 +389,17 @@ function verifyTenantToken(token, secret, options = {}) {
389
389
  let receivedBytes;
390
390
  let expectedBytes;
391
391
  try {
392
- receivedBytes = _chunkUG5ZNM5Jcjs.base64UrlToBytes.call(void 0, receivedSignature);
393
- expectedBytes = _chunkUG5ZNM5Jcjs.base64UrlToBytes.call(void 0, expectedSignature);
392
+ receivedBytes = _chunkI4BGZXQAcjs.base64UrlToBytes.call(void 0, receivedSignature);
393
+ expectedBytes = _chunkI4BGZXQAcjs.base64UrlToBytes.call(void 0, expectedSignature);
394
394
  } catch (e) {
395
395
  throw new Error("INVALID_TENANT_AUTH");
396
396
  }
397
- if (!_chunkUG5ZNM5Jcjs.timingSafeEqualBytes.call(void 0, receivedBytes, expectedBytes)) {
397
+ if (!_chunkI4BGZXQAcjs.timingSafeEqualBytes.call(void 0, receivedBytes, expectedBytes)) {
398
398
  throw new Error("INVALID_TENANT_AUTH");
399
399
  }
400
400
  let payload;
401
401
  try {
402
- payload = JSON.parse(_chunkUG5ZNM5Jcjs.utf8Decode.call(void 0, _chunkUG5ZNM5Jcjs.base64UrlToBytes.call(void 0, encodedPayload)));
402
+ payload = JSON.parse(_chunkI4BGZXQAcjs.utf8Decode.call(void 0, _chunkI4BGZXQAcjs.base64UrlToBytes.call(void 0, encodedPayload)));
403
403
  } catch (e2) {
404
404
  throw new Error("INVALID_TENANT_AUTH");
405
405
  }
@@ -627,15 +627,15 @@ async function createReiServer(config) {
627
627
  return {
628
628
  handlers: {
629
629
  initTenant: createInitTenantHandler(ctx),
630
- getUserKey: _chunkUG5ZNM5Jcjs.createGetUserKeyHandler.call(void 0, ctx),
631
- scheduleMessage: _chunkUG5ZNM5Jcjs.createScheduleMessageHandler.call(void 0, ctx),
630
+ getUserKey: _chunkI4BGZXQAcjs.createGetUserKeyHandler.call(void 0, ctx),
631
+ scheduleMessage: _chunkI4BGZXQAcjs.createScheduleMessageHandler.call(void 0, ctx),
632
632
  sendNotifications: createSendNotificationsHandler(ctx),
633
- updateMessage: _chunkUG5ZNM5Jcjs.createUpdateMessageHandler.call(void 0, ctx),
634
- cancelMessage: _chunkUG5ZNM5Jcjs.createCancelMessageHandler.call(void 0, ctx),
635
- messages: _chunkUG5ZNM5Jcjs.createMessagesHandler.call(void 0, ctx),
636
- getMessage: _chunkUG5ZNM5Jcjs.createGetMessageHandler.call(void 0, ctx),
637
- pushSubscription: _chunkUG5ZNM5Jcjs.createPushSubscriptionHandler.call(void 0, ctx),
638
- llmCredentials: _chunkUG5ZNM5Jcjs.createLlmCredentialsHandler.call(void 0, ctx)
633
+ updateMessage: _chunkI4BGZXQAcjs.createUpdateMessageHandler.call(void 0, ctx),
634
+ cancelMessage: _chunkI4BGZXQAcjs.createCancelMessageHandler.call(void 0, ctx),
635
+ messages: _chunkI4BGZXQAcjs.createMessagesHandler.call(void 0, ctx),
636
+ getMessage: _chunkI4BGZXQAcjs.createGetMessageHandler.call(void 0, ctx),
637
+ pushSubscription: _chunkI4BGZXQAcjs.createPushSubscriptionHandler.call(void 0, ctx),
638
+ llmCredentials: _chunkI4BGZXQAcjs.createLlmCredentialsHandler.call(void 0, ctx)
639
639
  }
640
640
  };
641
641
  }
@@ -703,4 +703,4 @@ async function createReiServer(config) {
703
703
 
704
704
 
705
705
 
706
- exports.CORS_ALLOW_HEADERS = _chunkUG5ZNM5Jcjs.CORS_ALLOW_HEADERS; exports.CORS_ALLOW_METHODS = _chunkUG5ZNM5Jcjs.CORS_ALLOW_METHODS; exports.CRED_ID_MAX_LENGTH = _chunkUG5ZNM5Jcjs.CRED_ID_MAX_LENGTH; exports.CRED_PUT_BATCH_MAX = _chunkUG5ZNM5Jcjs.CRED_PUT_BATCH_MAX; exports.CRED_REFS_MAX_ENTRIES = _chunkUG5ZNM5Jcjs.CRED_REFS_MAX_ENTRIES; exports.CRED_ROWS_PER_USER_MAX = _chunkUG5ZNM5Jcjs.CRED_ROWS_PER_USER_MAX; exports.DEFAULT_CLAIM_LEASE_MS = _chunkUG5ZNM5Jcjs.DEFAULT_CLAIM_LEASE_MS; exports.DEFAULT_HEARTBEAT_LEASE_TTL_MS = _chunkUG5ZNM5Jcjs.DEFAULT_HEARTBEAT_LEASE_TTL_MS; exports.DEFAULT_LEASE_HEARTBEAT_MS = _chunkUG5ZNM5Jcjs.DEFAULT_LEASE_HEARTBEAT_MS; exports.DEFAULT_MAX_DELIVERY_RETRIES = _chunkUG5ZNM5Jcjs.DEFAULT_MAX_DELIVERY_RETRIES; exports.DEFAULT_MAX_REQUEST_BODY_BYTES = _chunkUG5ZNM5Jcjs.DEFAULT_MAX_REQUEST_BODY_BYTES; exports.DEFAULT_MAX_SCHEDULED_TASKS_PER_FIRE = _chunkUG5ZNM5Jcjs.DEFAULT_MAX_SCHEDULED_TASKS_PER_FIRE; exports.DEFAULT_MAX_TOOL_ITERATIONS = _chunkUG5ZNM5Jcjs.DEFAULT_MAX_TOOL_ITERATIONS; exports.DEFAULT_TOTAL_TIMEOUT_MS = _chunkUG5ZNM5Jcjs.DEFAULT_TOTAL_TIMEOUT_MS; exports.DeploymentConfigError = _chunkUG5ZNM5Jcjs.DeploymentConfigError; exports.MAX_DEFER_AFTER_MS = _chunkUG5ZNM5Jcjs.MAX_DEFER_AFTER_MS; exports.MAX_PUSH_PAYLOAD_BYTES = _chunkUG5ZNM5Jcjs.MAX_PUSH_PAYLOAD_BYTES; exports.MAX_TASK_PAYLOAD_BYTES = _chunkUG5ZNM5Jcjs.MAX_TASK_PAYLOAD_BYTES; exports.MIN_SCHEDULE_LEAD_MS = _chunkUG5ZNM5Jcjs.MIN_SCHEDULE_LEAD_MS; exports.NonRetryableError = _chunkUG5ZNM5Jcjs.NonRetryableError; exports.PUSH_ENVELOPE_RESERVED_BYTES = _chunkUG5ZNM5Jcjs.PUSH_ENVELOPE_RESERVED_BYTES; exports.SCHEMA_VERSION = _chunkUG5ZNM5Jcjs.SCHEMA_VERSION; exports.WEB_PUSH_ENCRYPTION_OVERHEAD_BYTES = _chunkUG5ZNM5Jcjs.WEB_PUSH_ENCRYPTION_OVERHEAD_BYTES; exports.WEB_PUSH_MAX_BODY_BYTES = _chunkUG5ZNM5Jcjs.WEB_PUSH_MAX_BODY_BYTES; exports.advanceOccurrence = _chunkUG5ZNM5Jcjs.advanceOccurrence; exports.createAdapter = createAdapter; exports.createD1Adapter = _chunkUG5ZNM5Jcjs.createD1Adapter; exports.createReiServer = createReiServer; exports.createSingleUserCloudflareWorker = _chunkUG5ZNM5Jcjs.createSingleUserCloudflareWorker; exports.createSingleUserServer = _chunkUG5ZNM5Jcjs.createSingleUserServer; exports.createTenantToken = createTenantToken; exports.createWebCryptoWebPush = _chunkUG5ZNM5Jcjs.createWebCryptoWebPush; exports.decryptFromStorage = _chunkUG5ZNM5Jcjs.decryptFromStorage; exports.decryptPayload = _chunkUG5ZNM5Jcjs.decryptPayload; exports.deriveUserEncryptionKey = _chunkUG5ZNM5Jcjs.deriveUserEncryptionKey; exports.encryptForStorage = _chunkUG5ZNM5Jcjs.encryptForStorage; exports.ensureSchema = _chunkUG5ZNM5Jcjs.ensureSchema; exports.getSchemaVersion = _chunkUG5ZNM5Jcjs.getSchemaVersion; exports.hasChatCredRef = _chunkUG5ZNM5Jcjs.hasChatCredRef; exports.isNonRetryableError = _chunkUG5ZNM5Jcjs.isNonRetryableError; exports.isValidCredId = _chunkUG5ZNM5Jcjs.isValidCredId; exports.isValidISO8601 = _chunkUG5ZNM5Jcjs.isValidISO8601; exports.isValidTimeZoneId = _chunkUG5ZNM5Jcjs.isValidTimeZoneId; exports.isValidUUID = _chunkUG5ZNM5Jcjs.isValidUUID; exports.isValidUUIDv4 = _chunkUG5ZNM5Jcjs.isValidUUIDv4; exports.isValidUrl = _chunkUG5ZNM5Jcjs.isValidUrl; exports.measurePushPayload = _chunkUG5ZNM5Jcjs.measurePushPayload; exports.nextFutureOccurrence = _chunkUG5ZNM5Jcjs.nextFutureOccurrence; exports.planNextOccurrence = _chunkUG5ZNM5Jcjs.planNextOccurrence; exports.readRequestBody = _chunkUG5ZNM5Jcjs.readRequestBody; exports.resolveLlmCredential = _chunkUG5ZNM5Jcjs.resolveLlmCredential; exports.runScheduledTick = _chunkUG5ZNM5Jcjs.runScheduledTick; exports.runTask = _chunkUG5ZNM5Jcjs.runTask; exports.sanitizeErrorSummary = _chunkUG5ZNM5Jcjs.sanitizeErrorSummary; exports.summarizeErrorCause = _chunkUG5ZNM5Jcjs.summarizeErrorCause; exports.supportsLlmCredentialsStore = _chunkUG5ZNM5Jcjs.supportsLlmCredentialsStore; exports.validateAvatarUrl = _chunkUG5ZNM5Jcjs.validateAvatarUrl; exports.validateCredRefs = _chunkUG5ZNM5Jcjs.validateCredRefs; exports.validateCredValue = _chunkUG5ZNM5Jcjs.validateCredValue; exports.validateLlmMessagesArray = _chunkUG5ZNM5Jcjs.validateLlmMessagesArray; exports.validateScheduleMessagePayload = _chunkUG5ZNM5Jcjs.validateScheduleMessagePayload; exports.validateSplitPattern = _chunkUG5ZNM5Jcjs.validateSplitPattern; exports.verifyTenantToken = verifyTenantToken;
706
+ exports.CORS_ALLOW_HEADERS = _chunkI4BGZXQAcjs.CORS_ALLOW_HEADERS; exports.CORS_ALLOW_METHODS = _chunkI4BGZXQAcjs.CORS_ALLOW_METHODS; exports.CRED_ID_MAX_LENGTH = _chunkI4BGZXQAcjs.CRED_ID_MAX_LENGTH; exports.CRED_PUT_BATCH_MAX = _chunkI4BGZXQAcjs.CRED_PUT_BATCH_MAX; exports.CRED_REFS_MAX_ENTRIES = _chunkI4BGZXQAcjs.CRED_REFS_MAX_ENTRIES; exports.CRED_ROWS_PER_USER_MAX = _chunkI4BGZXQAcjs.CRED_ROWS_PER_USER_MAX; exports.DEFAULT_CLAIM_LEASE_MS = _chunkI4BGZXQAcjs.DEFAULT_CLAIM_LEASE_MS; exports.DEFAULT_HEARTBEAT_LEASE_TTL_MS = _chunkI4BGZXQAcjs.DEFAULT_HEARTBEAT_LEASE_TTL_MS; exports.DEFAULT_LEASE_HEARTBEAT_MS = _chunkI4BGZXQAcjs.DEFAULT_LEASE_HEARTBEAT_MS; exports.DEFAULT_MAX_DELIVERY_RETRIES = _chunkI4BGZXQAcjs.DEFAULT_MAX_DELIVERY_RETRIES; exports.DEFAULT_MAX_REQUEST_BODY_BYTES = _chunkI4BGZXQAcjs.DEFAULT_MAX_REQUEST_BODY_BYTES; exports.DEFAULT_MAX_SCHEDULED_TASKS_PER_FIRE = _chunkI4BGZXQAcjs.DEFAULT_MAX_SCHEDULED_TASKS_PER_FIRE; exports.DEFAULT_MAX_TOOL_ITERATIONS = _chunkI4BGZXQAcjs.DEFAULT_MAX_TOOL_ITERATIONS; exports.DEFAULT_TOTAL_TIMEOUT_MS = _chunkI4BGZXQAcjs.DEFAULT_TOTAL_TIMEOUT_MS; exports.DeploymentConfigError = _chunkI4BGZXQAcjs.DeploymentConfigError; exports.MAX_DEFER_AFTER_MS = _chunkI4BGZXQAcjs.MAX_DEFER_AFTER_MS; exports.MAX_PUSH_PAYLOAD_BYTES = _chunkI4BGZXQAcjs.MAX_PUSH_PAYLOAD_BYTES; exports.MAX_TASK_PAYLOAD_BYTES = _chunkI4BGZXQAcjs.MAX_TASK_PAYLOAD_BYTES; exports.MIN_SCHEDULE_LEAD_MS = _chunkI4BGZXQAcjs.MIN_SCHEDULE_LEAD_MS; exports.NonRetryableError = _chunkI4BGZXQAcjs.NonRetryableError; exports.PUSH_ENVELOPE_RESERVED_BYTES = _chunkI4BGZXQAcjs.PUSH_ENVELOPE_RESERVED_BYTES; exports.SCHEMA_VERSION = _chunkI4BGZXQAcjs.SCHEMA_VERSION; exports.WEB_PUSH_ENCRYPTION_OVERHEAD_BYTES = _chunkI4BGZXQAcjs.WEB_PUSH_ENCRYPTION_OVERHEAD_BYTES; exports.WEB_PUSH_MAX_BODY_BYTES = _chunkI4BGZXQAcjs.WEB_PUSH_MAX_BODY_BYTES; exports.advanceOccurrence = _chunkI4BGZXQAcjs.advanceOccurrence; exports.createAdapter = createAdapter; exports.createD1Adapter = _chunkI4BGZXQAcjs.createD1Adapter; exports.createReiServer = createReiServer; exports.createSingleUserCloudflareWorker = _chunkI4BGZXQAcjs.createSingleUserCloudflareWorker; exports.createSingleUserServer = _chunkI4BGZXQAcjs.createSingleUserServer; exports.createTenantToken = createTenantToken; exports.createWebCryptoWebPush = _chunkI4BGZXQAcjs.createWebCryptoWebPush; exports.decryptFromStorage = _chunkI4BGZXQAcjs.decryptFromStorage; exports.decryptPayload = _chunkI4BGZXQAcjs.decryptPayload; exports.deriveUserEncryptionKey = _chunkI4BGZXQAcjs.deriveUserEncryptionKey; exports.encryptForStorage = _chunkI4BGZXQAcjs.encryptForStorage; exports.ensureSchema = _chunkI4BGZXQAcjs.ensureSchema; exports.getSchemaVersion = _chunkI4BGZXQAcjs.getSchemaVersion; exports.hasChatCredRef = _chunkI4BGZXQAcjs.hasChatCredRef; exports.isNonRetryableError = _chunkI4BGZXQAcjs.isNonRetryableError; exports.isValidCredId = _chunkI4BGZXQAcjs.isValidCredId; exports.isValidISO8601 = _chunkI4BGZXQAcjs.isValidISO8601; exports.isValidTimeZoneId = _chunkI4BGZXQAcjs.isValidTimeZoneId; exports.isValidUUID = _chunkI4BGZXQAcjs.isValidUUID; exports.isValidUUIDv4 = _chunkI4BGZXQAcjs.isValidUUIDv4; exports.isValidUrl = _chunkI4BGZXQAcjs.isValidUrl; exports.measurePushPayload = _chunkI4BGZXQAcjs.measurePushPayload; exports.nextFutureOccurrence = _chunkI4BGZXQAcjs.nextFutureOccurrence; exports.planNextOccurrence = _chunkI4BGZXQAcjs.planNextOccurrence; exports.readRequestBody = _chunkI4BGZXQAcjs.readRequestBody; exports.resolveLlmCredential = _chunkI4BGZXQAcjs.resolveLlmCredential; exports.runScheduledTick = _chunkI4BGZXQAcjs.runScheduledTick; exports.runTask = _chunkI4BGZXQAcjs.runTask; exports.sanitizeErrorSummary = _chunkI4BGZXQAcjs.sanitizeErrorSummary; exports.summarizeErrorCause = _chunkI4BGZXQAcjs.summarizeErrorCause; exports.supportsLlmCredentialsStore = _chunkI4BGZXQAcjs.supportsLlmCredentialsStore; exports.validateAvatarUrl = _chunkI4BGZXQAcjs.validateAvatarUrl; exports.validateCredRefs = _chunkI4BGZXQAcjs.validateCredRefs; exports.validateCredValue = _chunkI4BGZXQAcjs.validateCredValue; exports.validateLlmMessagesArray = _chunkI4BGZXQAcjs.validateLlmMessagesArray; exports.validateScheduleMessagePayload = _chunkI4BGZXQAcjs.validateScheduleMessagePayload; exports.validateSplitPattern = _chunkI4BGZXQAcjs.validateSplitPattern; exports.verifyTenantToken = verifyTenantToken;
package/dist/index.mjs CHANGED
@@ -73,7 +73,7 @@ import {
73
73
  validateLlmMessagesArray,
74
74
  validateScheduleMessagePayload,
75
75
  validateSplitPattern
76
- } from "./chunk-INQD6LRY.mjs";
76
+ } from "./chunk-67RM2E46.mjs";
77
77
  import "./chunk-GN44PST5.mjs";
78
78
 
79
79
  // src/server/adapters/factory.js
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rei-standard/amsg-server",
3
- "version": "2.6.0-next.35",
3
+ "version": "2.6.0-next.36",
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",