@tecture/core 0.0.7 → 0.0.8
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/CHANGELOG.md +11 -0
- package/README.md +30 -0
- package/dist/cli.js +77 -3
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +10 -0
- package/dist/index.js +7 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@tecture/core` are documented here.
|
|
4
4
|
|
|
5
|
+
## [0.0.8] — 2026-06-16
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **Anonymous, opt-out usage telemetry** for the standalone viewer. Records only
|
|
10
|
+
the C4 diagram level and counts — never names, paths, or architecture content.
|
|
11
|
+
Disable with `TECTURE_TELEMETRY=0` or `DO_NOT_TRACK=1`; a one-time notice
|
|
12
|
+
prints on first run. (Only the standalone viewer reports; the VS Code webview
|
|
13
|
+
uses the extension's own telemetry, so there's no double-counting.)
|
|
14
|
+
- First `@tecture/core` README, documenting usage and the telemetry policy.
|
|
15
|
+
|
|
5
16
|
## [0.0.7] — 2026-06-02
|
|
6
17
|
|
|
7
18
|
### Changed
|
package/README.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# @tecture/core
|
|
2
|
+
|
|
3
|
+
Standalone browser viewer for [Tecture](https://github.com/tecture-io/tecture)
|
|
4
|
+
architecture docs — render the file-based architecture in `./architecture/`
|
|
5
|
+
(JSON + Markdown) as interactive, navigable diagrams.
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
From a repo that has an `architecture/` folder:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx @tecture/core
|
|
13
|
+
# → http://localhost:3000
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Options: `--port <n>`, `--architecture-path <dir>`, `--tecture-path <dir>`.
|
|
17
|
+
|
|
18
|
+
To generate the architecture in the first place, install the skill into your
|
|
19
|
+
coding agent with `npx @tecture/skill@latest` and run `/architecture-docs`.
|
|
20
|
+
|
|
21
|
+
## Telemetry
|
|
22
|
+
|
|
23
|
+
The viewer sends **anonymous** usage stats (which C4 diagram **level** is
|
|
24
|
+
viewed, and how often) to help prioritize improvements. It records only the
|
|
25
|
+
level (1/2/3) and counts — **never** your diagram names, node names, file
|
|
26
|
+
paths, or any architecture content. Events are keyed to a random id stored at
|
|
27
|
+
`~/.config/tecture/telemetry-id`.
|
|
28
|
+
|
|
29
|
+
It's opt-out: set `TECTURE_TELEMETRY=0` (or the standard `DO_NOT_TRACK=1`) to
|
|
30
|
+
disable it entirely. A one-time notice is printed on first run.
|
package/dist/cli.js
CHANGED
|
@@ -150,7 +150,7 @@ function send400(res, body) {
|
|
|
150
150
|
}
|
|
151
151
|
function createArchitectureRouter(opts) {
|
|
152
152
|
const router = Router2();
|
|
153
|
-
const { resolveSource, resolveLayoutStore } = opts;
|
|
153
|
+
const { resolveSource, resolveLayoutStore, report } = opts;
|
|
154
154
|
router.get("/", async (req, res, next) => {
|
|
155
155
|
try {
|
|
156
156
|
const source = await resolveSource(req);
|
|
@@ -195,6 +195,10 @@ function createArchitectureRouter(opts) {
|
|
|
195
195
|
edges: diagram.edges ?? []
|
|
196
196
|
};
|
|
197
197
|
res.json(body);
|
|
198
|
+
report?.capture(
|
|
199
|
+
"diagram.viewed",
|
|
200
|
+
typeof diagram.level === "number" ? { level: diagram.level } : {}
|
|
201
|
+
);
|
|
198
202
|
} catch (err) {
|
|
199
203
|
if (err instanceof DiagramNotFoundError) {
|
|
200
204
|
return send404(res, { error: "diagram_not_found", slug: err.slug });
|
|
@@ -353,7 +357,8 @@ function createApp(options) {
|
|
|
353
357
|
"/api/architecture",
|
|
354
358
|
createArchitectureRouter({
|
|
355
359
|
resolveSource: toSourceResolver(options.source),
|
|
356
|
-
resolveLayoutStore: toLayoutStoreResolver(options.layoutStore)
|
|
360
|
+
resolveLayoutStore: toLayoutStoreResolver(options.layoutStore),
|
|
361
|
+
report: options.report
|
|
357
362
|
})
|
|
358
363
|
);
|
|
359
364
|
app2.use("/api/architecture", architectureErrorHandler);
|
|
@@ -479,6 +484,72 @@ var FsLayoutStore = class {
|
|
|
479
484
|
}
|
|
480
485
|
};
|
|
481
486
|
|
|
487
|
+
// src/telemetry.ts
|
|
488
|
+
import { randomUUID } from "crypto";
|
|
489
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
490
|
+
import { homedir } from "os";
|
|
491
|
+
import { join as join2 } from "path";
|
|
492
|
+
|
|
493
|
+
// package.json
|
|
494
|
+
var version = "0.0.8";
|
|
495
|
+
|
|
496
|
+
// src/telemetry.ts
|
|
497
|
+
var POSTHOG_KEY = "phc_wTpBpKNGhboxPitLJYLSi8aGGCaYJ7fHbvLAT93edmHp";
|
|
498
|
+
var POSTHOG_HOST = "https://us.i.posthog.com";
|
|
499
|
+
var NOOP = { capture() {
|
|
500
|
+
} };
|
|
501
|
+
function disabledByEnv() {
|
|
502
|
+
const dnt = process.env.DO_NOT_TRACK;
|
|
503
|
+
if (dnt === "1" || dnt === "true") return true;
|
|
504
|
+
const t = process.env.TECTURE_TELEMETRY?.toLowerCase();
|
|
505
|
+
return t === "0" || t === "false" || t === "off" || t === "no";
|
|
506
|
+
}
|
|
507
|
+
function createTelemetry() {
|
|
508
|
+
if (POSTHOG_KEY.length === 0 || disabledByEnv()) return NOOP;
|
|
509
|
+
let distinctId;
|
|
510
|
+
let firstRun = false;
|
|
511
|
+
try {
|
|
512
|
+
const dir = join2(homedir(), ".config", "tecture");
|
|
513
|
+
const idFile = join2(dir, "telemetry-id");
|
|
514
|
+
if (existsSync2(idFile)) {
|
|
515
|
+
distinctId = readFileSync(idFile, "utf8").trim() || randomUUID();
|
|
516
|
+
} else {
|
|
517
|
+
distinctId = randomUUID();
|
|
518
|
+
mkdirSync(dir, { recursive: true });
|
|
519
|
+
writeFileSync(idFile, distinctId);
|
|
520
|
+
firstRun = true;
|
|
521
|
+
}
|
|
522
|
+
} catch {
|
|
523
|
+
distinctId = randomUUID();
|
|
524
|
+
}
|
|
525
|
+
if (firstRun) {
|
|
526
|
+
console.error(
|
|
527
|
+
"\nTecture collects anonymous usage stats (which diagram levels are viewed, how often)\nto help improve the tool. It never sends your architecture content, names, or paths.\nOpt out any time: set TECTURE_TELEMETRY=0 (or DO_NOT_TRACK=1).\n"
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
const superProps = {
|
|
531
|
+
coreVersion: version,
|
|
532
|
+
nodeVersion: process.version,
|
|
533
|
+
platform: process.platform
|
|
534
|
+
};
|
|
535
|
+
return {
|
|
536
|
+
capture(event, properties = {}) {
|
|
537
|
+
const body = JSON.stringify({
|
|
538
|
+
api_key: POSTHOG_KEY,
|
|
539
|
+
event,
|
|
540
|
+
distinct_id: distinctId,
|
|
541
|
+
properties: { ...superProps, ...properties, $process_person_profile: false }
|
|
542
|
+
});
|
|
543
|
+
void fetch(`${POSTHOG_HOST}/capture/`, {
|
|
544
|
+
method: "POST",
|
|
545
|
+
headers: { "content-type": "application/json" },
|
|
546
|
+
body
|
|
547
|
+
}).catch(() => {
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
|
|
482
553
|
// src/cli.ts
|
|
483
554
|
function parseArgs(argv) {
|
|
484
555
|
let port2 = Number(process.env.PORT ?? 3e3);
|
|
@@ -519,13 +590,16 @@ function parseArgs(argv) {
|
|
|
519
590
|
};
|
|
520
591
|
}
|
|
521
592
|
var { port, architecturePath, tecturePath } = parseArgs(process.argv.slice(2));
|
|
593
|
+
var telemetry = createTelemetry();
|
|
522
594
|
var app = createApp({
|
|
523
595
|
source: new FsArchitectureDataSource(architecturePath),
|
|
524
|
-
layoutStore: new FsLayoutStore(tecturePath)
|
|
596
|
+
layoutStore: new FsLayoutStore(tecturePath),
|
|
597
|
+
report: telemetry
|
|
525
598
|
});
|
|
526
599
|
app.listen(port, () => {
|
|
527
600
|
console.log(`Tecture IO running at http://localhost:${port}`);
|
|
528
601
|
console.log(`Architecture root: ${architecturePath}`);
|
|
529
602
|
console.log(`Tecture state: ${tecturePath}`);
|
|
603
|
+
telemetry.capture("viewer.opened");
|
|
530
604
|
});
|
|
531
605
|
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../src/server.ts","../src/routes/health.ts","../src/routes/architecture.ts","../../shared/src/validators.ts","../src/source/fs.ts"],"sourcesContent":["import { resolve } from \"node:path\";\nimport { createApp } from \"./server.js\";\nimport { FsArchitectureDataSource, FsLayoutStore } from \"./source/fs.js\";\n\ninterface CliOptions {\n port: number;\n architecturePath: string;\n tecturePath: string;\n}\n\nfunction parseArgs(argv: string[]): CliOptions {\n let port = Number(process.env.PORT ?? 3000);\n let architecturePath = process.env.ARCHITECTURE_PATH;\n let tecturePath = process.env.TECTURE_PATH;\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n if (arg === \"--port\" || arg === \"-p\") {\n const next = argv[i + 1];\n if (!next) throw new Error(`Missing value for ${arg}`);\n const parsed = Number(next);\n if (!Number.isInteger(parsed) || parsed <= 0 || parsed > 65535) {\n throw new Error(`Invalid port: ${next}`);\n }\n port = parsed;\n i++;\n } else if (arg === \"--architecture-path\" || arg === \"-a\") {\n const next = argv[i + 1];\n if (!next) throw new Error(`Missing value for ${arg}`);\n architecturePath = next;\n i++;\n } else if (arg === \"--tecture-path\" || arg === \"-t\") {\n const next = argv[i + 1];\n if (!next) throw new Error(`Missing value for ${arg}`);\n tecturePath = next;\n i++;\n } else if (arg === \"--help\" || arg === \"-h\") {\n console.log(\n \"Usage: tecture-io [--port <number>] [--architecture-path <path>] [--tecture-path <path>]\",\n );\n process.exit(0);\n }\n }\n return {\n port,\n architecturePath: resolve(architecturePath ?? \"architecture\"),\n tecturePath: resolve(tecturePath ?? \"architecture/.tecture\"),\n };\n}\n\nconst { port, architecturePath, tecturePath } = parseArgs(process.argv.slice(2));\nconst app = createApp({\n source: new FsArchitectureDataSource(architecturePath),\n layoutStore: new FsLayoutStore(tecturePath),\n});\n\napp.listen(port, () => {\n console.log(`Tecture IO running at http://localhost:${port}`);\n console.log(`Architecture root: ${architecturePath}`);\n console.log(`Tecture state: ${tecturePath}`);\n});\n","import { existsSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport express, { type Express, type Request } from \"express\";\nimport { healthRouter } from \"./routes/health.js\";\nimport {\n architectureErrorHandler,\n createArchitectureRouter,\n type LayoutStoreResolver,\n type SourceResolver,\n} from \"./routes/architecture.js\";\nimport type {\n ArchitectureDataSource,\n LayoutStore,\n} from \"./source/types.js\";\n\nexport interface CreateAppOptions {\n source: ArchitectureDataSource | SourceResolver;\n layoutStore?: LayoutStore | LayoutStoreResolver | null;\n}\n\nfunction toSourceResolver(\n value: ArchitectureDataSource | SourceResolver,\n): SourceResolver {\n if (typeof value === \"function\") return value as SourceResolver;\n return () => value;\n}\n\nfunction toLayoutStoreResolver(\n value: LayoutStore | LayoutStoreResolver | null | undefined,\n): LayoutStoreResolver | undefined {\n if (value == null) return undefined;\n if (typeof value === \"function\") return value as LayoutStoreResolver;\n return () => value;\n}\n\nexport function createApp(options: CreateAppOptions): Express {\n const app = express();\n\n app.use(express.json({ limit: \"256kb\" }));\n app.use(\"/api/health\", healthRouter);\n app.use(\n \"/api/architecture\",\n createArchitectureRouter({\n resolveSource: toSourceResolver(options.source),\n resolveLayoutStore: toLayoutStoreResolver(options.layoutStore),\n }),\n );\n app.use(\"/api/architecture\", architectureErrorHandler);\n\n const publicDir = fileURLToPath(new URL(\"./public\", import.meta.url));\n\n if (existsSync(publicDir)) {\n app.use(express.static(publicDir));\n app.get(\"*\", (req: Request, res, next) => {\n if (req.path.startsWith(\"/api/\")) return next();\n res.sendFile(fileURLToPath(new URL(\"./public/index.html\", import.meta.url)));\n });\n }\n\n return app;\n}\n","import { Router } from \"express\";\nimport type { ApiHealthResponse } from \"@tecture/shared\";\n\nexport const healthRouter: Router = Router();\n\nhealthRouter.get(\"/\", (_req, res) => {\n const body: ApiHealthResponse = {\n status: \"ok\",\n uptime: process.uptime(),\n timestamp: new Date().toISOString(),\n };\n res.json(body);\n});\n","import { Router, type Request, type Response, type NextFunction } from \"express\";\nimport type {\n ApiArchitectureError,\n ApiDiagram,\n ApiDiagramEdges,\n ApiDiagramLayout,\n ApiDiagramLayoutUpdate,\n ApiDiagramNodes,\n ApiNodeDetail,\n ManifestFile,\n} from \"@tecture/shared\";\nimport {\n type ArchitectureDataSource,\n DescriptionNotFoundError,\n DiagramNotFoundError,\n type LayoutStore,\n LayoutInvalidError,\n NodeNotFoundError,\n SLUG_RE,\n buildArchitectureSummary,\n emptyLayout,\n findNode,\n} from \"../source/types.js\";\n\nexport type SourceResolver = (\n req: Request,\n) => ArchitectureDataSource | Promise<ArchitectureDataSource>;\n\nexport type LayoutStoreResolver = (\n req: Request,\n) => LayoutStore | null | Promise<LayoutStore | null>;\n\nexport interface ArchitectureRouterOptions {\n resolveSource: SourceResolver;\n resolveLayoutStore?: LayoutStoreResolver;\n}\n\nfunction send501(res: Response, body: ApiArchitectureError): void {\n res.status(501).json(body);\n}\n\nfunction send404(res: Response, body: ApiArchitectureError): void {\n res.status(404).json(body);\n}\n\nfunction send400(res: Response, body: ApiArchitectureError): void {\n res.status(400).json(body);\n}\n\nexport function createArchitectureRouter(\n opts: ArchitectureRouterOptions,\n): Router {\n const router = Router();\n const { resolveSource, resolveLayoutStore } = opts;\n\n router.get(\"/\", async (req, res, next) => {\n try {\n const source = await resolveSource(req);\n const summary = await buildArchitectureSummary(source);\n res.json(summary);\n } catch (err) {\n next(err);\n }\n });\n\n router.get(\"/manifest\", async (req, res, next) => {\n try {\n const source = await resolveSource(req);\n const manifest: ManifestFile = await source.loadManifest();\n res.json(manifest);\n } catch (err) {\n next(err);\n }\n });\n\n router.get(\"/diagrams\", async (req, res, next) => {\n try {\n const source = await resolveSource(req);\n const summary = await buildArchitectureSummary(source);\n res.json(summary.diagrams);\n } catch (err) {\n next(err);\n }\n });\n\n router.get(\"/diagrams/:diagramId\", async (req, res, next) => {\n const { diagramId } = req.params;\n if (!SLUG_RE.test(diagramId)) {\n return send404(res, { error: \"diagram_not_found\", slug: diagramId });\n }\n try {\n const source = await resolveSource(req);\n const diagram = await source.loadDiagram(diagramId);\n const body: ApiDiagram = {\n slug: diagramId,\n name: diagram.name,\n level: diagram.level,\n meta: diagram.meta,\n nodes: diagram.nodes,\n edges: diagram.edges ?? [],\n };\n res.json(body);\n } catch (err) {\n if (err instanceof DiagramNotFoundError) {\n return send404(res, { error: \"diagram_not_found\", slug: err.slug });\n }\n next(err);\n }\n });\n\n router.get(\"/diagrams/:diagramId/nodes\", async (req, res, next) => {\n const { diagramId } = req.params;\n if (!SLUG_RE.test(diagramId)) {\n return send404(res, { error: \"diagram_not_found\", slug: diagramId });\n }\n try {\n const source = await resolveSource(req);\n const diagram = await source.loadDiagram(diagramId);\n const body: ApiDiagramNodes = { diagramId, nodes: diagram.nodes };\n res.json(body);\n } catch (err) {\n if (err instanceof DiagramNotFoundError) {\n return send404(res, { error: \"diagram_not_found\", slug: err.slug });\n }\n next(err);\n }\n });\n\n router.get(\"/diagrams/:diagramId/edges\", async (req, res, next) => {\n const { diagramId } = req.params;\n if (!SLUG_RE.test(diagramId)) {\n return send404(res, { error: \"diagram_not_found\", slug: diagramId });\n }\n try {\n const source = await resolveSource(req);\n const diagram = await source.loadDiagram(diagramId);\n const body: ApiDiagramEdges = { diagramId, edges: diagram.edges ?? [] };\n res.json(body);\n } catch (err) {\n if (err instanceof DiagramNotFoundError) {\n return send404(res, { error: \"diagram_not_found\", slug: err.slug });\n }\n next(err);\n }\n });\n\n router.get(\"/diagrams/:diagramId/layout\", async (req, res, next) => {\n const { diagramId } = req.params;\n if (!SLUG_RE.test(diagramId)) {\n return send404(res, { error: \"diagram_not_found\", slug: diagramId });\n }\n try {\n const source = await resolveSource(req);\n await source.loadDiagram(diagramId);\n const store = resolveLayoutStore ? await resolveLayoutStore(req) : null;\n const layout: ApiDiagramLayout = store\n ? await store.loadLayout(diagramId)\n : emptyLayout(diagramId);\n res.json(layout);\n } catch (err) {\n if (err instanceof DiagramNotFoundError) {\n return send404(res, { error: \"diagram_not_found\", slug: err.slug });\n }\n next(err);\n }\n });\n\n router.put(\"/diagrams/:diagramId/layout\", async (req, res, next) => {\n const { diagramId } = req.params;\n if (!SLUG_RE.test(diagramId)) {\n return send404(res, { error: \"diagram_not_found\", slug: diagramId });\n }\n try {\n const source = await resolveSource(req);\n await source.loadDiagram(diagramId);\n const store = resolveLayoutStore ? await resolveLayoutStore(req) : null;\n if (!store) {\n return send501(res, {\n error: \"layout_not_supported\",\n message: \"Layout persistence is not configured for this data source\",\n });\n }\n const update = req.body as ApiDiagramLayoutUpdate;\n const saved = await store.saveLayout(diagramId, update);\n res.json(saved);\n } catch (err) {\n if (err instanceof DiagramNotFoundError) {\n return send404(res, { error: \"diagram_not_found\", slug: err.slug });\n }\n if (err instanceof LayoutInvalidError) {\n return send400(res, { error: \"layout_invalid\", message: err.message });\n }\n next(err);\n }\n });\n\n router.get(\"/nodes/:nodeId\", async (req, res, next) => {\n const { nodeId } = req.params;\n if (!SLUG_RE.test(nodeId)) {\n return send404(res, { error: \"node_not_found\", id: nodeId });\n }\n try {\n const source = await resolveSource(req);\n const { node, diagramId } = await findNode(source, nodeId);\n const description = await source.loadDescription(nodeId);\n const body: ApiNodeDetail = { ...node, diagramId, description };\n res.json(body);\n } catch (err) {\n if (err instanceof NodeNotFoundError) {\n return send404(res, { error: \"node_not_found\", id: err.id });\n }\n if (err instanceof DescriptionNotFoundError) {\n return send404(res, { error: \"description_not_found\", id: err.id });\n }\n next(err);\n }\n });\n\n router.get(\"/nodes/:nodeId/description\", async (req, res, next) => {\n const { nodeId } = req.params;\n if (!SLUG_RE.test(nodeId)) {\n return send404(res, { error: \"description_not_found\", id: nodeId });\n }\n try {\n const source = await resolveSource(req);\n await findNode(source, nodeId);\n const markdown = await source.loadDescription(nodeId);\n res.type(\"text/markdown; charset=utf-8\").send(markdown);\n } catch (err) {\n if (err instanceof NodeNotFoundError) {\n return send404(res, { error: \"node_not_found\", id: err.id });\n }\n if (err instanceof DescriptionNotFoundError) {\n return send404(res, { error: \"description_not_found\", id: err.id });\n }\n next(err);\n }\n });\n\n return router;\n}\n\nexport function architectureErrorHandler(\n err: unknown,\n _req: Request,\n res: Response,\n next: NextFunction,\n): void {\n if (res.headersSent) return next(err);\n const message = err instanceof Error ? err.message : String(err);\n const body: ApiArchitectureError = {\n error: \"architecture_unreadable\",\n message,\n };\n res.status(500).json(body);\n}\n","import type {\n ApiArchitectureSummary,\n ApiDiagramLayoutUpdate,\n ApiDiagramSummary,\n ArchitectureNode,\n DiagramFile,\n DiagramLayoutFile,\n ManifestFile,\n NodeLayoutEntry,\n SourceHost,\n} from \"./index\";\n\nexport const SLUG_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/;\n\nexport class DiagramNotFoundError extends Error {\n constructor(public readonly slug: string) {\n super(`Diagram not found: ${slug}`);\n this.name = \"DiagramNotFoundError\";\n }\n}\n\nexport class NodeNotFoundError extends Error {\n constructor(public readonly id: string) {\n super(`Node not found: ${id}`);\n this.name = \"NodeNotFoundError\";\n }\n}\n\nexport class DescriptionNotFoundError extends Error {\n constructor(public readonly id: string) {\n super(`Description not found: ${id}`);\n this.name = \"DescriptionNotFoundError\";\n }\n}\n\nexport class LayoutInvalidError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"LayoutInvalidError\";\n }\n}\n\nexport interface ArchitectureDataSource {\n loadManifest(): Promise<ManifestFile>;\n loadDiagram(slug: string): Promise<DiagramFile>;\n loadDescription(nodeId: string): Promise<string>;\n}\n\nexport interface LayoutStore {\n loadLayout(slug: string): Promise<DiagramLayoutFile>;\n saveLayout(\n slug: string,\n update: ApiDiagramLayoutUpdate,\n ): Promise<DiagramLayoutFile>;\n}\n\nexport function isFiniteNumber(v: unknown): v is number {\n return typeof v === \"number\" && Number.isFinite(v);\n}\n\nexport function isValidLayoutEntry(v: unknown): v is NodeLayoutEntry {\n if (!v || typeof v !== \"object\") return false;\n const e = v as Record<string, unknown>;\n if (!isFiniteNumber(e.x) || !isFiniteNumber(e.y)) return false;\n if (\"width\" in e && e.width !== undefined && !isFiniteNumber(e.width)) return false;\n if (\"height\" in e && e.height !== undefined && !isFiniteNumber(e.height)) return false;\n return true;\n}\n\nexport function emptyLayout(slug: string): DiagramLayoutFile {\n return { version: 1, diagramId: slug, updatedAt: \"\", nodes: {} };\n}\n\nexport function normalizeLayoutUpdate(\n slug: string,\n update: ApiDiagramLayoutUpdate,\n): DiagramLayoutFile {\n if (!SLUG_RE.test(slug)) {\n throw new LayoutInvalidError(`invalid diagram slug: ${slug}`);\n }\n if (\n !update ||\n typeof update !== \"object\" ||\n !update.nodes ||\n typeof update.nodes !== \"object\"\n ) {\n throw new LayoutInvalidError(\n \"body must be { nodes: Record<string, { x, y, width?, height? }> }\",\n );\n }\n const nodes: Record<string, NodeLayoutEntry> = {};\n for (const [id, entry] of Object.entries(update.nodes)) {\n if (!SLUG_RE.test(id)) {\n throw new LayoutInvalidError(`invalid node id: ${id}`);\n }\n if (!isValidLayoutEntry(entry)) {\n throw new LayoutInvalidError(`invalid layout entry for ${id}`);\n }\n const out: NodeLayoutEntry = { x: entry.x, y: entry.y };\n if (isFiniteNumber(entry.width)) out.width = entry.width;\n if (isFiniteNumber(entry.height)) out.height = entry.height;\n nodes[id] = out;\n }\n return {\n version: 1,\n diagramId: slug,\n updatedAt: new Date().toISOString(),\n nodes,\n };\n}\n\nexport async function buildArchitectureSummary(\n source: ArchitectureDataSource,\n): Promise<ApiArchitectureSummary> {\n const manifest = await source.loadManifest();\n const diagrams = await Promise.all(\n manifest.diagrams.map(async (slug): Promise<ApiDiagramSummary> => {\n const diagram = await source.loadDiagram(slug);\n return {\n slug,\n name: diagram.name,\n level: diagram.level,\n nodeCount: diagram.nodes?.length ?? 0,\n edgeCount: diagram.edges?.length ?? 0,\n };\n }),\n );\n return {\n name: manifest.name,\n description: manifest.description,\n source: manifest.source,\n sourceHost: manifest.sourceHost,\n topDiagram: manifest.topDiagram,\n diagrams,\n };\n}\n\nconst HOST_DOMAINS: Array<{ host: SourceHost; match: RegExp }> = [\n { host: \"github\", match: /(^|\\.)github\\.com$/i },\n { host: \"gitlab\", match: /(^|\\.)gitlab\\.com$/i },\n { host: \"bitbucket\", match: /(^|\\.)bitbucket\\.org$/i },\n];\n\nfunction inferSourceHost(source: string): SourceHost | undefined {\n // Extract the hostname without relying on the URL global (kept lib-agnostic for shared):\n // scheme://[userinfo@]host[:port]/...\n const hostname = /^[a-z][a-z0-9+.-]*:\\/\\/(?:[^@/]*@)?([^:/?#]+)/i.exec(source)?.[1];\n if (!hostname) return undefined;\n return HOST_DOMAINS.find(({ match }) => match.test(hostname))?.host;\n}\n\n/**\n * Build a web URL that views `path` (a repo-root-relative file or directory, trailing \"/\" = dir)\n * on the source repo host. `HEAD` resolves to the default branch so links stay current. Falls back\n * to the repo root when the host is unknown, and returns undefined when there is no source.\n */\nexport function buildSourceUrl(\n source: string | undefined,\n host: SourceHost | undefined,\n path: string,\n): string | undefined {\n if (!source) return undefined;\n const base = source.replace(/\\/+$/, \"\").replace(/\\.git$/i, \"\");\n const resolvedHost = host ?? inferSourceHost(base);\n const isDir = path.endsWith(\"/\");\n const encoded = path\n .replace(/^\\/+/, \"\")\n .replace(/\\/+$/, \"\")\n .split(\"/\")\n .map(encodeURIComponent)\n .join(\"/\");\n if (!encoded) return base;\n switch (resolvedHost) {\n case \"github\":\n return `${base}/${isDir ? \"tree\" : \"blob\"}/HEAD/${encoded}`;\n case \"gitlab\":\n return `${base}/-/${isDir ? \"tree\" : \"blob\"}/HEAD/${encoded}`;\n case \"bitbucket\":\n return `${base}/src/HEAD/${encoded}`;\n default:\n return base;\n }\n}\n\nexport async function findNode(\n source: ArchitectureDataSource,\n nodeId: string,\n): Promise<{ node: ArchitectureNode; diagramId: string }> {\n if (!SLUG_RE.test(nodeId)) throw new NodeNotFoundError(nodeId);\n const manifest = await source.loadManifest();\n for (const slug of manifest.diagrams) {\n let diagram: DiagramFile;\n try {\n diagram = await source.loadDiagram(slug);\n } catch (err) {\n if (err instanceof DiagramNotFoundError) continue;\n throw err;\n }\n const match = diagram.nodes?.find((n) => n.id === nodeId);\n if (match) return { node: match, diagramId: slug };\n }\n throw new NodeNotFoundError(nodeId);\n}\n","import { mkdir, readFile, rename, writeFile } from \"node:fs/promises\";\nimport { dirname, join, resolve, sep } from \"node:path\";\nimport type {\n ApiDiagramLayoutUpdate,\n DiagramFile,\n DiagramLayoutFile,\n ManifestFile,\n NodeLayoutEntry,\n} from \"@tecture/shared\";\nimport {\n type ArchitectureDataSource,\n DescriptionNotFoundError,\n DiagramNotFoundError,\n type LayoutStore,\n SLUG_RE,\n emptyLayout,\n isFiniteNumber,\n isValidLayoutEntry,\n normalizeLayoutUpdate,\n} from \"./types.js\";\n\nexport function safeJoin(root: string, ...segments: string[]): string {\n const absRoot = resolve(root);\n const candidate = resolve(join(absRoot, ...segments));\n const guard = absRoot.endsWith(sep) ? absRoot : absRoot + sep;\n if (candidate !== absRoot && !candidate.startsWith(guard)) {\n throw new Error(`Path escapes architecture root: ${candidate}`);\n }\n return candidate;\n}\n\nfunction isNodeErrnoError(err: unknown): err is NodeJS.ErrnoException {\n return err instanceof Error && \"code\" in err;\n}\n\nasync function loadJsonFile<T>(path: string): Promise<T> {\n const raw = await readFile(path, \"utf8\");\n return JSON.parse(raw) as T;\n}\n\nexport class FsArchitectureDataSource implements ArchitectureDataSource {\n constructor(private readonly root: string) {}\n\n async loadManifest(): Promise<ManifestFile> {\n const path = safeJoin(this.root, \"manifest.json\");\n return loadJsonFile<ManifestFile>(path);\n }\n\n async loadDiagram(slug: string): Promise<DiagramFile> {\n if (!SLUG_RE.test(slug)) throw new DiagramNotFoundError(slug);\n const path = safeJoin(this.root, \"diagrams\", `${slug}.json`);\n try {\n return await loadJsonFile<DiagramFile>(path);\n } catch (err) {\n if (isNodeErrnoError(err) && err.code === \"ENOENT\") {\n throw new DiagramNotFoundError(slug);\n }\n throw err;\n }\n }\n\n async loadDescription(nodeId: string): Promise<string> {\n if (!SLUG_RE.test(nodeId)) throw new DescriptionNotFoundError(nodeId);\n const path = safeJoin(this.root, \"descriptions\", `${nodeId}.md`);\n try {\n return await readFile(path, \"utf8\");\n } catch (err) {\n if (isNodeErrnoError(err) && err.code === \"ENOENT\") {\n throw new DescriptionNotFoundError(nodeId);\n }\n throw err;\n }\n }\n}\n\nexport class FsLayoutStore implements LayoutStore {\n constructor(private readonly tectureRoot: string) {}\n\n private path(slug: string): string {\n return safeJoin(this.tectureRoot, \"layouts\", `${slug}.json`);\n }\n\n async loadLayout(slug: string): Promise<DiagramLayoutFile> {\n if (!SLUG_RE.test(slug)) return emptyLayout(slug);\n const path = this.path(slug);\n let raw: string;\n try {\n raw = await readFile(path, \"utf8\");\n } catch (err) {\n if (isNodeErrnoError(err) && err.code === \"ENOENT\") return emptyLayout(slug);\n console.warn(`[tecture] failed to read layout ${path}: ${String(err)}`);\n return emptyLayout(slug);\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n console.warn(`[tecture] malformed layout JSON at ${path}: ${String(err)}`);\n return emptyLayout(slug);\n }\n\n if (!parsed || typeof parsed !== \"object\") return emptyLayout(slug);\n const p = parsed as Record<string, unknown>;\n const nodes: Record<string, NodeLayoutEntry> = {};\n if (p.nodes && typeof p.nodes === \"object\") {\n for (const [id, entry] of Object.entries(p.nodes as Record<string, unknown>)) {\n if (!SLUG_RE.test(id)) continue;\n if (!isValidLayoutEntry(entry)) continue;\n const out: NodeLayoutEntry = { x: entry.x, y: entry.y };\n if (isFiniteNumber(entry.width)) out.width = entry.width;\n if (isFiniteNumber(entry.height)) out.height = entry.height;\n nodes[id] = out;\n }\n }\n return {\n version: 1,\n diagramId: slug,\n updatedAt: typeof p.updatedAt === \"string\" ? p.updatedAt : \"\",\n nodes,\n };\n }\n\n async saveLayout(\n slug: string,\n update: ApiDiagramLayoutUpdate,\n ): Promise<DiagramLayoutFile> {\n const file = normalizeLayoutUpdate(slug, update);\n const path = this.path(slug);\n await mkdir(dirname(path), { recursive: true });\n const tmp = `${path}.tmp`;\n await writeFile(tmp, `${JSON.stringify(file, null, 2)}\\n`, \"utf8\");\n await rename(tmp, path);\n return file;\n }\n}\n"],"mappings":"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","names":["resolve","Router","Router","app","port","architecturePath","tecturePath","resolve"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/server.ts","../src/routes/health.ts","../src/routes/architecture.ts","../../shared/src/validators.ts","../src/source/fs.ts","../src/telemetry.ts","../package.json"],"sourcesContent":["import { resolve } from \"node:path\";\nimport { createApp } from \"./server.js\";\nimport { FsArchitectureDataSource, FsLayoutStore } from \"./source/fs.js\";\nimport { createTelemetry } from \"./telemetry.js\";\n\ninterface CliOptions {\n port: number;\n architecturePath: string;\n tecturePath: string;\n}\n\nfunction parseArgs(argv: string[]): CliOptions {\n let port = Number(process.env.PORT ?? 3000);\n let architecturePath = process.env.ARCHITECTURE_PATH;\n let tecturePath = process.env.TECTURE_PATH;\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n if (arg === \"--port\" || arg === \"-p\") {\n const next = argv[i + 1];\n if (!next) throw new Error(`Missing value for ${arg}`);\n const parsed = Number(next);\n if (!Number.isInteger(parsed) || parsed <= 0 || parsed > 65535) {\n throw new Error(`Invalid port: ${next}`);\n }\n port = parsed;\n i++;\n } else if (arg === \"--architecture-path\" || arg === \"-a\") {\n const next = argv[i + 1];\n if (!next) throw new Error(`Missing value for ${arg}`);\n architecturePath = next;\n i++;\n } else if (arg === \"--tecture-path\" || arg === \"-t\") {\n const next = argv[i + 1];\n if (!next) throw new Error(`Missing value for ${arg}`);\n tecturePath = next;\n i++;\n } else if (arg === \"--help\" || arg === \"-h\") {\n console.log(\n \"Usage: tecture-io [--port <number>] [--architecture-path <path>] [--tecture-path <path>]\",\n );\n process.exit(0);\n }\n }\n return {\n port,\n architecturePath: resolve(architecturePath ?? \"architecture\"),\n tecturePath: resolve(tecturePath ?? \"architecture/.tecture\"),\n };\n}\n\nconst { port, architecturePath, tecturePath } = parseArgs(process.argv.slice(2));\nconst telemetry = createTelemetry();\nconst app = createApp({\n source: new FsArchitectureDataSource(architecturePath),\n layoutStore: new FsLayoutStore(tecturePath),\n report: telemetry,\n});\n\napp.listen(port, () => {\n console.log(`Tecture IO running at http://localhost:${port}`);\n console.log(`Architecture root: ${architecturePath}`);\n console.log(`Tecture state: ${tecturePath}`);\n telemetry.capture(\"viewer.opened\");\n});\n","import { existsSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport express, { type Express, type Request } from \"express\";\nimport { healthRouter } from \"./routes/health.js\";\nimport {\n architectureErrorHandler,\n createArchitectureRouter,\n type LayoutStoreResolver,\n type SourceResolver,\n} from \"./routes/architecture.js\";\nimport type {\n ArchitectureDataSource,\n LayoutStore,\n} from \"./source/types.js\";\nimport type { Reporter } from \"./telemetry.js\";\n\nexport interface CreateAppOptions {\n source: ArchitectureDataSource | SourceResolver;\n layoutStore?: LayoutStore | LayoutStoreResolver | null;\n /** Optional anonymous usage reporter (standalone viewer only). */\n report?: Reporter;\n}\n\nfunction toSourceResolver(\n value: ArchitectureDataSource | SourceResolver,\n): SourceResolver {\n if (typeof value === \"function\") return value as SourceResolver;\n return () => value;\n}\n\nfunction toLayoutStoreResolver(\n value: LayoutStore | LayoutStoreResolver | null | undefined,\n): LayoutStoreResolver | undefined {\n if (value == null) return undefined;\n if (typeof value === \"function\") return value as LayoutStoreResolver;\n return () => value;\n}\n\nexport function createApp(options: CreateAppOptions): Express {\n const app = express();\n\n app.use(express.json({ limit: \"256kb\" }));\n app.use(\"/api/health\", healthRouter);\n app.use(\n \"/api/architecture\",\n createArchitectureRouter({\n resolveSource: toSourceResolver(options.source),\n resolveLayoutStore: toLayoutStoreResolver(options.layoutStore),\n report: options.report,\n }),\n );\n app.use(\"/api/architecture\", architectureErrorHandler);\n\n const publicDir = fileURLToPath(new URL(\"./public\", import.meta.url));\n\n if (existsSync(publicDir)) {\n app.use(express.static(publicDir));\n app.get(\"*\", (req: Request, res, next) => {\n if (req.path.startsWith(\"/api/\")) return next();\n res.sendFile(fileURLToPath(new URL(\"./public/index.html\", import.meta.url)));\n });\n }\n\n return app;\n}\n","import { Router } from \"express\";\nimport type { ApiHealthResponse } from \"@tecture/shared\";\n\nexport const healthRouter: Router = Router();\n\nhealthRouter.get(\"/\", (_req, res) => {\n const body: ApiHealthResponse = {\n status: \"ok\",\n uptime: process.uptime(),\n timestamp: new Date().toISOString(),\n };\n res.json(body);\n});\n","import { Router, type Request, type Response, type NextFunction } from \"express\";\nimport type {\n ApiArchitectureError,\n ApiDiagram,\n ApiDiagramEdges,\n ApiDiagramLayout,\n ApiDiagramLayoutUpdate,\n ApiDiagramNodes,\n ApiNodeDetail,\n ManifestFile,\n} from \"@tecture/shared\";\nimport {\n type ArchitectureDataSource,\n DescriptionNotFoundError,\n DiagramNotFoundError,\n type LayoutStore,\n LayoutInvalidError,\n NodeNotFoundError,\n SLUG_RE,\n buildArchitectureSummary,\n emptyLayout,\n findNode,\n} from \"../source/types.js\";\nimport type { Reporter } from \"../telemetry.js\";\n\nexport type SourceResolver = (\n req: Request,\n) => ArchitectureDataSource | Promise<ArchitectureDataSource>;\n\nexport type LayoutStoreResolver = (\n req: Request,\n) => LayoutStore | null | Promise<LayoutStore | null>;\n\nexport interface ArchitectureRouterOptions {\n resolveSource: SourceResolver;\n resolveLayoutStore?: LayoutStoreResolver;\n /** Optional anonymous usage reporter (standalone viewer only). */\n report?: Reporter;\n}\n\nfunction send501(res: Response, body: ApiArchitectureError): void {\n res.status(501).json(body);\n}\n\nfunction send404(res: Response, body: ApiArchitectureError): void {\n res.status(404).json(body);\n}\n\nfunction send400(res: Response, body: ApiArchitectureError): void {\n res.status(400).json(body);\n}\n\nexport function createArchitectureRouter(\n opts: ArchitectureRouterOptions,\n): Router {\n const router = Router();\n const { resolveSource, resolveLayoutStore, report } = opts;\n\n router.get(\"/\", async (req, res, next) => {\n try {\n const source = await resolveSource(req);\n const summary = await buildArchitectureSummary(source);\n res.json(summary);\n } catch (err) {\n next(err);\n }\n });\n\n router.get(\"/manifest\", async (req, res, next) => {\n try {\n const source = await resolveSource(req);\n const manifest: ManifestFile = await source.loadManifest();\n res.json(manifest);\n } catch (err) {\n next(err);\n }\n });\n\n router.get(\"/diagrams\", async (req, res, next) => {\n try {\n const source = await resolveSource(req);\n const summary = await buildArchitectureSummary(source);\n res.json(summary.diagrams);\n } catch (err) {\n next(err);\n }\n });\n\n router.get(\"/diagrams/:diagramId\", async (req, res, next) => {\n const { diagramId } = req.params;\n if (!SLUG_RE.test(diagramId)) {\n return send404(res, { error: \"diagram_not_found\", slug: diagramId });\n }\n try {\n const source = await resolveSource(req);\n const diagram = await source.loadDiagram(diagramId);\n const body: ApiDiagram = {\n slug: diagramId,\n name: diagram.name,\n level: diagram.level,\n meta: diagram.meta,\n nodes: diagram.nodes,\n edges: diagram.edges ?? [],\n };\n res.json(body);\n // Anonymous usage signal: C4 level only, never the slug/name/content.\n report?.capture(\n \"diagram.viewed\",\n typeof diagram.level === \"number\" ? { level: diagram.level } : {},\n );\n } catch (err) {\n if (err instanceof DiagramNotFoundError) {\n return send404(res, { error: \"diagram_not_found\", slug: err.slug });\n }\n next(err);\n }\n });\n\n router.get(\"/diagrams/:diagramId/nodes\", async (req, res, next) => {\n const { diagramId } = req.params;\n if (!SLUG_RE.test(diagramId)) {\n return send404(res, { error: \"diagram_not_found\", slug: diagramId });\n }\n try {\n const source = await resolveSource(req);\n const diagram = await source.loadDiagram(diagramId);\n const body: ApiDiagramNodes = { diagramId, nodes: diagram.nodes };\n res.json(body);\n } catch (err) {\n if (err instanceof DiagramNotFoundError) {\n return send404(res, { error: \"diagram_not_found\", slug: err.slug });\n }\n next(err);\n }\n });\n\n router.get(\"/diagrams/:diagramId/edges\", async (req, res, next) => {\n const { diagramId } = req.params;\n if (!SLUG_RE.test(diagramId)) {\n return send404(res, { error: \"diagram_not_found\", slug: diagramId });\n }\n try {\n const source = await resolveSource(req);\n const diagram = await source.loadDiagram(diagramId);\n const body: ApiDiagramEdges = { diagramId, edges: diagram.edges ?? [] };\n res.json(body);\n } catch (err) {\n if (err instanceof DiagramNotFoundError) {\n return send404(res, { error: \"diagram_not_found\", slug: err.slug });\n }\n next(err);\n }\n });\n\n router.get(\"/diagrams/:diagramId/layout\", async (req, res, next) => {\n const { diagramId } = req.params;\n if (!SLUG_RE.test(diagramId)) {\n return send404(res, { error: \"diagram_not_found\", slug: diagramId });\n }\n try {\n const source = await resolveSource(req);\n await source.loadDiagram(diagramId);\n const store = resolveLayoutStore ? await resolveLayoutStore(req) : null;\n const layout: ApiDiagramLayout = store\n ? await store.loadLayout(diagramId)\n : emptyLayout(diagramId);\n res.json(layout);\n } catch (err) {\n if (err instanceof DiagramNotFoundError) {\n return send404(res, { error: \"diagram_not_found\", slug: err.slug });\n }\n next(err);\n }\n });\n\n router.put(\"/diagrams/:diagramId/layout\", async (req, res, next) => {\n const { diagramId } = req.params;\n if (!SLUG_RE.test(diagramId)) {\n return send404(res, { error: \"diagram_not_found\", slug: diagramId });\n }\n try {\n const source = await resolveSource(req);\n await source.loadDiagram(diagramId);\n const store = resolveLayoutStore ? await resolveLayoutStore(req) : null;\n if (!store) {\n return send501(res, {\n error: \"layout_not_supported\",\n message: \"Layout persistence is not configured for this data source\",\n });\n }\n const update = req.body as ApiDiagramLayoutUpdate;\n const saved = await store.saveLayout(diagramId, update);\n res.json(saved);\n } catch (err) {\n if (err instanceof DiagramNotFoundError) {\n return send404(res, { error: \"diagram_not_found\", slug: err.slug });\n }\n if (err instanceof LayoutInvalidError) {\n return send400(res, { error: \"layout_invalid\", message: err.message });\n }\n next(err);\n }\n });\n\n router.get(\"/nodes/:nodeId\", async (req, res, next) => {\n const { nodeId } = req.params;\n if (!SLUG_RE.test(nodeId)) {\n return send404(res, { error: \"node_not_found\", id: nodeId });\n }\n try {\n const source = await resolveSource(req);\n const { node, diagramId } = await findNode(source, nodeId);\n const description = await source.loadDescription(nodeId);\n const body: ApiNodeDetail = { ...node, diagramId, description };\n res.json(body);\n } catch (err) {\n if (err instanceof NodeNotFoundError) {\n return send404(res, { error: \"node_not_found\", id: err.id });\n }\n if (err instanceof DescriptionNotFoundError) {\n return send404(res, { error: \"description_not_found\", id: err.id });\n }\n next(err);\n }\n });\n\n router.get(\"/nodes/:nodeId/description\", async (req, res, next) => {\n const { nodeId } = req.params;\n if (!SLUG_RE.test(nodeId)) {\n return send404(res, { error: \"description_not_found\", id: nodeId });\n }\n try {\n const source = await resolveSource(req);\n await findNode(source, nodeId);\n const markdown = await source.loadDescription(nodeId);\n res.type(\"text/markdown; charset=utf-8\").send(markdown);\n } catch (err) {\n if (err instanceof NodeNotFoundError) {\n return send404(res, { error: \"node_not_found\", id: err.id });\n }\n if (err instanceof DescriptionNotFoundError) {\n return send404(res, { error: \"description_not_found\", id: err.id });\n }\n next(err);\n }\n });\n\n return router;\n}\n\nexport function architectureErrorHandler(\n err: unknown,\n _req: Request,\n res: Response,\n next: NextFunction,\n): void {\n if (res.headersSent) return next(err);\n const message = err instanceof Error ? err.message : String(err);\n const body: ApiArchitectureError = {\n error: \"architecture_unreadable\",\n message,\n };\n res.status(500).json(body);\n}\n","import type {\n ApiArchitectureSummary,\n ApiDiagramLayoutUpdate,\n ApiDiagramSummary,\n ArchitectureNode,\n DiagramFile,\n DiagramLayoutFile,\n ManifestFile,\n NodeLayoutEntry,\n SourceHost,\n} from \"./index\";\n\nexport const SLUG_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/;\n\nexport class DiagramNotFoundError extends Error {\n constructor(public readonly slug: string) {\n super(`Diagram not found: ${slug}`);\n this.name = \"DiagramNotFoundError\";\n }\n}\n\nexport class NodeNotFoundError extends Error {\n constructor(public readonly id: string) {\n super(`Node not found: ${id}`);\n this.name = \"NodeNotFoundError\";\n }\n}\n\nexport class DescriptionNotFoundError extends Error {\n constructor(public readonly id: string) {\n super(`Description not found: ${id}`);\n this.name = \"DescriptionNotFoundError\";\n }\n}\n\nexport class LayoutInvalidError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"LayoutInvalidError\";\n }\n}\n\nexport interface ArchitectureDataSource {\n loadManifest(): Promise<ManifestFile>;\n loadDiagram(slug: string): Promise<DiagramFile>;\n loadDescription(nodeId: string): Promise<string>;\n}\n\nexport interface LayoutStore {\n loadLayout(slug: string): Promise<DiagramLayoutFile>;\n saveLayout(\n slug: string,\n update: ApiDiagramLayoutUpdate,\n ): Promise<DiagramLayoutFile>;\n}\n\nexport function isFiniteNumber(v: unknown): v is number {\n return typeof v === \"number\" && Number.isFinite(v);\n}\n\nexport function isValidLayoutEntry(v: unknown): v is NodeLayoutEntry {\n if (!v || typeof v !== \"object\") return false;\n const e = v as Record<string, unknown>;\n if (!isFiniteNumber(e.x) || !isFiniteNumber(e.y)) return false;\n if (\"width\" in e && e.width !== undefined && !isFiniteNumber(e.width)) return false;\n if (\"height\" in e && e.height !== undefined && !isFiniteNumber(e.height)) return false;\n return true;\n}\n\nexport function emptyLayout(slug: string): DiagramLayoutFile {\n return { version: 1, diagramId: slug, updatedAt: \"\", nodes: {} };\n}\n\nexport function normalizeLayoutUpdate(\n slug: string,\n update: ApiDiagramLayoutUpdate,\n): DiagramLayoutFile {\n if (!SLUG_RE.test(slug)) {\n throw new LayoutInvalidError(`invalid diagram slug: ${slug}`);\n }\n if (\n !update ||\n typeof update !== \"object\" ||\n !update.nodes ||\n typeof update.nodes !== \"object\"\n ) {\n throw new LayoutInvalidError(\n \"body must be { nodes: Record<string, { x, y, width?, height? }> }\",\n );\n }\n const nodes: Record<string, NodeLayoutEntry> = {};\n for (const [id, entry] of Object.entries(update.nodes)) {\n if (!SLUG_RE.test(id)) {\n throw new LayoutInvalidError(`invalid node id: ${id}`);\n }\n if (!isValidLayoutEntry(entry)) {\n throw new LayoutInvalidError(`invalid layout entry for ${id}`);\n }\n const out: NodeLayoutEntry = { x: entry.x, y: entry.y };\n if (isFiniteNumber(entry.width)) out.width = entry.width;\n if (isFiniteNumber(entry.height)) out.height = entry.height;\n nodes[id] = out;\n }\n return {\n version: 1,\n diagramId: slug,\n updatedAt: new Date().toISOString(),\n nodes,\n };\n}\n\nexport async function buildArchitectureSummary(\n source: ArchitectureDataSource,\n): Promise<ApiArchitectureSummary> {\n const manifest = await source.loadManifest();\n const diagrams = await Promise.all(\n manifest.diagrams.map(async (slug): Promise<ApiDiagramSummary> => {\n const diagram = await source.loadDiagram(slug);\n return {\n slug,\n name: diagram.name,\n level: diagram.level,\n nodeCount: diagram.nodes?.length ?? 0,\n edgeCount: diagram.edges?.length ?? 0,\n };\n }),\n );\n return {\n name: manifest.name,\n description: manifest.description,\n source: manifest.source,\n sourceHost: manifest.sourceHost,\n topDiagram: manifest.topDiagram,\n diagrams,\n };\n}\n\nconst HOST_DOMAINS: Array<{ host: SourceHost; match: RegExp }> = [\n { host: \"github\", match: /(^|\\.)github\\.com$/i },\n { host: \"gitlab\", match: /(^|\\.)gitlab\\.com$/i },\n { host: \"bitbucket\", match: /(^|\\.)bitbucket\\.org$/i },\n];\n\nfunction inferSourceHost(source: string): SourceHost | undefined {\n // Extract the hostname without relying on the URL global (kept lib-agnostic for shared):\n // scheme://[userinfo@]host[:port]/...\n const hostname = /^[a-z][a-z0-9+.-]*:\\/\\/(?:[^@/]*@)?([^:/?#]+)/i.exec(source)?.[1];\n if (!hostname) return undefined;\n return HOST_DOMAINS.find(({ match }) => match.test(hostname))?.host;\n}\n\n/**\n * Build a web URL that views `path` (a repo-root-relative file or directory, trailing \"/\" = dir)\n * on the source repo host. `HEAD` resolves to the default branch so links stay current. Falls back\n * to the repo root when the host is unknown, and returns undefined when there is no source.\n */\nexport function buildSourceUrl(\n source: string | undefined,\n host: SourceHost | undefined,\n path: string,\n): string | undefined {\n if (!source) return undefined;\n const base = source.replace(/\\/+$/, \"\").replace(/\\.git$/i, \"\");\n const resolvedHost = host ?? inferSourceHost(base);\n const isDir = path.endsWith(\"/\");\n const encoded = path\n .replace(/^\\/+/, \"\")\n .replace(/\\/+$/, \"\")\n .split(\"/\")\n .map(encodeURIComponent)\n .join(\"/\");\n if (!encoded) return base;\n switch (resolvedHost) {\n case \"github\":\n return `${base}/${isDir ? \"tree\" : \"blob\"}/HEAD/${encoded}`;\n case \"gitlab\":\n return `${base}/-/${isDir ? \"tree\" : \"blob\"}/HEAD/${encoded}`;\n case \"bitbucket\":\n return `${base}/src/HEAD/${encoded}`;\n default:\n return base;\n }\n}\n\nexport async function findNode(\n source: ArchitectureDataSource,\n nodeId: string,\n): Promise<{ node: ArchitectureNode; diagramId: string }> {\n if (!SLUG_RE.test(nodeId)) throw new NodeNotFoundError(nodeId);\n const manifest = await source.loadManifest();\n for (const slug of manifest.diagrams) {\n let diagram: DiagramFile;\n try {\n diagram = await source.loadDiagram(slug);\n } catch (err) {\n if (err instanceof DiagramNotFoundError) continue;\n throw err;\n }\n const match = diagram.nodes?.find((n) => n.id === nodeId);\n if (match) return { node: match, diagramId: slug };\n }\n throw new NodeNotFoundError(nodeId);\n}\n","import { mkdir, readFile, rename, writeFile } from \"node:fs/promises\";\nimport { dirname, join, resolve, sep } from \"node:path\";\nimport type {\n ApiDiagramLayoutUpdate,\n DiagramFile,\n DiagramLayoutFile,\n ManifestFile,\n NodeLayoutEntry,\n} from \"@tecture/shared\";\nimport {\n type ArchitectureDataSource,\n DescriptionNotFoundError,\n DiagramNotFoundError,\n type LayoutStore,\n SLUG_RE,\n emptyLayout,\n isFiniteNumber,\n isValidLayoutEntry,\n normalizeLayoutUpdate,\n} from \"./types.js\";\n\nexport function safeJoin(root: string, ...segments: string[]): string {\n const absRoot = resolve(root);\n const candidate = resolve(join(absRoot, ...segments));\n const guard = absRoot.endsWith(sep) ? absRoot : absRoot + sep;\n if (candidate !== absRoot && !candidate.startsWith(guard)) {\n throw new Error(`Path escapes architecture root: ${candidate}`);\n }\n return candidate;\n}\n\nfunction isNodeErrnoError(err: unknown): err is NodeJS.ErrnoException {\n return err instanceof Error && \"code\" in err;\n}\n\nasync function loadJsonFile<T>(path: string): Promise<T> {\n const raw = await readFile(path, \"utf8\");\n return JSON.parse(raw) as T;\n}\n\nexport class FsArchitectureDataSource implements ArchitectureDataSource {\n constructor(private readonly root: string) {}\n\n async loadManifest(): Promise<ManifestFile> {\n const path = safeJoin(this.root, \"manifest.json\");\n return loadJsonFile<ManifestFile>(path);\n }\n\n async loadDiagram(slug: string): Promise<DiagramFile> {\n if (!SLUG_RE.test(slug)) throw new DiagramNotFoundError(slug);\n const path = safeJoin(this.root, \"diagrams\", `${slug}.json`);\n try {\n return await loadJsonFile<DiagramFile>(path);\n } catch (err) {\n if (isNodeErrnoError(err) && err.code === \"ENOENT\") {\n throw new DiagramNotFoundError(slug);\n }\n throw err;\n }\n }\n\n async loadDescription(nodeId: string): Promise<string> {\n if (!SLUG_RE.test(nodeId)) throw new DescriptionNotFoundError(nodeId);\n const path = safeJoin(this.root, \"descriptions\", `${nodeId}.md`);\n try {\n return await readFile(path, \"utf8\");\n } catch (err) {\n if (isNodeErrnoError(err) && err.code === \"ENOENT\") {\n throw new DescriptionNotFoundError(nodeId);\n }\n throw err;\n }\n }\n}\n\nexport class FsLayoutStore implements LayoutStore {\n constructor(private readonly tectureRoot: string) {}\n\n private path(slug: string): string {\n return safeJoin(this.tectureRoot, \"layouts\", `${slug}.json`);\n }\n\n async loadLayout(slug: string): Promise<DiagramLayoutFile> {\n if (!SLUG_RE.test(slug)) return emptyLayout(slug);\n const path = this.path(slug);\n let raw: string;\n try {\n raw = await readFile(path, \"utf8\");\n } catch (err) {\n if (isNodeErrnoError(err) && err.code === \"ENOENT\") return emptyLayout(slug);\n console.warn(`[tecture] failed to read layout ${path}: ${String(err)}`);\n return emptyLayout(slug);\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n console.warn(`[tecture] malformed layout JSON at ${path}: ${String(err)}`);\n return emptyLayout(slug);\n }\n\n if (!parsed || typeof parsed !== \"object\") return emptyLayout(slug);\n const p = parsed as Record<string, unknown>;\n const nodes: Record<string, NodeLayoutEntry> = {};\n if (p.nodes && typeof p.nodes === \"object\") {\n for (const [id, entry] of Object.entries(p.nodes as Record<string, unknown>)) {\n if (!SLUG_RE.test(id)) continue;\n if (!isValidLayoutEntry(entry)) continue;\n const out: NodeLayoutEntry = { x: entry.x, y: entry.y };\n if (isFiniteNumber(entry.width)) out.width = entry.width;\n if (isFiniteNumber(entry.height)) out.height = entry.height;\n nodes[id] = out;\n }\n }\n return {\n version: 1,\n diagramId: slug,\n updatedAt: typeof p.updatedAt === \"string\" ? p.updatedAt : \"\",\n nodes,\n };\n }\n\n async saveLayout(\n slug: string,\n update: ApiDiagramLayoutUpdate,\n ): Promise<DiagramLayoutFile> {\n const file = normalizeLayoutUpdate(slug, update);\n const path = this.path(slug);\n await mkdir(dirname(path), { recursive: true });\n const tmp = `${path}.tmp`;\n await writeFile(tmp, `${JSON.stringify(file, null, 2)}\\n`, \"utf8\");\n await rename(tmp, path);\n return file;\n }\n}\n","import { randomUUID } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { version as coreVersion } from \"../package.json\";\n\n// PostHog *project* API key — a public client key, safe to embed. Same project\n// as the VS Code extension + website, so standalone-viewer usage joins the same\n// funnel. Empty string disables telemetry entirely.\nconst POSTHOG_KEY = \"phc_wTpBpKNGhboxPitLJYLSi8aGGCaYJ7fHbvLAT93edmHp\";\nconst POSTHOG_HOST = \"https://us.i.posthog.com\";\n\ntype EventProps = Record<string, string | number | boolean>;\n\nexport interface Reporter {\n /** Send an anonymous usage event. No-op when telemetry is disabled. */\n capture(event: string, properties?: EventProps): void;\n}\n\nconst NOOP: Reporter = { capture() {} };\n\nfunction disabledByEnv(): boolean {\n const dnt = process.env.DO_NOT_TRACK;\n if (dnt === \"1\" || dnt === \"true\") return true;\n const t = process.env.TECTURE_TELEMETRY?.toLowerCase();\n return t === \"0\" || t === \"false\" || t === \"off\" || t === \"no\";\n}\n\n/**\n * Anonymous, opt-out usage telemetry for the standalone viewer (only ever runs\n * for `npx @tecture/core` — the VS Code extension talks to its own host, not\n * this server, so there is no double-counting).\n *\n * - Opt-out: honors `DO_NOT_TRACK=1` and `TECTURE_TELEMETRY=0`, and prints a\n * one-time notice on first run.\n * - Anonymous: a random id persisted under `~/.config/tecture/telemetry-id`\n * (so repeat runs count as one user). No machine name, user, or repo data.\n * - NEVER sends architecture content: only the C4 level (1/2/3) and counts.\n */\nexport function createTelemetry(): Reporter {\n if (POSTHOG_KEY.length === 0 || disabledByEnv()) return NOOP;\n\n let distinctId: string;\n let firstRun = false;\n try {\n const dir = join(homedir(), \".config\", \"tecture\");\n const idFile = join(dir, \"telemetry-id\");\n if (existsSync(idFile)) {\n distinctId = readFileSync(idFile, \"utf8\").trim() || randomUUID();\n } else {\n distinctId = randomUUID();\n mkdirSync(dir, { recursive: true });\n writeFileSync(idFile, distinctId);\n firstRun = true;\n }\n } catch {\n distinctId = randomUUID(); // ephemeral fallback if the config dir isn't writable\n }\n\n if (firstRun) {\n console.error(\n \"\\nTecture collects anonymous usage stats (which diagram levels are viewed, how often)\\n\" +\n \"to help improve the tool. It never sends your architecture content, names, or paths.\\n\" +\n \"Opt out any time: set TECTURE_TELEMETRY=0 (or DO_NOT_TRACK=1).\\n\",\n );\n }\n\n const superProps: EventProps = {\n coreVersion,\n nodeVersion: process.version,\n platform: process.platform,\n };\n\n return {\n capture(event, properties = {}) {\n const body = JSON.stringify({\n api_key: POSTHOG_KEY,\n event,\n distinct_id: distinctId,\n properties: { ...superProps, ...properties, $process_person_profile: false },\n });\n // Fire-and-forget: never block the server or throw on a dead network.\n void fetch(`${POSTHOG_HOST}/capture/`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body,\n }).catch(() => {});\n },\n };\n}\n","{\n \"name\": \"@tecture/core\",\n \"version\": \"0.0.8\",\n \"description\": \"Next generation architecture documentation generated by coding agents — render file-based architecture diagrams (JSON + Markdown) in your browser.\",\n \"keywords\": [\n \"c4\",\n \"architecture\",\n \"diagram\",\n \"documentation\",\n \"visualization\",\n \"reactflow\",\n \"agents\",\n \"llm\",\n \"coding-agents\"\n ],\n \"license\": \"MIT\",\n \"author\": \"Shanika Wijerathna <shanikacj@gmail.com>\",\n \"homepage\": \"https://github.com/tecture-io/tecture#readme\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/tecture-io/tecture.git\",\n \"directory\": \"packages/server\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/tecture-io/tecture/issues\"\n },\n \"type\": \"module\",\n \"main\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\"\n },\n \"./cli\": \"./dist/cli.js\"\n },\n \"bin\": \"./dist/cli.js\",\n \"files\": [\n \"dist\",\n \"CHANGELOG.md\"\n ],\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"scripts\": {\n \"dev\": \"tsx watch src/cli.ts --port 3001 --architecture-path ../../architecture --tecture-path ../../architecture/.tecture\",\n \"build\": \"tsup && node scripts/copy-web-assets.mjs\",\n \"start\": \"node dist/cli.js\",\n \"typecheck\": \"tsc --noEmit\"\n },\n \"dependencies\": {\n \"express\": \"^4.21.1\"\n },\n \"devDependencies\": {\n \"@tecture/shared\": \"workspace:*\",\n \"@tecture/web\": \"workspace:*\",\n \"@types/express\": \"^4.17.21\",\n \"@types/node\": \"^20.17.6\",\n \"tsup\": \"^8.3.5\",\n \"tsx\": \"^4.19.2\",\n \"typescript\": \"^5.6.3\"\n }\n}\n"],"mappings":";;;AAAA,SAAS,WAAAA,gBAAe;;;ACAxB,SAAS,kBAAkB;AAC3B,SAAS,qBAAqB;AAC9B,OAAO,aAA6C;;;ACFpD,SAAS,cAAc;AAGhB,IAAM,eAAuB,OAAO;AAE3C,aAAa,IAAI,KAAK,CAAC,MAAM,QAAQ;AACnC,QAAM,OAA0B;AAAA,IAC9B,QAAQ;AAAA,IACR,QAAQ,QAAQ,OAAO;AAAA,IACvB,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,EACpC;AACA,MAAI,KAAK,IAAI;AACf,CAAC;;;ACZD,SAAS,UAAAC,eAA8D;;;ACYhE,IAAM,UAAU;AAEhB,IAAM,uBAAN,cAAmC,MAAM;AAAA,EAC9C,YAA4B,MAAc;AACxC,UAAM,sBAAsB,IAAI,EAAE;AADR;AAE1B,SAAK,OAAO;AAAA,EACd;AAAA,EAH4B;AAI9B;AAEO,IAAM,oBAAN,cAAgC,MAAM;AAAA,EAC3C,YAA4B,IAAY;AACtC,UAAM,mBAAmB,EAAE,EAAE;AADH;AAE1B,SAAK,OAAO;AAAA,EACd;AAAA,EAH4B;AAI9B;AAEO,IAAM,2BAAN,cAAuC,MAAM;AAAA,EAClD,YAA4B,IAAY;AACtC,UAAM,0BAA0B,EAAE,EAAE;AADV;AAE1B,SAAK,OAAO;AAAA,EACd;AAAA,EAH4B;AAI9B;AAEO,IAAM,qBAAN,cAAiC,MAAM;AAAA,EAC5C,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAgBO,SAAS,eAAe,GAAyB;AACtD,SAAO,OAAO,MAAM,YAAY,OAAO,SAAS,CAAC;AACnD;AAEO,SAAS,mBAAmB,GAAkC;AACnE,MAAI,CAAC,KAAK,OAAO,MAAM,SAAU,QAAO;AACxC,QAAM,IAAI;AACV,MAAI,CAAC,eAAe,EAAE,CAAC,KAAK,CAAC,eAAe,EAAE,CAAC,EAAG,QAAO;AACzD,MAAI,WAAW,KAAK,EAAE,UAAU,UAAa,CAAC,eAAe,EAAE,KAAK,EAAG,QAAO;AAC9E,MAAI,YAAY,KAAK,EAAE,WAAW,UAAa,CAAC,eAAe,EAAE,MAAM,EAAG,QAAO;AACjF,SAAO;AACT;AAEO,SAAS,YAAY,MAAiC;AAC3D,SAAO,EAAE,SAAS,GAAG,WAAW,MAAM,WAAW,IAAI,OAAO,CAAC,EAAE;AACjE;AAEO,SAAS,sBACd,MACA,QACmB;AACnB,MAAI,CAAC,QAAQ,KAAK,IAAI,GAAG;AACvB,UAAM,IAAI,mBAAmB,yBAAyB,IAAI,EAAE;AAAA,EAC9D;AACA,MACE,CAAC,UACD,OAAO,WAAW,YAClB,CAAC,OAAO,SACR,OAAO,OAAO,UAAU,UACxB;AACA,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,QAAM,QAAyC,CAAC;AAChD,aAAW,CAAC,IAAI,KAAK,KAAK,OAAO,QAAQ,OAAO,KAAK,GAAG;AACtD,QAAI,CAAC,QAAQ,KAAK,EAAE,GAAG;AACrB,YAAM,IAAI,mBAAmB,oBAAoB,EAAE,EAAE;AAAA,IACvD;AACA,QAAI,CAAC,mBAAmB,KAAK,GAAG;AAC9B,YAAM,IAAI,mBAAmB,4BAA4B,EAAE,EAAE;AAAA,IAC/D;AACA,UAAM,MAAuB,EAAE,GAAG,MAAM,GAAG,GAAG,MAAM,EAAE;AACtD,QAAI,eAAe,MAAM,KAAK,EAAG,KAAI,QAAQ,MAAM;AACnD,QAAI,eAAe,MAAM,MAAM,EAAG,KAAI,SAAS,MAAM;AACrD,UAAM,EAAE,IAAI;AAAA,EACd;AACA,SAAO;AAAA,IACL,SAAS;AAAA,IACT,WAAW;AAAA,IACX,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IAClC;AAAA,EACF;AACF;AAEA,eAAsB,yBACpB,QACiC;AACjC,QAAM,WAAW,MAAM,OAAO,aAAa;AAC3C,QAAM,WAAW,MAAM,QAAQ;AAAA,IAC7B,SAAS,SAAS,IAAI,OAAO,SAAqC;AAChE,YAAM,UAAU,MAAM,OAAO,YAAY,IAAI;AAC7C,aAAO;AAAA,QACL;AAAA,QACA,MAAM,QAAQ;AAAA,QACd,OAAO,QAAQ;AAAA,QACf,WAAW,QAAQ,OAAO,UAAU;AAAA,QACpC,WAAW,QAAQ,OAAO,UAAU;AAAA,MACtC;AAAA,IACF,CAAC;AAAA,EACH;AACA,SAAO;AAAA,IACL,MAAM,SAAS;AAAA,IACf,aAAa,SAAS;AAAA,IACtB,QAAQ,SAAS;AAAA,IACjB,YAAY,SAAS;AAAA,IACrB,YAAY,SAAS;AAAA,IACrB;AAAA,EACF;AACF;AAiDA,eAAsB,SACpB,QACA,QACwD;AACxD,MAAI,CAAC,QAAQ,KAAK,MAAM,EAAG,OAAM,IAAI,kBAAkB,MAAM;AAC7D,QAAM,WAAW,MAAM,OAAO,aAAa;AAC3C,aAAW,QAAQ,SAAS,UAAU;AACpC,QAAI;AACJ,QAAI;AACF,gBAAU,MAAM,OAAO,YAAY,IAAI;AAAA,IACzC,SAAS,KAAK;AACZ,UAAI,eAAe,qBAAsB;AACzC,YAAM;AAAA,IACR;AACA,UAAM,QAAQ,QAAQ,OAAO,KAAK,CAAC,MAAM,EAAE,OAAO,MAAM;AACxD,QAAI,MAAO,QAAO,EAAE,MAAM,OAAO,WAAW,KAAK;AAAA,EACnD;AACA,QAAM,IAAI,kBAAkB,MAAM;AACpC;;;ADlKA,SAAS,QAAQ,KAAe,MAAkC;AAChE,MAAI,OAAO,GAAG,EAAE,KAAK,IAAI;AAC3B;AAEA,SAAS,QAAQ,KAAe,MAAkC;AAChE,MAAI,OAAO,GAAG,EAAE,KAAK,IAAI;AAC3B;AAEA,SAAS,QAAQ,KAAe,MAAkC;AAChE,MAAI,OAAO,GAAG,EAAE,KAAK,IAAI;AAC3B;AAEO,SAAS,yBACd,MACQ;AACR,QAAM,SAASC,QAAO;AACtB,QAAM,EAAE,eAAe,oBAAoB,OAAO,IAAI;AAEtD,SAAO,IAAI,KAAK,OAAO,KAAK,KAAK,SAAS;AACxC,QAAI;AACF,YAAM,SAAS,MAAM,cAAc,GAAG;AACtC,YAAM,UAAU,MAAM,yBAAyB,MAAM;AACrD,UAAI,KAAK,OAAO;AAAA,IAClB,SAAS,KAAK;AACZ,WAAK,GAAG;AAAA,IACV;AAAA,EACF,CAAC;AAED,SAAO,IAAI,aAAa,OAAO,KAAK,KAAK,SAAS;AAChD,QAAI;AACF,YAAM,SAAS,MAAM,cAAc,GAAG;AACtC,YAAM,WAAyB,MAAM,OAAO,aAAa;AACzD,UAAI,KAAK,QAAQ;AAAA,IACnB,SAAS,KAAK;AACZ,WAAK,GAAG;AAAA,IACV;AAAA,EACF,CAAC;AAED,SAAO,IAAI,aAAa,OAAO,KAAK,KAAK,SAAS;AAChD,QAAI;AACF,YAAM,SAAS,MAAM,cAAc,GAAG;AACtC,YAAM,UAAU,MAAM,yBAAyB,MAAM;AACrD,UAAI,KAAK,QAAQ,QAAQ;AAAA,IAC3B,SAAS,KAAK;AACZ,WAAK,GAAG;AAAA,IACV;AAAA,EACF,CAAC;AAED,SAAO,IAAI,wBAAwB,OAAO,KAAK,KAAK,SAAS;AAC3D,UAAM,EAAE,UAAU,IAAI,IAAI;AAC1B,QAAI,CAAC,QAAQ,KAAK,SAAS,GAAG;AAC5B,aAAO,QAAQ,KAAK,EAAE,OAAO,qBAAqB,MAAM,UAAU,CAAC;AAAA,IACrE;AACA,QAAI;AACF,YAAM,SAAS,MAAM,cAAc,GAAG;AACtC,YAAM,UAAU,MAAM,OAAO,YAAY,SAAS;AAClD,YAAM,OAAmB;AAAA,QACvB,MAAM;AAAA,QACN,MAAM,QAAQ;AAAA,QACd,OAAO,QAAQ;AAAA,QACf,MAAM,QAAQ;AAAA,QACd,OAAO,QAAQ;AAAA,QACf,OAAO,QAAQ,SAAS,CAAC;AAAA,MAC3B;AACA,UAAI,KAAK,IAAI;AAEb,cAAQ;AAAA,QACN;AAAA,QACA,OAAO,QAAQ,UAAU,WAAW,EAAE,OAAO,QAAQ,MAAM,IAAI,CAAC;AAAA,MAClE;AAAA,IACF,SAAS,KAAK;AACZ,UAAI,eAAe,sBAAsB;AACvC,eAAO,QAAQ,KAAK,EAAE,OAAO,qBAAqB,MAAM,IAAI,KAAK,CAAC;AAAA,MACpE;AACA,WAAK,GAAG;AAAA,IACV;AAAA,EACF,CAAC;AAED,SAAO,IAAI,8BAA8B,OAAO,KAAK,KAAK,SAAS;AACjE,UAAM,EAAE,UAAU,IAAI,IAAI;AAC1B,QAAI,CAAC,QAAQ,KAAK,SAAS,GAAG;AAC5B,aAAO,QAAQ,KAAK,EAAE,OAAO,qBAAqB,MAAM,UAAU,CAAC;AAAA,IACrE;AACA,QAAI;AACF,YAAM,SAAS,MAAM,cAAc,GAAG;AACtC,YAAM,UAAU,MAAM,OAAO,YAAY,SAAS;AAClD,YAAM,OAAwB,EAAE,WAAW,OAAO,QAAQ,MAAM;AAChE,UAAI,KAAK,IAAI;AAAA,IACf,SAAS,KAAK;AACZ,UAAI,eAAe,sBAAsB;AACvC,eAAO,QAAQ,KAAK,EAAE,OAAO,qBAAqB,MAAM,IAAI,KAAK,CAAC;AAAA,MACpE;AACA,WAAK,GAAG;AAAA,IACV;AAAA,EACF,CAAC;AAED,SAAO,IAAI,8BAA8B,OAAO,KAAK,KAAK,SAAS;AACjE,UAAM,EAAE,UAAU,IAAI,IAAI;AAC1B,QAAI,CAAC,QAAQ,KAAK,SAAS,GAAG;AAC5B,aAAO,QAAQ,KAAK,EAAE,OAAO,qBAAqB,MAAM,UAAU,CAAC;AAAA,IACrE;AACA,QAAI;AACF,YAAM,SAAS,MAAM,cAAc,GAAG;AACtC,YAAM,UAAU,MAAM,OAAO,YAAY,SAAS;AAClD,YAAM,OAAwB,EAAE,WAAW,OAAO,QAAQ,SAAS,CAAC,EAAE;AACtE,UAAI,KAAK,IAAI;AAAA,IACf,SAAS,KAAK;AACZ,UAAI,eAAe,sBAAsB;AACvC,eAAO,QAAQ,KAAK,EAAE,OAAO,qBAAqB,MAAM,IAAI,KAAK,CAAC;AAAA,MACpE;AACA,WAAK,GAAG;AAAA,IACV;AAAA,EACF,CAAC;AAED,SAAO,IAAI,+BAA+B,OAAO,KAAK,KAAK,SAAS;AAClE,UAAM,EAAE,UAAU,IAAI,IAAI;AAC1B,QAAI,CAAC,QAAQ,KAAK,SAAS,GAAG;AAC5B,aAAO,QAAQ,KAAK,EAAE,OAAO,qBAAqB,MAAM,UAAU,CAAC;AAAA,IACrE;AACA,QAAI;AACF,YAAM,SAAS,MAAM,cAAc,GAAG;AACtC,YAAM,OAAO,YAAY,SAAS;AAClC,YAAM,QAAQ,qBAAqB,MAAM,mBAAmB,GAAG,IAAI;AACnE,YAAM,SAA2B,QAC7B,MAAM,MAAM,WAAW,SAAS,IAChC,YAAY,SAAS;AACzB,UAAI,KAAK,MAAM;AAAA,IACjB,SAAS,KAAK;AACZ,UAAI,eAAe,sBAAsB;AACvC,eAAO,QAAQ,KAAK,EAAE,OAAO,qBAAqB,MAAM,IAAI,KAAK,CAAC;AAAA,MACpE;AACA,WAAK,GAAG;AAAA,IACV;AAAA,EACF,CAAC;AAED,SAAO,IAAI,+BAA+B,OAAO,KAAK,KAAK,SAAS;AAClE,UAAM,EAAE,UAAU,IAAI,IAAI;AAC1B,QAAI,CAAC,QAAQ,KAAK,SAAS,GAAG;AAC5B,aAAO,QAAQ,KAAK,EAAE,OAAO,qBAAqB,MAAM,UAAU,CAAC;AAAA,IACrE;AACA,QAAI;AACF,YAAM,SAAS,MAAM,cAAc,GAAG;AACtC,YAAM,OAAO,YAAY,SAAS;AAClC,YAAM,QAAQ,qBAAqB,MAAM,mBAAmB,GAAG,IAAI;AACnE,UAAI,CAAC,OAAO;AACV,eAAO,QAAQ,KAAK;AAAA,UAClB,OAAO;AAAA,UACP,SAAS;AAAA,QACX,CAAC;AAAA,MACH;AACA,YAAM,SAAS,IAAI;AACnB,YAAM,QAAQ,MAAM,MAAM,WAAW,WAAW,MAAM;AACtD,UAAI,KAAK,KAAK;AAAA,IAChB,SAAS,KAAK;AACZ,UAAI,eAAe,sBAAsB;AACvC,eAAO,QAAQ,KAAK,EAAE,OAAO,qBAAqB,MAAM,IAAI,KAAK,CAAC;AAAA,MACpE;AACA,UAAI,eAAe,oBAAoB;AACrC,eAAO,QAAQ,KAAK,EAAE,OAAO,kBAAkB,SAAS,IAAI,QAAQ,CAAC;AAAA,MACvE;AACA,WAAK,GAAG;AAAA,IACV;AAAA,EACF,CAAC;AAED,SAAO,IAAI,kBAAkB,OAAO,KAAK,KAAK,SAAS;AACrD,UAAM,EAAE,OAAO,IAAI,IAAI;AACvB,QAAI,CAAC,QAAQ,KAAK,MAAM,GAAG;AACzB,aAAO,QAAQ,KAAK,EAAE,OAAO,kBAAkB,IAAI,OAAO,CAAC;AAAA,IAC7D;AACA,QAAI;AACF,YAAM,SAAS,MAAM,cAAc,GAAG;AACtC,YAAM,EAAE,MAAM,UAAU,IAAI,MAAM,SAAS,QAAQ,MAAM;AACzD,YAAM,cAAc,MAAM,OAAO,gBAAgB,MAAM;AACvD,YAAM,OAAsB,EAAE,GAAG,MAAM,WAAW,YAAY;AAC9D,UAAI,KAAK,IAAI;AAAA,IACf,SAAS,KAAK;AACZ,UAAI,eAAe,mBAAmB;AACpC,eAAO,QAAQ,KAAK,EAAE,OAAO,kBAAkB,IAAI,IAAI,GAAG,CAAC;AAAA,MAC7D;AACA,UAAI,eAAe,0BAA0B;AAC3C,eAAO,QAAQ,KAAK,EAAE,OAAO,yBAAyB,IAAI,IAAI,GAAG,CAAC;AAAA,MACpE;AACA,WAAK,GAAG;AAAA,IACV;AAAA,EACF,CAAC;AAED,SAAO,IAAI,8BAA8B,OAAO,KAAK,KAAK,SAAS;AACjE,UAAM,EAAE,OAAO,IAAI,IAAI;AACvB,QAAI,CAAC,QAAQ,KAAK,MAAM,GAAG;AACzB,aAAO,QAAQ,KAAK,EAAE,OAAO,yBAAyB,IAAI,OAAO,CAAC;AAAA,IACpE;AACA,QAAI;AACF,YAAM,SAAS,MAAM,cAAc,GAAG;AACtC,YAAM,SAAS,QAAQ,MAAM;AAC7B,YAAM,WAAW,MAAM,OAAO,gBAAgB,MAAM;AACpD,UAAI,KAAK,8BAA8B,EAAE,KAAK,QAAQ;AAAA,IACxD,SAAS,KAAK;AACZ,UAAI,eAAe,mBAAmB;AACpC,eAAO,QAAQ,KAAK,EAAE,OAAO,kBAAkB,IAAI,IAAI,GAAG,CAAC;AAAA,MAC7D;AACA,UAAI,eAAe,0BAA0B;AAC3C,eAAO,QAAQ,KAAK,EAAE,OAAO,yBAAyB,IAAI,IAAI,GAAG,CAAC;AAAA,MACpE;AACA,WAAK,GAAG;AAAA,IACV;AAAA,EACF,CAAC;AAED,SAAO;AACT;AAEO,SAAS,yBACd,KACA,MACA,KACA,MACM;AACN,MAAI,IAAI,YAAa,QAAO,KAAK,GAAG;AACpC,QAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,QAAM,OAA6B;AAAA,IACjC,OAAO;AAAA,IACP;AAAA,EACF;AACA,MAAI,OAAO,GAAG,EAAE,KAAK,IAAI;AAC3B;;;AFhPA,SAAS,iBACP,OACgB;AAChB,MAAI,OAAO,UAAU,WAAY,QAAO;AACxC,SAAO,MAAM;AACf;AAEA,SAAS,sBACP,OACiC;AACjC,MAAI,SAAS,KAAM,QAAO;AAC1B,MAAI,OAAO,UAAU,WAAY,QAAO;AACxC,SAAO,MAAM;AACf;AAEO,SAAS,UAAU,SAAoC;AAC5D,QAAMC,OAAM,QAAQ;AAEpB,EAAAA,KAAI,IAAI,QAAQ,KAAK,EAAE,OAAO,QAAQ,CAAC,CAAC;AACxC,EAAAA,KAAI,IAAI,eAAe,YAAY;AACnC,EAAAA,KAAI;AAAA,IACF;AAAA,IACA,yBAAyB;AAAA,MACvB,eAAe,iBAAiB,QAAQ,MAAM;AAAA,MAC9C,oBAAoB,sBAAsB,QAAQ,WAAW;AAAA,MAC7D,QAAQ,QAAQ;AAAA,IAClB,CAAC;AAAA,EACH;AACA,EAAAA,KAAI,IAAI,qBAAqB,wBAAwB;AAErD,QAAM,YAAY,cAAc,IAAI,IAAI,YAAY,YAAY,GAAG,CAAC;AAEpE,MAAI,WAAW,SAAS,GAAG;AACzB,IAAAA,KAAI,IAAI,QAAQ,OAAO,SAAS,CAAC;AACjC,IAAAA,KAAI,IAAI,KAAK,CAAC,KAAc,KAAK,SAAS;AACxC,UAAI,IAAI,KAAK,WAAW,OAAO,EAAG,QAAO,KAAK;AAC9C,UAAI,SAAS,cAAc,IAAI,IAAI,uBAAuB,YAAY,GAAG,CAAC,CAAC;AAAA,IAC7E,CAAC;AAAA,EACH;AAEA,SAAOA;AACT;;;AIhEA,SAAS,OAAO,UAAU,QAAQ,iBAAiB;AACnD,SAAS,SAAS,MAAM,SAAS,WAAW;AAoBrC,SAAS,SAAS,SAAiB,UAA4B;AACpE,QAAM,UAAU,QAAQ,IAAI;AAC5B,QAAM,YAAY,QAAQ,KAAK,SAAS,GAAG,QAAQ,CAAC;AACpD,QAAM,QAAQ,QAAQ,SAAS,GAAG,IAAI,UAAU,UAAU;AAC1D,MAAI,cAAc,WAAW,CAAC,UAAU,WAAW,KAAK,GAAG;AACzD,UAAM,IAAI,MAAM,mCAAmC,SAAS,EAAE;AAAA,EAChE;AACA,SAAO;AACT;AAEA,SAAS,iBAAiB,KAA4C;AACpE,SAAO,eAAe,SAAS,UAAU;AAC3C;AAEA,eAAe,aAAgB,MAA0B;AACvD,QAAM,MAAM,MAAM,SAAS,MAAM,MAAM;AACvC,SAAO,KAAK,MAAM,GAAG;AACvB;AAEO,IAAM,2BAAN,MAAiE;AAAA,EACtE,YAA6B,MAAc;AAAd;AAAA,EAAe;AAAA,EAAf;AAAA,EAE7B,MAAM,eAAsC;AAC1C,UAAM,OAAO,SAAS,KAAK,MAAM,eAAe;AAChD,WAAO,aAA2B,IAAI;AAAA,EACxC;AAAA,EAEA,MAAM,YAAY,MAAoC;AACpD,QAAI,CAAC,QAAQ,KAAK,IAAI,EAAG,OAAM,IAAI,qBAAqB,IAAI;AAC5D,UAAM,OAAO,SAAS,KAAK,MAAM,YAAY,GAAG,IAAI,OAAO;AAC3D,QAAI;AACF,aAAO,MAAM,aAA0B,IAAI;AAAA,IAC7C,SAAS,KAAK;AACZ,UAAI,iBAAiB,GAAG,KAAK,IAAI,SAAS,UAAU;AAClD,cAAM,IAAI,qBAAqB,IAAI;AAAA,MACrC;AACA,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,MAAM,gBAAgB,QAAiC;AACrD,QAAI,CAAC,QAAQ,KAAK,MAAM,EAAG,OAAM,IAAI,yBAAyB,MAAM;AACpE,UAAM,OAAO,SAAS,KAAK,MAAM,gBAAgB,GAAG,MAAM,KAAK;AAC/D,QAAI;AACF,aAAO,MAAM,SAAS,MAAM,MAAM;AAAA,IACpC,SAAS,KAAK;AACZ,UAAI,iBAAiB,GAAG,KAAK,IAAI,SAAS,UAAU;AAClD,cAAM,IAAI,yBAAyB,MAAM;AAAA,MAC3C;AACA,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAEO,IAAM,gBAAN,MAA2C;AAAA,EAChD,YAA6B,aAAqB;AAArB;AAAA,EAAsB;AAAA,EAAtB;AAAA,EAErB,KAAK,MAAsB;AACjC,WAAO,SAAS,KAAK,aAAa,WAAW,GAAG,IAAI,OAAO;AAAA,EAC7D;AAAA,EAEA,MAAM,WAAW,MAA0C;AACzD,QAAI,CAAC,QAAQ,KAAK,IAAI,EAAG,QAAO,YAAY,IAAI;AAChD,UAAM,OAAO,KAAK,KAAK,IAAI;AAC3B,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,SAAS,MAAM,MAAM;AAAA,IACnC,SAAS,KAAK;AACZ,UAAI,iBAAiB,GAAG,KAAK,IAAI,SAAS,SAAU,QAAO,YAAY,IAAI;AAC3E,cAAQ,KAAK,mCAAmC,IAAI,KAAK,OAAO,GAAG,CAAC,EAAE;AACtE,aAAO,YAAY,IAAI;AAAA,IACzB;AAEA,QAAI;AACJ,QAAI;AACF,eAAS,KAAK,MAAM,GAAG;AAAA,IACzB,SAAS,KAAK;AACZ,cAAQ,KAAK,sCAAsC,IAAI,KAAK,OAAO,GAAG,CAAC,EAAE;AACzE,aAAO,YAAY,IAAI;AAAA,IACzB;AAEA,QAAI,CAAC,UAAU,OAAO,WAAW,SAAU,QAAO,YAAY,IAAI;AAClE,UAAM,IAAI;AACV,UAAM,QAAyC,CAAC;AAChD,QAAI,EAAE,SAAS,OAAO,EAAE,UAAU,UAAU;AAC1C,iBAAW,CAAC,IAAI,KAAK,KAAK,OAAO,QAAQ,EAAE,KAAgC,GAAG;AAC5E,YAAI,CAAC,QAAQ,KAAK,EAAE,EAAG;AACvB,YAAI,CAAC,mBAAmB,KAAK,EAAG;AAChC,cAAM,MAAuB,EAAE,GAAG,MAAM,GAAG,GAAG,MAAM,EAAE;AACtD,YAAI,eAAe,MAAM,KAAK,EAAG,KAAI,QAAQ,MAAM;AACnD,YAAI,eAAe,MAAM,MAAM,EAAG,KAAI,SAAS,MAAM;AACrD,cAAM,EAAE,IAAI;AAAA,MACd;AAAA,IACF;AACA,WAAO;AAAA,MACL,SAAS;AAAA,MACT,WAAW;AAAA,MACX,WAAW,OAAO,EAAE,cAAc,WAAW,EAAE,YAAY;AAAA,MAC3D;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,WACJ,MACA,QAC4B;AAC5B,UAAM,OAAO,sBAAsB,MAAM,MAAM;AAC/C,UAAM,OAAO,KAAK,KAAK,IAAI;AAC3B,UAAM,MAAM,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC9C,UAAM,MAAM,GAAG,IAAI;AACnB,UAAM,UAAU,KAAK,GAAG,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AAAA,GAAM,MAAM;AACjE,UAAM,OAAO,KAAK,IAAI;AACtB,WAAO;AAAA,EACT;AACF;;;ACvIA,SAAS,kBAAkB;AAC3B,SAAS,cAAAC,aAAY,WAAW,cAAc,qBAAqB;AACnE,SAAS,eAAe;AACxB,SAAS,QAAAC,aAAY;;;ACDnB,cAAW;;;ADOb,IAAM,cAAc;AACpB,IAAM,eAAe;AASrB,IAAM,OAAiB,EAAE,UAAU;AAAC,EAAE;AAEtC,SAAS,gBAAyB;AAChC,QAAM,MAAM,QAAQ,IAAI;AACxB,MAAI,QAAQ,OAAO,QAAQ,OAAQ,QAAO;AAC1C,QAAM,IAAI,QAAQ,IAAI,mBAAmB,YAAY;AACrD,SAAO,MAAM,OAAO,MAAM,WAAW,MAAM,SAAS,MAAM;AAC5D;AAaO,SAAS,kBAA4B;AAC1C,MAAI,YAAY,WAAW,KAAK,cAAc,EAAG,QAAO;AAExD,MAAI;AACJ,MAAI,WAAW;AACf,MAAI;AACF,UAAM,MAAMC,MAAK,QAAQ,GAAG,WAAW,SAAS;AAChD,UAAM,SAASA,MAAK,KAAK,cAAc;AACvC,QAAIC,YAAW,MAAM,GAAG;AACtB,mBAAa,aAAa,QAAQ,MAAM,EAAE,KAAK,KAAK,WAAW;AAAA,IACjE,OAAO;AACL,mBAAa,WAAW;AACxB,gBAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAClC,oBAAc,QAAQ,UAAU;AAChC,iBAAW;AAAA,IACb;AAAA,EACF,QAAQ;AACN,iBAAa,WAAW;AAAA,EAC1B;AAEA,MAAI,UAAU;AACZ,YAAQ;AAAA,MACN;AAAA,IAGF;AAAA,EACF;AAEA,QAAM,aAAyB;AAAA,IAC7B;AAAA,IACA,aAAa,QAAQ;AAAA,IACrB,UAAU,QAAQ;AAAA,EACpB;AAEA,SAAO;AAAA,IACL,QAAQ,OAAO,aAAa,CAAC,GAAG;AAC9B,YAAM,OAAO,KAAK,UAAU;AAAA,QAC1B,SAAS;AAAA,QACT;AAAA,QACA,aAAa;AAAA,QACb,YAAY,EAAE,GAAG,YAAY,GAAG,YAAY,yBAAyB,MAAM;AAAA,MAC7E,CAAC;AAED,WAAK,MAAM,GAAG,YAAY,aAAa;AAAA,QACrC,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C;AAAA,MACF,CAAC,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA,IACnB;AAAA,EACF;AACF;;;AN9EA,SAAS,UAAU,MAA4B;AAC7C,MAAIC,QAAO,OAAO,QAAQ,IAAI,QAAQ,GAAI;AAC1C,MAAIC,oBAAmB,QAAQ,IAAI;AACnC,MAAIC,eAAc,QAAQ,IAAI;AAC9B,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,UAAM,MAAM,KAAK,CAAC;AAClB,QAAI,QAAQ,YAAY,QAAQ,MAAM;AACpC,YAAM,OAAO,KAAK,IAAI,CAAC;AACvB,UAAI,CAAC,KAAM,OAAM,IAAI,MAAM,qBAAqB,GAAG,EAAE;AACrD,YAAM,SAAS,OAAO,IAAI;AAC1B,UAAI,CAAC,OAAO,UAAU,MAAM,KAAK,UAAU,KAAK,SAAS,OAAO;AAC9D,cAAM,IAAI,MAAM,iBAAiB,IAAI,EAAE;AAAA,MACzC;AACA,MAAAF,QAAO;AACP;AAAA,IACF,WAAW,QAAQ,yBAAyB,QAAQ,MAAM;AACxD,YAAM,OAAO,KAAK,IAAI,CAAC;AACvB,UAAI,CAAC,KAAM,OAAM,IAAI,MAAM,qBAAqB,GAAG,EAAE;AACrD,MAAAC,oBAAmB;AACnB;AAAA,IACF,WAAW,QAAQ,oBAAoB,QAAQ,MAAM;AACnD,YAAM,OAAO,KAAK,IAAI,CAAC;AACvB,UAAI,CAAC,KAAM,OAAM,IAAI,MAAM,qBAAqB,GAAG,EAAE;AACrD,MAAAC,eAAc;AACd;AAAA,IACF,WAAW,QAAQ,YAAY,QAAQ,MAAM;AAC3C,cAAQ;AAAA,QACN;AAAA,MACF;AACA,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AACA,SAAO;AAAA,IACL,MAAAF;AAAA,IACA,kBAAkBG,SAAQF,qBAAoB,cAAc;AAAA,IAC5D,aAAaE,SAAQD,gBAAe,uBAAuB;AAAA,EAC7D;AACF;AAEA,IAAM,EAAE,MAAM,kBAAkB,YAAY,IAAI,UAAU,QAAQ,KAAK,MAAM,CAAC,CAAC;AAC/E,IAAM,YAAY,gBAAgB;AAClC,IAAM,MAAM,UAAU;AAAA,EACpB,QAAQ,IAAI,yBAAyB,gBAAgB;AAAA,EACrD,aAAa,IAAI,cAAc,WAAW;AAAA,EAC1C,QAAQ;AACV,CAAC;AAED,IAAI,OAAO,MAAM,MAAM;AACrB,UAAQ,IAAI,0CAA0C,IAAI,EAAE;AAC5D,UAAQ,IAAI,sBAAsB,gBAAgB,EAAE;AACpD,UAAQ,IAAI,kBAAkB,WAAW,EAAE;AAC3C,YAAU,QAAQ,eAAe;AACnC,CAAC;","names":["resolve","Router","Router","app","existsSync","join","join","existsSync","port","architecturePath","tecturePath","resolve"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,11 +3,19 @@ import { ArchitectureDataSource, LayoutStore, ManifestFile, DiagramFile, Diagram
|
|
|
3
3
|
export * from '@tecture/shared';
|
|
4
4
|
export { ArchitectureDataSource, DescriptionNotFoundError, DiagramNotFoundError, LayoutInvalidError, LayoutStore, NodeNotFoundError, SLUG_RE, buildArchitectureSummary, emptyLayout, findNode, isFiniteNumber, isValidLayoutEntry, normalizeLayoutUpdate } from '@tecture/shared';
|
|
5
5
|
|
|
6
|
+
type EventProps = Record<string, string | number | boolean>;
|
|
7
|
+
interface Reporter {
|
|
8
|
+
/** Send an anonymous usage event. No-op when telemetry is disabled. */
|
|
9
|
+
capture(event: string, properties?: EventProps): void;
|
|
10
|
+
}
|
|
11
|
+
|
|
6
12
|
type SourceResolver = (req: Request) => ArchitectureDataSource | Promise<ArchitectureDataSource>;
|
|
7
13
|
type LayoutStoreResolver = (req: Request) => LayoutStore | null | Promise<LayoutStore | null>;
|
|
8
14
|
interface ArchitectureRouterOptions {
|
|
9
15
|
resolveSource: SourceResolver;
|
|
10
16
|
resolveLayoutStore?: LayoutStoreResolver;
|
|
17
|
+
/** Optional anonymous usage reporter (standalone viewer only). */
|
|
18
|
+
report?: Reporter;
|
|
11
19
|
}
|
|
12
20
|
declare function createArchitectureRouter(opts: ArchitectureRouterOptions): Router;
|
|
13
21
|
declare function architectureErrorHandler(err: unknown, _req: Request, res: Response, next: NextFunction): void;
|
|
@@ -15,6 +23,8 @@ declare function architectureErrorHandler(err: unknown, _req: Request, res: Resp
|
|
|
15
23
|
interface CreateAppOptions {
|
|
16
24
|
source: ArchitectureDataSource | SourceResolver;
|
|
17
25
|
layoutStore?: LayoutStore | LayoutStoreResolver | null;
|
|
26
|
+
/** Optional anonymous usage reporter (standalone viewer only). */
|
|
27
|
+
report?: Reporter;
|
|
18
28
|
}
|
|
19
29
|
declare function createApp(options: CreateAppOptions): Express;
|
|
20
30
|
|
package/dist/index.js
CHANGED
|
@@ -188,7 +188,7 @@ function send400(res, body) {
|
|
|
188
188
|
}
|
|
189
189
|
function createArchitectureRouter(opts) {
|
|
190
190
|
const router = Router2();
|
|
191
|
-
const { resolveSource, resolveLayoutStore } = opts;
|
|
191
|
+
const { resolveSource, resolveLayoutStore, report } = opts;
|
|
192
192
|
router.get("/", async (req, res, next) => {
|
|
193
193
|
try {
|
|
194
194
|
const source = await resolveSource(req);
|
|
@@ -233,6 +233,10 @@ function createArchitectureRouter(opts) {
|
|
|
233
233
|
edges: diagram.edges ?? []
|
|
234
234
|
};
|
|
235
235
|
res.json(body);
|
|
236
|
+
report?.capture(
|
|
237
|
+
"diagram.viewed",
|
|
238
|
+
typeof diagram.level === "number" ? { level: diagram.level } : {}
|
|
239
|
+
);
|
|
236
240
|
} catch (err) {
|
|
237
241
|
if (err instanceof DiagramNotFoundError) {
|
|
238
242
|
return send404(res, { error: "diagram_not_found", slug: err.slug });
|
|
@@ -391,7 +395,8 @@ function createApp(options) {
|
|
|
391
395
|
"/api/architecture",
|
|
392
396
|
createArchitectureRouter({
|
|
393
397
|
resolveSource: toSourceResolver(options.source),
|
|
394
|
-
resolveLayoutStore: toLayoutStoreResolver(options.layoutStore)
|
|
398
|
+
resolveLayoutStore: toLayoutStoreResolver(options.layoutStore),
|
|
399
|
+
report: options.report
|
|
395
400
|
})
|
|
396
401
|
);
|
|
397
402
|
app.use("/api/architecture", architectureErrorHandler);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server.ts","../src/routes/health.ts","../src/routes/architecture.ts","../../shared/src/validators.ts","../../shared/src/deepDive.ts","../src/source/fs.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport express, { type Express, type Request } from \"express\";\nimport { healthRouter } from \"./routes/health.js\";\nimport {\n architectureErrorHandler,\n createArchitectureRouter,\n type LayoutStoreResolver,\n type SourceResolver,\n} from \"./routes/architecture.js\";\nimport type {\n ArchitectureDataSource,\n LayoutStore,\n} from \"./source/types.js\";\n\nexport interface CreateAppOptions {\n source: ArchitectureDataSource | SourceResolver;\n layoutStore?: LayoutStore | LayoutStoreResolver | null;\n}\n\nfunction toSourceResolver(\n value: ArchitectureDataSource | SourceResolver,\n): SourceResolver {\n if (typeof value === \"function\") return value as SourceResolver;\n return () => value;\n}\n\nfunction toLayoutStoreResolver(\n value: LayoutStore | LayoutStoreResolver | null | undefined,\n): LayoutStoreResolver | undefined {\n if (value == null) return undefined;\n if (typeof value === \"function\") return value as LayoutStoreResolver;\n return () => value;\n}\n\nexport function createApp(options: CreateAppOptions): Express {\n const app = express();\n\n app.use(express.json({ limit: \"256kb\" }));\n app.use(\"/api/health\", healthRouter);\n app.use(\n \"/api/architecture\",\n createArchitectureRouter({\n resolveSource: toSourceResolver(options.source),\n resolveLayoutStore: toLayoutStoreResolver(options.layoutStore),\n }),\n );\n app.use(\"/api/architecture\", architectureErrorHandler);\n\n const publicDir = fileURLToPath(new URL(\"./public\", import.meta.url));\n\n if (existsSync(publicDir)) {\n app.use(express.static(publicDir));\n app.get(\"*\", (req: Request, res, next) => {\n if (req.path.startsWith(\"/api/\")) return next();\n res.sendFile(fileURLToPath(new URL(\"./public/index.html\", import.meta.url)));\n });\n }\n\n return app;\n}\n","import { Router } from \"express\";\nimport type { ApiHealthResponse } from \"@tecture/shared\";\n\nexport const healthRouter: Router = Router();\n\nhealthRouter.get(\"/\", (_req, res) => {\n const body: ApiHealthResponse = {\n status: \"ok\",\n uptime: process.uptime(),\n timestamp: new Date().toISOString(),\n };\n res.json(body);\n});\n","import { Router, type Request, type Response, type NextFunction } from \"express\";\nimport type {\n ApiArchitectureError,\n ApiDiagram,\n ApiDiagramEdges,\n ApiDiagramLayout,\n ApiDiagramLayoutUpdate,\n ApiDiagramNodes,\n ApiNodeDetail,\n ManifestFile,\n} from \"@tecture/shared\";\nimport {\n type ArchitectureDataSource,\n DescriptionNotFoundError,\n DiagramNotFoundError,\n type LayoutStore,\n LayoutInvalidError,\n NodeNotFoundError,\n SLUG_RE,\n buildArchitectureSummary,\n emptyLayout,\n findNode,\n} from \"../source/types.js\";\n\nexport type SourceResolver = (\n req: Request,\n) => ArchitectureDataSource | Promise<ArchitectureDataSource>;\n\nexport type LayoutStoreResolver = (\n req: Request,\n) => LayoutStore | null | Promise<LayoutStore | null>;\n\nexport interface ArchitectureRouterOptions {\n resolveSource: SourceResolver;\n resolveLayoutStore?: LayoutStoreResolver;\n}\n\nfunction send501(res: Response, body: ApiArchitectureError): void {\n res.status(501).json(body);\n}\n\nfunction send404(res: Response, body: ApiArchitectureError): void {\n res.status(404).json(body);\n}\n\nfunction send400(res: Response, body: ApiArchitectureError): void {\n res.status(400).json(body);\n}\n\nexport function createArchitectureRouter(\n opts: ArchitectureRouterOptions,\n): Router {\n const router = Router();\n const { resolveSource, resolveLayoutStore } = opts;\n\n router.get(\"/\", async (req, res, next) => {\n try {\n const source = await resolveSource(req);\n const summary = await buildArchitectureSummary(source);\n res.json(summary);\n } catch (err) {\n next(err);\n }\n });\n\n router.get(\"/manifest\", async (req, res, next) => {\n try {\n const source = await resolveSource(req);\n const manifest: ManifestFile = await source.loadManifest();\n res.json(manifest);\n } catch (err) {\n next(err);\n }\n });\n\n router.get(\"/diagrams\", async (req, res, next) => {\n try {\n const source = await resolveSource(req);\n const summary = await buildArchitectureSummary(source);\n res.json(summary.diagrams);\n } catch (err) {\n next(err);\n }\n });\n\n router.get(\"/diagrams/:diagramId\", async (req, res, next) => {\n const { diagramId } = req.params;\n if (!SLUG_RE.test(diagramId)) {\n return send404(res, { error: \"diagram_not_found\", slug: diagramId });\n }\n try {\n const source = await resolveSource(req);\n const diagram = await source.loadDiagram(diagramId);\n const body: ApiDiagram = {\n slug: diagramId,\n name: diagram.name,\n level: diagram.level,\n meta: diagram.meta,\n nodes: diagram.nodes,\n edges: diagram.edges ?? [],\n };\n res.json(body);\n } catch (err) {\n if (err instanceof DiagramNotFoundError) {\n return send404(res, { error: \"diagram_not_found\", slug: err.slug });\n }\n next(err);\n }\n });\n\n router.get(\"/diagrams/:diagramId/nodes\", async (req, res, next) => {\n const { diagramId } = req.params;\n if (!SLUG_RE.test(diagramId)) {\n return send404(res, { error: \"diagram_not_found\", slug: diagramId });\n }\n try {\n const source = await resolveSource(req);\n const diagram = await source.loadDiagram(diagramId);\n const body: ApiDiagramNodes = { diagramId, nodes: diagram.nodes };\n res.json(body);\n } catch (err) {\n if (err instanceof DiagramNotFoundError) {\n return send404(res, { error: \"diagram_not_found\", slug: err.slug });\n }\n next(err);\n }\n });\n\n router.get(\"/diagrams/:diagramId/edges\", async (req, res, next) => {\n const { diagramId } = req.params;\n if (!SLUG_RE.test(diagramId)) {\n return send404(res, { error: \"diagram_not_found\", slug: diagramId });\n }\n try {\n const source = await resolveSource(req);\n const diagram = await source.loadDiagram(diagramId);\n const body: ApiDiagramEdges = { diagramId, edges: diagram.edges ?? [] };\n res.json(body);\n } catch (err) {\n if (err instanceof DiagramNotFoundError) {\n return send404(res, { error: \"diagram_not_found\", slug: err.slug });\n }\n next(err);\n }\n });\n\n router.get(\"/diagrams/:diagramId/layout\", async (req, res, next) => {\n const { diagramId } = req.params;\n if (!SLUG_RE.test(diagramId)) {\n return send404(res, { error: \"diagram_not_found\", slug: diagramId });\n }\n try {\n const source = await resolveSource(req);\n await source.loadDiagram(diagramId);\n const store = resolveLayoutStore ? await resolveLayoutStore(req) : null;\n const layout: ApiDiagramLayout = store\n ? await store.loadLayout(diagramId)\n : emptyLayout(diagramId);\n res.json(layout);\n } catch (err) {\n if (err instanceof DiagramNotFoundError) {\n return send404(res, { error: \"diagram_not_found\", slug: err.slug });\n }\n next(err);\n }\n });\n\n router.put(\"/diagrams/:diagramId/layout\", async (req, res, next) => {\n const { diagramId } = req.params;\n if (!SLUG_RE.test(diagramId)) {\n return send404(res, { error: \"diagram_not_found\", slug: diagramId });\n }\n try {\n const source = await resolveSource(req);\n await source.loadDiagram(diagramId);\n const store = resolveLayoutStore ? await resolveLayoutStore(req) : null;\n if (!store) {\n return send501(res, {\n error: \"layout_not_supported\",\n message: \"Layout persistence is not configured for this data source\",\n });\n }\n const update = req.body as ApiDiagramLayoutUpdate;\n const saved = await store.saveLayout(diagramId, update);\n res.json(saved);\n } catch (err) {\n if (err instanceof DiagramNotFoundError) {\n return send404(res, { error: \"diagram_not_found\", slug: err.slug });\n }\n if (err instanceof LayoutInvalidError) {\n return send400(res, { error: \"layout_invalid\", message: err.message });\n }\n next(err);\n }\n });\n\n router.get(\"/nodes/:nodeId\", async (req, res, next) => {\n const { nodeId } = req.params;\n if (!SLUG_RE.test(nodeId)) {\n return send404(res, { error: \"node_not_found\", id: nodeId });\n }\n try {\n const source = await resolveSource(req);\n const { node, diagramId } = await findNode(source, nodeId);\n const description = await source.loadDescription(nodeId);\n const body: ApiNodeDetail = { ...node, diagramId, description };\n res.json(body);\n } catch (err) {\n if (err instanceof NodeNotFoundError) {\n return send404(res, { error: \"node_not_found\", id: err.id });\n }\n if (err instanceof DescriptionNotFoundError) {\n return send404(res, { error: \"description_not_found\", id: err.id });\n }\n next(err);\n }\n });\n\n router.get(\"/nodes/:nodeId/description\", async (req, res, next) => {\n const { nodeId } = req.params;\n if (!SLUG_RE.test(nodeId)) {\n return send404(res, { error: \"description_not_found\", id: nodeId });\n }\n try {\n const source = await resolveSource(req);\n await findNode(source, nodeId);\n const markdown = await source.loadDescription(nodeId);\n res.type(\"text/markdown; charset=utf-8\").send(markdown);\n } catch (err) {\n if (err instanceof NodeNotFoundError) {\n return send404(res, { error: \"node_not_found\", id: err.id });\n }\n if (err instanceof DescriptionNotFoundError) {\n return send404(res, { error: \"description_not_found\", id: err.id });\n }\n next(err);\n }\n });\n\n return router;\n}\n\nexport function architectureErrorHandler(\n err: unknown,\n _req: Request,\n res: Response,\n next: NextFunction,\n): void {\n if (res.headersSent) return next(err);\n const message = err instanceof Error ? err.message : String(err);\n const body: ApiArchitectureError = {\n error: \"architecture_unreadable\",\n message,\n };\n res.status(500).json(body);\n}\n","import type {\n ApiArchitectureSummary,\n ApiDiagramLayoutUpdate,\n ApiDiagramSummary,\n ArchitectureNode,\n DiagramFile,\n DiagramLayoutFile,\n ManifestFile,\n NodeLayoutEntry,\n SourceHost,\n} from \"./index\";\n\nexport const SLUG_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/;\n\nexport class DiagramNotFoundError extends Error {\n constructor(public readonly slug: string) {\n super(`Diagram not found: ${slug}`);\n this.name = \"DiagramNotFoundError\";\n }\n}\n\nexport class NodeNotFoundError extends Error {\n constructor(public readonly id: string) {\n super(`Node not found: ${id}`);\n this.name = \"NodeNotFoundError\";\n }\n}\n\nexport class DescriptionNotFoundError extends Error {\n constructor(public readonly id: string) {\n super(`Description not found: ${id}`);\n this.name = \"DescriptionNotFoundError\";\n }\n}\n\nexport class LayoutInvalidError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"LayoutInvalidError\";\n }\n}\n\nexport interface ArchitectureDataSource {\n loadManifest(): Promise<ManifestFile>;\n loadDiagram(slug: string): Promise<DiagramFile>;\n loadDescription(nodeId: string): Promise<string>;\n}\n\nexport interface LayoutStore {\n loadLayout(slug: string): Promise<DiagramLayoutFile>;\n saveLayout(\n slug: string,\n update: ApiDiagramLayoutUpdate,\n ): Promise<DiagramLayoutFile>;\n}\n\nexport function isFiniteNumber(v: unknown): v is number {\n return typeof v === \"number\" && Number.isFinite(v);\n}\n\nexport function isValidLayoutEntry(v: unknown): v is NodeLayoutEntry {\n if (!v || typeof v !== \"object\") return false;\n const e = v as Record<string, unknown>;\n if (!isFiniteNumber(e.x) || !isFiniteNumber(e.y)) return false;\n if (\"width\" in e && e.width !== undefined && !isFiniteNumber(e.width)) return false;\n if (\"height\" in e && e.height !== undefined && !isFiniteNumber(e.height)) return false;\n return true;\n}\n\nexport function emptyLayout(slug: string): DiagramLayoutFile {\n return { version: 1, diagramId: slug, updatedAt: \"\", nodes: {} };\n}\n\nexport function normalizeLayoutUpdate(\n slug: string,\n update: ApiDiagramLayoutUpdate,\n): DiagramLayoutFile {\n if (!SLUG_RE.test(slug)) {\n throw new LayoutInvalidError(`invalid diagram slug: ${slug}`);\n }\n if (\n !update ||\n typeof update !== \"object\" ||\n !update.nodes ||\n typeof update.nodes !== \"object\"\n ) {\n throw new LayoutInvalidError(\n \"body must be { nodes: Record<string, { x, y, width?, height? }> }\",\n );\n }\n const nodes: Record<string, NodeLayoutEntry> = {};\n for (const [id, entry] of Object.entries(update.nodes)) {\n if (!SLUG_RE.test(id)) {\n throw new LayoutInvalidError(`invalid node id: ${id}`);\n }\n if (!isValidLayoutEntry(entry)) {\n throw new LayoutInvalidError(`invalid layout entry for ${id}`);\n }\n const out: NodeLayoutEntry = { x: entry.x, y: entry.y };\n if (isFiniteNumber(entry.width)) out.width = entry.width;\n if (isFiniteNumber(entry.height)) out.height = entry.height;\n nodes[id] = out;\n }\n return {\n version: 1,\n diagramId: slug,\n updatedAt: new Date().toISOString(),\n nodes,\n };\n}\n\nexport async function buildArchitectureSummary(\n source: ArchitectureDataSource,\n): Promise<ApiArchitectureSummary> {\n const manifest = await source.loadManifest();\n const diagrams = await Promise.all(\n manifest.diagrams.map(async (slug): Promise<ApiDiagramSummary> => {\n const diagram = await source.loadDiagram(slug);\n return {\n slug,\n name: diagram.name,\n level: diagram.level,\n nodeCount: diagram.nodes?.length ?? 0,\n edgeCount: diagram.edges?.length ?? 0,\n };\n }),\n );\n return {\n name: manifest.name,\n description: manifest.description,\n source: manifest.source,\n sourceHost: manifest.sourceHost,\n topDiagram: manifest.topDiagram,\n diagrams,\n };\n}\n\nconst HOST_DOMAINS: Array<{ host: SourceHost; match: RegExp }> = [\n { host: \"github\", match: /(^|\\.)github\\.com$/i },\n { host: \"gitlab\", match: /(^|\\.)gitlab\\.com$/i },\n { host: \"bitbucket\", match: /(^|\\.)bitbucket\\.org$/i },\n];\n\nfunction inferSourceHost(source: string): SourceHost | undefined {\n // Extract the hostname without relying on the URL global (kept lib-agnostic for shared):\n // scheme://[userinfo@]host[:port]/...\n const hostname = /^[a-z][a-z0-9+.-]*:\\/\\/(?:[^@/]*@)?([^:/?#]+)/i.exec(source)?.[1];\n if (!hostname) return undefined;\n return HOST_DOMAINS.find(({ match }) => match.test(hostname))?.host;\n}\n\n/**\n * Build a web URL that views `path` (a repo-root-relative file or directory, trailing \"/\" = dir)\n * on the source repo host. `HEAD` resolves to the default branch so links stay current. Falls back\n * to the repo root when the host is unknown, and returns undefined when there is no source.\n */\nexport function buildSourceUrl(\n source: string | undefined,\n host: SourceHost | undefined,\n path: string,\n): string | undefined {\n if (!source) return undefined;\n const base = source.replace(/\\/+$/, \"\").replace(/\\.git$/i, \"\");\n const resolvedHost = host ?? inferSourceHost(base);\n const isDir = path.endsWith(\"/\");\n const encoded = path\n .replace(/^\\/+/, \"\")\n .replace(/\\/+$/, \"\")\n .split(\"/\")\n .map(encodeURIComponent)\n .join(\"/\");\n if (!encoded) return base;\n switch (resolvedHost) {\n case \"github\":\n return `${base}/${isDir ? \"tree\" : \"blob\"}/HEAD/${encoded}`;\n case \"gitlab\":\n return `${base}/-/${isDir ? \"tree\" : \"blob\"}/HEAD/${encoded}`;\n case \"bitbucket\":\n return `${base}/src/HEAD/${encoded}`;\n default:\n return base;\n }\n}\n\nexport async function findNode(\n source: ArchitectureDataSource,\n nodeId: string,\n): Promise<{ node: ArchitectureNode; diagramId: string }> {\n if (!SLUG_RE.test(nodeId)) throw new NodeNotFoundError(nodeId);\n const manifest = await source.loadManifest();\n for (const slug of manifest.diagrams) {\n let diagram: DiagramFile;\n try {\n diagram = await source.loadDiagram(slug);\n } catch (err) {\n if (err instanceof DiagramNotFoundError) continue;\n throw err;\n }\n const match = diagram.nodes?.find((n) => n.id === nodeId);\n if (match) return { node: match, diagramId: slug };\n }\n throw new NodeNotFoundError(nodeId);\n}\n","import type { ArchitectureNode, NodeMetaType } from \"./index\";\n\n/** Node types that map to first-party code worth a deep-dive. */\nconst CODE_NODE_TYPES: ReadonlySet<NodeMetaType> = new Set([\n \"service\",\n \"frontend\",\n \"gateway\",\n]);\n\n/**\n * Whether a node is worth deep-diving — i.e. first-party code the team owns.\n * True when the node carries a repo `path`, or its type is a code container\n * (service / frontend / gateway). People, external SaaS, and managed infra\n * (database / cache / queue / storage) have nothing in the repo to investigate.\n */\nexport function isDeepDivable(\n node: Pick<ArchitectureNode, \"path\" | \"meta\">,\n): boolean {\n if (node.path) return true;\n const type = node.meta?.type;\n return type !== undefined && CODE_NODE_TYPES.has(type);\n}\n\n/**\n * Build the prompt a user copies into a coding agent to enrich one component's\n * description. It invokes the skill's deep-dive via its slash command, keyed on\n * the node `id` (unambiguous) — it deliberately does not restate how to deep-dive,\n * since the architecture-docs skill already owns that (which files to read,\n * dependencies to trace, the target description file, prose-only, etc.).\n */\nexport function buildDeepDivePrompt(\n node: Pick<ArchitectureNode, \"id\">,\n): string {\n return `/architecture-docs deep-dive ${node.id}`;\n}\n","import { mkdir, readFile, rename, writeFile } from \"node:fs/promises\";\nimport { dirname, join, resolve, sep } from \"node:path\";\nimport type {\n ApiDiagramLayoutUpdate,\n DiagramFile,\n DiagramLayoutFile,\n ManifestFile,\n NodeLayoutEntry,\n} from \"@tecture/shared\";\nimport {\n type ArchitectureDataSource,\n DescriptionNotFoundError,\n DiagramNotFoundError,\n type LayoutStore,\n SLUG_RE,\n emptyLayout,\n isFiniteNumber,\n isValidLayoutEntry,\n normalizeLayoutUpdate,\n} from \"./types.js\";\n\nexport function safeJoin(root: string, ...segments: string[]): string {\n const absRoot = resolve(root);\n const candidate = resolve(join(absRoot, ...segments));\n const guard = absRoot.endsWith(sep) ? absRoot : absRoot + sep;\n if (candidate !== absRoot && !candidate.startsWith(guard)) {\n throw new Error(`Path escapes architecture root: ${candidate}`);\n }\n return candidate;\n}\n\nfunction isNodeErrnoError(err: unknown): err is NodeJS.ErrnoException {\n return err instanceof Error && \"code\" in err;\n}\n\nasync function loadJsonFile<T>(path: string): Promise<T> {\n const raw = await readFile(path, \"utf8\");\n return JSON.parse(raw) as T;\n}\n\nexport class FsArchitectureDataSource implements ArchitectureDataSource {\n constructor(private readonly root: string) {}\n\n async loadManifest(): Promise<ManifestFile> {\n const path = safeJoin(this.root, \"manifest.json\");\n return loadJsonFile<ManifestFile>(path);\n }\n\n async loadDiagram(slug: string): Promise<DiagramFile> {\n if (!SLUG_RE.test(slug)) throw new DiagramNotFoundError(slug);\n const path = safeJoin(this.root, \"diagrams\", `${slug}.json`);\n try {\n return await loadJsonFile<DiagramFile>(path);\n } catch (err) {\n if (isNodeErrnoError(err) && err.code === \"ENOENT\") {\n throw new DiagramNotFoundError(slug);\n }\n throw err;\n }\n }\n\n async loadDescription(nodeId: string): Promise<string> {\n if (!SLUG_RE.test(nodeId)) throw new DescriptionNotFoundError(nodeId);\n const path = safeJoin(this.root, \"descriptions\", `${nodeId}.md`);\n try {\n return await readFile(path, \"utf8\");\n } catch (err) {\n if (isNodeErrnoError(err) && err.code === \"ENOENT\") {\n throw new DescriptionNotFoundError(nodeId);\n }\n throw err;\n }\n }\n}\n\nexport class FsLayoutStore implements LayoutStore {\n constructor(private readonly tectureRoot: string) {}\n\n private path(slug: string): string {\n return safeJoin(this.tectureRoot, \"layouts\", `${slug}.json`);\n }\n\n async loadLayout(slug: string): Promise<DiagramLayoutFile> {\n if (!SLUG_RE.test(slug)) return emptyLayout(slug);\n const path = this.path(slug);\n let raw: string;\n try {\n raw = await readFile(path, \"utf8\");\n } catch (err) {\n if (isNodeErrnoError(err) && err.code === \"ENOENT\") return emptyLayout(slug);\n console.warn(`[tecture] failed to read layout ${path}: ${String(err)}`);\n return emptyLayout(slug);\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n console.warn(`[tecture] malformed layout JSON at ${path}: ${String(err)}`);\n return emptyLayout(slug);\n }\n\n if (!parsed || typeof parsed !== \"object\") return emptyLayout(slug);\n const p = parsed as Record<string, unknown>;\n const nodes: Record<string, NodeLayoutEntry> = {};\n if (p.nodes && typeof p.nodes === \"object\") {\n for (const [id, entry] of Object.entries(p.nodes as Record<string, unknown>)) {\n if (!SLUG_RE.test(id)) continue;\n if (!isValidLayoutEntry(entry)) continue;\n const out: NodeLayoutEntry = { x: entry.x, y: entry.y };\n if (isFiniteNumber(entry.width)) out.width = entry.width;\n if (isFiniteNumber(entry.height)) out.height = entry.height;\n nodes[id] = out;\n }\n }\n return {\n version: 1,\n diagramId: slug,\n updatedAt: typeof p.updatedAt === \"string\" ? p.updatedAt : \"\",\n nodes,\n };\n }\n\n async saveLayout(\n slug: string,\n update: ApiDiagramLayoutUpdate,\n ): Promise<DiagramLayoutFile> {\n const file = normalizeLayoutUpdate(slug, update);\n const path = this.path(slug);\n await mkdir(dirname(path), { recursive: true });\n const tmp = `${path}.tmp`;\n await writeFile(tmp, `${JSON.stringify(file, null, 2)}\\n`, \"utf8\");\n await rename(tmp, path);\n return file;\n }\n}\n"],"mappings":"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","names":["Router","Router"]}
|
|
1
|
+
{"version":3,"sources":["../src/server.ts","../src/routes/health.ts","../src/routes/architecture.ts","../../shared/src/validators.ts","../../shared/src/deepDive.ts","../src/source/fs.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport express, { type Express, type Request } from \"express\";\nimport { healthRouter } from \"./routes/health.js\";\nimport {\n architectureErrorHandler,\n createArchitectureRouter,\n type LayoutStoreResolver,\n type SourceResolver,\n} from \"./routes/architecture.js\";\nimport type {\n ArchitectureDataSource,\n LayoutStore,\n} from \"./source/types.js\";\nimport type { Reporter } from \"./telemetry.js\";\n\nexport interface CreateAppOptions {\n source: ArchitectureDataSource | SourceResolver;\n layoutStore?: LayoutStore | LayoutStoreResolver | null;\n /** Optional anonymous usage reporter (standalone viewer only). */\n report?: Reporter;\n}\n\nfunction toSourceResolver(\n value: ArchitectureDataSource | SourceResolver,\n): SourceResolver {\n if (typeof value === \"function\") return value as SourceResolver;\n return () => value;\n}\n\nfunction toLayoutStoreResolver(\n value: LayoutStore | LayoutStoreResolver | null | undefined,\n): LayoutStoreResolver | undefined {\n if (value == null) return undefined;\n if (typeof value === \"function\") return value as LayoutStoreResolver;\n return () => value;\n}\n\nexport function createApp(options: CreateAppOptions): Express {\n const app = express();\n\n app.use(express.json({ limit: \"256kb\" }));\n app.use(\"/api/health\", healthRouter);\n app.use(\n \"/api/architecture\",\n createArchitectureRouter({\n resolveSource: toSourceResolver(options.source),\n resolveLayoutStore: toLayoutStoreResolver(options.layoutStore),\n report: options.report,\n }),\n );\n app.use(\"/api/architecture\", architectureErrorHandler);\n\n const publicDir = fileURLToPath(new URL(\"./public\", import.meta.url));\n\n if (existsSync(publicDir)) {\n app.use(express.static(publicDir));\n app.get(\"*\", (req: Request, res, next) => {\n if (req.path.startsWith(\"/api/\")) return next();\n res.sendFile(fileURLToPath(new URL(\"./public/index.html\", import.meta.url)));\n });\n }\n\n return app;\n}\n","import { Router } from \"express\";\nimport type { ApiHealthResponse } from \"@tecture/shared\";\n\nexport const healthRouter: Router = Router();\n\nhealthRouter.get(\"/\", (_req, res) => {\n const body: ApiHealthResponse = {\n status: \"ok\",\n uptime: process.uptime(),\n timestamp: new Date().toISOString(),\n };\n res.json(body);\n});\n","import { Router, type Request, type Response, type NextFunction } from \"express\";\nimport type {\n ApiArchitectureError,\n ApiDiagram,\n ApiDiagramEdges,\n ApiDiagramLayout,\n ApiDiagramLayoutUpdate,\n ApiDiagramNodes,\n ApiNodeDetail,\n ManifestFile,\n} from \"@tecture/shared\";\nimport {\n type ArchitectureDataSource,\n DescriptionNotFoundError,\n DiagramNotFoundError,\n type LayoutStore,\n LayoutInvalidError,\n NodeNotFoundError,\n SLUG_RE,\n buildArchitectureSummary,\n emptyLayout,\n findNode,\n} from \"../source/types.js\";\nimport type { Reporter } from \"../telemetry.js\";\n\nexport type SourceResolver = (\n req: Request,\n) => ArchitectureDataSource | Promise<ArchitectureDataSource>;\n\nexport type LayoutStoreResolver = (\n req: Request,\n) => LayoutStore | null | Promise<LayoutStore | null>;\n\nexport interface ArchitectureRouterOptions {\n resolveSource: SourceResolver;\n resolveLayoutStore?: LayoutStoreResolver;\n /** Optional anonymous usage reporter (standalone viewer only). */\n report?: Reporter;\n}\n\nfunction send501(res: Response, body: ApiArchitectureError): void {\n res.status(501).json(body);\n}\n\nfunction send404(res: Response, body: ApiArchitectureError): void {\n res.status(404).json(body);\n}\n\nfunction send400(res: Response, body: ApiArchitectureError): void {\n res.status(400).json(body);\n}\n\nexport function createArchitectureRouter(\n opts: ArchitectureRouterOptions,\n): Router {\n const router = Router();\n const { resolveSource, resolveLayoutStore, report } = opts;\n\n router.get(\"/\", async (req, res, next) => {\n try {\n const source = await resolveSource(req);\n const summary = await buildArchitectureSummary(source);\n res.json(summary);\n } catch (err) {\n next(err);\n }\n });\n\n router.get(\"/manifest\", async (req, res, next) => {\n try {\n const source = await resolveSource(req);\n const manifest: ManifestFile = await source.loadManifest();\n res.json(manifest);\n } catch (err) {\n next(err);\n }\n });\n\n router.get(\"/diagrams\", async (req, res, next) => {\n try {\n const source = await resolveSource(req);\n const summary = await buildArchitectureSummary(source);\n res.json(summary.diagrams);\n } catch (err) {\n next(err);\n }\n });\n\n router.get(\"/diagrams/:diagramId\", async (req, res, next) => {\n const { diagramId } = req.params;\n if (!SLUG_RE.test(diagramId)) {\n return send404(res, { error: \"diagram_not_found\", slug: diagramId });\n }\n try {\n const source = await resolveSource(req);\n const diagram = await source.loadDiagram(diagramId);\n const body: ApiDiagram = {\n slug: diagramId,\n name: diagram.name,\n level: diagram.level,\n meta: diagram.meta,\n nodes: diagram.nodes,\n edges: diagram.edges ?? [],\n };\n res.json(body);\n // Anonymous usage signal: C4 level only, never the slug/name/content.\n report?.capture(\n \"diagram.viewed\",\n typeof diagram.level === \"number\" ? { level: diagram.level } : {},\n );\n } catch (err) {\n if (err instanceof DiagramNotFoundError) {\n return send404(res, { error: \"diagram_not_found\", slug: err.slug });\n }\n next(err);\n }\n });\n\n router.get(\"/diagrams/:diagramId/nodes\", async (req, res, next) => {\n const { diagramId } = req.params;\n if (!SLUG_RE.test(diagramId)) {\n return send404(res, { error: \"diagram_not_found\", slug: diagramId });\n }\n try {\n const source = await resolveSource(req);\n const diagram = await source.loadDiagram(diagramId);\n const body: ApiDiagramNodes = { diagramId, nodes: diagram.nodes };\n res.json(body);\n } catch (err) {\n if (err instanceof DiagramNotFoundError) {\n return send404(res, { error: \"diagram_not_found\", slug: err.slug });\n }\n next(err);\n }\n });\n\n router.get(\"/diagrams/:diagramId/edges\", async (req, res, next) => {\n const { diagramId } = req.params;\n if (!SLUG_RE.test(diagramId)) {\n return send404(res, { error: \"diagram_not_found\", slug: diagramId });\n }\n try {\n const source = await resolveSource(req);\n const diagram = await source.loadDiagram(diagramId);\n const body: ApiDiagramEdges = { diagramId, edges: diagram.edges ?? [] };\n res.json(body);\n } catch (err) {\n if (err instanceof DiagramNotFoundError) {\n return send404(res, { error: \"diagram_not_found\", slug: err.slug });\n }\n next(err);\n }\n });\n\n router.get(\"/diagrams/:diagramId/layout\", async (req, res, next) => {\n const { diagramId } = req.params;\n if (!SLUG_RE.test(diagramId)) {\n return send404(res, { error: \"diagram_not_found\", slug: diagramId });\n }\n try {\n const source = await resolveSource(req);\n await source.loadDiagram(diagramId);\n const store = resolveLayoutStore ? await resolveLayoutStore(req) : null;\n const layout: ApiDiagramLayout = store\n ? await store.loadLayout(diagramId)\n : emptyLayout(diagramId);\n res.json(layout);\n } catch (err) {\n if (err instanceof DiagramNotFoundError) {\n return send404(res, { error: \"diagram_not_found\", slug: err.slug });\n }\n next(err);\n }\n });\n\n router.put(\"/diagrams/:diagramId/layout\", async (req, res, next) => {\n const { diagramId } = req.params;\n if (!SLUG_RE.test(diagramId)) {\n return send404(res, { error: \"diagram_not_found\", slug: diagramId });\n }\n try {\n const source = await resolveSource(req);\n await source.loadDiagram(diagramId);\n const store = resolveLayoutStore ? await resolveLayoutStore(req) : null;\n if (!store) {\n return send501(res, {\n error: \"layout_not_supported\",\n message: \"Layout persistence is not configured for this data source\",\n });\n }\n const update = req.body as ApiDiagramLayoutUpdate;\n const saved = await store.saveLayout(diagramId, update);\n res.json(saved);\n } catch (err) {\n if (err instanceof DiagramNotFoundError) {\n return send404(res, { error: \"diagram_not_found\", slug: err.slug });\n }\n if (err instanceof LayoutInvalidError) {\n return send400(res, { error: \"layout_invalid\", message: err.message });\n }\n next(err);\n }\n });\n\n router.get(\"/nodes/:nodeId\", async (req, res, next) => {\n const { nodeId } = req.params;\n if (!SLUG_RE.test(nodeId)) {\n return send404(res, { error: \"node_not_found\", id: nodeId });\n }\n try {\n const source = await resolveSource(req);\n const { node, diagramId } = await findNode(source, nodeId);\n const description = await source.loadDescription(nodeId);\n const body: ApiNodeDetail = { ...node, diagramId, description };\n res.json(body);\n } catch (err) {\n if (err instanceof NodeNotFoundError) {\n return send404(res, { error: \"node_not_found\", id: err.id });\n }\n if (err instanceof DescriptionNotFoundError) {\n return send404(res, { error: \"description_not_found\", id: err.id });\n }\n next(err);\n }\n });\n\n router.get(\"/nodes/:nodeId/description\", async (req, res, next) => {\n const { nodeId } = req.params;\n if (!SLUG_RE.test(nodeId)) {\n return send404(res, { error: \"description_not_found\", id: nodeId });\n }\n try {\n const source = await resolveSource(req);\n await findNode(source, nodeId);\n const markdown = await source.loadDescription(nodeId);\n res.type(\"text/markdown; charset=utf-8\").send(markdown);\n } catch (err) {\n if (err instanceof NodeNotFoundError) {\n return send404(res, { error: \"node_not_found\", id: err.id });\n }\n if (err instanceof DescriptionNotFoundError) {\n return send404(res, { error: \"description_not_found\", id: err.id });\n }\n next(err);\n }\n });\n\n return router;\n}\n\nexport function architectureErrorHandler(\n err: unknown,\n _req: Request,\n res: Response,\n next: NextFunction,\n): void {\n if (res.headersSent) return next(err);\n const message = err instanceof Error ? err.message : String(err);\n const body: ApiArchitectureError = {\n error: \"architecture_unreadable\",\n message,\n };\n res.status(500).json(body);\n}\n","import type {\n ApiArchitectureSummary,\n ApiDiagramLayoutUpdate,\n ApiDiagramSummary,\n ArchitectureNode,\n DiagramFile,\n DiagramLayoutFile,\n ManifestFile,\n NodeLayoutEntry,\n SourceHost,\n} from \"./index\";\n\nexport const SLUG_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/;\n\nexport class DiagramNotFoundError extends Error {\n constructor(public readonly slug: string) {\n super(`Diagram not found: ${slug}`);\n this.name = \"DiagramNotFoundError\";\n }\n}\n\nexport class NodeNotFoundError extends Error {\n constructor(public readonly id: string) {\n super(`Node not found: ${id}`);\n this.name = \"NodeNotFoundError\";\n }\n}\n\nexport class DescriptionNotFoundError extends Error {\n constructor(public readonly id: string) {\n super(`Description not found: ${id}`);\n this.name = \"DescriptionNotFoundError\";\n }\n}\n\nexport class LayoutInvalidError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"LayoutInvalidError\";\n }\n}\n\nexport interface ArchitectureDataSource {\n loadManifest(): Promise<ManifestFile>;\n loadDiagram(slug: string): Promise<DiagramFile>;\n loadDescription(nodeId: string): Promise<string>;\n}\n\nexport interface LayoutStore {\n loadLayout(slug: string): Promise<DiagramLayoutFile>;\n saveLayout(\n slug: string,\n update: ApiDiagramLayoutUpdate,\n ): Promise<DiagramLayoutFile>;\n}\n\nexport function isFiniteNumber(v: unknown): v is number {\n return typeof v === \"number\" && Number.isFinite(v);\n}\n\nexport function isValidLayoutEntry(v: unknown): v is NodeLayoutEntry {\n if (!v || typeof v !== \"object\") return false;\n const e = v as Record<string, unknown>;\n if (!isFiniteNumber(e.x) || !isFiniteNumber(e.y)) return false;\n if (\"width\" in e && e.width !== undefined && !isFiniteNumber(e.width)) return false;\n if (\"height\" in e && e.height !== undefined && !isFiniteNumber(e.height)) return false;\n return true;\n}\n\nexport function emptyLayout(slug: string): DiagramLayoutFile {\n return { version: 1, diagramId: slug, updatedAt: \"\", nodes: {} };\n}\n\nexport function normalizeLayoutUpdate(\n slug: string,\n update: ApiDiagramLayoutUpdate,\n): DiagramLayoutFile {\n if (!SLUG_RE.test(slug)) {\n throw new LayoutInvalidError(`invalid diagram slug: ${slug}`);\n }\n if (\n !update ||\n typeof update !== \"object\" ||\n !update.nodes ||\n typeof update.nodes !== \"object\"\n ) {\n throw new LayoutInvalidError(\n \"body must be { nodes: Record<string, { x, y, width?, height? }> }\",\n );\n }\n const nodes: Record<string, NodeLayoutEntry> = {};\n for (const [id, entry] of Object.entries(update.nodes)) {\n if (!SLUG_RE.test(id)) {\n throw new LayoutInvalidError(`invalid node id: ${id}`);\n }\n if (!isValidLayoutEntry(entry)) {\n throw new LayoutInvalidError(`invalid layout entry for ${id}`);\n }\n const out: NodeLayoutEntry = { x: entry.x, y: entry.y };\n if (isFiniteNumber(entry.width)) out.width = entry.width;\n if (isFiniteNumber(entry.height)) out.height = entry.height;\n nodes[id] = out;\n }\n return {\n version: 1,\n diagramId: slug,\n updatedAt: new Date().toISOString(),\n nodes,\n };\n}\n\nexport async function buildArchitectureSummary(\n source: ArchitectureDataSource,\n): Promise<ApiArchitectureSummary> {\n const manifest = await source.loadManifest();\n const diagrams = await Promise.all(\n manifest.diagrams.map(async (slug): Promise<ApiDiagramSummary> => {\n const diagram = await source.loadDiagram(slug);\n return {\n slug,\n name: diagram.name,\n level: diagram.level,\n nodeCount: diagram.nodes?.length ?? 0,\n edgeCount: diagram.edges?.length ?? 0,\n };\n }),\n );\n return {\n name: manifest.name,\n description: manifest.description,\n source: manifest.source,\n sourceHost: manifest.sourceHost,\n topDiagram: manifest.topDiagram,\n diagrams,\n };\n}\n\nconst HOST_DOMAINS: Array<{ host: SourceHost; match: RegExp }> = [\n { host: \"github\", match: /(^|\\.)github\\.com$/i },\n { host: \"gitlab\", match: /(^|\\.)gitlab\\.com$/i },\n { host: \"bitbucket\", match: /(^|\\.)bitbucket\\.org$/i },\n];\n\nfunction inferSourceHost(source: string): SourceHost | undefined {\n // Extract the hostname without relying on the URL global (kept lib-agnostic for shared):\n // scheme://[userinfo@]host[:port]/...\n const hostname = /^[a-z][a-z0-9+.-]*:\\/\\/(?:[^@/]*@)?([^:/?#]+)/i.exec(source)?.[1];\n if (!hostname) return undefined;\n return HOST_DOMAINS.find(({ match }) => match.test(hostname))?.host;\n}\n\n/**\n * Build a web URL that views `path` (a repo-root-relative file or directory, trailing \"/\" = dir)\n * on the source repo host. `HEAD` resolves to the default branch so links stay current. Falls back\n * to the repo root when the host is unknown, and returns undefined when there is no source.\n */\nexport function buildSourceUrl(\n source: string | undefined,\n host: SourceHost | undefined,\n path: string,\n): string | undefined {\n if (!source) return undefined;\n const base = source.replace(/\\/+$/, \"\").replace(/\\.git$/i, \"\");\n const resolvedHost = host ?? inferSourceHost(base);\n const isDir = path.endsWith(\"/\");\n const encoded = path\n .replace(/^\\/+/, \"\")\n .replace(/\\/+$/, \"\")\n .split(\"/\")\n .map(encodeURIComponent)\n .join(\"/\");\n if (!encoded) return base;\n switch (resolvedHost) {\n case \"github\":\n return `${base}/${isDir ? \"tree\" : \"blob\"}/HEAD/${encoded}`;\n case \"gitlab\":\n return `${base}/-/${isDir ? \"tree\" : \"blob\"}/HEAD/${encoded}`;\n case \"bitbucket\":\n return `${base}/src/HEAD/${encoded}`;\n default:\n return base;\n }\n}\n\nexport async function findNode(\n source: ArchitectureDataSource,\n nodeId: string,\n): Promise<{ node: ArchitectureNode; diagramId: string }> {\n if (!SLUG_RE.test(nodeId)) throw new NodeNotFoundError(nodeId);\n const manifest = await source.loadManifest();\n for (const slug of manifest.diagrams) {\n let diagram: DiagramFile;\n try {\n diagram = await source.loadDiagram(slug);\n } catch (err) {\n if (err instanceof DiagramNotFoundError) continue;\n throw err;\n }\n const match = diagram.nodes?.find((n) => n.id === nodeId);\n if (match) return { node: match, diagramId: slug };\n }\n throw new NodeNotFoundError(nodeId);\n}\n","import type { ArchitectureNode, NodeMetaType } from \"./index\";\n\n/** Node types that map to first-party code worth a deep-dive. */\nconst CODE_NODE_TYPES: ReadonlySet<NodeMetaType> = new Set([\n \"service\",\n \"frontend\",\n \"gateway\",\n]);\n\n/**\n * Whether a node is worth deep-diving — i.e. first-party code the team owns.\n * True when the node carries a repo `path`, or its type is a code container\n * (service / frontend / gateway). People, external SaaS, and managed infra\n * (database / cache / queue / storage) have nothing in the repo to investigate.\n */\nexport function isDeepDivable(\n node: Pick<ArchitectureNode, \"path\" | \"meta\">,\n): boolean {\n if (node.path) return true;\n const type = node.meta?.type;\n return type !== undefined && CODE_NODE_TYPES.has(type);\n}\n\n/**\n * Build the prompt a user copies into a coding agent to enrich one component's\n * description. It invokes the skill's deep-dive via its slash command, keyed on\n * the node `id` (unambiguous) — it deliberately does not restate how to deep-dive,\n * since the architecture-docs skill already owns that (which files to read,\n * dependencies to trace, the target description file, prose-only, etc.).\n */\nexport function buildDeepDivePrompt(\n node: Pick<ArchitectureNode, \"id\">,\n): string {\n return `/architecture-docs deep-dive ${node.id}`;\n}\n","import { mkdir, readFile, rename, writeFile } from \"node:fs/promises\";\nimport { dirname, join, resolve, sep } from \"node:path\";\nimport type {\n ApiDiagramLayoutUpdate,\n DiagramFile,\n DiagramLayoutFile,\n ManifestFile,\n NodeLayoutEntry,\n} from \"@tecture/shared\";\nimport {\n type ArchitectureDataSource,\n DescriptionNotFoundError,\n DiagramNotFoundError,\n type LayoutStore,\n SLUG_RE,\n emptyLayout,\n isFiniteNumber,\n isValidLayoutEntry,\n normalizeLayoutUpdate,\n} from \"./types.js\";\n\nexport function safeJoin(root: string, ...segments: string[]): string {\n const absRoot = resolve(root);\n const candidate = resolve(join(absRoot, ...segments));\n const guard = absRoot.endsWith(sep) ? absRoot : absRoot + sep;\n if (candidate !== absRoot && !candidate.startsWith(guard)) {\n throw new Error(`Path escapes architecture root: ${candidate}`);\n }\n return candidate;\n}\n\nfunction isNodeErrnoError(err: unknown): err is NodeJS.ErrnoException {\n return err instanceof Error && \"code\" in err;\n}\n\nasync function loadJsonFile<T>(path: string): Promise<T> {\n const raw = await readFile(path, \"utf8\");\n return JSON.parse(raw) as T;\n}\n\nexport class FsArchitectureDataSource implements ArchitectureDataSource {\n constructor(private readonly root: string) {}\n\n async loadManifest(): Promise<ManifestFile> {\n const path = safeJoin(this.root, \"manifest.json\");\n return loadJsonFile<ManifestFile>(path);\n }\n\n async loadDiagram(slug: string): Promise<DiagramFile> {\n if (!SLUG_RE.test(slug)) throw new DiagramNotFoundError(slug);\n const path = safeJoin(this.root, \"diagrams\", `${slug}.json`);\n try {\n return await loadJsonFile<DiagramFile>(path);\n } catch (err) {\n if (isNodeErrnoError(err) && err.code === \"ENOENT\") {\n throw new DiagramNotFoundError(slug);\n }\n throw err;\n }\n }\n\n async loadDescription(nodeId: string): Promise<string> {\n if (!SLUG_RE.test(nodeId)) throw new DescriptionNotFoundError(nodeId);\n const path = safeJoin(this.root, \"descriptions\", `${nodeId}.md`);\n try {\n return await readFile(path, \"utf8\");\n } catch (err) {\n if (isNodeErrnoError(err) && err.code === \"ENOENT\") {\n throw new DescriptionNotFoundError(nodeId);\n }\n throw err;\n }\n }\n}\n\nexport class FsLayoutStore implements LayoutStore {\n constructor(private readonly tectureRoot: string) {}\n\n private path(slug: string): string {\n return safeJoin(this.tectureRoot, \"layouts\", `${slug}.json`);\n }\n\n async loadLayout(slug: string): Promise<DiagramLayoutFile> {\n if (!SLUG_RE.test(slug)) return emptyLayout(slug);\n const path = this.path(slug);\n let raw: string;\n try {\n raw = await readFile(path, \"utf8\");\n } catch (err) {\n if (isNodeErrnoError(err) && err.code === \"ENOENT\") return emptyLayout(slug);\n console.warn(`[tecture] failed to read layout ${path}: ${String(err)}`);\n return emptyLayout(slug);\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n console.warn(`[tecture] malformed layout JSON at ${path}: ${String(err)}`);\n return emptyLayout(slug);\n }\n\n if (!parsed || typeof parsed !== \"object\") return emptyLayout(slug);\n const p = parsed as Record<string, unknown>;\n const nodes: Record<string, NodeLayoutEntry> = {};\n if (p.nodes && typeof p.nodes === \"object\") {\n for (const [id, entry] of Object.entries(p.nodes as Record<string, unknown>)) {\n if (!SLUG_RE.test(id)) continue;\n if (!isValidLayoutEntry(entry)) continue;\n const out: NodeLayoutEntry = { x: entry.x, y: entry.y };\n if (isFiniteNumber(entry.width)) out.width = entry.width;\n if (isFiniteNumber(entry.height)) out.height = entry.height;\n nodes[id] = out;\n }\n }\n return {\n version: 1,\n diagramId: slug,\n updatedAt: typeof p.updatedAt === \"string\" ? p.updatedAt : \"\",\n nodes,\n };\n }\n\n async saveLayout(\n slug: string,\n update: ApiDiagramLayoutUpdate,\n ): Promise<DiagramLayoutFile> {\n const file = normalizeLayoutUpdate(slug, update);\n const path = this.path(slug);\n await mkdir(dirname(path), { recursive: true });\n const tmp = `${path}.tmp`;\n await writeFile(tmp, `${JSON.stringify(file, null, 2)}\\n`, \"utf8\");\n await rename(tmp, path);\n return file;\n }\n}\n"],"mappings":"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","names":["Router","Router"]}
|
package/package.json
CHANGED