specv 0.7.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/dist/client/assets/_basePickBy-CSIMrWn0.js +1 -0
  2. package/dist/client/assets/_baseUniq-DS9_HBMG.js +1 -0
  3. package/dist/client/assets/arc-DIZaMnM_.js +1 -0
  4. package/dist/client/assets/architecture-PBZL5I3N-D094VGdH.js +1 -0
  5. package/dist/client/assets/architectureDiagram-2XIMDMQ5-DKPA-V_Y.js +36 -0
  6. package/dist/client/assets/array-B57E4cSs.js +1 -0
  7. package/dist/client/assets/blockDiagram-WCTKOSBZ-8W_tqeZC.js +132 -0
  8. package/dist/client/assets/c4Diagram-IC4MRINW-CTK6yens.js +10 -0
  9. package/dist/client/assets/channel-C1flAtjp.js +1 -0
  10. package/dist/client/assets/chunk-4BX2VUAB-CcBYJeDb.js +1 -0
  11. package/dist/client/assets/chunk-55IACEB6-L9pTAbLx.js +1 -0
  12. package/dist/client/assets/chunk-7E7YKBS2-DrRJz76L.js +1 -0
  13. package/dist/client/assets/chunk-7R4GIKGN-BkJi0iaA.js +81 -0
  14. package/dist/client/assets/chunk-Bj-mKKzh.js +1 -0
  15. package/dist/client/assets/chunk-C72U2L5F-B3l1tjGH.js +1 -0
  16. package/dist/client/assets/chunk-EGIJ26TM-B_KFLtsH.js +1 -0
  17. package/dist/client/assets/{chunk-FMBD7UC4-DC2prbcd.js → chunk-FMBD7UC4-D9ESZW96.js} +2 -2
  18. package/dist/client/assets/chunk-GEFDOKGD-C6_ZIT2u.js +2 -0
  19. package/dist/client/assets/chunk-GLR3WWYH-CPjw0lCF.js +2 -0
  20. package/dist/client/assets/chunk-HHEYEP7N-Bbj7R61_.js +1 -0
  21. package/dist/client/assets/chunk-JSJVCQXG-BoOzhRR0.js +1 -0
  22. package/dist/client/assets/chunk-KX2RTZJC-CbfglzNG.js +1 -0
  23. package/dist/client/assets/chunk-KYZI473N-CE4wlLFN.js +53 -0
  24. package/dist/client/assets/chunk-L3YUKLVL-D-CZP06o.js +1 -0
  25. package/dist/client/assets/chunk-MX3YWQON-CLs1ee2z.js +1 -0
  26. package/dist/client/assets/chunk-NQ4KR5QH-BffWtcfU.js +220 -0
  27. package/dist/client/assets/chunk-O4XLMI2P-DQ4cyvVC.js +7 -0
  28. package/dist/client/assets/chunk-OZEHJAEY-BMIUrFJ-.js +1 -0
  29. package/dist/client/assets/chunk-PQ6SQG4A-BoL5dE49.js +1 -0
  30. package/dist/client/assets/chunk-PU5JKC2W-D-q6bk8q.js +70 -0
  31. package/dist/client/assets/chunk-QZHKN3VN-B0dTomYZ.js +1 -0
  32. package/dist/client/assets/chunk-R5LLSJPH-riaLdH7r.js +1 -0
  33. package/dist/client/assets/chunk-WL4C6EOR-C-Ox-NZw.js +189 -0
  34. package/dist/client/assets/chunk-XIRO2GV7-NA4IsotN.js +1 -0
  35. package/dist/client/assets/chunk-XPW4576I-BPiZrew2.js +32 -0
  36. package/dist/client/assets/chunk-XZSTWKYB-LC3hmT5H.js +94 -0
  37. package/dist/client/assets/chunk-YBOYWFTD-7L-eDyoW.js +1 -0
  38. package/dist/client/assets/classDiagram-VBA2DB6C-Br3HdLiq.js +1 -0
  39. package/dist/client/assets/classDiagram-v2-RAHNMMFH-DSpV_STv.js +1 -0
  40. package/dist/client/assets/clone-BLQRtvQt.js +1 -0
  41. package/dist/client/assets/cose-bilkent-S5V4N54A-C04j_G09.js +1 -0
  42. package/dist/client/assets/cytoscape.esm-CGHh3kiH.js +321 -0
  43. package/dist/client/assets/dagre-CLRc6Nwm.js +1 -0
  44. package/dist/client/assets/dagre-KLK3FWXG-DnwOWmc_.js +4 -0
  45. package/dist/client/assets/defaultLocale-6WHJ3B09.js +1 -0
  46. package/dist/client/assets/diagram-E7M64L7V-CsoHmk1p.js +24 -0
  47. package/dist/client/assets/diagram-IFDJBPK2-CbQmk5xs.js +43 -0
  48. package/dist/client/assets/diagram-P4PSJMXO-dyU-5Pol.js +24 -0
  49. package/dist/client/assets/dist-DR_E4Kst.js +1 -0
  50. package/dist/client/assets/erDiagram-INFDFZHY-CQsFj48p.js +70 -0
  51. package/dist/client/assets/flowDiagram-PKNHOUZH-Di01qXD0.js +162 -0
  52. package/dist/client/assets/ganttDiagram-A5KZAMGK-pHtqsNid.js +292 -0
  53. package/dist/client/assets/gitGraph-HDMCJU4V-DP_-MyCH.js +1 -0
  54. package/dist/client/assets/gitGraphDiagram-K3NZZRJ6-CsQImbT2.js +65 -0
  55. package/dist/client/assets/graphlib-C9-5qMNz.js +1 -0
  56. package/dist/client/assets/index-BhGts6P8.js +214 -0
  57. package/dist/client/assets/index-Cv250fZW.css +1 -0
  58. package/dist/client/assets/info-3K5VOQVL-DQgtS_PZ.js +1 -0
  59. package/dist/client/assets/infoDiagram-LFFYTUFH-CM_MA-DH.js +2 -0
  60. package/dist/client/assets/init-DiYCGS1R.js +1 -0
  61. package/dist/client/assets/isArrayLikeObject-Dp4Ly64k.js +1 -0
  62. package/dist/client/assets/isEmpty-DYhMw56x.js +1 -0
  63. package/dist/client/assets/ishikawaDiagram-PHBUUO56-CEil6JbV.js +70 -0
  64. package/dist/client/assets/journeyDiagram-4ABVD52K-C8D5-7a4.js +139 -0
  65. package/dist/client/assets/kanban-definition-K7BYSVSG-D1Dy_-sr.js +89 -0
  66. package/dist/client/assets/katex-LQIUYz1j.js +261 -0
  67. package/dist/client/assets/line-CVr_gqHO.js +1 -0
  68. package/dist/client/assets/linear-UdA9xx6X.js +1 -0
  69. package/dist/client/assets/math-B1fAsS3f.js +1 -0
  70. package/dist/client/assets/mermaid-block-DNspBXsu.js +11 -0
  71. package/dist/client/assets/mermaid-parser.core-DfrzNpd8.js +4 -0
  72. package/dist/client/assets/mindmap-definition-YRQLILUH-DwQXndKv.js +68 -0
  73. package/dist/client/assets/ordinal-C1FS1Sfe.js +1 -0
  74. package/dist/client/assets/packet-RMMSAZCW-BeKH5fVO.js +1 -0
  75. package/dist/client/assets/path-DL_QDy2X.js +1 -0
  76. package/dist/client/assets/pie-UPGHQEXC-B5IHlfZ0.js +1 -0
  77. package/dist/client/assets/pieDiagram-SKSYHLDU-DgnZPwhp.js +30 -0
  78. package/dist/client/assets/quadrantDiagram-337W2JSQ-CuujoKYe.js +7 -0
  79. package/dist/client/assets/radar-KQ55EAFF-BSNKRbeO.js +1 -0
  80. package/dist/client/assets/requirementDiagram-Z7DCOOCP-a33F13MK.js +73 -0
  81. package/dist/client/assets/rough.esm-By2WpMXC.js +1 -0
  82. package/dist/client/assets/sankeyDiagram-WA2Y5GQK-BdzUYz9Y.js +10 -0
  83. package/dist/client/assets/sequenceDiagram-2WXFIKYE-BUDCDh5e.js +145 -0
  84. package/dist/client/assets/src-D9XCNmfv.js +1 -0
  85. package/dist/client/assets/stateDiagram-RAJIS63D-CWhysv8q.js +1 -0
  86. package/dist/client/assets/stateDiagram-v2-FVOUBMTO-BiPShD7K.js +1 -0
  87. package/dist/client/assets/timeline-definition-YZTLITO2-Bvqbg0-L.js +61 -0
  88. package/dist/client/assets/treemap-KZPCXAKY-DKmbGZbe.js +1 -0
  89. package/dist/client/assets/vennDiagram-LZ73GAT5-BIaIoS9s.js +34 -0
  90. package/dist/client/assets/xychartDiagram-JWTSCODW-COZ4IKsP.js +7 -0
  91. package/dist/client/index.html +4 -2
  92. package/dist/server/cli.d.mts +9 -0
  93. package/dist/server/cli.mjs +401 -0
  94. package/package.json +24 -20
  95. package/dist/client/assets/_basePickBy-JgoM0ooD.js +0 -1
  96. package/dist/client/assets/_baseUniq-BPGBR6vF.js +0 -1
  97. package/dist/client/assets/arc-CThMKSs9.js +0 -1
  98. package/dist/client/assets/architectureDiagram-2XIMDMQ5-DzfSIOvN.js +0 -36
  99. package/dist/client/assets/blockDiagram-WCTKOSBZ-Ddzd1gw8.js +0 -132
  100. package/dist/client/assets/c4Diagram-IC4MRINW-BOFM2FDK.js +0 -10
  101. package/dist/client/assets/channel-YZBi4I_s.js +0 -1
  102. package/dist/client/assets/chunk-4BX2VUAB-DNvTbBhO.js +0 -1
  103. package/dist/client/assets/chunk-55IACEB6-VTl2c3uq.js +0 -1
  104. package/dist/client/assets/chunk-JSJVCQXG-D16D73pg.js +0 -1
  105. package/dist/client/assets/chunk-KX2RTZJC-lzCSppRt.js +0 -1
  106. package/dist/client/assets/chunk-NQ4KR5QH-Dm8-ZfYx.js +0 -220
  107. package/dist/client/assets/chunk-QZHKN3VN-BDJj6ch0.js +0 -1
  108. package/dist/client/assets/chunk-WL4C6EOR-DReLGTEp.js +0 -189
  109. package/dist/client/assets/classDiagram-VBA2DB6C-RXSC8Nxo.js +0 -1
  110. package/dist/client/assets/classDiagram-v2-RAHNMMFH-RXSC8Nxo.js +0 -1
  111. package/dist/client/assets/clone-BfeBigwB.js +0 -1
  112. package/dist/client/assets/cose-bilkent-S5V4N54A-Bg_dM5Mk.js +0 -1
  113. package/dist/client/assets/cytoscape.esm-5J0xJHOV.js +0 -321
  114. package/dist/client/assets/dagre-KLK3FWXG-CA61AJEd.js +0 -4
  115. package/dist/client/assets/defaultLocale-DX6XiGOO.js +0 -1
  116. package/dist/client/assets/diagram-E7M64L7V-C7Myv_34.js +0 -24
  117. package/dist/client/assets/diagram-IFDJBPK2-ClQH9Fha.js +0 -43
  118. package/dist/client/assets/diagram-P4PSJMXO-BsO56LP5.js +0 -24
  119. package/dist/client/assets/erDiagram-INFDFZHY-69ePyd9U.js +0 -70
  120. package/dist/client/assets/flowDiagram-PKNHOUZH-DW5jwimt.js +0 -162
  121. package/dist/client/assets/ganttDiagram-A5KZAMGK-DRkHzN-i.js +0 -292
  122. package/dist/client/assets/gitGraphDiagram-K3NZZRJ6-CnMPsV7V.js +0 -65
  123. package/dist/client/assets/graph-DL9a2Eq1.js +0 -1
  124. package/dist/client/assets/index-CstOaARo.js +0 -367
  125. package/dist/client/assets/index-J2bJsGXA.css +0 -1
  126. package/dist/client/assets/infoDiagram-LFFYTUFH-BsvQO7qE.js +0 -2
  127. package/dist/client/assets/init-Gi6I4Gst.js +0 -1
  128. package/dist/client/assets/ishikawaDiagram-PHBUUO56-Bj1kH8Tq.js +0 -70
  129. package/dist/client/assets/journeyDiagram-4ABVD52K-DN8A8Si0.js +0 -139
  130. package/dist/client/assets/kanban-definition-K7BYSVSG-DHRhIZtq.js +0 -89
  131. package/dist/client/assets/layout-CW2ZoTQx.js +0 -1
  132. package/dist/client/assets/linear-BMTURWKv.js +0 -1
  133. package/dist/client/assets/mermaid-block-mMz1yMUG.js +0 -249
  134. package/dist/client/assets/mindmap-definition-YRQLILUH-uZjEvns8.js +0 -68
  135. package/dist/client/assets/ordinal-Cboi1Yqb.js +0 -1
  136. package/dist/client/assets/pieDiagram-SKSYHLDU-AMNdAR9s.js +0 -30
  137. package/dist/client/assets/quadrantDiagram-337W2JSQ-DP5gFZrq.js +0 -7
  138. package/dist/client/assets/requirementDiagram-Z7DCOOCP-DAfejtVK.js +0 -73
  139. package/dist/client/assets/sankeyDiagram-WA2Y5GQK-DDS6vee1.js +0 -10
  140. package/dist/client/assets/sequenceDiagram-2WXFIKYE-CryP14Kv.js +0 -145
  141. package/dist/client/assets/stateDiagram-RAJIS63D-dHRtI695.js +0 -1
  142. package/dist/client/assets/stateDiagram-v2-FVOUBMTO-B9qPhCGr.js +0 -1
  143. package/dist/client/assets/timeline-definition-YZTLITO2-DW-uUIsR.js +0 -61
  144. package/dist/client/assets/treemap-KZPCXAKY-BSGDMBBN.js +0 -162
  145. package/dist/client/assets/vennDiagram-LZ73GAT5-zBQsJgKs.js +0 -34
  146. package/dist/client/assets/xychartDiagram-JWTSCODW-CrR0K61K.js +0 -7
  147. package/dist/server/cli.d.ts +0 -9
  148. package/dist/server/cli.js +0 -515
@@ -0,0 +1,401 @@
1
+ #!/usr/bin/env node
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { serve } from "@hono/node-server";
6
+ import { serveStatic } from "@hono/node-server/serve-static";
7
+ import { program } from "commander";
8
+ import { Hono } from "hono";
9
+ import fs$1 from "node:fs/promises";
10
+ import os from "node:os";
11
+ import { execFile } from "node:child_process";
12
+ import { promisify } from "node:util";
13
+ import qrcode from "qrcode-terminal";
14
+ //#region src/server/files.ts
15
+ const IGNORED_DIRS = new Set([
16
+ "node_modules",
17
+ ".git",
18
+ ".hg",
19
+ ".svn",
20
+ "__pycache__",
21
+ ".next",
22
+ ".nuxt"
23
+ ]);
24
+ const MAX_DEPTH = 10;
25
+ const sortEntries = (entries) => entries.toSorted((a, b) => {
26
+ if (a.isDirectory() && !b.isDirectory()) return -1;
27
+ if (!a.isDirectory() && b.isDirectory()) return 1;
28
+ return a.name.localeCompare(b.name);
29
+ });
30
+ const buildFileNode = (entry, relativePath) => ({
31
+ name: entry.name,
32
+ path: path.join(relativePath, entry.name)
33
+ });
34
+ const buildDirNode = (entry, relativePath, children) => ({
35
+ children,
36
+ name: entry.name,
37
+ path: path.join(relativePath, entry.name)
38
+ });
39
+ const readAndSortEntries = async (baseDir, relativePath) => {
40
+ return sortEntries(await fs$1.readdir(path.join(baseDir, relativePath), { withFileTypes: true }));
41
+ };
42
+ const collectNodes = async (sorted, baseDir, relativePath, depth, scan) => {
43
+ const result = [];
44
+ for (const entry of sorted) if (entry.isDirectory() && !IGNORED_DIRS.has(entry.name)) {
45
+ const children = await scan(baseDir, path.join(relativePath, entry.name), depth + 1);
46
+ if (children.length > 0) result.push(buildDirNode(entry, relativePath, children));
47
+ } else if (!entry.isDirectory() && entry.name.endsWith(".md")) result.push(buildFileNode(entry, relativePath));
48
+ return result;
49
+ };
50
+ const scanMarkdownFiles = async (baseDir, relativePath = "", depth = 0) => {
51
+ if (depth >= MAX_DEPTH) return [];
52
+ return collectNodes(await readAndSortEntries(baseDir, relativePath), baseDir, relativePath, depth, scanMarkdownFiles);
53
+ };
54
+ //#endregion
55
+ //#region src/server/security.ts
56
+ var SecurityError = class extends Error {
57
+ constructor(message) {
58
+ super(message);
59
+ this.name = "SecurityError";
60
+ }
61
+ };
62
+ const isWithinBaseDir = (targetPath, baseDir) => targetPath.startsWith(baseDir + path.sep) || targetPath === baseDir;
63
+ const ALLOWED_IMAGE_EXTENSIONS = new Set([
64
+ ".png",
65
+ ".jpg",
66
+ ".jpeg",
67
+ ".gif",
68
+ ".svg",
69
+ ".webp"
70
+ ]);
71
+ const validateExtension = (filePath) => {
72
+ if (!filePath.endsWith(".md")) throw new SecurityError(`Only .md files are allowed: ${filePath}`);
73
+ };
74
+ const validateImageExtension = (filePath) => {
75
+ const ext = path.extname(filePath).toLowerCase();
76
+ if (!ALLOWED_IMAGE_EXTENSIONS.has(ext)) throw new SecurityError(`Unsupported image format: ${filePath}`);
77
+ };
78
+ const resolveRealPath = (resolved, baseDir, filePath) => {
79
+ try {
80
+ const realPath = fs.realpathSync(resolved);
81
+ if (!isWithinBaseDir(realPath, baseDir)) throw new SecurityError(`Path traversal detected: ${filePath}`);
82
+ return realPath;
83
+ } catch (error) {
84
+ if (error instanceof SecurityError) throw error;
85
+ return resolved;
86
+ }
87
+ };
88
+ const validatePath = (filePath, baseDir) => {
89
+ const decoded = decodeURIComponent(filePath);
90
+ const resolved = path.resolve(baseDir, decoded);
91
+ if (!isWithinBaseDir(resolved, baseDir)) throw new SecurityError(`Path traversal detected: ${filePath}`);
92
+ validateExtension(decoded);
93
+ return resolveRealPath(resolved, baseDir, filePath);
94
+ };
95
+ const validateImagePath = (filePath, baseDir) => {
96
+ const decoded = decodeURIComponent(filePath);
97
+ const resolved = path.resolve(baseDir, decoded);
98
+ if (!isWithinBaseDir(resolved, baseDir)) throw new SecurityError(`Path traversal detected: ${filePath}`);
99
+ validateImageExtension(decoded);
100
+ return resolveRealPath(resolved, baseDir, filePath);
101
+ };
102
+ //#endregion
103
+ //#region src/server/watcher.ts
104
+ const DEBOUNCE_MS = 200;
105
+ const isIgnored = (filePath) => {
106
+ return filePath.split(path.sep).some((part) => IGNORED_DIRS.has(part));
107
+ };
108
+ const isMarkdown = (filePath) => path.extname(filePath) === ".md";
109
+ const isSafePath = (baseDir, filePath) => {
110
+ const normalized = path.normalize(filePath);
111
+ const resolved = path.resolve(baseDir, normalized);
112
+ return resolved.startsWith(baseDir + path.sep) || resolved === baseDir;
113
+ };
114
+ const resolveEventType = (fullPath, normalized, knownFiles) => {
115
+ if (!fs.existsSync(fullPath)) {
116
+ knownFiles.delete(normalized);
117
+ return "unlink";
118
+ }
119
+ if (knownFiles.has(normalized)) return "change";
120
+ knownFiles.add(normalized);
121
+ return "add";
122
+ };
123
+ const scanExisting = (dir, knownFiles, rel = "") => {
124
+ try {
125
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) if (entry.isDirectory() && !IGNORED_DIRS.has(entry.name)) scanExisting(path.join(dir, entry.name), knownFiles, path.join(rel, entry.name));
126
+ else if (entry.isFile() && isMarkdown(entry.name)) knownFiles.add(path.join(rel, entry.name));
127
+ } catch {}
128
+ };
129
+ const createWatcher = (baseDir, onEvent) => {
130
+ const timers = /* @__PURE__ */ new Map();
131
+ const knownFiles = /* @__PURE__ */ new Set();
132
+ scanExisting(baseDir, knownFiles);
133
+ const watcher = fs.watch(baseDir, { recursive: true }, (_event, filename) => {
134
+ if (!filename) return;
135
+ const normalized = path.normalize(filename);
136
+ if (!isMarkdown(normalized) || isIgnored(normalized)) return;
137
+ if (!isSafePath(baseDir, normalized)) return;
138
+ if (timers.has(normalized)) clearTimeout(timers.get(normalized));
139
+ timers.set(normalized, setTimeout(() => {
140
+ timers.delete(normalized);
141
+ onEvent({
142
+ path: normalized,
143
+ type: resolveEventType(path.resolve(baseDir, normalized), normalized, knownFiles)
144
+ });
145
+ }, DEBOUNCE_MS));
146
+ });
147
+ return { close: () => {
148
+ watcher.close();
149
+ for (const timer of timers.values()) clearTimeout(timer);
150
+ timers.clear();
151
+ } };
152
+ };
153
+ //#endregion
154
+ //#region src/server/api.ts
155
+ const IMAGE_CONTENT_TYPES = {
156
+ ".gif": "image/gif",
157
+ ".jpeg": "image/jpeg",
158
+ ".jpg": "image/jpeg",
159
+ ".png": "image/png",
160
+ ".svg": "image/svg+xml",
161
+ ".webp": "image/webp"
162
+ };
163
+ const readFile = async (filePath, baseDir) => {
164
+ const resolvedPath = validatePath(filePath, baseDir);
165
+ return await fs$1.readFile(resolvedPath, "utf8");
166
+ };
167
+ const readImage = async (filePath, baseDir) => {
168
+ const resolvedPath = validateImagePath(filePath, baseDir);
169
+ return {
170
+ contentType: IMAGE_CONTENT_TYPES[path.extname(resolvedPath).toLowerCase()] ?? "application/octet-stream",
171
+ data: await fs$1.readFile(resolvedPath)
172
+ };
173
+ };
174
+ const TREE_DEBOUNCE_MS = 300;
175
+ const setupWatcher = (baseDir, factory, broadcast) => {
176
+ let treeDebounceTimer = null;
177
+ factory(baseDir, (event) => {
178
+ if (event.type === "change") broadcast({
179
+ data: JSON.stringify({ path: event.path }),
180
+ event: "file-changed"
181
+ });
182
+ else {
183
+ if (treeDebounceTimer) clearTimeout(treeDebounceTimer);
184
+ treeDebounceTimer = setTimeout(async () => {
185
+ try {
186
+ const files = await scanMarkdownFiles(baseDir);
187
+ broadcast({
188
+ data: JSON.stringify({ files }),
189
+ event: "tree-changed"
190
+ });
191
+ } catch {}
192
+ }, TREE_DEBOUNCE_MS);
193
+ }
194
+ });
195
+ };
196
+ const createApiRouter = (baseDir, options) => {
197
+ const api = new Hono();
198
+ const sseListeners = /* @__PURE__ */ new Set();
199
+ const encoder = new TextEncoder();
200
+ const broadcast = (msg) => {
201
+ for (const listener of sseListeners) listener(msg);
202
+ };
203
+ setupWatcher(baseDir, options?.createWatcher ?? createWatcher, broadcast);
204
+ api.get("/api/files", async (c) => {
205
+ try {
206
+ const files = await scanMarkdownFiles(baseDir);
207
+ return c.json({ files });
208
+ } catch {
209
+ return c.json({ error: "Failed to scan files" }, 500);
210
+ }
211
+ });
212
+ api.get("/api/file", async (c) => {
213
+ const filePath = c.req.query("path");
214
+ if (!filePath) return c.json({ error: "path query parameter is required" }, 400);
215
+ try {
216
+ const content = await readFile(filePath, baseDir);
217
+ return c.text(content);
218
+ } catch (error) {
219
+ if (error instanceof SecurityError) return c.json({ error: error.message }, 400);
220
+ return c.json({ error: "File not found" }, 404);
221
+ }
222
+ });
223
+ api.get("/api/image", async (c) => {
224
+ const filePath = c.req.query("path");
225
+ if (!filePath) return c.json({ error: "path query parameter is required" }, 400);
226
+ try {
227
+ const { contentType, data } = await readImage(filePath, baseDir);
228
+ return c.body(new Uint8Array(data), 200, { "Content-Type": contentType });
229
+ } catch (error) {
230
+ if (error instanceof SecurityError) return c.json({ error: error.message }, 400);
231
+ return c.json({ error: "Image not found" }, 404);
232
+ }
233
+ });
234
+ api.get("/api/watch", (_c) => {
235
+ let listener = null;
236
+ const stream = new ReadableStream({
237
+ cancel() {
238
+ if (listener) sseListeners.delete(listener);
239
+ },
240
+ start(controller) {
241
+ listener = (msg) => {
242
+ controller.enqueue(encoder.encode(`event: ${msg.event}\ndata: ${msg.data}\n\n`));
243
+ };
244
+ sseListeners.add(listener);
245
+ }
246
+ });
247
+ return new Response(stream, { headers: {
248
+ "Cache-Control": "no-cache",
249
+ Connection: "keep-alive",
250
+ "Content-Type": "text/event-stream"
251
+ } });
252
+ });
253
+ return api;
254
+ };
255
+ //#endregion
256
+ //#region src/server/network.ts
257
+ const getLocalIpAddress = () => {
258
+ const interfaces = os.networkInterfaces();
259
+ for (const addrs of Object.values(interfaces)) {
260
+ if (!addrs) continue;
261
+ for (const addr of addrs) if (addr.family === "IPv4" && !addr.internal) return addr.address;
262
+ }
263
+ return null;
264
+ };
265
+ //#endregion
266
+ //#region src/server/open-browser.ts
267
+ const execFile$1 = promisify(execFile);
268
+ const getCommand = (url) => {
269
+ if (process.platform === "darwin") return {
270
+ args: [url],
271
+ command: "open"
272
+ };
273
+ if (process.platform === "win32") return {
274
+ args: [
275
+ "/c",
276
+ "start",
277
+ "",
278
+ url
279
+ ],
280
+ command: "cmd"
281
+ };
282
+ return {
283
+ args: [url],
284
+ command: "xdg-open"
285
+ };
286
+ };
287
+ const openInBrowser = async (url) => {
288
+ const { args, command } = getCommand(url);
289
+ await execFile$1(command, args);
290
+ };
291
+ //#endregion
292
+ //#region src/server/qr-display.ts
293
+ const displayQrCode = (url) => {
294
+ qrcode.generate(url, { small: true }, (code) => {
295
+ console.log(code);
296
+ });
297
+ };
298
+ //#endregion
299
+ //#region src/server/cli.ts
300
+ const packageJsonPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../package.json");
301
+ const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
302
+ const currentDir = import.meta.dirname;
303
+ const DISCONNECT_GRACE_MS = 2e3;
304
+ const registerLifecycle = (app, onDisconnect) => {
305
+ let connections = 0;
306
+ let graceTimer = null;
307
+ app.get("/api/lifecycle", (_c) => {
308
+ connections += 1;
309
+ if (graceTimer) {
310
+ clearTimeout(graceTimer);
311
+ graceTimer = null;
312
+ }
313
+ const stream = new ReadableStream({
314
+ cancel() {
315
+ connections -= 1;
316
+ if (connections === 0) graceTimer = setTimeout(() => {
317
+ if (connections === 0) onDisconnect();
318
+ }, DISCONNECT_GRACE_MS);
319
+ },
320
+ start(controller) {
321
+ controller.enqueue("data: connected\n\n");
322
+ }
323
+ });
324
+ return new Response(stream, { headers: {
325
+ "Cache-Control": "no-cache",
326
+ Connection: "keep-alive",
327
+ "Content-Type": "text/event-stream"
328
+ } });
329
+ });
330
+ };
331
+ const serveClient = (app, clientDir) => {
332
+ const indexHtmlPath = path.join(clientDir, "index.html");
333
+ if (fs.existsSync(indexHtmlPath)) {
334
+ const indexHtml = fs.readFileSync(indexHtmlPath, "utf8");
335
+ app.use("/*", serveStatic({ root: clientDir }));
336
+ app.get("/*", (c) => c.html(indexHtml));
337
+ }
338
+ };
339
+ const createApp = (baseDir, clientDir, onDisconnect) => {
340
+ const app = new Hono();
341
+ if (onDisconnect) registerLifecycle(app, onDisconnect);
342
+ app.route("/", createApiRouter(baseDir));
343
+ serveClient(app, clientDir);
344
+ return app;
345
+ };
346
+ const getNetworkConfig = (options) => ({
347
+ enableNetwork: options.host,
348
+ hostname: options.host ? "0.0.0.0" : "127.0.0.1"
349
+ });
350
+ program.name("specv").description("Local Markdown preview with GitHub-style rendering").version(pkg.version).option("-p, --port <number>", "Port number", "4649").option("--host", "Expose to local network (enables QR code)").option("--no-auto-close", "Disable auto-close on client disconnect").action((options) => {
351
+ const baseDir = process.cwd();
352
+ const startPort = Number.parseInt(options.port, 10);
353
+ const clientDir = path.join(currentDir, "../client");
354
+ const networkConfig = getNetworkConfig({ host: Boolean(options.host) });
355
+ const app = createApp(baseDir, clientDir, options.autoClose ? () => {
356
+ console.log("Client disconnected, shutting down server...");
357
+ process.exit(0);
358
+ } : void 0);
359
+ const tryListen = (port) => {
360
+ serve({
361
+ fetch: app.fetch,
362
+ hostname: networkConfig.hostname,
363
+ port
364
+ }, async (info) => {
365
+ const localUrl = `http://localhost:${info.port}`;
366
+ console.log("");
367
+ console.log(` specv v${program.version()}`);
368
+ console.log("");
369
+ console.log(` ➜ Local: ${localUrl}`);
370
+ const ip = networkConfig.enableNetwork ? getLocalIpAddress() : null;
371
+ const networkUrl = ip ? `http://${ip}:${info.port}` : null;
372
+ if (networkUrl) console.log(` ➜ Network: ${networkUrl}`);
373
+ else if (!networkConfig.enableNetwork) console.log(" ➜ Network: use --host to expose to local network");
374
+ console.log("");
375
+ console.log(` Serving: ${baseDir}`);
376
+ if (networkUrl && !process.env.SPECV_HOST) {
377
+ console.log("");
378
+ displayQrCode(networkUrl);
379
+ }
380
+ console.log("");
381
+ console.log(" Press Ctrl+C to stop");
382
+ try {
383
+ await openInBrowser(localUrl);
384
+ } catch {
385
+ console.log(` Open ${localUrl} in your browser`);
386
+ }
387
+ }).on("error", (err) => {
388
+ if (err.code === "EADDRINUSE" && port < startPort + 10) {
389
+ console.log(`Port ${port} is in use, trying ${port + 1}...`);
390
+ tryListen(port + 1);
391
+ } else {
392
+ console.error(`Failed to start server: ${err.message}`);
393
+ process.exit(1);
394
+ }
395
+ });
396
+ };
397
+ tryListen(startPort);
398
+ });
399
+ program.parse();
400
+ //#endregion
401
+ export { getNetworkConfig };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specv",
3
- "version": "0.7.1",
3
+ "version": "0.8.0",
4
4
  "description": "Local Markdown preview with GitHub-style rendering",
5
5
  "keywords": [
6
6
  "cli",
@@ -18,7 +18,7 @@
18
18
  "url": "git+https://github.com/daiki-beppu/specv.git"
19
19
  },
20
20
  "bin": {
21
- "specv": "./dist/server/cli.js"
21
+ "specv": "./dist/server/cli.mjs"
22
22
  },
23
23
  "files": [
24
24
  "dist/"
@@ -26,19 +26,18 @@
26
26
  "type": "module",
27
27
  "scripts": {
28
28
  "dev": "pnpm run dev:server & pnpm run dev:client",
29
- "dev:client": "vite",
30
- "dev:host": "tsx src/server/cli.ts --host & SPECV_HOST=1 vite",
29
+ "dev:client": "vp dev",
30
+ "dev:host": "tsx src/server/cli.ts --host & SPECV_HOST=1 vp dev",
31
31
  "dev:server": "tsx src/server/cli.ts",
32
- "build:client": "vite build",
33
- "build:server": "tsup src/server/cli.ts --format esm --out-dir dist/server --dts",
32
+ "build:client": "vp build",
33
+ "build:server": "vp pack",
34
34
  "build": "pnpm run build:client && pnpm run build:server",
35
- "test": "vitest run",
35
+ "test": "vp test run",
36
36
  "test:e2e": "playwright test",
37
37
  "prepublishOnly": "pnpm run build",
38
- "check": "ultracite check",
39
- "fix": "ultracite fix",
38
+ "check": "vp check",
39
+ "fix": "vp check --fix",
40
40
  "knip": "knip",
41
- "typecheck": "tsc --noEmit",
42
41
  "prepare": "lefthook install"
43
42
  },
44
43
  "dependencies": {
@@ -57,21 +56,23 @@
57
56
  "remark-emoji": "^5.0.2",
58
57
  "remark-frontmatter": "^5.0.0",
59
58
  "remark-github-blockquote-alert": "^2.0.1",
60
- "remark-math": "^6.0.0"
59
+ "remark-math": "^6.0.0",
60
+ "yaml": "^2.8.2"
61
61
  },
62
62
  "devDependencies": {
63
63
  "@base-ui/react": "^1.2.0",
64
64
  "@playwright/test": "^1.58.2",
65
65
  "@tailwindcss/typography": "^0.5.19",
66
- "@tailwindcss/vite": "^4.2.1",
66
+ "@tailwindcss/vite": "^4.2.2",
67
67
  "@tanstack/react-hotkeys": "^0.4.1",
68
68
  "@testing-library/react": "^16.3.2",
69
69
  "@types/jsdom": "^28.0.0",
70
+ "@types/mdast": "^4.0.4",
70
71
  "@types/node": "^25.4.0",
71
72
  "@types/qrcode-terminal": "^0.12.2",
72
73
  "@types/react": "^19.2.14",
73
74
  "@types/react-dom": "^19.2.3",
74
- "@vitejs/plugin-react": "^5.1.4",
75
+ "@vitejs/plugin-react": "^6.0.1",
75
76
  "class-variance-authority": "^0.7.1",
76
77
  "clsx": "^2.1.1",
77
78
  "fzf": "^0.5.2",
@@ -79,8 +80,6 @@
79
80
  "knip": "^5.86.0",
80
81
  "lefthook": "^2.1.3",
81
82
  "lucide-react": "^0.577.0",
82
- "oxfmt": "^0.41.0",
83
- "oxlint": "^1.56.0",
84
83
  "prism-react-renderer": "^2.4.1",
85
84
  "react": "^19.2.4",
86
85
  "react-dom": "^19.2.4",
@@ -92,14 +91,19 @@
92
91
  "shadcn": "^4.0.5",
93
92
  "tailwind-merge": "^3.5.0",
94
93
  "tailwindcss": "^4.2.1",
95
- "tsup": "^8.5.1",
96
94
  "tsx": "^4.21.0",
97
95
  "tw-animate-css": "^1.4.0",
98
96
  "typescript": "^5.9.3",
99
- "ultracite": "7.3.2",
100
97
  "unified": "^11.0.5",
101
- "vite": "^7.3.1",
102
- "vitest": "^4.0.18"
98
+ "vite": "npm:@voidzero-dev/vite-plus-core@latest",
99
+ "vite-plus": "latest",
100
+ "vitest": "npm:@voidzero-dev/vite-plus-test@latest"
103
101
  },
104
- "packageManager": "pnpm@10.32.1"
102
+ "packageManager": "pnpm@10.32.1",
103
+ "pnpm": {
104
+ "overrides": {
105
+ "vite": "npm:@voidzero-dev/vite-plus-core@latest",
106
+ "vitest": "npm:@voidzero-dev/vite-plus-test@latest"
107
+ }
108
+ }
105
109
  }
@@ -1 +0,0 @@
1
- import{e as x,c as O,g as m,k as P,h as p,j as w,l as c,m as A,n as I,t as N,o as _}from"./_baseUniq-BPGBR6vF.js";import{aT as g,aC as $,aU as E,aV as F,aW as M,aX as l,aY as T,aZ as B,a_ as y,a$ as S}from"./mermaid-block-mMz1yMUG.js";var C=/\s/;function G(n){for(var r=n.length;r--&&C.test(n.charAt(r)););return r}var H=/^\s+/;function L(n){return n&&n.slice(0,G(n)+1).replace(H,"")}var o=NaN,R=/^[-+]0x[0-9a-f]+$/i,W=/^0b[01]+$/i,X=/^0o[0-7]+$/i,Y=parseInt;function q(n){if(typeof n=="number")return n;if(x(n))return o;if(g(n)){var r=typeof n.valueOf=="function"?n.valueOf():n;n=g(r)?r+"":r}if(typeof n!="string")return n===0?n:+n;n=L(n);var t=W.test(n);return t||X.test(n)?Y(n.slice(2),t?2:8):R.test(n)?o:+n}var v=1/0,z=17976931348623157e292;function K(n){if(!n)return n===0?n:0;if(n=q(n),n===v||n===-v){var r=n<0?-1:1;return r*z}return n===n?n:0}function U(n){var r=K(n),t=r%1;return r===r?t?r-t:r:0}function fn(n){var r=n==null?0:n.length;return r?O(n):[]}var b=Object.prototype,Z=b.hasOwnProperty,dn=$(function(n,r){n=Object(n);var t=-1,i=r.length,a=i>2?r[2]:void 0;for(a&&E(r[0],r[1],a)&&(i=1);++t<i;)for(var f=r[t],e=F(f),s=-1,d=e.length;++s<d;){var u=e[s],h=n[u];(h===void 0||M(h,b[u])&&!Z.call(n,u))&&(n[u]=f[u])}return n});function un(n){var r=n==null?0:n.length;return r?n[r-1]:void 0}function D(n){return function(r,t,i){var a=Object(r);if(!l(r)){var f=m(t);r=P(r),t=function(s){return f(a[s],s,a)}}var e=n(r,t,i);return e>-1?a[f?r[e]:e]:void 0}}var J=Math.max;function Q(n,r,t){var i=n==null?0:n.length;if(!i)return-1;var a=t==null?0:U(t);return a<0&&(a=J(i+a,0)),p(n,m(r),a)}var hn=D(Q);function V(n,r){var t=-1,i=l(n)?Array(n.length):[];return w(n,function(a,f,e){i[++t]=r(a,f,e)}),i}function gn(n,r){var t=T(n)?c:V;return t(n,m(r))}var j=Object.prototype,k=j.hasOwnProperty;function nn(n,r){return n!=null&&k.call(n,r)}function mn(n,r){return n!=null&&A(n,r,nn)}function rn(n,r){return n<r}function tn(n,r,t){for(var i=-1,a=n.length;++i<a;){var f=n[i],e=r(f);if(e!=null&&(s===void 0?e===e&&!x(e):t(e,s)))var s=e,d=f}return d}function on(n){return n&&n.length?tn(n,B,rn):void 0}function an(n,r,t,i){if(!g(n))return n;r=I(r,n);for(var a=-1,f=r.length,e=f-1,s=n;s!=null&&++a<f;){var d=N(r[a]),u=t;if(d==="__proto__"||d==="constructor"||d==="prototype")return n;if(a!=e){var h=s[d];u=void 0,u===void 0&&(u=g(h)?h:y(r[a+1])?[]:{})}S(s,d,u),s=s[d]}return n}function vn(n,r,t){for(var i=-1,a=r.length,f={};++i<a;){var e=r[i],s=_(n,e);t(s,e)&&an(f,I(e,n),s)}return f}export{rn as a,tn as b,V as c,vn as d,on as e,fn as f,hn as g,mn as h,dn as i,U as j,un as l,gn as m,K as t};
@@ -1 +0,0 @@
1
- import{bg as S,bh as Rn,aY as T,bi as w,aX as sn,bj as xn,bk as mn,bl as Cn,bm as un,bn as x,aV as B,bo as Mn,bp as on,bq as Fn,br as E,be as gn,bs as R,aT as ln,bt as Dn,bu as D,bv as Gn,bw as Bn,bx as _,a$ as Un,by as Nn,aW as Kn,bz as X,bA as jn,bB as Hn,a_ as qn,aZ as cn,bc as Yn,bC as C}from"./mermaid-block-mMz1yMUG.js";var Zn="[object Symbol]";function U(n){return typeof n=="symbol"||S(n)&&Rn(n)==Zn}function bn(n,r){for(var e=-1,t=n==null?0:n.length,f=Array(t);++e<t;)f[e]=r(n[e],e,n);return f}var J=w?w.prototype:void 0,Q=J?J.toString:void 0;function dn(n){if(typeof n=="string")return n;if(T(n))return bn(n,dn)+"";if(U(n))return Q?Q.call(n):"";var r=n+"";return r=="0"&&1/n==-1/0?"-0":r}function Xn(){}function pn(n,r){for(var e=-1,t=n==null?0:n.length;++e<t&&r(n[e],e,n)!==!1;);return n}function Jn(n,r,e,t){for(var f=n.length,i=e+-1;++i<f;)if(r(n[i],i,n))return i;return-1}function Qn(n){return n!==n}function Wn(n,r,e){for(var t=e-1,f=n.length;++t<f;)if(n[t]===r)return t;return-1}function zn(n,r,e){return r===r?Wn(n,r,e):Jn(n,Qn,e)}function Vn(n,r){var e=n==null?0:n.length;return!!e&&zn(n,r,0)>-1}function $(n){return sn(n)?xn(n):mn(n)}var kn=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,nr=/^\w*$/;function N(n,r){if(T(n))return!1;var e=typeof n;return e=="number"||e=="symbol"||e=="boolean"||n==null||U(n)?!0:nr.test(n)||!kn.test(n)||r!=null&&n in Object(r)}var rr=500;function er(n){var r=Cn(n,function(t){return e.size===rr&&e.clear(),t}),e=r.cache;return r}var tr=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,ir=/\\(\\)?/g,fr=er(function(n){var r=[];return n.charCodeAt(0)===46&&r.push(""),n.replace(tr,function(e,t,f,i){r.push(f?i.replace(ir,"$1"):t||e)}),r});function ar(n){return n==null?"":dn(n)}function An(n,r){return T(n)?n:N(n,r)?[n]:fr(ar(n))}function m(n){if(typeof n=="string"||U(n))return n;var r=n+"";return r=="0"&&1/n==-1/0?"-0":r}function yn(n,r){r=An(r,n);for(var e=0,t=r.length;n!=null&&e<t;)n=n[m(r[e++])];return e&&e==t?n:void 0}function sr(n,r,e){var t=n==null?void 0:yn(n,r);return t===void 0?e:t}function K(n,r){for(var e=-1,t=r.length,f=n.length;++e<t;)n[f+e]=r[e];return n}var W=w?w.isConcatSpreadable:void 0;function ur(n){return T(n)||un(n)||!!(W&&n&&n[W])}function Ot(n,r,e,t,f){var i=-1,a=n.length;for(e||(e=ur),f||(f=[]);++i<a;){var s=n[i];e(s)?K(f,s):t||(f[f.length]=s)}return f}function or(n,r,e,t){var f=-1,i=n==null?0:n.length;for(t&&i&&(e=n[++f]);++f<i;)e=r(e,n[f],f,n);return e}function gr(n,r){return n&&x(r,$(r),n)}function lr(n,r){return n&&x(r,B(r),n)}function Tn(n,r){for(var e=-1,t=n==null?0:n.length,f=0,i=[];++e<t;){var a=n[e];r(a,e,n)&&(i[f++]=a)}return i}function hn(){return[]}var cr=Object.prototype,br=cr.propertyIsEnumerable,z=Object.getOwnPropertySymbols,j=z?function(n){return n==null?[]:(n=Object(n),Tn(z(n),function(r){return br.call(n,r)}))}:hn;function dr(n,r){return x(n,j(n),r)}var pr=Object.getOwnPropertySymbols,wn=pr?function(n){for(var r=[];n;)K(r,j(n)),n=Mn(n);return r}:hn;function Ar(n,r){return x(n,wn(n),r)}function On(n,r,e){var t=r(n);return T(n)?t:K(t,e(n))}function G(n){return On(n,$,j)}function yr(n){return On(n,B,wn)}var Tr=Object.prototype,hr=Tr.hasOwnProperty;function wr(n){var r=n.length,e=new n.constructor(r);return r&&typeof n[0]=="string"&&hr.call(n,"index")&&(e.index=n.index,e.input=n.input),e}function Or(n,r){var e=r?on(n.buffer):n.buffer;return new n.constructor(e,n.byteOffset,n.byteLength)}var $r=/\w*$/;function _r(n){var r=new n.constructor(n.source,$r.exec(n));return r.lastIndex=n.lastIndex,r}var V=w?w.prototype:void 0,k=V?V.valueOf:void 0;function Sr(n){return k?Object(k.call(n)):{}}var Er="[object Boolean]",Ir="[object Date]",Pr="[object Map]",vr="[object Number]",Lr="[object RegExp]",Rr="[object Set]",xr="[object String]",mr="[object Symbol]",Cr="[object ArrayBuffer]",Mr="[object DataView]",Fr="[object Float32Array]",Dr="[object Float64Array]",Gr="[object Int8Array]",Br="[object Int16Array]",Ur="[object Int32Array]",Nr="[object Uint8Array]",Kr="[object Uint8ClampedArray]",jr="[object Uint16Array]",Hr="[object Uint32Array]";function qr(n,r,e){var t=n.constructor;switch(r){case Cr:return on(n);case Er:case Ir:return new t(+n);case Mr:return Or(n,e);case Fr:case Dr:case Gr:case Br:case Ur:case Nr:case Kr:case jr:case Hr:return Fn(n,e);case Pr:return new t;case vr:case xr:return new t(n);case Lr:return _r(n);case Rr:return new t;case mr:return Sr(n)}}var Yr="[object Map]";function Zr(n){return S(n)&&E(n)==Yr}var nn=R&&R.isMap,Xr=nn?gn(nn):Zr,Jr="[object Set]";function Qr(n){return S(n)&&E(n)==Jr}var rn=R&&R.isSet,Wr=rn?gn(rn):Qr,zr=1,Vr=2,kr=4,$n="[object Arguments]",ne="[object Array]",re="[object Boolean]",ee="[object Date]",te="[object Error]",_n="[object Function]",ie="[object GeneratorFunction]",fe="[object Map]",ae="[object Number]",Sn="[object Object]",se="[object RegExp]",ue="[object Set]",oe="[object String]",ge="[object Symbol]",le="[object WeakMap]",ce="[object ArrayBuffer]",be="[object DataView]",de="[object Float32Array]",pe="[object Float64Array]",Ae="[object Int8Array]",ye="[object Int16Array]",Te="[object Int32Array]",he="[object Uint8Array]",we="[object Uint8ClampedArray]",Oe="[object Uint16Array]",$e="[object Uint32Array]",g={};g[$n]=g[ne]=g[ce]=g[be]=g[re]=g[ee]=g[de]=g[pe]=g[Ae]=g[ye]=g[Te]=g[fe]=g[ae]=g[Sn]=g[se]=g[ue]=g[oe]=g[ge]=g[he]=g[we]=g[Oe]=g[$e]=!0;g[te]=g[_n]=g[le]=!1;function M(n,r,e,t,f,i){var a,s=r&zr,u=r&Vr,b=r&kr;if(a!==void 0)return a;if(!ln(n))return n;var l=T(n);if(l){if(a=wr(n),!s)return Dn(n,a)}else{var o=E(n),c=o==_n||o==ie;if(D(n))return Gn(n,s);if(o==Sn||o==$n||c&&!f){if(a=u||c?{}:Bn(n),!s)return u?Ar(n,lr(a,n)):dr(n,gr(a,n))}else{if(!g[o])return f?n:{};a=qr(n,o,s)}}i||(i=new _);var h=i.get(n);if(h)return h;i.set(n,a),Wr(n)?n.forEach(function(d){a.add(M(d,r,e,d,n,i))}):Xr(n)&&n.forEach(function(d,p){a.set(p,M(d,r,e,p,n,i))});var A=b?u?yr:G:u?B:$,y=l?void 0:A(n);return pn(y||n,function(d,p){y&&(p=d,d=n[p]),Un(a,p,M(d,r,e,p,n,i))}),a}var _e="__lodash_hash_undefined__";function Se(n){return this.__data__.set(n,_e),this}function Ee(n){return this.__data__.has(n)}function I(n){var r=-1,e=n==null?0:n.length;for(this.__data__=new Nn;++r<e;)this.add(n[r])}I.prototype.add=I.prototype.push=Se;I.prototype.has=Ee;function Ie(n,r){for(var e=-1,t=n==null?0:n.length;++e<t;)if(r(n[e],e,n))return!0;return!1}function En(n,r){return n.has(r)}var Pe=1,ve=2;function In(n,r,e,t,f,i){var a=e&Pe,s=n.length,u=r.length;if(s!=u&&!(a&&u>s))return!1;var b=i.get(n),l=i.get(r);if(b&&l)return b==r&&l==n;var o=-1,c=!0,h=e&ve?new I:void 0;for(i.set(n,r),i.set(r,n);++o<s;){var A=n[o],y=r[o];if(t)var d=a?t(y,A,o,r,n,i):t(A,y,o,n,r,i);if(d!==void 0){if(d)continue;c=!1;break}if(h){if(!Ie(r,function(p,O){if(!En(h,O)&&(A===p||f(A,p,e,t,i)))return h.push(O)})){c=!1;break}}else if(!(A===y||f(A,y,e,t,i))){c=!1;break}}return i.delete(n),i.delete(r),c}function Le(n){var r=-1,e=Array(n.size);return n.forEach(function(t,f){e[++r]=[f,t]}),e}function H(n){var r=-1,e=Array(n.size);return n.forEach(function(t){e[++r]=t}),e}var Re=1,xe=2,me="[object Boolean]",Ce="[object Date]",Me="[object Error]",Fe="[object Map]",De="[object Number]",Ge="[object RegExp]",Be="[object Set]",Ue="[object String]",Ne="[object Symbol]",Ke="[object ArrayBuffer]",je="[object DataView]",en=w?w.prototype:void 0,F=en?en.valueOf:void 0;function He(n,r,e,t,f,i,a){switch(e){case je:if(n.byteLength!=r.byteLength||n.byteOffset!=r.byteOffset)return!1;n=n.buffer,r=r.buffer;case Ke:return!(n.byteLength!=r.byteLength||!i(new X(n),new X(r)));case me:case Ce:case De:return Kn(+n,+r);case Me:return n.name==r.name&&n.message==r.message;case Ge:case Ue:return n==r+"";case Fe:var s=Le;case Be:var u=t&Re;if(s||(s=H),n.size!=r.size&&!u)return!1;var b=a.get(n);if(b)return b==r;t|=xe,a.set(n,r);var l=In(s(n),s(r),t,f,i,a);return a.delete(n),l;case Ne:if(F)return F.call(n)==F.call(r)}return!1}var qe=1,Ye=Object.prototype,Ze=Ye.hasOwnProperty;function Xe(n,r,e,t,f,i){var a=e&qe,s=G(n),u=s.length,b=G(r),l=b.length;if(u!=l&&!a)return!1;for(var o=u;o--;){var c=s[o];if(!(a?c in r:Ze.call(r,c)))return!1}var h=i.get(n),A=i.get(r);if(h&&A)return h==r&&A==n;var y=!0;i.set(n,r),i.set(r,n);for(var d=a;++o<u;){c=s[o];var p=n[c],O=r[c];if(t)var Z=a?t(O,p,c,r,n,i):t(p,O,c,n,r,i);if(!(Z===void 0?p===O||f(p,O,e,t,i):Z)){y=!1;break}d||(d=c=="constructor")}if(y&&!d){var P=n.constructor,v=r.constructor;P!=v&&"constructor"in n&&"constructor"in r&&!(typeof P=="function"&&P instanceof P&&typeof v=="function"&&v instanceof v)&&(y=!1)}return i.delete(n),i.delete(r),y}var Je=1,tn="[object Arguments]",fn="[object Array]",L="[object Object]",Qe=Object.prototype,an=Qe.hasOwnProperty;function We(n,r,e,t,f,i){var a=T(n),s=T(r),u=a?fn:E(n),b=s?fn:E(r);u=u==tn?L:u,b=b==tn?L:b;var l=u==L,o=b==L,c=u==b;if(c&&D(n)){if(!D(r))return!1;a=!0,l=!1}if(c&&!l)return i||(i=new _),a||jn(n)?In(n,r,e,t,f,i):He(n,r,u,e,t,f,i);if(!(e&Je)){var h=l&&an.call(n,"__wrapped__"),A=o&&an.call(r,"__wrapped__");if(h||A){var y=h?n.value():n,d=A?r.value():r;return i||(i=new _),f(y,d,e,t,i)}}return c?(i||(i=new _),Xe(n,r,e,t,f,i)):!1}function q(n,r,e,t,f){return n===r?!0:n==null||r==null||!S(n)&&!S(r)?n!==n&&r!==r:We(n,r,e,t,q,f)}var ze=1,Ve=2;function ke(n,r,e,t){var f=e.length,i=f;if(n==null)return!i;for(n=Object(n);f--;){var a=e[f];if(a[2]?a[1]!==n[a[0]]:!(a[0]in n))return!1}for(;++f<i;){a=e[f];var s=a[0],u=n[s],b=a[1];if(a[2]){if(u===void 0&&!(s in n))return!1}else{var l=new _,o;if(!(o===void 0?q(b,u,ze|Ve,t,l):o))return!1}}return!0}function Pn(n){return n===n&&!ln(n)}function nt(n){for(var r=$(n),e=r.length;e--;){var t=r[e],f=n[t];r[e]=[t,f,Pn(f)]}return r}function vn(n,r){return function(e){return e==null?!1:e[n]===r&&(r!==void 0||n in Object(e))}}function rt(n){var r=nt(n);return r.length==1&&r[0][2]?vn(r[0][0],r[0][1]):function(e){return e===n||ke(e,n,r)}}function et(n,r){return n!=null&&r in Object(n)}function tt(n,r,e){r=An(r,n);for(var t=-1,f=r.length,i=!1;++t<f;){var a=m(r[t]);if(!(i=n!=null&&e(n,a)))break;n=n[a]}return i||++t!=f?i:(f=n==null?0:n.length,!!f&&Hn(f)&&qn(a,f)&&(T(n)||un(n)))}function it(n,r){return n!=null&&tt(n,r,et)}var ft=1,at=2;function st(n,r){return N(n)&&Pn(r)?vn(m(n),r):function(e){var t=sr(e,n);return t===void 0&&t===r?it(e,n):q(r,t,ft|at)}}function ut(n){return function(r){return r?.[n]}}function ot(n){return function(r){return yn(r,n)}}function gt(n){return N(n)?ut(m(n)):ot(n)}function Ln(n){return typeof n=="function"?n:n==null?cn:typeof n=="object"?T(n)?st(n[0],n[1]):rt(n):gt(n)}function lt(n,r){return n&&Yn(n,r,$)}function ct(n,r){return function(e,t){if(e==null)return e;if(!sn(e))return n(e,t);for(var f=e.length,i=-1,a=Object(e);++i<f&&t(a[i],i,a)!==!1;);return e}}var Y=ct(lt);function bt(n){return typeof n=="function"?n:cn}function $t(n,r){var e=T(n)?pn:Y;return e(n,bt(r))}function dt(n,r){var e=[];return Y(n,function(t,f,i){r(t,f,i)&&e.push(t)}),e}function _t(n,r){var e=T(n)?Tn:dt;return e(n,Ln(r))}function pt(n,r){return bn(r,function(e){return n[e]})}function St(n){return n==null?[]:pt(n,$(n))}function Et(n){return n===void 0}function At(n,r,e,t,f){return f(n,function(i,a,s){e=t?(t=!1,i):r(e,i,a,s)}),e}function It(n,r,e){var t=T(n)?or:At,f=arguments.length<3;return t(n,Ln(r),e,f,Y)}var yt=1/0,Tt=C&&1/H(new C([,-0]))[1]==yt?function(n){return new C(n)}:Xn,ht=200;function Pt(n,r,e){var t=-1,f=Vn,i=n.length,a=!0,s=[],u=s;if(i>=ht){var b=r?null:Tt(n);if(b)return H(b);a=!1,f=En,u=new I}else u=r?[]:s;n:for(;++t<i;){var l=n[t],o=r?r(l):l;if(l=l!==0?l:0,a&&o===o){for(var c=u.length;c--;)if(u[c]===o)continue n;r&&u.push(o),s.push(l)}else f(u,o,e)||(u!==s&&u.push(o),s.push(l))}return s}export{Tn as A,dt as B,Ie as C,Xn as D,I as S,Pt as a,M as b,Ot as c,$t as d,U as e,_t as f,Ln as g,Jn as h,Et as i,Y as j,$ as k,bn as l,tt as m,An as n,yn as o,bt as p,lt as q,It as r,it as s,m as t,ar as u,St as v,Vn as w,En as x,zn as y,yr as z};
@@ -1 +0,0 @@
1
- import{a0 as ln,a1 as an,a2 as H,a3 as q,a4 as B,a5 as un,a6 as y,a7 as tn,a8 as L,a9 as _,aa as rn,ab as o,ac as on,ad as sn,ae as fn}from"./mermaid-block-mMz1yMUG.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,I,D,v,A,C,a){var O=I-l,i=D-h,n=C-v,d=a-A,u=d*O-n*i;if(!(u*u<y))return u=(n*(h-A)-d*(l-v))/u,[l+u*O,h+u*i]}function W(l,h,I,D,v,A,C){var a=l-I,O=h-D,i=(C?A:-A)/L(a*a+O*O),n=i*O,d=-i*a,u=l+n,s=h+d,f=I+n,c=D+d,F=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,R=m*m+g*g,T=v-A,P=u*c-f*s,S=(g<0?-1:1)*L(on(0,T*T*R-P*P)),j=(P*g-m*S)/R,z=(-P*m-g*S)/R,w=(P*g+m*S)/R,p=(-P*m+g*S)/R,x=j-F,e=z-t,r=w-F,G=p-t;return x*x+e*e>r*r+G*G&&(j=w,z=p),{cx:j,cy:z,x01:-n,y01:-d,x11:j*(v/T-1),y11:z*(v/T-1)}}function hn(){var l=cn,h=yn,I=B(0),D=null,v=gn,A=dn,C=mn,a=null,O=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-un,c=A.apply(this,arguments)-un,F=rn(c-f),t=c>f;if(a||(a=n=O()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(F>tn-y)a.moveTo(s*H(f),s*q(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*H(c),u*q(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,R=f,T=c,P=F,S=F,j=C.apply(this,arguments)/2,z=j>y&&(D?+D.apply(this,arguments):L(u*u+s*s)),w=_(rn(s-u)/2,+I.apply(this,arguments)),p=w,x=w,e,r;if(z>y){var G=sn(z/u*q(j)),M=sn(z/s*q(j));(P-=G*2)>y?(G*=t?1:-1,R+=G,T-=G):(P=0,R=T=(f+c)/2),(S-=M*2)>y?(M*=t?1:-1,m+=M,g-=M):(S=0,m=g=(f+c)/2)}var J=s*H(m),K=s*q(m),N=u*H(T),Q=u*q(T);if(w>y){var U=s*H(g),V=s*q(g),X=u*H(R),Y=u*q(R),E;if(F<an)if(E=pn(J,K,X,Y,U,V,N,Q)){var Z=J-E[0],$=K-E[1],b=U-E[0],k=V-E[1],nn=1/q(fn((Z*b+$*k)/(L(Z*Z+$*$)*L(b*b+k*k)))/2),en=L(E[0]*E[0]+E[1]*E[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}S>y?x>y?(e=W(X,Y,J,K,s,x,t),r=W(U,V,N,Q,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(J,K),a.arc(0,0,s,m,g,!t)):a.moveTo(J,K),!(u>y)||!(P>y)?a.lineTo(N,Q):p>y?(e=W(N,Q,U,V,u,-p,t),r=W(J,K,X,Y,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-an/2;return[H(d)*n,q(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:B(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:B(+n),i):h},i.cornerRadius=function(n){return arguments.length?(I=typeof n=="function"?n:B(+n),i):I},i.padRadius=function(n){return arguments.length?(D=n==null?null:typeof n=="function"?n:B(+n),i):D},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:B(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:B(+n),i):A},i.padAngle=function(n){return arguments.length?(C=typeof n=="function"?n:B(+n),i):C},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};