@lmzhen/dsh-evolution-core 0.1.0-rc.55 → 0.1.0-rc.57

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 CHANGED
@@ -197,7 +197,11 @@ function emptyRecord() {
197
197
  archived_at: null
198
198
  };
199
199
  }
200
- const isTimestamp = (value) => value === null || typeof value === "string";
200
+ /** A timestamp passes only when `Date.parse` yields a finite epoch (N-3): a bare
201
+ * string check let garbage like "not-a-date" propagate as Invalid Date → NaN
202
+ * into quality math and lifecycle comparisons. */
203
+ const validTimestamp = (value) => typeof value === "string" && Number.isFinite(Date.parse(value));
204
+ const nullableTimestamp = (value) => value === null || validTimestamp(value);
201
205
  /**
202
206
  * Field-level normalization for one sidecar record (rc.42 audit P2-3): the
203
207
  * spread used to copy any junk through verbatim, so a corrupted file could
@@ -205,6 +209,8 @@ const isTimestamp = (value) => value === null || typeof value === "string";
205
209
  * NaN. Every field falls back to its `emptyRecord()` baseline unless it has
206
210
  * exactly the declared type; an invalid `created_at` anchors the age clock at
207
211
  * now (first-sight defer semantics for a record whose age is unknowable).
212
+ * Timestamps additionally require a parseable date (N-3): `"not-a-date"`
213
+ * would otherwise survive the type check as Invalid Date.
208
214
  * Pure — exported for unit tests; `loadUsage` is the production caller.
209
215
  */
210
216
  function normalizeUsageRecord(record) {
@@ -218,13 +224,13 @@ function normalizeUsageRecord(record) {
218
224
  use_count: num(raw.use_count, base.use_count),
219
225
  view_count: num(raw.view_count, base.view_count),
220
226
  patch_count: num(raw.patch_count, base.patch_count),
221
- last_used_at: isTimestamp(raw.last_used_at) ? raw.last_used_at : base.last_used_at,
222
- last_viewed_at: isTimestamp(raw.last_viewed_at) ? raw.last_viewed_at : base.last_viewed_at,
223
- last_patched_at: isTimestamp(raw.last_patched_at) ? raw.last_patched_at : base.last_patched_at,
224
- created_at: typeof raw.created_at === "string" ? raw.created_at : base.created_at,
227
+ last_used_at: nullableTimestamp(raw.last_used_at) ? raw.last_used_at : base.last_used_at,
228
+ last_viewed_at: nullableTimestamp(raw.last_viewed_at) ? raw.last_viewed_at : base.last_viewed_at,
229
+ last_patched_at: nullableTimestamp(raw.last_patched_at) ? raw.last_patched_at : base.last_patched_at,
230
+ created_at: validTimestamp(raw.created_at) ? raw.created_at : base.created_at,
225
231
  state: raw.state === "stale" || raw.state === "archived" ? raw.state : "active",
226
232
  pinned: bool(raw.pinned, base.pinned),
227
- archived_at: isTimestamp(raw.archived_at) ? raw.archived_at : base.archived_at,
233
+ archived_at: nullableTimestamp(raw.archived_at) ? raw.archived_at : base.archived_at,
228
234
  quality_score: typeof raw.quality_score === "number" && Number.isFinite(raw.quality_score) ? raw.quality_score : void 0,
229
235
  quality_warn: typeof raw.quality_warn === "boolean" ? raw.quality_warn : void 0
230
236
  };
@@ -2308,6 +2314,7 @@ var SkillLibrary = class {
2308
2314
  if (await this.io.exists(dest)) {
2309
2315
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:T]/g, "").slice(0, 14);
2310
2316
  dest = join(archiveRoot, `${name.trim()}-${stamp}`);
2317
+ while (await this.io.exists(dest)) dest = join(archiveRoot, `${name.trim()}-${stamp}-${Math.random().toString(36).slice(2, 8)}`);
2311
2318
  }
2312
2319
  if (this.io.isSymlink) {
2313
2320
  if (await this.io.isSymlink(dir) === true) return {
@@ -2623,7 +2630,7 @@ var SkillLibrary = class {
2623
2630
  return null;
2624
2631
  }
2625
2632
  }
2626
- /** Keep only the newest N snapshots (Hermes keep=5 parity); oldest folded into .backups history. */
2633
+ /** Keep only the newest N snapshots (Hermes keep=5 parity); older ones are removed outright. */
2627
2634
  async retainSnapshots(keep) {
2628
2635
  const snapshots = await this.listSnapshots();
2629
2636
  for (const snapshot of snapshots.slice(keep)) try {
@@ -167,7 +167,7 @@ export declare class SkillLibrary {
167
167
  snapshotAll(reason?: string, extras?: SnapshotExtra[]): Promise<string>;
168
168
  /** Read and normalize a snapshot manifest; null when the file is missing or unparsable. */
169
169
  readSnapshotManifest(path: string): Promise<SnapshotManifest | null>;
170
- /** Keep only the newest N snapshots (Hermes keep=5 parity); oldest folded into .backups history. */
170
+ /** Keep only the newest N snapshots (Hermes keep=5 parity); older ones are removed outright. */
171
171
  private retainSnapshots;
172
172
  listSnapshots(): Promise<Array<{
173
173
  path: string;
@@ -29,6 +29,8 @@ export declare function emptyRecord(): UsageRecord;
29
29
  * NaN. Every field falls back to its `emptyRecord()` baseline unless it has
30
30
  * exactly the declared type; an invalid `created_at` anchors the age clock at
31
31
  * now (first-sight defer semantics for a record whose age is unknowable).
32
+ * Timestamps additionally require a parseable date (N-3): `"not-a-date"`
33
+ * would otherwise survive the type check as Invalid Date.
32
34
  * Pure — exported for unit tests; `loadUsage` is the production caller.
33
35
  */
34
36
  export declare function normalizeUsageRecord(record: unknown): UsageRecord;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lmzhen/dsh-evolution-core",
3
3
  "description": "Shared stores, prompts, signals and lifecycle logic for the dsh-evolution plugin family (community build)",
4
- "version": "0.1.0-rc.55",
4
+ "version": "0.1.0-rc.57",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -32,13 +32,13 @@
32
32
  ],
33
33
  "license": "MIT",
34
34
  "peerDependencies": {
35
- "@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
35
+ "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
36
36
  "@deepseek-ai/cordis": "^4.0.1",
37
- "@deepseek-ai/dsh-session": "^0.1.0-rc.6"
37
+ "@deepseek-ai/dsh-session": "^0.1.1-rc.2"
38
38
  },
39
39
  "devDependencies": {
40
- "@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
40
+ "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
41
41
  "@deepseek-ai/cordis": "^4.0.1",
42
- "@deepseek-ai/dsh-session": "^0.1.0-rc.6"
42
+ "@deepseek-ai/dsh-session": "^0.1.1-rc.2"
43
43
  }
44
44
  }