@lmzhen/dsh-evolution-state-json 0.32.3 → 0.32.4
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/README.md +2 -2
- package/lib/index.js +3 -52
- package/lib/types/index.d.ts +4 -4
- package/package.json +7 -8
- package/lib/types/state-store.d.ts +0 -23
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ change.
|
|
|
17
17
|
## Known limitations
|
|
18
18
|
|
|
19
19
|
- Both tables are capped (200 pending, 500 sessions), and past the pending cap the DOMAIN provider deletes instead of archiving: this provider keeps an audit sidecar. The `updatedAt` stamp is stripped on read, so the consumer-facing record shape is unchanged.
|
|
20
|
-
-
|
|
20
|
+
- All shipped JSON media are **single-process**: the IO backend has no cross-process lock. A second process sharing `DSH_HOME` is not coordinated; route multi-process deployments to a platform-provided cross-process backend before relying on this provider.
|
|
21
21
|
|
|
22
22
|
**Runtime invariant:** No companion is published. The platform auto-assembles nothing and the family mounts no `<pkg>/invariant` cordis row, so a companion here would never execute (v37 S2.1 / I-3).
|
|
23
23
|
|
|
@@ -25,4 +25,4 @@ change.
|
|
|
25
25
|
|
|
26
26
|
- P2-4 (v15): the live pending map is BOUNDED — resolved (approved/rejected) records are kept to the most recent `PENDING_RESOLVED_CAP` (200, seam constant in `evolution-state-storage`); the oldest by `resolvedAt` rotate into the `pending-state-archive.json` sidecar (with `.bak` rotation), which is JSON-provider-specific.
|
|
27
27
|
- V25-11 (v25): the review-state table is likewise BOUNDED: `REVIEW_STATE_SESSION_CAP` (500, seam constant) rows keyed by session; on every save the least-recently-active sessions (provider-stamped `updatedAt`, stored on disk only) are pruned.
|
|
28
|
-
- JSON provider serializes writers inside one process
|
|
28
|
+
- The JSON provider serializes writers inside one process only (the internal transact wrapper is not public API, audit v10 S-03). It does **not** claim cross-process safety; the shipped IO backend declares single-process.
|
package/lib/index.js
CHANGED
|
@@ -1,55 +1,7 @@
|
|
|
1
1
|
import z from "@deepseek-ai/schemastery";
|
|
2
|
-
import { evolutionHome, isAbsent, isPresent, isRecord, isUnknown, makeSerialQueue, probeText, transactIo, transactTaskGuard } from "@lmzhen/dsh-evolution-core";
|
|
2
|
+
import { WriteKernel, evolutionHome, isAbsent, isPresent, isRecord, isUnknown, makeSerialQueue, probeText, transactIo, transactTaskGuard } from "@lmzhen/dsh-evolution-core";
|
|
3
3
|
import { CURATOR_STATE_FILE, CURATOR_STATE_KEY, CURATOR_STATE_TABLE, PENDING_ARCHIVE_BAK_FILE, PENDING_ARCHIVE_FILE, PENDING_LEGACY_FILE, PENDING_RESOLVED_CAP, PENDING_STATE_FILE, PENDING_TABLE, PROVIDER_JSON, REVIEW_STATE_FILE, REVIEW_STATE_SESSION_CAP, REVIEW_STATE_TABLE, assertCloneable, canClaimPending, canResolvePending, cloneRecord, recordIssue, releasedStatus, selectPendingOverflow, selectSessionOverflow } from "@lmzhen/dsh-evolution-state-storage";
|
|
4
4
|
import { basename, dirname, isAbsolute, join } from "node:path";
|
|
5
|
-
//#region lib/types/state-store.js
|
|
6
|
-
const safeName = (collection) => String(collection).replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
7
|
-
var JsonStateStore = class {
|
|
8
|
-
name = "json-state-store";
|
|
9
|
-
capabilities = { crossProcess: false };
|
|
10
|
-
io;
|
|
11
|
-
root;
|
|
12
|
-
serial = makeSerialQueue();
|
|
13
|
-
constructor(io, root) {
|
|
14
|
-
this.io = io;
|
|
15
|
-
this.root = root;
|
|
16
|
-
}
|
|
17
|
-
path(collection) {
|
|
18
|
-
return join(this.root, "state-store", safeName(collection) + ".json");
|
|
19
|
-
}
|
|
20
|
-
async load(collection) {
|
|
21
|
-
const text = await this.io.readText(this.path(collection));
|
|
22
|
-
if (text === null) return {
|
|
23
|
-
schemaVersion: 1,
|
|
24
|
-
records: {}
|
|
25
|
-
};
|
|
26
|
-
const parsed = JSON.parse(text);
|
|
27
|
-
if (parsed.schemaVersion !== 1 || parsed.records === null || typeof parsed.records !== "object" || Array.isArray(parsed.records)) throw new Error("state-store collection is corrupt: " + String(collection));
|
|
28
|
-
return parsed;
|
|
29
|
-
}
|
|
30
|
-
async read(collection, key) {
|
|
31
|
-
return (await this.load(collection)).records[key];
|
|
32
|
-
}
|
|
33
|
-
async transact(collection, key, fn) {
|
|
34
|
-
return this.serial(async () => {
|
|
35
|
-
let next;
|
|
36
|
-
await transactIo(this.io, this.path(collection), async (current) => {
|
|
37
|
-
const file = current === null ? {
|
|
38
|
-
schemaVersion: 1,
|
|
39
|
-
records: {}
|
|
40
|
-
} : JSON.parse(current);
|
|
41
|
-
next = await fn(file.records[key]);
|
|
42
|
-
file.records[key] = next;
|
|
43
|
-
return JSON.stringify(file);
|
|
44
|
-
});
|
|
45
|
-
return next;
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
async list(collection) {
|
|
49
|
-
return Object.values((await this.load(collection)).records);
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
//#endregion
|
|
53
5
|
//#region lib/types/index.js
|
|
54
6
|
/**
|
|
55
7
|
* JSON-file evolution state provider over the IO seam.
|
|
@@ -228,7 +180,7 @@ async function ensureCorruptCopy(ctx, io, root, file, bad, memory) {
|
|
|
228
180
|
return wrote;
|
|
229
181
|
}
|
|
230
182
|
async function jsonTransact(ctx, io, root, file, task, options) {
|
|
231
|
-
await
|
|
183
|
+
await new WriteKernel({ io: io() }).run({ path: join(root, file) }, async (current) => {
|
|
232
184
|
let parsed = null;
|
|
233
185
|
if (current !== null) {
|
|
234
186
|
try {
|
|
@@ -752,7 +704,6 @@ function apply(ctx, rawConfig = {}) {
|
|
|
752
704
|
}
|
|
753
705
|
};
|
|
754
706
|
ctx.effect(() => ctx.evolutionStateStorage.registerProvider(provider), "evolution-state-json.provider");
|
|
755
|
-
ctx.provide("evolutionStateStore", new JsonStateStore(io(), root));
|
|
756
707
|
}
|
|
757
708
|
//#endregion
|
|
758
|
-
export { Config,
|
|
709
|
+
export { Config, apply, inject, jsonTransact, name, newQuarantineMemory };
|
package/lib/types/index.d.ts
CHANGED
|
@@ -15,9 +15,10 @@ export interface Config {
|
|
|
15
15
|
}
|
|
16
16
|
export declare const Config: z<Config>;
|
|
17
17
|
/**
|
|
18
|
-
*
|
|
19
|
-
* mutation runs inside the IO backend's transact lock (via transactIo)
|
|
20
|
-
* second process sharing DSH_HOME
|
|
18
|
+
* In-process JSON-file RMW (v3-audit M-8): every read-modify-write state
|
|
19
|
+
* mutation runs inside the IO backend's transact lock (via transactIo). The
|
|
20
|
+
* shipped backend is single-process; a second process sharing DSH_HOME is not
|
|
21
|
+
* coordinated and must not be assumed safe.
|
|
21
22
|
* `task` returns the next value; null = ENSURE-ABSENT (the file is deleted
|
|
22
23
|
* when it exists — the parity of the transact seam, V6-32). For a record-map
|
|
23
24
|
* file the return must be null or a plain object map of records, and an
|
|
@@ -57,5 +58,4 @@ export declare function jsonTransact<T>(ctx: Context, io: () => EvolutionIoLike,
|
|
|
57
58
|
memory: QuarantineMemory;
|
|
58
59
|
}): Promise<void>;
|
|
59
60
|
export declare function apply(ctx: Context, rawConfig?: Config): void;
|
|
60
|
-
export { JsonStateStore } from './state-store.ts';
|
|
61
61
|
//# 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.32.
|
|
4
|
+
"version": "0.32.4",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -27,18 +27,17 @@
|
|
|
27
27
|
"license": "MIT",
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@deepseek-ai/schemastery": "^3.18.5",
|
|
30
|
-
"@lmzhen/dsh-evolution-
|
|
31
|
-
"@lmzhen/dsh-evolution-core": "^0.32.3"
|
|
30
|
+
"@lmzhen/dsh-evolution-core": "^0.32.4"
|
|
32
31
|
},
|
|
33
32
|
"peerDependencies": {
|
|
34
33
|
"@deepseek-ai/cordis": "^4.0.5",
|
|
35
|
-
"@lmzhen/dsh-evolution-io": "^0.32.
|
|
36
|
-
"@lmzhen/dsh-evolution-state-storage": "^0.32.
|
|
34
|
+
"@lmzhen/dsh-evolution-io": "^0.32.4",
|
|
35
|
+
"@lmzhen/dsh-evolution-state-storage": "^0.32.4"
|
|
37
36
|
},
|
|
38
37
|
"devDependencies": {
|
|
39
|
-
"@lmzhen/dsh-evolution-io": "^0.32.
|
|
40
|
-
"@lmzhen/dsh-evolution-state-storage": "^0.32.
|
|
41
|
-
"@lmzhen/dsh-evolution-io-node": "^0.32.
|
|
38
|
+
"@lmzhen/dsh-evolution-io": "^0.32.4",
|
|
39
|
+
"@lmzhen/dsh-evolution-state-storage": "^0.32.4",
|
|
40
|
+
"@lmzhen/dsh-evolution-io-node": "^0.32.4"
|
|
42
41
|
},
|
|
43
42
|
"engines": {
|
|
44
43
|
"dsh": ">=0.2.1-rc.2 <0.2.2-0"
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generic JSON-file implementation of the contract StateStore.
|
|
3
|
-
*
|
|
4
|
-
* One file per collection; records use a key map. `transact` delegates to the
|
|
5
|
-
* IO seam's transact, so the medium's serialization/CAS behavior is preserved.
|
|
6
|
-
* The node backend is process-local, so capabilities.crossProcess is false.
|
|
7
|
-
*/
|
|
8
|
-
import type { EvolutionCollection, EvolutionStateCapabilities, EvolutionStateStore } from '@lmzhen/dsh-evolution-contract';
|
|
9
|
-
import { type EvolutionIoLike } from '@lmzhen/dsh-evolution-core';
|
|
10
|
-
export declare class JsonStateStore implements EvolutionStateStore {
|
|
11
|
-
readonly name = "json-state-store";
|
|
12
|
-
readonly capabilities: EvolutionStateCapabilities;
|
|
13
|
-
private readonly io;
|
|
14
|
-
private readonly root;
|
|
15
|
-
private readonly serial;
|
|
16
|
-
constructor(io: EvolutionIoLike, root: string);
|
|
17
|
-
private path;
|
|
18
|
-
private load;
|
|
19
|
-
read<T>(collection: EvolutionCollection, key: string): Promise<T | undefined>;
|
|
20
|
-
transact<T>(collection: EvolutionCollection, key: string, fn: (current: T | undefined) => Promise<T>): Promise<T>;
|
|
21
|
-
list<T>(collection: EvolutionCollection): Promise<T[]>;
|
|
22
|
-
}
|
|
23
|
-
//# sourceMappingURL=state-store.d.ts.map
|