@k-l-lambda/portolan 0.0.0-stage → 0.1.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/README.md +104 -2
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +42 -0
- package/dist/cli.js.map +1 -0
- package/dist/derive.d.ts +17 -0
- package/dist/derive.js +67 -0
- package/dist/derive.js.map +1 -0
- package/dist/edit.d.ts +69 -0
- package/dist/edit.js +302 -0
- package/dist/edit.js.map +1 -0
- package/dist/format.d.ts +9 -0
- package/dist/format.js +35 -0
- package/dist/format.js.map +1 -0
- package/dist/generated/line-parser.d.ts +213 -0
- package/dist/generated/line-parser.js +1920 -0
- package/dist/history.d.ts +90 -0
- package/dist/history.js +182 -0
- package/dist/history.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/parse.d.ts +33 -0
- package/dist/parse.js +468 -0
- package/dist/parse.js.map +1 -0
- package/dist/resolve.d.ts +41 -0
- package/dist/resolve.js +214 -0
- package/dist/resolve.js.map +1 -0
- package/dist/server.d.ts +9 -0
- package/dist/server.js +376 -0
- package/dist/server.js.map +1 -0
- package/dist/threads.d.ts +93 -0
- package/dist/threads.js +128 -0
- package/dist/threads.js.map +1 -0
- package/dist/types.d.ts +62 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/docs/rhumb-spec.md +289 -0
- package/docs/skill.md +320 -0
- package/package.json +78 -4
- package/web/dist/assets/index-CB0_S5Fp.js +106 -0
- package/web/dist/assets/index-Czapgw6a.css +1 -0
- package/web/dist/favicon.svg +16 -0
- package/web/dist/index.html +14 -0
package/dist/resolve.js
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
// Anchor resolution (spec section 4): link target → file, heading and entry line.
|
|
2
|
+
// File access goes through `readFile` so the same code can run against a server or tests.
|
|
3
|
+
import { dirname, isAbsolute, resolve as resolvePath } from "node:path";
|
|
4
|
+
import { diagnostic } from "./parse.js";
|
|
5
|
+
const MARKDOWN = /\.(md|markdown|rhumb)$/i;
|
|
6
|
+
export function resolveAnchor(anchor, ctx) {
|
|
7
|
+
const out = { url: null, file: null, heading: null, entryLine: null, line: null, lineEnd: null, diagnostics: [] };
|
|
8
|
+
const warn = (msg) => out.diagnostics.push(diagnostic("W003", anchor.line, msg));
|
|
9
|
+
if (anchor.kind === "url") {
|
|
10
|
+
out.url = anchor.target;
|
|
11
|
+
return out;
|
|
12
|
+
}
|
|
13
|
+
let path = anchor.path ?? "";
|
|
14
|
+
if (anchor.kind === "prefix") {
|
|
15
|
+
const template = ctx.links[anchor.prefix];
|
|
16
|
+
if (template === undefined)
|
|
17
|
+
return out; // E005 is reported by the parser.
|
|
18
|
+
path = template.replace("{path}", path);
|
|
19
|
+
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(path)) {
|
|
20
|
+
out.url = path + (anchor.fragment ? `#${anchor.fragment}` : "");
|
|
21
|
+
return out;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
path = safeDecode(path);
|
|
25
|
+
out.file = isAbsolute(path) ? path : resolvePath(ctx.baseDir, path);
|
|
26
|
+
const text = ctx.readFile(out.file);
|
|
27
|
+
if (text === null) {
|
|
28
|
+
warn(`File not found: ${out.file}`);
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
31
|
+
if (anchor.fragment_error) {
|
|
32
|
+
warn(`Bad link fragment in ${anchor.target}: ${anchor.fragment_error}`);
|
|
33
|
+
return out;
|
|
34
|
+
}
|
|
35
|
+
const lines = text.replace(/\r\n?/g, "\n").split("\n");
|
|
36
|
+
if (lines.length > 1 && lines[lines.length - 1] === "")
|
|
37
|
+
lines.pop();
|
|
38
|
+
// Absolute lines work in any text file.
|
|
39
|
+
if (anchor.lines) {
|
|
40
|
+
const [a, b] = anchor.lines;
|
|
41
|
+
if (b > lines.length)
|
|
42
|
+
warn(`Line ${b > a ? `range L${a}-L${b}` : `L${a}`} is past the end of ${out.file} (${lines.length} lines)`);
|
|
43
|
+
else
|
|
44
|
+
(out.line = a), (out.lineEnd = b);
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
// Section: the heading's lines (Markdown only), else the whole file.
|
|
48
|
+
let from = 1, to = lines.length;
|
|
49
|
+
if (anchor.fragment) {
|
|
50
|
+
if (!MARKDOWN.test(out.file)) {
|
|
51
|
+
warn(`Heading #${anchor.fragment} used on a non-Markdown file ${out.file}`);
|
|
52
|
+
return out;
|
|
53
|
+
}
|
|
54
|
+
const h = findHeading(text, anchor.fragment, out.file, warn);
|
|
55
|
+
if (!h)
|
|
56
|
+
return out;
|
|
57
|
+
out.heading = { text: h.text, line: h.line };
|
|
58
|
+
from = h.line;
|
|
59
|
+
to = (h.end ?? lines.length + 1) - 1;
|
|
60
|
+
}
|
|
61
|
+
if (anchor.text_fragment) {
|
|
62
|
+
if (!out.heading)
|
|
63
|
+
return out;
|
|
64
|
+
out.entryLine = findEntry(lines, from, to + 1, anchor.text_fragment);
|
|
65
|
+
if (out.entryLine === null)
|
|
66
|
+
warn(`Text "${anchor.text_fragment}" not found under #${anchor.fragment}`);
|
|
67
|
+
return out;
|
|
68
|
+
}
|
|
69
|
+
let base = out.heading?.line ?? null;
|
|
70
|
+
if (anchor.line_prefix !== null) {
|
|
71
|
+
base = null;
|
|
72
|
+
for (let i = from; i <= to; i++) {
|
|
73
|
+
if (lines[i - 1].replace(/^\s+/, "").startsWith(anchor.line_prefix)) {
|
|
74
|
+
base = i;
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (base === null) {
|
|
79
|
+
warn(`No line starting with "${anchor.line_prefix}" ${anchor.fragment ? `under #${anchor.fragment}` : `in ${out.file}`}`);
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (base === null)
|
|
84
|
+
return out; // heading only
|
|
85
|
+
const target = base + (anchor.line_offset ?? 0);
|
|
86
|
+
if (target < from || target > to) {
|
|
87
|
+
const n = anchor.line_offset;
|
|
88
|
+
warn(`Offset ${n > 0 ? "+" : "-"}L${Math.abs(n)} from line ${base} leaves ${anchor.fragment ? `#${anchor.fragment}` : out.file} (lines ${from}-${to})`);
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
if (anchor.line_prefix !== null || anchor.line_offset !== null)
|
|
92
|
+
(out.line = target), (out.lineEnd = target);
|
|
93
|
+
return out;
|
|
94
|
+
}
|
|
95
|
+
/** The heading a slug (or unique slug prefix) names, with the line where its section ends. */
|
|
96
|
+
function findHeading(text, slug, file, warn) {
|
|
97
|
+
const headings = markdownHeadings(text);
|
|
98
|
+
const exact = headings.filter((h) => h.slug === slug);
|
|
99
|
+
const matches = exact.length > 0 ? exact : headings.filter((h) => h.slug.startsWith(slug));
|
|
100
|
+
if (matches.length === 0) {
|
|
101
|
+
warn(`Heading #${slug} not found in ${file}`);
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
if (matches.length > 1) {
|
|
105
|
+
warn(`Heading prefix #${slug} is ambiguous in ${file}`);
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
const h = matches[0];
|
|
109
|
+
return { ...h, end: headings.find((x) => x.line > h.line && x.depth <= h.depth)?.line ?? null };
|
|
110
|
+
}
|
|
111
|
+
/** Resolves every anchor in the document and returns only the diagnostics. */
|
|
112
|
+
export function checkAnchors(doc, ctx) {
|
|
113
|
+
const all = [];
|
|
114
|
+
const walk = (nodes) => nodes.forEach((n) => (all.push(n), walk(n.children)));
|
|
115
|
+
walk(doc.nodes);
|
|
116
|
+
return all.flatMap((n) => n.anchors.flatMap((a) => resolveAnchor(a, ctx).diagnostics));
|
|
117
|
+
}
|
|
118
|
+
/** The Markdown excerpt an anchor points at: the matched entry, else the heading section. */
|
|
119
|
+
export function excerpt(text, r, maxLines = 200) {
|
|
120
|
+
const lines = text.replace(/\r\n?/g, "\n").split("\n");
|
|
121
|
+
if (r.line !== null) {
|
|
122
|
+
// A range is shown as is; a single line extends to the end of its list entry or paragraph.
|
|
123
|
+
let end = r.lineEnd !== null && r.lineEnd > r.line ? r.lineEnd : r.line;
|
|
124
|
+
if (end === r.line) {
|
|
125
|
+
const first = lines[r.line - 1] ?? "";
|
|
126
|
+
const indent = first.search(/\S/);
|
|
127
|
+
const listItem = /^\s*[*-]\s/.test(first);
|
|
128
|
+
while (end < lines.length && end - r.line < maxLines - 1) {
|
|
129
|
+
const next = lines[end];
|
|
130
|
+
if (listItem ? isEntryBoundary(next, indent) : next.trim() === "" || /^#{1,6}\s/.test(next))
|
|
131
|
+
break;
|
|
132
|
+
end++;
|
|
133
|
+
}
|
|
134
|
+
while (end > r.line && lines[end - 1].trim() === "")
|
|
135
|
+
end--;
|
|
136
|
+
}
|
|
137
|
+
return lines.slice(r.line - 1, Math.min(end, r.line - 1 + maxLines)).join("\n");
|
|
138
|
+
}
|
|
139
|
+
const start = r.entryLine ?? r.heading?.line;
|
|
140
|
+
if (!start)
|
|
141
|
+
return null;
|
|
142
|
+
let end = start;
|
|
143
|
+
if (r.entryLine) {
|
|
144
|
+
// An entry ends at the next top-level list item or heading.
|
|
145
|
+
const indent = lines[start - 1].search(/\S/);
|
|
146
|
+
while (end < lines.length && !isEntryBoundary(lines[end], indent))
|
|
147
|
+
end++;
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
const depth = /^(#+)/.exec(lines[start - 1])[1].length;
|
|
151
|
+
while (end < lines.length && !(/^(#+)\s/.exec(lines[end])?.[1].length <= depth))
|
|
152
|
+
end++;
|
|
153
|
+
}
|
|
154
|
+
return lines.slice(start - 1, Math.min(end, start - 1 + maxLines)).join("\n");
|
|
155
|
+
}
|
|
156
|
+
export function contextFor(rhumbPath, doc, readFile) {
|
|
157
|
+
return { baseDir: dirname(rhumbPath), links: doc.links, readFile };
|
|
158
|
+
}
|
|
159
|
+
/** ATX headings outside fenced code, with GitHub-style slugs (duplicates get -1, -2…). */
|
|
160
|
+
export function markdownHeadings(text) {
|
|
161
|
+
const out = [];
|
|
162
|
+
const seen = new Map();
|
|
163
|
+
let fence = null;
|
|
164
|
+
text.replace(/\r\n?/g, "\n").split("\n").forEach((line, i) => {
|
|
165
|
+
const f = /^\s*(```|~~~)/.exec(line);
|
|
166
|
+
if (f) {
|
|
167
|
+
fence = fence === null ? f[1] : fence === f[1] ? null : fence;
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
if (fence !== null)
|
|
171
|
+
return;
|
|
172
|
+
const m = /^(#{1,6})\s+(.*?)\s*#*\s*$/.exec(line);
|
|
173
|
+
if (!m)
|
|
174
|
+
return;
|
|
175
|
+
const base = githubSlug(m[2]);
|
|
176
|
+
const n = seen.get(base) ?? 0;
|
|
177
|
+
seen.set(base, n + 1);
|
|
178
|
+
out.push({ text: m[2], slug: n === 0 ? base : `${base}-${n}`, depth: m[1].length, line: i + 1 });
|
|
179
|
+
});
|
|
180
|
+
return out;
|
|
181
|
+
}
|
|
182
|
+
export function githubSlug(heading) {
|
|
183
|
+
return heading
|
|
184
|
+
.replace(/<[^>]*>/g, "")
|
|
185
|
+
.toLowerCase()
|
|
186
|
+
.replace(/[^\p{L}\p{N}\p{M}\s_-]/gu, "")
|
|
187
|
+
.replace(/\s/g, "-");
|
|
188
|
+
}
|
|
189
|
+
function findEntry(lines, from, to, needle) {
|
|
190
|
+
let entry = null;
|
|
191
|
+
for (let i = from; i < to - 1; i++) {
|
|
192
|
+
const line = lines[i];
|
|
193
|
+
if (/^[*-]\s/.test(line))
|
|
194
|
+
entry = i + 1;
|
|
195
|
+
if (entry !== null && line.includes(needle))
|
|
196
|
+
return entry;
|
|
197
|
+
}
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
function isEntryBoundary(line, indent) {
|
|
201
|
+
if (/^#{1,6}\s/.test(line))
|
|
202
|
+
return true;
|
|
203
|
+
const m = /^(\s*)[*-]\s/.exec(line);
|
|
204
|
+
return m !== null && m[1].length <= indent;
|
|
205
|
+
}
|
|
206
|
+
function safeDecode(s) {
|
|
207
|
+
try {
|
|
208
|
+
return decodeURIComponent(s);
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
return s;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
//# sourceMappingURL=resolve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"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"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Server } from "node:http";
|
|
2
|
+
export interface ServerOptions {
|
|
3
|
+
/** A directory (serves every .rhumb below it) or a single .rhumb file. */
|
|
4
|
+
root: string;
|
|
5
|
+
/** Built web app; defaults to web/dist next to src/. */
|
|
6
|
+
webDir?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function versionOf(source: string): string;
|
|
9
|
+
export declare function createRhumbServer(options: ServerOptions): Server;
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
// Local HTTP server for the Portolan frontend. Serves every .rhumb file under a root
|
|
2
|
+
// directory (or a single file): AST, derived values, anchor excerpts and threads; applies
|
|
3
|
+
// edits; pushes file changes over Server-Sent Events; and serves the built web app.
|
|
4
|
+
//
|
|
5
|
+
// Security: there is no authentication. The server binds to 127.0.0.1 by default and
|
|
6
|
+
// rejects requests whose Host header is not a loopback name (DNS-rebinding guard). Do not
|
|
7
|
+
// bind it to other interfaces until authentication exists.
|
|
8
|
+
import { createHash } from "node:crypto";
|
|
9
|
+
import { existsSync, readdirSync, readFileSync, statSync, watch, writeFileSync } from "node:fs";
|
|
10
|
+
import { createServer } from "node:http";
|
|
11
|
+
import { basename, dirname, extname, join, relative, resolve, sep } from "node:path";
|
|
12
|
+
import { fileURLToPath } from "node:url";
|
|
13
|
+
import { derive } from "./derive.js";
|
|
14
|
+
import { applyEdit, EditError } from "./edit.js";
|
|
15
|
+
import { HistoryTracker, nodeTimes } from "./history.js";
|
|
16
|
+
import { isStarred, parse } from "./parse.js";
|
|
17
|
+
import { checkAnchors, contextFor, excerpt, resolveAnchor } from "./resolve.js";
|
|
18
|
+
import { ThreadStore } from "./threads.js";
|
|
19
|
+
class HttpError extends Error {
|
|
20
|
+
status;
|
|
21
|
+
constructor(status, message) {
|
|
22
|
+
super(message);
|
|
23
|
+
this.status = status;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const MAX_BODY = 1 << 20;
|
|
27
|
+
const MAX_LINKED_FILE = 2 << 20;
|
|
28
|
+
const LOOPBACK_HOSTS = new Set(["localhost", "127.0.0.1", "[::1]"]);
|
|
29
|
+
const SKIP_DIRS = new Set(["node_modules", ".git", "dist"]);
|
|
30
|
+
const DEFAULT_WEB_DIR = fileURLToPath(new URL("../web/dist/", import.meta.url));
|
|
31
|
+
const MIME = {
|
|
32
|
+
".html": "text/html; charset=utf-8", ".js": "text/javascript; charset=utf-8",
|
|
33
|
+
".css": "text/css; charset=utf-8", ".svg": "image/svg+xml", ".png": "image/png",
|
|
34
|
+
".ico": "image/x-icon", ".json": "application/json; charset=utf-8", ".woff2": "font/woff2",
|
|
35
|
+
};
|
|
36
|
+
export function versionOf(source) {
|
|
37
|
+
return createHash("sha256").update(source).digest("hex").slice(0, 16);
|
|
38
|
+
}
|
|
39
|
+
function readLinked(path) {
|
|
40
|
+
try {
|
|
41
|
+
if (!existsSync(path) || statSync(path).size > MAX_LINKED_FILE)
|
|
42
|
+
return null;
|
|
43
|
+
return readFileSync(path, "utf8");
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/** Relative POSIX paths of all .rhumb files under `dir`, sorted. */
|
|
50
|
+
function scan(dir) {
|
|
51
|
+
const out = [];
|
|
52
|
+
const walk = (d) => {
|
|
53
|
+
for (const entry of readdirSync(d, { withFileTypes: true })) {
|
|
54
|
+
if (entry.name.startsWith(".") || SKIP_DIRS.has(entry.name))
|
|
55
|
+
continue;
|
|
56
|
+
const full = join(d, entry.name);
|
|
57
|
+
if (entry.isDirectory())
|
|
58
|
+
walk(full);
|
|
59
|
+
else if (entry.isFile() && entry.name.endsWith(".rhumb"))
|
|
60
|
+
out.push(relative(dir, full).split(sep).join("/"));
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
walk(dir);
|
|
64
|
+
return out.sort();
|
|
65
|
+
}
|
|
66
|
+
export function createRhumbServer(options) {
|
|
67
|
+
const rootPath = resolve(options.root);
|
|
68
|
+
const singleFile = statSync(rootPath).isFile() ? basename(rootPath) : null;
|
|
69
|
+
const rootDir = singleFile ? dirname(rootPath) : rootPath;
|
|
70
|
+
const webDir = resolve(options.webDir ?? DEFAULT_WEB_DIR);
|
|
71
|
+
const clients = new Set();
|
|
72
|
+
const versions = new Map();
|
|
73
|
+
const history = new HistoryTracker();
|
|
74
|
+
const listFiles = () => (singleFile ? [singleFile] : scan(rootDir));
|
|
75
|
+
/** Maps a client-supplied relative path to an absolute .rhumb path inside the root. */
|
|
76
|
+
const fileFor = (rel) => {
|
|
77
|
+
const name = rel ?? singleFile;
|
|
78
|
+
if (!name)
|
|
79
|
+
throw new HttpError(400, "Missing file parameter");
|
|
80
|
+
const abs = resolve(rootDir, name);
|
|
81
|
+
if (!abs.startsWith(rootDir + sep) || !abs.endsWith(".rhumb"))
|
|
82
|
+
throw new HttpError(400, "Invalid file");
|
|
83
|
+
if (singleFile && name !== singleFile)
|
|
84
|
+
throw new HttpError(404, "File not found");
|
|
85
|
+
if (!existsSync(abs) || !statSync(abs).isFile())
|
|
86
|
+
throw new HttpError(404, "File not found");
|
|
87
|
+
return { rel: relative(rootDir, abs).split(sep).join("/"), abs };
|
|
88
|
+
};
|
|
89
|
+
const load = (abs) => {
|
|
90
|
+
const source = readFileSync(abs, "utf8");
|
|
91
|
+
return { source, version: versionOf(source), doc: parse(source, { fileName: abs }) };
|
|
92
|
+
};
|
|
93
|
+
const broadcast = (event, data) => {
|
|
94
|
+
for (const res of clients)
|
|
95
|
+
res.write(`event: ${event}\ndata: ${JSON.stringify(data)}\n\n`);
|
|
96
|
+
};
|
|
97
|
+
// ---------- hot reload ----------
|
|
98
|
+
// One non-recursive watcher per directory. Node's recursive watcher on Linux stops
|
|
99
|
+
// reporting a file after it is deleted and recreated (what many editors do on save), so
|
|
100
|
+
// directories are watched individually and a periodic rescan catches anything missed.
|
|
101
|
+
// Events: `change` {file, version} when a file's content changes (including recreation),
|
|
102
|
+
// `files` {} when files appear or disappear, `threads` {file} when a sidecar changes,
|
|
103
|
+
// `history` {repo} when a repository's HEAD moves (commit, checkout, pull).
|
|
104
|
+
const stamps = new Map(); // rel → "mtime:size", to skip unchanged files when polling
|
|
105
|
+
const dirWatchers = new Map();
|
|
106
|
+
const pending = new Map();
|
|
107
|
+
const debounce = (key, fn) => {
|
|
108
|
+
clearTimeout(pending.get(key));
|
|
109
|
+
pending.set(key, setTimeout(() => (pending.delete(key), fn()), 50));
|
|
110
|
+
};
|
|
111
|
+
/** Re-reads one file; `force` skips the mtime/size shortcut (mtime can be coarse). */
|
|
112
|
+
const checkFile = (rel, force) => {
|
|
113
|
+
const abs = join(rootDir, rel);
|
|
114
|
+
let stat;
|
|
115
|
+
try {
|
|
116
|
+
stat = statSync(abs);
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
stat = null;
|
|
120
|
+
}
|
|
121
|
+
if (!stat?.isFile()) {
|
|
122
|
+
stamps.delete(rel);
|
|
123
|
+
if (versions.delete(rel))
|
|
124
|
+
broadcast("files", {});
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const stamp = `${stat.mtimeMs}:${stat.size}`;
|
|
128
|
+
if (!force && stamps.get(rel) === stamp)
|
|
129
|
+
return;
|
|
130
|
+
stamps.set(rel, stamp);
|
|
131
|
+
const version = versionOf(readFileSync(abs, "utf8"));
|
|
132
|
+
const known = versions.has(rel);
|
|
133
|
+
if (versions.get(rel) === version)
|
|
134
|
+
return;
|
|
135
|
+
versions.set(rel, version);
|
|
136
|
+
if (!known)
|
|
137
|
+
broadcast("files", {});
|
|
138
|
+
broadcast("change", { file: rel, version });
|
|
139
|
+
};
|
|
140
|
+
const dirsUnder = (dir) => {
|
|
141
|
+
const out = [dir];
|
|
142
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
143
|
+
if (entry.isDirectory() && !entry.name.startsWith(".") && !SKIP_DIRS.has(entry.name)) {
|
|
144
|
+
out.push(...dirsUnder(join(dir, entry.name)));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return out;
|
|
148
|
+
};
|
|
149
|
+
const syncWatchers = () => {
|
|
150
|
+
const dirs = new Set(singleFile ? [rootDir] : dirsUnder(rootDir));
|
|
151
|
+
for (const [dir, w] of dirWatchers)
|
|
152
|
+
if (!dirs.has(dir))
|
|
153
|
+
(w.close(), dirWatchers.delete(dir));
|
|
154
|
+
for (const dir of dirs) {
|
|
155
|
+
if (dirWatchers.has(dir))
|
|
156
|
+
continue;
|
|
157
|
+
try {
|
|
158
|
+
const w = watch(dir, (_type, name) => onEvent(dir, name?.toString() ?? null));
|
|
159
|
+
w.on("error", () => (w.close(), dirWatchers.delete(dir)));
|
|
160
|
+
dirWatchers.set(dir, w);
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
// Directory vanished between listing and watching; the next rescan handles it.
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
const rescan = () => {
|
|
168
|
+
history.pollHeads().then((repos) => repos.forEach((repo) => broadcast("history", { repo })), () => { });
|
|
169
|
+
syncWatchers();
|
|
170
|
+
const now = new Set(listFiles());
|
|
171
|
+
for (const rel of now)
|
|
172
|
+
checkFile(rel, false);
|
|
173
|
+
for (const rel of [...versions.keys()])
|
|
174
|
+
if (!now.has(rel))
|
|
175
|
+
checkFile(rel, true);
|
|
176
|
+
};
|
|
177
|
+
const onEvent = (dir, name) => {
|
|
178
|
+
const rel = name ? relative(rootDir, join(dir, name)).split(sep).join("/") : null;
|
|
179
|
+
if (rel && singleFile && rel !== singleFile && rel !== singleFile.replace(/\.rhumb$/, ".threads.jsonl"))
|
|
180
|
+
return;
|
|
181
|
+
if (rel?.endsWith(".threads.jsonl")) {
|
|
182
|
+
return debounce(rel, () => broadcast("threads", { file: rel.replace(/\.threads\.jsonl$/, ".rhumb") }));
|
|
183
|
+
}
|
|
184
|
+
if (rel?.endsWith(".rhumb"))
|
|
185
|
+
return debounce(rel, () => checkFile(rel, true));
|
|
186
|
+
// Anything else may be a new or removed directory, or an unnamed event.
|
|
187
|
+
debounce("*rescan", rescan);
|
|
188
|
+
};
|
|
189
|
+
rescan();
|
|
190
|
+
const poll = setInterval(rescan, 2000);
|
|
191
|
+
poll.unref();
|
|
192
|
+
const server = createServer(async (req, res) => {
|
|
193
|
+
try {
|
|
194
|
+
const host = (req.headers.host ?? "").replace(/:\d+$/, "");
|
|
195
|
+
if (!LOOPBACK_HOSTS.has(host))
|
|
196
|
+
return send(res, 403, { error: "Host not allowed" });
|
|
197
|
+
const url = new URL(req.url ?? "/", "http://localhost");
|
|
198
|
+
await route(req, res, url);
|
|
199
|
+
}
|
|
200
|
+
catch (err) {
|
|
201
|
+
if (res.headersSent)
|
|
202
|
+
return;
|
|
203
|
+
const status = err instanceof HttpError ? err.status : 500;
|
|
204
|
+
send(res, status, { error: String(err.message ?? err) });
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
async function route(req, res, url) {
|
|
208
|
+
const key = `${req.method} ${url.pathname}`;
|
|
209
|
+
const param = url.searchParams.get("file");
|
|
210
|
+
if (key === "GET /api/files") {
|
|
211
|
+
const files = listFiles().map((rel) => {
|
|
212
|
+
const { doc } = load(join(rootDir, rel));
|
|
213
|
+
const derived = derive(doc);
|
|
214
|
+
const roots = doc.nodes.map((n) => derived.nodes.find((s) => s.line === n.line).progress);
|
|
215
|
+
const progress = { done: 0, total: 0 };
|
|
216
|
+
for (const p of roots)
|
|
217
|
+
if (p)
|
|
218
|
+
(progress.done += p.done, progress.total += p.total);
|
|
219
|
+
const levels = [...doc.diagnostics, ...derived.diagnostics].map((d) => d.level);
|
|
220
|
+
const starred = [];
|
|
221
|
+
const walk = (ns) => ns.forEach((n) => {
|
|
222
|
+
if (n.id && isStarred(n))
|
|
223
|
+
starred.push({ id: n.id, title: n.title, status: n.status });
|
|
224
|
+
walk(n.children);
|
|
225
|
+
});
|
|
226
|
+
walk(doc.nodes);
|
|
227
|
+
return {
|
|
228
|
+
file: rel, title: doc.title, nodes: derived.nodes.length, progress, starred,
|
|
229
|
+
errors: levels.filter((l) => l === "error").length,
|
|
230
|
+
warnings: levels.filter((l) => l === "warning").length,
|
|
231
|
+
};
|
|
232
|
+
});
|
|
233
|
+
return send(res, 200, { root: rootDir, files });
|
|
234
|
+
}
|
|
235
|
+
if (key === "GET /api/doc") {
|
|
236
|
+
const { rel, abs } = fileFor(param);
|
|
237
|
+
const { version, doc } = load(abs);
|
|
238
|
+
const derived = derive(doc);
|
|
239
|
+
const anchors = checkAnchors(doc, contextFor(abs, doc, readLinked));
|
|
240
|
+
const diagnostics = [...doc.diagnostics, ...derived.diagnostics, ...anchors].sort((a, b) => a.line - b.line);
|
|
241
|
+
const h = await history.file(abs, version);
|
|
242
|
+
const freshness = {
|
|
243
|
+
repo: h.repo, head: h.head, tracked: h.tracked, mtime: h.mtime, dirtyLines: h.dirtyLines,
|
|
244
|
+
lastCommit: h.lastCommit, nodes: nodeTimes(doc, h), commits: h.commits,
|
|
245
|
+
};
|
|
246
|
+
return send(res, 200, { file: rel, version, doc: { ...doc, diagnostics }, derived: derived.nodes, freshness });
|
|
247
|
+
}
|
|
248
|
+
if (key === "POST /api/edit") {
|
|
249
|
+
// Optimistic concurrency: the client sends the version it edited against.
|
|
250
|
+
const { abs } = fileFor(param);
|
|
251
|
+
const body = await readJson(req);
|
|
252
|
+
const { source, version } = load(abs);
|
|
253
|
+
if (body.version !== version)
|
|
254
|
+
return send(res, 409, { error: "Document changed", version });
|
|
255
|
+
try {
|
|
256
|
+
const result = applyEdit(source, body.edit);
|
|
257
|
+
writeFileSync(abs, result.source);
|
|
258
|
+
if (result.renamed)
|
|
259
|
+
new ThreadStore(abs).retarget(result.renamed.from, result.renamed.to);
|
|
260
|
+
const { source: _s, ...rest } = result;
|
|
261
|
+
return send(res, 200, { ...rest, version: versionOf(result.source) });
|
|
262
|
+
}
|
|
263
|
+
catch (err) {
|
|
264
|
+
if (err instanceof EditError)
|
|
265
|
+
return send(res, 400, { error: err.message });
|
|
266
|
+
throw err;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
if (key === "GET /api/anchor") {
|
|
270
|
+
// ?node=<line>&index=<n>: the n-th anchor of the node on that line.
|
|
271
|
+
const { abs } = fileFor(param);
|
|
272
|
+
const { doc } = load(abs);
|
|
273
|
+
const node = findByLine(doc.nodes, Number(url.searchParams.get("node")));
|
|
274
|
+
const anchor = node?.anchors[Number(url.searchParams.get("index") ?? 0)];
|
|
275
|
+
if (!anchor)
|
|
276
|
+
return send(res, 404, { error: "Anchor not found" });
|
|
277
|
+
const r = resolveAnchor(anchor, contextFor(abs, doc, readLinked));
|
|
278
|
+
const text = r.file ? readLinked(r.file) : null;
|
|
279
|
+
return send(res, 200, { anchor, ...r, excerpt: text ? excerpt(text, r) : null });
|
|
280
|
+
}
|
|
281
|
+
if (key === "GET /api/threads") {
|
|
282
|
+
const { abs } = fileFor(param);
|
|
283
|
+
const { doc } = load(abs);
|
|
284
|
+
const ids = new Set();
|
|
285
|
+
const walk = (ns) => ns.forEach((n) => (n.id && ids.add(n.id), walk(n.children)));
|
|
286
|
+
walk(doc.nodes);
|
|
287
|
+
const status = url.searchParams.get("status");
|
|
288
|
+
const { threads, problems } = new ThreadStore(abs).load();
|
|
289
|
+
return send(res, 200, {
|
|
290
|
+
threads: threads.filter((t) => !status || t.status === status).map((t) => ({ ...t, orphan: !ids.has(t.target) })),
|
|
291
|
+
problems,
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
if (key === "POST /api/threads") {
|
|
295
|
+
const { abs } = fileFor(param);
|
|
296
|
+
const body = (await readJson(req));
|
|
297
|
+
try {
|
|
298
|
+
return send(res, 200, { event: new ThreadStore(abs).apply(body) });
|
|
299
|
+
}
|
|
300
|
+
catch (err) {
|
|
301
|
+
return send(res, 400, { error: err.message });
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
if (key === "GET /api/events") {
|
|
305
|
+
res.writeHead(200, { "content-type": "text/event-stream", "cache-control": "no-cache", connection: "keep-alive" });
|
|
306
|
+
res.write("event: hello\ndata: {}\n\n");
|
|
307
|
+
clients.add(res);
|
|
308
|
+
const ping = setInterval(() => res.write(": ping\n\n"), 30_000);
|
|
309
|
+
req.on("close", () => (clearInterval(ping), clients.delete(res)));
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
if (url.pathname.startsWith("/api/"))
|
|
313
|
+
return send(res, 404, { error: "Not found" });
|
|
314
|
+
if (req.method === "GET")
|
|
315
|
+
return serveStatic(res, url.pathname);
|
|
316
|
+
send(res, 404, { error: "Not found" });
|
|
317
|
+
}
|
|
318
|
+
function serveStatic(res, pathname) {
|
|
319
|
+
const index = join(webDir, "index.html");
|
|
320
|
+
if (!existsSync(index)) {
|
|
321
|
+
res.writeHead(503, { "content-type": "text/plain; charset=utf-8" });
|
|
322
|
+
return res.end("Web app is not built. Run `pnpm build:web` in the portolan repo.\n");
|
|
323
|
+
}
|
|
324
|
+
let path = resolve(webDir, "." + decodeURIComponent(pathname));
|
|
325
|
+
// Unknown paths fall back to index.html (client-side routing uses the hash, but be lenient).
|
|
326
|
+
if (!path.startsWith(webDir + sep) || !existsSync(path) || !statSync(path).isFile())
|
|
327
|
+
path = index;
|
|
328
|
+
res.writeHead(200, {
|
|
329
|
+
"content-type": MIME[extname(path)] ?? "application/octet-stream",
|
|
330
|
+
"x-content-type-options": "nosniff",
|
|
331
|
+
"content-security-policy": "default-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:",
|
|
332
|
+
});
|
|
333
|
+
res.end(readFileSync(path));
|
|
334
|
+
}
|
|
335
|
+
server.on("close", () => {
|
|
336
|
+
clearInterval(poll);
|
|
337
|
+
for (const w of dirWatchers.values())
|
|
338
|
+
w.close();
|
|
339
|
+
for (const t of pending.values())
|
|
340
|
+
clearTimeout(t);
|
|
341
|
+
for (const res of clients)
|
|
342
|
+
res.end();
|
|
343
|
+
});
|
|
344
|
+
return server;
|
|
345
|
+
}
|
|
346
|
+
function findByLine(nodes, line) {
|
|
347
|
+
for (const n of nodes) {
|
|
348
|
+
if (n.line === line)
|
|
349
|
+
return n;
|
|
350
|
+
const hit = findByLine(n.children, line);
|
|
351
|
+
if (hit)
|
|
352
|
+
return hit;
|
|
353
|
+
}
|
|
354
|
+
return undefined;
|
|
355
|
+
}
|
|
356
|
+
function send(res, status, body) {
|
|
357
|
+
res.writeHead(status, { "content-type": "application/json; charset=utf-8" });
|
|
358
|
+
res.end(JSON.stringify(body));
|
|
359
|
+
}
|
|
360
|
+
async function readJson(req) {
|
|
361
|
+
let size = 0;
|
|
362
|
+
const chunks = [];
|
|
363
|
+
for await (const chunk of req) {
|
|
364
|
+
size += chunk.length;
|
|
365
|
+
if (size > MAX_BODY)
|
|
366
|
+
throw new HttpError(413, "Request body too large");
|
|
367
|
+
chunks.push(chunk);
|
|
368
|
+
}
|
|
369
|
+
try {
|
|
370
|
+
return JSON.parse(Buffer.concat(chunks).toString("utf8") || "{}");
|
|
371
|
+
}
|
|
372
|
+
catch {
|
|
373
|
+
throw new HttpError(400, "Invalid JSON body");
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|