@lmzhen/dsh-evolution-state-json 0.3.36 → 0.3.37
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 +20 -10
- package/lib/types/index.d.ts +6 -5
- package/package.json +6 -6
package/lib/index.js
CHANGED
|
@@ -55,11 +55,12 @@ async function quarantine(io, root, file, raw, reason) {
|
|
|
55
55
|
* Cross-process JSON-file RMW (v3-audit M-8): every read-modify-write state
|
|
56
56
|
* mutation runs inside the IO backend's transact lock (via transactIo) so a
|
|
57
57
|
* second process sharing DSH_HOME cannot interleave its claim/resolve.
|
|
58
|
-
* `task` returns the next value
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* `
|
|
58
|
+
* `task` returns the next value; null = ENSURE-ABSENT (the file is deleted
|
|
59
|
+
* when it exists — the parity of the transact seam, V6-32). For a record-map
|
|
60
|
+
* file the return must be null or a plain object map of records, and an
|
|
61
|
+
* array/scalar would be persisted as a corrupt map — so it fails loud before
|
|
62
|
+
* any write (0.3.28, V4-08). The legacy `pending.json` merge stays inside the
|
|
63
|
+
* task via `readJson` where relevant.
|
|
63
64
|
*/
|
|
64
65
|
async function jsonTransact(io, root, file, task) {
|
|
65
66
|
await transactIo(io(), join(root, file), async (current) => {
|
|
@@ -104,6 +105,12 @@ function apply(ctx, rawConfig) {
|
|
|
104
105
|
if (Array.isArray(rawArchive)) {
|
|
105
106
|
for (const entry of rawArchive) if (entry && typeof entry.id === "string") ids.add(entry.id);
|
|
106
107
|
}
|
|
108
|
+
try {
|
|
109
|
+
const rawBak = await readJson("pending-state-archive.json.bak");
|
|
110
|
+
if (Array.isArray(rawBak)) {
|
|
111
|
+
for (const entry of rawBak) if (entry && typeof entry.id === "string") ids.add(entry.id);
|
|
112
|
+
}
|
|
113
|
+
} catch {}
|
|
107
114
|
} catch {}
|
|
108
115
|
archivedIdsCache = ids;
|
|
109
116
|
return ids;
|
|
@@ -170,9 +177,10 @@ function apply(ctx, rawConfig) {
|
|
|
170
177
|
return Number.isNaN(parsed) ? Number.MAX_SAFE_INTEGER : parsed;
|
|
171
178
|
};
|
|
172
179
|
const oldest = resolved.sort((a, b) => entryTime(a) - entryTime(b)).slice(0, overflow);
|
|
173
|
-
const
|
|
180
|
+
const oldestKeys = /* @__PURE__ */ new Set();
|
|
181
|
+
for (const [key, value] of Object.entries(map)) if (oldest.includes(value)) oldestKeys.add(key);
|
|
174
182
|
const kept = {};
|
|
175
|
-
for (const [key, value] of Object.entries(map)) if (!
|
|
183
|
+
for (const [key, value] of Object.entries(map)) if (!oldestKeys.has(key)) kept[key] = value;
|
|
176
184
|
return {
|
|
177
185
|
map: kept,
|
|
178
186
|
evicted: oldest
|
|
@@ -197,19 +205,21 @@ function apply(ctx, rawConfig) {
|
|
|
197
205
|
if (Array.isArray(parsed)) archive = parsed;
|
|
198
206
|
} catch {}
|
|
199
207
|
const archiveKeys = /* @__PURE__ */ new Set();
|
|
200
|
-
|
|
208
|
+
const collapsed = archive.filter((entry) => {
|
|
201
209
|
const key = pendingArchiveKey(entry);
|
|
202
210
|
if (archiveKeys.has(key)) return false;
|
|
203
211
|
archiveKeys.add(key);
|
|
204
212
|
return true;
|
|
205
213
|
});
|
|
214
|
+
const hadDuplicates = collapsed.length !== archive.length;
|
|
215
|
+
archive = collapsed;
|
|
206
216
|
const seen = new Set(archive.map(pendingArchiveKey));
|
|
207
217
|
const fresh = records.filter((record) => !seen.has(pendingArchiveKey(record)));
|
|
208
|
-
if (fresh.length === 0) return current;
|
|
218
|
+
if (fresh.length === 0 && !hadDuplicates) return current;
|
|
209
219
|
const next = [...archive, ...fresh];
|
|
210
220
|
if (next.length > ARCHIVE_RESOLVED_CAP) {
|
|
211
221
|
if (archive.length > 0) await io().writeText(pathOf("pending-state-archive.json.bak"), JSON.stringify(archive, null, 2)).catch(() => {});
|
|
212
|
-
return JSON.stringify(fresh.slice(-5e3), null, 2);
|
|
222
|
+
return JSON.stringify((fresh.length > 0 ? fresh : next).slice(-5e3), null, 2);
|
|
213
223
|
}
|
|
214
224
|
return JSON.stringify(next, null, 2);
|
|
215
225
|
});
|
package/lib/types/index.d.ts
CHANGED
|
@@ -18,11 +18,12 @@ export declare const Config: z<Config>;
|
|
|
18
18
|
* Cross-process JSON-file RMW (v3-audit M-8): every read-modify-write state
|
|
19
19
|
* mutation runs inside the IO backend's transact lock (via transactIo) so a
|
|
20
20
|
* second process sharing DSH_HOME cannot interleave its claim/resolve.
|
|
21
|
-
* `task` returns the next value
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* `
|
|
21
|
+
* `task` returns the next value; null = ENSURE-ABSENT (the file is deleted
|
|
22
|
+
* when it exists — the parity of the transact seam, V6-32). For a record-map
|
|
23
|
+
* file the return must be null or a plain object map of records, and an
|
|
24
|
+
* array/scalar would be persisted as a corrupt map — so it fails loud before
|
|
25
|
+
* any write (0.3.28, V4-08). The legacy `pending.json` merge stays inside the
|
|
26
|
+
* task via `readJson` where relevant.
|
|
26
27
|
*/
|
|
27
28
|
export declare function jsonTransact<T>(io: () => EvolutionIoLike, root: string, file: string, task: (current: T | null) => T | null | Promise<T | null>): Promise<void>;
|
|
28
29
|
export declare function apply(ctx: Context, rawConfig: Config): void;
|
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.37",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -31,17 +31,17 @@
|
|
|
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.37"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
37
|
"@deepseek-ai/dsh-invariants": "^0.1.1-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.37",
|
|
40
|
+
"@lmzhen/dsh-evolution-state-storage": "^0.3.37"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
|
|
44
|
-
"@lmzhen/dsh-evolution-io": "^0.3.
|
|
45
|
-
"@lmzhen/dsh-evolution-state-storage": "^0.3.
|
|
44
|
+
"@lmzhen/dsh-evolution-io": "^0.3.37",
|
|
45
|
+
"@lmzhen/dsh-evolution-state-storage": "^0.3.37"
|
|
46
46
|
}
|
|
47
47
|
}
|