@lmzhen/dsh-evolution-state-domain 0.3.79 → 0.3.81

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.
Files changed (2) hide show
  1. package/lib/index.js +35 -2
  2. package/package.json +3 -3
package/lib/index.js CHANGED
@@ -90,13 +90,19 @@ const OPEN_MAX_ATTEMPTS = 3;
90
90
  const OPEN_RETRY_BASE_MS = 100;
91
91
  /** Retry cannot heal these — the stored record or the medium fails its own
92
92
  * schema/version, or the backend cannot serve the domain at all. Both storage
93
- * vocabularies (DomainError, StorageError) carry the code as stable API. */
93
+ * vocabularies (DomainError, StorageError) carry the code as stable API.
94
+ * S1-D3: `already-open` (DomainError, upstream open()'s single-open constraint)
95
+ * is deterministic too — a competing consumer of the `evolution` domain never
96
+ * goes away on retry, so burning the full backoff budget (3× loadAll) and then
97
+ * leaving `opening` cleared only re-runs the same futile budget on every later
98
+ * call. */
94
99
  const DETERMINISTIC_OPEN_CODES = new Set([
95
100
  "invalid-record",
96
101
  "malformed-medium",
97
102
  "version-mismatch",
98
103
  "facet-unsupported",
99
104
  "backend-not-found",
105
+ "already-open",
100
106
  "closed"
101
107
  ]);
102
108
  function isTransientOpenFailure(error) {
@@ -130,6 +136,7 @@ function apply(ctx) {
130
136
  domain = await opening;
131
137
  return domain;
132
138
  }
139
+ let pendingKeyWarned = false;
133
140
  const provider = {
134
141
  name: PROVIDER_DOMAIN,
135
142
  async loadReviewState(sessionId) {
@@ -202,7 +209,33 @@ function apply(ctx) {
202
209
  else await table.delete(CURATOR_STATE_KEY);
203
210
  },
204
211
  async listPending(status = "pending") {
205
- return [...(await ensure()).table(PENDING_TABLE).entries()].filter(([, value]) => value.status === status).map(([, value]) => structuredClone(value));
212
+ const table = (await ensure()).table(PENDING_TABLE);
213
+ const entries = [...table.entries()];
214
+ const drifted = [];
215
+ for (const [key, record] of entries) if (key !== record.id && typeof record.id === "string" && record.id !== "") drifted.push({
216
+ key,
217
+ record
218
+ });
219
+ if (drifted.length > 0) {
220
+ if (!pendingKeyWarned) {
221
+ pendingKeyWarned = true;
222
+ ctx.logger.warn(`evolution-state-domain: ${drifted.length} pending table entr(ies) were keyed by something other than record.id — re-keyed by id, so a drifted row is reachable by claim/resolve again`);
223
+ }
224
+ for (const { key, record } of drifted) try {
225
+ await table.put(record.id, structuredClone(record));
226
+ await table.delete(key);
227
+ } catch (error) {
228
+ ctx.logger.warn(`evolution-state-domain: pending re-key for "${key}" failed (will retry on the next list): ${error instanceof Error ? error.message : String(error)}`);
229
+ break;
230
+ }
231
+ }
232
+ const final = drifted.length > 0 ? [...table.entries()] : entries;
233
+ const seen = /* @__PURE__ */ new Set();
234
+ return final.filter(([, value]) => {
235
+ if (seen.has(value.id)) return false;
236
+ seen.add(value.id);
237
+ return value.status === status;
238
+ }).map(([, value]) => structuredClone(value));
206
239
  },
207
240
  async savePending(record) {
208
241
  const issue = recordIssue(PENDING_TABLE, record) ?? assertCloneable(record);
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.79",
4
+ "version": "0.3.81",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -31,12 +31,12 @@
31
31
  "peerDependencies": {
32
32
  "@deepseek-ai/cordis": "^4.0.1",
33
33
  "@deepseek-ai/dsh-storage-domain": "^0.1.5-rc.2",
34
- "@lmzhen/dsh-evolution-state-storage": "^0.3.79"
34
+ "@lmzhen/dsh-evolution-state-storage": "^0.3.81"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@deepseek-ai/dsh-storage-domain": "^0.1.5-rc.2",
38
38
  "@deepseek-ai/dsh-storage": "^0.1.5-rc.2",
39
39
  "@deepseek-ai/dsh-storage-json": "^0.1.5-rc.2",
40
- "@lmzhen/dsh-evolution-state-storage": "^0.3.79"
40
+ "@lmzhen/dsh-evolution-state-storage": "^0.3.81"
41
41
  }
42
42
  }