strikethroo 3.0.1 → 3.1.0
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/README.md +20 -5
- package/dist/cli.js +19 -128
- package/dist/cli.js.map +1 -1
- package/dist/serve/archive.d.ts +43 -0
- package/dist/serve/archive.d.ts.map +1 -0
- package/dist/serve/archive.js +124 -0
- package/dist/serve/archive.js.map +1 -0
- package/dist/serve/derivation.d.ts +78 -0
- package/dist/serve/derivation.d.ts.map +1 -0
- package/dist/serve/derivation.js +237 -0
- package/dist/serve/derivation.js.map +1 -0
- package/dist/serve/events.d.ts +41 -0
- package/dist/serve/events.d.ts.map +1 -0
- package/dist/serve/events.js +91 -0
- package/dist/serve/events.js.map +1 -0
- package/dist/serve/markdown.d.ts +75 -0
- package/dist/serve/markdown.d.ts.map +1 -0
- package/dist/serve/markdown.js +221 -0
- package/dist/serve/markdown.js.map +1 -0
- package/dist/serve/root.d.ts +48 -0
- package/dist/serve/root.d.ts.map +1 -0
- package/dist/serve/root.js +110 -0
- package/dist/serve/root.js.map +1 -0
- package/dist/serve/self-review.d.ts +70 -0
- package/dist/serve/self-review.d.ts.map +1 -0
- package/dist/serve/self-review.js +164 -0
- package/dist/serve/self-review.js.map +1 -0
- package/dist/serve/server.d.ts +70 -0
- package/dist/serve/server.d.ts.map +1 -0
- package/dist/serve/server.js +368 -0
- package/dist/serve/server.js.map +1 -0
- package/dist/serve/watcher.d.ts +32 -0
- package/dist/serve/watcher.d.ts.map +1 -0
- package/dist/serve/watcher.js +99 -0
- package/dist/serve/watcher.js.map +1 -0
- package/dist/serve/workspace-model.d.ts +80 -0
- package/dist/serve/workspace-model.d.ts.map +1 -0
- package/dist/serve/workspace-model.js +176 -0
- package/dist/serve/workspace-model.js.map +1 -0
- package/dist/skill-scripts/shared/frontmatter.d.ts +2 -0
- package/dist/skill-scripts/shared/frontmatter.d.ts.map +1 -0
- package/dist/skill-scripts/shared/frontmatter.js +37 -0
- package/dist/skill-scripts/shared/frontmatter.js.map +1 -0
- package/dist/skill-scripts/shared/plan-scan.d.ts +10 -0
- package/dist/skill-scripts/shared/plan-scan.d.ts.map +1 -0
- package/dist/skill-scripts/shared/plan-scan.js +91 -0
- package/dist/skill-scripts/shared/plan-scan.js.map +1 -0
- package/dist/skill-scripts/shared/root.d.ts +2 -0
- package/dist/skill-scripts/shared/root.d.ts.map +1 -0
- package/dist/skill-scripts/shared/root.js +104 -0
- package/dist/skill-scripts/shared/root.js.map +1 -0
- package/dist-web/assets/Fraunces-Italic-VariableFont_SOFT_WONK_opsz_wght-CuXX_kIb.ttf +0 -0
- package/dist-web/assets/Fraunces-VariableFont_SOFT_WONK_opsz_wght-DLWfSgHL.ttf +0 -0
- package/dist-web/assets/Outfit-VariableFont_wght-B6lQlNBM.ttf +0 -0
- package/dist-web/assets/SFMonoBold-DXHgGUoo.otf +0 -0
- package/dist-web/assets/SFMonoBoldItalic-CdGEImHi.otf +0 -0
- package/dist-web/assets/SFMonoHeavy-DL92f7rT.otf +0 -0
- package/dist-web/assets/SFMonoHeavyItalic-DnE00LFH.otf +0 -0
- package/dist-web/assets/SFMonoLight-CaMC1gg-.otf +0 -0
- package/dist-web/assets/SFMonoLightItalic-DG3Owzn7.otf +0 -0
- package/dist-web/assets/SFMonoMedium-ypRDCKSo.otf +0 -0
- package/dist-web/assets/SFMonoMediumItalic-DkJk-c-x.otf +0 -0
- package/dist-web/assets/SFMonoRegular-77Uh1IPk.otf +0 -0
- package/dist-web/assets/SFMonoRegularItalic-Cn01bACm.otf +0 -0
- package/dist-web/assets/SFMonoSemibold-CvhtzcyM.otf +0 -0
- package/dist-web/assets/SFMonoSemiboldItalic-D75NMH13.otf +0 -0
- package/dist-web/assets/arc-BWWIDtzc.js +1 -0
- package/dist-web/assets/architectureDiagram-3BPJPVTR-DeYxmq4e.js +36 -0
- package/dist-web/assets/blockDiagram-GPEHLZMM-DR85OQGh.js +132 -0
- package/dist-web/assets/c4Diagram-AAUBKEIU-B5p4vlmU.js +10 -0
- package/dist-web/assets/channel-BaP08-ph.js +1 -0
- package/dist-web/assets/chunk-2J33WTMH-duXs_Bbu.js +1 -0
- package/dist-web/assets/chunk-4BX2VUAB-DR1619KE.js +1 -0
- package/dist-web/assets/chunk-55IACEB6-UacLKom4.js +1 -0
- package/dist-web/assets/chunk-727SXJPM-DW2a_NI7.js +206 -0
- package/dist-web/assets/chunk-AQP2D5EJ-Bu5jn-_L.js +231 -0
- package/dist-web/assets/chunk-FMBD7UC4-eJsExyuh.js +15 -0
- package/dist-web/assets/chunk-ND2GUHAM-BqKmGd94.js +1 -0
- package/dist-web/assets/chunk-QZHKN3VN--ynTiOCz.js +1 -0
- package/dist-web/assets/classDiagram-4FO5ZUOK-V5dHIfdR.js +1 -0
- package/dist-web/assets/classDiagram-v2-Q7XG4LA2-V5dHIfdR.js +1 -0
- package/dist-web/assets/cose-bilkent-S5V4N54A-Ccz3r4S1.js +1 -0
- package/dist-web/assets/cytoscape.esm-BHYC38rz.js +331 -0
- package/dist-web/assets/dagre-BM42HDAG-BESSLbjB.js +4 -0
- package/dist-web/assets/defaultLocale-CrowFXzY.js +1 -0
- package/dist-web/assets/diagram-2AECGRRQ-BB5cYRbY.js +43 -0
- package/dist-web/assets/diagram-5GNKFQAL-3lmcZFDS.js +10 -0
- package/dist-web/assets/diagram-KO2AKTUF-DUMp202y.js +3 -0
- package/dist-web/assets/diagram-LMA3HP47-DCGXhLm1.js +24 -0
- package/dist-web/assets/diagram-OG6HWLK6-D247gj5y.js +24 -0
- package/dist-web/assets/erDiagram-TEJ5UH35-GI1vhsr2.js +85 -0
- package/dist-web/assets/flowDiagram-I6XJVG4X-iqUPWZuf.js +162 -0
- package/dist-web/assets/ganttDiagram-6RSMTGT7-DfG1X-Pe.js +292 -0
- package/dist-web/assets/gitGraphDiagram-PVQCEYII-B2MrV3y6.js +106 -0
- package/dist-web/assets/graph-Dqkl27Ch.js +1 -0
- package/dist-web/assets/index-Bm5I46eC.css +1 -0
- package/dist-web/assets/index-CvfrFREc.js +125 -0
- package/dist-web/assets/infoDiagram-5YYISTIA-DGqU5K4a.js +2 -0
- package/dist-web/assets/init-Gi6I4Gst.js +1 -0
- package/dist-web/assets/ishikawaDiagram-YF4QCWOH-DrMH3jRf.js +70 -0
- package/dist-web/assets/journeyDiagram-JHISSGLW-BH4wiwsd.js +139 -0
- package/dist-web/assets/kanban-definition-UN3LZRKU-DEV_dEgE.js +89 -0
- package/dist-web/assets/katex-C5jXJg4s.js +257 -0
- package/dist-web/assets/layout-DO9UvE1P.js +1 -0
- package/dist-web/assets/linear-D81nAxzR.js +1 -0
- package/dist-web/assets/mermaid.core-BaP7clO2.js +307 -0
- package/dist-web/assets/mindmap-definition-RKZ34NQL-DM4iAZ4M.js +96 -0
- package/dist-web/assets/ordinal-Cboi1Yqb.js +1 -0
- package/dist-web/assets/pieDiagram-4H26LBE5-DinO6q5i.js +30 -0
- package/dist-web/assets/quadrantDiagram-W4KKPZXB-CYvCm7U6.js +7 -0
- package/dist-web/assets/requirementDiagram-4Y6WPE33-DXfPIJ5m.js +84 -0
- package/dist-web/assets/sankeyDiagram-5OEKKPKP-CfovFay4.js +40 -0
- package/dist-web/assets/sequenceDiagram-3UESZ5HK-BboUGmEB.js +162 -0
- package/dist-web/assets/stateDiagram-AJRCARHV-CsB2FtnQ.js +1 -0
- package/dist-web/assets/stateDiagram-v2-BHNVJYJU-D0D_KesS.js +1 -0
- package/dist-web/assets/timeline-definition-PNZ67QCA-Cma1yLcq.js +120 -0
- package/dist-web/assets/vennDiagram-CIIHVFJN-DhFGEgCQ.js +34 -0
- package/dist-web/assets/wardley-L42UT6IY-DYkCeynG.js +173 -0
- package/dist-web/assets/wardleyDiagram-YWT4CUSO-jogT61m_.js +78 -0
- package/dist-web/assets/xychartDiagram-2RQKCTM6-Cn3W0E3s.js +7 -0
- package/dist-web/favicon.svg +13 -0
- package/dist-web/index.html +30 -0
- package/package.json +28 -8
- package/templates/harness/skills/st-full-workflow/SKILL.md +2 -2
- package/templates/harness/skills/st-generate-tasks/SKILL.md +2 -2
- package/dist/plan-utils.d.ts +0 -35
- package/dist/plan-utils.d.ts.map +0 -1
- package/dist/plan-utils.js +0 -123
- package/dist/plan-utils.js.map +0 -1
- package/dist/plan.d.ts +0 -27
- package/dist/plan.d.ts.map +0 -1
- package/dist/plan.js +0 -279
- package/dist/plan.js.map +0 -1
- package/dist/status.d.ts +0 -73
- package/dist/status.d.ts.map +0 -1
- package/dist/status.js +0 -375
- package/dist/status.js.map +0 -1
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Per-plan derivation layer for the serve data layer.
|
|
4
|
+
*
|
|
5
|
+
* Scans a plan directory's `tasks/*.md` into structured task records, computes
|
|
6
|
+
* the derived plan lifecycle state (`drafted` -> `ready` -> `doing` -> `done`)
|
|
7
|
+
* with done/total counts, and resolves execution phases (from a blueprint
|
|
8
|
+
* document when present, otherwise inferred from task `group` + `dependencies`).
|
|
9
|
+
*
|
|
10
|
+
* Everything is synchronous and read-only: file reads only, no watches, no
|
|
11
|
+
* network, no writes.
|
|
12
|
+
*/
|
|
13
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
16
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
17
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
18
|
+
}
|
|
19
|
+
Object.defineProperty(o, k2, desc);
|
|
20
|
+
}) : (function(o, m, k, k2) {
|
|
21
|
+
if (k2 === undefined) k2 = k;
|
|
22
|
+
o[k2] = m[k];
|
|
23
|
+
}));
|
|
24
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
25
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
26
|
+
}) : function(o, v) {
|
|
27
|
+
o["default"] = v;
|
|
28
|
+
});
|
|
29
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
30
|
+
var ownKeys = function(o) {
|
|
31
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
32
|
+
var ar = [];
|
|
33
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
34
|
+
return ar;
|
|
35
|
+
};
|
|
36
|
+
return ownKeys(o);
|
|
37
|
+
};
|
|
38
|
+
return function (mod) {
|
|
39
|
+
if (mod && mod.__esModule) return mod;
|
|
40
|
+
var result = {};
|
|
41
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
42
|
+
__setModuleDefault(result, mod);
|
|
43
|
+
return result;
|
|
44
|
+
};
|
|
45
|
+
})();
|
|
46
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
|
+
exports.resolvePhases = exports.inferPhases = exports.parseBlueprintPhases = exports.deriveState = exports.scanTasks = void 0;
|
|
48
|
+
const fs = __importStar(require("fs"));
|
|
49
|
+
const path = __importStar(require("path"));
|
|
50
|
+
const markdown_1 = require("./markdown");
|
|
51
|
+
const STATUS_DONE = 'completed';
|
|
52
|
+
const STATUS_NOT_STARTED = 'pending';
|
|
53
|
+
/** Reads a plan directory's `tasks/*.md` into structured records. Missing dir -> []. */
|
|
54
|
+
const scanTasks = (planDir) => {
|
|
55
|
+
const tasksDir = path.join(planDir, 'tasks');
|
|
56
|
+
let entries;
|
|
57
|
+
try {
|
|
58
|
+
entries = fs.readdirSync(tasksDir, { withFileTypes: true });
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
return entries
|
|
64
|
+
.filter(e => e.isFile() && e.name.endsWith('.md'))
|
|
65
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
66
|
+
.flatMap(e => {
|
|
67
|
+
const filePath = path.join(tasksDir, e.name);
|
|
68
|
+
let content;
|
|
69
|
+
try {
|
|
70
|
+
content = fs.readFileSync(filePath, 'utf8');
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
const fm = (0, markdown_1.parseFrontmatter)(content);
|
|
76
|
+
const body = (0, markdown_1.extractBody)(content);
|
|
77
|
+
const dependencies = fm.dependencies
|
|
78
|
+
.map(d => (typeof d === 'number' ? d : parseInt(d, 10)))
|
|
79
|
+
.filter((d) => !Number.isNaN(d));
|
|
80
|
+
return [
|
|
81
|
+
{
|
|
82
|
+
id: fm.id,
|
|
83
|
+
name: (0, markdown_1.extractTitle)(body) ?? e.name.replace(/\.md$/, ''),
|
|
84
|
+
group: fm.group,
|
|
85
|
+
dependencies,
|
|
86
|
+
status: fm.status,
|
|
87
|
+
skills: fm.skills,
|
|
88
|
+
file: e.name,
|
|
89
|
+
body,
|
|
90
|
+
sections: (0, markdown_1.sectionBody)(body).sections,
|
|
91
|
+
},
|
|
92
|
+
];
|
|
93
|
+
});
|
|
94
|
+
};
|
|
95
|
+
exports.scanTasks = scanTasks;
|
|
96
|
+
/** Classifies a task status. Unknown/in-progress values count as "started". */
|
|
97
|
+
const classify = (status) => {
|
|
98
|
+
if (status === STATUS_DONE)
|
|
99
|
+
return 'done';
|
|
100
|
+
if (status === STATUS_NOT_STARTED)
|
|
101
|
+
return 'notStarted';
|
|
102
|
+
return 'started';
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* Computes the derived plan state and counts. Never throws on unknown statuses.
|
|
106
|
+
*
|
|
107
|
+
* - empty list -> `drafted`
|
|
108
|
+
* - tasks present, none started or done -> `ready`
|
|
109
|
+
* - all done -> `done`
|
|
110
|
+
* - otherwise -> `doing`
|
|
111
|
+
*/
|
|
112
|
+
const deriveState = (tasks) => {
|
|
113
|
+
const total = tasks.length;
|
|
114
|
+
if (total === 0) {
|
|
115
|
+
return { state: 'drafted', done: 0, total: 0 };
|
|
116
|
+
}
|
|
117
|
+
let done = 0;
|
|
118
|
+
let started = 0;
|
|
119
|
+
for (const task of tasks) {
|
|
120
|
+
const klass = classify(task.status);
|
|
121
|
+
if (klass === 'done') {
|
|
122
|
+
done += 1;
|
|
123
|
+
started += 1;
|
|
124
|
+
}
|
|
125
|
+
else if (klass === 'started') {
|
|
126
|
+
started += 1;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
let state;
|
|
130
|
+
if (done === total) {
|
|
131
|
+
state = 'done';
|
|
132
|
+
}
|
|
133
|
+
else if (started === 0) {
|
|
134
|
+
state = 'ready';
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
state = 'doing';
|
|
138
|
+
}
|
|
139
|
+
return { state, done, total };
|
|
140
|
+
};
|
|
141
|
+
exports.deriveState = deriveState;
|
|
142
|
+
const BLUEPRINT_SECTION_RE = /^##[ \t]+Execution Blueprint[ \t]*$/m;
|
|
143
|
+
const PHASE_HEADING_RE = /^###[ \t]+(?:✅[ \t]*)?Phase[ \t]+(\d+)[ \t]*:?[ \t]*(.*?)[ \t]*$/gm;
|
|
144
|
+
const TASK_REF_RE = /Task[ \t]+0*(\d+)/i;
|
|
145
|
+
/**
|
|
146
|
+
* Parses phases from an "## Execution Blueprint" section if the plan body
|
|
147
|
+
* contains one. Returns undefined when no blueprint section is present.
|
|
148
|
+
*/
|
|
149
|
+
const parseBlueprintPhases = (planBody) => {
|
|
150
|
+
const sectionMatch = planBody.match(BLUEPRINT_SECTION_RE);
|
|
151
|
+
if (!sectionMatch || sectionMatch.index === undefined)
|
|
152
|
+
return undefined;
|
|
153
|
+
const blueprint = planBody.slice(sectionMatch.index);
|
|
154
|
+
const headings = [];
|
|
155
|
+
PHASE_HEADING_RE.lastIndex = 0;
|
|
156
|
+
let m;
|
|
157
|
+
while ((m = PHASE_HEADING_RE.exec(blueprint)) !== null) {
|
|
158
|
+
headings.push({
|
|
159
|
+
index: m.index,
|
|
160
|
+
afterHeading: m.index + m[0].length,
|
|
161
|
+
name: (m[2] ?? '').trim(),
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
if (headings.length === 0)
|
|
165
|
+
return undefined;
|
|
166
|
+
const phases = [];
|
|
167
|
+
for (let i = 0; i < headings.length; i++) {
|
|
168
|
+
const current = headings[i];
|
|
169
|
+
const next = headings[i + 1];
|
|
170
|
+
const end = next ? next.index : blueprint.length;
|
|
171
|
+
const segment = blueprint.slice(current.afterHeading, end);
|
|
172
|
+
const taskIds = [];
|
|
173
|
+
for (const line of segment.split(/\r?\n/)) {
|
|
174
|
+
const trimmed = line.trim();
|
|
175
|
+
// Only consider bulleted task references to avoid matching prose.
|
|
176
|
+
if (!trimmed.startsWith('-') && !trimmed.startsWith('*'))
|
|
177
|
+
continue;
|
|
178
|
+
const ref = trimmed.match(TASK_REF_RE);
|
|
179
|
+
if (ref && ref[1] !== undefined) {
|
|
180
|
+
const id = parseInt(ref[1], 10);
|
|
181
|
+
if (!Number.isNaN(id) && !taskIds.includes(id))
|
|
182
|
+
taskIds.push(id);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
phases.push({
|
|
186
|
+
index: i + 1,
|
|
187
|
+
name: current.name.length > 0 ? current.name : undefined,
|
|
188
|
+
taskIds,
|
|
189
|
+
parallel: taskIds.length > 1,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
return phases;
|
|
193
|
+
};
|
|
194
|
+
exports.parseBlueprintPhases = parseBlueprintPhases;
|
|
195
|
+
/**
|
|
196
|
+
* Infers phases from tasks by grouping on satisfied dependencies. Tasks whose
|
|
197
|
+
* dependencies are all already emitted (or absent from the task set) form a
|
|
198
|
+
* phase; dependents fall into later phases. Best-effort and cycle-safe: if no
|
|
199
|
+
* progress is made, the remaining tasks are emitted as a final phase rather
|
|
200
|
+
* than looping forever. Never throws.
|
|
201
|
+
*/
|
|
202
|
+
const inferPhases = (tasks) => {
|
|
203
|
+
const withIds = tasks.filter((t) => typeof t.id === 'number');
|
|
204
|
+
if (withIds.length === 0)
|
|
205
|
+
return [];
|
|
206
|
+
const idSet = new Set(withIds.map(t => t.id));
|
|
207
|
+
const emitted = new Set();
|
|
208
|
+
const phases = [];
|
|
209
|
+
let remaining = [...withIds];
|
|
210
|
+
while (remaining.length > 0) {
|
|
211
|
+
const ready = remaining.filter(t => t.dependencies.every(dep => !idSet.has(dep) || emitted.has(dep)));
|
|
212
|
+
const batch = ready.length > 0 ? ready : remaining; // no progress -> emit the rest
|
|
213
|
+
const taskIds = batch.map(t => t.id);
|
|
214
|
+
phases.push({
|
|
215
|
+
index: phases.length + 1,
|
|
216
|
+
taskIds,
|
|
217
|
+
parallel: taskIds.length > 1,
|
|
218
|
+
});
|
|
219
|
+
for (const id of taskIds)
|
|
220
|
+
emitted.add(id);
|
|
221
|
+
remaining = remaining.filter(t => !emitted.has(t.id));
|
|
222
|
+
}
|
|
223
|
+
return phases;
|
|
224
|
+
};
|
|
225
|
+
exports.inferPhases = inferPhases;
|
|
226
|
+
/**
|
|
227
|
+
* Resolves a plan's phases: parses a blueprint document's phase list when the
|
|
228
|
+
* plan body contains one, otherwise infers phases from task dependencies.
|
|
229
|
+
*/
|
|
230
|
+
const resolvePhases = (planBody, tasks) => {
|
|
231
|
+
const fromBlueprint = (0, exports.parseBlueprintPhases)(planBody);
|
|
232
|
+
if (fromBlueprint && fromBlueprint.length > 0)
|
|
233
|
+
return fromBlueprint;
|
|
234
|
+
return (0, exports.inferPhases)(tasks);
|
|
235
|
+
};
|
|
236
|
+
exports.resolvePhases = resolvePhases;
|
|
237
|
+
//# sourceMappingURL=derivation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"derivation.js","sourceRoot":"","sources":["../../src/serve/derivation.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEH,uCAAyB;AACzB,2CAA6B;AAC7B,yCAMoB;AA0CpB,MAAM,WAAW,GAAG,WAAW,CAAC;AAChC,MAAM,kBAAkB,GAAG,SAAS,CAAC;AAErC,wFAAwF;AACjF,MAAM,SAAS,GAAG,CAAC,OAAe,EAAU,EAAE;IACnD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC7C,IAAI,OAAoB,CAAC;IACzB,IAAI,CAAC;QACH,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,QAAQ,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,OAAO,OAAO;SACX,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;SACjD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;SAC5C,OAAO,CAAC,CAAC,CAAC,EAAE;QACX,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QAC7C,IAAI,OAAe,CAAC;QACpB,IAAI,CAAC;YACH,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC9C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,MAAM,EAAE,GAAG,IAAA,2BAAgB,EAAC,OAAO,CAAC,CAAC;QACrC,MAAM,IAAI,GAAG,IAAA,sBAAW,EAAC,OAAO,CAAC,CAAC;QAClC,MAAM,YAAY,GAAG,EAAE,CAAC,YAAY;aACjC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;aACvD,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAChD,OAAO;YACL;gBACE,EAAE,EAAE,EAAE,CAAC,EAAE;gBACT,IAAI,EAAE,IAAA,uBAAY,EAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;gBACvD,KAAK,EAAE,EAAE,CAAC,KAAK;gBACf,YAAY;gBACZ,MAAM,EAAE,EAAE,CAAC,MAAM;gBACjB,MAAM,EAAE,EAAE,CAAC,MAAM;gBACjB,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,IAAI;gBACJ,QAAQ,EAAE,IAAA,sBAAW,EAAC,IAAI,CAAC,CAAC,QAAQ;aACrC;SACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACP,CAAC,CAAC;AAvCW,QAAA,SAAS,aAuCpB;AAEF,+EAA+E;AAC/E,MAAM,QAAQ,GAAG,CAAC,MAA0B,EAAqC,EAAE;IACjF,IAAI,MAAM,KAAK,WAAW;QAAE,OAAO,MAAM,CAAC;IAC1C,IAAI,MAAM,KAAK,kBAAkB;QAAE,OAAO,YAAY,CAAC;IACvD,OAAO,SAAS,CAAC;AACnB,CAAC,CAAC;AAEF;;;;;;;GAOG;AACI,MAAM,WAAW,GAAG,CAAC,KAAa,EAAgB,EAAE;IACzD,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC;IAC3B,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;QAChB,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;IACjD,CAAC;IAED,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACpC,IAAI,KAAK,KAAK,MAAM,EAAE,CAAC;YACrB,IAAI,IAAI,CAAC,CAAC;YACV,OAAO,IAAI,CAAC,CAAC;QACf,CAAC;aAAM,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YAC/B,OAAO,IAAI,CAAC,CAAC;QACf,CAAC;IACH,CAAC;IAED,IAAI,KAAgB,CAAC;IACrB,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;QACnB,KAAK,GAAG,MAAM,CAAC;IACjB,CAAC;SAAM,IAAI,OAAO,KAAK,CAAC,EAAE,CAAC;QACzB,KAAK,GAAG,OAAO,CAAC;IAClB,CAAC;SAAM,CAAC;QACN,KAAK,GAAG,OAAO,CAAC;IAClB,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AAChC,CAAC,CAAC;AA5BW,QAAA,WAAW,eA4BtB;AAEF,MAAM,oBAAoB,GAAG,sCAAsC,CAAC;AACpE,MAAM,gBAAgB,GAAG,oEAAoE,CAAC;AAC9F,MAAM,WAAW,GAAG,oBAAoB,CAAC;AAEzC;;;GAGG;AACI,MAAM,oBAAoB,GAAG,CAAC,QAAgB,EAAuB,EAAE;IAC5E,MAAM,YAAY,GAAG,QAAQ,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;IAC1D,IAAI,CAAC,YAAY,IAAI,YAAY,CAAC,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAExE,MAAM,SAAS,GAAG,QAAQ,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IACrD,MAAM,QAAQ,GAAiE,EAAE,CAAC;IAClF,gBAAgB,CAAC,SAAS,GAAG,CAAC,CAAC;IAC/B,IAAI,CAAyB,CAAC;IAC9B,OAAO,CAAC,CAAC,GAAG,gBAAgB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QACvD,QAAQ,CAAC,IAAI,CAAC;YACZ,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,YAAY,EAAE,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM;YACnC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE;SAC1B,CAAC,CAAC;IACL,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAE5C,MAAM,MAAM,GAAY,EAAE,CAAC;IAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACzC,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAE,CAAC;QAC7B,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC;QACjD,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC;QAE3D,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;YAC1C,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;YAC5B,kEAAkE;YAClE,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;gBAAE,SAAS;YACnE,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;YACvC,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,SAAS,EAAE,CAAC;gBAChC,MAAM,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBAChC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;oBAAE,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACnE,CAAC;QACH,CAAC;QAED,MAAM,CAAC,IAAI,CAAC;YACV,KAAK,EAAE,CAAC,GAAG,CAAC;YACZ,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;YACxD,OAAO;YACP,QAAQ,EAAE,OAAO,CAAC,MAAM,GAAG,CAAC;SAC7B,CAAC,CAAC;IACL,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AA7CW,QAAA,oBAAoB,wBA6C/B;AAEF;;;;;;GAMG;AACI,MAAM,WAAW,GAAG,CAAC,KAAa,EAAW,EAAE;IACpD,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAA8B,EAAE,CAAC,OAAO,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC;IAC1F,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEpC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC9C,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,MAAM,MAAM,GAAY,EAAE,CAAC;IAC3B,IAAI,SAAS,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC;IAE7B,OAAO,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CACjC,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CACjE,CAAC;QAEF,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,+BAA+B;QACnF,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACrC,MAAM,CAAC,IAAI,CAAC;YACV,KAAK,EAAE,MAAM,CAAC,MAAM,GAAG,CAAC;YACxB,OAAO;YACP,QAAQ,EAAE,OAAO,CAAC,MAAM,GAAG,CAAC;SAC7B,CAAC,CAAC;QACH,KAAK,MAAM,EAAE,IAAI,OAAO;YAAE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC1C,SAAS,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACxD,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AA1BW,QAAA,WAAW,eA0BtB;AAEF;;;GAGG;AACI,MAAM,aAAa,GAAG,CAAC,QAAgB,EAAE,KAAa,EAAW,EAAE;IACxE,MAAM,aAAa,GAAG,IAAA,4BAAoB,EAAC,QAAQ,CAAC,CAAC;IACrD,IAAI,aAAa,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,aAAa,CAAC;IACpE,OAAO,IAAA,mBAAW,EAAC,KAAK,CAAC,CAAC;AAC5B,CAAC,CAAC;AAJW,QAAA,aAAa,iBAIxB"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-Sent Events change stream for `npx strikethroo serve`.
|
|
3
|
+
*
|
|
4
|
+
* Provides `GET /api/events`: a `text/event-stream` that broadcasts a single
|
|
5
|
+
* coalesced `changed` ping whenever the workspace mutates on disk. The event
|
|
6
|
+
* carries no diff payload — clients refetch the affected API data (the browser
|
|
7
|
+
* consumer is a later plan).
|
|
8
|
+
*
|
|
9
|
+
* Exposes an {@link EventsHub} that owns the connected-client set and the
|
|
10
|
+
* workspace watcher, plus an `ApiHandler` the server registers so this module
|
|
11
|
+
* stays decoupled from routing. Connections are cleaned up on disconnect so no
|
|
12
|
+
* listeners or sockets leak. Node built-ins only.
|
|
13
|
+
*/
|
|
14
|
+
import * as http from 'http';
|
|
15
|
+
/**
|
|
16
|
+
* Owns the SSE client set and the workspace watcher. One hub per server. Start
|
|
17
|
+
* the watcher with {@link start} after the server is listening; release it with
|
|
18
|
+
* {@link close} when the server closes.
|
|
19
|
+
*/
|
|
20
|
+
export declare class EventsHub {
|
|
21
|
+
private readonly workspaceDir;
|
|
22
|
+
private readonly debounceMs?;
|
|
23
|
+
private readonly clients;
|
|
24
|
+
private watcher;
|
|
25
|
+
constructor(workspaceDir: string, debounceMs?: number | undefined);
|
|
26
|
+
/** Begins watching the workspace; coalesced changes broadcast to all clients. */
|
|
27
|
+
start(): void;
|
|
28
|
+
/** Stops the watcher and ends every open client connection. */
|
|
29
|
+
close(): void;
|
|
30
|
+
/** Number of currently connected SSE clients (used by tests). */
|
|
31
|
+
get clientCount(): number;
|
|
32
|
+
/** Sends a single coalesced `changed` event to every connected client. */
|
|
33
|
+
private broadcast;
|
|
34
|
+
/** Attaches an SSE response: writes headers, registers cleanup on close. */
|
|
35
|
+
handleConnection(req: http.IncomingMessage, res: http.ServerResponse): void;
|
|
36
|
+
/** An {@link ApiHandler} for `GET /api/events`; returns false otherwise. */
|
|
37
|
+
apiHandler: (req: http.IncomingMessage, res: http.ServerResponse, ctx: {
|
|
38
|
+
pathname: string;
|
|
39
|
+
}) => boolean;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/serve/events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAM7B;;;;GAIG;AACH,qBAAa,SAAS;IAKlB,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC;IAL9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkC;IAC1D,OAAO,CAAC,OAAO,CAAiC;gBAG7B,YAAY,EAAE,MAAM,EACpB,UAAU,CAAC,EAAE,MAAM,YAAA;IAGtC,iFAAiF;IACjF,KAAK,IAAI,IAAI;IASb,+DAA+D;IAC/D,KAAK,IAAI,IAAI;IAWb,iEAAiE;IACjE,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,0EAA0E;IAC1E,OAAO,CAAC,SAAS;IAMjB,4EAA4E;IAC5E,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,GAAG,EAAE,IAAI,CAAC,cAAc,GAAG,IAAI;IAyB3E,4EAA4E;IAC5E,UAAU,GACR,KAAK,IAAI,CAAC,eAAe,EACzB,KAAK,IAAI,CAAC,cAAc,EACxB,KAAK;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,KACxB,OAAO,CAMR;CACH"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Server-Sent Events change stream for `npx strikethroo serve`.
|
|
4
|
+
*
|
|
5
|
+
* Provides `GET /api/events`: a `text/event-stream` that broadcasts a single
|
|
6
|
+
* coalesced `changed` ping whenever the workspace mutates on disk. The event
|
|
7
|
+
* carries no diff payload — clients refetch the affected API data (the browser
|
|
8
|
+
* consumer is a later plan).
|
|
9
|
+
*
|
|
10
|
+
* Exposes an {@link EventsHub} that owns the connected-client set and the
|
|
11
|
+
* workspace watcher, plus an `ApiHandler` the server registers so this module
|
|
12
|
+
* stays decoupled from routing. Connections are cleaned up on disconnect so no
|
|
13
|
+
* listeners or sockets leak. Node built-ins only.
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.EventsHub = void 0;
|
|
17
|
+
const watcher_1 = require("./watcher");
|
|
18
|
+
/** Keep-alive comment interval (ms) to hold idle proxies/streams open. */
|
|
19
|
+
const KEEP_ALIVE_MS = 15000;
|
|
20
|
+
/**
|
|
21
|
+
* Owns the SSE client set and the workspace watcher. One hub per server. Start
|
|
22
|
+
* the watcher with {@link start} after the server is listening; release it with
|
|
23
|
+
* {@link close} when the server closes.
|
|
24
|
+
*/
|
|
25
|
+
class EventsHub {
|
|
26
|
+
constructor(workspaceDir, debounceMs) {
|
|
27
|
+
this.workspaceDir = workspaceDir;
|
|
28
|
+
this.debounceMs = debounceMs;
|
|
29
|
+
this.clients = new Set();
|
|
30
|
+
this.watcher = null;
|
|
31
|
+
/** An {@link ApiHandler} for `GET /api/events`; returns false otherwise. */
|
|
32
|
+
this.apiHandler = (req, res, ctx) => {
|
|
33
|
+
if (ctx.pathname === '/api/events' || ctx.pathname === '/api/events/') {
|
|
34
|
+
this.handleConnection(req, res);
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/** Begins watching the workspace; coalesced changes broadcast to all clients. */
|
|
41
|
+
start() {
|
|
42
|
+
if (this.watcher)
|
|
43
|
+
return;
|
|
44
|
+
this.watcher = (0, watcher_1.createWorkspaceWatcher)(this.workspaceDir, () => this.broadcast(), this.debounceMs);
|
|
45
|
+
}
|
|
46
|
+
/** Stops the watcher and ends every open client connection. */
|
|
47
|
+
close() {
|
|
48
|
+
if (this.watcher) {
|
|
49
|
+
this.watcher.close();
|
|
50
|
+
this.watcher = null;
|
|
51
|
+
}
|
|
52
|
+
for (const res of this.clients) {
|
|
53
|
+
res.end();
|
|
54
|
+
}
|
|
55
|
+
this.clients.clear();
|
|
56
|
+
}
|
|
57
|
+
/** Number of currently connected SSE clients (used by tests). */
|
|
58
|
+
get clientCount() {
|
|
59
|
+
return this.clients.size;
|
|
60
|
+
}
|
|
61
|
+
/** Sends a single coalesced `changed` event to every connected client. */
|
|
62
|
+
broadcast() {
|
|
63
|
+
for (const res of this.clients) {
|
|
64
|
+
res.write('event: changed\ndata: {}\n\n');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/** Attaches an SSE response: writes headers, registers cleanup on close. */
|
|
68
|
+
handleConnection(req, res) {
|
|
69
|
+
res.writeHead(200, {
|
|
70
|
+
'Content-Type': 'text/event-stream',
|
|
71
|
+
'Cache-Control': 'no-cache',
|
|
72
|
+
Connection: 'keep-alive',
|
|
73
|
+
});
|
|
74
|
+
// Initial comment opens the stream and flushes headers to the client.
|
|
75
|
+
res.write(': connected\n\n');
|
|
76
|
+
this.clients.add(res);
|
|
77
|
+
const keepAlive = setInterval(() => {
|
|
78
|
+
res.write(': keep-alive\n\n');
|
|
79
|
+
}, KEEP_ALIVE_MS);
|
|
80
|
+
// Don't let the keep-alive timer hold the event loop / process open.
|
|
81
|
+
keepAlive.unref?.();
|
|
82
|
+
const cleanup = () => {
|
|
83
|
+
clearInterval(keepAlive);
|
|
84
|
+
this.clients.delete(res);
|
|
85
|
+
};
|
|
86
|
+
req.on('close', cleanup);
|
|
87
|
+
res.on('close', cleanup);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
exports.EventsHub = EventsHub;
|
|
91
|
+
//# sourceMappingURL=events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/serve/events.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;GAYG;;;AAGH,uCAAqE;AAErE,0EAA0E;AAC1E,MAAM,aAAa,GAAG,KAAK,CAAC;AAE5B;;;;GAIG;AACH,MAAa,SAAS;IAIpB,YACmB,YAAoB,EACpB,UAAmB;QADnB,iBAAY,GAAZ,YAAY,CAAQ;QACpB,eAAU,GAAV,UAAU,CAAS;QALrB,YAAO,GAAG,IAAI,GAAG,EAAuB,CAAC;QAClD,YAAO,GAA4B,IAAI,CAAC;QAmEhD,4EAA4E;QAC5E,eAAU,GAAG,CACX,GAAyB,EACzB,GAAwB,EACxB,GAAyB,EAChB,EAAE;YACX,IAAI,GAAG,CAAC,QAAQ,KAAK,aAAa,IAAI,GAAG,CAAC,QAAQ,KAAK,cAAc,EAAE,CAAC;gBACtE,IAAI,CAAC,gBAAgB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;gBAChC,OAAO,IAAI,CAAC;YACd,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC,CAAC;IAzEC,CAAC;IAEJ,iFAAiF;IACjF,KAAK;QACH,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO;QACzB,IAAI,CAAC,OAAO,GAAG,IAAA,gCAAsB,EACnC,IAAI,CAAC,YAAY,EACjB,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,EACtB,IAAI,CAAC,UAAU,CAChB,CAAC;IACJ,CAAC;IAED,+DAA+D;IAC/D,KAAK;QACH,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YACrB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACtB,CAAC;QACD,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC/B,GAAG,CAAC,GAAG,EAAE,CAAC;QACZ,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;IAED,iEAAiE;IACjE,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;IAC3B,CAAC;IAED,0EAA0E;IAClE,SAAS;QACf,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC/B,GAAG,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC;IAED,4EAA4E;IAC5E,gBAAgB,CAAC,GAAyB,EAAE,GAAwB;QAClE,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE;YACjB,cAAc,EAAE,mBAAmB;YACnC,eAAe,EAAE,UAAU;YAC3B,UAAU,EAAE,YAAY;SACzB,CAAC,CAAC;QACH,sEAAsE;QACtE,GAAG,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;QAE7B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAEtB,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE;YACjC,GAAG,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;QAChC,CAAC,EAAE,aAAa,CAAC,CAAC;QAClB,qEAAqE;QACrE,SAAS,CAAC,KAAK,EAAE,EAAE,CAAC;QAEpB,MAAM,OAAO,GAAG,GAAS,EAAE;YACzB,aAAa,CAAC,SAAS,CAAC,CAAC;YACzB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC,CAAC;QACF,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACzB,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC3B,CAAC;CAcF;AAjFD,8BAiFC"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure, dependency-light markdown parsing primitives for the serve data layer.
|
|
3
|
+
*
|
|
4
|
+
* These functions operate on already-read string content. File reading itself
|
|
5
|
+
* belongs to the task scanner and the model assembler. Everything here is
|
|
6
|
+
* synchronous, read-only, and side-effect-free: no `fs`, no network, no writes.
|
|
7
|
+
*
|
|
8
|
+
* The existing `src/skill-scripts/shared/frontmatter.ts` only recovers the
|
|
9
|
+
* numeric `id`; it is intentionally not forked. This module provides a richer
|
|
10
|
+
* frontmatter reader the UI needs (summary, created, status, group,
|
|
11
|
+
* dependencies[], skills[]).
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Parsed frontmatter. All fields are optional because real-world files vary and
|
|
15
|
+
* malformed/missing frontmatter must degrade gracefully rather than throw.
|
|
16
|
+
* `extra` retains any scalar keys not covered by the typed fields.
|
|
17
|
+
*/
|
|
18
|
+
export interface ParsedFrontmatter {
|
|
19
|
+
id?: number;
|
|
20
|
+
summary?: string;
|
|
21
|
+
created?: string;
|
|
22
|
+
status?: string;
|
|
23
|
+
group?: string;
|
|
24
|
+
dependencies: Array<number | string>;
|
|
25
|
+
skills: string[];
|
|
26
|
+
extra: Record<string, string>;
|
|
27
|
+
}
|
|
28
|
+
/** A named `## ` section of a markdown body. */
|
|
29
|
+
export interface MarkdownSection {
|
|
30
|
+
/** Heading text (the remainder of the `## ` line, trimmed). */
|
|
31
|
+
heading: string;
|
|
32
|
+
/** Section content from just after the heading line up to the next `## ` or EOF. */
|
|
33
|
+
content: string;
|
|
34
|
+
/** Character offset of the heading start within the body. */
|
|
35
|
+
start: number;
|
|
36
|
+
/** Character offset of the section end (exclusive) within the body. */
|
|
37
|
+
end: number;
|
|
38
|
+
}
|
|
39
|
+
/** Result of sectioning a markdown body. */
|
|
40
|
+
export interface SectionedBody {
|
|
41
|
+
/** Everything after the closing frontmatter `---`, verbatim. */
|
|
42
|
+
rawBody: string;
|
|
43
|
+
/** Ordered list of `## ` sections. */
|
|
44
|
+
sections: MarkdownSection[];
|
|
45
|
+
}
|
|
46
|
+
/** A single extracted mermaid fenced block. */
|
|
47
|
+
export interface MermaidBlock {
|
|
48
|
+
/** Raw mermaid source (inner content of the fenced block). */
|
|
49
|
+
source: string;
|
|
50
|
+
/** True when the block falls within an "Architectural Approach" section. */
|
|
51
|
+
isArchitecturalApproach: boolean;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Reads the leading frontmatter block into typed fields. Never throws: malformed
|
|
55
|
+
* lines are skipped and absent keys fall back to defaults (undefined scalars,
|
|
56
|
+
* empty arrays).
|
|
57
|
+
*/
|
|
58
|
+
export declare const parseFrontmatter: (content: string) => ParsedFrontmatter;
|
|
59
|
+
/** Returns the body: everything after the closing frontmatter `---`, or the whole content. */
|
|
60
|
+
export declare const extractBody: (content: string) => string;
|
|
61
|
+
/**
|
|
62
|
+
* Splits a markdown body into the verbatim `rawBody` plus an ordered list of
|
|
63
|
+
* `## ` sections addressable by heading text. Section access is additive:
|
|
64
|
+
* callers may always fall back to `rawBody`.
|
|
65
|
+
*/
|
|
66
|
+
export declare const sectionBody: (body: string) => SectionedBody;
|
|
67
|
+
/**
|
|
68
|
+
* Extracts every fenced ```mermaid block's raw source from a body. Blocks whose
|
|
69
|
+
* position falls within an "Architectural Approach" section are flagged. Plans
|
|
70
|
+
* with no mermaid block yield an empty list (never an error).
|
|
71
|
+
*/
|
|
72
|
+
export declare const extractMermaidBlocks: (body: string) => MermaidBlock[];
|
|
73
|
+
/** Returns the text of the first `# ` (level-1) heading in a body, or undefined. */
|
|
74
|
+
export declare const extractTitle: (body: string) => string | undefined;
|
|
75
|
+
//# sourceMappingURL=markdown.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/serve/markdown.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;IACrC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B;AAED,gDAAgD;AAChD,MAAM,WAAW,eAAe;IAC9B,+DAA+D;IAC/D,OAAO,EAAE,MAAM,CAAC;IAChB,oFAAoF;IACpF,OAAO,EAAE,MAAM,CAAC;IAChB,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,GAAG,EAAE,MAAM,CAAC;CACb;AAED,4CAA4C;AAC5C,MAAM,WAAW,aAAa;IAC5B,gEAAgE;IAChE,OAAO,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,QAAQ,EAAE,eAAe,EAAE,CAAC;CAC7B;AAED,+CAA+C;AAC/C,MAAM,WAAW,YAAY;IAC3B,8DAA8D;IAC9D,MAAM,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,uBAAuB,EAAE,OAAO,CAAC;CAClC;AAwCD;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAI,SAAS,MAAM,KAAG,iBAqFlD,CAAC;AAEF,8FAA8F;AAC9F,eAAO,MAAM,WAAW,GAAI,SAAS,MAAM,KAAG,MAI7C,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,WAAW,GAAI,MAAM,MAAM,KAAG,aA6B1C,CAAC;AAIF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAAI,MAAM,MAAM,KAAG,YAAY,EAmB/D,CAAC;AAEF,oFAAoF;AACpF,eAAO,MAAM,YAAY,GAAI,MAAM,MAAM,KAAG,MAAM,GAAG,SAGpD,CAAC"}
|