@lmzhen/dsh-evolution-state-domain 0.3.66 → 0.3.68
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 +1 -0
- package/lib/index.js +29 -12
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -24,4 +24,5 @@ Independent of request-prefix construction. This package does not alter the asse
|
|
|
24
24
|
|
|
25
25
|
- Requires the host-plane `storage-domain` facility. The bundle row stays dormant when it is absent.
|
|
26
26
|
- P2-4 (v15): the live pending table 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` are deleted on resolve. The audit ARCHIVE sidecar that the json provider maintains beyond the cap is json-specific (the domain seam has no sidecar facility) — resolved records past the cap are gone, not archived.
|
|
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`) are pruned (delete failures warn and retry on the next save). The stamp is stripped on read, so the consumer-facing record shape is unchanged.
|
|
27
28
|
|
package/lib/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { DomainError, defineDomain, domainTable } from "@deepseek-ai/dsh-storage-domain";
|
|
3
|
-
import { CURATOR_STATE_KEY, CURATOR_STATE_TABLE, PENDING_RESOLVED_CAP, PENDING_TABLE, PROVIDER_DOMAIN, REVIEW_STATE_TABLE, assertCloneable, canClaimPending, canResolvePending, cloneRecord, recordIssue, releasedStatus } from "@lmzhen/dsh-evolution-state-storage";
|
|
3
|
+
import { CURATOR_STATE_KEY, CURATOR_STATE_TABLE, PENDING_RESOLVED_CAP, PENDING_TABLE, PROVIDER_DOMAIN, REVIEW_STATE_SESSION_CAP, REVIEW_STATE_TABLE, assertCloneable, canClaimPending, canResolvePending, cloneRecord, recordIssue, releasedStatus, selectPendingOverflow, selectSessionOverflow } from "@lmzhen/dsh-evolution-state-storage";
|
|
4
4
|
//#region lib/types/index.js
|
|
5
5
|
/**
|
|
6
6
|
* storage-domain provider for evolution state.
|
|
@@ -117,14 +117,36 @@ function apply(ctx) {
|
|
|
117
117
|
name: PROVIDER_DOMAIN,
|
|
118
118
|
async loadReviewState(sessionId) {
|
|
119
119
|
const record = (await ensure()).table(REVIEW_STATE_TABLE).get(sessionId);
|
|
120
|
-
|
|
120
|
+
if (record === void 0) return null;
|
|
121
|
+
const { updatedAt: _stamp, ...rest } = record;
|
|
122
|
+
return structuredClone(rest);
|
|
121
123
|
},
|
|
122
124
|
async saveReviewState(sessionId, record) {
|
|
123
125
|
const issue = recordIssue(REVIEW_STATE_TABLE, record) ?? assertCloneable(record);
|
|
124
126
|
if (issue !== null) throw new Error(`evolution-state-domain: refusing to persist an invalid review-state record: ${issue}`);
|
|
125
127
|
const parsed = reviewStateSchema.safeParse(record);
|
|
126
128
|
if (!parsed.success) throw new Error(`evolution-state-domain: refusing to persist an invalid review-state record: ${parsed.error.issues[0]?.message ?? "schema mismatch"}`);
|
|
127
|
-
|
|
129
|
+
const table = (await ensure()).table(REVIEW_STATE_TABLE);
|
|
130
|
+
const stamped = {
|
|
131
|
+
...cloneRecord(parsed.data),
|
|
132
|
+
updatedAt: Date.now()
|
|
133
|
+
};
|
|
134
|
+
await table.put(sessionId, stamped);
|
|
135
|
+
const entries = [...table.entries()].map(([key, row]) => ({
|
|
136
|
+
key,
|
|
137
|
+
updatedAt: row.updatedAt ?? 0
|
|
138
|
+
})).filter((entry) => entry.key !== sessionId);
|
|
139
|
+
const victims = new Set(selectSessionOverflow(entries, {
|
|
140
|
+
keyOf: (entry) => entry.key,
|
|
141
|
+
stampOf: (entry) => entry.updatedAt
|
|
142
|
+
}, REVIEW_STATE_SESSION_CAP));
|
|
143
|
+
for (const entry of entries.filter((candidate) => victims.has(candidate.key))) {
|
|
144
|
+
const current = table.get(entry.key);
|
|
145
|
+
if (current === void 0 || (current.updatedAt ?? 0) !== entry.updatedAt) continue;
|
|
146
|
+
await table.delete(entry.key).catch((error) => {
|
|
147
|
+
ctx.logger.warn(`evolution-state-domain: review-state session-cap eviction for "${entry.key}" failed (will retry on the next save): ${error instanceof Error ? error.message : String(error)}`);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
128
150
|
},
|
|
129
151
|
async loadCuratorState() {
|
|
130
152
|
const record = (await ensure()).table(CURATOR_STATE_TABLE).get(CURATOR_STATE_KEY);
|
|
@@ -140,7 +162,7 @@ function apply(ctx) {
|
|
|
140
162
|
async transactCuratorState(task) {
|
|
141
163
|
const table = (await ensure()).table(CURATOR_STATE_TABLE);
|
|
142
164
|
const guarded = (current) => {
|
|
143
|
-
const next = task(current);
|
|
165
|
+
const next = task(current === null ? null : cloneRecord(current));
|
|
144
166
|
if (next === null) return null;
|
|
145
167
|
const issue = recordIssue(CURATOR_STATE_TABLE, next) ?? assertCloneable(next);
|
|
146
168
|
if (issue !== null) throw new Error(`evolution-state-domain: refusing to persist an invalid curator-state record: ${issue}`);
|
|
@@ -230,16 +252,11 @@ function apply(ctx) {
|
|
|
230
252
|
return resolved.record;
|
|
231
253
|
});
|
|
232
254
|
if (resolved.record !== null) {
|
|
233
|
-
const
|
|
255
|
+
const evictedRecords = new Set(selectPendingOverflow([...table.entries()].map(([, record]) => record), PENDING_RESOLVED_CAP));
|
|
256
|
+
const evicted = [...table.entries()].filter(([, record]) => evictedRecords.has(record)).map(([key, record]) => ({
|
|
234
257
|
key,
|
|
235
258
|
record
|
|
236
|
-
}))
|
|
237
|
-
const resolvedAtMs = (record) => {
|
|
238
|
-
const parsed = Date.parse(record.resolvedAt ?? "");
|
|
239
|
-
return Number.isNaN(parsed) ? Number.MAX_SAFE_INTEGER : parsed;
|
|
240
|
-
};
|
|
241
|
-
resolvedEntries.sort((a, b) => resolvedAtMs(a.record) - resolvedAtMs(b.record));
|
|
242
|
-
const evicted = resolvedEntries.slice(0, Math.max(0, resolvedEntries.length - PENDING_RESOLVED_CAP));
|
|
259
|
+
}));
|
|
243
260
|
for (const entry of evicted) {
|
|
244
261
|
const current = table.get(entry.key);
|
|
245
262
|
if (!(current !== void 0 && (current.status === "approved" || current.status === "rejected") && current.resolvedAt === entry.record.resolvedAt)) continue;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lmzhen/dsh-evolution-state-domain",
|
|
3
3
|
"description": "storage-domain provider for evolution state (community build)",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.68",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -36,13 +36,13 @@
|
|
|
36
36
|
"@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
|
|
37
37
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
38
38
|
"@deepseek-ai/dsh-storage-domain": "^0.1.1-rc.2",
|
|
39
|
-
"@lmzhen/dsh-evolution-state-storage": "^0.3.
|
|
39
|
+
"@lmzhen/dsh-evolution-state-storage": "^0.3.68"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
|
|
43
43
|
"@deepseek-ai/dsh-storage-domain": "^0.1.1-rc.2",
|
|
44
44
|
"@deepseek-ai/dsh-storage": "^0.1.1-rc.2",
|
|
45
45
|
"@deepseek-ai/dsh-storage-json": "^0.1.1-rc.2",
|
|
46
|
-
"@lmzhen/dsh-evolution-state-storage": "^0.3.
|
|
46
|
+
"@lmzhen/dsh-evolution-state-storage": "^0.3.68"
|
|
47
47
|
}
|
|
48
48
|
}
|