dsh-vscode-mode 0.23.1 → 0.23.3
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 +3 -0
- package/lib/client.js +234 -47
- package/lib/client.js.map +2 -2
- package/lib/index.js +221 -46
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client/missingReview.ts +24 -0
- package/src/client/reviewBatch.ts +5 -0
- package/src/client/ui/EditorView.ts +172 -19
- package/src/client/ui/OfficialDiffBar.tsx +18 -3
- package/src/client/ui/SidecarDiffBar.tsx +3 -2
- package/src/client/ui/reviewAllRow.tsx +5 -4
- package/src/missingReview.ts +44 -5
- package/src/officialCapture.ts +11 -0
- package/src/reviewDb.ts +23 -0
- package/src/reviewRpc.ts +140 -37
- package/src/rpc.ts +6 -2
- package/src/shared/review.ts +2 -1
- package/src/shared/rpc.ts +4 -1
package/lib/index.js
CHANGED
|
@@ -7649,6 +7649,25 @@ function reviewPage(cwd, cursor = 0, history = false, path) {
|
|
|
7649
7649
|
}))]
|
|
7650
7650
|
};
|
|
7651
7651
|
}
|
|
7652
|
+
/**
|
|
7653
|
+
* @public
|
|
7654
|
+
* @author ddj 2026年10月31号
|
|
7655
|
+
* @description: 该文件是否还有未处理的官方待审改动。缺失文件预览据此区分「需要去差异栏 Keep 结案」
|
|
7656
|
+
* 与「记录都已处理、可直接关闭标签页」,避免已结案的删除文件继续提示待审。
|
|
7657
|
+
* @param cwd Migrated workspace
|
|
7658
|
+
* @param path Host path of the file
|
|
7659
|
+
* @returns 存在未决官方记录时为 true
|
|
7660
|
+
*/
|
|
7661
|
+
function officialPending(cwd, path) {
|
|
7662
|
+
if (typeof path !== "string") return false;
|
|
7663
|
+
const db = dbFor(cwd);
|
|
7664
|
+
if (!db.prepare("SELECT 1 FROM sqlite_master WHERE name='review_files' AND type='table'").get()) return false;
|
|
7665
|
+
if (path.length > 1 && path[1] === ":") {
|
|
7666
|
+
const folded = path.split(String.fromCharCode(92)).join("/").toLowerCase();
|
|
7667
|
+
return !!db.prepare("SELECT 1 FROM review_files WHERE current=1 AND decision='pending' AND lower(replace(identity,char(92),'/'))=? LIMIT 1").get(folded);
|
|
7668
|
+
}
|
|
7669
|
+
return !!db.prepare("SELECT 1 FROM review_files WHERE current=1 AND decision='pending' AND identity=? LIMIT 1").get(path);
|
|
7670
|
+
}
|
|
7652
7671
|
/** @public @author ddj 2026年10月26号 @param cwd Migrated workspace. @param path Legacy write target. @returns Whether any official version owns the file, including decided history. */
|
|
7653
7672
|
function hasOfficial(cwd, path) {
|
|
7654
7673
|
if (typeof path !== "string") return false;
|
|
@@ -7926,20 +7945,31 @@ function installCapture(ctx, onSnapshot) {
|
|
|
7926
7945
|
log.warn("官方历史补采不可用:缺少 sessionQuery;后续实时事件仍会采集");
|
|
7927
7946
|
return;
|
|
7928
7947
|
}
|
|
7948
|
+
const source = ctx.get("workspaceChanges");
|
|
7949
|
+
if (!source?.summary) {
|
|
7950
|
+
log.warn("官方历史补采不可用:缺少 workspaceChanges;后续实时事件仍会采集");
|
|
7951
|
+
return;
|
|
7952
|
+
}
|
|
7929
7953
|
try {
|
|
7930
7954
|
if (session.header?.cwd) {
|
|
7931
7955
|
await loadBucket(ctx, session.header.cwd);
|
|
7932
7956
|
await repairPaths(session.header.cwd, resolvePath);
|
|
7933
7957
|
}
|
|
7934
7958
|
const events = await query.listEvents(session.id);
|
|
7959
|
+
let expired = 0;
|
|
7935
7960
|
for (const event of events) {
|
|
7936
7961
|
if (event.type !== "workspace/changes" || event.seq < (session.inheritedEventCount ?? 0)) continue;
|
|
7962
|
+
if (!source.summary(session.id, event.seq)) {
|
|
7963
|
+
expired += 1;
|
|
7964
|
+
continue;
|
|
7965
|
+
}
|
|
7937
7966
|
const window = await query.readEvent({
|
|
7938
7967
|
sessionId: session.id,
|
|
7939
7968
|
seq: event.seq
|
|
7940
7969
|
});
|
|
7941
7970
|
collector.enqueue(session, window.target);
|
|
7942
7971
|
}
|
|
7972
|
+
if (expired) log.info(`官方历史补采跳过 ${expired} 轮已失效事件:官方 summary 不跨宿主重启保留,实时事件照常采集`);
|
|
7943
7973
|
} catch (error) {
|
|
7944
7974
|
log.warn("官方历史补采失败:" + String(error));
|
|
7945
7975
|
}
|
|
@@ -15971,6 +16001,10 @@ function finishUndo(cwd, request) {
|
|
|
15971
16001
|
//#region src/reviewRpc.ts
|
|
15972
16002
|
/** 批量写入快照的硬上限:待审积压超过它说明状态异常,宁可拒绝也不做无界写循环。 */
|
|
15973
16003
|
const BATCH_LIMIT = 2e3;
|
|
16004
|
+
/** 冷端 Undo 说明:Undo 会校验证据并写回源文件,只能在持有该会话的那一端执行。 */
|
|
16005
|
+
const UNDO_NEEDS_LIVE = "Undo 需要在本端活跃的会话上执行:该会话当前在另一端(Web / 桌面)激活。Keep 不受影响,可在任一端执行;或在本端打开该会话后重试。";
|
|
16006
|
+
/** 冷端待审判定说明:待审库是按工作区的本地存储,判定需要与正在编辑的那一端保持一致。 */
|
|
16007
|
+
const PENDING_NEEDS_LIVE = "待审条目的判定需要在本端活跃的会话上执行:该会话当前在另一端激活,请在那一端处理,或在本端打开该会话后重试。";
|
|
15974
16008
|
/**
|
|
15975
16009
|
* @private
|
|
15976
16010
|
* @author ddj 2026年10月31号
|
|
@@ -16011,6 +16045,39 @@ function batchSnapshot(cwd, pending) {
|
|
|
16011
16045
|
};
|
|
16012
16046
|
}
|
|
16013
16047
|
/**
|
|
16048
|
+
* @private
|
|
16049
|
+
* @author ddj 2026年10月31号
|
|
16050
|
+
* @description: 文件已在磁盘删除时修正官方记录的能力说明。Keep 只写审查库、仍可结案,
|
|
16051
|
+
* Undo 需要读写源文件、必然不可用:把「缺少原始字节」这种误导性原因换成可执行说明。
|
|
16052
|
+
* 只改文案与 undo 位,不参与判定写入。
|
|
16053
|
+
* @param ctx Host 能力
|
|
16054
|
+
* @param cwd 工作区
|
|
16055
|
+
* @param detail 官方记录详情
|
|
16056
|
+
* @param capability 已按冷/热端调整过的能力
|
|
16057
|
+
* @returns 修正后的能力;无法确认删除时原样返回
|
|
16058
|
+
*/
|
|
16059
|
+
async function absenceCapability(ctx, cwd, detail, capability) {
|
|
16060
|
+
if (!capability.keep) return capability;
|
|
16061
|
+
if (detail.diff.kind === "text" && !detail.diff.after) return {
|
|
16062
|
+
...capability,
|
|
16063
|
+
undo: false,
|
|
16064
|
+
reason: "该记录删除了这个文件:Keep 按删除结案;Undo 需要完整文件内容,官方差异只有片段,不可用。"
|
|
16065
|
+
};
|
|
16066
|
+
const fs = ctx.get("fs");
|
|
16067
|
+
if (!fs) return capability;
|
|
16068
|
+
try {
|
|
16069
|
+
const target = await resolveTargetIn(ctx, cwd, detail.item.currentPath);
|
|
16070
|
+
if (await fs.stat(target)) return capability;
|
|
16071
|
+
} catch {
|
|
16072
|
+
return capability;
|
|
16073
|
+
}
|
|
16074
|
+
return {
|
|
16075
|
+
...capability,
|
|
16076
|
+
undo: false,
|
|
16077
|
+
reason: "文件已在磁盘删除:Keep 按删除结案;Undo 需要读写该文件,当前不可用。"
|
|
16078
|
+
};
|
|
16079
|
+
}
|
|
16080
|
+
/**
|
|
16014
16081
|
* @public
|
|
16015
16082
|
* @author ddj 2026年10月26号 / 2026年10月28号
|
|
16016
16083
|
* @description: 审查 RPC 处理器。工作区一律由 host 解析,绝不采信调用方 cwd:
|
|
@@ -16020,19 +16087,61 @@ function batchSnapshot(cwd, pending) {
|
|
|
16020
16087
|
* @returns 审查相关处理器表
|
|
16021
16088
|
*/
|
|
16022
16089
|
function reviewHandlers(ctx, registry) {
|
|
16023
|
-
/**
|
|
16024
|
-
|
|
16090
|
+
/**
|
|
16091
|
+
* @private
|
|
16092
|
+
* @author ddj 2026年10月31号
|
|
16093
|
+
* @description: 解析会话作用域,并标明本端是否持有该会话的活跃实例。
|
|
16094
|
+
* 工作区一律由 host 解析,绝不采信调用方 cwd;冷读作用域只带 cwd,不带会话实例。
|
|
16095
|
+
* @param sessionId 请求作用域
|
|
16096
|
+
* @returns 已迁移工作区与活跃标记
|
|
16097
|
+
* @throws 会话不存在、会话无工作区或活跃库迁移未完成
|
|
16098
|
+
*/
|
|
16099
|
+
async function scopeOf(sessionId) {
|
|
16025
16100
|
const scope = await resolveScope(ctx, sessionId);
|
|
16026
16101
|
if ("err" in scope) throw new Error("会话不存在或没有工作区");
|
|
16027
16102
|
await bucketOf(registry, ctx, scope.cwd);
|
|
16028
|
-
return
|
|
16103
|
+
return {
|
|
16104
|
+
cwd: scope.cwd,
|
|
16105
|
+
live: !!scope.session
|
|
16106
|
+
};
|
|
16029
16107
|
}
|
|
16030
|
-
/**
|
|
16031
|
-
|
|
16032
|
-
|
|
16033
|
-
|
|
16034
|
-
|
|
16035
|
-
|
|
16108
|
+
/**
|
|
16109
|
+
* @private
|
|
16110
|
+
* @author ddj 2026年10月31号
|
|
16111
|
+
* @description: 本端是否持有该会话的活跃实例(纯诊断用,不解析工作区)。
|
|
16112
|
+
* @param sessionId 请求作用域
|
|
16113
|
+
* @returns 本端加载了该会话时为 true
|
|
16114
|
+
*/
|
|
16115
|
+
function liveOf(sessionId) {
|
|
16116
|
+
return !!sessionOf(ctx, sessionId);
|
|
16117
|
+
}
|
|
16118
|
+
/**
|
|
16119
|
+
* @private
|
|
16120
|
+
* @author ddj 2026年10月31号
|
|
16121
|
+
* @description: 按本端是否活跃覆写面板能力位。活跃端保持数据库结论;
|
|
16122
|
+
* 冷端关闭依赖会话的操作(Undo、待审判定)并给出跨端原因,Keep 仍按数据库结论放行。
|
|
16123
|
+
* @param capability 数据库推导的能力位
|
|
16124
|
+
* @param live 本端是否持有活跃会话
|
|
16125
|
+
* @param pending 是否为待审投影条目
|
|
16126
|
+
* @returns 面板可用的能力位
|
|
16127
|
+
*/
|
|
16128
|
+
function liveCapabilityOf(capability, live, pending) {
|
|
16129
|
+
if (live) return {
|
|
16130
|
+
...capability,
|
|
16131
|
+
live: true
|
|
16132
|
+
};
|
|
16133
|
+
if (pending) return {
|
|
16134
|
+
keep: false,
|
|
16135
|
+
undo: false,
|
|
16136
|
+
reason: PENDING_NEEDS_LIVE,
|
|
16137
|
+
live: false
|
|
16138
|
+
};
|
|
16139
|
+
return {
|
|
16140
|
+
...capability,
|
|
16141
|
+
undo: false,
|
|
16142
|
+
live: false,
|
|
16143
|
+
reason: capability.undo ? UNDO_NEEDS_LIVE : capability.reason
|
|
16144
|
+
};
|
|
16036
16145
|
}
|
|
16037
16146
|
/**
|
|
16038
16147
|
* @private
|
|
@@ -16045,7 +16154,11 @@ function reviewHandlers(ctx, registry) {
|
|
|
16045
16154
|
*/
|
|
16046
16155
|
function pendingPageOf(cwd, sessionId) {
|
|
16047
16156
|
try {
|
|
16048
|
-
|
|
16157
|
+
const page = loadPage(cwd, sessionId);
|
|
16158
|
+
return page.items.some((item) => item.settled === true) ? {
|
|
16159
|
+
...page,
|
|
16160
|
+
items: page.items.filter((item) => item.settled !== true)
|
|
16161
|
+
} : page;
|
|
16049
16162
|
} catch {
|
|
16050
16163
|
return {
|
|
16051
16164
|
items: [],
|
|
@@ -16057,7 +16170,7 @@ function reviewHandlers(ctx, registry) {
|
|
|
16057
16170
|
}
|
|
16058
16171
|
return {
|
|
16059
16172
|
"edrv.review.list": async (args) => {
|
|
16060
|
-
const cwd = await
|
|
16173
|
+
const { cwd } = await scopeOf(args.sessionId);
|
|
16061
16174
|
if (args.all && !args.history && args.path === void 0) return batchSnapshot(cwd, pendingPageOf(cwd, args.sessionId));
|
|
16062
16175
|
const official = reviewPage(cwd, args.cursor, args.history, args.path);
|
|
16063
16176
|
const pending = pendingPageOf(cwd, args.sessionId);
|
|
@@ -16077,63 +16190,97 @@ function reviewHandlers(ctx, registry) {
|
|
|
16077
16190
|
}
|
|
16078
16191
|
};
|
|
16079
16192
|
}
|
|
16193
|
+
const merged = mergedPage(official, pending, cwd);
|
|
16194
|
+
log.debug(`[review] list live=${liveOf(args.sessionId) ? 1 : 0} path=${args.path ?? "(all)"} items=${merged.items.length} pending=${merged.pending} covered=${merged.overview?.coveredPaths.length ?? -1}`);
|
|
16080
16195
|
return {
|
|
16081
16196
|
ok: true,
|
|
16082
|
-
page:
|
|
16197
|
+
page: merged
|
|
16083
16198
|
};
|
|
16084
16199
|
},
|
|
16085
16200
|
"edrv.review.detail": async (args) => {
|
|
16086
|
-
const
|
|
16201
|
+
const scope = await scopeOf(args.sessionId);
|
|
16202
|
+
const cwd = scope.cwd;
|
|
16087
16203
|
if (args.id.startsWith("pending:")) {
|
|
16088
16204
|
const detail = loadDetail(cwd, args.id.slice(8));
|
|
16089
16205
|
if (!detail) return {
|
|
16090
16206
|
ok: false,
|
|
16091
16207
|
error: "待审条目不存在"
|
|
16092
16208
|
};
|
|
16209
|
+
const mapped = pendingDetailOf(detail, new Date(detail.item.updatedAt).toISOString(), cwd);
|
|
16093
16210
|
return {
|
|
16094
16211
|
ok: true,
|
|
16095
|
-
detail:
|
|
16212
|
+
detail: {
|
|
16213
|
+
...mapped,
|
|
16214
|
+
capability: liveCapabilityOf(mapped.capability, scope.live, true)
|
|
16215
|
+
}
|
|
16096
16216
|
};
|
|
16097
16217
|
}
|
|
16098
16218
|
const detail = reviewDetail(cwd, args.id);
|
|
16099
|
-
|
|
16219
|
+
if (!detail) {
|
|
16220
|
+
log.debug(`[review] detail live=${scope.live ? 1 : 0} id=${args.id} found=0`);
|
|
16221
|
+
return {
|
|
16222
|
+
ok: false,
|
|
16223
|
+
error: "官方审查记录不存在"
|
|
16224
|
+
};
|
|
16225
|
+
}
|
|
16226
|
+
const capability = await absenceCapability(ctx, cwd, detail, liveCapabilityOf(detail.capability, scope.live, false));
|
|
16227
|
+
log.debug(`[review] detail live=${scope.live ? 1 : 0} id=${args.id} found=1 keep=${capability.keep ? 1 : 0} undo=${capability.undo ? 1 : 0}`);
|
|
16228
|
+
return {
|
|
16100
16229
|
ok: true,
|
|
16101
|
-
detail
|
|
16102
|
-
|
|
16103
|
-
|
|
16104
|
-
|
|
16230
|
+
detail: {
|
|
16231
|
+
...detail,
|
|
16232
|
+
capability
|
|
16233
|
+
}
|
|
16105
16234
|
};
|
|
16106
16235
|
},
|
|
16107
16236
|
"edrv.review.decide": async (args) => {
|
|
16108
|
-
const cwd = await liveWorkspace(args.sessionId);
|
|
16109
|
-
if (args.id.startsWith("pending:")) {
|
|
16110
|
-
const result = await applyDecide(ctx.get("fs"), cwd, {
|
|
16111
|
-
sessionId: args.sessionId,
|
|
16112
|
-
id: args.id.slice(8),
|
|
16113
|
-
revision: args.revision,
|
|
16114
|
-
operationId: args.operationId,
|
|
16115
|
-
decision: args.decision,
|
|
16116
|
-
scope: args.scope === "hunk" ? "block" : "file",
|
|
16117
|
-
...args.blockId === void 0 ? {} : { blockId: args.blockId }
|
|
16118
|
-
});
|
|
16119
|
-
if (!result.ok) return {
|
|
16120
|
-
ok: false,
|
|
16121
|
-
error: result.reason
|
|
16122
|
-
};
|
|
16123
|
-
return {
|
|
16124
|
-
ok: true,
|
|
16125
|
-
revision: result.flags.revision
|
|
16126
|
-
};
|
|
16127
|
-
}
|
|
16128
16237
|
try {
|
|
16238
|
+
const scope = await scopeOf(args.sessionId);
|
|
16239
|
+
if (args.id.startsWith("pending:")) {
|
|
16240
|
+
if (!scope.live) return {
|
|
16241
|
+
ok: false,
|
|
16242
|
+
error: PENDING_NEEDS_LIVE
|
|
16243
|
+
};
|
|
16244
|
+
const result = await applyDecide(ctx.get("fs"), scope.cwd, {
|
|
16245
|
+
sessionId: args.sessionId,
|
|
16246
|
+
id: args.id.slice(8),
|
|
16247
|
+
revision: args.revision,
|
|
16248
|
+
operationId: args.operationId,
|
|
16249
|
+
decision: args.decision,
|
|
16250
|
+
scope: args.scope === "hunk" ? "block" : "file",
|
|
16251
|
+
...args.blockId === void 0 ? {} : { blockId: args.blockId }
|
|
16252
|
+
});
|
|
16253
|
+
if (!result.ok) return {
|
|
16254
|
+
ok: false,
|
|
16255
|
+
error: result.reason
|
|
16256
|
+
};
|
|
16257
|
+
return {
|
|
16258
|
+
ok: true,
|
|
16259
|
+
revision: result.flags.revision
|
|
16260
|
+
};
|
|
16261
|
+
}
|
|
16262
|
+
if (args.decision === "rejected") {
|
|
16263
|
+
if (!scope.live) return {
|
|
16264
|
+
ok: false,
|
|
16265
|
+
error: UNDO_NEEDS_LIVE
|
|
16266
|
+
};
|
|
16267
|
+
return {
|
|
16268
|
+
ok: true,
|
|
16269
|
+
...await undoReview(ctx, sessionOf(ctx, args.sessionId), scope.cwd, args)
|
|
16270
|
+
};
|
|
16271
|
+
}
|
|
16272
|
+
const result = decideReview(scope.cwd, args);
|
|
16273
|
+
if (!scope.live) log.info(`[review] 冷端 Keep 官方记录 ${args.id}`);
|
|
16129
16274
|
return {
|
|
16130
16275
|
ok: true,
|
|
16131
|
-
...
|
|
16276
|
+
...result
|
|
16132
16277
|
};
|
|
16133
16278
|
} catch (error) {
|
|
16279
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
16280
|
+
log.warn(`[review] decide 失败 id=${args.id} decision=${args.decision}: ${message}`);
|
|
16134
16281
|
return {
|
|
16135
16282
|
ok: false,
|
|
16136
|
-
error:
|
|
16283
|
+
error: message
|
|
16137
16284
|
};
|
|
16138
16285
|
}
|
|
16139
16286
|
}
|
|
@@ -16386,6 +16533,29 @@ function pendingHandlers(ctx, registry) {
|
|
|
16386
16533
|
/** Missing historical files: frozen, whole-file deletion reviews. */
|
|
16387
16534
|
const frozen = /* @__PURE__ */ new Map();
|
|
16388
16535
|
const locks = /* @__PURE__ */ new Set();
|
|
16536
|
+
/**
|
|
16537
|
+
* @public
|
|
16538
|
+
* @author ddj 2026年10月31号
|
|
16539
|
+
* @description: 给缺失文件预览的可预期拒绝挂稳定错误码。客户端据此显示终态说明,
|
|
16540
|
+
* 而不是把内部错误原文当「加载失败」反复重试;也不让上游把 SQLite 报错原文透给编辑器。
|
|
16541
|
+
* @param code 稳定错误码(official-covered / no-records / no-evidence / too-large)
|
|
16542
|
+
* @param message 可读原因
|
|
16543
|
+
* @returns 带 code 的 Error
|
|
16544
|
+
*/
|
|
16545
|
+
function refusal(code, message) {
|
|
16546
|
+
return Object.assign(new Error(message), { code });
|
|
16547
|
+
}
|
|
16548
|
+
/**
|
|
16549
|
+
* @public
|
|
16550
|
+
* @author ddj 2026年10月31号
|
|
16551
|
+
* @description: 从任意错误里取回缺失文件预览的稳定拒绝码;不是本模块抛出的错误一律返回 undefined。
|
|
16552
|
+
* @param error 捕获到的错误
|
|
16553
|
+
* @returns 稳定拒绝码或 undefined
|
|
16554
|
+
*/
|
|
16555
|
+
function refusalCodeOf(error) {
|
|
16556
|
+
const code = error?.code;
|
|
16557
|
+
return code === "official-covered" || code === "official-decided" || code === "no-records" || code === "no-evidence" || code === "too-large" ? code : void 0;
|
|
16558
|
+
}
|
|
16389
16559
|
/** @private @author ddj 2026年10月28号 @param target Resolved absolute path. @returns Platform file identity. */
|
|
16390
16560
|
function fileKey(target) {
|
|
16391
16561
|
const normalized = resolve(target).replace(/\\/g, "/");
|
|
@@ -16436,13 +16606,16 @@ async function fileAbsent(ctx, cwd, path) {
|
|
|
16436
16606
|
async function missingReview(ctx, session, cwd, bucket, path) {
|
|
16437
16607
|
const abs = targetPathOf(ctx, await resolveTargetIn(ctx, cwd, path));
|
|
16438
16608
|
if (!abs) throw new Error("无法解析目标文件路径");
|
|
16439
|
-
if (hasOfficial(cwd, path) || hasOfficial(cwd, abs))
|
|
16609
|
+
if (hasOfficial(cwd, path) || hasOfficial(cwd, abs)) {
|
|
16610
|
+
if (!officialPending(cwd, abs) && !officialPending(cwd, path)) throw refusal("official-decided", "该文件已在磁盘删除,官方审查记录均已处理:可直接关闭该标签页");
|
|
16611
|
+
throw refusal("official-covered", "该文件已有官方审查记录(磁盘上已删除):请在差异栏对该待审条目执行 Keep 结案;整文件删除预览只适用于待审历史记录");
|
|
16612
|
+
}
|
|
16440
16613
|
if (!await fileAbsent(ctx, cwd, abs)) throw new Error("文件已重新出现,请刷新");
|
|
16441
16614
|
const records = await fileRecords(ctx, cwd, bucket, abs);
|
|
16442
16615
|
const latest = records.at(-1);
|
|
16443
|
-
if (!records.some((record) => !record.archived && !recordResolved(record))) throw
|
|
16444
|
-
if (!latest || typeof latest.after !== "string") throw
|
|
16445
|
-
if (Buffer.byteLength(latest.after, "utf8") > 8388608) throw
|
|
16616
|
+
if (!records.some((record) => !record.archived && !recordResolved(record))) throw refusal("no-records", "磁盘上已没有该文件,且没有待审历史记录:可直接关闭该标签页");
|
|
16617
|
+
if (!latest || typeof latest.after !== "string") throw refusal("no-evidence", "磁盘上已没有该文件,且缺少可靠的完整历史快照,无法展示或恢复;不会用局部差异拼接文件");
|
|
16618
|
+
if (Buffer.byteLength(latest.after, "utf8") > 8388608) throw refusal("too-large", "磁盘上已没有该文件,且历史快照过大,无法整文件预览");
|
|
16446
16619
|
for (const [token, item] of frozen) if (item.expires < Date.now()) frozen.delete(token);
|
|
16447
16620
|
if (frozen.size >= 256) throw new Error("缺失文件预览过多,请稍后重试");
|
|
16448
16621
|
const view = {
|
|
@@ -17232,9 +17405,11 @@ function buildHandlers(ctx, registry, searcher = newSearcher(ctx), contentSearch
|
|
|
17232
17405
|
review: await missingReview(ctx, sc.session, sc.cwd, bucket, args.path)
|
|
17233
17406
|
};
|
|
17234
17407
|
} catch (error) {
|
|
17408
|
+
const code = refusalCodeOf(error);
|
|
17235
17409
|
return {
|
|
17236
17410
|
ok: false,
|
|
17237
|
-
error: String(error)
|
|
17411
|
+
error: String(error),
|
|
17412
|
+
...code ? { code } : {}
|
|
17238
17413
|
};
|
|
17239
17414
|
}
|
|
17240
17415
|
},
|