mirror of
https://github.com/backnotprop/plannotator.git
synced 2026-09-14 14:17:26 +08:00
929e8f9f59
The Pi extension is distributed and executed as raw TypeScript through Pi's jiti loader. Phantom .js paths force jiti through its last-resort fallback, while extensionless paths still require probing. Exact .ts paths avoid both and match the files that actually ship. - rewrite relative .js and extensionless specifiers in hand-written sources (the existing noEmit bundler tsconfig permits .ts imports) - make vendor.sh emit .ts in explicit rewrite rules and normalize both source styles across verbatim-copied generated modules - guard hand-written and generated sources against either inexact form Extension module import: 43.5ms -> 15ms median (PI_TIMING, n=12 interleaved). bun test: 73/73 (pi-extension), 456/456 (server); all seven TypeScript project checks and the packed-artifact audit pass. Co-authored-by: dca123 <dca123@users.noreply.github.com> Co-authored-by: Michael Ramos <mdramos8@gmail.com>
86 lines
2.0 KiB
TypeScript
86 lines
2.0 KiB
TypeScript
/**
|
|
* Editor annotation handler (in-memory store for VS Code integration).
|
|
* EditorAnnotation type, createEditorAnnotationHandler
|
|
*/
|
|
|
|
import { randomUUID } from "node:crypto";
|
|
import type { IncomingMessage } from "node:http";
|
|
import { json, parseBody } from "./helpers.ts";
|
|
|
|
interface EditorAnnotation {
|
|
id: string;
|
|
filePath: string;
|
|
selectedText: string;
|
|
lineStart: number;
|
|
lineEnd: number;
|
|
comment?: string;
|
|
createdAt: number;
|
|
}
|
|
|
|
export function createEditorAnnotationHandler() {
|
|
const annotations: EditorAnnotation[] = [];
|
|
|
|
return {
|
|
async handle(
|
|
req: IncomingMessage,
|
|
res: import("node:http").ServerResponse,
|
|
url: URL,
|
|
): Promise<boolean> {
|
|
if (url.pathname === "/api/editor-annotations" && req.method === "GET") {
|
|
json(res, { annotations });
|
|
return true;
|
|
}
|
|
|
|
if (url.pathname === "/api/editor-annotation" && req.method === "POST") {
|
|
try {
|
|
const body = await parseBody(req);
|
|
if (
|
|
!body.filePath ||
|
|
!body.selectedText ||
|
|
!body.lineStart ||
|
|
!body.lineEnd
|
|
) {
|
|
json(res, { error: "Missing required fields" }, 400);
|
|
return true;
|
|
}
|
|
|
|
const annotation: EditorAnnotation = {
|
|
id: randomUUID(),
|
|
filePath: String(body.filePath),
|
|
selectedText: String(body.selectedText),
|
|
lineStart: Number(body.lineStart),
|
|
lineEnd: Number(body.lineEnd),
|
|
comment: typeof body.comment === "string" ? body.comment : undefined,
|
|
createdAt: Date.now(),
|
|
};
|
|
|
|
annotations.push(annotation);
|
|
json(res, { id: annotation.id });
|
|
} catch {
|
|
json(res, { error: "Invalid JSON" }, 400);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if (
|
|
url.pathname === "/api/editor-annotation" &&
|
|
req.method === "DELETE"
|
|
) {
|
|
const id = url.searchParams.get("id");
|
|
if (!id) {
|
|
json(res, { error: "Missing id parameter" }, 400);
|
|
return true;
|
|
}
|
|
const idx = annotations.findIndex((annotation) => annotation.id === id);
|
|
if (idx !== -1) {
|
|
annotations.splice(idx, 1);
|
|
}
|
|
json(res, { ok: true });
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
},
|
|
};
|
|
}
|