@remnic/core 10.0.100 → 10.0.101

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remnic/core",
3
- "version": "10.0.100",
3
+ "version": "10.0.101",
4
4
  "description": "Framework-agnostic Remnic memory engine — orchestrator, storage, extraction, search, trust zones",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -3386,7 +3386,7 @@
3386
3386
  "core"
3387
3387
  ],
3388
3388
  "peerDependencies": {
3389
- "@remnic/coding-graph": "^10.0.100"
3389
+ "@remnic/coding-graph": "^10.0.101"
3390
3390
  },
3391
3391
  "peerDependenciesMeta": {
3392
3392
  "@remnic/coding-graph": {
@@ -6,6 +6,26 @@ import test from "node:test";
6
6
  import { StorageManager, parseEntityFile } from "./storage.js";
7
7
  import { sleep } from "./utils/sleep.js";
8
8
 
9
+ const deferredStores: StorageManager[] = [];
10
+
11
+ // Opens a store the way the daemon does: the canonical-id migration starts in
12
+ // the background. Registered so teardown can wait for it.
13
+ async function deferStore(dir: string): Promise<StorageManager> {
14
+ const store = new StorageManager(dir);
15
+ deferredStores.push(store);
16
+ await store.ensureDirectories({ waitForEntityMigration: false });
17
+ return store;
18
+ }
19
+
20
+ // The deferred migration keeps writing under dir until it settles. Release any
21
+ // lock the test holds, wait for every registered migration, then remove dir.
22
+ async function disposeStore(dir: string): Promise<void> {
23
+ await rm(path.join(dir, "state", "entity-canonical-id-migration.lock"), { force: true });
24
+ await Promise.all(deferredStores.splice(0).map((store) => store.drainEntityCanonicalIdMigration()));
25
+ StorageManager.clearAllStaticCaches();
26
+ await rm(dir, { recursive: true, force: true });
27
+ }
28
+
9
29
  async function legacyStore() {
10
30
  const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-lazy-compat-"));
11
31
  const seed = new StorageManager(dir);
@@ -15,8 +35,7 @@ async function legacyStore() {
15
35
  await rm(journal, { force: true });
16
36
  const lock = path.join(dir, "state", "entity-canonical-id-migration.lock");
17
37
  await writeFile(lock, `${process.pid} test-holder ${new Date().toISOString()}\n`);
18
- const storage = new StorageManager(dir);
19
- await storage.ensureDirectories({ waitForEntityMigration: false });
38
+ const storage = await deferStore(dir);
20
39
  return { dir, id, journal, lock, storage };
21
40
  }
22
41
 
@@ -27,8 +46,7 @@ test("canonical reads find a legacy file without a journal or migration", async
27
46
  await assert.rejects(() => readFile(fixture.journal), { code: "ENOENT" });
28
47
  assert.ok(await readFile(fixture.lock));
29
48
  } finally {
30
- StorageManager.clearAllStaticCaches();
31
- await rm(fixture.dir, { recursive: true, force: true });
49
+ await disposeStore(fixture.dir);
32
50
  }
33
51
  });
34
52
 
@@ -39,8 +57,7 @@ test("ambiguous legacy canonical matches are refused rather than picking a claim
39
57
  await writeFile(path.join(fixture.dir, "entities", "other-legacy-person.md"), raw);
40
58
  await assert.rejects(() => fixture.storage.readEntity(fixture.id), /ambiguous/i);
41
59
  } finally {
42
- StorageManager.clearAllStaticCaches();
43
- await rm(fixture.dir, { recursive: true, force: true });
60
+ await disposeStore(fixture.dir);
44
61
  }
45
62
  });
46
63
 
@@ -50,8 +67,9 @@ test("completed migration skips corpus reads on repeated misses in a fresh read-
50
67
  const seed = new StorageManager(dir);
51
68
  await seed.writeEntity("Existing Person", "person", ["Synthetic fact."]);
52
69
  await seed.ensureDirectories();
53
- const reader = new StorageManager(dir);
54
- await reader.ensureDirectories({ waitForEntityMigration: false });
70
+ const reader = await deferStore(dir);
71
+ // Let the startup migration finish so only the readEntity calls below are counted.
72
+ await reader.drainEntityCanonicalIdMigration();
55
73
  // Bracket access is TypeScript's typed escape hatch for the private reader.
56
74
  const originalRead = reader["readStorageSecureFile"].bind(reader);
57
75
  const reads: string[] = [];
@@ -63,8 +81,7 @@ test("completed migration skips corpus reads on repeated misses in a fresh read-
63
81
  assert.equal(await reader.readEntity("person-missing"), "");
64
82
  assert.deepEqual(reads, ["person-missing.md", "person-missing.md"], "completed-store misses must not scan entity contents");
65
83
  } finally {
66
- StorageManager.clearAllStaticCaches();
67
- await rm(dir, { recursive: true, force: true });
84
+ await disposeStore(dir);
68
85
  }
69
86
  });
70
87
 
@@ -74,8 +91,7 @@ test("an external entities change invalidates the amortized completion verdict o
74
91
  const seed = new StorageManager(dir);
75
92
  const id = await seed.writeEntity("Café", "project", ["Synthetic legacy fact."]);
76
93
  await seed.ensureDirectories();
77
- const reader = new StorageManager(dir);
78
- await reader.ensureDirectories({ waitForEntityMigration: false });
94
+ const reader = await deferStore(dir);
79
95
  const canonical = reader.normalizeEntityName("Café", "project");
80
96
  // First miss computes and caches the completion verdict.
81
97
  assert.equal(await reader.readEntity("project-missing"), "");
@@ -84,8 +100,7 @@ test("an external entities change invalidates the amortized completion verdict o
84
100
  await rename(path.join(dir, "entities", `${id}.md`), path.join(dir, "entities", "legacy-cafe.md"));
85
101
  assert.match(await reader.readEntity(canonical), /Synthetic legacy fact/);
86
102
  } finally {
87
- StorageManager.clearAllStaticCaches();
88
- await rm(dir, { recursive: true, force: true });
103
+ await disposeStore(dir);
89
104
  }
90
105
  });
91
106
 
@@ -99,8 +114,7 @@ test("an external aliases change invalidates the amortized completion verdict on
99
114
  const journal = await readFile(journalPath, "utf8");
100
115
  assert.equal(JSON.parse(journal).complete, true);
101
116
 
102
- const reader = new StorageManager(dir);
103
- await reader.ensureDirectories({ waitForEntityMigration: false });
117
+ const reader = await deferStore(dir);
104
118
  // Warm amortized completion (and any same-reader completion memo) on a miss.
105
119
  assert.equal(await reader.readEntity("project-missing"), "");
106
120
 
@@ -121,8 +135,7 @@ test("an external aliases change invalidates the amortized completion verdict on
121
135
  assert.equal(await readFile(journalPath, "utf8"), journal, "read compatibility must not replay migration");
122
136
  assert.ok(await readFile(lock));
123
137
  } finally {
124
- StorageManager.clearAllStaticCaches();
125
- await rm(dir, { recursive: true, force: true });
138
+ await disposeStore(dir);
126
139
  }
127
140
  });
128
141
 
@@ -146,16 +159,14 @@ for (const change of ["aliases", "raw legacy write"]) {
146
159
  }
147
160
  const lock = path.join(dir, "state", "entity-canonical-id-migration.lock");
148
161
  await writeFile(lock, `${process.pid} test-holder ${new Date().toISOString()}\n`);
149
- const reader = new StorageManager(dir);
150
- await reader.ensureDirectories({ waitForEntityMigration: false });
162
+ const reader = await deferStore(dir);
151
163
  const canonical = reader.normalizeEntityName("Café", "project");
152
164
  if (change === "aliases") assert.notEqual(canonical, previousId);
153
165
  assert.match(await reader.readEntity(canonical), /Synthetic legacy fact/);
154
166
  assert.equal(await readFile(journalPath, "utf8"), journal, "read compatibility must not replay migration");
155
167
  assert.ok(await readFile(lock));
156
168
  } finally {
157
- StorageManager.clearAllStaticCaches();
158
- await rm(dir, { recursive: true, force: true });
169
+ await disposeStore(dir);
159
170
  }
160
171
  });
161
172
  }
@@ -221,8 +232,7 @@ for (const mutation of mutations) {
221
232
  const journal = JSON.parse(await readFile(fixture.journal, "utf8"));
222
233
  assert.equal(journal.complete, true);
223
234
  } finally {
224
- StorageManager.clearAllStaticCaches();
225
- await rm(fixture.dir, { recursive: true, force: true });
235
+ await disposeStore(fixture.dir);
226
236
  }
227
237
  });
228
238
  }