@iasiv5/dsh-skins 1.5.11 → 1.5.12
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/lib/client.js +145 -26
- package/lib/index.js +95 -26
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -940,9 +940,11 @@ ${darkSelector}{${dark.join(";")}}`;
|
|
|
940
940
|
"personalization.panelLabel": "个性化设置",
|
|
941
941
|
"personalization.saveFailed": "保存失败,请重试",
|
|
942
942
|
"personalization.saveConflict": "配置已在其他窗口更新,请重试",
|
|
943
|
+
"personalization.fieldReverted": "壁纸已失效,已回退默认",
|
|
944
|
+
"personalization.library.previewRef": "未保存引用",
|
|
943
945
|
"personalization.library.clear": "清空图库",
|
|
944
946
|
"personalization.library.clearConfirm": "将永久删除全部 {count} 张上传图片,且不可恢复。以下壁纸设置将回退为默认:{names}",
|
|
945
|
-
"personalization.library.clearFailed": "
|
|
947
|
+
"personalization.library.clearFailed": "清空图库失败,已刷新剩余列表",
|
|
946
948
|
"personalization.library.uploading": "正在上传…",
|
|
947
949
|
"personalization.library.uploadingBatch": "正在上传 {done}/{total}…",
|
|
948
950
|
"personalization.library.uploadSomeFailed": "已上传 {ok} 张,{failed} 张失败({reason})",
|
|
@@ -1013,9 +1015,11 @@ ${darkSelector}{${dark.join(";")}}`;
|
|
|
1013
1015
|
"personalization.panelLabel": "Personalization settings",
|
|
1014
1016
|
"personalization.saveFailed": "Save failed; try again",
|
|
1015
1017
|
"personalization.saveConflict": "Settings changed in another window; try again",
|
|
1018
|
+
"personalization.fieldReverted": "Wallpaper no longer exists; reset to default",
|
|
1019
|
+
"personalization.library.previewRef": "unsaved reference",
|
|
1016
1020
|
"personalization.library.clear": "Clear library",
|
|
1017
1021
|
"personalization.library.clearConfirm": "This permanently deletes all {count} uploaded images. These wallpaper settings fall back to defaults: {names}",
|
|
1018
|
-
"personalization.library.clearFailed": "
|
|
1022
|
+
"personalization.library.clearFailed": "Clear library failed; the remaining list was refreshed",
|
|
1019
1023
|
"personalization.library.uploading": "Uploading…",
|
|
1020
1024
|
"personalization.library.uploadingBatch": "Uploading {done}/{total}…",
|
|
1021
1025
|
"personalization.library.uploadSomeFailed": "Uploaded {ok}; {failed} failed ({reason})",
|
|
@@ -1260,11 +1264,16 @@ ${darkSelector}{${dark.join(";")}}`;
|
|
|
1260
1264
|
setDeletingId(null);
|
|
1261
1265
|
}
|
|
1262
1266
|
};
|
|
1267
|
+
const previewRefsFor = (assetId) => (configClient.dirtyFields?.() ?? []).filter((entry) => entry.value === assetId).map((entry) => `${labelFor(entry.skinId)} · ${entry.key} · ${tr("personalization.library.previewRef")}`);
|
|
1263
1268
|
const removeAsset = (asset) => {
|
|
1264
1269
|
if (deletingId !== null || clearing) return;
|
|
1265
1270
|
const references = state.references[asset.id] ?? [];
|
|
1266
|
-
|
|
1267
|
-
|
|
1271
|
+
const previewRefs = previewRefsFor(asset.id);
|
|
1272
|
+
if (references.length === 0 && previewRefs.length === 0) return deleteAsset(asset);
|
|
1273
|
+
const names = [
|
|
1274
|
+
...references.map((entry) => `${labelFor(entry.skinId)} · ${entry.key}`),
|
|
1275
|
+
...previewRefs
|
|
1276
|
+
].join("\n");
|
|
1268
1277
|
requestConfirm(
|
|
1269
1278
|
`${tr("personalization.library.deleteConfirm")}
|
|
1270
1279
|
${names}`,
|
|
@@ -1273,26 +1282,26 @@ ${names}`,
|
|
|
1273
1282
|
};
|
|
1274
1283
|
const clearLibrary = () => {
|
|
1275
1284
|
if (clearing || deletingId !== null || library.length === 0) return;
|
|
1276
|
-
const
|
|
1285
|
+
const names = /* @__PURE__ */ new Map();
|
|
1277
1286
|
for (const asset of library) {
|
|
1278
1287
|
for (const entry of state.references[asset.id] ?? []) {
|
|
1279
|
-
|
|
1288
|
+
names.set(`${labelFor(entry.skinId)} · ${entry.key}`, true);
|
|
1289
|
+
}
|
|
1290
|
+
for (const label of previewRefsFor(asset.id)) {
|
|
1291
|
+
names.set(label, true);
|
|
1280
1292
|
}
|
|
1281
1293
|
}
|
|
1282
|
-
const
|
|
1294
|
+
const listed = [...names.keys()].join("、");
|
|
1283
1295
|
requestConfirm(tr("personalization.library.clearConfirm", {
|
|
1284
1296
|
count: library.length,
|
|
1285
|
-
names:
|
|
1297
|
+
names: listed === "" ? "—" : listed
|
|
1286
1298
|
}), async () => {
|
|
1287
1299
|
setClearing(true);
|
|
1288
1300
|
try {
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
void configClient.refetch();
|
|
1294
|
-
return;
|
|
1295
|
-
}
|
|
1301
|
+
const result = await configClient.clearLibrary();
|
|
1302
|
+
if (result.error) {
|
|
1303
|
+
setMessage(tr("personalization.library.clearFailed"));
|
|
1304
|
+
void configClient.refetch();
|
|
1296
1305
|
}
|
|
1297
1306
|
} catch {
|
|
1298
1307
|
setMessage(tr("personalization.library.clearFailed"));
|
|
@@ -1405,7 +1414,10 @@ ${names}`,
|
|
|
1405
1414
|
);
|
|
1406
1415
|
}
|
|
1407
1416
|
}
|
|
1408
|
-
if (lastAssetId !== null)
|
|
1417
|
+
if (lastAssetId !== null) {
|
|
1418
|
+
onValue(lastAssetId);
|
|
1419
|
+
await configClient.flushNow();
|
|
1420
|
+
}
|
|
1409
1421
|
setMessage(firstError === null ? null : ok === 0 ? firstError : tr("personalization.library.uploadSomeFailed", {
|
|
1410
1422
|
ok,
|
|
1411
1423
|
failed: files.length - ok,
|
|
@@ -1417,6 +1429,7 @@ ${names}`,
|
|
|
1417
1429
|
if (result.asset) {
|
|
1418
1430
|
setMessage(null);
|
|
1419
1431
|
onValue(result.asset.id);
|
|
1432
|
+
await configClient.flushNow();
|
|
1420
1433
|
} else {
|
|
1421
1434
|
setMessage(resolveHostErrorText(
|
|
1422
1435
|
{ code: result.error, message: tr("personalization.library.uploadFailed") },
|
|
@@ -1554,6 +1567,15 @@ ${names}`,
|
|
|
1554
1567
|
jsx("div", { className: "dsh-skins-pz-muted", children: state.lastFlushError })
|
|
1555
1568
|
] }));
|
|
1556
1569
|
}
|
|
1570
|
+
if (Array.isArray(state.revertedFields) && state.revertedFields.length > 0) {
|
|
1571
|
+
statusCluster.push(jsx("div", { key: "reverted", className: "dsh-skins-pz-status", children: [
|
|
1572
|
+
jsx("div", { children: tr("personalization.fieldReverted") }),
|
|
1573
|
+
jsx("div", {
|
|
1574
|
+
className: "dsh-skins-pz-muted",
|
|
1575
|
+
children: state.revertedFields.map((entry) => `${labelFor(entry.skinId)} · ${entry.key}`).join("、")
|
|
1576
|
+
})
|
|
1577
|
+
] }));
|
|
1578
|
+
}
|
|
1557
1579
|
return jsx("div", { className: "dsh-skins-pz", children: [
|
|
1558
1580
|
// Header row (`.dsh-skins-pz-head`): the heading stays the focus
|
|
1559
1581
|
// target (issue #12); the reset and collapse controls pack at its
|
|
@@ -2193,6 +2215,7 @@ ${names}`,
|
|
|
2193
2215
|
let writeChain = Promise.resolve();
|
|
2194
2216
|
let lastFlushCode = null;
|
|
2195
2217
|
let lastFlushError = null;
|
|
2218
|
+
let revertedFields = [];
|
|
2196
2219
|
let channel = null;
|
|
2197
2220
|
function setFlushFailure(code, message) {
|
|
2198
2221
|
lastFlushCode = code;
|
|
@@ -2218,6 +2241,7 @@ ${names}`,
|
|
|
2218
2241
|
quota: snapshot.quota,
|
|
2219
2242
|
lastFlushCode,
|
|
2220
2243
|
lastFlushError,
|
|
2244
|
+
revertedFields: revertedFields.map((entry) => ({ ...entry })),
|
|
2221
2245
|
dirtyCount: previews.size
|
|
2222
2246
|
};
|
|
2223
2247
|
}
|
|
@@ -2374,8 +2398,31 @@ ${names}`,
|
|
|
2374
2398
|
if (!response.ok) {
|
|
2375
2399
|
const errorBody = await response.json().catch(() => null);
|
|
2376
2400
|
const errorMessage = errorBody?.error ?? `HTTP ${response.status}`;
|
|
2377
|
-
|
|
2401
|
+
const issues = Array.isArray(errorBody?.params?.issues) ? errorBody.params.issues : null;
|
|
2402
|
+
let excised = false;
|
|
2403
|
+
let onlyDangling = issues !== null && issues.length > 0;
|
|
2404
|
+
if (issues !== null) {
|
|
2405
|
+
for (const issue of issues) {
|
|
2406
|
+
const dangling = issue.code === "MISSING_ASSET" || issue.code === "BAD_ASSET";
|
|
2407
|
+
if (!dangling) onlyDangling = false;
|
|
2408
|
+
if (dangling && typeof issue.skinId === "string" && typeof issue.key === "string") {
|
|
2409
|
+
const composite = `${issue.skinId} ${issue.key}`;
|
|
2410
|
+
if (previews.has(composite)) {
|
|
2411
|
+
previews.delete(composite);
|
|
2412
|
+
excised = true;
|
|
2413
|
+
const alreadyNoted = revertedFields.some(
|
|
2414
|
+
(entry) => entry.skinId === issue.skinId && entry.key === issue.key
|
|
2415
|
+
);
|
|
2416
|
+
if (!alreadyNoted) revertedFields.push({ skinId: issue.skinId, key: issue.key });
|
|
2417
|
+
}
|
|
2418
|
+
}
|
|
2419
|
+
}
|
|
2420
|
+
}
|
|
2421
|
+
const healed = onlyDangling && excised;
|
|
2422
|
+
setFlushFailure(null, healed ? null : errorMessage);
|
|
2378
2423
|
emit();
|
|
2424
|
+
if (excised && previews.size > 0) void flushNow();
|
|
2425
|
+
if (healed) return { flushed: 0, blocked: null, selfHealed: true };
|
|
2379
2426
|
return { flushed: 0, blocked: "error", errorMessage };
|
|
2380
2427
|
}
|
|
2381
2428
|
await response.json().catch(() => null);
|
|
@@ -2383,6 +2430,7 @@ ${names}`,
|
|
|
2383
2430
|
if (previews.get(composite) === value) previews.delete(composite);
|
|
2384
2431
|
}
|
|
2385
2432
|
setFlushFailure(null, null);
|
|
2433
|
+
revertedFields = [];
|
|
2386
2434
|
await refetch();
|
|
2387
2435
|
announce();
|
|
2388
2436
|
return { flushed: count };
|
|
@@ -2508,22 +2556,91 @@ ${names}`,
|
|
|
2508
2556
|
async function concludeUpload(outcome) {
|
|
2509
2557
|
if (outcome.error !== void 0) return { error: outcome.error };
|
|
2510
2558
|
const body = await outcome.response.json();
|
|
2511
|
-
|
|
2559
|
+
if (body?.asset?.id !== void 0) {
|
|
2560
|
+
snapshot = {
|
|
2561
|
+
...snapshot,
|
|
2562
|
+
revision: typeof body.revision === "number" ? body.revision : snapshot.revision,
|
|
2563
|
+
library: [...snapshot.library, body.asset],
|
|
2564
|
+
quota: {
|
|
2565
|
+
count: (snapshot.quota?.count ?? 0) + 1,
|
|
2566
|
+
totalBytes: (snapshot.quota?.totalBytes ?? 0) + (body.asset.byteLength ?? 0)
|
|
2567
|
+
}
|
|
2568
|
+
};
|
|
2569
|
+
emit();
|
|
2570
|
+
}
|
|
2512
2571
|
announce();
|
|
2513
2572
|
return { asset: body.asset };
|
|
2514
2573
|
}
|
|
2574
|
+
function runGalleryOp(operations, goalAchieved) {
|
|
2575
|
+
writeChain = writeChain.then(async () => {
|
|
2576
|
+
if (disposed) return { error: "disposed" };
|
|
2577
|
+
const blocked = gateWrites();
|
|
2578
|
+
if (blocked !== null) return { error: blocked };
|
|
2579
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
2580
|
+
let response;
|
|
2581
|
+
try {
|
|
2582
|
+
response = await request("/config", {
|
|
2583
|
+
method: "PATCH",
|
|
2584
|
+
headers: { "content-type": "application/json" },
|
|
2585
|
+
body: JSON.stringify({ baseRevision: snapshot.revision, operations })
|
|
2586
|
+
});
|
|
2587
|
+
} catch {
|
|
2588
|
+
return { error: "OFFLINE" };
|
|
2589
|
+
}
|
|
2590
|
+
if (disposed) return { error: "disposed" };
|
|
2591
|
+
if (response.status === 409) {
|
|
2592
|
+
const conflictBody = await response.json().catch(() => null);
|
|
2593
|
+
if (conflictBody?.code === "STORE_READONLY") {
|
|
2594
|
+
setFlushFailure(null, null);
|
|
2595
|
+
setStatus("unsupported-readonly");
|
|
2596
|
+
return { error: "conflict" };
|
|
2597
|
+
}
|
|
2598
|
+
await refetch();
|
|
2599
|
+
if (goalAchieved()) return { affectedSkins: [] };
|
|
2600
|
+
if (attempt === 1) {
|
|
2601
|
+
setFlushFailure(null, null);
|
|
2602
|
+
emit();
|
|
2603
|
+
return { error: "REVISION_CONFLICT" };
|
|
2604
|
+
}
|
|
2605
|
+
continue;
|
|
2606
|
+
}
|
|
2607
|
+
if (!response.ok) {
|
|
2608
|
+
const body2 = await response.json().catch(() => null);
|
|
2609
|
+
if (body2?.code === "INVALID_CONFIG" && body2.params?.issues?.some((issue) => issue.code === "ASSET_NOT_FOUND")) {
|
|
2610
|
+
await refetch();
|
|
2611
|
+
if (goalAchieved()) return { affectedSkins: [] };
|
|
2612
|
+
return { error: "ASSET_NOT_FOUND" };
|
|
2613
|
+
}
|
|
2614
|
+
return { error: body2?.code ?? `HTTP ${response.status}` };
|
|
2615
|
+
}
|
|
2616
|
+
const body = await response.json().catch(() => null);
|
|
2617
|
+
await refetch();
|
|
2618
|
+
announce();
|
|
2619
|
+
return { affectedSkins: body?.affectedSkins ?? [], revision: body?.revision };
|
|
2620
|
+
}
|
|
2621
|
+
return { error: "REVISION_CONFLICT" };
|
|
2622
|
+
});
|
|
2623
|
+
return writeChain;
|
|
2624
|
+
}
|
|
2515
2625
|
async function deleteImage(id) {
|
|
2516
2626
|
const blocked = gateWrites();
|
|
2517
2627
|
if (blocked !== null) return { error: blocked };
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2628
|
+
return runGalleryOp(
|
|
2629
|
+
[{ op: "deleteAsset", assetId: id }],
|
|
2630
|
+
() => !snapshot.library.some((entry) => entry.id === id)
|
|
2631
|
+
);
|
|
2632
|
+
}
|
|
2633
|
+
function clearLibrary() {
|
|
2634
|
+
return runGalleryOp([{ op: "clearLibrary" }], () => snapshot.library.length === 0);
|
|
2635
|
+
}
|
|
2636
|
+
function dirtyFields() {
|
|
2637
|
+
const fields = [];
|
|
2638
|
+
for (const [composite, value] of previews) {
|
|
2639
|
+
if (value === null) continue;
|
|
2640
|
+
const [skinId, key] = composite.split(" ");
|
|
2641
|
+
fields.push({ skinId, key, value });
|
|
2522
2642
|
}
|
|
2523
|
-
|
|
2524
|
-
await refetch();
|
|
2525
|
-
announce();
|
|
2526
|
-
return { affectedSkins: body.affectedSkins ?? [], revision: body.revision };
|
|
2643
|
+
return fields;
|
|
2527
2644
|
}
|
|
2528
2645
|
async function confirmRecovery() {
|
|
2529
2646
|
const response = await request("/recovery", { method: "POST" });
|
|
@@ -2548,6 +2665,8 @@ ${names}`,
|
|
|
2548
2665
|
effectiveOverrides,
|
|
2549
2666
|
uploadImage,
|
|
2550
2667
|
deleteImage,
|
|
2668
|
+
clearLibrary,
|
|
2669
|
+
dirtyFields,
|
|
2551
2670
|
confirmRecovery,
|
|
2552
2671
|
assetUrl,
|
|
2553
2672
|
getState: publicState,
|
package/lib/index.js
CHANGED
|
@@ -693,6 +693,20 @@ function createPersonalizationStore(options = {}) {
|
|
|
693
693
|
if (mode === "recovery") base.recovery = recovery;
|
|
694
694
|
return base;
|
|
695
695
|
}
|
|
696
|
+
function applyGalleryDelete(draft, id, affectedSkins) {
|
|
697
|
+
const meta = draft.library[id];
|
|
698
|
+
for (const [skinId, section] of Object.entries(draft.skins)) {
|
|
699
|
+
for (const field of listAssetFields(skinId)) {
|
|
700
|
+
if (section?.[field.key] === id) {
|
|
701
|
+
delete section[field.key];
|
|
702
|
+
affectedSkins.push({ skinId, key: field.key });
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
if (Object.keys(section).length === 0) delete draft.skins[skinId];
|
|
706
|
+
}
|
|
707
|
+
delete draft.library[id];
|
|
708
|
+
return meta;
|
|
709
|
+
}
|
|
696
710
|
async function applyOperations({ baseRevision, operations }) {
|
|
697
711
|
init();
|
|
698
712
|
requireNormal("配置写入");
|
|
@@ -703,36 +717,100 @@ function createPersonalizationStore(options = {}) {
|
|
|
703
717
|
if (!Array.isArray(operations) || operations.length === 0 || operations.length > 64) {
|
|
704
718
|
throw codedError("INVALID_CONFIG", "operations 必须是 1–64 个元素的数组");
|
|
705
719
|
}
|
|
706
|
-
|
|
720
|
+
const issues = [];
|
|
721
|
+
for (let index = 0; index < operations.length; index += 1) {
|
|
722
|
+
const operation = operations[index];
|
|
707
723
|
if (operation === null || typeof operation !== "object") {
|
|
708
|
-
|
|
724
|
+
issues.push({ opIndex: index, code: "INVALID_OP" });
|
|
725
|
+
continue;
|
|
709
726
|
}
|
|
710
727
|
const { op, skinId, key } = operation;
|
|
711
|
-
if (op
|
|
712
|
-
|
|
713
|
-
|
|
728
|
+
if (op === "set" || op === "delete") {
|
|
729
|
+
if (getField(skinId, key) === null) {
|
|
730
|
+
issues.push({ opIndex: index, skinId, key, code: "UNKNOWN_FIELD" });
|
|
731
|
+
}
|
|
732
|
+
} else if (op === "deleteAsset") {
|
|
733
|
+
const assetId = operation.assetId;
|
|
734
|
+
if (typeof assetId !== "string" || !ASSET_ID_PATTERN.test(assetId)) {
|
|
735
|
+
issues.push({ opIndex: index, assetId, code: "INVALID_ASSET_ID" });
|
|
736
|
+
} else if (state.library[assetId] === void 0) {
|
|
737
|
+
issues.push({ opIndex: index, assetId, code: "ASSET_NOT_FOUND" });
|
|
738
|
+
}
|
|
739
|
+
} else if (op !== "clearLibrary") {
|
|
740
|
+
issues.push({ opIndex: index, code: "INVALID_OP" });
|
|
714
741
|
}
|
|
715
742
|
}
|
|
716
743
|
const draft = structuredClone(state);
|
|
717
744
|
const provider = metaProviderFactory(draft.library);
|
|
745
|
+
for (let index = 0; index < operations.length; index += 1) {
|
|
746
|
+
const operation = operations[index];
|
|
747
|
+
if (operation?.op !== "set") continue;
|
|
748
|
+
const verdict = validateOverride(operation.skinId, operation.key, operation.value, provider);
|
|
749
|
+
if (!verdict.ok) {
|
|
750
|
+
issues.push({ opIndex: index, skinId: operation.skinId, key: operation.key, code: verdict.code });
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
if (issues.length > 0) {
|
|
754
|
+
issues.sort((a, b) => a.opIndex - b.opIndex);
|
|
755
|
+
throw codedError("INVALID_CONFIG", `operations 校验失败(${issues[0].code})`, {
|
|
756
|
+
code: issues[0].code,
|
|
757
|
+
// retained for old readers (ADR-0015 Q&A R1-8)
|
|
758
|
+
issues
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
const affectedSkins = [];
|
|
762
|
+
const removedAssets = [];
|
|
763
|
+
let galleryOps = false;
|
|
764
|
+
const hasFieldOps = operations.some((operation) => operation?.op === "set" || operation?.op === "delete");
|
|
718
765
|
for (const operation of operations) {
|
|
719
766
|
const { op, skinId, key } = operation;
|
|
720
|
-
if (op === "set") {
|
|
721
|
-
|
|
722
|
-
if (
|
|
723
|
-
|
|
767
|
+
if (op === "set" || op === "delete") {
|
|
768
|
+
draft.skins[skinId] = draft.skins[skinId] ?? {};
|
|
769
|
+
if (op === "set") draft.skins[skinId][key] = operation.value;
|
|
770
|
+
else delete draft.skins[skinId][key];
|
|
771
|
+
} else if (op === "deleteAsset") {
|
|
772
|
+
galleryOps = true;
|
|
773
|
+
const meta = applyGalleryDelete(draft, operation.assetId, affectedSkins);
|
|
774
|
+
if (meta !== void 0) removedAssets.push(meta);
|
|
775
|
+
} else if (op === "clearLibrary") {
|
|
776
|
+
galleryOps = true;
|
|
777
|
+
for (const id of Object.keys(draft.library)) {
|
|
778
|
+
const meta = applyGalleryDelete(draft, id, affectedSkins);
|
|
779
|
+
if (meta !== void 0) removedAssets.push(meta);
|
|
724
780
|
}
|
|
725
781
|
}
|
|
726
782
|
}
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
783
|
+
if (galleryOps && !hasFieldOps && removedAssets.length === 0) {
|
|
784
|
+
return { revision: state.revision, affectedSkins };
|
|
785
|
+
}
|
|
786
|
+
if (galleryOps) {
|
|
787
|
+
const finalProvider = metaProviderFactory(draft.library);
|
|
788
|
+
for (const [skinId, section] of Object.entries(draft.skins)) {
|
|
789
|
+
for (const field of listAssetFields(skinId)) {
|
|
790
|
+
const value = section?.[field.key];
|
|
791
|
+
if (value === void 0) continue;
|
|
792
|
+
const verdict = validateOverride(skinId, field.key, value, finalProvider);
|
|
793
|
+
if (!verdict.ok) {
|
|
794
|
+
throw codedError("INVALID_CONFIG", `终态校验失败:${skinId}.${field.key} 引用已删除资产`, {
|
|
795
|
+
code: verdict.code,
|
|
796
|
+
issues: [{ skinId, key: field.key, code: verdict.code }]
|
|
797
|
+
});
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
}
|
|
731
801
|
}
|
|
732
802
|
draft.revision += 1;
|
|
733
803
|
commitState(draft);
|
|
804
|
+
for (const meta of removedAssets) {
|
|
805
|
+
try {
|
|
806
|
+
fs.unlinkSync(blobFileFor(meta.id, meta.extension));
|
|
807
|
+
} catch {
|
|
808
|
+
}
|
|
809
|
+
}
|
|
734
810
|
gcNow();
|
|
735
|
-
|
|
811
|
+
const result = { revision: draft.revision };
|
|
812
|
+
if (galleryOps) result.affectedSkins = affectedSkins;
|
|
813
|
+
return result;
|
|
736
814
|
});
|
|
737
815
|
}
|
|
738
816
|
function ingestAssetMeta(buffer, { declaredMime } = {}) {
|
|
@@ -829,23 +907,14 @@ function createPersonalizationStore(options = {}) {
|
|
|
829
907
|
throw codedError("INVALID_ASSET_ID", "非法的图片 id");
|
|
830
908
|
}
|
|
831
909
|
return enqueue(() => {
|
|
832
|
-
|
|
833
|
-
if (meta === void 0) throw codedError("ASSET_NOT_FOUND", "图片不存在");
|
|
910
|
+
if (state.library[id] === void 0) throw codedError("ASSET_NOT_FOUND", "图片不存在");
|
|
834
911
|
const draft = structuredClone(state);
|
|
835
912
|
const affectedSkins = [];
|
|
836
|
-
|
|
837
|
-
for (const field of listAssetFields(skinId)) {
|
|
838
|
-
if (section?.[field.key] === id) {
|
|
839
|
-
delete section[field.key];
|
|
840
|
-
affectedSkins.push({ skinId, key: field.key });
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
}
|
|
844
|
-
delete draft.library[id];
|
|
913
|
+
const meta = applyGalleryDelete(draft, id, affectedSkins);
|
|
845
914
|
draft.revision += 1;
|
|
846
915
|
commitState(draft);
|
|
847
916
|
try {
|
|
848
|
-
fs.unlinkSync(blobFileFor(id, meta.extension));
|
|
917
|
+
fs.unlinkSync(blobFileFor(meta.id, meta.extension));
|
|
849
918
|
} catch {
|
|
850
919
|
}
|
|
851
920
|
gcNow();
|