@flanksource/clicky-ui 0.3.12 → 0.3.13

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 (52) hide show
  1. package/dist/data/ai/SessionViewer.input.cjs +2 -1
  2. package/dist/data/ai/SessionViewer.input.cjs.map +1 -1
  3. package/dist/data/ai/SessionViewer.input.d.ts.map +1 -1
  4. package/dist/data/ai/SessionViewer.input.js +2 -1
  5. package/dist/data/ai/SessionViewer.input.js.map +1 -1
  6. package/dist/data/diagnostics/error-diagnostics.cjs +38 -6
  7. package/dist/data/diagnostics/error-diagnostics.cjs.map +1 -1
  8. package/dist/data/diagnostics/error-diagnostics.js +38 -6
  9. package/dist/data/diagnostics/error-diagnostics.js.map +1 -1
  10. package/dist/data/diagnostics/jvm-stacktrace.cjs +35 -3
  11. package/dist/data/diagnostics/jvm-stacktrace.cjs.map +1 -1
  12. package/dist/data/diagnostics/jvm-stacktrace.d.ts.map +1 -1
  13. package/dist/data/diagnostics/jvm-stacktrace.js +35 -3
  14. package/dist/data/diagnostics/jvm-stacktrace.js.map +1 -1
  15. package/dist/data/diagnostics/stacktrace-parse.cjs +19 -5
  16. package/dist/data/diagnostics/stacktrace-parse.cjs.map +1 -1
  17. package/dist/data/diagnostics/stacktrace-parse.d.ts.map +1 -1
  18. package/dist/data/diagnostics/stacktrace-parse.js +19 -5
  19. package/dist/data/diagnostics/stacktrace-parse.js.map +1 -1
  20. package/dist/data/diagnostics/stacktrace.cjs +15 -2
  21. package/dist/data/diagnostics/stacktrace.cjs.map +1 -1
  22. package/dist/data/diagnostics/stacktrace.js +15 -2
  23. package/dist/data/diagnostics/stacktrace.js.map +1 -1
  24. package/dist/data/test-runner/routePath.cjs +3 -1
  25. package/dist/data/test-runner/routePath.cjs.map +1 -1
  26. package/dist/data/test-runner/routePath.d.ts.map +1 -1
  27. package/dist/data/test-runner/routePath.js +3 -1
  28. package/dist/data/test-runner/routePath.js.map +1 -1
  29. package/dist/data/version-info.cjs +3 -3
  30. package/dist/data/version-info.js +3 -3
  31. package/dist/lib/string.cjs +15 -0
  32. package/dist/lib/string.cjs.map +1 -0
  33. package/dist/lib/string.d.ts +3 -0
  34. package/dist/lib/string.d.ts.map +1 -0
  35. package/dist/lib/string.js +15 -0
  36. package/dist/lib/string.js.map +1 -0
  37. package/dist/rpc/OperationActionDialog.cjs +4 -10
  38. package/dist/rpc/OperationActionDialog.cjs.map +1 -1
  39. package/dist/rpc/OperationActionDialog.d.ts.map +1 -1
  40. package/dist/rpc/OperationActionDialog.js +2 -8
  41. package/dist/rpc/OperationActionDialog.js.map +1 -1
  42. package/dist/rpc/OperationCommandPage.cjs +3 -2
  43. package/dist/rpc/OperationCommandPage.cjs.map +1 -1
  44. package/dist/rpc/OperationCommandPage.d.ts.map +1 -1
  45. package/dist/rpc/OperationCommandPage.js +3 -2
  46. package/dist/rpc/OperationCommandPage.js.map +1 -1
  47. package/dist/rpc/apiClient.cjs +2 -1
  48. package/dist/rpc/apiClient.cjs.map +1 -1
  49. package/dist/rpc/apiClient.d.ts.map +1 -1
  50. package/dist/rpc/apiClient.js +2 -1
  51. package/dist/rpc/apiClient.js.map +1 -1
  52. package/package.json +1 -1
@@ -123,9 +123,41 @@ function parseThreadBlock(block) {
123
123
  };
124
124
  }
125
125
  function extractHeaderStateTrail(rest) {
126
- var _a, _b;
127
- const tail = rest.match(/nid=0x[0-9a-f]+\s+(?<desc>[^[]+?)(?:\s+\[0x[0-9a-f]+\])?\s*$/i);
128
- return (_b = (_a = tail == null ? void 0 : tail.groups) == null ? void 0 : _a.desc) == null ? void 0 : _b.trim();
126
+ const lower = rest.toLowerCase();
127
+ const marker = "nid=0x";
128
+ const markerStart = lower.indexOf(marker);
129
+ if (markerStart === -1) return void 0;
130
+ let stateStart = markerStart + marker.length;
131
+ const hexStart = stateStart;
132
+ while (isHexDigit(rest[stateStart])) stateStart++;
133
+ if (stateStart === hexStart || !isWhitespace(rest[stateStart])) return void 0;
134
+ while (isWhitespace(rest[stateStart])) stateStart++;
135
+ let stateEnd = rest.length;
136
+ while (stateEnd > stateStart && isWhitespace(rest[stateEnd - 1])) stateEnd--;
137
+ if (rest[stateEnd - 1] === "]") {
138
+ const addressStart = lower.lastIndexOf("[0x", stateEnd - 1);
139
+ if (addressStart > stateStart && isHexSequence(rest, addressStart + 3, stateEnd - 1) && isWhitespace(rest[addressStart - 1])) {
140
+ stateEnd = addressStart;
141
+ while (stateEnd > stateStart && isWhitespace(rest[stateEnd - 1])) stateEnd--;
142
+ }
143
+ }
144
+ const state = rest.slice(stateStart, stateEnd);
145
+ return state || void 0;
146
+ }
147
+ function isHexDigit(value) {
148
+ if (value === void 0) return false;
149
+ const code = value.toLowerCase().charCodeAt(0);
150
+ return code >= 48 && code <= 57 || code >= 97 && code <= 102;
151
+ }
152
+ function isHexSequence(value, start, end) {
153
+ if (start >= end) return false;
154
+ for (let index = start; index < end; index++) {
155
+ if (!isHexDigit(value[index])) return false;
156
+ }
157
+ return true;
158
+ }
159
+ function isWhitespace(value) {
160
+ return value !== void 0 && value.trim() === "";
129
161
  }
130
162
  function deriveSyntheticId(name, rest) {
131
163
  const tid = /\btid=(0x[0-9a-f]+)/i.exec(rest);
@@ -1 +1 @@
1
- {"version":3,"file":"jvm-stacktrace.js","sources":["../../../src/data/diagnostics/jvm-stacktrace.ts"],"sourcesContent":["export interface ParsedThreadFrame {\n functionName: string;\n displayName: string;\n file?: string;\n line?: number;\n location?: string;\n kind: \"frame\" | \"locked\" | \"waiting_on\" | \"waiting_to_lock\" | \"parking\";\n runtime: boolean;\n nativeMethod: boolean;\n annotationText?: string;\n // Optional fully-qualified class name (e.g. \"com.example.admin.pas.Foo\").\n // Lets renderers filter or group by package without re-splitting\n // functionName. Populated by parseJavaStackTrace; left undefined by the\n // thread-dump parser since its frames are raw \"fn(src)\" pairs.\n class?: string;\n method?: string;\n // Optional source-context window populated by clicky.SourceResolver\n // (or its TS equivalent). sourceLines[line - sourceStartLine] is the focal\n // line. Empty/undefined when no resolver was attached.\n sourceLines?: string[];\n sourceLineNumbers?: number[];\n sourceStartLine?: number;\n sourceLanguage?: string;\n}\n\nexport interface ParsedThread {\n id: number;\n nid?: string;\n name: string;\n state: string;\n rawState: string;\n priority?: number;\n daemon?: boolean;\n frames: ParsedThreadFrame[];\n raw: string;\n userFrameCount: number;\n topFunction?: string;\n searchText: string;\n}\n\nconst headerRe = /^\"(?<name>[^\"]*)\"(?<rest>.*)$/;\nconst stateRe = /^\\s*java\\.lang\\.Thread\\.State:\\s+(?<state>[A-Z_]+)(?:\\s+\\((?<sub>[^)]+)\\))?/;\nconst frameRe = /^\\s*at\\s+(?<fn>[^\\s(]+)\\((?<src>[^)]+)\\)\\s*$/;\nconst srcLineRe = /^(?<file>[^:]+):(?<line>\\d+)$/;\nconst annotationRe =\n /^\\s*-\\s+(?<kind>locked|waiting on|waiting to lock|parking to wait for)\\b(?<rest>.*)$/;\n\nexport function parseJvmThreadDump(text: string): ParsedThread[] {\n const trimmed = text.trim();\n if (!trimmed) return [];\n\n // Split on blank lines OR on a new `\"...\"` header that follows immediately.\n const blocks = splitIntoThreadBlocks(trimmed);\n const threads: ParsedThread[] = [];\n\n for (const block of blocks) {\n const parsed = parseThreadBlock(block);\n if (parsed) threads.push(parsed);\n }\n return threads;\n}\n\nfunction splitIntoThreadBlocks(text: string): string[] {\n const out: string[] = [];\n let current: string[] = [];\n const lines = text.split(\"\\n\");\n for (const raw of lines) {\n const line = raw.replace(/\\r$/, \"\");\n if (line.startsWith('\"') && current.length > 0) {\n out.push(current.join(\"\\n\"));\n current = [line];\n } else {\n current.push(line);\n }\n }\n if (current.length > 0) out.push(current.join(\"\\n\"));\n return out;\n}\n\nfunction parseThreadBlock(block: string): ParsedThread | null {\n const lines = block.split(\"\\n\");\n const header = lines[0]?.trim() ?? \"\";\n const headerMatch = headerRe.exec(header);\n if (!headerMatch?.groups) return null;\n\n const name = headerMatch.groups.name;\n if (!name) return null;\n const rest = headerMatch.groups.rest ?? \"\";\n const idMatch = /#(\\d+)\\b/.exec(rest);\n const prioMatch = /\\bprio=(\\d+)/.exec(rest);\n const nidMatch = /\\bnid=(0x[0-9a-f]+)/i.exec(rest);\n const daemon = /\\bdaemon\\b/.test(rest);\n const headerStateTrail = extractHeaderStateTrail(rest);\n\n let rawState = headerStateTrail ?? \"\";\n let state = normalizeJvmState(rawState);\n const frames: ParsedThreadFrame[] = [];\n\n for (let i = 1; i < lines.length; i++) {\n const line = lines[i];\n if (line == null) continue;\n if (!line.trim()) continue;\n\n const stateMatch = stateRe.exec(line);\n if (stateMatch?.groups) {\n const primary = stateMatch.groups.state;\n if (!primary) continue;\n const sub = stateMatch.groups.sub;\n rawState = sub ? `${primary} (${sub})` : primary;\n state = normalizeJvmState(primary);\n continue;\n }\n\n const frameMatch = frameRe.exec(line);\n if (frameMatch?.groups) {\n const functionName = frameMatch.groups.fn;\n const src = frameMatch.groups.src;\n if (!functionName || !src) continue;\n const frame: ParsedThreadFrame = {\n functionName,\n displayName: sanitizeJvmFunctionName(functionName),\n kind: \"frame\",\n runtime: isJvmRuntimeFrame(functionName),\n nativeMethod: src === \"Native Method\",\n ...splitClassMethod(functionName),\n };\n const srcMatch = srcLineRe.exec(src);\n if (srcMatch?.groups?.file && srcMatch.groups.line) {\n frame.file = srcMatch.groups.file;\n frame.line = Number(srcMatch.groups.line);\n frame.location = `${frame.file}:${frame.line}`;\n } else if (src === \"Native Method\") {\n frame.location = \"Native Method\";\n } else {\n frame.location = src;\n }\n frames.push(frame);\n continue;\n }\n\n const annoMatch = annotationRe.exec(line);\n if (annoMatch?.groups?.kind) {\n const annotationKind = annoMatch.groups.kind;\n const kind = mapAnnotationKind(annotationKind);\n frames.push({\n functionName: annotationKind,\n displayName: annotationKind,\n kind,\n runtime: false,\n nativeMethod: false,\n annotationText: (annoMatch.groups.rest ?? \"\").trim(),\n });\n continue;\n }\n }\n\n const userFrameCount = frames.filter((f) => f.kind === \"frame\" && !f.runtime).length;\n const topFunction = frames.find((f) => f.kind === \"frame\")?.functionName;\n const searchText = [\n header,\n ...frames.map((f) => `${f.functionName} ${f.location ?? \"\"} ${f.annotationText ?? \"\"}`),\n ]\n .join(\"\\n\")\n .toLowerCase();\n\n return {\n id: idMatch ? Number(idMatch[1]) : deriveSyntheticId(name, rest),\n name,\n state: state || \"unknown\",\n rawState: rawState || \"\",\n daemon,\n frames,\n raw: block,\n userFrameCount,\n searchText,\n ...(nidMatch?.[1] !== undefined ? { nid: nidMatch[1] } : {}),\n ...(prioMatch?.[1] !== undefined ? { priority: Number(prioMatch[1]) } : {}),\n ...(topFunction !== undefined ? { topFunction } : {}),\n };\n}\n\nfunction extractHeaderStateTrail(rest: string): string | undefined {\n // Example header tails: \"... nid=0x1903 waiting on condition [0x...]\"\n // Capture the descriptor between nid=... and the trailing [address].\n const tail = rest.match(/nid=0x[0-9a-f]+\\s+(?<desc>[^[]+?)(?:\\s+\\[0x[0-9a-f]+\\])?\\s*$/i);\n return tail?.groups?.desc?.trim();\n}\n\nfunction deriveSyntheticId(name: string, rest: string): number {\n const tid = /\\btid=(0x[0-9a-f]+)/i.exec(rest);\n if (tid?.[1]) {\n // stable non-negative int derived from tid hex suffix\n const hex = tid[1].slice(2);\n const n = Number.parseInt(hex.slice(-8), 16);\n if (Number.isFinite(n)) return n;\n }\n // Fallback: hash of name\n let h = 0;\n for (let i = 0; i < name.length; i++) h = (h * 31 + name.charCodeAt(i)) | 0;\n return Math.abs(h);\n}\n\nfunction mapAnnotationKind(kind: string): ParsedThreadFrame[\"kind\"] {\n switch (kind) {\n case \"locked\":\n return \"locked\";\n case \"waiting on\":\n return \"waiting_on\";\n case \"waiting to lock\":\n return \"waiting_to_lock\";\n case \"parking to wait for\":\n return \"parking\";\n default:\n return \"frame\";\n }\n}\n\nexport function countThreadsByState(threads: ParsedThread[]): Map<string, number> {\n const counts = new Map<string, number>();\n for (const t of threads) counts.set(t.state, (counts.get(t.state) ?? 0) + 1);\n return counts;\n}\n\nfunction normalizeJvmState(value: string): string {\n if (!value) return \"\";\n const upper = value.trim().toUpperCase();\n if (upper.startsWith(\"RUNNABLE\")) return \"runnable\";\n if (upper.startsWith(\"TIMED_WAITING\")) return \"timed_waiting\";\n if (upper.startsWith(\"WAITING\")) return \"waiting\";\n if (upper.startsWith(\"BLOCKED\")) return \"blocked\";\n if (upper.startsWith(\"NEW\")) return \"new\";\n if (upper.startsWith(\"TERMINATED\")) return \"terminated\";\n // Header fragments like \"waiting on condition\", \"runnable\", \"sleeping\"\n const lower = value.trim().toLowerCase();\n if (lower.includes(\"runnable\")) return \"runnable\";\n if (lower.includes(\"waiting on condition\") || lower.includes(\"sleeping\")) return \"timed_waiting\";\n if (lower.includes(\"waiting\")) return \"waiting\";\n if (lower.includes(\"blocked\")) return \"blocked\";\n return lower.split(/\\s+/)[0] ?? \"\";\n}\n\nconst runtimePrefixes = [\"java.\", \"javax.\", \"sun.\", \"jdk.\", \"com.sun.\", \"or\" + \"acle.jrockit.\"];\nfunction isJvmRuntimeFrame(functionName: string): boolean {\n return runtimePrefixes.some((p) => functionName.startsWith(p));\n}\n\n// splitClassMethod separates a JVM frame's `fully.qualified.Class.method` into\n// its class and method. The method is the last dot segment; everything before\n// is the (possibly inner) class. Returns no fields when the name has no dot, so\n// frames like a bare `<init>` are left without a class rather than guessed.\nfunction splitClassMethod(functionName: string): { class?: string; method?: string } {\n const lastDot = functionName.lastIndexOf(\".\");\n if (lastDot <= 0) return {};\n return {\n class: functionName.slice(0, lastDot),\n method: functionName.slice(lastDot + 1),\n };\n}\n\nfunction sanitizeJvmFunctionName(functionName: string): string {\n // e.g. \"com.example.App$Inner.method\" → \"App$Inner.method\"\n const parts = functionName.split(\".\");\n if (parts.length < 2) return functionName;\n const method = parts[parts.length - 1];\n const cls = parts[parts.length - 2];\n return `${cls}.${method}`;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"jvm-stacktrace.js","sources":["../../../src/data/diagnostics/jvm-stacktrace.ts"],"sourcesContent":["export interface ParsedThreadFrame {\n functionName: string;\n displayName: string;\n file?: string;\n line?: number;\n location?: string;\n kind: \"frame\" | \"locked\" | \"waiting_on\" | \"waiting_to_lock\" | \"parking\";\n runtime: boolean;\n nativeMethod: boolean;\n annotationText?: string;\n // Optional fully-qualified class name (e.g. \"com.example.admin.pas.Foo\").\n // Lets renderers filter or group by package without re-splitting\n // functionName. Populated by parseJavaStackTrace; left undefined by the\n // thread-dump parser since its frames are raw \"fn(src)\" pairs.\n class?: string;\n method?: string;\n // Optional source-context window populated by clicky.SourceResolver\n // (or its TS equivalent). sourceLines[line - sourceStartLine] is the focal\n // line. Empty/undefined when no resolver was attached.\n sourceLines?: string[];\n sourceLineNumbers?: number[];\n sourceStartLine?: number;\n sourceLanguage?: string;\n}\n\nexport interface ParsedThread {\n id: number;\n nid?: string;\n name: string;\n state: string;\n rawState: string;\n priority?: number;\n daemon?: boolean;\n frames: ParsedThreadFrame[];\n raw: string;\n userFrameCount: number;\n topFunction?: string;\n searchText: string;\n}\n\nconst headerRe = /^\"(?<name>[^\"]*)\"(?<rest>.*)$/;\nconst stateRe = /^\\s*java\\.lang\\.Thread\\.State:\\s+(?<state>[A-Z_]+)(?:\\s+\\((?<sub>[^)]+)\\))?/;\nconst frameRe = /^\\s*at\\s+(?<fn>[^\\s(]+)\\((?<src>[^)]+)\\)\\s*$/;\nconst srcLineRe = /^(?<file>[^:]+):(?<line>\\d+)$/;\nconst annotationRe =\n /^\\s*-\\s+(?<kind>locked|waiting on|waiting to lock|parking to wait for)\\b(?<rest>.*)$/;\n\nexport function parseJvmThreadDump(text: string): ParsedThread[] {\n const trimmed = text.trim();\n if (!trimmed) return [];\n\n // Split on blank lines OR on a new `\"...\"` header that follows immediately.\n const blocks = splitIntoThreadBlocks(trimmed);\n const threads: ParsedThread[] = [];\n\n for (const block of blocks) {\n const parsed = parseThreadBlock(block);\n if (parsed) threads.push(parsed);\n }\n return threads;\n}\n\nfunction splitIntoThreadBlocks(text: string): string[] {\n const out: string[] = [];\n let current: string[] = [];\n const lines = text.split(\"\\n\");\n for (const raw of lines) {\n const line = raw.replace(/\\r$/, \"\");\n if (line.startsWith('\"') && current.length > 0) {\n out.push(current.join(\"\\n\"));\n current = [line];\n } else {\n current.push(line);\n }\n }\n if (current.length > 0) out.push(current.join(\"\\n\"));\n return out;\n}\n\nfunction parseThreadBlock(block: string): ParsedThread | null {\n const lines = block.split(\"\\n\");\n const header = lines[0]?.trim() ?? \"\";\n const headerMatch = headerRe.exec(header);\n if (!headerMatch?.groups) return null;\n\n const name = headerMatch.groups.name;\n if (!name) return null;\n const rest = headerMatch.groups.rest ?? \"\";\n const idMatch = /#(\\d+)\\b/.exec(rest);\n const prioMatch = /\\bprio=(\\d+)/.exec(rest);\n const nidMatch = /\\bnid=(0x[0-9a-f]+)/i.exec(rest);\n const daemon = /\\bdaemon\\b/.test(rest);\n const headerStateTrail = extractHeaderStateTrail(rest);\n\n let rawState = headerStateTrail ?? \"\";\n let state = normalizeJvmState(rawState);\n const frames: ParsedThreadFrame[] = [];\n\n for (let i = 1; i < lines.length; i++) {\n const line = lines[i];\n if (line == null) continue;\n if (!line.trim()) continue;\n\n const stateMatch = stateRe.exec(line);\n if (stateMatch?.groups) {\n const primary = stateMatch.groups.state;\n if (!primary) continue;\n const sub = stateMatch.groups.sub;\n rawState = sub ? `${primary} (${sub})` : primary;\n state = normalizeJvmState(primary);\n continue;\n }\n\n const frameMatch = frameRe.exec(line);\n if (frameMatch?.groups) {\n const functionName = frameMatch.groups.fn;\n const src = frameMatch.groups.src;\n if (!functionName || !src) continue;\n const frame: ParsedThreadFrame = {\n functionName,\n displayName: sanitizeJvmFunctionName(functionName),\n kind: \"frame\",\n runtime: isJvmRuntimeFrame(functionName),\n nativeMethod: src === \"Native Method\",\n ...splitClassMethod(functionName),\n };\n const srcMatch = srcLineRe.exec(src);\n if (srcMatch?.groups?.file && srcMatch.groups.line) {\n frame.file = srcMatch.groups.file;\n frame.line = Number(srcMatch.groups.line);\n frame.location = `${frame.file}:${frame.line}`;\n } else if (src === \"Native Method\") {\n frame.location = \"Native Method\";\n } else {\n frame.location = src;\n }\n frames.push(frame);\n continue;\n }\n\n const annoMatch = annotationRe.exec(line);\n if (annoMatch?.groups?.kind) {\n const annotationKind = annoMatch.groups.kind;\n const kind = mapAnnotationKind(annotationKind);\n frames.push({\n functionName: annotationKind,\n displayName: annotationKind,\n kind,\n runtime: false,\n nativeMethod: false,\n annotationText: (annoMatch.groups.rest ?? \"\").trim(),\n });\n continue;\n }\n }\n\n const userFrameCount = frames.filter((f) => f.kind === \"frame\" && !f.runtime).length;\n const topFunction = frames.find((f) => f.kind === \"frame\")?.functionName;\n const searchText = [\n header,\n ...frames.map((f) => `${f.functionName} ${f.location ?? \"\"} ${f.annotationText ?? \"\"}`),\n ]\n .join(\"\\n\")\n .toLowerCase();\n\n return {\n id: idMatch ? Number(idMatch[1]) : deriveSyntheticId(name, rest),\n name,\n state: state || \"unknown\",\n rawState: rawState || \"\",\n daemon,\n frames,\n raw: block,\n userFrameCount,\n searchText,\n ...(nidMatch?.[1] !== undefined ? { nid: nidMatch[1] } : {}),\n ...(prioMatch?.[1] !== undefined ? { priority: Number(prioMatch[1]) } : {}),\n ...(topFunction !== undefined ? { topFunction } : {}),\n };\n}\n\nfunction extractHeaderStateTrail(rest: string): string | undefined {\n const lower = rest.toLowerCase();\n const marker = \"nid=0x\";\n const markerStart = lower.indexOf(marker);\n if (markerStart === -1) return undefined;\n\n let stateStart = markerStart + marker.length;\n const hexStart = stateStart;\n while (isHexDigit(rest[stateStart])) stateStart++;\n if (stateStart === hexStart || !isWhitespace(rest[stateStart])) return undefined;\n while (isWhitespace(rest[stateStart])) stateStart++;\n\n let stateEnd = rest.length;\n while (stateEnd > stateStart && isWhitespace(rest[stateEnd - 1])) stateEnd--;\n if (rest[stateEnd - 1] === \"]\") {\n const addressStart = lower.lastIndexOf(\"[0x\", stateEnd - 1);\n if (\n addressStart > stateStart &&\n isHexSequence(rest, addressStart + 3, stateEnd - 1) &&\n isWhitespace(rest[addressStart - 1])\n ) {\n stateEnd = addressStart;\n while (stateEnd > stateStart && isWhitespace(rest[stateEnd - 1])) stateEnd--;\n }\n }\n\n const state = rest.slice(stateStart, stateEnd);\n return state || undefined;\n}\n\nfunction isHexDigit(value: string | undefined): boolean {\n if (value === undefined) return false;\n const code = value.toLowerCase().charCodeAt(0);\n return (code >= 48 && code <= 57) || (code >= 97 && code <= 102);\n}\n\nfunction isHexSequence(value: string, start: number, end: number): boolean {\n if (start >= end) return false;\n for (let index = start; index < end; index++) {\n if (!isHexDigit(value[index])) return false;\n }\n return true;\n}\n\nfunction isWhitespace(value: string | undefined): boolean {\n return value !== undefined && value.trim() === \"\";\n}\n\nfunction deriveSyntheticId(name: string, rest: string): number {\n const tid = /\\btid=(0x[0-9a-f]+)/i.exec(rest);\n if (tid?.[1]) {\n // stable non-negative int derived from tid hex suffix\n const hex = tid[1].slice(2);\n const n = Number.parseInt(hex.slice(-8), 16);\n if (Number.isFinite(n)) return n;\n }\n // Fallback: hash of name\n let h = 0;\n for (let i = 0; i < name.length; i++) h = (h * 31 + name.charCodeAt(i)) | 0;\n return Math.abs(h);\n}\n\nfunction mapAnnotationKind(kind: string): ParsedThreadFrame[\"kind\"] {\n switch (kind) {\n case \"locked\":\n return \"locked\";\n case \"waiting on\":\n return \"waiting_on\";\n case \"waiting to lock\":\n return \"waiting_to_lock\";\n case \"parking to wait for\":\n return \"parking\";\n default:\n return \"frame\";\n }\n}\n\nexport function countThreadsByState(threads: ParsedThread[]): Map<string, number> {\n const counts = new Map<string, number>();\n for (const t of threads) counts.set(t.state, (counts.get(t.state) ?? 0) + 1);\n return counts;\n}\n\nfunction normalizeJvmState(value: string): string {\n if (!value) return \"\";\n const upper = value.trim().toUpperCase();\n if (upper.startsWith(\"RUNNABLE\")) return \"runnable\";\n if (upper.startsWith(\"TIMED_WAITING\")) return \"timed_waiting\";\n if (upper.startsWith(\"WAITING\")) return \"waiting\";\n if (upper.startsWith(\"BLOCKED\")) return \"blocked\";\n if (upper.startsWith(\"NEW\")) return \"new\";\n if (upper.startsWith(\"TERMINATED\")) return \"terminated\";\n // Header fragments like \"waiting on condition\", \"runnable\", \"sleeping\"\n const lower = value.trim().toLowerCase();\n if (lower.includes(\"runnable\")) return \"runnable\";\n if (lower.includes(\"waiting on condition\") || lower.includes(\"sleeping\")) return \"timed_waiting\";\n if (lower.includes(\"waiting\")) return \"waiting\";\n if (lower.includes(\"blocked\")) return \"blocked\";\n return lower.split(/\\s+/)[0] ?? \"\";\n}\n\nconst runtimePrefixes = [\"java.\", \"javax.\", \"sun.\", \"jdk.\", \"com.sun.\", \"or\" + \"acle.jrockit.\"];\nfunction isJvmRuntimeFrame(functionName: string): boolean {\n return runtimePrefixes.some((p) => functionName.startsWith(p));\n}\n\n// splitClassMethod separates a JVM frame's `fully.qualified.Class.method` into\n// its class and method. The method is the last dot segment; everything before\n// is the (possibly inner) class. Returns no fields when the name has no dot, so\n// frames like a bare `<init>` are left without a class rather than guessed.\nfunction splitClassMethod(functionName: string): { class?: string; method?: string } {\n const lastDot = functionName.lastIndexOf(\".\");\n if (lastDot <= 0) return {};\n return {\n class: functionName.slice(0, lastDot),\n method: functionName.slice(lastDot + 1),\n };\n}\n\nfunction sanitizeJvmFunctionName(functionName: string): string {\n // e.g. \"com.example.App$Inner.method\" → \"App$Inner.method\"\n const parts = functionName.split(\".\");\n if (parts.length < 2) return functionName;\n const method = parts[parts.length - 1];\n const cls = parts[parts.length - 2];\n return `${cls}.${method}`;\n}\n"],"names":[],"mappings":"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"}
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const frameRe = /^\s*at\s+([\w$.]+)\.([\w$<>]+)\(([^)]+)\)/;
4
- const headerRe = /^([\w.$]+(?:Exception|Error|Throwable))(?::\s*(.*))?$/;
5
4
  const continuationRe = /^\.\.\.\s+\d+\s+more$/;
6
5
  const runtimePrefixes = ["java.", "javax.", "sun.", "jdk.", "com.sun.", "oracle.jrockit."];
7
6
  function parseJavaStackTrace(input) {
@@ -30,10 +29,10 @@ function parseJavaStackTrace(input) {
30
29
  continue;
31
30
  }
32
31
  if (!out.exceptionClass) {
33
- const headerMatch = headerRe.exec(trimmed);
34
- if (headerMatch && headerMatch[1]) {
35
- out.exceptionClass = headerMatch[1];
36
- if (headerMatch[2]) out.message = headerMatch[2];
32
+ const header = parseExceptionHeader(trimmed);
33
+ if (header) {
34
+ out.exceptionClass = header.exceptionClass;
35
+ if (header.message) out.message = header.message;
37
36
  continue;
38
37
  }
39
38
  }
@@ -44,6 +43,21 @@ function parseJavaStackTrace(input) {
44
43
  }
45
44
  return out;
46
45
  }
46
+ function parseExceptionHeader(value) {
47
+ const separator = value.indexOf(":");
48
+ const exceptionClass = separator === -1 ? value : value.slice(0, separator);
49
+ if (!exceptionClass || !["Exception", "Error", "Throwable"].some((suffix) => exceptionClass.endsWith(suffix))) {
50
+ return null;
51
+ }
52
+ for (const character of exceptionClass) {
53
+ const code = character.charCodeAt(0);
54
+ const valid = code >= 48 && code <= 57 || code >= 65 && code <= 90 || code >= 97 && code <= 122 || character === "_" || character === "." || character === "$";
55
+ if (!valid) return null;
56
+ }
57
+ if (separator === -1) return { exceptionClass };
58
+ const message = value.slice(separator + 1).trimStart();
59
+ return message ? { exceptionClass, message } : { exceptionClass };
60
+ }
47
61
  function buildFrame(cls, method, locRaw) {
48
62
  const loc = locRaw.trim();
49
63
  let file;
@@ -1 +1 @@
1
- {"version":3,"file":"stacktrace-parse.cjs","sources":["../../../src/data/diagnostics/stacktrace-parse.ts"],"sourcesContent":["import type { ParsedThreadFrame } from \"./jvm-stacktrace\";\n\n// ParsedStackFrame is the React mirror of Go's clicky api.StackFrame. We reuse\n// ParsedThreadFrame so the existing JvmStackFrameRow renderer can display it.\nexport type ParsedStackFrame = ParsedThreadFrame;\n\nexport interface ParsedStackTrace {\n exceptionClass?: string;\n message?: string;\n causedBy: string[];\n frames: ParsedStackFrame[];\n language: \"java\";\n}\n\nconst frameRe = /^\\s*at\\s+([\\w$.]+)\\.([\\w$<>]+)\\(([^)]+)\\)/;\nconst headerRe = /^([\\w.$]+(?:Exception|Error|Throwable))(?::\\s*(.*))?$/;\nconst continuationRe = /^\\.\\.\\.\\s+\\d+\\s+more$/;\n\nconst runtimePrefixes = [\"java.\", \"javax.\", \"sun.\", \"jdk.\", \"com.sun.\", \"or\" + \"acle.jrockit.\"];\n\n// parseJavaStackTrace decodes a free-form Java exception dump (the body that\n// printStackTrace() emits, optionally wrapped by EclipseLink \"Internal\n// Exception:\" / \"Caused by:\" markers) into a renderable shape. It is the\n// React-side counterpart to Go's api.ParseJavaStackTrace.\nexport function parseJavaStackTrace(input: string): ParsedStackTrace {\n const out: ParsedStackTrace = {\n causedBy: [],\n frames: [],\n language: \"java\",\n };\n if (!input || !input.trim()) return out;\n\n const headerLines: string[] = [];\n for (const raw of input.split(\"\\n\")) {\n const trimmed = raw.trim();\n if (!trimmed) continue;\n if (continuationRe.test(trimmed)) continue;\n\n const frameMatch = frameRe.exec(raw);\n if (frameMatch && frameMatch[1] && frameMatch[2] && frameMatch[3] !== undefined) {\n out.frames.push(buildFrame(frameMatch[1], frameMatch[2], frameMatch[3]));\n continue;\n }\n\n if (trimmed.startsWith(\"Caused by:\")) {\n out.causedBy.push(trimmed.slice(\"Caused by:\".length).trim());\n continue;\n }\n if (trimmed.startsWith(\"Internal Exception:\")) {\n out.causedBy.push(trimmed.slice(\"Internal Exception:\".length).trim());\n continue;\n }\n\n if (!out.exceptionClass) {\n const headerMatch = headerRe.exec(trimmed);\n if (headerMatch && headerMatch[1]) {\n out.exceptionClass = headerMatch[1];\n if (headerMatch[2]) out.message = headerMatch[2];\n continue;\n }\n }\n\n headerLines.push(trimmed);\n }\n\n if (!out.message && headerLines.length > 0) {\n out.message = headerLines.join(\" \");\n }\n\n return out;\n}\n\nfunction buildFrame(cls: string, method: string, locRaw: string): ParsedStackFrame {\n const loc = locRaw.trim();\n let file: string | undefined;\n let line: number | undefined;\n let nativeMethod = false;\n if (loc === \"Native Method\") {\n nativeMethod = true;\n } else if (loc !== \"Unknown Source\") {\n const cleaned = (loc.split(\" ~[\")[0] ?? loc).trim();\n const i = cleaned.lastIndexOf(\":\");\n if (i >= 0) {\n file = cleaned.slice(0, i);\n const n = Number(cleaned.slice(i + 1));\n if (Number.isFinite(n)) line = n;\n } else {\n file = cleaned;\n }\n }\n const functionName = `${cls}.${method}`;\n const location = file\n ? line\n ? `${file}:${line}`\n : file\n : nativeMethod\n ? \"Native Method\"\n : undefined;\n const frame: ParsedStackFrame = {\n functionName,\n displayName: shortDisplay(cls, method),\n kind: \"frame\",\n runtime: runtimePrefixes.some((p) => cls.startsWith(p)),\n nativeMethod,\n class: cls,\n method,\n };\n if (file !== undefined) frame.file = file;\n if (line !== undefined) frame.line = line;\n if (location !== undefined) frame.location = location;\n return frame;\n}\n\nfunction shortDisplay(cls: string, method: string): string {\n const parts = cls.split(\".\");\n const last = parts[parts.length - 1] ?? cls;\n return `${last}.${method}`;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"stacktrace-parse.cjs","sources":["../../../src/data/diagnostics/stacktrace-parse.ts"],"sourcesContent":["import type { ParsedThreadFrame } from \"./jvm-stacktrace\";\n\n// ParsedStackFrame is the React mirror of Go's clicky api.StackFrame. We reuse\n// ParsedThreadFrame so the existing JvmStackFrameRow renderer can display it.\nexport type ParsedStackFrame = ParsedThreadFrame;\n\nexport interface ParsedStackTrace {\n exceptionClass?: string;\n message?: string;\n causedBy: string[];\n frames: ParsedStackFrame[];\n language: \"java\";\n}\n\nconst frameRe = /^\\s*at\\s+([\\w$.]+)\\.([\\w$<>]+)\\(([^)]+)\\)/;\nconst continuationRe = /^\\.\\.\\.\\s+\\d+\\s+more$/;\n\nconst runtimePrefixes = [\"java.\", \"javax.\", \"sun.\", \"jdk.\", \"com.sun.\", \"or\" + \"acle.jrockit.\"];\n\n// parseJavaStackTrace decodes a free-form Java exception dump (the body that\n// printStackTrace() emits, optionally wrapped by EclipseLink \"Internal\n// Exception:\" / \"Caused by:\" markers) into a renderable shape. It is the\n// React-side counterpart to Go's api.ParseJavaStackTrace.\nexport function parseJavaStackTrace(input: string): ParsedStackTrace {\n const out: ParsedStackTrace = {\n causedBy: [],\n frames: [],\n language: \"java\",\n };\n if (!input || !input.trim()) return out;\n\n const headerLines: string[] = [];\n for (const raw of input.split(\"\\n\")) {\n const trimmed = raw.trim();\n if (!trimmed) continue;\n if (continuationRe.test(trimmed)) continue;\n\n const frameMatch = frameRe.exec(raw);\n if (frameMatch && frameMatch[1] && frameMatch[2] && frameMatch[3] !== undefined) {\n out.frames.push(buildFrame(frameMatch[1], frameMatch[2], frameMatch[3]));\n continue;\n }\n\n if (trimmed.startsWith(\"Caused by:\")) {\n out.causedBy.push(trimmed.slice(\"Caused by:\".length).trim());\n continue;\n }\n if (trimmed.startsWith(\"Internal Exception:\")) {\n out.causedBy.push(trimmed.slice(\"Internal Exception:\".length).trim());\n continue;\n }\n\n if (!out.exceptionClass) {\n const header = parseExceptionHeader(trimmed);\n if (header) {\n out.exceptionClass = header.exceptionClass;\n if (header.message) out.message = header.message;\n continue;\n }\n }\n\n headerLines.push(trimmed);\n }\n\n if (!out.message && headerLines.length > 0) {\n out.message = headerLines.join(\" \");\n }\n\n return out;\n}\n\nfunction parseExceptionHeader(\n value: string,\n): { exceptionClass: string; message?: string } | null {\n const separator = value.indexOf(\":\");\n const exceptionClass = separator === -1 ? value : value.slice(0, separator);\n if (\n !exceptionClass ||\n ![\"Exception\", \"Error\", \"Throwable\"].some((suffix) => exceptionClass.endsWith(suffix))\n ) {\n return null;\n }\n for (const character of exceptionClass) {\n const code = character.charCodeAt(0);\n const valid =\n (code >= 48 && code <= 57) ||\n (code >= 65 && code <= 90) ||\n (code >= 97 && code <= 122) ||\n character === \"_\" ||\n character === \".\" ||\n character === \"$\";\n if (!valid) return null;\n }\n\n if (separator === -1) return { exceptionClass };\n const message = value.slice(separator + 1).trimStart();\n return message ? { exceptionClass, message } : { exceptionClass };\n}\n\nfunction buildFrame(cls: string, method: string, locRaw: string): ParsedStackFrame {\n const loc = locRaw.trim();\n let file: string | undefined;\n let line: number | undefined;\n let nativeMethod = false;\n if (loc === \"Native Method\") {\n nativeMethod = true;\n } else if (loc !== \"Unknown Source\") {\n const cleaned = (loc.split(\" ~[\")[0] ?? loc).trim();\n const i = cleaned.lastIndexOf(\":\");\n if (i >= 0) {\n file = cleaned.slice(0, i);\n const n = Number(cleaned.slice(i + 1));\n if (Number.isFinite(n)) line = n;\n } else {\n file = cleaned;\n }\n }\n const functionName = `${cls}.${method}`;\n const location = file\n ? line\n ? `${file}:${line}`\n : file\n : nativeMethod\n ? \"Native Method\"\n : undefined;\n const frame: ParsedStackFrame = {\n functionName,\n displayName: shortDisplay(cls, method),\n kind: \"frame\",\n runtime: runtimePrefixes.some((p) => cls.startsWith(p)),\n nativeMethod,\n class: cls,\n method,\n };\n if (file !== undefined) frame.file = file;\n if (line !== undefined) frame.line = line;\n if (location !== undefined) frame.location = location;\n return frame;\n}\n\nfunction shortDisplay(cls: string, method: string): string {\n const parts = cls.split(\".\");\n const last = parts[parts.length - 1] ?? cls;\n return `${last}.${method}`;\n}\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"stacktrace-parse.d.ts","sourceRoot":"","sources":["../../../src/data/diagnostics/stacktrace-parse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAI1D,MAAM,MAAM,gBAAgB,GAAG,iBAAiB,CAAC;AAEjD,MAAM,WAAW,gBAAgB;IAC/B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAYD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,CA8CnE"}
1
+ {"version":3,"file":"stacktrace-parse.d.ts","sourceRoot":"","sources":["../../../src/data/diagnostics/stacktrace-parse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAI1D,MAAM,MAAM,gBAAgB,GAAG,iBAAiB,CAAC;AAEjD,MAAM,WAAW,gBAAgB;IAC/B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAWD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,CA8CnE"}
@@ -1,5 +1,4 @@
1
1
  const frameRe = /^\s*at\s+([\w$.]+)\.([\w$<>]+)\(([^)]+)\)/;
2
- const headerRe = /^([\w.$]+(?:Exception|Error|Throwable))(?::\s*(.*))?$/;
3
2
  const continuationRe = /^\.\.\.\s+\d+\s+more$/;
4
3
  const runtimePrefixes = ["java.", "javax.", "sun.", "jdk.", "com.sun.", "oracle.jrockit."];
5
4
  function parseJavaStackTrace(input) {
@@ -28,10 +27,10 @@ function parseJavaStackTrace(input) {
28
27
  continue;
29
28
  }
30
29
  if (!out.exceptionClass) {
31
- const headerMatch = headerRe.exec(trimmed);
32
- if (headerMatch && headerMatch[1]) {
33
- out.exceptionClass = headerMatch[1];
34
- if (headerMatch[2]) out.message = headerMatch[2];
30
+ const header = parseExceptionHeader(trimmed);
31
+ if (header) {
32
+ out.exceptionClass = header.exceptionClass;
33
+ if (header.message) out.message = header.message;
35
34
  continue;
36
35
  }
37
36
  }
@@ -42,6 +41,21 @@ function parseJavaStackTrace(input) {
42
41
  }
43
42
  return out;
44
43
  }
44
+ function parseExceptionHeader(value) {
45
+ const separator = value.indexOf(":");
46
+ const exceptionClass = separator === -1 ? value : value.slice(0, separator);
47
+ if (!exceptionClass || !["Exception", "Error", "Throwable"].some((suffix) => exceptionClass.endsWith(suffix))) {
48
+ return null;
49
+ }
50
+ for (const character of exceptionClass) {
51
+ const code = character.charCodeAt(0);
52
+ const valid = code >= 48 && code <= 57 || code >= 65 && code <= 90 || code >= 97 && code <= 122 || character === "_" || character === "." || character === "$";
53
+ if (!valid) return null;
54
+ }
55
+ if (separator === -1) return { exceptionClass };
56
+ const message = value.slice(separator + 1).trimStart();
57
+ return message ? { exceptionClass, message } : { exceptionClass };
58
+ }
45
59
  function buildFrame(cls, method, locRaw) {
46
60
  const loc = locRaw.trim();
47
61
  let file;
@@ -1 +1 @@
1
- {"version":3,"file":"stacktrace-parse.js","sources":["../../../src/data/diagnostics/stacktrace-parse.ts"],"sourcesContent":["import type { ParsedThreadFrame } from \"./jvm-stacktrace\";\n\n// ParsedStackFrame is the React mirror of Go's clicky api.StackFrame. We reuse\n// ParsedThreadFrame so the existing JvmStackFrameRow renderer can display it.\nexport type ParsedStackFrame = ParsedThreadFrame;\n\nexport interface ParsedStackTrace {\n exceptionClass?: string;\n message?: string;\n causedBy: string[];\n frames: ParsedStackFrame[];\n language: \"java\";\n}\n\nconst frameRe = /^\\s*at\\s+([\\w$.]+)\\.([\\w$<>]+)\\(([^)]+)\\)/;\nconst headerRe = /^([\\w.$]+(?:Exception|Error|Throwable))(?::\\s*(.*))?$/;\nconst continuationRe = /^\\.\\.\\.\\s+\\d+\\s+more$/;\n\nconst runtimePrefixes = [\"java.\", \"javax.\", \"sun.\", \"jdk.\", \"com.sun.\", \"or\" + \"acle.jrockit.\"];\n\n// parseJavaStackTrace decodes a free-form Java exception dump (the body that\n// printStackTrace() emits, optionally wrapped by EclipseLink \"Internal\n// Exception:\" / \"Caused by:\" markers) into a renderable shape. It is the\n// React-side counterpart to Go's api.ParseJavaStackTrace.\nexport function parseJavaStackTrace(input: string): ParsedStackTrace {\n const out: ParsedStackTrace = {\n causedBy: [],\n frames: [],\n language: \"java\",\n };\n if (!input || !input.trim()) return out;\n\n const headerLines: string[] = [];\n for (const raw of input.split(\"\\n\")) {\n const trimmed = raw.trim();\n if (!trimmed) continue;\n if (continuationRe.test(trimmed)) continue;\n\n const frameMatch = frameRe.exec(raw);\n if (frameMatch && frameMatch[1] && frameMatch[2] && frameMatch[3] !== undefined) {\n out.frames.push(buildFrame(frameMatch[1], frameMatch[2], frameMatch[3]));\n continue;\n }\n\n if (trimmed.startsWith(\"Caused by:\")) {\n out.causedBy.push(trimmed.slice(\"Caused by:\".length).trim());\n continue;\n }\n if (trimmed.startsWith(\"Internal Exception:\")) {\n out.causedBy.push(trimmed.slice(\"Internal Exception:\".length).trim());\n continue;\n }\n\n if (!out.exceptionClass) {\n const headerMatch = headerRe.exec(trimmed);\n if (headerMatch && headerMatch[1]) {\n out.exceptionClass = headerMatch[1];\n if (headerMatch[2]) out.message = headerMatch[2];\n continue;\n }\n }\n\n headerLines.push(trimmed);\n }\n\n if (!out.message && headerLines.length > 0) {\n out.message = headerLines.join(\" \");\n }\n\n return out;\n}\n\nfunction buildFrame(cls: string, method: string, locRaw: string): ParsedStackFrame {\n const loc = locRaw.trim();\n let file: string | undefined;\n let line: number | undefined;\n let nativeMethod = false;\n if (loc === \"Native Method\") {\n nativeMethod = true;\n } else if (loc !== \"Unknown Source\") {\n const cleaned = (loc.split(\" ~[\")[0] ?? loc).trim();\n const i = cleaned.lastIndexOf(\":\");\n if (i >= 0) {\n file = cleaned.slice(0, i);\n const n = Number(cleaned.slice(i + 1));\n if (Number.isFinite(n)) line = n;\n } else {\n file = cleaned;\n }\n }\n const functionName = `${cls}.${method}`;\n const location = file\n ? line\n ? `${file}:${line}`\n : file\n : nativeMethod\n ? \"Native Method\"\n : undefined;\n const frame: ParsedStackFrame = {\n functionName,\n displayName: shortDisplay(cls, method),\n kind: \"frame\",\n runtime: runtimePrefixes.some((p) => cls.startsWith(p)),\n nativeMethod,\n class: cls,\n method,\n };\n if (file !== undefined) frame.file = file;\n if (line !== undefined) frame.line = line;\n if (location !== undefined) frame.location = location;\n return frame;\n}\n\nfunction shortDisplay(cls: string, method: string): string {\n const parts = cls.split(\".\");\n const last = parts[parts.length - 1] ?? cls;\n return `${last}.${method}`;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"stacktrace-parse.js","sources":["../../../src/data/diagnostics/stacktrace-parse.ts"],"sourcesContent":["import type { ParsedThreadFrame } from \"./jvm-stacktrace\";\n\n// ParsedStackFrame is the React mirror of Go's clicky api.StackFrame. We reuse\n// ParsedThreadFrame so the existing JvmStackFrameRow renderer can display it.\nexport type ParsedStackFrame = ParsedThreadFrame;\n\nexport interface ParsedStackTrace {\n exceptionClass?: string;\n message?: string;\n causedBy: string[];\n frames: ParsedStackFrame[];\n language: \"java\";\n}\n\nconst frameRe = /^\\s*at\\s+([\\w$.]+)\\.([\\w$<>]+)\\(([^)]+)\\)/;\nconst continuationRe = /^\\.\\.\\.\\s+\\d+\\s+more$/;\n\nconst runtimePrefixes = [\"java.\", \"javax.\", \"sun.\", \"jdk.\", \"com.sun.\", \"or\" + \"acle.jrockit.\"];\n\n// parseJavaStackTrace decodes a free-form Java exception dump (the body that\n// printStackTrace() emits, optionally wrapped by EclipseLink \"Internal\n// Exception:\" / \"Caused by:\" markers) into a renderable shape. It is the\n// React-side counterpart to Go's api.ParseJavaStackTrace.\nexport function parseJavaStackTrace(input: string): ParsedStackTrace {\n const out: ParsedStackTrace = {\n causedBy: [],\n frames: [],\n language: \"java\",\n };\n if (!input || !input.trim()) return out;\n\n const headerLines: string[] = [];\n for (const raw of input.split(\"\\n\")) {\n const trimmed = raw.trim();\n if (!trimmed) continue;\n if (continuationRe.test(trimmed)) continue;\n\n const frameMatch = frameRe.exec(raw);\n if (frameMatch && frameMatch[1] && frameMatch[2] && frameMatch[3] !== undefined) {\n out.frames.push(buildFrame(frameMatch[1], frameMatch[2], frameMatch[3]));\n continue;\n }\n\n if (trimmed.startsWith(\"Caused by:\")) {\n out.causedBy.push(trimmed.slice(\"Caused by:\".length).trim());\n continue;\n }\n if (trimmed.startsWith(\"Internal Exception:\")) {\n out.causedBy.push(trimmed.slice(\"Internal Exception:\".length).trim());\n continue;\n }\n\n if (!out.exceptionClass) {\n const header = parseExceptionHeader(trimmed);\n if (header) {\n out.exceptionClass = header.exceptionClass;\n if (header.message) out.message = header.message;\n continue;\n }\n }\n\n headerLines.push(trimmed);\n }\n\n if (!out.message && headerLines.length > 0) {\n out.message = headerLines.join(\" \");\n }\n\n return out;\n}\n\nfunction parseExceptionHeader(\n value: string,\n): { exceptionClass: string; message?: string } | null {\n const separator = value.indexOf(\":\");\n const exceptionClass = separator === -1 ? value : value.slice(0, separator);\n if (\n !exceptionClass ||\n ![\"Exception\", \"Error\", \"Throwable\"].some((suffix) => exceptionClass.endsWith(suffix))\n ) {\n return null;\n }\n for (const character of exceptionClass) {\n const code = character.charCodeAt(0);\n const valid =\n (code >= 48 && code <= 57) ||\n (code >= 65 && code <= 90) ||\n (code >= 97 && code <= 122) ||\n character === \"_\" ||\n character === \".\" ||\n character === \"$\";\n if (!valid) return null;\n }\n\n if (separator === -1) return { exceptionClass };\n const message = value.slice(separator + 1).trimStart();\n return message ? { exceptionClass, message } : { exceptionClass };\n}\n\nfunction buildFrame(cls: string, method: string, locRaw: string): ParsedStackFrame {\n const loc = locRaw.trim();\n let file: string | undefined;\n let line: number | undefined;\n let nativeMethod = false;\n if (loc === \"Native Method\") {\n nativeMethod = true;\n } else if (loc !== \"Unknown Source\") {\n const cleaned = (loc.split(\" ~[\")[0] ?? loc).trim();\n const i = cleaned.lastIndexOf(\":\");\n if (i >= 0) {\n file = cleaned.slice(0, i);\n const n = Number(cleaned.slice(i + 1));\n if (Number.isFinite(n)) line = n;\n } else {\n file = cleaned;\n }\n }\n const functionName = `${cls}.${method}`;\n const location = file\n ? line\n ? `${file}:${line}`\n : file\n : nativeMethod\n ? \"Native Method\"\n : undefined;\n const frame: ParsedStackFrame = {\n functionName,\n displayName: shortDisplay(cls, method),\n kind: \"frame\",\n runtime: runtimePrefixes.some((p) => cls.startsWith(p)),\n nativeMethod,\n class: cls,\n method,\n };\n if (file !== undefined) frame.file = file;\n if (line !== undefined) frame.line = line;\n if (location !== undefined) frame.location = location;\n return frame;\n}\n\nfunction shortDisplay(cls: string, method: string): string {\n const parts = cls.split(\".\");\n const last = parts[parts.length - 1] ?? cls;\n return `${last}.${method}`;\n}\n"],"names":[],"mappings":"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"}
@@ -32,7 +32,7 @@ function parseGoroutineDump(text) {
32
32
  var _a, _b;
33
33
  const trimmed = text.trim();
34
34
  if (!trimmed) return [];
35
- const blocks = trimmed.split(/\n\s*\n+/);
35
+ const blocks = splitGoroutineBlocks(trimmed);
36
36
  const goroutines = [];
37
37
  for (const block of blocks) {
38
38
  const lines = block.split("\n").map((line) => line.replace(/\r$/, ""));
@@ -98,11 +98,24 @@ function normalizeState(value) {
98
98
  function isRuntimeFrame(functionName) {
99
99
  return functionName.startsWith("runtime.") || functionName.startsWith("runtime/") || functionName.startsWith("internal/") || functionName.startsWith("runtime/internal/") || functionName.startsWith("syscall.") || functionName.startsWith("reflect.");
100
100
  }
101
+ function splitGoroutineBlocks(text) {
102
+ const blocks = [];
103
+ let lines = [];
104
+ for (const line of text.split("\n")) {
105
+ if (line.trim()) {
106
+ lines.push(line);
107
+ } else if (lines.length > 0) {
108
+ blocks.push(lines.join("\n"));
109
+ lines = [];
110
+ }
111
+ }
112
+ if (lines.length > 0) blocks.push(lines.join("\n"));
113
+ return blocks;
114
+ }
101
115
  function sanitizeFunctionName(functionName) {
102
116
  let name = functionName.trim();
103
117
  const paren = name.indexOf("(");
104
118
  if (paren !== -1) name = name.slice(0, paren);
105
- name = name.replace(/\.\(\*([^)]+)\)\./g, ".$1.");
106
119
  return stripPackageQualifier(name);
107
120
  }
108
121
  function stripPackageQualifier(name) {
@@ -1 +1 @@
1
- {"version":3,"file":"stacktrace.cjs","sources":["../../../src/data/diagnostics/stacktrace.ts"],"sourcesContent":["import { parseJvmThreadDump, countThreadsByState, type ParsedThread } from \"./jvm-stacktrace\";\n\nexport type DumpFormat = \"go\" | \"jvm\" | \"unknown\";\n\nexport type ParsedStack =\n | { format: \"go\"; goroutines: ParsedGoroutine[] }\n | { format: \"jvm\"; threads: ParsedThread[] }\n | { format: \"unknown\"; goroutines: []; threads: [] };\n\nexport function detectDumpFormat(text: string): DumpFormat {\n const trimmed = text.trim();\n if (!trimmed) return \"unknown\";\n // JVM header: `\"name\" ... prio=` OR `\"name\" ... tid=`\n if (/^\"[^\"]*\"[^\\n]*\\b(prio=|tid=|nid=)/m.test(trimmed)) return \"jvm\";\n // Go header: `goroutine N [state]:`\n if (/^goroutine\\s+\\d+\\s+\\[[^\\]]+\\]:/m.test(trimmed)) return \"go\";\n return \"unknown\";\n}\n\nexport function parseStackDump(text: string): ParsedStack {\n const format = detectDumpFormat(text);\n if (format === \"jvm\") {\n return { format: \"jvm\", threads: parseJvmThreadDump(text) };\n }\n if (format === \"go\") {\n return { format: \"go\", goroutines: parseGoroutineDump(text) };\n }\n return { format: \"unknown\", goroutines: [], threads: [] };\n}\n\nexport function countStackByState(stack: ParsedStack): Map<string, number> {\n if (stack.format === \"jvm\") return countThreadsByState(stack.threads);\n if (stack.format === \"go\") return countGoroutinesByState(stack.goroutines);\n return new Map();\n}\n\nexport interface ParsedGoroutineFrame {\n functionName: string;\n displayName: string;\n file?: string;\n location?: string;\n line?: number;\n kind: \"frame\" | \"created_by\";\n runtime: boolean;\n}\n\nexport interface ParsedGoroutine {\n id: number;\n state: string;\n rawState: string;\n frames: ParsedGoroutineFrame[];\n raw: string;\n userFrameCount: number;\n topFunction?: string;\n searchText: string;\n}\n\nconst headerRe = /^goroutine\\s+(\\d+)\\s+\\[(.+?)\\]:$/;\nconst fileRe = /^\\s*(.+?):(\\d+)(?:\\s+\\+0x[0-9a-f]+)?$/i;\nconst goSrcPrefixRe = /^\\/usr\\/local\\/go[\\d.]+\\/src\\//;\nconst goWorkspacePrefixRe = /^.*?\\/go\\/src\\//;\nconst goPkgModPrefixRe = /^.*?\\/go\\/pkg\\/mod\\//;\n\nexport function parseGoroutineDump(text: string): ParsedGoroutine[] {\n const trimmed = text.trim();\n if (!trimmed) return [];\n\n const blocks = trimmed.split(/\\n\\s*\\n+/);\n const goroutines: ParsedGoroutine[] = [];\n\n for (const block of blocks) {\n const lines = block.split(\"\\n\").map((line) => line.replace(/\\r$/, \"\"));\n const header = lines[0]?.trim();\n const match = headerRe.exec(header || \"\");\n if (!match) continue;\n\n const id = Number(match[1] ?? 0);\n const rawState = match[2] ?? \"\";\n const state = normalizeState(rawState);\n const frames: ParsedGoroutineFrame[] = [];\n\n for (let i = 1; i < lines.length; i++) {\n const line = lines[i];\n if (line == null) continue;\n const trimmedLine = line.trim();\n if (!trimmedLine) continue;\n\n const fileMatch = fileRe.exec(trimmedLine);\n const previousFrame = frames[frames.length - 1];\n if (fileMatch && previousFrame && !previousFrame.file) {\n previousFrame.file = fileMatch[1] ?? \"\";\n previousFrame.line = Number(fileMatch[2] ?? 0);\n continue;\n }\n\n const kind = trimmedLine.startsWith(\"created by \") ? \"created_by\" : \"frame\";\n const functionName =\n kind === \"created_by\" ? trimmedLine.slice(\"created by \".length) : trimmedLine;\n frames.push({\n functionName,\n displayName: sanitizeFunctionName(functionName),\n kind,\n runtime: isRuntimeFrame(functionName),\n });\n }\n\n for (const frame of frames) {\n if (frame.file) {\n frame.file = normalizeFilePath(frame.file);\n frame.location = `${frame.file}${frame.line ? `:${frame.line}` : \"\"}`;\n }\n }\n\n const topFunction = frames.find((frame) => frame.kind === \"frame\")?.functionName;\n\n goroutines.push({\n id,\n state,\n rawState,\n frames,\n raw: block,\n userFrameCount: frames.filter((frame) => !frame.runtime && frame.kind === \"frame\").length,\n searchText: `${header}\\n${frames\n .map((frame) => `${frame.functionName} ${frame.file || \"\"}`)\n .join(\"\\n\")}`.toLowerCase(),\n ...(topFunction !== undefined ? { topFunction } : {}),\n });\n }\n\n return goroutines;\n}\n\nexport function countGoroutinesByState(goroutines: ParsedGoroutine[]): Map<string, number> {\n const counts = new Map<string, number>();\n for (const goroutine of goroutines) {\n counts.set(goroutine.state, (counts.get(goroutine.state) || 0) + 1);\n }\n return counts;\n}\n\nfunction normalizeState(value: string): string {\n return (value.split(\",\")[0] ?? \"\").trim().toLowerCase();\n}\n\nfunction isRuntimeFrame(functionName: string): boolean {\n return (\n functionName.startsWith(\"runtime.\") ||\n functionName.startsWith(\"runtime/\") ||\n functionName.startsWith(\"internal/\") ||\n functionName.startsWith(\"runtime/internal/\") ||\n functionName.startsWith(\"syscall.\") ||\n functionName.startsWith(\"reflect.\")\n );\n}\n\nfunction sanitizeFunctionName(functionName: string): string {\n let name = functionName.trim();\n const paren = name.indexOf(\"(\");\n if (paren !== -1) name = name.slice(0, paren);\n name = name.replace(/\\.\\(\\*([^)]+)\\)\\./g, \".$1.\");\n return stripPackageQualifier(name);\n}\n\nfunction stripPackageQualifier(name: string): string {\n return name.replace(/^((?:[^./\\s]+\\/)+)([^./\\s]+)\\./, \"$2.\");\n}\n\nfunction normalizeFilePath(path: string): string {\n return path\n .replace(goSrcPrefixRe, \"\")\n .replace(goWorkspacePrefixRe, \"\")\n .replace(goPkgModPrefixRe, \"\");\n}\n"],"names":["parseJvmThreadDump","countThreadsByState"],"mappings":";;;AASO,SAAS,iBAAiB,MAA0B;AACzD,QAAM,UAAU,KAAK,KAAA;AACrB,MAAI,CAAC,QAAS,QAAO;AAErB,MAAI,qCAAqC,KAAK,OAAO,EAAG,QAAO;AAE/D,MAAI,kCAAkC,KAAK,OAAO,EAAG,QAAO;AAC5D,SAAO;AACT;AAEO,SAAS,eAAe,MAA2B;AACxD,QAAM,SAAS,iBAAiB,IAAI;AACpC,MAAI,WAAW,OAAO;AACpB,WAAO,EAAE,QAAQ,OAAO,SAASA,cAAAA,mBAAmB,IAAI,EAAA;AAAA,EAC1D;AACA,MAAI,WAAW,MAAM;AACnB,WAAO,EAAE,QAAQ,MAAM,YAAY,mBAAmB,IAAI,EAAA;AAAA,EAC5D;AACA,SAAO,EAAE,QAAQ,WAAW,YAAY,CAAA,GAAI,SAAS,GAAC;AACxD;AAEO,SAAS,kBAAkB,OAAyC;AACzE,MAAI,MAAM,WAAW,MAAO,QAAOC,cAAAA,oBAAoB,MAAM,OAAO;AACpE,MAAI,MAAM,WAAW,KAAM,QAAO,uBAAuB,MAAM,UAAU;AACzE,6BAAW,IAAA;AACb;AAuBA,MAAM,WAAW;AACjB,MAAM,SAAS;AACf,MAAM,gBAAgB;AACtB,MAAM,sBAAsB;AAC5B,MAAM,mBAAmB;AAElB,SAAS,mBAAmB,MAAiC;;AAClE,QAAM,UAAU,KAAK,KAAA;AACrB,MAAI,CAAC,QAAS,QAAO,CAAA;AAErB,QAAM,SAAS,QAAQ,MAAM,UAAU;AACvC,QAAM,aAAgC,CAAA;AAEtC,aAAW,SAAS,QAAQ;AAC1B,UAAM,QAAQ,MAAM,MAAM,IAAI,EAAE,IAAI,CAAC,SAAS,KAAK,QAAQ,OAAO,EAAE,CAAC;AACrE,UAAM,UAAS,WAAM,CAAC,MAAP,mBAAU;AACzB,UAAM,QAAQ,SAAS,KAAK,UAAU,EAAE;AACxC,QAAI,CAAC,MAAO;AAEZ,UAAM,KAAK,OAAO,MAAM,CAAC,KAAK,CAAC;AAC/B,UAAM,WAAW,MAAM,CAAC,KAAK;AAC7B,UAAM,QAAQ,eAAe,QAAQ;AACrC,UAAM,SAAiC,CAAA;AAEvC,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,YAAM,OAAO,MAAM,CAAC;AACpB,UAAI,QAAQ,KAAM;AAClB,YAAM,cAAc,KAAK,KAAA;AACzB,UAAI,CAAC,YAAa;AAElB,YAAM,YAAY,OAAO,KAAK,WAAW;AACzC,YAAM,gBAAgB,OAAO,OAAO,SAAS,CAAC;AAC9C,UAAI,aAAa,iBAAiB,CAAC,cAAc,MAAM;AACrD,sBAAc,OAAO,UAAU,CAAC,KAAK;AACrC,sBAAc,OAAO,OAAO,UAAU,CAAC,KAAK,CAAC;AAC7C;AAAA,MACF;AAEA,YAAM,OAAO,YAAY,WAAW,aAAa,IAAI,eAAe;AACpE,YAAM,eACJ,SAAS,eAAe,YAAY,MAAM,cAAc,MAAM,IAAI;AACpE,aAAO,KAAK;AAAA,QACV;AAAA,QACA,aAAa,qBAAqB,YAAY;AAAA,QAC9C;AAAA,QACA,SAAS,eAAe,YAAY;AAAA,MAAA,CACrC;AAAA,IACH;AAEA,eAAW,SAAS,QAAQ;AAC1B,UAAI,MAAM,MAAM;AACd,cAAM,OAAO,kBAAkB,MAAM,IAAI;AACzC,cAAM,WAAW,GAAG,MAAM,IAAI,GAAG,MAAM,OAAO,IAAI,MAAM,IAAI,KAAK,EAAE;AAAA,MACrE;AAAA,IACF;AAEA,UAAM,eAAc,YAAO,KAAK,CAAC,UAAU,MAAM,SAAS,OAAO,MAA7C,mBAAgD;AAEpE,eAAW,KAAK;AAAA,MACd;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,KAAK;AAAA,MACL,gBAAgB,OAAO,OAAO,CAAC,UAAU,CAAC,MAAM,WAAW,MAAM,SAAS,OAAO,EAAE;AAAA,MACnF,YAAY,GAAG,MAAM;AAAA,EAAK,OACvB,IAAI,CAAC,UAAU,GAAG,MAAM,YAAY,IAAI,MAAM,QAAQ,EAAE,EAAE,EAC1D,KAAK,IAAI,CAAC,GAAG,YAAA;AAAA,MAChB,GAAI,gBAAgB,SAAY,EAAE,gBAAgB,CAAA;AAAA,IAAC,CACpD;AAAA,EACH;AAEA,SAAO;AACT;AAEO,SAAS,uBAAuB,YAAoD;AACzF,QAAM,6BAAa,IAAA;AACnB,aAAW,aAAa,YAAY;AAClC,WAAO,IAAI,UAAU,QAAQ,OAAO,IAAI,UAAU,KAAK,KAAK,KAAK,CAAC;AAAA,EACpE;AACA,SAAO;AACT;AAEA,SAAS,eAAe,OAAuB;AAC7C,UAAQ,MAAM,MAAM,GAAG,EAAE,CAAC,KAAK,IAAI,KAAA,EAAO,YAAA;AAC5C;AAEA,SAAS,eAAe,cAA+B;AACrD,SACE,aAAa,WAAW,UAAU,KAClC,aAAa,WAAW,UAAU,KAClC,aAAa,WAAW,WAAW,KACnC,aAAa,WAAW,mBAAmB,KAC3C,aAAa,WAAW,UAAU,KAClC,aAAa,WAAW,UAAU;AAEtC;AAEA,SAAS,qBAAqB,cAA8B;AAC1D,MAAI,OAAO,aAAa,KAAA;AACxB,QAAM,QAAQ,KAAK,QAAQ,GAAG;AAC9B,MAAI,UAAU,GAAI,QAAO,KAAK,MAAM,GAAG,KAAK;AAC5C,SAAO,KAAK,QAAQ,sBAAsB,MAAM;AAChD,SAAO,sBAAsB,IAAI;AACnC;AAEA,SAAS,sBAAsB,MAAsB;AACnD,SAAO,KAAK,QAAQ,kCAAkC,KAAK;AAC7D;AAEA,SAAS,kBAAkB,MAAsB;AAC/C,SAAO,KACJ,QAAQ,eAAe,EAAE,EACzB,QAAQ,qBAAqB,EAAE,EAC/B,QAAQ,kBAAkB,EAAE;AACjC;;;;;;"}
1
+ {"version":3,"file":"stacktrace.cjs","sources":["../../../src/data/diagnostics/stacktrace.ts"],"sourcesContent":["import { parseJvmThreadDump, countThreadsByState, type ParsedThread } from \"./jvm-stacktrace\";\n\nexport type DumpFormat = \"go\" | \"jvm\" | \"unknown\";\n\nexport type ParsedStack =\n | { format: \"go\"; goroutines: ParsedGoroutine[] }\n | { format: \"jvm\"; threads: ParsedThread[] }\n | { format: \"unknown\"; goroutines: []; threads: [] };\n\nexport function detectDumpFormat(text: string): DumpFormat {\n const trimmed = text.trim();\n if (!trimmed) return \"unknown\";\n // JVM header: `\"name\" ... prio=` OR `\"name\" ... tid=`\n if (/^\"[^\"]*\"[^\\n]*\\b(prio=|tid=|nid=)/m.test(trimmed)) return \"jvm\";\n // Go header: `goroutine N [state]:`\n if (/^goroutine\\s+\\d+\\s+\\[[^\\]]+\\]:/m.test(trimmed)) return \"go\";\n return \"unknown\";\n}\n\nexport function parseStackDump(text: string): ParsedStack {\n const format = detectDumpFormat(text);\n if (format === \"jvm\") {\n return { format: \"jvm\", threads: parseJvmThreadDump(text) };\n }\n if (format === \"go\") {\n return { format: \"go\", goroutines: parseGoroutineDump(text) };\n }\n return { format: \"unknown\", goroutines: [], threads: [] };\n}\n\nexport function countStackByState(stack: ParsedStack): Map<string, number> {\n if (stack.format === \"jvm\") return countThreadsByState(stack.threads);\n if (stack.format === \"go\") return countGoroutinesByState(stack.goroutines);\n return new Map();\n}\n\nexport interface ParsedGoroutineFrame {\n functionName: string;\n displayName: string;\n file?: string;\n location?: string;\n line?: number;\n kind: \"frame\" | \"created_by\";\n runtime: boolean;\n}\n\nexport interface ParsedGoroutine {\n id: number;\n state: string;\n rawState: string;\n frames: ParsedGoroutineFrame[];\n raw: string;\n userFrameCount: number;\n topFunction?: string;\n searchText: string;\n}\n\nconst headerRe = /^goroutine\\s+(\\d+)\\s+\\[(.+?)\\]:$/;\nconst fileRe = /^\\s*(.+?):(\\d+)(?:\\s+\\+0x[0-9a-f]+)?$/i;\nconst goSrcPrefixRe = /^\\/usr\\/local\\/go[\\d.]+\\/src\\//;\nconst goWorkspacePrefixRe = /^.*?\\/go\\/src\\//;\nconst goPkgModPrefixRe = /^.*?\\/go\\/pkg\\/mod\\//;\n\nexport function parseGoroutineDump(text: string): ParsedGoroutine[] {\n const trimmed = text.trim();\n if (!trimmed) return [];\n\n const blocks = splitGoroutineBlocks(trimmed);\n const goroutines: ParsedGoroutine[] = [];\n\n for (const block of blocks) {\n const lines = block.split(\"\\n\").map((line) => line.replace(/\\r$/, \"\"));\n const header = lines[0]?.trim();\n const match = headerRe.exec(header || \"\");\n if (!match) continue;\n\n const id = Number(match[1] ?? 0);\n const rawState = match[2] ?? \"\";\n const state = normalizeState(rawState);\n const frames: ParsedGoroutineFrame[] = [];\n\n for (let i = 1; i < lines.length; i++) {\n const line = lines[i];\n if (line == null) continue;\n const trimmedLine = line.trim();\n if (!trimmedLine) continue;\n\n const fileMatch = fileRe.exec(trimmedLine);\n const previousFrame = frames[frames.length - 1];\n if (fileMatch && previousFrame && !previousFrame.file) {\n previousFrame.file = fileMatch[1] ?? \"\";\n previousFrame.line = Number(fileMatch[2] ?? 0);\n continue;\n }\n\n const kind = trimmedLine.startsWith(\"created by \") ? \"created_by\" : \"frame\";\n const functionName =\n kind === \"created_by\" ? trimmedLine.slice(\"created by \".length) : trimmedLine;\n frames.push({\n functionName,\n displayName: sanitizeFunctionName(functionName),\n kind,\n runtime: isRuntimeFrame(functionName),\n });\n }\n\n for (const frame of frames) {\n if (frame.file) {\n frame.file = normalizeFilePath(frame.file);\n frame.location = `${frame.file}${frame.line ? `:${frame.line}` : \"\"}`;\n }\n }\n\n const topFunction = frames.find((frame) => frame.kind === \"frame\")?.functionName;\n\n goroutines.push({\n id,\n state,\n rawState,\n frames,\n raw: block,\n userFrameCount: frames.filter((frame) => !frame.runtime && frame.kind === \"frame\").length,\n searchText: `${header}\\n${frames\n .map((frame) => `${frame.functionName} ${frame.file || \"\"}`)\n .join(\"\\n\")}`.toLowerCase(),\n ...(topFunction !== undefined ? { topFunction } : {}),\n });\n }\n\n return goroutines;\n}\n\nexport function countGoroutinesByState(goroutines: ParsedGoroutine[]): Map<string, number> {\n const counts = new Map<string, number>();\n for (const goroutine of goroutines) {\n counts.set(goroutine.state, (counts.get(goroutine.state) || 0) + 1);\n }\n return counts;\n}\n\nfunction normalizeState(value: string): string {\n return (value.split(\",\")[0] ?? \"\").trim().toLowerCase();\n}\n\nfunction isRuntimeFrame(functionName: string): boolean {\n return (\n functionName.startsWith(\"runtime.\") ||\n functionName.startsWith(\"runtime/\") ||\n functionName.startsWith(\"internal/\") ||\n functionName.startsWith(\"runtime/internal/\") ||\n functionName.startsWith(\"syscall.\") ||\n functionName.startsWith(\"reflect.\")\n );\n}\n\nfunction splitGoroutineBlocks(text: string): string[] {\n const blocks: string[] = [];\n let lines: string[] = [];\n for (const line of text.split(\"\\n\")) {\n if (line.trim()) {\n lines.push(line);\n } else if (lines.length > 0) {\n blocks.push(lines.join(\"\\n\"));\n lines = [];\n }\n }\n if (lines.length > 0) blocks.push(lines.join(\"\\n\"));\n return blocks;\n}\n\nfunction sanitizeFunctionName(functionName: string): string {\n let name = functionName.trim();\n const paren = name.indexOf(\"(\");\n if (paren !== -1) name = name.slice(0, paren);\n return stripPackageQualifier(name);\n}\n\nfunction stripPackageQualifier(name: string): string {\n return name.replace(/^((?:[^./\\s]+\\/)+)([^./\\s]+)\\./, \"$2.\");\n}\n\nfunction normalizeFilePath(path: string): string {\n return path\n .replace(goSrcPrefixRe, \"\")\n .replace(goWorkspacePrefixRe, \"\")\n .replace(goPkgModPrefixRe, \"\");\n}\n"],"names":["parseJvmThreadDump","countThreadsByState"],"mappings":";;;AASO,SAAS,iBAAiB,MAA0B;AACzD,QAAM,UAAU,KAAK,KAAA;AACrB,MAAI,CAAC,QAAS,QAAO;AAErB,MAAI,qCAAqC,KAAK,OAAO,EAAG,QAAO;AAE/D,MAAI,kCAAkC,KAAK,OAAO,EAAG,QAAO;AAC5D,SAAO;AACT;AAEO,SAAS,eAAe,MAA2B;AACxD,QAAM,SAAS,iBAAiB,IAAI;AACpC,MAAI,WAAW,OAAO;AACpB,WAAO,EAAE,QAAQ,OAAO,SAASA,cAAAA,mBAAmB,IAAI,EAAA;AAAA,EAC1D;AACA,MAAI,WAAW,MAAM;AACnB,WAAO,EAAE,QAAQ,MAAM,YAAY,mBAAmB,IAAI,EAAA;AAAA,EAC5D;AACA,SAAO,EAAE,QAAQ,WAAW,YAAY,CAAA,GAAI,SAAS,GAAC;AACxD;AAEO,SAAS,kBAAkB,OAAyC;AACzE,MAAI,MAAM,WAAW,MAAO,QAAOC,cAAAA,oBAAoB,MAAM,OAAO;AACpE,MAAI,MAAM,WAAW,KAAM,QAAO,uBAAuB,MAAM,UAAU;AACzE,6BAAW,IAAA;AACb;AAuBA,MAAM,WAAW;AACjB,MAAM,SAAS;AACf,MAAM,gBAAgB;AACtB,MAAM,sBAAsB;AAC5B,MAAM,mBAAmB;AAElB,SAAS,mBAAmB,MAAiC;;AAClE,QAAM,UAAU,KAAK,KAAA;AACrB,MAAI,CAAC,QAAS,QAAO,CAAA;AAErB,QAAM,SAAS,qBAAqB,OAAO;AAC3C,QAAM,aAAgC,CAAA;AAEtC,aAAW,SAAS,QAAQ;AAC1B,UAAM,QAAQ,MAAM,MAAM,IAAI,EAAE,IAAI,CAAC,SAAS,KAAK,QAAQ,OAAO,EAAE,CAAC;AACrE,UAAM,UAAS,WAAM,CAAC,MAAP,mBAAU;AACzB,UAAM,QAAQ,SAAS,KAAK,UAAU,EAAE;AACxC,QAAI,CAAC,MAAO;AAEZ,UAAM,KAAK,OAAO,MAAM,CAAC,KAAK,CAAC;AAC/B,UAAM,WAAW,MAAM,CAAC,KAAK;AAC7B,UAAM,QAAQ,eAAe,QAAQ;AACrC,UAAM,SAAiC,CAAA;AAEvC,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,YAAM,OAAO,MAAM,CAAC;AACpB,UAAI,QAAQ,KAAM;AAClB,YAAM,cAAc,KAAK,KAAA;AACzB,UAAI,CAAC,YAAa;AAElB,YAAM,YAAY,OAAO,KAAK,WAAW;AACzC,YAAM,gBAAgB,OAAO,OAAO,SAAS,CAAC;AAC9C,UAAI,aAAa,iBAAiB,CAAC,cAAc,MAAM;AACrD,sBAAc,OAAO,UAAU,CAAC,KAAK;AACrC,sBAAc,OAAO,OAAO,UAAU,CAAC,KAAK,CAAC;AAC7C;AAAA,MACF;AAEA,YAAM,OAAO,YAAY,WAAW,aAAa,IAAI,eAAe;AACpE,YAAM,eACJ,SAAS,eAAe,YAAY,MAAM,cAAc,MAAM,IAAI;AACpE,aAAO,KAAK;AAAA,QACV;AAAA,QACA,aAAa,qBAAqB,YAAY;AAAA,QAC9C;AAAA,QACA,SAAS,eAAe,YAAY;AAAA,MAAA,CACrC;AAAA,IACH;AAEA,eAAW,SAAS,QAAQ;AAC1B,UAAI,MAAM,MAAM;AACd,cAAM,OAAO,kBAAkB,MAAM,IAAI;AACzC,cAAM,WAAW,GAAG,MAAM,IAAI,GAAG,MAAM,OAAO,IAAI,MAAM,IAAI,KAAK,EAAE;AAAA,MACrE;AAAA,IACF;AAEA,UAAM,eAAc,YAAO,KAAK,CAAC,UAAU,MAAM,SAAS,OAAO,MAA7C,mBAAgD;AAEpE,eAAW,KAAK;AAAA,MACd;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,KAAK;AAAA,MACL,gBAAgB,OAAO,OAAO,CAAC,UAAU,CAAC,MAAM,WAAW,MAAM,SAAS,OAAO,EAAE;AAAA,MACnF,YAAY,GAAG,MAAM;AAAA,EAAK,OACvB,IAAI,CAAC,UAAU,GAAG,MAAM,YAAY,IAAI,MAAM,QAAQ,EAAE,EAAE,EAC1D,KAAK,IAAI,CAAC,GAAG,YAAA;AAAA,MAChB,GAAI,gBAAgB,SAAY,EAAE,gBAAgB,CAAA;AAAA,IAAC,CACpD;AAAA,EACH;AAEA,SAAO;AACT;AAEO,SAAS,uBAAuB,YAAoD;AACzF,QAAM,6BAAa,IAAA;AACnB,aAAW,aAAa,YAAY;AAClC,WAAO,IAAI,UAAU,QAAQ,OAAO,IAAI,UAAU,KAAK,KAAK,KAAK,CAAC;AAAA,EACpE;AACA,SAAO;AACT;AAEA,SAAS,eAAe,OAAuB;AAC7C,UAAQ,MAAM,MAAM,GAAG,EAAE,CAAC,KAAK,IAAI,KAAA,EAAO,YAAA;AAC5C;AAEA,SAAS,eAAe,cAA+B;AACrD,SACE,aAAa,WAAW,UAAU,KAClC,aAAa,WAAW,UAAU,KAClC,aAAa,WAAW,WAAW,KACnC,aAAa,WAAW,mBAAmB,KAC3C,aAAa,WAAW,UAAU,KAClC,aAAa,WAAW,UAAU;AAEtC;AAEA,SAAS,qBAAqB,MAAwB;AACpD,QAAM,SAAmB,CAAA;AACzB,MAAI,QAAkB,CAAA;AACtB,aAAW,QAAQ,KAAK,MAAM,IAAI,GAAG;AACnC,QAAI,KAAK,QAAQ;AACf,YAAM,KAAK,IAAI;AAAA,IACjB,WAAW,MAAM,SAAS,GAAG;AAC3B,aAAO,KAAK,MAAM,KAAK,IAAI,CAAC;AAC5B,cAAQ,CAAA;AAAA,IACV;AAAA,EACF;AACA,MAAI,MAAM,SAAS,EAAG,QAAO,KAAK,MAAM,KAAK,IAAI,CAAC;AAClD,SAAO;AACT;AAEA,SAAS,qBAAqB,cAA8B;AAC1D,MAAI,OAAO,aAAa,KAAA;AACxB,QAAM,QAAQ,KAAK,QAAQ,GAAG;AAC9B,MAAI,UAAU,GAAI,QAAO,KAAK,MAAM,GAAG,KAAK;AAC5C,SAAO,sBAAsB,IAAI;AACnC;AAEA,SAAS,sBAAsB,MAAsB;AACnD,SAAO,KAAK,QAAQ,kCAAkC,KAAK;AAC7D;AAEA,SAAS,kBAAkB,MAAsB;AAC/C,SAAO,KACJ,QAAQ,eAAe,EAAE,EACzB,QAAQ,qBAAqB,EAAE,EAC/B,QAAQ,kBAAkB,EAAE;AACjC;;;;;;"}
@@ -30,7 +30,7 @@ function parseGoroutineDump(text) {
30
30
  var _a, _b;
31
31
  const trimmed = text.trim();
32
32
  if (!trimmed) return [];
33
- const blocks = trimmed.split(/\n\s*\n+/);
33
+ const blocks = splitGoroutineBlocks(trimmed);
34
34
  const goroutines = [];
35
35
  for (const block of blocks) {
36
36
  const lines = block.split("\n").map((line) => line.replace(/\r$/, ""));
@@ -96,11 +96,24 @@ function normalizeState(value) {
96
96
  function isRuntimeFrame(functionName) {
97
97
  return functionName.startsWith("runtime.") || functionName.startsWith("runtime/") || functionName.startsWith("internal/") || functionName.startsWith("runtime/internal/") || functionName.startsWith("syscall.") || functionName.startsWith("reflect.");
98
98
  }
99
+ function splitGoroutineBlocks(text) {
100
+ const blocks = [];
101
+ let lines = [];
102
+ for (const line of text.split("\n")) {
103
+ if (line.trim()) {
104
+ lines.push(line);
105
+ } else if (lines.length > 0) {
106
+ blocks.push(lines.join("\n"));
107
+ lines = [];
108
+ }
109
+ }
110
+ if (lines.length > 0) blocks.push(lines.join("\n"));
111
+ return blocks;
112
+ }
99
113
  function sanitizeFunctionName(functionName) {
100
114
  let name = functionName.trim();
101
115
  const paren = name.indexOf("(");
102
116
  if (paren !== -1) name = name.slice(0, paren);
103
- name = name.replace(/\.\(\*([^)]+)\)\./g, ".$1.");
104
117
  return stripPackageQualifier(name);
105
118
  }
106
119
  function stripPackageQualifier(name) {
@@ -1 +1 @@
1
- {"version":3,"file":"stacktrace.js","sources":["../../../src/data/diagnostics/stacktrace.ts"],"sourcesContent":["import { parseJvmThreadDump, countThreadsByState, type ParsedThread } from \"./jvm-stacktrace\";\n\nexport type DumpFormat = \"go\" | \"jvm\" | \"unknown\";\n\nexport type ParsedStack =\n | { format: \"go\"; goroutines: ParsedGoroutine[] }\n | { format: \"jvm\"; threads: ParsedThread[] }\n | { format: \"unknown\"; goroutines: []; threads: [] };\n\nexport function detectDumpFormat(text: string): DumpFormat {\n const trimmed = text.trim();\n if (!trimmed) return \"unknown\";\n // JVM header: `\"name\" ... prio=` OR `\"name\" ... tid=`\n if (/^\"[^\"]*\"[^\\n]*\\b(prio=|tid=|nid=)/m.test(trimmed)) return \"jvm\";\n // Go header: `goroutine N [state]:`\n if (/^goroutine\\s+\\d+\\s+\\[[^\\]]+\\]:/m.test(trimmed)) return \"go\";\n return \"unknown\";\n}\n\nexport function parseStackDump(text: string): ParsedStack {\n const format = detectDumpFormat(text);\n if (format === \"jvm\") {\n return { format: \"jvm\", threads: parseJvmThreadDump(text) };\n }\n if (format === \"go\") {\n return { format: \"go\", goroutines: parseGoroutineDump(text) };\n }\n return { format: \"unknown\", goroutines: [], threads: [] };\n}\n\nexport function countStackByState(stack: ParsedStack): Map<string, number> {\n if (stack.format === \"jvm\") return countThreadsByState(stack.threads);\n if (stack.format === \"go\") return countGoroutinesByState(stack.goroutines);\n return new Map();\n}\n\nexport interface ParsedGoroutineFrame {\n functionName: string;\n displayName: string;\n file?: string;\n location?: string;\n line?: number;\n kind: \"frame\" | \"created_by\";\n runtime: boolean;\n}\n\nexport interface ParsedGoroutine {\n id: number;\n state: string;\n rawState: string;\n frames: ParsedGoroutineFrame[];\n raw: string;\n userFrameCount: number;\n topFunction?: string;\n searchText: string;\n}\n\nconst headerRe = /^goroutine\\s+(\\d+)\\s+\\[(.+?)\\]:$/;\nconst fileRe = /^\\s*(.+?):(\\d+)(?:\\s+\\+0x[0-9a-f]+)?$/i;\nconst goSrcPrefixRe = /^\\/usr\\/local\\/go[\\d.]+\\/src\\//;\nconst goWorkspacePrefixRe = /^.*?\\/go\\/src\\//;\nconst goPkgModPrefixRe = /^.*?\\/go\\/pkg\\/mod\\//;\n\nexport function parseGoroutineDump(text: string): ParsedGoroutine[] {\n const trimmed = text.trim();\n if (!trimmed) return [];\n\n const blocks = trimmed.split(/\\n\\s*\\n+/);\n const goroutines: ParsedGoroutine[] = [];\n\n for (const block of blocks) {\n const lines = block.split(\"\\n\").map((line) => line.replace(/\\r$/, \"\"));\n const header = lines[0]?.trim();\n const match = headerRe.exec(header || \"\");\n if (!match) continue;\n\n const id = Number(match[1] ?? 0);\n const rawState = match[2] ?? \"\";\n const state = normalizeState(rawState);\n const frames: ParsedGoroutineFrame[] = [];\n\n for (let i = 1; i < lines.length; i++) {\n const line = lines[i];\n if (line == null) continue;\n const trimmedLine = line.trim();\n if (!trimmedLine) continue;\n\n const fileMatch = fileRe.exec(trimmedLine);\n const previousFrame = frames[frames.length - 1];\n if (fileMatch && previousFrame && !previousFrame.file) {\n previousFrame.file = fileMatch[1] ?? \"\";\n previousFrame.line = Number(fileMatch[2] ?? 0);\n continue;\n }\n\n const kind = trimmedLine.startsWith(\"created by \") ? \"created_by\" : \"frame\";\n const functionName =\n kind === \"created_by\" ? trimmedLine.slice(\"created by \".length) : trimmedLine;\n frames.push({\n functionName,\n displayName: sanitizeFunctionName(functionName),\n kind,\n runtime: isRuntimeFrame(functionName),\n });\n }\n\n for (const frame of frames) {\n if (frame.file) {\n frame.file = normalizeFilePath(frame.file);\n frame.location = `${frame.file}${frame.line ? `:${frame.line}` : \"\"}`;\n }\n }\n\n const topFunction = frames.find((frame) => frame.kind === \"frame\")?.functionName;\n\n goroutines.push({\n id,\n state,\n rawState,\n frames,\n raw: block,\n userFrameCount: frames.filter((frame) => !frame.runtime && frame.kind === \"frame\").length,\n searchText: `${header}\\n${frames\n .map((frame) => `${frame.functionName} ${frame.file || \"\"}`)\n .join(\"\\n\")}`.toLowerCase(),\n ...(topFunction !== undefined ? { topFunction } : {}),\n });\n }\n\n return goroutines;\n}\n\nexport function countGoroutinesByState(goroutines: ParsedGoroutine[]): Map<string, number> {\n const counts = new Map<string, number>();\n for (const goroutine of goroutines) {\n counts.set(goroutine.state, (counts.get(goroutine.state) || 0) + 1);\n }\n return counts;\n}\n\nfunction normalizeState(value: string): string {\n return (value.split(\",\")[0] ?? \"\").trim().toLowerCase();\n}\n\nfunction isRuntimeFrame(functionName: string): boolean {\n return (\n functionName.startsWith(\"runtime.\") ||\n functionName.startsWith(\"runtime/\") ||\n functionName.startsWith(\"internal/\") ||\n functionName.startsWith(\"runtime/internal/\") ||\n functionName.startsWith(\"syscall.\") ||\n functionName.startsWith(\"reflect.\")\n );\n}\n\nfunction sanitizeFunctionName(functionName: string): string {\n let name = functionName.trim();\n const paren = name.indexOf(\"(\");\n if (paren !== -1) name = name.slice(0, paren);\n name = name.replace(/\\.\\(\\*([^)]+)\\)\\./g, \".$1.\");\n return stripPackageQualifier(name);\n}\n\nfunction stripPackageQualifier(name: string): string {\n return name.replace(/^((?:[^./\\s]+\\/)+)([^./\\s]+)\\./, \"$2.\");\n}\n\nfunction normalizeFilePath(path: string): string {\n return path\n .replace(goSrcPrefixRe, \"\")\n .replace(goWorkspacePrefixRe, \"\")\n .replace(goPkgModPrefixRe, \"\");\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"stacktrace.js","sources":["../../../src/data/diagnostics/stacktrace.ts"],"sourcesContent":["import { parseJvmThreadDump, countThreadsByState, type ParsedThread } from \"./jvm-stacktrace\";\n\nexport type DumpFormat = \"go\" | \"jvm\" | \"unknown\";\n\nexport type ParsedStack =\n | { format: \"go\"; goroutines: ParsedGoroutine[] }\n | { format: \"jvm\"; threads: ParsedThread[] }\n | { format: \"unknown\"; goroutines: []; threads: [] };\n\nexport function detectDumpFormat(text: string): DumpFormat {\n const trimmed = text.trim();\n if (!trimmed) return \"unknown\";\n // JVM header: `\"name\" ... prio=` OR `\"name\" ... tid=`\n if (/^\"[^\"]*\"[^\\n]*\\b(prio=|tid=|nid=)/m.test(trimmed)) return \"jvm\";\n // Go header: `goroutine N [state]:`\n if (/^goroutine\\s+\\d+\\s+\\[[^\\]]+\\]:/m.test(trimmed)) return \"go\";\n return \"unknown\";\n}\n\nexport function parseStackDump(text: string): ParsedStack {\n const format = detectDumpFormat(text);\n if (format === \"jvm\") {\n return { format: \"jvm\", threads: parseJvmThreadDump(text) };\n }\n if (format === \"go\") {\n return { format: \"go\", goroutines: parseGoroutineDump(text) };\n }\n return { format: \"unknown\", goroutines: [], threads: [] };\n}\n\nexport function countStackByState(stack: ParsedStack): Map<string, number> {\n if (stack.format === \"jvm\") return countThreadsByState(stack.threads);\n if (stack.format === \"go\") return countGoroutinesByState(stack.goroutines);\n return new Map();\n}\n\nexport interface ParsedGoroutineFrame {\n functionName: string;\n displayName: string;\n file?: string;\n location?: string;\n line?: number;\n kind: \"frame\" | \"created_by\";\n runtime: boolean;\n}\n\nexport interface ParsedGoroutine {\n id: number;\n state: string;\n rawState: string;\n frames: ParsedGoroutineFrame[];\n raw: string;\n userFrameCount: number;\n topFunction?: string;\n searchText: string;\n}\n\nconst headerRe = /^goroutine\\s+(\\d+)\\s+\\[(.+?)\\]:$/;\nconst fileRe = /^\\s*(.+?):(\\d+)(?:\\s+\\+0x[0-9a-f]+)?$/i;\nconst goSrcPrefixRe = /^\\/usr\\/local\\/go[\\d.]+\\/src\\//;\nconst goWorkspacePrefixRe = /^.*?\\/go\\/src\\//;\nconst goPkgModPrefixRe = /^.*?\\/go\\/pkg\\/mod\\//;\n\nexport function parseGoroutineDump(text: string): ParsedGoroutine[] {\n const trimmed = text.trim();\n if (!trimmed) return [];\n\n const blocks = splitGoroutineBlocks(trimmed);\n const goroutines: ParsedGoroutine[] = [];\n\n for (const block of blocks) {\n const lines = block.split(\"\\n\").map((line) => line.replace(/\\r$/, \"\"));\n const header = lines[0]?.trim();\n const match = headerRe.exec(header || \"\");\n if (!match) continue;\n\n const id = Number(match[1] ?? 0);\n const rawState = match[2] ?? \"\";\n const state = normalizeState(rawState);\n const frames: ParsedGoroutineFrame[] = [];\n\n for (let i = 1; i < lines.length; i++) {\n const line = lines[i];\n if (line == null) continue;\n const trimmedLine = line.trim();\n if (!trimmedLine) continue;\n\n const fileMatch = fileRe.exec(trimmedLine);\n const previousFrame = frames[frames.length - 1];\n if (fileMatch && previousFrame && !previousFrame.file) {\n previousFrame.file = fileMatch[1] ?? \"\";\n previousFrame.line = Number(fileMatch[2] ?? 0);\n continue;\n }\n\n const kind = trimmedLine.startsWith(\"created by \") ? \"created_by\" : \"frame\";\n const functionName =\n kind === \"created_by\" ? trimmedLine.slice(\"created by \".length) : trimmedLine;\n frames.push({\n functionName,\n displayName: sanitizeFunctionName(functionName),\n kind,\n runtime: isRuntimeFrame(functionName),\n });\n }\n\n for (const frame of frames) {\n if (frame.file) {\n frame.file = normalizeFilePath(frame.file);\n frame.location = `${frame.file}${frame.line ? `:${frame.line}` : \"\"}`;\n }\n }\n\n const topFunction = frames.find((frame) => frame.kind === \"frame\")?.functionName;\n\n goroutines.push({\n id,\n state,\n rawState,\n frames,\n raw: block,\n userFrameCount: frames.filter((frame) => !frame.runtime && frame.kind === \"frame\").length,\n searchText: `${header}\\n${frames\n .map((frame) => `${frame.functionName} ${frame.file || \"\"}`)\n .join(\"\\n\")}`.toLowerCase(),\n ...(topFunction !== undefined ? { topFunction } : {}),\n });\n }\n\n return goroutines;\n}\n\nexport function countGoroutinesByState(goroutines: ParsedGoroutine[]): Map<string, number> {\n const counts = new Map<string, number>();\n for (const goroutine of goroutines) {\n counts.set(goroutine.state, (counts.get(goroutine.state) || 0) + 1);\n }\n return counts;\n}\n\nfunction normalizeState(value: string): string {\n return (value.split(\",\")[0] ?? \"\").trim().toLowerCase();\n}\n\nfunction isRuntimeFrame(functionName: string): boolean {\n return (\n functionName.startsWith(\"runtime.\") ||\n functionName.startsWith(\"runtime/\") ||\n functionName.startsWith(\"internal/\") ||\n functionName.startsWith(\"runtime/internal/\") ||\n functionName.startsWith(\"syscall.\") ||\n functionName.startsWith(\"reflect.\")\n );\n}\n\nfunction splitGoroutineBlocks(text: string): string[] {\n const blocks: string[] = [];\n let lines: string[] = [];\n for (const line of text.split(\"\\n\")) {\n if (line.trim()) {\n lines.push(line);\n } else if (lines.length > 0) {\n blocks.push(lines.join(\"\\n\"));\n lines = [];\n }\n }\n if (lines.length > 0) blocks.push(lines.join(\"\\n\"));\n return blocks;\n}\n\nfunction sanitizeFunctionName(functionName: string): string {\n let name = functionName.trim();\n const paren = name.indexOf(\"(\");\n if (paren !== -1) name = name.slice(0, paren);\n return stripPackageQualifier(name);\n}\n\nfunction stripPackageQualifier(name: string): string {\n return name.replace(/^((?:[^./\\s]+\\/)+)([^./\\s]+)\\./, \"$2.\");\n}\n\nfunction normalizeFilePath(path: string): string {\n return path\n .replace(goSrcPrefixRe, \"\")\n .replace(goWorkspacePrefixRe, \"\")\n .replace(goPkgModPrefixRe, \"\");\n}\n"],"names":[],"mappings":"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"}
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  function slugify(value) {
4
- const slug = (value || "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
4
+ let slug = (value || "").toLowerCase().replace(/[^a-z0-9]+/g, "-");
5
+ if (slug.startsWith("-")) slug = slug.slice(1);
6
+ if (slug.endsWith("-")) slug = slug.slice(0, -1);
5
7
  return slug || "node";
6
8
  }
7
9
  function annotateRoutePaths(nodes, parentSegments = []) {
@@ -1 +1 @@
1
- {"version":3,"file":"routePath.cjs","sources":["../../../src/data/test-runner/routePath.ts"],"sourcesContent":["// Stable, URL-safe paths for selection/navigation. A host that wants to encode\n// the selected node in its route annotates the forest once with annotateRoutePaths\n// (writing Test.route_path), then resolves a path back to a node with\n// findNodeByRoutePath. Tree's nodeKey already prefers route_path, so an annotated\n// forest also gets stable React keys for free.\n\nimport type { Test } from \"./types\";\n\nexport function slugify(value: string): string {\n const slug = (value || \"\")\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n return slug || \"node\";\n}\n\n/**\n * Return a deep copy of the forest with a stable, unique `route_path` on every\n * node. Each segment is the slug of the node's name; siblings that slug to the\n * same value are disambiguated with a `~N` ordinal so paths stay unique.\n */\nexport function annotateRoutePaths(nodes: Test[], parentSegments: string[] = []): Test[] {\n const counts = new Map<string, number>();\n for (const node of nodes) {\n const slug = slugify(node.name);\n counts.set(slug, (counts.get(slug) || 0) + 1);\n }\n\n const seen = new Map<string, number>();\n return nodes.map((node) => {\n const slug = slugify(node.name);\n const ordinal = (seen.get(slug) || 0) + 1;\n seen.set(slug, ordinal);\n const finalSlug = (counts.get(slug) || 0) > 1 ? `${slug}~${ordinal}` : slug;\n const segments = [...parentSegments, finalSlug];\n const annotated: Test = { ...node, route_path: segments.join(\"/\") };\n if (node.children) annotated.children = annotateRoutePaths(node.children, segments);\n return annotated;\n });\n}\n\n/** Depth-first lookup of the node whose route_path equals target, or null. */\nexport function findNodeByRoutePath(nodes: Test[], target: string): Test | null {\n if (!target) return null;\n for (const node of nodes) {\n if (node.route_path === target) return node;\n if (node.children) {\n const child = findNodeByRoutePath(node.children, target);\n if (child) return child;\n }\n }\n return null;\n}\n"],"names":[],"mappings":";;AAQO,SAAS,QAAQ,OAAuB;AAC7C,QAAM,QAAQ,SAAS,IACpB,YAAA,EACA,QAAQ,eAAe,GAAG,EAC1B,QAAQ,YAAY,EAAE;AACzB,SAAO,QAAQ;AACjB;AAOO,SAAS,mBAAmB,OAAe,iBAA2B,IAAY;AACvF,QAAM,6BAAa,IAAA;AACnB,aAAW,QAAQ,OAAO;AACxB,UAAM,OAAO,QAAQ,KAAK,IAAI;AAC9B,WAAO,IAAI,OAAO,OAAO,IAAI,IAAI,KAAK,KAAK,CAAC;AAAA,EAC9C;AAEA,QAAM,2BAAW,IAAA;AACjB,SAAO,MAAM,IAAI,CAAC,SAAS;AACzB,UAAM,OAAO,QAAQ,KAAK,IAAI;AAC9B,UAAM,WAAW,KAAK,IAAI,IAAI,KAAK,KAAK;AACxC,SAAK,IAAI,MAAM,OAAO;AACtB,UAAM,aAAa,OAAO,IAAI,IAAI,KAAK,KAAK,IAAI,GAAG,IAAI,IAAI,OAAO,KAAK;AACvE,UAAM,WAAW,CAAC,GAAG,gBAAgB,SAAS;AAC9C,UAAM,YAAkB,EAAE,GAAG,MAAM,YAAY,SAAS,KAAK,GAAG,EAAA;AAChE,QAAI,KAAK,SAAU,WAAU,WAAW,mBAAmB,KAAK,UAAU,QAAQ;AAClF,WAAO;AAAA,EACT,CAAC;AACH;AAGO,SAAS,oBAAoB,OAAe,QAA6B;AAC9E,MAAI,CAAC,OAAQ,QAAO;AACpB,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,eAAe,OAAQ,QAAO;AACvC,QAAI,KAAK,UAAU;AACjB,YAAM,QAAQ,oBAAoB,KAAK,UAAU,MAAM;AACvD,UAAI,MAAO,QAAO;AAAA,IACpB;AAAA,EACF;AACA,SAAO;AACT;;;;"}
1
+ {"version":3,"file":"routePath.cjs","sources":["../../../src/data/test-runner/routePath.ts"],"sourcesContent":["// Stable, URL-safe paths for selection/navigation. A host that wants to encode\n// the selected node in its route annotates the forest once with annotateRoutePaths\n// (writing Test.route_path), then resolves a path back to a node with\n// findNodeByRoutePath. Tree's nodeKey already prefers route_path, so an annotated\n// forest also gets stable React keys for free.\n\nimport type { Test } from \"./types\";\n\nexport function slugify(value: string): string {\n let slug = (value || \"\").toLowerCase().replace(/[^a-z0-9]+/g, \"-\");\n if (slug.startsWith(\"-\")) slug = slug.slice(1);\n if (slug.endsWith(\"-\")) slug = slug.slice(0, -1);\n return slug || \"node\";\n}\n\n/**\n * Return a deep copy of the forest with a stable, unique `route_path` on every\n * node. Each segment is the slug of the node's name; siblings that slug to the\n * same value are disambiguated with a `~N` ordinal so paths stay unique.\n */\nexport function annotateRoutePaths(nodes: Test[], parentSegments: string[] = []): Test[] {\n const counts = new Map<string, number>();\n for (const node of nodes) {\n const slug = slugify(node.name);\n counts.set(slug, (counts.get(slug) || 0) + 1);\n }\n\n const seen = new Map<string, number>();\n return nodes.map((node) => {\n const slug = slugify(node.name);\n const ordinal = (seen.get(slug) || 0) + 1;\n seen.set(slug, ordinal);\n const finalSlug = (counts.get(slug) || 0) > 1 ? `${slug}~${ordinal}` : slug;\n const segments = [...parentSegments, finalSlug];\n const annotated: Test = { ...node, route_path: segments.join(\"/\") };\n if (node.children) annotated.children = annotateRoutePaths(node.children, segments);\n return annotated;\n });\n}\n\n/** Depth-first lookup of the node whose route_path equals target, or null. */\nexport function findNodeByRoutePath(nodes: Test[], target: string): Test | null {\n if (!target) return null;\n for (const node of nodes) {\n if (node.route_path === target) return node;\n if (node.children) {\n const child = findNodeByRoutePath(node.children, target);\n if (child) return child;\n }\n }\n return null;\n}\n"],"names":[],"mappings":";;AAQO,SAAS,QAAQ,OAAuB;AAC7C,MAAI,QAAQ,SAAS,IAAI,cAAc,QAAQ,eAAe,GAAG;AACjE,MAAI,KAAK,WAAW,GAAG,EAAG,QAAO,KAAK,MAAM,CAAC;AAC7C,MAAI,KAAK,SAAS,GAAG,UAAU,KAAK,MAAM,GAAG,EAAE;AAC/C,SAAO,QAAQ;AACjB;AAOO,SAAS,mBAAmB,OAAe,iBAA2B,IAAY;AACvF,QAAM,6BAAa,IAAA;AACnB,aAAW,QAAQ,OAAO;AACxB,UAAM,OAAO,QAAQ,KAAK,IAAI;AAC9B,WAAO,IAAI,OAAO,OAAO,IAAI,IAAI,KAAK,KAAK,CAAC;AAAA,EAC9C;AAEA,QAAM,2BAAW,IAAA;AACjB,SAAO,MAAM,IAAI,CAAC,SAAS;AACzB,UAAM,OAAO,QAAQ,KAAK,IAAI;AAC9B,UAAM,WAAW,KAAK,IAAI,IAAI,KAAK,KAAK;AACxC,SAAK,IAAI,MAAM,OAAO;AACtB,UAAM,aAAa,OAAO,IAAI,IAAI,KAAK,KAAK,IAAI,GAAG,IAAI,IAAI,OAAO,KAAK;AACvE,UAAM,WAAW,CAAC,GAAG,gBAAgB,SAAS;AAC9C,UAAM,YAAkB,EAAE,GAAG,MAAM,YAAY,SAAS,KAAK,GAAG,EAAA;AAChE,QAAI,KAAK,SAAU,WAAU,WAAW,mBAAmB,KAAK,UAAU,QAAQ;AAClF,WAAO;AAAA,EACT,CAAC;AACH;AAGO,SAAS,oBAAoB,OAAe,QAA6B;AAC9E,MAAI,CAAC,OAAQ,QAAO;AACpB,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,eAAe,OAAQ,QAAO;AACvC,QAAI,KAAK,UAAU;AACjB,YAAM,QAAQ,oBAAoB,KAAK,UAAU,MAAM;AACvD,UAAI,MAAO,QAAO;AAAA,IACpB;AAAA,EACF;AACA,SAAO;AACT;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"routePath.d.ts","sourceRoot":"","sources":["../../../src/data/test-runner/routePath.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAEpC,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAM7C;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,cAAc,GAAE,MAAM,EAAO,GAAG,IAAI,EAAE,CAkBvF;AAED,8EAA8E;AAC9E,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAU9E"}
1
+ {"version":3,"file":"routePath.d.ts","sourceRoot":"","sources":["../../../src/data/test-runner/routePath.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAEpC,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAK7C;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,cAAc,GAAE,MAAM,EAAO,GAAG,IAAI,EAAE,CAkBvF;AAED,8EAA8E;AAC9E,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAU9E"}
@@ -1,5 +1,7 @@
1
1
  function slugify(value) {
2
- const slug = (value || "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
2
+ let slug = (value || "").toLowerCase().replace(/[^a-z0-9]+/g, "-");
3
+ if (slug.startsWith("-")) slug = slug.slice(1);
4
+ if (slug.endsWith("-")) slug = slug.slice(0, -1);
3
5
  return slug || "node";
4
6
  }
5
7
  function annotateRoutePaths(nodes, parentSegments = []) {
@@ -1 +1 @@
1
- {"version":3,"file":"routePath.js","sources":["../../../src/data/test-runner/routePath.ts"],"sourcesContent":["// Stable, URL-safe paths for selection/navigation. A host that wants to encode\n// the selected node in its route annotates the forest once with annotateRoutePaths\n// (writing Test.route_path), then resolves a path back to a node with\n// findNodeByRoutePath. Tree's nodeKey already prefers route_path, so an annotated\n// forest also gets stable React keys for free.\n\nimport type { Test } from \"./types\";\n\nexport function slugify(value: string): string {\n const slug = (value || \"\")\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n return slug || \"node\";\n}\n\n/**\n * Return a deep copy of the forest with a stable, unique `route_path` on every\n * node. Each segment is the slug of the node's name; siblings that slug to the\n * same value are disambiguated with a `~N` ordinal so paths stay unique.\n */\nexport function annotateRoutePaths(nodes: Test[], parentSegments: string[] = []): Test[] {\n const counts = new Map<string, number>();\n for (const node of nodes) {\n const slug = slugify(node.name);\n counts.set(slug, (counts.get(slug) || 0) + 1);\n }\n\n const seen = new Map<string, number>();\n return nodes.map((node) => {\n const slug = slugify(node.name);\n const ordinal = (seen.get(slug) || 0) + 1;\n seen.set(slug, ordinal);\n const finalSlug = (counts.get(slug) || 0) > 1 ? `${slug}~${ordinal}` : slug;\n const segments = [...parentSegments, finalSlug];\n const annotated: Test = { ...node, route_path: segments.join(\"/\") };\n if (node.children) annotated.children = annotateRoutePaths(node.children, segments);\n return annotated;\n });\n}\n\n/** Depth-first lookup of the node whose route_path equals target, or null. */\nexport function findNodeByRoutePath(nodes: Test[], target: string): Test | null {\n if (!target) return null;\n for (const node of nodes) {\n if (node.route_path === target) return node;\n if (node.children) {\n const child = findNodeByRoutePath(node.children, target);\n if (child) return child;\n }\n }\n return null;\n}\n"],"names":[],"mappings":"AAQO,SAAS,QAAQ,OAAuB;AAC7C,QAAM,QAAQ,SAAS,IACpB,YAAA,EACA,QAAQ,eAAe,GAAG,EAC1B,QAAQ,YAAY,EAAE;AACzB,SAAO,QAAQ;AACjB;AAOO,SAAS,mBAAmB,OAAe,iBAA2B,IAAY;AACvF,QAAM,6BAAa,IAAA;AACnB,aAAW,QAAQ,OAAO;AACxB,UAAM,OAAO,QAAQ,KAAK,IAAI;AAC9B,WAAO,IAAI,OAAO,OAAO,IAAI,IAAI,KAAK,KAAK,CAAC;AAAA,EAC9C;AAEA,QAAM,2BAAW,IAAA;AACjB,SAAO,MAAM,IAAI,CAAC,SAAS;AACzB,UAAM,OAAO,QAAQ,KAAK,IAAI;AAC9B,UAAM,WAAW,KAAK,IAAI,IAAI,KAAK,KAAK;AACxC,SAAK,IAAI,MAAM,OAAO;AACtB,UAAM,aAAa,OAAO,IAAI,IAAI,KAAK,KAAK,IAAI,GAAG,IAAI,IAAI,OAAO,KAAK;AACvE,UAAM,WAAW,CAAC,GAAG,gBAAgB,SAAS;AAC9C,UAAM,YAAkB,EAAE,GAAG,MAAM,YAAY,SAAS,KAAK,GAAG,EAAA;AAChE,QAAI,KAAK,SAAU,WAAU,WAAW,mBAAmB,KAAK,UAAU,QAAQ;AAClF,WAAO;AAAA,EACT,CAAC;AACH;AAGO,SAAS,oBAAoB,OAAe,QAA6B;AAC9E,MAAI,CAAC,OAAQ,QAAO;AACpB,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,eAAe,OAAQ,QAAO;AACvC,QAAI,KAAK,UAAU;AACjB,YAAM,QAAQ,oBAAoB,KAAK,UAAU,MAAM;AACvD,UAAI,MAAO,QAAO;AAAA,IACpB;AAAA,EACF;AACA,SAAO;AACT;"}
1
+ {"version":3,"file":"routePath.js","sources":["../../../src/data/test-runner/routePath.ts"],"sourcesContent":["// Stable, URL-safe paths for selection/navigation. A host that wants to encode\n// the selected node in its route annotates the forest once with annotateRoutePaths\n// (writing Test.route_path), then resolves a path back to a node with\n// findNodeByRoutePath. Tree's nodeKey already prefers route_path, so an annotated\n// forest also gets stable React keys for free.\n\nimport type { Test } from \"./types\";\n\nexport function slugify(value: string): string {\n let slug = (value || \"\").toLowerCase().replace(/[^a-z0-9]+/g, \"-\");\n if (slug.startsWith(\"-\")) slug = slug.slice(1);\n if (slug.endsWith(\"-\")) slug = slug.slice(0, -1);\n return slug || \"node\";\n}\n\n/**\n * Return a deep copy of the forest with a stable, unique `route_path` on every\n * node. Each segment is the slug of the node's name; siblings that slug to the\n * same value are disambiguated with a `~N` ordinal so paths stay unique.\n */\nexport function annotateRoutePaths(nodes: Test[], parentSegments: string[] = []): Test[] {\n const counts = new Map<string, number>();\n for (const node of nodes) {\n const slug = slugify(node.name);\n counts.set(slug, (counts.get(slug) || 0) + 1);\n }\n\n const seen = new Map<string, number>();\n return nodes.map((node) => {\n const slug = slugify(node.name);\n const ordinal = (seen.get(slug) || 0) + 1;\n seen.set(slug, ordinal);\n const finalSlug = (counts.get(slug) || 0) > 1 ? `${slug}~${ordinal}` : slug;\n const segments = [...parentSegments, finalSlug];\n const annotated: Test = { ...node, route_path: segments.join(\"/\") };\n if (node.children) annotated.children = annotateRoutePaths(node.children, segments);\n return annotated;\n });\n}\n\n/** Depth-first lookup of the node whose route_path equals target, or null. */\nexport function findNodeByRoutePath(nodes: Test[], target: string): Test | null {\n if (!target) return null;\n for (const node of nodes) {\n if (node.route_path === target) return node;\n if (node.children) {\n const child = findNodeByRoutePath(node.children, target);\n if (child) return child;\n }\n }\n return null;\n}\n"],"names":[],"mappings":"AAQO,SAAS,QAAQ,OAAuB;AAC7C,MAAI,QAAQ,SAAS,IAAI,cAAc,QAAQ,eAAe,GAAG;AACjE,MAAI,KAAK,WAAW,GAAG,EAAG,QAAO,KAAK,MAAM,CAAC;AAC7C,MAAI,KAAK,SAAS,GAAG,UAAU,KAAK,MAAM,GAAG,EAAE;AAC/C,SAAO,QAAQ;AACjB;AAOO,SAAS,mBAAmB,OAAe,iBAA2B,IAAY;AACvF,QAAM,6BAAa,IAAA;AACnB,aAAW,QAAQ,OAAO;AACxB,UAAM,OAAO,QAAQ,KAAK,IAAI;AAC9B,WAAO,IAAI,OAAO,OAAO,IAAI,IAAI,KAAK,KAAK,CAAC;AAAA,EAC9C;AAEA,QAAM,2BAAW,IAAA;AACjB,SAAO,MAAM,IAAI,CAAC,SAAS;AACzB,UAAM,OAAO,QAAQ,KAAK,IAAI;AAC9B,UAAM,WAAW,KAAK,IAAI,IAAI,KAAK,KAAK;AACxC,SAAK,IAAI,MAAM,OAAO;AACtB,UAAM,aAAa,OAAO,IAAI,IAAI,KAAK,KAAK,IAAI,GAAG,IAAI,IAAI,OAAO,KAAK;AACvE,UAAM,WAAW,CAAC,GAAG,gBAAgB,SAAS;AAC9C,UAAM,YAAkB,EAAE,GAAG,MAAM,YAAY,SAAS,KAAK,GAAG,EAAA;AAChE,QAAI,KAAK,SAAU,WAAU,WAAW,mBAAmB,KAAK,UAAU,QAAQ;AAClF,WAAO;AAAA,EACT,CAAC;AACH;AAGO,SAAS,oBAAoB,OAAe,QAA6B;AAC9E,MAAI,CAAC,OAAQ,QAAO;AACpB,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,eAAe,OAAQ,QAAO;AACvC,QAAI,KAAK,UAAU;AACjB,YAAM,QAAQ,oBAAoB,KAAK,UAAU,MAAM;AACvD,UAAI,MAAO,QAAO;AAAA,IACpB;AAAA,EACF;AACA,SAAO;AACT;"}
@@ -15,9 +15,9 @@ function detectMode() {
15
15
  }
16
16
  function getVersionInfo() {
17
17
  return {
18
- commit: read(() => "8467248c", ""),
19
- tag: read(() => "clicky-ui@0.3.11", ""),
20
- date: read(() => "2026-07-16T15:46:57.662Z", ""),
18
+ commit: read(() => "0eb4c401", ""),
19
+ tag: read(() => "clicky-ui@0.3.12", ""),
20
+ date: read(() => "2026-07-16T18:21:17.612Z", ""),
21
21
  dirty: read(() => true, false),
22
22
  mode: detectMode()
23
23
  };
@@ -13,9 +13,9 @@ function detectMode() {
13
13
  }
14
14
  function getVersionInfo() {
15
15
  return {
16
- commit: read(() => "8467248c", ""),
17
- tag: read(() => "clicky-ui@0.3.11", ""),
18
- date: read(() => "2026-07-16T15:46:39.266Z", ""),
16
+ commit: read(() => "0eb4c401", ""),
17
+ tag: read(() => "clicky-ui@0.3.12", ""),
18
+ date: read(() => "2026-07-16T18:20:59.418Z", ""),
19
19
  dirty: read(() => true, false),
20
20
  mode: detectMode()
21
21
  };
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ function stripLeadingSlashes(value) {
4
+ let start = 0;
5
+ while (start < value.length && value[start] === "/") start++;
6
+ return value.slice(start);
7
+ }
8
+ function stripTrailingSlashes(value) {
9
+ let end = value.length;
10
+ while (end > 0 && value[end - 1] === "/") end--;
11
+ return value.slice(0, end);
12
+ }
13
+ exports.stripLeadingSlashes = stripLeadingSlashes;
14
+ exports.stripTrailingSlashes = stripTrailingSlashes;
15
+ //# sourceMappingURL=string.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"string.cjs","sources":["../../src/lib/string.ts"],"sourcesContent":["export function stripLeadingSlashes(value: string): string {\n let start = 0;\n while (start < value.length && value[start] === \"/\") start++;\n return value.slice(start);\n}\n\nexport function stripTrailingSlashes(value: string): string {\n let end = value.length;\n while (end > 0 && value[end - 1] === \"/\") end--;\n return value.slice(0, end);\n}\n"],"names":[],"mappings":";;AAAO,SAAS,oBAAoB,OAAuB;AACzD,MAAI,QAAQ;AACZ,SAAO,QAAQ,MAAM,UAAU,MAAM,KAAK,MAAM,IAAK;AACrD,SAAO,MAAM,MAAM,KAAK;AAC1B;AAEO,SAAS,qBAAqB,OAAuB;AAC1D,MAAI,MAAM,MAAM;AAChB,SAAO,MAAM,KAAK,MAAM,MAAM,CAAC,MAAM,IAAK;AAC1C,SAAO,MAAM,MAAM,GAAG,GAAG;AAC3B;;;"}
@@ -0,0 +1,3 @@
1
+ export declare function stripLeadingSlashes(value: string): string;
2
+ export declare function stripTrailingSlashes(value: string): string;
3
+ //# sourceMappingURL=string.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"string.d.ts","sourceRoot":"","sources":["../../src/lib/string.ts"],"names":[],"mappings":"AAAA,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIzD;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAI1D"}
@@ -0,0 +1,15 @@
1
+ function stripLeadingSlashes(value) {
2
+ let start = 0;
3
+ while (start < value.length && value[start] === "/") start++;
4
+ return value.slice(start);
5
+ }
6
+ function stripTrailingSlashes(value) {
7
+ let end = value.length;
8
+ while (end > 0 && value[end - 1] === "/") end--;
9
+ return value.slice(0, end);
10
+ }
11
+ export {
12
+ stripLeadingSlashes,
13
+ stripTrailingSlashes
14
+ };
15
+ //# sourceMappingURL=string.js.map