@lmzhen/dsh-evolution-activity 0.3.80 → 0.3.82
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 +37 -4
- package/lib/types/index.d.ts +51 -0
- package/package.json +5 -5
package/lib/index.js
CHANGED
|
@@ -69,24 +69,57 @@ function parseActivityContent(raw) {
|
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
/**
|
|
72
|
+
* H-06: the read barrier over the sidecar, with the corruption verdict.
|
|
73
|
+
*
|
|
74
|
+
* @param root - the evolution state root (the sidecar lives under it).
|
|
75
|
+
* @param io - the IO provider to read through.
|
|
76
|
+
* @returns the parsed records and whether the bytes were readable.
|
|
77
|
+
*/
|
|
78
|
+
async function loadActivityState(root, io) {
|
|
79
|
+
const raw = await io.readText(activityFile(root));
|
|
80
|
+
return {
|
|
81
|
+
records: parseActivityContent(raw),
|
|
82
|
+
corrupt: isCorruptActivity(raw)
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
72
86
|
* H-06: the read barrier over the sidecar. V24-13 (v24): `loadActivity` now
|
|
73
87
|
* HAS a production consumer — `evolution-replay` backfills its `/evolution
|
|
74
88
|
* replay` leaderboard from this store at mount (the two packages'
|
|
75
89
|
* "persistence is the activity store's job" contract is actually wired
|
|
76
90
|
* through this call). The single-writer rule is unchanged: `apply()`'s
|
|
77
|
-
* transact listener remains the only WRITE path.
|
|
91
|
+
* transact listener remains the only WRITE path. Consumers that must not read
|
|
92
|
+
* an unreadable sidecar as "no history" use {@link loadActivityState}.
|
|
78
93
|
*/
|
|
79
94
|
async function loadActivity(root, io) {
|
|
80
|
-
return
|
|
95
|
+
return (await loadActivityState(root, io)).records;
|
|
81
96
|
}
|
|
82
97
|
/** True when bytes exist but are not a readable activity envelope: unparsable
|
|
83
98
|
* JSON, or a missing `items` array (a scalar/array/other-shaped file). A missing
|
|
84
99
|
* file (null) is NOT corruption — it is a first write. */
|
|
100
|
+
/**
|
|
101
|
+
* v43 audit (H-3 / J-6 / FLOW6-6): an UNSUPPORTED version is corrupt for this
|
|
102
|
+
* writer. The read side has always ignored `version` and taken `items` as-is,
|
|
103
|
+
* so a future-version sidecar used to fold fine and then be rewritten as
|
|
104
|
+
* `version: ACTIVITY_FILE_VERSION` on the next append — a silent downgrade that
|
|
105
|
+
* destroyed whatever the newer format carried. This guard is the write side's
|
|
106
|
+
* half of the pair: unknown version ⇒ the original bytes are quarantined (the
|
|
107
|
+
* existing `.corrupt` path) before a current-version file replaces them, which
|
|
108
|
+
* is the posture `evolution-events` already takes for the same shape.
|
|
109
|
+
* Residual, closed by FLOW6-6: `parseActivityContent` still answers `[]` for
|
|
110
|
+
* such a file, but {@link loadActivityState} now carries the verdict beside the
|
|
111
|
+
* records, so a read-only consumer can distinguish "newer format" from "no
|
|
112
|
+
* history" instead of presenting a partial view as the recorded truth.
|
|
113
|
+
* @internal Exported for this package's own tests (siblings `parseActivityContent`
|
|
114
|
+
* and `serializeActivity` are exported for the same reason).
|
|
115
|
+
*/
|
|
85
116
|
function isCorruptActivity(raw) {
|
|
86
117
|
if (raw === null) return false;
|
|
87
118
|
try {
|
|
88
119
|
const parsed = JSON.parse(raw);
|
|
89
|
-
|
|
120
|
+
if (typeof parsed !== "object" || parsed === null || !Array.isArray(parsed.items)) return true;
|
|
121
|
+
const version = parsed.version;
|
|
122
|
+
return version !== void 0 && version !== 2;
|
|
90
123
|
} catch {
|
|
91
124
|
return true;
|
|
92
125
|
}
|
|
@@ -128,4 +161,4 @@ function apply(ctx, rawConfig = {}) {
|
|
|
128
161
|
});
|
|
129
162
|
}
|
|
130
163
|
//#endregion
|
|
131
|
-
export { ACTIVITY_FILE_VERSION, Config, DEFAULT_MAX_ITEMS, activityFile, apply, applyActivityEvent, loadActivity, name, parseActivityContent, serializeActivity };
|
|
164
|
+
export { ACTIVITY_FILE_VERSION, Config, DEFAULT_MAX_ITEMS, activityFile, apply, applyActivityEvent, isCorruptActivity, loadActivity, loadActivityState, name, parseActivityContent, serializeActivity };
|
package/lib/types/index.d.ts
CHANGED
|
@@ -60,7 +60,58 @@ export declare function parseActivityContent(raw: string | null): EvolutionActiv
|
|
|
60
60
|
* through this call). The single-writer rule is unchanged: `apply()`'s
|
|
61
61
|
* transact listener remains the only WRITE path.
|
|
62
62
|
*/
|
|
63
|
+
/**
|
|
64
|
+
* FLOW6-6 (v43, the read side of H-3): the sidecar's records PLUS whether the
|
|
65
|
+
* bytes could be read as THIS format. `parseActivityContent` answers `[]` for
|
|
66
|
+
* a future-version or unparsable file, so a read-only consumer could not tell
|
|
67
|
+
* "newer format" from "no history" and presented a partial/empty view as the
|
|
68
|
+
* recorded truth. The write side already quarantines those bytes
|
|
69
|
+
* ({@link isCorruptActivity}); this is the channel the readers were missing.
|
|
70
|
+
*/
|
|
71
|
+
export interface ActivityLoad {
|
|
72
|
+
records: EvolutionActivityRecord[];
|
|
73
|
+
/** Bytes exist but are not a readable current-version envelope. A MISSING
|
|
74
|
+
* file is not corruption — it is a first write. */
|
|
75
|
+
corrupt: boolean;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* H-06: the read barrier over the sidecar, with the corruption verdict.
|
|
79
|
+
*
|
|
80
|
+
* @param root - the evolution state root (the sidecar lives under it).
|
|
81
|
+
* @param io - the IO provider to read through.
|
|
82
|
+
* @returns the parsed records and whether the bytes were readable.
|
|
83
|
+
*/
|
|
84
|
+
export declare function loadActivityState(root: string, io: EvolutionIoLike): Promise<ActivityLoad>;
|
|
85
|
+
/**
|
|
86
|
+
* H-06: the read barrier over the sidecar. V24-13 (v24): `loadActivity` now
|
|
87
|
+
* HAS a production consumer — `evolution-replay` backfills its `/evolution
|
|
88
|
+
* replay` leaderboard from this store at mount (the two packages'
|
|
89
|
+
* "persistence is the activity store's job" contract is actually wired
|
|
90
|
+
* through this call). The single-writer rule is unchanged: `apply()`'s
|
|
91
|
+
* transact listener remains the only WRITE path. Consumers that must not read
|
|
92
|
+
* an unreadable sidecar as "no history" use {@link loadActivityState}.
|
|
93
|
+
*/
|
|
63
94
|
export declare function loadActivity(root: string, io: EvolutionIoLike): Promise<EvolutionActivityRecord[]>;
|
|
95
|
+
/** True when bytes exist but are not a readable activity envelope: unparsable
|
|
96
|
+
* JSON, or a missing `items` array (a scalar/array/other-shaped file). A missing
|
|
97
|
+
* file (null) is NOT corruption — it is a first write. */
|
|
98
|
+
/**
|
|
99
|
+
* v43 audit (H-3 / J-6 / FLOW6-6): an UNSUPPORTED version is corrupt for this
|
|
100
|
+
* writer. The read side has always ignored `version` and taken `items` as-is,
|
|
101
|
+
* so a future-version sidecar used to fold fine and then be rewritten as
|
|
102
|
+
* `version: ACTIVITY_FILE_VERSION` on the next append — a silent downgrade that
|
|
103
|
+
* destroyed whatever the newer format carried. This guard is the write side's
|
|
104
|
+
* half of the pair: unknown version ⇒ the original bytes are quarantined (the
|
|
105
|
+
* existing `.corrupt` path) before a current-version file replaces them, which
|
|
106
|
+
* is the posture `evolution-events` already takes for the same shape.
|
|
107
|
+
* Residual, closed by FLOW6-6: `parseActivityContent` still answers `[]` for
|
|
108
|
+
* such a file, but {@link loadActivityState} now carries the verdict beside the
|
|
109
|
+
* records, so a read-only consumer can distinguish "newer format" from "no
|
|
110
|
+
* history" instead of presenting a partial view as the recorded truth.
|
|
111
|
+
* @internal Exported for this package's own tests (siblings `parseActivityContent`
|
|
112
|
+
* and `serializeActivity` are exported for the same reason).
|
|
113
|
+
*/
|
|
114
|
+
export declare function isCorruptActivity(raw: string | null): boolean;
|
|
64
115
|
export declare const name = "evolution-activity";
|
|
65
116
|
export interface Config {
|
|
66
117
|
/** Bounded sidecar: how many recent outcomes are kept. */
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lmzhen/dsh-evolution-activity",
|
|
3
3
|
"description": "Durable activity store for self-evolution plan outcomes (community build)",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.82",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -27,15 +27,15 @@
|
|
|
27
27
|
"license": "MIT",
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@deepseek-ai/schemastery": "^3.18.1",
|
|
30
|
-
"@lmzhen/dsh-evolution-core": "^0.3.
|
|
30
|
+
"@lmzhen/dsh-evolution-core": "^0.3.82"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
33
|
"@deepseek-ai/cordis": "^4.0.1"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
37
|
-
"@lmzhen/dsh-evolution-core": "^0.3.
|
|
38
|
-
"@lmzhen/dsh-evolution-io": "^0.3.
|
|
39
|
-
"@lmzhen/dsh-evolution-io-node": "^0.3.
|
|
37
|
+
"@lmzhen/dsh-evolution-core": "^0.3.82",
|
|
38
|
+
"@lmzhen/dsh-evolution-io": "^0.3.82",
|
|
39
|
+
"@lmzhen/dsh-evolution-io-node": "^0.3.82"
|
|
40
40
|
}
|
|
41
41
|
}
|