@shiyuphan/follow-me 0.2.0 → 0.3.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 +44 -55
- package/dist/src/cli.js +566 -188
- package/dist/src/comments.js +66 -5
- package/dist/src/git.js +12 -0
- package/dist/src/host.js +19 -8
- package/dist/src/install.js +21 -0
- package/dist/src/plan.js +1 -1
- package/dist/src/prompt.js +343 -39
- package/dist/src/server.js +176 -67
- package/dist/src/session.js +13 -11
- package/dist/ui/dist/assets/abnfDiagram-WEYOIA3C-DcmzRkVZ.js +1 -0
- package/dist/ui/dist/assets/arc-T5QB0T1C.js +1 -0
- package/dist/ui/dist/assets/architecture-WOLXFQ4H-DUIkQTeE.js +1 -0
- package/dist/ui/dist/assets/architectureDiagram-ZHVYKT53-BUYi4-bA.js +36 -0
- package/dist/ui/dist/assets/array-BifhSqXX.js +1 -0
- package/dist/ui/dist/assets/blockDiagram-RJAE7QA3-dyJxtT9e.js +140 -0
- package/dist/ui/dist/assets/c4Diagram-XDPPPE54-CrAgiaZR.js +38 -0
- package/dist/ui/dist/assets/channel-wW7ebm0i.js +1 -0
- package/dist/ui/dist/assets/chunk-2E7WHVMD-C6fAf4Vj.js +88 -0
- package/dist/ui/dist/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
- package/dist/ui/dist/assets/chunk-4EMNKR2T-BLZjwZ4Q.js +1 -0
- package/dist/ui/dist/assets/chunk-55XCU76K-DGLK2Q-L.js +1 -0
- package/dist/ui/dist/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
- package/dist/ui/dist/assets/chunk-7S6XSTFO-BablszTX.js +2 -0
- package/dist/ui/dist/assets/chunk-AQDTSKZB-Cuk3B_vb.js +72 -0
- package/dist/ui/dist/assets/chunk-DSNLSHJZ-DiC78RqE.js +127 -0
- package/dist/ui/dist/assets/chunk-DUW6YSOI-Dgx8z5s3.js +1 -0
- package/dist/ui/dist/assets/chunk-HAGFOTED-X_RbvixX.js +62 -0
- package/dist/ui/dist/assets/chunk-HNE4Q36P-C14_x5ck.js +1 -0
- package/dist/ui/dist/assets/chunk-J5ZVWO5B-oDCd-gWO.js +1 -0
- package/dist/ui/dist/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
- package/dist/ui/dist/assets/chunk-NGNAAXSQ-C2Wy9Xmz.js +136 -0
- package/dist/ui/dist/assets/chunk-NQOKRVIF-Dxd0dtYR.js +213 -0
- package/dist/ui/dist/assets/chunk-UHW7PFBP-BIxePNUS.js +1 -0
- package/dist/ui/dist/assets/chunk-VQ5W62QI-CKSKLbj4.js +1 -0
- package/dist/ui/dist/assets/chunk-WQQL4CDK-CWNmZGlR.js +10 -0
- package/dist/ui/dist/assets/chunk-XXDRQBXY-D-I5QHgG.js +1 -0
- package/dist/ui/dist/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
- package/dist/ui/dist/assets/chunk-YZO67HFG-DFP68CFd.js +1 -0
- package/dist/ui/dist/assets/chunk-ZAUX2LTX-DY90d4QG.js +1 -0
- package/dist/ui/dist/assets/chunk-ZCHMQSOR-Dlpr-5UC.js +2 -0
- package/dist/ui/dist/assets/classDiagram-v2-4MYWR3UA-CH9uhoLJ.js +217 -0
- package/dist/ui/dist/assets/cose-bilkent-JH36ORCC-DtYHrp8D.js +1 -0
- package/dist/ui/dist/assets/cynefin-EF2NZ3EQ-BtslbtrY.js +1 -0
- package/dist/ui/dist/assets/cynefinDiagram-WF24UDRP-DUU1Jcc7.js +62 -0
- package/dist/ui/dist/assets/cytoscape.esm-BegBNzOV.js +321 -0
- package/dist/ui/dist/assets/dagre-NK4GIZVX-CDlP-dRW.js +4 -0
- package/dist/ui/dist/assets/defaultLocale-BFoDCU3G.js +1 -0
- package/dist/ui/dist/assets/diagram-CBK3MWTC-DiV2fn-r.js +24 -0
- package/dist/ui/dist/assets/diagram-I5XIIV2D-Bg5RehGi.js +24 -0
- package/dist/ui/dist/assets/diagram-ODIL2UCW-CFmazz3f.js +30 -0
- package/dist/ui/dist/assets/diagram-PFBEJUXE-uw3ZBqDp.js +3 -0
- package/dist/ui/dist/assets/diagram-RJA3I4VV-uEOYYRYa.js +41 -0
- package/dist/ui/dist/assets/diagram-TYUNFSNR-BN58LF_3.js +200 -0
- package/dist/ui/dist/assets/dist-tWS_lXDS.js +1 -0
- package/dist/ui/dist/assets/ebnfDiagram-V4P3TTPT-B68Tz-w8.js +1 -0
- package/dist/ui/dist/assets/elk-QONTBCTQ-DZZohNea.js +27 -0
- package/dist/ui/dist/assets/erDiagram-KYQIGQAA-BH_m74pv.js +99 -0
- package/dist/ui/dist/assets/eventmodeling-K75KTNOO-CTp5FHos.js +1 -0
- package/dist/ui/dist/assets/flowDiagram-IUGLLWBU-CsCvexry.js +1 -0
- package/dist/ui/dist/assets/ganttDiagram-R7GPNTJY-BDau7_0f.js +292 -0
- package/dist/ui/dist/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- package/dist/ui/dist/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- package/dist/ui/dist/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/dist/ui/dist/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/dist/ui/dist/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- package/dist/ui/dist/assets/gitGraph-VSP46ZUC-Bv84IoCm.js +1 -0
- package/dist/ui/dist/assets/gitGraphDiagram-PDYL7XHY-DzXcMlm8.js +106 -0
- package/dist/ui/dist/assets/graphlib-DS17s2tU.js +1 -0
- package/dist/ui/dist/assets/index-BTJoqVVN.js +133 -0
- package/dist/ui/dist/assets/index-D60U3smc.css +1 -0
- package/dist/ui/dist/assets/info-OHQRW6UA-222J0O_j.js +1 -0
- package/dist/ui/dist/assets/infoDiagram-MVDRKAX5-DE2sa3Vz.js +2 -0
- package/dist/ui/dist/assets/init-C-OQMol4.js +1 -0
- package/dist/ui/dist/assets/ishikawaDiagram-HPEHV2GE-BJDk0Zcj.js +70 -0
- package/dist/ui/dist/assets/journeyDiagram-BOSHW7O7-JSUXX5Hb.js +139 -0
- package/dist/ui/dist/assets/kanban-definition-FNSA4HC5-Bg79b49M.js +89 -0
- package/dist/ui/dist/assets/katex-B_JcIwqj.js +257 -0
- package/dist/ui/dist/assets/line-Bk5qQSIa.js +1 -0
- package/dist/ui/dist/assets/linear-PFnVMru5.js +1 -0
- package/dist/ui/dist/assets/mermaid-parser.core-Dcxlzc-S.js +7 -0
- package/dist/ui/dist/assets/mermaid.core-TeOHQJ00.js +44 -0
- package/dist/ui/dist/assets/mindmap-definition-6OY57UG3-IaNk8NnW.js +98 -0
- package/dist/ui/dist/assets/ordinal-BDEzSJ7C.js +1 -0
- package/dist/ui/dist/assets/packet-JDAUHWVQ-DMff5cVK.js +1 -0
- package/dist/ui/dist/assets/path-fybaL0A-.js +1 -0
- package/dist/ui/dist/assets/pegDiagram-ECIUJNBX-66Xu4YRc.js +1 -0
- package/dist/ui/dist/assets/pie-XZMESJXO-GEtzLCIM.js +1 -0
- package/dist/ui/dist/assets/pieDiagram-GNMPY74T-7jMBi-kU.js +39 -0
- package/dist/ui/dist/assets/prism-bash-D6zCJ74D.js +1 -0
- package/dist/ui/dist/assets/prism-c-04YixN25.js +1 -0
- package/dist/ui/dist/assets/prism-clike-DapgWyxx.js +1 -0
- package/dist/ui/dist/assets/prism-core-FDHlWQ43.js +1 -0
- package/dist/ui/dist/assets/prism-cpp-C-lJbC-6.js +1 -0
- package/dist/ui/dist/assets/prism-css-CPgOzxXp.js +1 -0
- package/dist/ui/dist/assets/prism-go-C_8qAH5n.js +1 -0
- package/dist/ui/dist/assets/prism-java-6wtusvIl.js +1 -0
- package/dist/ui/dist/assets/prism-javascript-BnO-swvr.js +1 -0
- package/dist/ui/dist/assets/prism-json-Dp_-W-Hv.js +1 -0
- package/dist/ui/dist/assets/prism-jsx-D-D0NWtC.js +1 -0
- package/dist/ui/dist/assets/prism-kotlin-BUlJgC-o.js +1 -0
- package/dist/ui/dist/assets/prism-markdown-Bc9-36OC.js +1 -0
- package/dist/ui/dist/assets/prism-markup-Cp04rIa1.js +1 -0
- package/dist/ui/dist/assets/prism-markup-templating-KkSMBj6Y.js +1 -0
- package/dist/ui/dist/assets/prism-php-Co4fM40K.js +1 -0
- package/dist/ui/dist/assets/prism-python-CfiSVtM6.js +1 -0
- package/dist/ui/dist/assets/prism-ruby-DFCASkuD.js +1 -0
- package/dist/ui/dist/assets/prism-rust-Bu9N8X3N.js +1 -0
- package/dist/ui/dist/assets/prism-sql-CYhVDMEE.js +1 -0
- package/dist/ui/dist/assets/prism-swift-B0kWc-ne.js +1 -0
- package/dist/ui/dist/assets/prism-toml-DKb54GAT.js +1 -0
- package/dist/ui/dist/assets/prism-tsx-p6J2Kc_4.js +1 -0
- package/dist/ui/dist/assets/prism-typescript-BMqONzxf.js +1 -0
- package/dist/ui/dist/assets/prism-yaml-C0JHu8gr.js +1 -0
- package/dist/ui/dist/assets/quadrantDiagram-D57K4MC3-8hIWayC2.js +7 -0
- package/dist/ui/dist/assets/radar-ABXABTNO-BTdwUOOF.js +1 -0
- package/dist/ui/dist/assets/railroad-I3PHUGI6-DcMbDqBL.js +1 -0
- package/dist/ui/dist/assets/railroad-abnf-LNEFI6M7-gTzUTWeI.js +1 -0
- package/dist/ui/dist/assets/railroad-ebnf-DWYD2UWJ-CNtdUPtk.js +1 -0
- package/dist/ui/dist/assets/railroad-peg-JR7BVNR7-DOiExAtE.js +1 -0
- package/dist/ui/dist/assets/railroadDiagram-Y72NRBL7-2kE_ygvM.js +1 -0
- package/dist/ui/dist/assets/requirementDiagram-EQYSW5RA-CoIAHRq2.js +84 -0
- package/dist/ui/dist/assets/rough.esm-Dy-Kn_BL.js +1 -0
- package/dist/ui/dist/assets/sankeyDiagram-SY24JHO5-7p8D3wim.js +40 -0
- package/dist/ui/dist/assets/sequenceDiagram-BLEHNHQU-C6QFnMvn.js +169 -0
- package/dist/ui/dist/assets/sizeCapture-INFHLROL-3uHXrFqA.js +1 -0
- package/dist/ui/dist/assets/src-FgijoBVs.js +1 -0
- package/dist/ui/dist/assets/stateDiagram-v2-A6IESKWZ-n9eAb_bT.js +291 -0
- package/dist/ui/dist/assets/swimlanes-B3ENRKFQ-U18mtUzX.js +1 -0
- package/dist/ui/dist/assets/swimlanesDiagram-AYQRV4AK-DX07QVy9.js +8 -0
- package/dist/ui/dist/assets/timeline-definition-JXYKGLGB-CD2Z8QGP.js +120 -0
- package/dist/ui/dist/assets/treeView-D4JQ5SDB-sqUjH5gB.js +1 -0
- package/dist/ui/dist/assets/treemap-SAJKECNS-DvZ38FP7.js +1 -0
- package/dist/ui/dist/assets/usecaseDiagram-WPOLBNNX-CqOGidaZ.js +328 -0
- package/dist/ui/dist/assets/vennDiagram-Q3Y3IDCW-D1SXSBQ5.js +34 -0
- package/dist/ui/dist/assets/wardley-7MLQ67FV-Bpwio-Dx.js +1 -0
- package/dist/ui/dist/assets/wardleyDiagram-IVAIDKWA-JSUXHMnk.js +78 -0
- package/dist/ui/dist/assets/xychartDiagram-4URNHDU4-B4IFqQTN.js +7 -0
- package/dist/ui/dist/index.html +15 -0
- package/dist/ui/dist/style.css +121 -0
- package/package.json +31 -10
- package/skills/follow-me/SKILL.md +15 -89
- package/dist/src/anthropic.js +0 -133
- package/dist/ui/app.js +0 -2201
- package/dist/ui/concerns.js +0 -165
- package/dist/ui/highlight.js +0 -221
- package/dist/ui/index.html +0 -94
- package/dist/ui/live.js +0 -184
- package/dist/ui/playback.js +0 -579
- package/dist/ui/questions.js +0 -189
- package/dist/ui/style.css +0 -1331
- package/dist/ui/summary.js +0 -45
package/dist/src/server.js
CHANGED
|
@@ -6,7 +6,7 @@ import { basename, isAbsolute, join } from "node:path";
|
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
import { isReplyId, parseComment, parseDraft, parseNewDecision, parseNewReply, } from "./comments.js";
|
|
8
8
|
import { isConcernId, isPlanVersion } from "./plan.js";
|
|
9
|
-
import { renderReport } from "./prompt.js";
|
|
9
|
+
import { renderReport, reportItems } from "./prompt.js";
|
|
10
10
|
import { isLockToken } from "./session.js";
|
|
11
11
|
/** The requested port is taken; host.ts decides whether that is our server or a stranger. */
|
|
12
12
|
export class PortInUseError extends Error {
|
|
@@ -23,19 +23,16 @@ export class RegistrationError extends Error {
|
|
|
23
23
|
}
|
|
24
24
|
/** GET /api/health answers with this, so a CLI can tell our server from another program. */
|
|
25
25
|
export const HEALTH_APP = "follow-me";
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
/** The page Vite builds from ui/ into ui/dist; `npm run build` makes it. */
|
|
27
|
+
const UI_DIR = new URL("../ui/dist/", import.meta.url);
|
|
28
|
+
/** Hashed bundles under /assets/ and the few flat files of ui/public at the root. */
|
|
29
|
+
const STATIC_FILE = /^\/((?:assets\/)?[\w-]+(?:\.[\w-]+)*\.(js|css|woff2|svg))$/;
|
|
29
30
|
const STATIC_TYPES = new Map([
|
|
30
31
|
["js", "text/javascript; charset=utf-8"],
|
|
31
32
|
["css", "text/css; charset=utf-8"],
|
|
33
|
+
["woff2", "font/woff2"],
|
|
34
|
+
["svg", "image/svg+xml"],
|
|
32
35
|
]);
|
|
33
|
-
/** Mermaid's ESM entry imports its chunks by relative path, so they are served beside it. */
|
|
34
|
-
const MERMAID_ENTRY = new URL(import.meta.resolve("mermaid/dist/mermaid.esm.min.mjs"));
|
|
35
|
-
const MERMAID_CHUNK = /^\/vendor\/chunks\/mermaid\.esm\.min\/([\w-]+\.mjs)$/;
|
|
36
|
-
/** Prism is a plain script per component, loaded one at a time by the page as languages need. */
|
|
37
|
-
const PRISM_CORE = new URL(import.meta.resolve("prismjs/components/prism-core.min.js"));
|
|
38
|
-
const PRISM_PATH = /^\/vendor\/prism\/([a-z][a-z-]*)\.js$/;
|
|
39
36
|
const MAX_BODY_BYTES = 1_000_000;
|
|
40
37
|
const BASE32 = "abcdefghijklmnopqrstuvwxyz234567";
|
|
41
38
|
const COMMENT_ID = /^c_[a-z2-7]{6}$/;
|
|
@@ -85,7 +82,7 @@ export async function loadDraft(reviewsDir, version) {
|
|
|
85
82
|
}
|
|
86
83
|
catch (error) {
|
|
87
84
|
if (isRecord(error) && error["code"] === "ENOENT") {
|
|
88
|
-
return { comments: [], concerns: [], resolved: [] };
|
|
85
|
+
return { comments: [], concerns: [], resolved: [], excluded: { comments: [], concerns: [] } };
|
|
89
86
|
}
|
|
90
87
|
throw error;
|
|
91
88
|
}
|
|
@@ -196,7 +193,7 @@ function indexPage(rows, now) {
|
|
|
196
193
|
const body = items.length === 0
|
|
197
194
|
? ` <section class="empty">
|
|
198
195
|
<h1>No sessions yet</h1>
|
|
199
|
-
<p>
|
|
196
|
+
<p>Ask your coding agent to review a branch with follow-me. Each review opens here.</p>
|
|
200
197
|
<p><a href="https://github.com/phanshiyu/follow-me#readme">How follow-me works</a></p>
|
|
201
198
|
</section>`
|
|
202
199
|
: ` <h1>Sessions</h1>
|
|
@@ -221,44 +218,18 @@ ${body}
|
|
|
221
218
|
</html>
|
|
222
219
|
`;
|
|
223
220
|
}
|
|
224
|
-
|
|
225
|
-
function vendorFile(path) {
|
|
226
|
-
const part = PRISM_PATH.exec(path)?.[1];
|
|
227
|
-
if (part !== undefined) {
|
|
228
|
-
return part === "core" ? PRISM_CORE : new URL(`prism-${part}.min.js`, PRISM_CORE);
|
|
229
|
-
}
|
|
230
|
-
if (path === "/vendor/mermaid.mjs")
|
|
231
|
-
return MERMAID_ENTRY;
|
|
232
|
-
const chunk = MERMAID_CHUNK.exec(path)?.[1];
|
|
233
|
-
return chunk === undefined ? null : new URL(`chunks/mermaid.esm.min/${chunk}`, MERMAID_ENTRY);
|
|
234
|
-
}
|
|
235
|
-
async function readUi(name) {
|
|
236
|
-
try {
|
|
237
|
-
return await readFile(fileURLToPath(new URL(name, UI_DIR)));
|
|
238
|
-
}
|
|
239
|
-
catch (error) {
|
|
240
|
-
if (isRecord(error) && error["code"] === "ENOENT")
|
|
241
|
-
throw new HttpError(404, "not found");
|
|
242
|
-
throw error;
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
async function sendVendor(res, file) {
|
|
246
|
-
let bytes;
|
|
221
|
+
async function readUi(dir, name) {
|
|
247
222
|
try {
|
|
248
|
-
|
|
223
|
+
return await readFile(fileURLToPath(new URL(name, dir)));
|
|
249
224
|
}
|
|
250
225
|
catch (error) {
|
|
251
226
|
if (isRecord(error) && error["code"] === "ENOENT")
|
|
252
|
-
|
|
227
|
+
return null;
|
|
253
228
|
throw error;
|
|
254
229
|
}
|
|
255
|
-
// The bundle only changes with the installed package, and it is several megabytes.
|
|
256
|
-
res.writeHead(200, {
|
|
257
|
-
"content-type": "text/javascript; charset=utf-8",
|
|
258
|
-
"cache-control": "max-age=86400",
|
|
259
|
-
});
|
|
260
|
-
res.end(bytes);
|
|
261
230
|
}
|
|
231
|
+
const NOT_BUILT = `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="color-scheme" content="light dark" /><title>follow-me</title></head><body style="font-family: system-ui; max-width: 40rem; margin: 4rem auto; padding: 0 1rem"><h1>The review page is not built</h1><p>This follow-me runs from a checkout without <code>ui/dist</code>. Run <code>npm run build</code> (or <code>npm run ui:build</code>) in the checkout, then reload.</p></body></html>
|
|
232
|
+
`;
|
|
262
233
|
function viewOf(registration, loaded, draft) {
|
|
263
234
|
const { state, reviewsDir } = loaded;
|
|
264
235
|
return {
|
|
@@ -267,7 +238,10 @@ function viewOf(registration, loaded, draft) {
|
|
|
267
238
|
comments: new Map(state.comments.map((c) => [c.id, c])),
|
|
268
239
|
decisions: new Map(draft.concerns.map((d) => [d.concern, d])),
|
|
269
240
|
resolved: new Set(draft.resolved),
|
|
241
|
+
excludedComments: new Set(draft.excluded.comments),
|
|
242
|
+
excludedConcerns: new Set(draft.excluded.concerns),
|
|
270
243
|
unitIds: new Set(state.units.map((u) => u.id)),
|
|
244
|
+
hunkRows: new Map(state.units.flatMap((u) => u.kind === "hunk" ? [[u.id, u.body.length]] : [])),
|
|
271
245
|
concernIds: new Set(state.commits.flatMap((c) => (c.concerns ?? []).map((k) => k.id))),
|
|
272
246
|
draftFile: draftPath(reviewsDir, state.version),
|
|
273
247
|
reviewFile: join(reviewsDir, `v${state.version}.json`),
|
|
@@ -286,6 +260,38 @@ function mintReplyId(taken) {
|
|
|
286
260
|
return id;
|
|
287
261
|
}
|
|
288
262
|
}
|
|
263
|
+
/**
|
|
264
|
+
* What the agent may see of the log: a pending flag is held back with everything said about it,
|
|
265
|
+
* and reaches the agent as an ordinary "comment" event when the reviewer submits.
|
|
266
|
+
*/
|
|
267
|
+
function forAgent(log) {
|
|
268
|
+
const held = new Set();
|
|
269
|
+
const out = [];
|
|
270
|
+
for (const event of log) {
|
|
271
|
+
if (event.type === "comment" || event.type === "comment-updated") {
|
|
272
|
+
const { comment } = event;
|
|
273
|
+
if (comment.pending === true) {
|
|
274
|
+
held.add(comment.id);
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
if (held.delete(comment.id))
|
|
278
|
+
out.push({ ...event, type: "comment" });
|
|
279
|
+
else
|
|
280
|
+
out.push(event);
|
|
281
|
+
}
|
|
282
|
+
else if (event.type === "comment-deleted") {
|
|
283
|
+
if (!held.delete(event.id))
|
|
284
|
+
out.push(event);
|
|
285
|
+
}
|
|
286
|
+
else if (event.type === "reply") {
|
|
287
|
+
if (!held.has(event.commentId))
|
|
288
|
+
out.push(event);
|
|
289
|
+
}
|
|
290
|
+
else
|
|
291
|
+
out.push(event);
|
|
292
|
+
}
|
|
293
|
+
return out;
|
|
294
|
+
}
|
|
289
295
|
const DEFAULT_POLL_MS = 25_000;
|
|
290
296
|
const MAX_POLL_MS = 60_000;
|
|
291
297
|
const STREAM_PING_MS = 15_000;
|
|
@@ -308,17 +314,25 @@ function hostSession(id, registration, loaded, draft, load) {
|
|
|
308
314
|
for (const listener of [...listeners])
|
|
309
315
|
listener();
|
|
310
316
|
};
|
|
311
|
-
const
|
|
312
|
-
|
|
313
|
-
|
|
317
|
+
const seen = (agent) => (agent ? forAgent(events) : events);
|
|
318
|
+
const waitForEvents = (since, timeoutMs, signal, agent) => new Promise((resolve) => {
|
|
319
|
+
const news = () => seen(agent).filter((e) => e.seq > since);
|
|
320
|
+
const answer = () => resolve({ seq: events.length, events: news() });
|
|
321
|
+
if (news().length > 0 || timeoutMs === 0 || signal.aborted)
|
|
314
322
|
return answer();
|
|
315
323
|
const wake = () => {
|
|
324
|
+
if (news().length === 0 && !signal.aborted)
|
|
325
|
+
return;
|
|
316
326
|
clearTimeout(timer);
|
|
317
327
|
waiters.delete(wake);
|
|
318
328
|
signal.removeEventListener("abort", wake);
|
|
319
329
|
answer();
|
|
320
330
|
};
|
|
321
|
-
const timer = setTimeout(
|
|
331
|
+
const timer = setTimeout(() => {
|
|
332
|
+
waiters.delete(wake);
|
|
333
|
+
signal.removeEventListener("abort", wake);
|
|
334
|
+
answer();
|
|
335
|
+
}, timeoutMs);
|
|
322
336
|
waiters.add(wake);
|
|
323
337
|
signal.addEventListener("abort", wake);
|
|
324
338
|
});
|
|
@@ -330,6 +344,10 @@ function hostSession(id, registration, loaded, draft, load) {
|
|
|
330
344
|
if (swapping)
|
|
331
345
|
throw new HttpError(409, "the session is switching plan versions; try again");
|
|
332
346
|
};
|
|
347
|
+
/** The exclusions as stored, or null when nothing is excluded. */
|
|
348
|
+
const excludedOf = () => view.excludedComments.size + view.excludedConcerns.size === 0
|
|
349
|
+
? null
|
|
350
|
+
: { comments: [...view.excludedComments], concerns: [...view.excludedConcerns] };
|
|
333
351
|
// Writes are chained so two rapid edits never race on the same file.
|
|
334
352
|
let writes = Promise.resolve();
|
|
335
353
|
const saveDraft = () => {
|
|
@@ -337,12 +355,14 @@ function hostSession(id, registration, loaded, draft, load) {
|
|
|
337
355
|
const file = view.draftFile;
|
|
338
356
|
const concerns = [...view.decisions.values()];
|
|
339
357
|
const resolved = [...view.resolved];
|
|
358
|
+
const excluded = excludedOf();
|
|
340
359
|
const snapshot = JSON.stringify({
|
|
341
360
|
schema: "follow-me/review-draft@1",
|
|
342
361
|
planVersion: state.version,
|
|
343
362
|
comments: [...view.comments.values()],
|
|
344
363
|
...(concerns.length === 0 ? {} : { concerns }),
|
|
345
364
|
...(resolved.length === 0 ? {} : { resolved }),
|
|
365
|
+
...(excluded === null ? {} : { excluded }),
|
|
346
366
|
}, null, 2);
|
|
347
367
|
const dir = view.loaded.reviewsDir;
|
|
348
368
|
writes = writes.then(async () => {
|
|
@@ -363,20 +383,43 @@ function hostSession(id, registration, loaded, draft, load) {
|
|
|
363
383
|
if (anchor.kind === "hunk" && !view.unitIds.has(anchor.unit)) {
|
|
364
384
|
throw new HttpError(400, "anchor names an unknown unit");
|
|
365
385
|
}
|
|
386
|
+
if (anchor.kind === "hunk" && anchor.offsets !== undefined) {
|
|
387
|
+
const rows = view.hunkRows.get(anchor.unit);
|
|
388
|
+
if (rows === undefined)
|
|
389
|
+
throw new HttpError(400, "anchor.offsets names a unit without rows");
|
|
390
|
+
if (anchor.offsets.some((o) => o >= rows)) {
|
|
391
|
+
throw new HttpError(400, `anchor.offsets is out of range, the unit has ${rows} rows`);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
366
394
|
return parsed;
|
|
367
395
|
};
|
|
396
|
+
/** Clears `pending` on every held flag at once and tells the agent what it missed. */
|
|
397
|
+
const release = () => {
|
|
398
|
+
const held = [...view.comments.values()].filter((c) => c.pending === true);
|
|
399
|
+
if (held.length === 0)
|
|
400
|
+
return 0;
|
|
401
|
+
emit({ type: "submitted" });
|
|
402
|
+
for (const comment of held) {
|
|
403
|
+
const { pending: _held, ...open } = comment;
|
|
404
|
+
view.comments.set(comment.id, open);
|
|
405
|
+
emit({ type: "comment", comment: open });
|
|
406
|
+
}
|
|
407
|
+
return held.length;
|
|
408
|
+
};
|
|
368
409
|
const review = () => {
|
|
369
410
|
const concerns = [...view.decisions.values()];
|
|
370
411
|
const resolved = [...view.resolved];
|
|
412
|
+
const excluded = excludedOf();
|
|
371
413
|
return {
|
|
372
414
|
schema: "follow-me/review@1",
|
|
373
415
|
mode: view.loaded.state.mode,
|
|
374
416
|
planVersion: view.loaded.state.version,
|
|
375
417
|
planHash: view.loaded.state.planHash,
|
|
376
418
|
endedAt: new Date().toISOString(),
|
|
377
|
-
comments: [...view.comments.values()],
|
|
419
|
+
comments: [...view.comments.values()].filter((c) => c.pending !== true),
|
|
378
420
|
...(concerns.length === 0 ? {} : { concerns }),
|
|
379
421
|
...(resolved.length === 0 ? {} : { resolved }),
|
|
422
|
+
...(excluded === null ? {} : { excluded }),
|
|
380
423
|
};
|
|
381
424
|
};
|
|
382
425
|
const report = (finished) => {
|
|
@@ -412,24 +455,26 @@ function hostSession(id, registration, loaded, draft, load) {
|
|
|
412
455
|
mode: view.loaded.state.mode,
|
|
413
456
|
version: view.loaded.state.version,
|
|
414
457
|
comments: view.comments.size,
|
|
458
|
+
openedAt: new Date(openedAt).toISOString(),
|
|
415
459
|
}),
|
|
416
460
|
snapshot: () => ({
|
|
417
461
|
...view.loaded.state,
|
|
418
462
|
comments: [...view.comments.values()],
|
|
419
463
|
concerns: [...view.decisions.values()],
|
|
420
464
|
resolved: [...view.resolved],
|
|
465
|
+
excluded: { comments: [...view.excludedComments], concerns: [...view.excludedConcerns] },
|
|
421
466
|
seq: events.length,
|
|
422
467
|
previous: view.loaded.previous,
|
|
423
468
|
}),
|
|
424
469
|
/** Events after `since`, now and as they happen, until unfollowed or the review ends. */
|
|
425
470
|
latest: () => events.length,
|
|
426
|
-
follow: (since, send, stop) => {
|
|
471
|
+
follow: (since, send, stop, agent) => {
|
|
427
472
|
if (since > events.length) {
|
|
428
473
|
throw new HttpError(409, `since ${since} is ahead of this session's log (${events.length})`);
|
|
429
474
|
}
|
|
430
475
|
let last = since;
|
|
431
476
|
const flush = () => {
|
|
432
|
-
for (const event of
|
|
477
|
+
for (const event of seen(agent).filter((e) => e.seq > last)) {
|
|
433
478
|
send(event);
|
|
434
479
|
last = event.seq;
|
|
435
480
|
if (event.type === "ended") {
|
|
@@ -443,13 +488,21 @@ function hostSession(id, registration, loaded, draft, load) {
|
|
|
443
488
|
flush();
|
|
444
489
|
return () => listeners.delete(flush);
|
|
445
490
|
},
|
|
446
|
-
poll: (since, timeoutMs, signal) => {
|
|
491
|
+
poll: (since, timeoutMs, signal, agent) => {
|
|
447
492
|
if (since > events.length) {
|
|
448
493
|
throw new HttpError(409, `since ${since} is ahead of this session's log (${events.length})`);
|
|
449
494
|
}
|
|
450
|
-
return waitForEvents(since, timeoutMs, signal);
|
|
495
|
+
return waitForEvents(since, timeoutMs, signal, agent);
|
|
451
496
|
},
|
|
452
497
|
report: () => report(review()),
|
|
498
|
+
reportItems: () => {
|
|
499
|
+
const { state } = view.loaded;
|
|
500
|
+
const units = new Map(state.units.map((u) => [u.id, u]));
|
|
501
|
+
return reportItems(review(), state.commits, units, {
|
|
502
|
+
branch: state.anchor.branch,
|
|
503
|
+
responses: [],
|
|
504
|
+
});
|
|
505
|
+
},
|
|
453
506
|
add: async (raw) => {
|
|
454
507
|
open();
|
|
455
508
|
if (!isRecord(raw))
|
|
@@ -479,6 +532,7 @@ function hostSession(id, registration, loaded, draft, load) {
|
|
|
479
532
|
if (gone === undefined)
|
|
480
533
|
throw new HttpError(404, "no such comment");
|
|
481
534
|
view.comments.delete(target);
|
|
535
|
+
view.excludedComments.delete(gone.id);
|
|
482
536
|
emit({ type: "comment-deleted", id: gone.id });
|
|
483
537
|
await saveDraft();
|
|
484
538
|
},
|
|
@@ -506,15 +560,52 @@ function hostSession(id, registration, loaded, draft, load) {
|
|
|
506
560
|
if (!isConcernId(target) || !view.concernIds.has(target)) {
|
|
507
561
|
throw new HttpError(404, "no such concern in this plan");
|
|
508
562
|
}
|
|
509
|
-
const
|
|
510
|
-
if (typeof
|
|
511
|
-
throw new HttpError(400, `malformed decision: ${
|
|
563
|
+
const parsed = parseNewDecision(target, raw);
|
|
564
|
+
if (typeof parsed === "string")
|
|
565
|
+
throw new HttpError(400, `malformed decision: ${parsed}`);
|
|
566
|
+
const decision = { ...parsed, decidedAt: new Date().toISOString() };
|
|
512
567
|
view.decisions.set(target, decision);
|
|
513
568
|
const { concern: concernId, ...rest } = decision;
|
|
514
569
|
emit({ type: "concern", concernId, ...rest });
|
|
515
570
|
await saveDraft();
|
|
516
571
|
return decision;
|
|
517
572
|
},
|
|
573
|
+
/** Leaves a comment or an agent concern out of the hand-off, or puts it back. */
|
|
574
|
+
exclude: async (raw) => {
|
|
575
|
+
open();
|
|
576
|
+
const body = isRecord(raw) ? raw : {};
|
|
577
|
+
const { comment, concern, excluded } = body;
|
|
578
|
+
if (typeof excluded !== "boolean" || (comment === undefined) === (concern === undefined)) {
|
|
579
|
+
throw new HttpError(400, "expected { comment } or { concern }, and { excluded: boolean }");
|
|
580
|
+
}
|
|
581
|
+
if (comment !== undefined) {
|
|
582
|
+
if (!isCommentId(comment) || !view.comments.has(comment)) {
|
|
583
|
+
throw new HttpError(404, "no such comment");
|
|
584
|
+
}
|
|
585
|
+
if (excluded)
|
|
586
|
+
view.excludedComments.add(comment);
|
|
587
|
+
else
|
|
588
|
+
view.excludedComments.delete(comment);
|
|
589
|
+
}
|
|
590
|
+
else {
|
|
591
|
+
if (!isConcernId(concern) || !view.concernIds.has(concern)) {
|
|
592
|
+
throw new HttpError(404, "no such concern in this plan");
|
|
593
|
+
}
|
|
594
|
+
if (excluded)
|
|
595
|
+
view.excludedConcerns.add(concern);
|
|
596
|
+
else
|
|
597
|
+
view.excludedConcerns.delete(concern);
|
|
598
|
+
}
|
|
599
|
+
await saveDraft();
|
|
600
|
+
return { comments: [...view.excludedComments], concerns: [...view.excludedConcerns] };
|
|
601
|
+
},
|
|
602
|
+
/** Releases every held flag in one go; the reviewer pressed Submit. */
|
|
603
|
+
submit: async () => {
|
|
604
|
+
open();
|
|
605
|
+
const released = release();
|
|
606
|
+
await saveDraft();
|
|
607
|
+
return { released };
|
|
608
|
+
},
|
|
518
609
|
resolve: async (raw) => {
|
|
519
610
|
open();
|
|
520
611
|
const body = isRecord(raw) ? raw : {};
|
|
@@ -551,6 +642,7 @@ function hostSession(id, registration, loaded, draft, load) {
|
|
|
551
642
|
try {
|
|
552
643
|
await writes;
|
|
553
644
|
const next = await load({ ...view.registration, version, hash, lockToken: null }, view.loaded.release);
|
|
645
|
+
release();
|
|
554
646
|
if (hasWork())
|
|
555
647
|
await writeReview(review());
|
|
556
648
|
else
|
|
@@ -566,6 +658,7 @@ function hostSession(id, registration, loaded, draft, load) {
|
|
|
566
658
|
end: async () => {
|
|
567
659
|
open();
|
|
568
660
|
ending = true;
|
|
661
|
+
release();
|
|
569
662
|
const finished = review();
|
|
570
663
|
const file = await writeReview(finished);
|
|
571
664
|
view.loaded.release();
|
|
@@ -588,6 +681,7 @@ function sameRegistration(a, b) {
|
|
|
588
681
|
* itself once it has had no session for idleMs.
|
|
589
682
|
*/
|
|
590
683
|
export function startServer(opts) {
|
|
684
|
+
const uiDir = opts.uiDir ?? UI_DIR;
|
|
591
685
|
const registry = new Map();
|
|
592
686
|
const pending = new Map();
|
|
593
687
|
let boundPort = 0;
|
|
@@ -671,17 +765,21 @@ export function startServer(opts) {
|
|
|
671
765
|
const method = req.method ?? "GET";
|
|
672
766
|
const url = new URL(req.url ?? "/", "http://localhost");
|
|
673
767
|
const path = url.pathname;
|
|
674
|
-
const asset = method === "GET" ? STATIC_FILE.exec(path)
|
|
675
|
-
if (asset !==
|
|
676
|
-
const
|
|
677
|
-
const
|
|
678
|
-
|
|
768
|
+
const asset = method === "GET" ? STATIC_FILE.exec(path) : null;
|
|
769
|
+
if (asset !== null) {
|
|
770
|
+
const [, name = "", ext = ""] = asset;
|
|
771
|
+
const bytes = await readUi(uiDir, name);
|
|
772
|
+
if (bytes === null)
|
|
773
|
+
throw new HttpError(404, "not found");
|
|
774
|
+
// Vite hashes every name under assets/, so those never change under the same URL.
|
|
775
|
+
const cache = name.startsWith("assets/") ? "max-age=31536000, immutable" : "no-store";
|
|
776
|
+
res.writeHead(200, {
|
|
777
|
+
"content-type": STATIC_TYPES.get(ext) ?? "text/plain",
|
|
778
|
+
"cache-control": cache,
|
|
779
|
+
});
|
|
679
780
|
res.end(bytes);
|
|
680
781
|
return;
|
|
681
782
|
}
|
|
682
|
-
const vendor = method === "GET" ? vendorFile(path) : null;
|
|
683
|
-
if (vendor)
|
|
684
|
-
return sendVendor(res, vendor);
|
|
685
783
|
if (method === "GET" && path === "/") {
|
|
686
784
|
const rows = [...registry.values()].map((h) => ({
|
|
687
785
|
summary: h.summary(),
|
|
@@ -711,11 +809,12 @@ export function startServer(opts) {
|
|
|
711
809
|
res.end();
|
|
712
810
|
return;
|
|
713
811
|
}
|
|
714
|
-
if (method === "GET" && rest === "/") {
|
|
812
|
+
if (method === "GET" && (rest === "/" || rest === "/report")) {
|
|
715
813
|
if (!isSessionId(rawId) || !registry.has(rawId)) {
|
|
716
814
|
return sendHtml(res, 404, '<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="color-scheme" content="light dark" /><title>follow-me</title><link rel="stylesheet" href="/style.css" /></head><body class="index"><header class="topbar"><strong>follow-me</strong></header><main class="index-main"><section class="empty"><h1>This session has ended</h1><p>It finished, or it never existed. <a href="/">All sessions</a></p></section></main></body></html>\n');
|
|
717
815
|
}
|
|
718
|
-
|
|
816
|
+
const page = await readUi(uiDir, "index.html");
|
|
817
|
+
return page === null ? sendHtml(res, 503, NOT_BUILT) : sendHtml(res, 200, page.toString());
|
|
719
818
|
}
|
|
720
819
|
const session = hosted(rawId);
|
|
721
820
|
if (method === "GET" && rest === "/api/state")
|
|
@@ -733,7 +832,7 @@ export function startServer(opts) {
|
|
|
733
832
|
});
|
|
734
833
|
res.write(": follow-me\n\n");
|
|
735
834
|
const ping = setInterval(() => res.write(": ping\n\n"), STREAM_PING_MS);
|
|
736
|
-
const unfollow = session.follow(since, (event) => res.write(`id: ${event.seq}\ndata: ${JSON.stringify(event)}\n\n`), () => res.end());
|
|
835
|
+
const unfollow = session.follow(since, (event) => res.write(`id: ${event.seq}\ndata: ${JSON.stringify(event)}\n\n`), () => res.end(), url.searchParams.get("audience") === "agent");
|
|
737
836
|
res.on("close", () => {
|
|
738
837
|
clearInterval(ping);
|
|
739
838
|
unfollow();
|
|
@@ -745,7 +844,11 @@ export function startServer(opts) {
|
|
|
745
844
|
res.on("close", () => abort.abort());
|
|
746
845
|
const since = queryInt(url, "since", 0, Number.MAX_SAFE_INTEGER, 0);
|
|
747
846
|
const timeout = queryInt(url, "timeout", 0, MAX_POLL_MS, DEFAULT_POLL_MS);
|
|
748
|
-
|
|
847
|
+
const agent = url.searchParams.get("audience") === "agent";
|
|
848
|
+
return sendJson(res, 200, await session.poll(since, timeout, abort.signal, agent));
|
|
849
|
+
}
|
|
850
|
+
if (method === "GET" && rest === "/api/report" && url.searchParams.get("format") === "json") {
|
|
851
|
+
return sendJson(res, 200, session.reportItems());
|
|
749
852
|
}
|
|
750
853
|
if (method === "GET" && rest === "/api/report") {
|
|
751
854
|
res.writeHead(200, { "content-type": "text/markdown; charset=utf-8" });
|
|
@@ -762,6 +865,12 @@ export function startServer(opts) {
|
|
|
762
865
|
await session.swap(version, hash);
|
|
763
866
|
return sendJson(res, 200, session.summary());
|
|
764
867
|
}
|
|
868
|
+
if (method === "POST" && rest === "/api/submit") {
|
|
869
|
+
return sendJson(res, 200, await session.submit());
|
|
870
|
+
}
|
|
871
|
+
if (method === "POST" && rest === "/api/excluded") {
|
|
872
|
+
return sendJson(res, 200, await session.exclude(await readJson(req)));
|
|
873
|
+
}
|
|
765
874
|
if (method === "POST" && rest === "/api/resolved") {
|
|
766
875
|
return sendJson(res, 200, { resolved: await session.resolve(await readJson(req)) });
|
|
767
876
|
}
|
package/dist/src/session.js
CHANGED
|
@@ -202,8 +202,9 @@ function parseApplied(v) {
|
|
|
202
202
|
function makeSession(root, dir, anchor, diff) {
|
|
203
203
|
const plansDir = join(dir, "plans");
|
|
204
204
|
const splitReviews = join(dir, "reviews");
|
|
205
|
-
|
|
206
|
-
const
|
|
205
|
+
// Older builds wrote reviewer reviews to reviews/walk/; they stay readable, nothing new goes there.
|
|
206
|
+
const legacyWalkDir = join(splitReviews, "walk");
|
|
207
|
+
const reviewsDir = () => splitReviews;
|
|
207
208
|
const draftPath = join(dir, "draft.json");
|
|
208
209
|
const promptPath = join(dir, "prompt.md");
|
|
209
210
|
const appliedPath = join(dir, "applied.json");
|
|
@@ -216,16 +217,16 @@ function makeSession(root, dir, anchor, diff) {
|
|
|
216
217
|
}
|
|
217
218
|
return { version, file, hash: planHash(file) };
|
|
218
219
|
});
|
|
219
|
-
const reviewsIn = (
|
|
220
|
+
const reviewsIn = (dir) => versions(dir).map(({ version, path }) => {
|
|
220
221
|
const review = parseReview(readJson(path));
|
|
221
222
|
if (typeof review === "string")
|
|
222
223
|
throw new SessionFileError(`${path}: ${review}`);
|
|
223
224
|
return { version, review };
|
|
224
225
|
});
|
|
225
|
-
const reviews = () => reviewsIn(
|
|
226
|
-
const walkReviews = () => [...reviews().filter((r) => r.review.mode === "walk"), ...reviewsIn(
|
|
227
|
-
const draftVersions = (
|
|
228
|
-
const dir = reviewsDir(
|
|
226
|
+
const reviews = () => reviewsIn(splitReviews);
|
|
227
|
+
const walkReviews = () => [...reviews().filter((r) => r.review.mode === "walk"), ...reviewsIn(legacyWalkDir)].sort((a, b) => a.version - b.version);
|
|
228
|
+
const draftVersions = () => {
|
|
229
|
+
const dir = reviewsDir();
|
|
229
230
|
if (!existsSync(dir))
|
|
230
231
|
return [];
|
|
231
232
|
return readdirSync(dir).flatMap((name) => {
|
|
@@ -234,11 +235,11 @@ function makeSession(root, dir, anchor, diff) {
|
|
|
234
235
|
});
|
|
235
236
|
};
|
|
236
237
|
const draftReview = (mode, version) => {
|
|
237
|
-
const path = join(reviewsDir(
|
|
238
|
+
const path = join(reviewsDir(), `v${version}.draft.json`);
|
|
238
239
|
const draft = parseDraft(readJson(path));
|
|
239
240
|
if (typeof draft === "string")
|
|
240
241
|
throw new SessionFileError(`${path}: ${draft}`);
|
|
241
|
-
const { comments, concerns, resolved } = draft;
|
|
242
|
+
const { comments, concerns, resolved, excluded } = draft;
|
|
242
243
|
return {
|
|
243
244
|
schema: "follow-me/review@1",
|
|
244
245
|
mode,
|
|
@@ -248,6 +249,7 @@ function makeSession(root, dir, anchor, diff) {
|
|
|
248
249
|
comments,
|
|
249
250
|
...(concerns.length === 0 ? {} : { concerns }),
|
|
250
251
|
...(resolved.length === 0 ? {} : { resolved }),
|
|
252
|
+
...(excluded.comments.length + excluded.concerns.length === 0 ? {} : { excluded }),
|
|
251
253
|
};
|
|
252
254
|
};
|
|
253
255
|
const priorReview = (mode, before) => {
|
|
@@ -255,7 +257,7 @@ function makeSession(root, dir, anchor, diff) {
|
|
|
255
257
|
const done = (mode === "walk" ? walkReviews() : reviews().filter((r) => r.review.mode === mode))
|
|
256
258
|
.filter((r) => below(r.version))
|
|
257
259
|
.at(-1);
|
|
258
|
-
const unfinished = draftVersions(
|
|
260
|
+
const unfinished = draftVersions()
|
|
259
261
|
.filter((v) => below(v) && v > (done?.version ?? 0))
|
|
260
262
|
.sort((a, b) => a - b)
|
|
261
263
|
.at(-1);
|
|
@@ -283,7 +285,7 @@ function makeSession(root, dir, anchor, diff) {
|
|
|
283
285
|
writeDraft: (draft) => writeJson(draftPath, draft),
|
|
284
286
|
writePrompt: (text) => writeText(promptPath, text),
|
|
285
287
|
writeReview: (review) => {
|
|
286
|
-
const path = join(reviewsDir(
|
|
288
|
+
const path = join(reviewsDir(), `v${review.planVersion}.json`);
|
|
287
289
|
if (existsSync(path))
|
|
288
290
|
throw new SessionFileError(`${path} is already written`);
|
|
289
291
|
writeJson(path, review);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{m as e,t}from"./mermaid-parser.core-Dcxlzc-S.js";import{n}from"./chunk-Y2CYZVJY-DsF7k-Jl.js";import{m as r}from"./src-FgijoBVs.js";import"./chunk-DSNLSHJZ-DiC78RqE.js";import{n as i,r as a,t as o}from"./chunk-2E7WHVMD-C6fAf4Vj.js";import{t as s}from"./chunk-JWPE2WC7-DVXcaiue.js";import"./mermaid.core-TeOHQJ00.js";var c=e().RailroadAbnf.parser.LangiumParser,l=n(e=>{let t=e.alternatives.map(u);return t.length===1?t[0]:{type:`choice`,alternatives:t}},`transformAlternation`),u=n(e=>{let t=e.elements.map(f);return t.length===1?t[0]:{type:`sequence`,elements:t}},`transformConcatenation`),d=n(e=>{if(e.includes(`*`)){let[t,n]=e.split(`*`);return{min:t?parseInt(t,10):0,max:n?parseInt(n,10):1/0}}let t=parseInt(e,10);return{min:t,max:t}},`parseRepeat`),f=n(e=>{let t=p(e.primary);if(!e.repeat)return t;let{min:n,max:r}=d(e.repeat);return n===0&&r===1?{type:`optional`,element:t}:{type:`repetition`,element:t,min:n,max:r}},`transformElement`),p=n(e=>{switch(e.$type){case`AbnfStringLiteral`:return{type:`terminal`,value:e.value};case`AbnfNumVal`:return{type:`terminal`,value:e.value};case`AbnfRuleName`:return{type:`nonterminal`,name:e.name};case`AbnfGroup`:return l(e.element);case`AbnfOptionalGroup`:return{type:`optional`,element:l(e.element)};default:throw Error(`Unsupported ABNF primary node: ${e.$type}`)}},`transformPrimary`),m=n(e=>({name:e.name,definition:l(e.definition)}),`transformRule`),h=n(e=>{s(e,o),e.title&&o.setTitle(e.title),e.rules.map(e=>o.addRule(m(e)))},`populateDb`),g={parser:{parse:n(e=>{o.clear(),r.debug(`[ABNF Parser] Starting Langium parse`);let n=c.parse(e);if(n.lexerErrors.length>0||n.parserErrors.length>0)throw new t(n);let i=n.value;r.debug(`[ABNF Parser] Parsed rules:`,i.rules.length),h(i),r.debug(`[ABNF Parser] Parse complete`)},`parse`),parser:{yy:o}},db:o,renderer:a,styles:i};export{g as diagram};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{n as e,t}from"./path-fybaL0A-.js";import{a as n,c as r,d as i,f as a,i as o,l as s,m as c,n as l,o as u,p as d,r as f,u as p}from"./dist-tWS_lXDS.js";function m(e){return e.innerRadius}function h(e){return e.outerRadius}function g(e){return e.startAngle}function _(e){return e.endAngle}function v(e){return e&&e.padAngle}function y(e,t,n,r,i,a,o,s){var c=n-e,l=r-t,u=o-i,d=s-a,f=d*c-u*l;if(!(f*f<1e-12))return f=(u*(t-a)-d*(e-i))/f,[e+f*c,t+f*l]}function b(e,t,n,r,i,a,o){var c=e-n,l=t-r,u=(o?a:-a)/d(c*c+l*l),f=u*l,p=-u*c,m=e+f,h=t+p,g=n+f,_=r+p,v=(m+g)/2,y=(h+_)/2,b=g-m,x=_-h,S=b*b+x*x,C=i-a,w=m*_-g*h,T=(x<0?-1:1)*d(s(0,C*C*S-w*w)),E=(w*x-b*T)/S,D=(-w*b-x*T)/S,O=(w*x+b*T)/S,k=(-w*b+x*T)/S,A=E-v,j=D-y,M=O-v,N=k-y;return A*A+j*j>M*M+N*N&&(E=O,D=k),{cx:E,cy:D,x01:-f,y01:-p,x11:E*(i/C-1),y11:D*(i/C-1)}}function x(){var s=m,x=h,S=e(0),C=null,w=g,T=_,E=v,D=null,O=t(k);function k(){var e,t,m=+s.apply(this,arguments),h=+x.apply(this,arguments),g=w.apply(this,arguments)-r,_=T.apply(this,arguments)-r,v=l(_-g),k=_>g;if(D||=e=O(),h<m&&(t=h,h=m,m=t),!(h>1e-12))D.moveTo(0,0);else if(v>c-1e-12)D.moveTo(h*u(g),h*a(g)),D.arc(0,0,h,g,_,!k),m>1e-12&&(D.moveTo(m*u(_),m*a(_)),D.arc(0,0,m,_,g,k));else{var A=g,j=_,M=g,N=_,P=v,F=v,I=E.apply(this,arguments)/2,L=I>1e-12&&(C?+C.apply(this,arguments):d(m*m+h*h)),R=p(l(h-m)/2,+S.apply(this,arguments)),z=R,B=R,V,H;if(L>1e-12){var U=o(L/m*a(I)),W=o(L/h*a(I));(P-=U*2)>1e-12?(U*=k?1:-1,M+=U,N-=U):(P=0,M=N=(g+_)/2),(F-=W*2)>1e-12?(W*=k?1:-1,A+=W,j-=W):(F=0,A=j=(g+_)/2)}var G=h*u(A),K=h*a(A),q=m*u(N),J=m*a(N);if(R>1e-12){var Y=h*u(j),X=h*a(j),Z=m*u(M),Q=m*a(M),$;if(v<i){if($=y(G,K,Z,Q,Y,X,q,J)){var ee=G-$[0],te=K-$[1],ne=Y-$[0],re=X-$[1],ie=1/a(f((ee*ne+te*re)/(d(ee*ee+te*te)*d(ne*ne+re*re)))/2),ae=d($[0]*$[0]+$[1]*$[1]);z=p(R,(m-ae)/(ie-1)),B=p(R,(h-ae)/(ie+1))}else z=B=0}}F>1e-12?B>1e-12?(V=b(Z,Q,G,K,h,B,k),H=b(Y,X,q,J,h,B,k),D.moveTo(V.cx+V.x01,V.cy+V.y01),B<R?D.arc(V.cx,V.cy,B,n(V.y01,V.x01),n(H.y01,H.x01),!k):(D.arc(V.cx,V.cy,B,n(V.y01,V.x01),n(V.y11,V.x11),!k),D.arc(0,0,h,n(V.cy+V.y11,V.cx+V.x11),n(H.cy+H.y11,H.cx+H.x11),!k),D.arc(H.cx,H.cy,B,n(H.y11,H.x11),n(H.y01,H.x01),!k))):(D.moveTo(G,K),D.arc(0,0,h,A,j,!k)):D.moveTo(G,K),!(m>1e-12)||!(P>1e-12)?D.lineTo(q,J):z>1e-12?(V=b(q,J,Y,X,m,-z,k),H=b(G,K,Z,Q,m,-z,k),D.lineTo(V.cx+V.x01,V.cy+V.y01),z<R?D.arc(V.cx,V.cy,z,n(V.y01,V.x01),n(H.y01,H.x01),!k):(D.arc(V.cx,V.cy,z,n(V.y01,V.x01),n(V.y11,V.x11),!k),D.arc(0,0,m,n(V.cy+V.y11,V.cx+V.x11),n(H.cy+H.y11,H.cx+H.x11),k),D.arc(H.cx,H.cy,z,n(H.y11,H.x11),n(H.y01,H.x01),!k))):D.arc(0,0,m,N,M,k)}if(D.closePath(),e)return D=null,e+``||null}return k.centroid=function(){var e=(+s.apply(this,arguments)+ +x.apply(this,arguments))/2,t=(+w.apply(this,arguments)+ +T.apply(this,arguments))/2-i/2;return[u(t)*e,a(t)*e]},k.innerRadius=function(t){return arguments.length?(s=typeof t==`function`?t:e(+t),k):s},k.outerRadius=function(t){return arguments.length?(x=typeof t==`function`?t:e(+t),k):x},k.cornerRadius=function(t){return arguments.length?(S=typeof t==`function`?t:e(+t),k):S},k.padRadius=function(t){return arguments.length?(C=t==null?null:typeof t==`function`?t:e(+t),k):C},k.startAngle=function(t){return arguments.length?(w=typeof t==`function`?t:e(+t),k):w},k.endAngle=function(t){return arguments.length?(T=typeof t==`function`?t:e(+t),k):T},k.padAngle=function(t){return arguments.length?(E=typeof t==`function`?t:e(+t),k):E},k.context=function(e){return arguments.length?(D=e??null,k):D},k}export{x as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./chunk-NGNAAXSQ-C2Wy9Xmz.js";export{M as createArchitectureServices}from"./mermaid-parser.core-Dcxlzc-S.js";
|