@lmzhen/dsh-evolution-state-json 0.3.71 → 0.3.73
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 +32 -6
- package/lib/types/index.d.ts +3 -1
- package/package.json +7 -7
package/lib/index.js
CHANGED
|
@@ -175,7 +175,7 @@ async function ensureCorruptCopy(ctx, io, root, file, bad) {
|
|
|
175
175
|
}
|
|
176
176
|
return wrote;
|
|
177
177
|
}
|
|
178
|
-
async function jsonTransact(ctx, io, root, file, task) {
|
|
178
|
+
async function jsonTransact(ctx, io, root, file, task, options) {
|
|
179
179
|
await transactIo(io(), join(root, file), async (current) => {
|
|
180
180
|
let parsed = null;
|
|
181
181
|
if (current !== null) {
|
|
@@ -194,9 +194,14 @@ async function jsonTransact(ctx, io, root, file, task) {
|
|
|
194
194
|
for (const [id, record] of Object.entries(parsed)) if (failing.some(([failedId]) => failedId === id)) bad[id] = record;
|
|
195
195
|
else good[id] = record;
|
|
196
196
|
reportGateViolation(ctx, file, failing);
|
|
197
|
-
if (await ensureCorruptCopy(ctx, io, root, file, bad))
|
|
198
|
-
|
|
199
|
-
|
|
197
|
+
if (await ensureCorruptCopy(ctx, io, root, file, bad)) {
|
|
198
|
+
parsed = good;
|
|
199
|
+
options?.onGateDrop?.(file, failing.map(([id]) => id));
|
|
200
|
+
} else {
|
|
201
|
+
options?.onGateDrop?.(file, []);
|
|
202
|
+
ctx.logger.warn(`evolution-state-json: keeping ${failing.length} malformed record(s) in ${file} — the quarantine copy could not be written, so rewriting the file without them would destroy the only copy`);
|
|
203
|
+
}
|
|
204
|
+
} else options?.onGateDrop?.(file, []);
|
|
200
205
|
}
|
|
201
206
|
const next = await task(parsed);
|
|
202
207
|
if (next !== null && RECORD_MAP_FILES.has(file) && !isPlainRecord(next)) throw writeGateError(`evolution state file "${file}" task returned ${Array.isArray(next) ? "an array" : typeof next} (expected null or a plain JSON object map of records); not written.`);
|
|
@@ -212,6 +217,13 @@ function apply(ctx, rawConfig = {}) {
|
|
|
212
217
|
if (rawConfig.root !== void 0 && rawConfig.root.trim() !== "" && !isAbsolute(rawConfig.root)) ctx.logger.warn(`evolution-state-json: config.root "${rawConfig.root}" is a relative path and resolves against the process CWD ("${root}") — pass an absolute path to make the store location launch-independent`);
|
|
213
218
|
const io = () => ctx.evolutionIo.provider();
|
|
214
219
|
const pathOf = (file) => join(root, file);
|
|
220
|
+
const gateDroppedCurrent = /* @__PURE__ */ new Set();
|
|
221
|
+
const noteGateDrop = (file, ids) => {
|
|
222
|
+
if (file !== PENDING_STATE_FILE) return;
|
|
223
|
+
gateDroppedCurrent.clear();
|
|
224
|
+
for (const id of ids) gateDroppedCurrent.add(id);
|
|
225
|
+
};
|
|
226
|
+
let warnedPendingCapacity = false;
|
|
215
227
|
async function readJson(file) {
|
|
216
228
|
const raw = await io().readText(pathOf(file));
|
|
217
229
|
if (raw === null) return null;
|
|
@@ -232,8 +244,13 @@ function apply(ctx, rawConfig = {}) {
|
|
|
232
244
|
else good[id] = record;
|
|
233
245
|
reportGateViolation(ctx, file, failing);
|
|
234
246
|
await ensureCorruptCopy(ctx, io, root, file, bad);
|
|
247
|
+
if (file === PENDING_STATE_FILE) {
|
|
248
|
+
gateDroppedCurrent.clear();
|
|
249
|
+
for (const id of Object.keys(bad)) gateDroppedCurrent.add(id);
|
|
250
|
+
}
|
|
235
251
|
return good;
|
|
236
252
|
}
|
|
253
|
+
if (file === PENDING_STATE_FILE) gateDroppedCurrent.clear();
|
|
237
254
|
return parsed;
|
|
238
255
|
}
|
|
239
256
|
const mutate = makeSerialQueue();
|
|
@@ -265,6 +282,7 @@ function apply(ctx, rawConfig = {}) {
|
|
|
265
282
|
for (const [id, record] of Object.entries(legacy)) {
|
|
266
283
|
if (id in (current ?? {})) continue;
|
|
267
284
|
if (archivedIds.has(id)) continue;
|
|
285
|
+
if (gateDroppedCurrent.has(id)) continue;
|
|
268
286
|
retired[id] = record;
|
|
269
287
|
}
|
|
270
288
|
return retired;
|
|
@@ -279,11 +297,12 @@ function apply(ctx, rawConfig = {}) {
|
|
|
279
297
|
for (const [id, record] of Object.entries(legacy)) {
|
|
280
298
|
if (id in merged) continue;
|
|
281
299
|
if (archivedIds.has(id)) continue;
|
|
300
|
+
if (gateDroppedCurrent.has(id)) continue;
|
|
282
301
|
merged[id] = record;
|
|
283
302
|
retired[id] = record;
|
|
284
303
|
}
|
|
285
304
|
return merged;
|
|
286
|
-
});
|
|
305
|
+
}, { onGateDrop: noteGateDrop });
|
|
287
306
|
await io().rename(pathOf(PENDING_LEGACY_FILE), `${pathOf(PENDING_LEGACY_FILE)}.migrated`);
|
|
288
307
|
legacyMigrated = true;
|
|
289
308
|
return retired;
|
|
@@ -464,10 +483,17 @@ function apply(ctx, rawConfig = {}) {
|
|
|
464
483
|
async savePending(record) {
|
|
465
484
|
await mutate(async () => {
|
|
466
485
|
await jsonTransact(ctx, io, root, PENDING_STATE_FILE, async (current) => {
|
|
467
|
-
|
|
486
|
+
const map = {
|
|
468
487
|
...await mergedWithFilteredLegacy(legacyMigrated ? null : await readJson(PENDING_LEGACY_FILE), current ?? {}),
|
|
469
488
|
[record.id]: record
|
|
470
489
|
};
|
|
490
|
+
const liveCount = Object.values(map).filter((entry) => entry.status === "pending" || entry.status === "executing").length;
|
|
491
|
+
if (liveCount > PENDING_RESOLVED_CAP$1 && !warnedPendingCapacity) {
|
|
492
|
+
warnedPendingCapacity = true;
|
|
493
|
+
ctx.logger.warn(`evolution-state-json: ${liveCount} pending/executing staged records exceed the resolved cap (${PENDING_RESOLVED_CAP$1}) — they are never trimmed by design; resolve or reject them (/evolution pending) or the file keeps growing`);
|
|
494
|
+
}
|
|
495
|
+
if (liveCount <= PENDING_RESOLVED_CAP$1) warnedPendingCapacity = false;
|
|
496
|
+
return map;
|
|
471
497
|
});
|
|
472
498
|
});
|
|
473
499
|
},
|
package/lib/types/index.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ export interface Config {
|
|
|
14
14
|
root?: string;
|
|
15
15
|
}
|
|
16
16
|
export declare const Config: z<Config>;
|
|
17
|
-
export declare function jsonTransact<T>(ctx: Context, io: () => EvolutionIoLike, root: string, file: string, task: (current: T | null) => T | null | Promise<T | null
|
|
17
|
+
export declare function jsonTransact<T>(ctx: Context, io: () => EvolutionIoLike, root: string, file: string, task: (current: T | null) => T | null | Promise<T | null>, options?: {
|
|
18
|
+
onGateDrop?: (file: string, ids: string[]) => void;
|
|
19
|
+
}): Promise<void>;
|
|
18
20
|
export declare function apply(ctx: Context, rawConfig?: Config): void;
|
|
19
21
|
//# sourceMappingURL=index.d.ts.map
|
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.3.
|
|
4
|
+
"version": "0.3.73",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -31,18 +31,18 @@
|
|
|
31
31
|
"license": "MIT",
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@deepseek-ai/schemastery": "^3.18.1",
|
|
34
|
-
"@lmzhen/dsh-evolution-core": "^0.3.
|
|
34
|
+
"@lmzhen/dsh-evolution-core": "^0.3.73"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
37
|
"@deepseek-ai/dsh-invariants": "^0.1.5-rc.2",
|
|
38
38
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
39
|
-
"@lmzhen/dsh-evolution-io": "^0.3.
|
|
40
|
-
"@lmzhen/dsh-evolution-state-storage": "^0.3.
|
|
39
|
+
"@lmzhen/dsh-evolution-io": "^0.3.73",
|
|
40
|
+
"@lmzhen/dsh-evolution-state-storage": "^0.3.73"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@deepseek-ai/dsh-invariants": "^0.1.5-rc.2",
|
|
44
|
-
"@lmzhen/dsh-evolution-io": "^0.3.
|
|
45
|
-
"@lmzhen/dsh-evolution-state-storage": "^0.3.
|
|
46
|
-
"@lmzhen/dsh-evolution-io-node": "^0.3.
|
|
44
|
+
"@lmzhen/dsh-evolution-io": "^0.3.73",
|
|
45
|
+
"@lmzhen/dsh-evolution-state-storage": "^0.3.73",
|
|
46
|
+
"@lmzhen/dsh-evolution-io-node": "^0.3.73"
|
|
47
47
|
}
|
|
48
48
|
}
|