dsh-plugin-update 0.5.8 → 0.7.0
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/CHANGELOG.md +16 -4
- package/README.md +46 -9
- package/dist/bilingual.d.ts +4 -4
- package/dist/bilingual.js +300 -1
- package/dist/changelog.d.ts +19 -13
- package/dist/changelog.js +60 -22
- package/dist/diag.d.ts +3 -0
- package/dist/diag.js +32 -20
- package/dist/entry-batch.d.ts +10 -2
- package/dist/entry-batch.js +21 -7
- package/dist/entry.d.ts +31 -2
- package/dist/entry.js +83 -27
- package/dist/host-batch.js +104 -13
- package/dist/http.d.ts +2 -2
- package/dist/http.js +1 -1
- package/dist/panel-batch.d.ts +53 -11
- package/dist/panel-batch.js +500 -153
- package/dist/panel.d.ts +84 -25
- package/dist/panel.js +618 -364
- package/dist/store.d.ts +42 -0
- package/dist/store.js +139 -0
- package/package.json +1 -1
package/dist/store.d.ts
CHANGED
|
@@ -131,6 +131,48 @@ export declare function createBatchDiskPorts(homeDir: string, profileDir: string
|
|
|
131
131
|
writeBatch: (session: BatchSession) => Promise<void>;
|
|
132
132
|
clearBatch: () => Promise<void>;
|
|
133
133
|
};
|
|
134
|
+
/** 批量属主隔离与知识/偏好落盘(#59 D1:知识、一轮、属主各回各家)。 */
|
|
135
|
+
export declare const INVENTORY_FILE = "inventory.json";
|
|
136
|
+
export declare const PREFS_FILE = "prefs.json";
|
|
137
|
+
export declare const INVENTORY_FILE_MAX_BYTES: number;
|
|
138
|
+
export declare const PREFS_FILE_MAX_BYTES: number;
|
|
139
|
+
export declare function sanitizeBatchOwner(owner: unknown): string | null;
|
|
140
|
+
export interface BatchOwnerPaths {
|
|
141
|
+
directory: string;
|
|
142
|
+
batchFile: string;
|
|
143
|
+
inventoryFile: string;
|
|
144
|
+
prefsFile: string;
|
|
145
|
+
}
|
|
146
|
+
export declare function batchPathsForOwner(homeDir: string, profileDir: string, owner: unknown): BatchOwnerPaths | null;
|
|
147
|
+
export interface BatchInventoryEntry {
|
|
148
|
+
lastCheckedAt: number;
|
|
149
|
+
installedVersion: string | null;
|
|
150
|
+
latestVersion: string | null;
|
|
151
|
+
canInstall: boolean | null;
|
|
152
|
+
error: string | null;
|
|
153
|
+
}
|
|
154
|
+
export interface BatchInventory {
|
|
155
|
+
version: 1;
|
|
156
|
+
updatedAt: number;
|
|
157
|
+
entries: Record<string, BatchInventoryEntry>;
|
|
158
|
+
}
|
|
159
|
+
export declare function emptyBatchInventory(nowMs?: number): BatchInventory;
|
|
160
|
+
export declare function normalizeBatchInventory(raw: unknown, nowMs?: number): BatchInventory;
|
|
161
|
+
export interface BatchPrefs {
|
|
162
|
+
checkOnOpen?: boolean;
|
|
163
|
+
}
|
|
164
|
+
export declare function normalizeBatchPrefs(raw: unknown): BatchPrefs;
|
|
165
|
+
export interface BatchOwnerPorts {
|
|
166
|
+
paths: BatchOwnerPaths | null;
|
|
167
|
+
readBatch: () => Promise<BatchSession>;
|
|
168
|
+
writeBatch: (session: BatchSession) => Promise<void>;
|
|
169
|
+
clearBatch: () => Promise<void>;
|
|
170
|
+
readInventory: () => Promise<BatchInventory>;
|
|
171
|
+
writeInventory: (inventory: BatchInventory) => Promise<void>;
|
|
172
|
+
readPrefs: () => Promise<BatchPrefs>;
|
|
173
|
+
writePrefs: (prefs: BatchPrefs) => Promise<void>;
|
|
174
|
+
}
|
|
175
|
+
export declare function createBatchOwnerPorts(homeDir: string, profileDir: string, owner: unknown, deps?: BatchPortsDeps): BatchOwnerPorts;
|
|
134
176
|
/** 按插件 + 版本持久化的跳过记录(#16,与三冻结落盘名并存的第四文件,不碰旧树)。
|
|
135
177
|
*
|
|
136
178
|
* 键:跳过只按「插件标识 + 版本」记——目录已按插件标识隔离(pathsForUpdate),
|
package/dist/store.js
CHANGED
|
@@ -328,6 +328,135 @@ function createBatchDiskPorts(homeDir, profileDir, deps = {}) {
|
|
|
328
328
|
}
|
|
329
329
|
return { paths, readBatch, writeBatch, clearBatch };
|
|
330
330
|
}
|
|
331
|
+
const INVENTORY_FILE = "inventory.json";
|
|
332
|
+
const PREFS_FILE = "prefs.json";
|
|
333
|
+
const INVENTORY_FILE_MAX_BYTES = 1024 * 1024;
|
|
334
|
+
const PREFS_FILE_MAX_BYTES = 64 * 1024;
|
|
335
|
+
function sanitizeBatchOwner(owner) {
|
|
336
|
+
if (typeof owner !== "string" || !owner) return null;
|
|
337
|
+
if (owner.length < 1 || owner.length > 64) return null;
|
|
338
|
+
if (!/^[A-Za-z0-9_-]+$/.test(owner)) return null;
|
|
339
|
+
return owner;
|
|
340
|
+
}
|
|
341
|
+
function batchPathsForOwner(homeDir, profileDir, owner) {
|
|
342
|
+
const clean = sanitizeBatchOwner(owner);
|
|
343
|
+
if (!clean) return null;
|
|
344
|
+
const queue = queuePathsForUpdate(homeDir, profileDir);
|
|
345
|
+
if (!queue) return null;
|
|
346
|
+
const directory = join(queue.directory, clean);
|
|
347
|
+
return { directory, batchFile: join(directory, BATCH_FILE), inventoryFile: join(directory, INVENTORY_FILE), prefsFile: join(directory, PREFS_FILE) };
|
|
348
|
+
}
|
|
349
|
+
function emptyBatchInventory(nowMs) {
|
|
350
|
+
const at = typeof nowMs === "number" && Number.isFinite(nowMs) && nowMs >= 0 ? nowMs : 0;
|
|
351
|
+
return { version: 1, updatedAt: at, entries: {} };
|
|
352
|
+
}
|
|
353
|
+
function asMillisOrZero(value) {
|
|
354
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : 0;
|
|
355
|
+
}
|
|
356
|
+
function asNullableText(value) {
|
|
357
|
+
return typeof value === "string" && value ? value : null;
|
|
358
|
+
}
|
|
359
|
+
function normalizeBatchInventory(raw, nowMs) {
|
|
360
|
+
const fallback = emptyBatchInventory(nowMs);
|
|
361
|
+
try {
|
|
362
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return fallback;
|
|
363
|
+
const input = raw;
|
|
364
|
+
if (input["version"] !== 1) return fallback;
|
|
365
|
+
const entries = {};
|
|
366
|
+
const rawEntries = input["entries"];
|
|
367
|
+
if (rawEntries && typeof rawEntries === "object" && !Array.isArray(rawEntries)) {
|
|
368
|
+
for (const [key, item] of Object.entries(rawEntries)) {
|
|
369
|
+
if (!key || typeof item !== "object" || !item || Array.isArray(item)) continue;
|
|
370
|
+
const e = item;
|
|
371
|
+
const canInstallRaw = e["canInstall"];
|
|
372
|
+
entries[key] = {
|
|
373
|
+
lastCheckedAt: asMillisOrZero(e["lastCheckedAt"]),
|
|
374
|
+
installedVersion: asNullableText(e["installedVersion"]),
|
|
375
|
+
latestVersion: asNullableText(e["latestVersion"]),
|
|
376
|
+
canInstall: typeof canInstallRaw === "boolean" ? canInstallRaw : null,
|
|
377
|
+
error: asNullableText(e["error"])
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
return { version: 1, updatedAt: asMillisOrZero(input["updatedAt"]), entries };
|
|
382
|
+
} catch {
|
|
383
|
+
return fallback;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
function normalizeBatchPrefs(raw) {
|
|
387
|
+
try {
|
|
388
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return {};
|
|
389
|
+
const v = raw["checkOnOpen"];
|
|
390
|
+
if (typeof v === "boolean") return { checkOnOpen: v };
|
|
391
|
+
return {};
|
|
392
|
+
} catch {
|
|
393
|
+
return {};
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
async function readJsonFile(filename, maxBytes, readText, statFile) {
|
|
397
|
+
try {
|
|
398
|
+
if ((await statFile(filename)).size > maxBytes) return null;
|
|
399
|
+
return JSON.parse(await readText(filename, "utf8"));
|
|
400
|
+
} catch (error) {
|
|
401
|
+
if (error && error.code === "ENOENT") return null;
|
|
402
|
+
return null;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
async function writeJsonFileAtomic(filename, value, directory) {
|
|
406
|
+
await mkdir(directory, { recursive: true, mode: 448 });
|
|
407
|
+
const temporary = filename + "." + randomUUID() + ".tmp";
|
|
408
|
+
try {
|
|
409
|
+
await writeFile(temporary, JSON.stringify(value, null, 2) + "\n", { mode: 384, flag: "wx" });
|
|
410
|
+
await rename(temporary, filename);
|
|
411
|
+
} catch {
|
|
412
|
+
throw batchFail();
|
|
413
|
+
} finally {
|
|
414
|
+
await unlink(temporary).catch(() => {
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
function createBatchOwnerPorts(homeDir, profileDir, owner, deps = {}) {
|
|
419
|
+
const paths = batchPathsForOwner(homeDir, profileDir, owner);
|
|
420
|
+
const readText = deps.readFileImpl ?? ((filename, encoding) => readFile(filename, encoding));
|
|
421
|
+
const statFile = deps.statImpl ?? ((filename) => stat(filename));
|
|
422
|
+
async function readBatch() {
|
|
423
|
+
if (!paths) return emptyBatchSession();
|
|
424
|
+
const raw = await readJsonFile(paths.batchFile, BATCH_FILE_MAX_BYTES, readText, statFile);
|
|
425
|
+
if (raw === null) return emptyBatchSession();
|
|
426
|
+
return normalizeBatchSession(raw);
|
|
427
|
+
}
|
|
428
|
+
async function writeBatch(session) {
|
|
429
|
+
if (!paths) throw batchFail();
|
|
430
|
+
await writeJsonFileAtomic(paths.batchFile, normalizeBatchSession(session), paths.directory);
|
|
431
|
+
}
|
|
432
|
+
async function clearBatch() {
|
|
433
|
+
if (!paths) return;
|
|
434
|
+
await writeBatch(emptyBatchSession());
|
|
435
|
+
}
|
|
436
|
+
async function readInventory() {
|
|
437
|
+
if (!paths) return emptyBatchInventory();
|
|
438
|
+
const raw = await readJsonFile(paths.inventoryFile, INVENTORY_FILE_MAX_BYTES, readText, statFile);
|
|
439
|
+
if (raw === null) return emptyBatchInventory();
|
|
440
|
+
return normalizeBatchInventory(raw);
|
|
441
|
+
}
|
|
442
|
+
async function writeInventory(inventory) {
|
|
443
|
+
if (!paths) throw batchFail();
|
|
444
|
+
const normalized = normalizeBatchInventory(inventory, inventory.updatedAt);
|
|
445
|
+
normalized.updatedAt = asMillisOrZero(inventory.updatedAt);
|
|
446
|
+
await writeJsonFileAtomic(paths.inventoryFile, normalized, paths.directory);
|
|
447
|
+
}
|
|
448
|
+
async function readPrefs() {
|
|
449
|
+
if (!paths) return {};
|
|
450
|
+
const raw = await readJsonFile(paths.prefsFile, PREFS_FILE_MAX_BYTES, readText, statFile);
|
|
451
|
+
if (raw === null) return {};
|
|
452
|
+
return normalizeBatchPrefs(raw);
|
|
453
|
+
}
|
|
454
|
+
async function writePrefs(prefs) {
|
|
455
|
+
if (!paths) throw batchFail();
|
|
456
|
+
await writeJsonFileAtomic(paths.prefsFile, normalizeBatchPrefs(prefs), paths.directory);
|
|
457
|
+
}
|
|
458
|
+
return { paths, readBatch, writeBatch, clearBatch, readInventory, writeInventory, readPrefs, writePrefs };
|
|
459
|
+
}
|
|
331
460
|
const MAX_SKIPPED_ENTRIES = 50;
|
|
332
461
|
function skipKey(pluginId, version) {
|
|
333
462
|
return `${String(pluginId)}@${String(version)}`;
|
|
@@ -693,25 +822,35 @@ const __executorInternals = { INSTALL_STDIO, TERMINATION_GRACE_MS, CLI_PACKAGE_N
|
|
|
693
822
|
export {
|
|
694
823
|
BATCH_FILE,
|
|
695
824
|
BATCH_FILE_MAX_BYTES,
|
|
825
|
+
INVENTORY_FILE,
|
|
826
|
+
INVENTORY_FILE_MAX_BYTES,
|
|
696
827
|
MAX_SKIPPED_ENTRIES,
|
|
828
|
+
PREFS_FILE,
|
|
829
|
+
PREFS_FILE_MAX_BYTES,
|
|
697
830
|
QUEUE_DIR_SEGMENT,
|
|
698
831
|
QUEUE_FILE,
|
|
699
832
|
QUEUE_LOCK_FILE,
|
|
700
833
|
QUEUE_LOCK_STALE_MS,
|
|
701
834
|
__executorInternals,
|
|
702
835
|
addSkipped,
|
|
836
|
+
batchPathsForOwner,
|
|
703
837
|
batchPathsForUpdate,
|
|
704
838
|
clearSkipped,
|
|
705
839
|
createBatchDiskPorts,
|
|
840
|
+
createBatchOwnerPorts,
|
|
706
841
|
createSkipDiskPorts,
|
|
707
842
|
createUpdateDiskPorts,
|
|
708
843
|
createUpdateExecutor,
|
|
709
844
|
createUpdateQueuePorts,
|
|
845
|
+
emptyBatchInventory,
|
|
710
846
|
isVersionSkipped,
|
|
711
847
|
legacyPathsForUpdate,
|
|
848
|
+
normalizeBatchInventory,
|
|
849
|
+
normalizeBatchPrefs,
|
|
712
850
|
normalizeSkipped,
|
|
713
851
|
pathsForUpdate,
|
|
714
852
|
queuePathsForUpdate,
|
|
715
853
|
resolveCliEntry,
|
|
854
|
+
sanitizeBatchOwner,
|
|
716
855
|
skipKey
|
|
717
856
|
};
|