@principal-ai/principal-view-core 0.28.5 → 0.28.7
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/dist/auxiliary/AuxiliaryManifestValidator.d.ts +4 -1
- package/dist/auxiliary/AuxiliaryManifestValidator.d.ts.map +1 -1
- package/dist/auxiliary/AuxiliaryManifestValidator.js +12 -9
- package/dist/auxiliary/AuxiliaryManifestValidator.js.map +1 -1
- package/dist/events/EventsCanvasValidator.d.ts +3 -0
- package/dist/events/EventsCanvasValidator.d.ts.map +1 -1
- package/dist/events/EventsCanvasValidator.js +5 -4
- package/dist/events/EventsCanvasValidator.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/node.d.ts +4 -0
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +11 -2
- package/dist/node.js.map +1 -1
- package/dist/scopes/ScopesCanvasValidator.d.ts +3 -0
- package/dist/scopes/ScopesCanvasValidator.d.ts.map +1 -1
- package/dist/scopes/ScopesCanvasValidator.js +5 -4
- package/dist/scopes/ScopesCanvasValidator.js.map +1 -1
- package/dist/storage/topic-types.d.ts +189 -0
- package/dist/storage/topic-types.d.ts.map +1 -0
- package/dist/storage/topic-types.js +59 -0
- package/dist/storage/topic-types.js.map +1 -0
- package/dist/storage/topicStore.d.ts +130 -0
- package/dist/storage/topicStore.d.ts.map +1 -0
- package/dist/storage/topicStore.js +389 -0
- package/dist/storage/topicStore.js.map +1 -0
- package/package.json +1 -1
- package/src/auxiliary/AuxiliaryManifestValidator.test.ts +14 -13
- package/src/auxiliary/AuxiliaryManifestValidator.ts +12 -9
- package/src/browser-safety.test.ts +170 -0
- package/src/events/EventsCanvasValidator.test.ts +2 -1
- package/src/events/EventsCanvasValidator.ts +5 -4
- package/src/index.ts +13 -0
- package/src/node.ts +24 -0
- package/src/scopes/ScopesCanvasValidator.test.ts +6 -5
- package/src/scopes/ScopesCanvasValidator.ts +5 -4
- package/src/storage/topic-types.ts +220 -0
- package/src/storage/topicStore.test.ts +156 -0
- package/src/storage/topicStore.ts +481 -0
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* File-per-topic store under `~/.principal/topics/`.
|
|
4
|
+
*
|
|
5
|
+
* Layout (mirrors the trail store at `~/.principal/trails/`, so topics become
|
|
6
|
+
* locally greppable and an agent can read one directly):
|
|
7
|
+
*
|
|
8
|
+
* ~/.principal/topics/
|
|
9
|
+
* _index.json private, rebuildable manifest (entries[])
|
|
10
|
+
* <id>.json one pretty-printed DraftTopic per file
|
|
11
|
+
*
|
|
12
|
+
* Topics are the *least* repo-bound artifact (a bundle of trails spanning
|
|
13
|
+
* repos), so — unlike trails, which bucket by repo Purl — they are stored flat
|
|
14
|
+
* by id. No purl, no buckets, no `node:os`/Purl dependency beyond `homedir()`.
|
|
15
|
+
*
|
|
16
|
+
* The `_index.json` manifest is store-private and rebuildable: it is rebuilt by
|
|
17
|
+
* scanning the directory whenever it is missing or unparseable. It exists only
|
|
18
|
+
* to make `getTopics()` / `list()` cheap (no per-file read for listing).
|
|
19
|
+
*
|
|
20
|
+
* There is intentionally NO eviction cap. The trail store caps trails per repo
|
|
21
|
+
* (`PER_REPO_CAP`); topics are few, user-curated, and must never be silently
|
|
22
|
+
* dropped, so the cap is deliberately not carried over.
|
|
23
|
+
*
|
|
24
|
+
* Migration from the legacy single-blob `~/.alexandria/topics.json` is explicit
|
|
25
|
+
* (`migrateFromLegacyBlob`) — it is never run automatically on load; the desktop
|
|
26
|
+
* triggers it from a Settings action.
|
|
27
|
+
*/
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.TopicStore = exports.LEGACY_TOPICS_BLOB = exports.TOPICS_DIR = exports.PRINCIPAL_DIR = void 0;
|
|
30
|
+
const node_os_1 = require("node:os");
|
|
31
|
+
const node_path_1 = require("node:path");
|
|
32
|
+
const promises_1 = require("node:fs/promises");
|
|
33
|
+
/** Root of the global, cross-repo principal narration store. */
|
|
34
|
+
exports.PRINCIPAL_DIR = (0, node_path_1.join)((0, node_os_1.homedir)(), '.principal');
|
|
35
|
+
/** File-per-topic directory. */
|
|
36
|
+
exports.TOPICS_DIR = (0, node_path_1.join)(exports.PRINCIPAL_DIR, 'topics');
|
|
37
|
+
/** Legacy single-blob path the migration reads from. */
|
|
38
|
+
exports.LEGACY_TOPICS_BLOB = (0, node_path_1.join)((0, node_os_1.homedir)(), '.alexandria', 'topics.json');
|
|
39
|
+
const INDEX_FILENAME = '_index.json';
|
|
40
|
+
const DESCRIPTION_PREVIEW_MAX = 200;
|
|
41
|
+
const emptyIndex = () => ({ version: 1, entries: [] });
|
|
42
|
+
const sanitizeSegment = (value) => value.replace(/[^A-Za-z0-9._-]/g, '_');
|
|
43
|
+
const nowIso = () => new Date().toISOString();
|
|
44
|
+
/** Locally-unique topic id, matching the legacy `topic-<ts>-<rand>` shape. */
|
|
45
|
+
const generateTopicId = () => `topic-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
|
|
46
|
+
const descriptionPreview = (description) => {
|
|
47
|
+
if (!description)
|
|
48
|
+
return '';
|
|
49
|
+
const trimmed = description.trim();
|
|
50
|
+
if (trimmed.length <= DESCRIPTION_PREVIEW_MAX)
|
|
51
|
+
return trimmed;
|
|
52
|
+
return `${trimmed.slice(0, DESCRIPTION_PREVIEW_MAX - 1)}…`;
|
|
53
|
+
};
|
|
54
|
+
const buildEntry = (topic, sizeBytes) => ({
|
|
55
|
+
id: topic.id,
|
|
56
|
+
title: topic.title || 'Untitled topic',
|
|
57
|
+
descriptionPreview: descriptionPreview(topic.description),
|
|
58
|
+
trailCount: topic.trailIds.length,
|
|
59
|
+
state: topic.status?.state,
|
|
60
|
+
createdAt: topic.createdAt,
|
|
61
|
+
updatedAt: topic.updatedAt,
|
|
62
|
+
createdBy: topic.createdBy,
|
|
63
|
+
hasAssets: (topic.assets?.length ?? 0) > 0,
|
|
64
|
+
sizeBytes,
|
|
65
|
+
fileName: `${sanitizeSegment(topic.id)}.json`,
|
|
66
|
+
});
|
|
67
|
+
class TopicStore {
|
|
68
|
+
/**
|
|
69
|
+
* @param baseDir file-per-topic directory (defaults to `TOPICS_DIR`)
|
|
70
|
+
* @param legacyBlobPath legacy blob the migration reads (defaults to
|
|
71
|
+
* `LEGACY_TOPICS_BLOB`); injectable for tests.
|
|
72
|
+
*/
|
|
73
|
+
constructor(baseDir = exports.TOPICS_DIR, legacyBlobPath = exports.LEGACY_TOPICS_BLOB) {
|
|
74
|
+
this.index = null;
|
|
75
|
+
this.writeQueue = Promise.resolve();
|
|
76
|
+
this.baseDir = baseDir;
|
|
77
|
+
this.indexPath = (0, node_path_1.join)(baseDir, INDEX_FILENAME);
|
|
78
|
+
this.legacyBlobPath = legacyBlobPath;
|
|
79
|
+
}
|
|
80
|
+
// ===== Topic CRUD =====
|
|
81
|
+
async getTopics() {
|
|
82
|
+
const idx = await this.getIndex();
|
|
83
|
+
const topics = [];
|
|
84
|
+
for (const entry of idx.entries) {
|
|
85
|
+
const topic = await this.readTopic(entry.fileName);
|
|
86
|
+
if (topic)
|
|
87
|
+
topics.push(topic);
|
|
88
|
+
}
|
|
89
|
+
return topics;
|
|
90
|
+
}
|
|
91
|
+
async getTopic(id) {
|
|
92
|
+
const idx = await this.getIndex();
|
|
93
|
+
const entry = idx.entries.find((e) => e.id === id);
|
|
94
|
+
if (!entry)
|
|
95
|
+
return null;
|
|
96
|
+
return this.readTopic(entry.fileName);
|
|
97
|
+
}
|
|
98
|
+
/** Cheap listing straight off the manifest — no per-topic file read. */
|
|
99
|
+
async list() {
|
|
100
|
+
const idx = await this.getIndex();
|
|
101
|
+
return idx.entries
|
|
102
|
+
.slice()
|
|
103
|
+
.sort((a, b) => (a.updatedAt < b.updatedAt ? 1 : -1));
|
|
104
|
+
}
|
|
105
|
+
async createTopic(input) {
|
|
106
|
+
const idx = await this.getIndex();
|
|
107
|
+
const now = nowIso();
|
|
108
|
+
const id = input.id?.trim() || generateTopicId();
|
|
109
|
+
if (idx.entries.some((e) => e.id === id)) {
|
|
110
|
+
throw new Error(`Topic with id '${id}' already exists`);
|
|
111
|
+
}
|
|
112
|
+
const topic = {
|
|
113
|
+
...input,
|
|
114
|
+
id,
|
|
115
|
+
title: input.title || 'Untitled topic',
|
|
116
|
+
trailIds: input.trailIds ?? [],
|
|
117
|
+
createdAt: input.createdAt ?? now,
|
|
118
|
+
updatedAt: input.updatedAt ?? now,
|
|
119
|
+
};
|
|
120
|
+
await this.writeTopic(topic, idx);
|
|
121
|
+
return topic;
|
|
122
|
+
}
|
|
123
|
+
async updateTopic(id, updates) {
|
|
124
|
+
const idx = await this.getIndex();
|
|
125
|
+
const existing = await this.requireTopic(idx, id);
|
|
126
|
+
const next = {
|
|
127
|
+
...existing,
|
|
128
|
+
...updates,
|
|
129
|
+
id: existing.id,
|
|
130
|
+
trailIds: existing.trailIds,
|
|
131
|
+
createdAt: existing.createdAt,
|
|
132
|
+
updatedAt: nowIso(),
|
|
133
|
+
};
|
|
134
|
+
await this.writeTopic(next, idx);
|
|
135
|
+
return next;
|
|
136
|
+
}
|
|
137
|
+
async deleteTopic(id) {
|
|
138
|
+
const idx = await this.getIndex();
|
|
139
|
+
const entryIdx = idx.entries.findIndex((e) => e.id === id);
|
|
140
|
+
if (entryIdx < 0)
|
|
141
|
+
return false;
|
|
142
|
+
const { fileName } = idx.entries[entryIdx];
|
|
143
|
+
idx.entries.splice(entryIdx, 1);
|
|
144
|
+
await this.unlinkRelative(fileName);
|
|
145
|
+
await this.persistIndex();
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
// ===== Trail membership =====
|
|
149
|
+
async addTrailToTopic(topicId, trailId) {
|
|
150
|
+
const idx = await this.getIndex();
|
|
151
|
+
const topic = await this.requireTopic(idx, topicId);
|
|
152
|
+
if (topic.trailIds.includes(trailId))
|
|
153
|
+
return topic;
|
|
154
|
+
const next = {
|
|
155
|
+
...topic,
|
|
156
|
+
trailIds: [...topic.trailIds, trailId],
|
|
157
|
+
updatedAt: nowIso(),
|
|
158
|
+
};
|
|
159
|
+
await this.writeTopic(next, idx);
|
|
160
|
+
return next;
|
|
161
|
+
}
|
|
162
|
+
async removeTrailFromTopic(topicId, trailId) {
|
|
163
|
+
const idx = await this.getIndex();
|
|
164
|
+
const topic = await this.requireTopic(idx, topicId);
|
|
165
|
+
if (!topic.trailIds.includes(trailId))
|
|
166
|
+
return topic;
|
|
167
|
+
const next = {
|
|
168
|
+
...topic,
|
|
169
|
+
trailIds: topic.trailIds.filter((t) => t !== trailId),
|
|
170
|
+
updatedAt: nowIso(),
|
|
171
|
+
};
|
|
172
|
+
await this.writeTopic(next, idx);
|
|
173
|
+
return next;
|
|
174
|
+
}
|
|
175
|
+
async reorderTopicTrails(topicId, trailIds) {
|
|
176
|
+
const idx = await this.getIndex();
|
|
177
|
+
const topic = await this.requireTopic(idx, topicId);
|
|
178
|
+
const current = new Set(topic.trailIds);
|
|
179
|
+
const next = new Set(trailIds);
|
|
180
|
+
if (current.size !== next.size || [...current].some((t) => !next.has(t))) {
|
|
181
|
+
throw new Error('reorderTopicTrails expects a permutation of the existing trail list');
|
|
182
|
+
}
|
|
183
|
+
const updated = {
|
|
184
|
+
...topic,
|
|
185
|
+
trailIds: [...trailIds],
|
|
186
|
+
updatedAt: nowIso(),
|
|
187
|
+
};
|
|
188
|
+
await this.writeTopic(updated, idx);
|
|
189
|
+
return updated;
|
|
190
|
+
}
|
|
191
|
+
async getTopicsForTrail(trailId) {
|
|
192
|
+
const topics = await this.getTopics();
|
|
193
|
+
return topics.filter((t) => t.trailIds.includes(trailId));
|
|
194
|
+
}
|
|
195
|
+
// ===== Migration =====
|
|
196
|
+
/**
|
|
197
|
+
* One-shot migration from the legacy single-blob `~/.alexandria/topics.json`
|
|
198
|
+
* to file-per-topic. Reads the blob's `topics[]`, writes each as
|
|
199
|
+
* `<id>.json`, rebuilds the index, then renames the blob to `<blob>.bak` so
|
|
200
|
+
* a re-run is a no-op (mirrors the trail store's `migrateLegacyIfPresent`).
|
|
201
|
+
*
|
|
202
|
+
* Explicit by design — the desktop calls this from a Settings action, never
|
|
203
|
+
* on load. Idempotent: once the blob is `.bak`'d, subsequent calls report
|
|
204
|
+
* `noLegacyBlob`. An existing topic id is overwritten (the blob wins on a
|
|
205
|
+
* first migration), so re-importing after edits is the caller's decision.
|
|
206
|
+
*/
|
|
207
|
+
async migrateFromLegacyBlob() {
|
|
208
|
+
let raw;
|
|
209
|
+
try {
|
|
210
|
+
raw = await (0, promises_1.readFile)(this.legacyBlobPath, 'utf8');
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
return { migrated: 0, skipped: 0, noLegacyBlob: true };
|
|
214
|
+
}
|
|
215
|
+
let blob;
|
|
216
|
+
try {
|
|
217
|
+
blob = JSON.parse(raw);
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
throw new Error(`Legacy topics blob at ${this.legacyBlobPath} is not valid JSON; not migrating.`);
|
|
221
|
+
}
|
|
222
|
+
const idx = await this.getIndex();
|
|
223
|
+
let migrated = 0;
|
|
224
|
+
let skipped = 0;
|
|
225
|
+
for (const legacy of blob.topics ?? []) {
|
|
226
|
+
if (!legacy?.id) {
|
|
227
|
+
skipped++;
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
const now = nowIso();
|
|
231
|
+
const topic = {
|
|
232
|
+
...legacy,
|
|
233
|
+
title: legacy.title || 'Untitled topic',
|
|
234
|
+
trailIds: legacy.trailIds ?? [],
|
|
235
|
+
createdAt: legacy.createdAt ?? now,
|
|
236
|
+
updatedAt: legacy.updatedAt ?? now,
|
|
237
|
+
};
|
|
238
|
+
try {
|
|
239
|
+
await this.writeTopic(topic, idx);
|
|
240
|
+
migrated++;
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
skipped++;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
const backupPath = `${this.legacyBlobPath}.bak`;
|
|
247
|
+
let backedUp;
|
|
248
|
+
if (!(await this.pathExists(backupPath))) {
|
|
249
|
+
try {
|
|
250
|
+
await (0, promises_1.rename)(this.legacyBlobPath, backupPath);
|
|
251
|
+
backedUp = backupPath;
|
|
252
|
+
}
|
|
253
|
+
catch {
|
|
254
|
+
// Non-fatal: topics are migrated; the blob just wasn't renamed.
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return {
|
|
258
|
+
migrated,
|
|
259
|
+
skipped,
|
|
260
|
+
noLegacyBlob: false,
|
|
261
|
+
...(backedUp ? { backupPath: backedUp } : {}),
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
// ===== Internals =====
|
|
265
|
+
async getIndex() {
|
|
266
|
+
if (!this.index) {
|
|
267
|
+
await (0, promises_1.mkdir)(this.baseDir, { recursive: true });
|
|
268
|
+
this.index = await this.loadIndex();
|
|
269
|
+
}
|
|
270
|
+
return this.index;
|
|
271
|
+
}
|
|
272
|
+
async requireTopic(idx, id) {
|
|
273
|
+
const entry = idx.entries.find((e) => e.id === id);
|
|
274
|
+
if (!entry)
|
|
275
|
+
throw new Error(`Topic with id '${id}' not found`);
|
|
276
|
+
const topic = await this.readTopic(entry.fileName);
|
|
277
|
+
if (!topic)
|
|
278
|
+
throw new Error(`Topic with id '${id}' not found`);
|
|
279
|
+
return topic;
|
|
280
|
+
}
|
|
281
|
+
/** Write a topic file and upsert its index entry. Does not generate ids. */
|
|
282
|
+
async writeTopic(topic, idx) {
|
|
283
|
+
const fileName = `${sanitizeSegment(topic.id)}.json`;
|
|
284
|
+
const file = (0, node_path_1.join)(this.baseDir, fileName);
|
|
285
|
+
await (0, promises_1.mkdir)((0, node_path_1.dirname)(file), { recursive: true });
|
|
286
|
+
const serialized = JSON.stringify(topic, null, 2);
|
|
287
|
+
await (0, promises_1.writeFile)(file, serialized, 'utf8');
|
|
288
|
+
const entry = buildEntry(topic, Buffer.byteLength(serialized, 'utf8'));
|
|
289
|
+
const at = idx.entries.findIndex((e) => e.id === topic.id);
|
|
290
|
+
if (at >= 0)
|
|
291
|
+
idx.entries[at] = entry;
|
|
292
|
+
else
|
|
293
|
+
idx.entries.push(entry);
|
|
294
|
+
await this.persistIndex();
|
|
295
|
+
}
|
|
296
|
+
async readTopic(fileName) {
|
|
297
|
+
try {
|
|
298
|
+
const raw = await (0, promises_1.readFile)((0, node_path_1.join)(this.baseDir, fileName), 'utf8');
|
|
299
|
+
return JSON.parse(raw);
|
|
300
|
+
}
|
|
301
|
+
catch (err) {
|
|
302
|
+
console.error('[TopicStore] Failed to read topic', fileName, err);
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
async unlinkRelative(fileName) {
|
|
307
|
+
try {
|
|
308
|
+
await (0, promises_1.unlink)((0, node_path_1.join)(this.baseDir, fileName));
|
|
309
|
+
}
|
|
310
|
+
catch (err) {
|
|
311
|
+
const code = err.code;
|
|
312
|
+
if (code !== 'ENOENT') {
|
|
313
|
+
console.error('[TopicStore] Failed to unlink', fileName, err);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
async pathExists(path) {
|
|
318
|
+
try {
|
|
319
|
+
await (0, promises_1.access)(path);
|
|
320
|
+
return true;
|
|
321
|
+
}
|
|
322
|
+
catch {
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
async loadIndex() {
|
|
327
|
+
try {
|
|
328
|
+
const raw = await (0, promises_1.readFile)(this.indexPath, 'utf8');
|
|
329
|
+
const parsed = JSON.parse(raw);
|
|
330
|
+
if (parsed?.version === 1 && Array.isArray(parsed.entries)) {
|
|
331
|
+
return { version: 1, entries: parsed.entries };
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
catch (err) {
|
|
335
|
+
const code = err.code;
|
|
336
|
+
if (code !== 'ENOENT') {
|
|
337
|
+
console.warn('[TopicStore] _index.json unreadable, rebuilding', err);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
// No auto-migration here: the legacy blob is imported only via the
|
|
341
|
+
// explicit migrateFromLegacyBlob() Settings action.
|
|
342
|
+
return this.rebuildIndex();
|
|
343
|
+
}
|
|
344
|
+
/** Rebuild the manifest by scanning every `<id>.json` in the directory. */
|
|
345
|
+
async rebuildIndex() {
|
|
346
|
+
const idx = emptyIndex();
|
|
347
|
+
let names;
|
|
348
|
+
try {
|
|
349
|
+
names = await (0, promises_1.readdir)(this.baseDir);
|
|
350
|
+
}
|
|
351
|
+
catch {
|
|
352
|
+
return idx;
|
|
353
|
+
}
|
|
354
|
+
for (const name of names) {
|
|
355
|
+
if (name === INDEX_FILENAME || !name.endsWith('.json'))
|
|
356
|
+
continue;
|
|
357
|
+
const topic = await this.readTopic(name);
|
|
358
|
+
if (!topic?.id)
|
|
359
|
+
continue;
|
|
360
|
+
const createdAt = topic.createdAt ?? nowIso();
|
|
361
|
+
const stamped = {
|
|
362
|
+
...topic,
|
|
363
|
+
createdAt,
|
|
364
|
+
updatedAt: topic.updatedAt ?? createdAt,
|
|
365
|
+
title: topic.title || 'Untitled topic',
|
|
366
|
+
trailIds: topic.trailIds ?? [],
|
|
367
|
+
};
|
|
368
|
+
const raw = JSON.stringify(stamped, null, 2);
|
|
369
|
+
idx.entries.push(buildEntry(stamped, Buffer.byteLength(raw, 'utf8')));
|
|
370
|
+
}
|
|
371
|
+
this.index = idx;
|
|
372
|
+
await this.persistIndex();
|
|
373
|
+
return idx;
|
|
374
|
+
}
|
|
375
|
+
persistIndex() {
|
|
376
|
+
if (!this.index)
|
|
377
|
+
return Promise.resolve();
|
|
378
|
+
const snapshot = this.index;
|
|
379
|
+
this.writeQueue = this.writeQueue
|
|
380
|
+
.catch(() => undefined)
|
|
381
|
+
.then(async () => {
|
|
382
|
+
await (0, promises_1.mkdir)(this.baseDir, { recursive: true });
|
|
383
|
+
await (0, promises_1.writeFile)(this.indexPath, JSON.stringify(snapshot, null, 2), 'utf8');
|
|
384
|
+
});
|
|
385
|
+
return this.writeQueue;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
exports.TopicStore = TopicStore;
|
|
389
|
+
//# sourceMappingURL=topicStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"topicStore.js","sourceRoot":"","sources":["../../src/storage/topicStore.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -2,6 +2,7 @@ import { describe, expect, test } from 'bun:test';
|
|
|
2
2
|
import { mkdtempSync, mkdirSync, writeFileSync } from 'fs';
|
|
3
3
|
import { tmpdir } from 'os';
|
|
4
4
|
import { join } from 'path';
|
|
5
|
+
import { NodeFileSystemAdapter } from '@principal-ai/repository-abstraction/node';
|
|
5
6
|
import { AuxiliaryManifestValidator } from './AuxiliaryManifestValidator';
|
|
6
7
|
import type { AuxiliaryManifest } from '../types/auxiliary';
|
|
7
8
|
|
|
@@ -16,15 +17,15 @@ function makeRepo(layout: string[]): string {
|
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
describe('AuxiliaryManifestValidator', () => {
|
|
19
|
-
const validator = new AuxiliaryManifestValidator();
|
|
20
|
+
const validator = new AuxiliaryManifestValidator(new NodeFileSystemAdapter());
|
|
20
21
|
|
|
21
|
-
test('no manifest is a no-op', () => {
|
|
22
|
-
const result = validator.validate({ basePath: '/tmp' });
|
|
22
|
+
test('no manifest is a no-op', async () => {
|
|
23
|
+
const result = await validator.validate({ basePath: '/tmp' });
|
|
23
24
|
expect(result.valid).toBe(true);
|
|
24
25
|
expect(result.violations).toHaveLength(0);
|
|
25
26
|
});
|
|
26
27
|
|
|
27
|
-
test('passes for a clean manifest', () => {
|
|
28
|
+
test('passes for a clean manifest', async () => {
|
|
28
29
|
const root = makeRepo(['docs/README.md', '.github/workflows/ci.yml']);
|
|
29
30
|
const manifest: AuxiliaryManifest = {
|
|
30
31
|
areas: [
|
|
@@ -32,12 +33,12 @@ describe('AuxiliaryManifestValidator', () => {
|
|
|
32
33
|
{ name: 'GitHub', paths: ['.github'], description: 'CI config' },
|
|
33
34
|
],
|
|
34
35
|
};
|
|
35
|
-
const result = validator.validate({ manifest, basePath: root });
|
|
36
|
+
const result = await validator.validate({ manifest, basePath: root });
|
|
36
37
|
expect(result.valid).toBe(true);
|
|
37
38
|
expect(result.violations).toHaveLength(0);
|
|
38
39
|
});
|
|
39
40
|
|
|
40
|
-
test('flags duplicate area names', () => {
|
|
41
|
+
test('flags duplicate area names', async () => {
|
|
41
42
|
const root = makeRepo(['docs/README.md', 'misc/note.txt']);
|
|
42
43
|
const manifest: AuxiliaryManifest = {
|
|
43
44
|
areas: [
|
|
@@ -45,19 +46,19 @@ describe('AuxiliaryManifestValidator', () => {
|
|
|
45
46
|
{ name: 'Docs', paths: ['misc'], description: 'b' },
|
|
46
47
|
],
|
|
47
48
|
};
|
|
48
|
-
const result = validator.validate({ manifest, basePath: root });
|
|
49
|
+
const result = await validator.validate({ manifest, basePath: root });
|
|
49
50
|
const dup = result.violations.find((v) => v.ruleId === 'auxiliary-area-name-duplicate');
|
|
50
51
|
expect(dup).toBeDefined();
|
|
51
52
|
expect(dup!.severity).toBe('error');
|
|
52
53
|
expect(result.valid).toBe(false);
|
|
53
54
|
});
|
|
54
55
|
|
|
55
|
-
test('warns when a declared path does not exist', () => {
|
|
56
|
+
test('warns when a declared path does not exist', async () => {
|
|
56
57
|
const root = makeRepo(['docs/README.md']);
|
|
57
58
|
const manifest: AuxiliaryManifest = {
|
|
58
59
|
areas: [{ name: 'Ghost', paths: ['nope'], description: 'missing' }],
|
|
59
60
|
};
|
|
60
|
-
const result = validator.validate({ manifest, basePath: root });
|
|
61
|
+
const result = await validator.validate({ manifest, basePath: root });
|
|
61
62
|
const missing = result.violations.find((v) => v.ruleId === 'auxiliary-area-path-missing');
|
|
62
63
|
expect(missing).toBeDefined();
|
|
63
64
|
expect(missing!.severity).toBe('warn');
|
|
@@ -65,7 +66,7 @@ describe('AuxiliaryManifestValidator', () => {
|
|
|
65
66
|
expect(result.valid).toBe(true);
|
|
66
67
|
});
|
|
67
68
|
|
|
68
|
-
test('flags overlap between two areas', () => {
|
|
69
|
+
test('flags overlap between two areas', async () => {
|
|
69
70
|
const root = makeRepo(['docs/sub/file.md']);
|
|
70
71
|
const manifest: AuxiliaryManifest = {
|
|
71
72
|
areas: [
|
|
@@ -73,19 +74,19 @@ describe('AuxiliaryManifestValidator', () => {
|
|
|
73
74
|
{ name: 'B', paths: ['docs/sub'], description: 'b' },
|
|
74
75
|
],
|
|
75
76
|
};
|
|
76
|
-
const result = validator.validate({ manifest, basePath: root });
|
|
77
|
+
const result = await validator.validate({ manifest, basePath: root });
|
|
77
78
|
const overlap = result.violations.find((v) => v.ruleId === 'auxiliary-area-paths-overlap');
|
|
78
79
|
expect(overlap).toBeDefined();
|
|
79
80
|
expect(overlap!.severity).toBe('error');
|
|
80
81
|
expect(result.valid).toBe(false);
|
|
81
82
|
});
|
|
82
83
|
|
|
83
|
-
test('flags duplicate paths within a single area as self-overlap', () => {
|
|
84
|
+
test('flags duplicate paths within a single area as self-overlap', async () => {
|
|
84
85
|
const root = makeRepo(['docs/file.md']);
|
|
85
86
|
const manifest: AuxiliaryManifest = {
|
|
86
87
|
areas: [{ name: 'A', paths: ['docs', 'docs'], description: 'a' }],
|
|
87
88
|
};
|
|
88
|
-
const result = validator.validate({ manifest, basePath: root });
|
|
89
|
+
const result = await validator.validate({ manifest, basePath: root });
|
|
89
90
|
const self = result.violations.find((v) => v.ruleId === 'auxiliary-area-paths-self-overlap');
|
|
90
91
|
expect(self).toBeDefined();
|
|
91
92
|
});
|
|
@@ -11,8 +11,7 @@
|
|
|
11
11
|
* checks for scopes/namespaces.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import {
|
|
15
|
-
import { resolve } from 'path';
|
|
14
|
+
import type { FileSystemAdapter } from '@principal-ai/repository-abstraction';
|
|
16
15
|
import type { AuxiliaryManifest } from '../types/auxiliary';
|
|
17
16
|
import { pathsOverlap } from '../events/path-helpers';
|
|
18
17
|
|
|
@@ -45,7 +44,9 @@ export interface AuxiliaryManifestValidationResult {
|
|
|
45
44
|
const DEFAULT_MANIFEST_PATH = '.principal-views/auxiliary.manifest.json';
|
|
46
45
|
|
|
47
46
|
export class AuxiliaryManifestValidator {
|
|
48
|
-
|
|
47
|
+
constructor(private fsAdapter: FileSystemAdapter) {}
|
|
48
|
+
|
|
49
|
+
async validate(context: AuxiliaryManifestValidationContext): Promise<AuxiliaryManifestValidationResult> {
|
|
49
50
|
const violations: AuxiliaryManifestViolation[] = [];
|
|
50
51
|
const { manifest, basePath } = context;
|
|
51
52
|
const file = context.manifestPath || DEFAULT_MANIFEST_PATH;
|
|
@@ -76,13 +77,15 @@ export class AuxiliaryManifestValidator {
|
|
|
76
77
|
|
|
77
78
|
// Path existence + collect for overlap pass
|
|
78
79
|
const declaredPaths: Array<{ areaName: string; areaIndex: number; pathIndex: number; path: string }> = [];
|
|
79
|
-
areas.
|
|
80
|
+
for (let areaIdx = 0; areaIdx < areas.length; areaIdx++) {
|
|
81
|
+
const area = areas[areaIdx];
|
|
80
82
|
const paths = area.paths || [];
|
|
81
|
-
paths.
|
|
83
|
+
for (let pathIdx = 0; pathIdx < paths.length; pathIdx++) {
|
|
84
|
+
const p = paths[pathIdx];
|
|
82
85
|
declaredPaths.push({ areaName: area.name, areaIndex: areaIdx, pathIndex: pathIdx, path: p });
|
|
83
86
|
|
|
84
|
-
const resolved =
|
|
85
|
-
if (!
|
|
87
|
+
const resolved = this.fsAdapter.join(basePath, p);
|
|
88
|
+
if (!(await this.fsAdapter.exists(resolved))) {
|
|
86
89
|
violations.push({
|
|
87
90
|
ruleId: 'auxiliary-area-path-missing',
|
|
88
91
|
severity: 'warn',
|
|
@@ -93,8 +96,8 @@ export class AuxiliaryManifestValidator {
|
|
|
93
96
|
suggestion: 'Verify the path exists relative to the repository root, or remove it from the area declaration.',
|
|
94
97
|
});
|
|
95
98
|
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
98
101
|
|
|
99
102
|
// Path overlap. Unlike scopes, areas have no dotted-name hierarchy, so
|
|
100
103
|
// parent-child nesting between two areas is just as ambiguous as a flat
|