@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 +2 -2
- package/dist/adapters/d1.d.ts +7 -1
- package/dist/adapters/interface.d.ts +2 -1
- package/dist/{chunk-INQD6LRY.mjs → chunk-67RM2E46.mjs} +83 -24
- package/dist/{chunk-UG5ZNM5J.cjs → chunk-I4BGZXQA.cjs} +88 -29
- package/dist/cloudflare/single-user-worker.d.ts +4 -2
- package/dist/cloudflare.cjs +2 -2
- package/dist/cloudflare.mjs +1 -1
- package/dist/index.cjs +25 -25
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
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
|
|
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
|
|
package/dist/adapters/d1.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
5514
|
-
|
|
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.
|
|
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
|
|
7836
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7848
|
-
|
|
7849
|
-
if (
|
|
7850
|
-
|
|
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
|
-
|
|
8347
|
-
|
|
8348
|
-
|
|
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
|
-
|
|
8610
|
-
|
|
8611
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
5514
|
-
|
|
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.
|
|
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
|
|
7836
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7848
|
-
|
|
7849
|
-
if (
|
|
7850
|
-
|
|
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 || _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 (
|
|
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',
|
|
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 (
|
|
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',
|
|
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',
|
|
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
|
-
|
|
8347
|
-
|
|
8348
|
-
|
|
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
|
-
|
|
8610
|
-
|
|
8611
|
-
|
|
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;
|
package/dist/cloudflare.cjs
CHANGED
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
|
|
31
|
-
var
|
|
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 =
|
|
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;
|
package/dist/cloudflare.mjs
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
|
|
74
74
|
|
|
75
75
|
|
|
76
|
-
var
|
|
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 =
|
|
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 =
|
|
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 && !
|
|
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
|
|
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.${
|
|
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 =
|
|
287
|
-
const authTag =
|
|
288
|
-
const ciphertext =
|
|
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
|
|
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 =
|
|
373
|
-
const encodedPayload =
|
|
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 =
|
|
393
|
-
expectedBytes =
|
|
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 (!
|
|
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(
|
|
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:
|
|
631
|
-
scheduleMessage:
|
|
630
|
+
getUserKey: _chunkI4BGZXQAcjs.createGetUserKeyHandler.call(void 0, ctx),
|
|
631
|
+
scheduleMessage: _chunkI4BGZXQAcjs.createScheduleMessageHandler.call(void 0, ctx),
|
|
632
632
|
sendNotifications: createSendNotificationsHandler(ctx),
|
|
633
|
-
updateMessage:
|
|
634
|
-
cancelMessage:
|
|
635
|
-
messages:
|
|
636
|
-
getMessage:
|
|
637
|
-
pushSubscription:
|
|
638
|
-
llmCredentials:
|
|
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 =
|
|
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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rei-standard/amsg-server",
|
|
3
|
-
"version": "2.6.0-next.
|
|
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",
|