@deskwork/core 0.10.2 → 0.11.1
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/calendar/parse.d.ts +24 -0
- package/dist/calendar/parse.d.ts.map +1 -0
- package/dist/calendar/parse.js +64 -0
- package/dist/calendar/parse.js.map +1 -0
- package/dist/calendar/render.d.ts +3 -0
- package/dist/calendar/render.d.ts.map +1 -0
- package/dist/calendar/render.js +46 -0
- package/dist/calendar/render.js.map +1 -0
- package/dist/doctor/migrate.d.ts +11 -0
- package/dist/doctor/migrate.d.ts.map +1 -0
- package/dist/doctor/migrate.js +101 -0
- package/dist/doctor/migrate.js.map +1 -0
- package/dist/doctor/repair.d.ts +9 -0
- package/dist/doctor/repair.d.ts.map +1 -0
- package/dist/doctor/repair.js +35 -0
- package/dist/doctor/repair.js.map +1 -0
- package/dist/doctor/validate.d.ts +11 -0
- package/dist/doctor/validate.d.ts.map +1 -0
- package/dist/doctor/validate.js +405 -0
- package/dist/doctor/validate.js.map +1 -0
- package/dist/iterate/iterate.d.ts +13 -0
- package/dist/iterate/iterate.d.ts.map +1 -0
- package/dist/iterate/iterate.js +69 -0
- package/dist/iterate/iterate.js.map +1 -0
- package/dist/journal/append.d.ts +3 -0
- package/dist/journal/append.d.ts.map +1 -0
- package/dist/journal/append.js +18 -0
- package/dist/journal/append.js.map +1 -0
- package/dist/journal/index.d.ts +3 -0
- package/dist/journal/index.d.ts.map +1 -0
- package/dist/journal/index.js +3 -0
- package/dist/journal/index.js.map +1 -0
- package/dist/journal/read.d.ts +9 -0
- package/dist/journal/read.d.ts.map +1 -0
- package/dist/journal/read.js +41 -0
- package/dist/journal/read.js.map +1 -0
- package/dist/schema/annotation.d.ts +49 -0
- package/dist/schema/annotation.d.ts.map +1 -0
- package/dist/schema/annotation.js +13 -0
- package/dist/schema/annotation.js.map +1 -0
- package/dist/schema/entry.d.ts +57 -0
- package/dist/schema/entry.d.ts.map +1 -0
- package/dist/schema/entry.js +47 -0
- package/dist/schema/entry.js.map +1 -0
- package/dist/schema/journal-events.d.ts +275 -0
- package/dist/schema/journal-events.d.ts.map +1 -0
- package/dist/schema/journal-events.js +60 -0
- package/dist/schema/journal-events.js.map +1 -0
- package/dist/sidecar/index.d.ts +6 -0
- package/dist/sidecar/index.d.ts.map +1 -0
- package/dist/sidecar/index.js +6 -0
- package/dist/sidecar/index.js.map +1 -0
- package/dist/sidecar/lookup.d.ts +2 -0
- package/dist/sidecar/lookup.d.ts.map +1 -0
- package/dist/sidecar/lookup.js +30 -0
- package/dist/sidecar/lookup.js.map +1 -0
- package/dist/sidecar/paths.d.ts +3 -0
- package/dist/sidecar/paths.d.ts.map +1 -0
- package/dist/sidecar/paths.js +8 -0
- package/dist/sidecar/paths.js.map +1 -0
- package/dist/sidecar/read-all.d.ts +14 -0
- package/dist/sidecar/read-all.d.ts.map +1 -0
- package/dist/sidecar/read-all.js +49 -0
- package/dist/sidecar/read-all.js.map +1 -0
- package/dist/sidecar/read.d.ts +3 -0
- package/dist/sidecar/read.d.ts.map +1 -0
- package/dist/sidecar/read.js +30 -0
- package/dist/sidecar/read.js.map +1 -0
- package/dist/sidecar/write.d.ts +3 -0
- package/dist/sidecar/write.d.ts.map +1 -0
- package/dist/sidecar/write.js +16 -0
- package/dist/sidecar/write.js.map +1 -0
- package/package.json +27 -2
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
import { readdir, readFile, stat } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { EntrySchema, isLinearPipelineStage, isOffPipelineStage, } from "../schema/entry.js";
|
|
4
|
+
import { extractEntriesForMigration } from "../calendar/parse.js";
|
|
5
|
+
import { readJournalEvents } from "../journal/read.js";
|
|
6
|
+
async function validateSchema(projectRoot) {
|
|
7
|
+
const failures = [];
|
|
8
|
+
const dir = join(projectRoot, '.deskwork', 'entries');
|
|
9
|
+
let names = [];
|
|
10
|
+
try {
|
|
11
|
+
names = await readdir(dir);
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return failures;
|
|
15
|
+
}
|
|
16
|
+
for (const name of names.filter((n) => n.endsWith('.json'))) {
|
|
17
|
+
const path = join(dir, name);
|
|
18
|
+
const raw = await readFile(path, 'utf8');
|
|
19
|
+
let json;
|
|
20
|
+
try {
|
|
21
|
+
json = JSON.parse(raw);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
failures.push({ category: 'schema', message: 'JSON parse failed', path });
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
const result = EntrySchema.safeParse(json);
|
|
28
|
+
if (!result.success) {
|
|
29
|
+
failures.push({ category: 'schema', message: result.error.message, path });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return failures;
|
|
33
|
+
}
|
|
34
|
+
async function readSidecarUuids(projectRoot) {
|
|
35
|
+
const dir = join(projectRoot, '.deskwork', 'entries');
|
|
36
|
+
const uuids = new Set();
|
|
37
|
+
let names = [];
|
|
38
|
+
try {
|
|
39
|
+
names = await readdir(dir);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return uuids;
|
|
43
|
+
}
|
|
44
|
+
for (const name of names.filter((n) => n.endsWith('.json'))) {
|
|
45
|
+
uuids.add(name.replace(/\.json$/, ''));
|
|
46
|
+
}
|
|
47
|
+
return uuids;
|
|
48
|
+
}
|
|
49
|
+
async function validateCalendarSidecar(projectRoot) {
|
|
50
|
+
const failures = [];
|
|
51
|
+
const calendarPath = join(projectRoot, '.deskwork', 'calendar.md');
|
|
52
|
+
let md;
|
|
53
|
+
try {
|
|
54
|
+
md = await readFile(calendarPath, 'utf8');
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return failures;
|
|
58
|
+
}
|
|
59
|
+
const calendarEntries = extractEntriesForMigration(md);
|
|
60
|
+
const calendarUuids = new Set(calendarEntries.map((e) => e.uuid));
|
|
61
|
+
const sidecarUuids = await readSidecarUuids(projectRoot);
|
|
62
|
+
for (const uuid of calendarUuids) {
|
|
63
|
+
if (!sidecarUuids.has(uuid)) {
|
|
64
|
+
failures.push({
|
|
65
|
+
category: 'calendar-sidecar',
|
|
66
|
+
message: `calendar.md lists uuid ${uuid} but no sidecar exists at .deskwork/entries/${uuid}.json`,
|
|
67
|
+
entryId: uuid,
|
|
68
|
+
path: calendarPath,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
for (const uuid of sidecarUuids) {
|
|
73
|
+
if (!calendarUuids.has(uuid)) {
|
|
74
|
+
failures.push({
|
|
75
|
+
category: 'calendar-sidecar',
|
|
76
|
+
message: `sidecar ${uuid}.json exists but calendar.md does not list this uuid`,
|
|
77
|
+
entryId: uuid,
|
|
78
|
+
path: join(projectRoot, '.deskwork', 'entries', `${uuid}.json`),
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return failures;
|
|
83
|
+
}
|
|
84
|
+
async function loadSidecars(projectRoot) {
|
|
85
|
+
const dir = join(projectRoot, '.deskwork', 'entries');
|
|
86
|
+
let names = [];
|
|
87
|
+
try {
|
|
88
|
+
names = await readdir(dir);
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return [];
|
|
92
|
+
}
|
|
93
|
+
const out = [];
|
|
94
|
+
for (const name of names.filter((n) => n.endsWith('.json'))) {
|
|
95
|
+
const path = join(dir, name);
|
|
96
|
+
const raw = await readFile(path, 'utf8');
|
|
97
|
+
let json;
|
|
98
|
+
try {
|
|
99
|
+
json = JSON.parse(raw);
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
const result = EntrySchema.safeParse(json);
|
|
105
|
+
if (!result.success)
|
|
106
|
+
continue;
|
|
107
|
+
out.push({ filename: name, path, entry: result.data });
|
|
108
|
+
}
|
|
109
|
+
return out;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Stage-conventional artifact path. Returns null when a stage does not have a
|
|
113
|
+
* primary on-disk artifact (e.g. Blocked / Cancelled).
|
|
114
|
+
*
|
|
115
|
+
* Note: Published shares the Drafting/Final path (`docs/<slug>/index.md`).
|
|
116
|
+
*/
|
|
117
|
+
function artifactPathForStage(projectRoot, slug, stage) {
|
|
118
|
+
switch (stage) {
|
|
119
|
+
case 'Ideas':
|
|
120
|
+
return join(projectRoot, 'docs', slug, 'scrapbook', 'idea.md');
|
|
121
|
+
case 'Planned':
|
|
122
|
+
return join(projectRoot, 'docs', slug, 'scrapbook', 'plan.md');
|
|
123
|
+
case 'Outlining':
|
|
124
|
+
return join(projectRoot, 'docs', slug, 'scrapbook', 'outline.md');
|
|
125
|
+
case 'Drafting':
|
|
126
|
+
case 'Final':
|
|
127
|
+
case 'Published':
|
|
128
|
+
return join(projectRoot, 'docs', slug, 'index.md');
|
|
129
|
+
case 'Blocked':
|
|
130
|
+
case 'Cancelled':
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Minimal frontmatter `deskwork.stage` extractor.
|
|
136
|
+
*
|
|
137
|
+
* The plugin's frontmatter is YAML-ish, but for the validator's narrow purpose
|
|
138
|
+
* (read `deskwork.stage`) a regex avoids pulling a full YAML parser into the
|
|
139
|
+
* validator codepath. We match the `deskwork:` block and find a `stage:` line
|
|
140
|
+
* inside it, accepting any indentation.
|
|
141
|
+
*/
|
|
142
|
+
function extractDeskworkStage(markdown) {
|
|
143
|
+
const fmMatch = markdown.match(/^---\n([\s\S]*?)\n---/);
|
|
144
|
+
if (!fmMatch)
|
|
145
|
+
return undefined;
|
|
146
|
+
const fm = fmMatch[1];
|
|
147
|
+
// Find the line `deskwork:` (the block opener) and then any subsequent
|
|
148
|
+
// indented child line of the form ` stage: <value>` before either the next
|
|
149
|
+
// unindented line or end-of-frontmatter.
|
|
150
|
+
const lines = fm.split('\n');
|
|
151
|
+
let inBlock = false;
|
|
152
|
+
for (const line of lines) {
|
|
153
|
+
if (!inBlock) {
|
|
154
|
+
if (/^deskwork:\s*$/.test(line))
|
|
155
|
+
inBlock = true;
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
// Exit the block when we hit a non-indented, non-empty line.
|
|
159
|
+
if (line.length > 0 && !/^\s/.test(line))
|
|
160
|
+
break;
|
|
161
|
+
const m = line.match(/^\s+stage:\s*([^\s#]+)/);
|
|
162
|
+
if (m)
|
|
163
|
+
return m[1];
|
|
164
|
+
}
|
|
165
|
+
return undefined;
|
|
166
|
+
}
|
|
167
|
+
async function fileExists(path) {
|
|
168
|
+
try {
|
|
169
|
+
await stat(path);
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
async function validateFrontmatterSidecar(projectRoot) {
|
|
177
|
+
const failures = [];
|
|
178
|
+
const sidecars = await loadSidecars(projectRoot);
|
|
179
|
+
for (const { entry, path: sidecarPath } of sidecars) {
|
|
180
|
+
const artifactPath = artifactPathForStage(projectRoot, entry.slug, entry.currentStage);
|
|
181
|
+
if (!artifactPath)
|
|
182
|
+
continue;
|
|
183
|
+
if (!(await fileExists(artifactPath)))
|
|
184
|
+
continue; // file-presence handles missing artifacts
|
|
185
|
+
const md = await readFile(artifactPath, 'utf8');
|
|
186
|
+
const fmStage = extractDeskworkStage(md);
|
|
187
|
+
if (fmStage === undefined)
|
|
188
|
+
continue;
|
|
189
|
+
if (fmStage !== entry.currentStage) {
|
|
190
|
+
failures.push({
|
|
191
|
+
category: 'frontmatter-sidecar',
|
|
192
|
+
message: `frontmatter deskwork.stage=${fmStage} at ${artifactPath} does not match sidecar currentStage=${entry.currentStage}`,
|
|
193
|
+
entryId: entry.uuid,
|
|
194
|
+
path: sidecarPath,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return failures;
|
|
199
|
+
}
|
|
200
|
+
async function validateJournalSidecar(projectRoot) {
|
|
201
|
+
const failures = [];
|
|
202
|
+
const sidecars = await loadSidecars(projectRoot);
|
|
203
|
+
for (const { entry, path } of sidecars) {
|
|
204
|
+
const events = await readJournalEvents(projectRoot, { entryId: entry.uuid });
|
|
205
|
+
// readJournalEvents sorts ascending by `at`, so the latest is at the end.
|
|
206
|
+
const stageTransitions = events.filter((e) => e.kind === 'stage-transition');
|
|
207
|
+
const latestStageTransition = stageTransitions.at(-1);
|
|
208
|
+
if (latestStageTransition && latestStageTransition.kind === 'stage-transition') {
|
|
209
|
+
if (latestStageTransition.to !== entry.currentStage) {
|
|
210
|
+
failures.push({
|
|
211
|
+
category: 'journal-sidecar',
|
|
212
|
+
message: `latest stage-transition.to=${latestStageTransition.to} does not match sidecar currentStage=${entry.currentStage}`,
|
|
213
|
+
entryId: entry.uuid,
|
|
214
|
+
path,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
const reviewChanges = events.filter((e) => e.kind === 'review-state-change');
|
|
219
|
+
const latestReview = reviewChanges.at(-1);
|
|
220
|
+
if (latestReview && latestReview.kind === 'review-state-change') {
|
|
221
|
+
const sidecarReview = entry.reviewState ?? null;
|
|
222
|
+
if (latestReview.to !== sidecarReview) {
|
|
223
|
+
failures.push({
|
|
224
|
+
category: 'journal-sidecar',
|
|
225
|
+
message: `latest review-state-change.to=${String(latestReview.to)} does not match sidecar reviewState=${String(sidecarReview)}`,
|
|
226
|
+
entryId: entry.uuid,
|
|
227
|
+
path,
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return failures;
|
|
233
|
+
}
|
|
234
|
+
async function validateIterationHistory(projectRoot) {
|
|
235
|
+
const failures = [];
|
|
236
|
+
const sidecars = await loadSidecars(projectRoot);
|
|
237
|
+
for (const { entry, path } of sidecars) {
|
|
238
|
+
const stages = Object.keys(entry.iterationByStage);
|
|
239
|
+
// Migration tolerance: a sidecar with no recorded iteration counts is
|
|
240
|
+
// exempt from journal-vs-sidecar checks (legacy history may exist in the
|
|
241
|
+
// journal that simply hasn't been backfilled into iterationByStage).
|
|
242
|
+
if (stages.length === 0)
|
|
243
|
+
continue;
|
|
244
|
+
const events = await readJournalEvents(projectRoot, {
|
|
245
|
+
entryId: entry.uuid,
|
|
246
|
+
kinds: ['iteration'],
|
|
247
|
+
});
|
|
248
|
+
// Count iterations per stage in the journal.
|
|
249
|
+
const journalCount = {};
|
|
250
|
+
for (const e of events) {
|
|
251
|
+
if (e.kind !== 'iteration')
|
|
252
|
+
continue;
|
|
253
|
+
journalCount[e.stage] = (journalCount[e.stage] ?? 0) + 1;
|
|
254
|
+
}
|
|
255
|
+
// Compare sidecar counts to journal counts for every stage we know about
|
|
256
|
+
// (union of sidecar-recorded stages and journal-witnessed stages).
|
|
257
|
+
const allStages = new Set([...stages, ...Object.keys(journalCount)]);
|
|
258
|
+
for (const stage of allStages) {
|
|
259
|
+
const sidecarN = entry.iterationByStage[stage] ?? 0;
|
|
260
|
+
const journalN = journalCount[stage] ?? 0;
|
|
261
|
+
if (sidecarN === 0)
|
|
262
|
+
continue; // migration tolerance: only flag stages the sidecar tracks
|
|
263
|
+
if (sidecarN !== journalN) {
|
|
264
|
+
failures.push({
|
|
265
|
+
category: 'iteration-history',
|
|
266
|
+
message: `iterationByStage[${stage}]=${sidecarN} but journal has ${journalN} iteration event(s) for that stage`,
|
|
267
|
+
entryId: entry.uuid,
|
|
268
|
+
path,
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return failures;
|
|
274
|
+
}
|
|
275
|
+
async function validateFilePresence(projectRoot) {
|
|
276
|
+
const failures = [];
|
|
277
|
+
const sidecars = await loadSidecars(projectRoot);
|
|
278
|
+
for (const { entry, path } of sidecars) {
|
|
279
|
+
const artifactPath = artifactPathForStage(projectRoot, entry.slug, entry.currentStage);
|
|
280
|
+
if (!artifactPath)
|
|
281
|
+
continue;
|
|
282
|
+
if (!(await fileExists(artifactPath))) {
|
|
283
|
+
failures.push({
|
|
284
|
+
category: 'file-presence',
|
|
285
|
+
message: `sidecar currentStage=${entry.currentStage} requires artifact at ${artifactPath} but the file is missing`,
|
|
286
|
+
entryId: entry.uuid,
|
|
287
|
+
path,
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return failures;
|
|
292
|
+
}
|
|
293
|
+
async function validateStageInvariants(projectRoot) {
|
|
294
|
+
const failures = [];
|
|
295
|
+
const sidecars = await loadSidecars(projectRoot);
|
|
296
|
+
for (const { entry, path } of sidecars) {
|
|
297
|
+
// Off-pipeline stages (Blocked, Cancelled) MUST record priorStage so the
|
|
298
|
+
// editor knows where to send the entry on resume.
|
|
299
|
+
if (isOffPipelineStage(entry.currentStage)) {
|
|
300
|
+
if (!entry.priorStage) {
|
|
301
|
+
failures.push({
|
|
302
|
+
category: 'stage-invariants',
|
|
303
|
+
message: `currentStage=${entry.currentStage} requires priorStage to be set`,
|
|
304
|
+
entryId: entry.uuid,
|
|
305
|
+
path,
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
// Pipeline-stage entries MUST NOT carry a priorStage — that field is for
|
|
310
|
+
// off-pipeline entries to remember where they paused.
|
|
311
|
+
if (isLinearPipelineStage(entry.currentStage) && entry.priorStage !== undefined) {
|
|
312
|
+
failures.push({
|
|
313
|
+
category: 'stage-invariants',
|
|
314
|
+
message: `pipeline-stage entry (currentStage=${entry.currentStage}) must not have priorStage set (got ${entry.priorStage})`,
|
|
315
|
+
entryId: entry.uuid,
|
|
316
|
+
path,
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
// Published is frozen — no further iterations are allowed.
|
|
320
|
+
const publishedIters = entry.iterationByStage.Published ?? 0;
|
|
321
|
+
if (publishedIters > 1) {
|
|
322
|
+
failures.push({
|
|
323
|
+
category: 'stage-invariants',
|
|
324
|
+
message: `iterationByStage.Published=${publishedIters} but Published is frozen (max 1)`,
|
|
325
|
+
entryId: entry.uuid,
|
|
326
|
+
path,
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return failures;
|
|
331
|
+
}
|
|
332
|
+
async function validateCrossEntry(projectRoot) {
|
|
333
|
+
const failures = [];
|
|
334
|
+
const sidecars = await loadSidecars(projectRoot);
|
|
335
|
+
// UUID-filename mismatch: the canonical name is `<uuid>.json`. If the body's
|
|
336
|
+
// uuid field doesn't match the filename, downstream lookups break.
|
|
337
|
+
for (const { filename, path, entry } of sidecars) {
|
|
338
|
+
const expectedUuid = filename.replace(/\.json$/, '');
|
|
339
|
+
if (entry.uuid !== expectedUuid) {
|
|
340
|
+
failures.push({
|
|
341
|
+
category: 'cross-entry',
|
|
342
|
+
message: `sidecar filename ${filename} does not match body uuid ${entry.uuid}`,
|
|
343
|
+
entryId: entry.uuid,
|
|
344
|
+
path,
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
// Slug uniqueness: each slug should map to at most one entry. Group sidecars
|
|
349
|
+
// by slug and emit one failure per duplicate cluster.
|
|
350
|
+
const bySlug = new Map();
|
|
351
|
+
for (const sc of sidecars) {
|
|
352
|
+
const list = bySlug.get(sc.entry.slug) ?? [];
|
|
353
|
+
list.push(sc);
|
|
354
|
+
bySlug.set(sc.entry.slug, list);
|
|
355
|
+
}
|
|
356
|
+
for (const [slug, group] of bySlug) {
|
|
357
|
+
if (group.length < 2)
|
|
358
|
+
continue;
|
|
359
|
+
const uuids = group.map((g) => g.entry.uuid).join(', ');
|
|
360
|
+
for (const sc of group) {
|
|
361
|
+
failures.push({
|
|
362
|
+
category: 'cross-entry',
|
|
363
|
+
message: `slug "${slug}" is shared by ${group.length} sidecars (uuids: ${uuids})`,
|
|
364
|
+
entryId: sc.entry.uuid,
|
|
365
|
+
path: sc.path,
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
// UUID uniqueness across body fields. Same body uuid in two different files
|
|
370
|
+
// is unusual (the filename naming convention prevents the obvious case) but
|
|
371
|
+
// worth catching when it happens.
|
|
372
|
+
const byUuid = new Map();
|
|
373
|
+
for (const sc of sidecars) {
|
|
374
|
+
const list = byUuid.get(sc.entry.uuid) ?? [];
|
|
375
|
+
list.push(sc);
|
|
376
|
+
byUuid.set(sc.entry.uuid, list);
|
|
377
|
+
}
|
|
378
|
+
for (const [uuid, group] of byUuid) {
|
|
379
|
+
if (group.length < 2)
|
|
380
|
+
continue;
|
|
381
|
+
const filenames = group.map((g) => g.filename).join(', ');
|
|
382
|
+
for (const sc of group) {
|
|
383
|
+
failures.push({
|
|
384
|
+
category: 'cross-entry',
|
|
385
|
+
message: `uuid ${uuid} appears in ${group.length} sidecar bodies (files: ${filenames})`,
|
|
386
|
+
entryId: uuid,
|
|
387
|
+
path: sc.path,
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return failures;
|
|
392
|
+
}
|
|
393
|
+
export async function validateAll(projectRoot) {
|
|
394
|
+
const failures = [];
|
|
395
|
+
failures.push(...(await validateSchema(projectRoot)));
|
|
396
|
+
failures.push(...(await validateCalendarSidecar(projectRoot)));
|
|
397
|
+
failures.push(...(await validateFrontmatterSidecar(projectRoot)));
|
|
398
|
+
failures.push(...(await validateJournalSidecar(projectRoot)));
|
|
399
|
+
failures.push(...(await validateIterationHistory(projectRoot)));
|
|
400
|
+
failures.push(...(await validateFilePresence(projectRoot)));
|
|
401
|
+
failures.push(...(await validateStageInvariants(projectRoot)));
|
|
402
|
+
failures.push(...(await validateCrossEntry(projectRoot)));
|
|
403
|
+
return { failures };
|
|
404
|
+
}
|
|
405
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/doctor/validate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Stage } from '../schema/entry.ts';
|
|
2
|
+
interface IterateOptions {
|
|
3
|
+
uuid: string;
|
|
4
|
+
}
|
|
5
|
+
interface IterateResult {
|
|
6
|
+
entryId: string;
|
|
7
|
+
stage: Stage;
|
|
8
|
+
version: number;
|
|
9
|
+
reviewState: 'in-review';
|
|
10
|
+
}
|
|
11
|
+
export declare function iterateEntry(projectRoot: string, opts: IterateOptions): Promise<IterateResult>;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=iterate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"iterate.d.ts","sourceRoot":"","sources":["../../src/iterate/iterate.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAS,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAEvD,UAAU,cAAc;IACtB,IAAI,EAAE,MAAM,CAAC;CAEd;AAED,UAAU,aAAa;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,KAAK,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,WAAW,CAAC;CAC1B;AAaD,wBAAsB,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CA6DpG"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { readSidecar } from "../sidecar/read.js";
|
|
4
|
+
import { writeSidecar } from "../sidecar/write.js";
|
|
5
|
+
import { appendJournalEvent } from "../journal/append.js";
|
|
6
|
+
const STAGE_ARTIFACT_PATH = {
|
|
7
|
+
Ideas: (slug, contentDir) => join(contentDir, slug, 'scrapbook', 'idea.md'),
|
|
8
|
+
Planned: (slug, contentDir) => join(contentDir, slug, 'scrapbook', 'plan.md'),
|
|
9
|
+
Outlining: (slug, contentDir) => join(contentDir, slug, 'scrapbook', 'outline.md'),
|
|
10
|
+
Drafting: (slug, contentDir) => join(contentDir, slug, 'index.md'),
|
|
11
|
+
Final: (slug, contentDir) => join(contentDir, slug, 'index.md'),
|
|
12
|
+
Published: null,
|
|
13
|
+
Blocked: null,
|
|
14
|
+
Cancelled: null,
|
|
15
|
+
};
|
|
16
|
+
export async function iterateEntry(projectRoot, opts) {
|
|
17
|
+
const sidecar = await readSidecar(projectRoot, opts.uuid);
|
|
18
|
+
if (sidecar.currentStage === 'Published') {
|
|
19
|
+
throw new Error('Cannot iterate: Published entries are frozen.');
|
|
20
|
+
}
|
|
21
|
+
if (sidecar.currentStage === 'Blocked' || sidecar.currentStage === 'Cancelled') {
|
|
22
|
+
throw new Error(`Cannot iterate: entry is ${sidecar.currentStage}; induct it back into the pipeline first.`);
|
|
23
|
+
}
|
|
24
|
+
const pathFn = STAGE_ARTIFACT_PATH[sidecar.currentStage];
|
|
25
|
+
if (!pathFn) {
|
|
26
|
+
throw new Error(`Cannot iterate at stage ${sidecar.currentStage}: no artifact path defined.`);
|
|
27
|
+
}
|
|
28
|
+
const contentDir = join(projectRoot, 'docs'); // FIXME: read from .deskwork/config.json
|
|
29
|
+
const artifactPath = pathFn(sidecar.slug, contentDir);
|
|
30
|
+
const markdown = await readFile(artifactPath, 'utf8');
|
|
31
|
+
const priorVersion = sidecar.iterationByStage[sidecar.currentStage] ?? 0;
|
|
32
|
+
const newVersion = priorVersion + 1;
|
|
33
|
+
const at = new Date().toISOString();
|
|
34
|
+
// Emit journal event first; doctor reconciles drift if we crash mid-operation
|
|
35
|
+
await appendJournalEvent(projectRoot, {
|
|
36
|
+
kind: 'iteration',
|
|
37
|
+
at,
|
|
38
|
+
entryId: sidecar.uuid,
|
|
39
|
+
stage: sidecar.currentStage,
|
|
40
|
+
version: newVersion,
|
|
41
|
+
markdown,
|
|
42
|
+
});
|
|
43
|
+
// Update sidecar
|
|
44
|
+
const updated = {
|
|
45
|
+
...sidecar,
|
|
46
|
+
iterationByStage: { ...sidecar.iterationByStage, [sidecar.currentStage]: newVersion },
|
|
47
|
+
reviewState: 'in-review',
|
|
48
|
+
updatedAt: at,
|
|
49
|
+
};
|
|
50
|
+
await writeSidecar(projectRoot, updated);
|
|
51
|
+
// Emit review-state-change if state actually changed
|
|
52
|
+
if (sidecar.reviewState !== 'in-review') {
|
|
53
|
+
await appendJournalEvent(projectRoot, {
|
|
54
|
+
kind: 'review-state-change',
|
|
55
|
+
at,
|
|
56
|
+
entryId: sidecar.uuid,
|
|
57
|
+
stage: sidecar.currentStage,
|
|
58
|
+
from: sidecar.reviewState ?? null,
|
|
59
|
+
to: 'in-review',
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
entryId: sidecar.uuid,
|
|
64
|
+
stage: sidecar.currentStage,
|
|
65
|
+
version: newVersion,
|
|
66
|
+
reviewState: 'in-review',
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=iterate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"iterate.js","sourceRoot":"","sources":["../../src/iterate/iterate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAe1D,MAAM,mBAAmB,GAAyE;IAChG,KAAK,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,CAAC;IAC3E,OAAO,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,CAAC;IAC7E,SAAS,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,YAAY,CAAC;IAClF,QAAQ,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,EAAE,UAAU,CAAC;IAClE,KAAK,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,EAAE,UAAU,CAAC;IAC/D,SAAS,EAAE,IAAI;IACf,OAAO,EAAE,IAAI;IACb,SAAS,EAAE,IAAI;CAChB,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,WAAmB,EAAE,IAAoB;IAC1E,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IAE1D,IAAI,OAAO,CAAC,YAAY,KAAK,WAAW,EAAE,CAAC;QACzC,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;IACnE,CAAC;IACD,IAAI,OAAO,CAAC,YAAY,KAAK,SAAS,IAAI,OAAO,CAAC,YAAY,KAAK,WAAW,EAAE,CAAC;QAC/E,MAAM,IAAI,KAAK,CAAC,4BAA4B,OAAO,CAAC,YAAY,2CAA2C,CAAC,CAAC;IAC/G,CAAC;IAED,MAAM,MAAM,GAAG,mBAAmB,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IACzD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,2BAA2B,OAAO,CAAC,YAAY,6BAA6B,CAAC,CAAC;IAChG,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAE,yCAAyC;IACxF,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IACtD,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;IAEtD,MAAM,YAAY,GAAG,OAAO,CAAC,gBAAgB,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IACzE,MAAM,UAAU,GAAG,YAAY,GAAG,CAAC,CAAC;IAEpC,MAAM,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAEpC,8EAA8E;IAC9E,MAAM,kBAAkB,CAAC,WAAW,EAAE;QACpC,IAAI,EAAE,WAAW;QACjB,EAAE;QACF,OAAO,EAAE,OAAO,CAAC,IAAI;QACrB,KAAK,EAAE,OAAO,CAAC,YAAY;QAC3B,OAAO,EAAE,UAAU;QACnB,QAAQ;KACT,CAAC,CAAC;IAEH,iBAAiB;IACjB,MAAM,OAAO,GAAU;QACrB,GAAG,OAAO;QACV,gBAAgB,EAAE,EAAE,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,UAAU,EAAE;QACrF,WAAW,EAAE,WAAW;QACxB,SAAS,EAAE,EAAE;KACd,CAAC;IACF,MAAM,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAEzC,qDAAqD;IACrD,IAAI,OAAO,CAAC,WAAW,KAAK,WAAW,EAAE,CAAC;QACxC,MAAM,kBAAkB,CAAC,WAAW,EAAE;YACpC,IAAI,EAAE,qBAAqB;YAC3B,EAAE;YACF,OAAO,EAAE,OAAO,CAAC,IAAI;YACrB,KAAK,EAAE,OAAO,CAAC,YAAY;YAC3B,IAAI,EAAE,OAAO,CAAC,WAAW,IAAI,IAAI;YACjC,EAAE,EAAE,WAAW;SAChB,CAAC,CAAC;IACL,CAAC;IAED,OAAO;QACL,OAAO,EAAE,OAAO,CAAC,IAAI;QACrB,KAAK,EAAE,OAAO,CAAC,YAAY;QAC3B,OAAO,EAAE,UAAU;QACnB,WAAW,EAAE,WAAW;KACzB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"append.d.ts","sourceRoot":"","sources":["../../src/journal/append.ts"],"names":[],"mappings":"AAGA,OAAO,EAAsB,KAAK,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAEpF,wBAAsB,kBAAkB,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAYlG"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
4
|
+
import { JournalEventSchema } from "../schema/journal-events.js";
|
|
5
|
+
export async function appendJournalEvent(projectRoot, event) {
|
|
6
|
+
const result = JournalEventSchema.safeParse(event);
|
|
7
|
+
if (!result.success) {
|
|
8
|
+
throw new Error(`appendJournalEvent refused: schema invalid: ${result.error.message}`);
|
|
9
|
+
}
|
|
10
|
+
const dir = join(projectRoot, '.deskwork', 'review-journal', 'history');
|
|
11
|
+
await mkdir(dir, { recursive: true });
|
|
12
|
+
const eventId = randomUUID();
|
|
13
|
+
const tsKey = event.at.replace(/[:.]/g, '-');
|
|
14
|
+
const path = join(dir, `${tsKey}-${eventId}.json`);
|
|
15
|
+
await writeFile(path, JSON.stringify(event, null, 2));
|
|
16
|
+
return path;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=append.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"append.js","sourceRoot":"","sources":["../../src/journal/append.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAqB,MAAM,6BAA6B,CAAC;AAEpF,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,WAAmB,EAAE,KAAmB;IAC/E,MAAM,MAAM,GAAG,kBAAkB,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACnD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,+CAA+C,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;IACzF,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,EAAE,WAAW,EAAE,gBAAgB,EAAE,SAAS,CAAC,CAAC;IACxE,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,MAAM,KAAK,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IAC7C,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,OAAO,OAAO,CAAC,CAAC;IACnD,MAAM,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACtD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/journal/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/journal/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type JournalEvent } from '../schema/journal-events.ts';
|
|
2
|
+
interface ReadOptions {
|
|
3
|
+
entryId?: string;
|
|
4
|
+
stage?: string;
|
|
5
|
+
kinds?: string[];
|
|
6
|
+
}
|
|
7
|
+
export declare function readJournalEvents(projectRoot: string, opts?: ReadOptions): Promise<JournalEvent[]>;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=read.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../src/journal/read.ts"],"names":[],"mappings":"AAEA,OAAO,EAAsB,KAAK,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAEpF,UAAU,WAAW;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,wBAAsB,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CA0B5G"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { readdir, readFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { JournalEventSchema } from "../schema/journal-events.js";
|
|
4
|
+
export async function readJournalEvents(projectRoot, opts = {}) {
|
|
5
|
+
const dir = join(projectRoot, '.deskwork', 'review-journal', 'history');
|
|
6
|
+
let names;
|
|
7
|
+
try {
|
|
8
|
+
names = await readdir(dir);
|
|
9
|
+
}
|
|
10
|
+
catch (err) {
|
|
11
|
+
const error = err;
|
|
12
|
+
if (error.code === 'ENOENT')
|
|
13
|
+
return [];
|
|
14
|
+
throw err;
|
|
15
|
+
}
|
|
16
|
+
const events = [];
|
|
17
|
+
for (const name of names.filter(n => n.endsWith('.json'))) {
|
|
18
|
+
const raw = await readFile(join(dir, name), 'utf8');
|
|
19
|
+
let json;
|
|
20
|
+
try {
|
|
21
|
+
json = JSON.parse(raw);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
const parsed = JournalEventSchema.safeParse(json);
|
|
27
|
+
if (!parsed.success)
|
|
28
|
+
continue;
|
|
29
|
+
const e = parsed.data;
|
|
30
|
+
if (opts.entryId && e.entryId !== opts.entryId)
|
|
31
|
+
continue;
|
|
32
|
+
if (opts.stage && 'stage' in e && e.stage !== opts.stage)
|
|
33
|
+
continue;
|
|
34
|
+
if (opts.kinds && !opts.kinds.includes(e.kind))
|
|
35
|
+
continue;
|
|
36
|
+
events.push(e);
|
|
37
|
+
}
|
|
38
|
+
events.sort((a, b) => a.at.localeCompare(b.at));
|
|
39
|
+
return events;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=read.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read.js","sourceRoot":"","sources":["../../src/journal/read.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,kBAAkB,EAAqB,MAAM,6BAA6B,CAAC;AAQpF,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,WAAmB,EAAE,OAAoB,EAAE;IACjF,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,EAAE,WAAW,EAAE,gBAAgB,EAAE,SAAS,CAAC,CAAC;IACxE,IAAI,KAAe,CAAC;IACpB,IAAI,CAAC;QACH,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,KAAK,GAAG,GAA4B,CAAC;QAC3C,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,EAAE,CAAC;QACvC,MAAM,GAAG,CAAC;IACZ,CAAC;IAED,MAAM,MAAM,GAAmB,EAAE,CAAC;IAClC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;QAC1D,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;QACpD,IAAI,IAAa,CAAC;QAClB,IAAI,CAAC;YAAC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC;YAAC,SAAS;QAAC,CAAC;QACnD,MAAM,MAAM,GAAG,kBAAkB,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAClD,IAAI,CAAC,MAAM,CAAC,OAAO;YAAE,SAAS;QAC9B,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC;QACtB,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,KAAK,IAAI,CAAC,OAAO;YAAE,SAAS;QACzD,IAAI,IAAI,CAAC,KAAK,IAAI,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK;YAAE,SAAS;QACnE,IAAI,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;YAAE,SAAS;QACzD,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;IACD,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAChD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|