@lmzhen/dsh-evolution-state-json 0.4.0 → 0.4.1
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/index.js +35 -5
- package/package.json +7 -7
package/lib/index.js
CHANGED
|
@@ -274,6 +274,36 @@ function apply(ctx, rawConfig = {}) {
|
|
|
274
274
|
if (file === PENDING_STATE_FILE) gateDroppedCurrent.clear();
|
|
275
275
|
return parsed;
|
|
276
276
|
}
|
|
277
|
+
/** A5 (audit P2-9): the LEGACY pending sidecar is read LENIENTLY — the
|
|
278
|
+
* deliberate opposite of the live state file's fail-loud posture. This
|
|
279
|
+
* file's whole purpose is to be retired, but the retirement merge reads
|
|
280
|
+
* it first, so one truncated pre-migration `pending.json` used to make
|
|
281
|
+
* EVERY pending read and mutation throw `EvolutionStateCorruptFile`
|
|
282
|
+
* forever (a permanent operator outage no warning can repair). A
|
|
283
|
+
* QUARANTINE-CLASS failure (unreadable/corrupt content) is renamed aside
|
|
284
|
+
* so every later load sees a clean absence; any OTHER failure (EACCES,
|
|
285
|
+
* EIO — a permission-locked but possibly VALID file) is left in place and
|
|
286
|
+
* only skips this read, so a transient hold never exiles real records.
|
|
287
|
+
* Either way the view continues current-only with one warn per process.
|
|
288
|
+
* Note the moved-aside copy is NOT auto-swept (the stale-copy sweep only
|
|
289
|
+
* inspects entries prefixed like the file being written, and nothing
|
|
290
|
+
* writes a `pending.json` prefix anymore) — it persists until an operator
|
|
291
|
+
* deletes it after rescue, at most one per corruption episode. */
|
|
292
|
+
let legacyQuarantineWarned = false;
|
|
293
|
+
async function readLegacyPending() {
|
|
294
|
+
try {
|
|
295
|
+
return await readJson(PENDING_LEGACY_FILE);
|
|
296
|
+
} catch (error) {
|
|
297
|
+
if (error instanceof Error && error.name === QUARANTINE_ERROR_NAME) try {
|
|
298
|
+
await io().rename(pathOf(PENDING_LEGACY_FILE), `${pathOf(PENDING_LEGACY_FILE)}.corrupt.${Date.now()}`);
|
|
299
|
+
} catch {}
|
|
300
|
+
if (!legacyQuarantineWarned) {
|
|
301
|
+
legacyQuarantineWarned = true;
|
|
302
|
+
ctx.logger.warn(`evolution-state-json: legacy ${PENDING_LEGACY_FILE} is unreadable and was set aside (${error instanceof Error ? error.message : String(error)}); continuing WITHOUT legacy records`);
|
|
303
|
+
}
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
277
307
|
const mutate = makeSerialQueue();
|
|
278
308
|
let legacyMigrated = false;
|
|
279
309
|
async function readArchivedIds() {
|
|
@@ -356,7 +386,7 @@ function apply(ctx, rawConfig = {}) {
|
|
|
356
386
|
}
|
|
357
387
|
}
|
|
358
388
|
async function loadPendingMap() {
|
|
359
|
-
const [current, legacy] = await Promise.all([readJson(PENDING_STATE_FILE),
|
|
389
|
+
const [current, legacy] = await Promise.all([readJson(PENDING_STATE_FILE), readLegacyPending()]);
|
|
360
390
|
const keyed = keyPendingById(current);
|
|
361
391
|
if (legacy !== null) return {
|
|
362
392
|
...await retireLegacyOnce(legacy),
|
|
@@ -564,7 +594,7 @@ function apply(ctx, rawConfig = {}) {
|
|
|
564
594
|
const guard = transactTaskGuard(`pending record "${record.id}" (${PENDING_STATE_FILE})`);
|
|
565
595
|
await pendingTransact(guard.wrap(async (current) => {
|
|
566
596
|
const map = {
|
|
567
|
-
...await mergedWithFilteredLegacy(legacyMigrated ? null : await
|
|
597
|
+
...await mergedWithFilteredLegacy(legacyMigrated ? null : await readLegacyPending(), current ?? {}),
|
|
568
598
|
[record.id]: record
|
|
569
599
|
};
|
|
570
600
|
warnPendingGrowth(map, "save");
|
|
@@ -577,7 +607,7 @@ function apply(ctx, rawConfig = {}) {
|
|
|
577
607
|
return await mutate(async () => {
|
|
578
608
|
const slot = { claimed: null };
|
|
579
609
|
await pendingTransact(async (current) => {
|
|
580
|
-
const map = { ...await mergedWithFilteredLegacy(legacyMigrated ? null : await
|
|
610
|
+
const map = { ...await mergedWithFilteredLegacy(legacyMigrated ? null : await readLegacyPending(), current ?? {}) };
|
|
581
611
|
const record = map[id] ?? null;
|
|
582
612
|
if (record === null || !canClaimPending(record.status)) return map;
|
|
583
613
|
const now = Date.now();
|
|
@@ -597,7 +627,7 @@ function apply(ctx, rawConfig = {}) {
|
|
|
597
627
|
async releasePendingClaim(id, claimId) {
|
|
598
628
|
await mutate(async () => {
|
|
599
629
|
await pendingTransact(async (current) => {
|
|
600
|
-
const map = { ...await mergedWithFilteredLegacy(legacyMigrated ? null : await
|
|
630
|
+
const map = { ...await mergedWithFilteredLegacy(legacyMigrated ? null : await readLegacyPending(), current ?? {}) };
|
|
601
631
|
const record = map[id];
|
|
602
632
|
if (!record || record.claimedBy !== claimId) return map;
|
|
603
633
|
if (record.status !== "pending" && record.status !== "executing") return map;
|
|
@@ -615,7 +645,7 @@ function apply(ctx, rawConfig = {}) {
|
|
|
615
645
|
applied: false
|
|
616
646
|
};
|
|
617
647
|
await pendingTransact(async (current) => {
|
|
618
|
-
const map = { ...await mergedWithFilteredLegacy(legacyMigrated ? null : await
|
|
648
|
+
const map = { ...await mergedWithFilteredLegacy(legacyMigrated ? null : await readLegacyPending(), current ?? {}) };
|
|
619
649
|
const record = map[id] ?? null;
|
|
620
650
|
if (record === null || !canResolvePending(record.status)) {
|
|
621
651
|
result = {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lmzhen/dsh-evolution-state-json",
|
|
3
3
|
"description": "JSON-file evolution state provider over the IO seam (community build)",
|
|
4
|
-
"version": "0.4.
|
|
4
|
+
"version": "0.4.1",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -27,16 +27,16 @@
|
|
|
27
27
|
"license": "MIT",
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@deepseek-ai/schemastery": "^3.18.1",
|
|
30
|
-
"@lmzhen/dsh-evolution-core": "^0.4.
|
|
30
|
+
"@lmzhen/dsh-evolution-core": "^0.4.1"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
33
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
34
|
-
"@lmzhen/dsh-evolution-io": "^0.4.
|
|
35
|
-
"@lmzhen/dsh-evolution-state-storage": "^0.4.
|
|
34
|
+
"@lmzhen/dsh-evolution-io": "^0.4.1",
|
|
35
|
+
"@lmzhen/dsh-evolution-state-storage": "^0.4.1"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@lmzhen/dsh-evolution-io": "^0.4.
|
|
39
|
-
"@lmzhen/dsh-evolution-state-storage": "^0.4.
|
|
40
|
-
"@lmzhen/dsh-evolution-io-node": "^0.4.
|
|
38
|
+
"@lmzhen/dsh-evolution-io": "^0.4.1",
|
|
39
|
+
"@lmzhen/dsh-evolution-state-storage": "^0.4.1",
|
|
40
|
+
"@lmzhen/dsh-evolution-io-node": "^0.4.1"
|
|
41
41
|
}
|
|
42
42
|
}
|