@tsplc/debug 0.2.16 → 0.2.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/format.js CHANGED
@@ -1,4 +1,5 @@
1
1
  // packages/debug/src/paths.ts
2
+ import { isArrayIndexSegment, symbolPathSegments } from "@tsplc/opcua/contract";
2
3
  var typeOf = (manifest, id) => manifest.types[id] ?? { kind: "scalar", iec: id };
3
4
 
4
5
  // packages/debug/src/ui/format.ts
@@ -11,30 +12,39 @@ var valueStateLabels = {
11
12
  constant: "constant"
12
13
  };
13
14
  var valueState = (quality, ageMs, context) => {
14
- if (quality === "unavailable")
15
+ if (quality === "unavailable") {
15
16
  return "unavailable";
16
- if (!context.connected || ageMs > 3 * context.intervalMs)
17
+ }
18
+ if (!context.connected || ageMs > 3 * context.intervalMs) {
17
19
  return "stale";
18
- if (quality === "bad")
20
+ }
21
+ if (quality === "bad") {
19
22
  return "bad";
20
- if (quality === "uncertain")
23
+ }
24
+ if (quality === "uncertain") {
21
25
  return "uncertain";
26
+ }
22
27
  return "live";
23
28
  };
24
29
  var formatAge = (ms) => {
25
- if (ms < 1000)
30
+ if (ms < 1000) {
26
31
  return "now";
27
- if (ms < 60000)
32
+ }
33
+ if (ms < 60000) {
28
34
  return `${Math.floor(ms / 1000)} s ago`;
29
- if (ms < 3600000)
35
+ }
36
+ if (ms < 3600000) {
30
37
  return `${Math.floor(ms / 60000)} min ago`;
38
+ }
31
39
  return `${Math.floor(ms / 3600000)} h ago`;
32
40
  };
33
41
  var formatDuration = (ms) => {
34
- if (ms < 1000)
42
+ if (ms < 1000) {
35
43
  return `${Math.round(ms)} ms`;
36
- if (ms < 60000)
44
+ }
45
+ if (ms < 60000) {
37
46
  return `${(ms / 1000).toFixed(1)} s`;
47
+ }
38
48
  return `${Math.floor(ms / 60000)} min ${Math.floor(ms % 60000 / 1000)} s`;
39
49
  };
40
50
  var formatInterval = (ms) => ms < 1000 ? `${ms} ms` : `${ms / 1000} s`;
@@ -45,24 +55,31 @@ var formatClock = (iso) => {
45
55
  };
46
56
  var formatReadAt = (iso) => `read ${formatClock(iso).slice(0, 8)}`;
47
57
  var formatScalar = (value) => {
48
- if (typeof value === "boolean")
58
+ if (typeof value === "boolean") {
49
59
  return value ? "TRUE" : "FALSE";
50
- if (typeof value === "number")
60
+ }
61
+ if (typeof value === "number") {
51
62
  return Number.isInteger(value) ? String(value) : String(Number(value.toPrecision(6)));
52
- if (typeof value === "string")
63
+ }
64
+ if (typeof value === "string") {
53
65
  return JSON.stringify(value);
54
- if (value === null || value === undefined)
66
+ }
67
+ if (value === null || value === undefined) {
55
68
  return "—";
69
+ }
56
70
  return JSON.stringify(value);
57
71
  };
58
72
  var formatValue = (manifest, typeId, value) => {
59
73
  const type = typeOf(manifest, typeId);
60
- if (type.kind !== "enum")
74
+ if (type.kind !== "enum") {
61
75
  return formatScalar(value);
62
- if (typeof value === "string" && Object.hasOwn(type.members, value))
76
+ }
77
+ if (typeof value === "string" && Object.hasOwn(type.members, value)) {
63
78
  return value;
64
- if (typeof value !== "number")
79
+ }
80
+ if (typeof value !== "number") {
65
81
  return formatScalar(value);
82
+ }
66
83
  const member = Object.entries(type.members).find(([, number]) => number === value)?.[0];
67
84
  return member ?? `${value} (not a ${type.name})`;
68
85
  };
@@ -84,8 +101,9 @@ var formatProperties = (properties) => Object.entries(properties).map(([key, val
84
101
  return `${key}=${text.length > PROPERTY_TEXT ? `${text.slice(0, PROPERTY_TEXT)}…` : text}`;
85
102
  }).join(" ");
86
103
  var fileHref = (projectRoot, file, line, column) => {
87
- if (projectRoot === undefined)
104
+ if (projectRoot === undefined) {
88
105
  return;
106
+ }
89
107
  const root = projectRoot.replaceAll("\\", "/").replace(/\/$/, "");
90
108
  const absolute = `${root}/${file}`.replace(/^\/?/, "/");
91
109
  return `vscode://file${absolute}:${line}:${column}`;
@@ -117,5 +135,5 @@ export {
117
135
  valueStateLabels
118
136
  };
119
137
 
120
- //# debugId=78E800313C3FB5FD64756E2164756E21
138
+ //# debugId=2270CEF4083E476B64756E2164756E21
121
139
  //# sourceMappingURL=format.js.map
@@ -2,10 +2,10 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/paths.ts", "../src/ui/format.ts"],
4
4
  "sourcesContent": [
5
- "import type { DebugManifest, DebugType, SourceRange } from \"./manifest.ts\";\n\n/**\n * TypeScript paths over a manifest: `program.diverters.binTrackers[10].bin` names a root\n * (`program.diverters`), then record fields and array indexes. Pure, so the browser UI can use it.\n */\n\n/** A path that reads a target, or a constant the compiler folded. */\nexport type ResolvedPath =\n | {\n kind: \"target\";\n path: string;\n target: string;\n type: string;\n readable: boolean;\n generated?: true;\n declaration?: SourceRange;\n }\n | { kind: \"constant\"; path: string; type: string; value: unknown; declaration?: SourceRange };\n\n/** Roots or constants that share a prefix, such as `program` above `program.diverters`. */\ninterface GroupPath {\n kind: \"group\";\n path: string;\n}\n\n/** A child in the instance tree. */\nexport type PathNode = { name: string } & (ResolvedPath | GroupPath);\n\nconst segmentPattern = /[^.[\\]]+|\\[(?:-?\\d+|\\*)\\]/g;\n\nexport const pathSegments = (path: string) => path.match(segmentPattern) ?? [];\n\nexport const joinPath = (prefix: string, segment: string) =>\n segment.startsWith(\"[\") || !prefix ? `${prefix}${segment}` : `${prefix}.${segment}`;\n\n/** Scalar type ids are their IEC names, and may have no entry in `types`. */\nexport const typeOf = (manifest: DebugManifest, id: string): DebugType =>\n manifest.types[id] ?? { kind: \"scalar\", iec: id };\n\nexport const createPaths = (manifest: DebugManifest) => {\n const typeOfId = (id: string) => typeOf(manifest, id);\n // I/O bindings are readable symbols with TypeScript paths of their own.\n const bases = new Map<string, Extract<ResolvedPath, { kind: \"target\" }>>([\n ...manifest.roots.map(\n ({ path, ...root }) => [path, { kind: \"target\" as const, path, ...root }] as const,\n ),\n ...manifest.io.map(\n ({ path, target, type }) =>\n [path, { kind: \"target\" as const, path, target, type, readable: true }] as const,\n ),\n ]);\n const constants = new Map(\n manifest.constants.map(({ path, ...constant }) => [\n path,\n { kind: \"constant\" as const, path, ...constant },\n ]),\n );\n /** Children of the prefixes of every root and constant path, which are groups unless they resolve. */\n const groups = new Map<string, Set<string>>();\n for (const path of [...bases.keys(), ...constants.keys()]) {\n let prefix = \"\";\n for (const segment of pathSegments(path)) {\n let children = groups.get(prefix);\n if (!children) groups.set(prefix, (children = new Set()));\n children.add(segment);\n prefix = joinPath(prefix, segment);\n }\n }\n\n const resolve = (path: string): ResolvedPath | undefined => {\n const constant = constants.get(path);\n if (constant) return constant;\n const segments = pathSegments(path);\n for (let length = segments.length; length > 0; length--) {\n const base = bases.get(segments.slice(0, length).reduce(joinPath, \"\"));\n if (!base) continue;\n let resolved: ResolvedPath = base;\n for (const segment of segments.slice(length)) {\n const type = typeOfId(resolved.type);\n if (type.kind === \"record\") {\n const field = type.fields.find(({ name }) => name === segment);\n if (!field) return;\n resolved = {\n kind: \"target\",\n path: joinPath(resolved.path, segment),\n target: `${resolved.target}.${field.target}`,\n type: field.type,\n readable: resolved.readable,\n ...(field.generated && { generated: true }),\n ...(field.declaration && { declaration: field.declaration }),\n };\n } else if (type.kind === \"array\") {\n const index = /^\\[(-?\\d+)\\]$/.exec(segment)?.[1];\n if (\n index === undefined ||\n Number(index) < type.lower ||\n Number(index) >= type.lower + type.length\n )\n return;\n resolved = {\n kind: \"target\",\n path: joinPath(resolved.path, segment),\n target: `${resolved.target}[${index}]`,\n type: type.element,\n readable: resolved.readable,\n };\n } else return;\n }\n return resolved;\n }\n };\n\n const typedChildren = (resolved: ResolvedPath | undefined) => {\n const type = resolved?.kind === \"target\" ? typeOfId(resolved.type) : undefined;\n if (type?.kind === \"record\") return type.fields.map(({ name }) => name);\n if (type?.kind === \"array\")\n return Array.from({ length: type.length }, (_, index) => `[${type.lower + index}]`);\n return [];\n };\n\n /** A path's fields or indexes, and the roots and constants below it. */\n const children = (path: string): PathNode[] => {\n const names = [...typedChildren(resolve(path)), ...(groups.get(path) ?? [])];\n return [...new Set(names)].map((name) => {\n const child = joinPath(path, name);\n return { name, ...(resolve(child) ?? { kind: \"group\", path: child }) };\n });\n };\n\n const isLeaf = (node: ResolvedPath | GroupPath) =>\n node.kind === \"constant\" ||\n (node.kind === \"target\" && [\"scalar\", \"enum\"].includes(typeOfId(node.type).kind));\n\n /**\n * Leaf paths matching `patterns`, where `*` stands for one segment. A pattern naming a record,\n * array or group stands for all its leaves; wildcards and expansion skip compiler-made fields.\n * Paths that don't resolve are kept, so callers can report them.\n */\n const expand = (patterns: readonly string[], limit = 2000) => {\n const leaves = new Set<string>();\n const add = (path: string) => {\n leaves.add(path);\n if (leaves.size > limit) throw new RangeError(`More than ${limit} values match`);\n };\n const below = (node: ResolvedPath | GroupPath) => {\n if (isLeaf(node)) return add(node.path);\n for (const child of children(node.path))\n if (!(child.kind === \"target\" && child.generated)) below(child);\n };\n const match = (prefix: string, rest: readonly string[], wildcard: boolean) => {\n const [head, ...tail] = rest;\n if (head === undefined) {\n const node: ResolvedPath | GroupPath | undefined =\n resolve(prefix) ?? (groups.has(prefix) ? { kind: \"group\", path: prefix } : undefined);\n if (node) below(node);\n else if (!wildcard) add(prefix);\n return;\n }\n if (head === \"*\" || head === \"[*]\") {\n for (const child of children(prefix))\n if (\n !(child.kind === \"target\" && child.generated) &&\n (head === \"*\" || child.name.startsWith(\"[\"))\n )\n match(child.path, tail, true);\n } else match(joinPath(prefix, head), tail, wildcard);\n };\n for (const pattern of patterns) match(\"\", pathSegments(pattern), false);\n return [...leaves];\n };\n\n /** A value as TypeScript shows it: enum members by name, BOOL as a boolean. */\n const decode = (typeId: string, raw: unknown) => {\n const type = typeOfId(typeId);\n if (type.kind === \"enum\")\n return Object.entries(type.members).find(([, value]) => value === raw)?.[0] ?? raw;\n if (type.kind === \"scalar\" && type.iec === \"BOOL\" && typeof raw === \"number\") return raw !== 0;\n return raw;\n };\n\n return { typeOf: typeOfId, resolve, children, isLeaf, expand, decode };\n};\n\nexport type Paths = ReturnType<typeof createPaths>;\n",
6
- "import type { Quality } from \"../api.ts\";\nimport type { DebugManifest, SourceRange } from \"../manifest.ts\";\nimport { typeOf } from \"../paths.ts\";\n\n/** How a value is shown: each state has its own styling and label, so none passes for live. */\nexport type ValueState = \"live\" | \"uncertain\" | \"bad\" | \"stale\" | \"unavailable\" | \"constant\";\n\nexport const valueStateLabels: Record<ValueState, string> = {\n live: \"good\",\n uncertain: \"uncertain\",\n bad: \"bad\",\n stale: \"stale\",\n unavailable: \"unavailable\",\n constant: \"constant\",\n};\n\n/** A sample is stale when the controller connection is down or the sample is older than three sample intervals. */\nexport const valueState = (\n quality: Quality,\n ageMs: number,\n context: { connected: boolean; intervalMs: number },\n): ValueState => {\n if (quality === \"unavailable\") return \"unavailable\";\n if (!context.connected || ageMs > 3 * context.intervalMs) return \"stale\";\n if (quality === \"bad\") return \"bad\";\n if (quality === \"uncertain\") return \"uncertain\";\n return \"live\";\n};\n\nexport const formatAge = (ms: number) => {\n if (ms < 1000) return \"now\";\n if (ms < 60_000) return `${Math.floor(ms / 1000)} s ago`;\n if (ms < 3_600_000) return `${Math.floor(ms / 60_000)} min ago`;\n return `${Math.floor(ms / 3_600_000)} h ago`;\n};\n\nexport const formatDuration = (ms: number) => {\n if (ms < 1000) return `${Math.round(ms)} ms`;\n if (ms < 60_000) return `${(ms / 1000).toFixed(1)} s`;\n return `${Math.floor(ms / 60_000)} min ${Math.floor((ms % 60_000) / 1000)} s`;\n};\n\n/** How often values are read: `250 ms`, `1 s`, `1.5 s`. */\nexport const formatInterval = (ms: number) => (ms < 1000 ? `${ms} ms` : `${ms / 1000} s`);\n\n/** Local time of day with milliseconds, for ordering events by eye. */\nexport const formatClock = (iso: string) => {\n const date = new Date(iso);\n const pad = (value: number, width = 2) => String(value).padStart(width, \"0\");\n return `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}.${pad(date.getMilliseconds(), 3)}`;\n};\n\n/** When a value was read, to the second: `read 14:03:27`. */\nexport const formatReadAt = (iso: string) => `read ${formatClock(iso).slice(0, 8)}`;\n\n/** A value of unknown type: booleans as IEC literals, strings quoted. */\nexport const formatScalar = (value: unknown): string => {\n if (typeof value === \"boolean\") return value ? \"TRUE\" : \"FALSE\";\n if (typeof value === \"number\")\n return Number.isInteger(value) ? String(value) : String(Number(value.toPrecision(6)));\n if (typeof value === \"string\") return JSON.stringify(value);\n if (value === null || value === undefined) return \"—\";\n return JSON.stringify(value);\n};\n\n/** A value in the program's terms: enum members by name, whether the host decoded them already or not. */\nexport const formatValue = (manifest: DebugManifest, typeId: string, value: unknown): string => {\n const type = typeOf(manifest, typeId);\n if (type.kind !== \"enum\") return formatScalar(value);\n if (typeof value === \"string\" && Object.hasOwn(type.members, value)) return value;\n if (typeof value !== \"number\") return formatScalar(value);\n const member = Object.entries(type.members).find(([, number]) => number === value)?.[0];\n return member ?? `${value} (not a ${type.name})`;\n};\n\nexport const typeLabel = (manifest: DebugManifest, typeId: string): string => {\n const type = typeOf(manifest, typeId);\n switch (type.kind) {\n case \"scalar\":\n return type.iec;\n case \"enum\":\n case \"record\":\n return type.name;\n case \"array\":\n return `${typeLabel(manifest, type.element)}[${type.length}]`;\n }\n};\n\nconst PROPERTY_TEXT = 16;\n\n/** A record's properties as `barcode=CON-MD3-884… scanner=3`, long strings cut short. */\nexport const formatProperties = (properties: Record<string, unknown>) =>\n Object.entries(properties)\n .map(([key, value]) => {\n const text = typeof value === \"string\" ? value : formatScalar(value);\n return `${key}=${text.length > PROPERTY_TEXT ? `${text.slice(0, PROPERTY_TEXT)}…` : text}`;\n })\n .join(\" \");\n\n/** A VS Code link to a project file, when the project root on the debugger host is known. */\nconst fileHref = (projectRoot: string | undefined, file: string, line: number, column: number) => {\n if (projectRoot === undefined) return undefined;\n const root = projectRoot.replaceAll(\"\\\\\", \"/\").replace(/\\/$/, \"\");\n const absolute = `${root}/${file}`.replace(/^\\/?/, \"/\");\n return `vscode://file${absolute}:${line}:${column}`;\n};\n\n/** `file:line`, and a VS Code link when the project root on the debugger host is known. */\nexport const sourceLocation = (\n manifest: DebugManifest,\n projectRoot: string | undefined,\n range: SourceRange,\n) => {\n const file = manifest.sources[range.file] ?? `source ${range.file}`;\n const href = fileHref(projectRoot, file, range.line, range.column);\n return { label: `${file}:${range.line}`, ...(href && { href }) };\n};\n\n/** A record's project-relative `file:line` as `basename:line`, with the same VS Code link. */\nexport const recordLocation = (projectRoot: string | undefined, source: string) => {\n const [, file = source, line] = /^(.*):(\\d+)$/.exec(source) ?? [];\n const label = `${file.slice(file.lastIndexOf(\"/\") + 1)}${line ? `:${line}` : \"\"}`;\n const href = line === undefined ? undefined : fileHref(projectRoot, file, Number(line), 1);\n return { label, ...(href && { href }) };\n};\n"
5
+ "import { isArrayIndexSegment, symbolPathSegments } from \"@tsplc/opcua/contract\";\nimport type { DebugManifest, DebugType, SourceRange } from \"./manifest.ts\";\n\n/**\n * TypeScript paths over a manifest: `program.diverters.binTrackers[10].bin` names a root\n * (`program.diverters`), then record fields and array indexes. Pure, so the browser UI can use it.\n */\n\n/** A path that reads a target, or a constant the compiler folded. */\nexport type ResolvedPath =\n\t| {\n\t\t\tkind: \"target\";\n\t\t\tpath: string;\n\t\t\ttarget: string;\n\t\t\ttype: string;\n\t\t\treadable: boolean;\n\t\t\tgenerated?: true;\n\t\t\tdeclaration?: SourceRange;\n\t }\n\t| {\n\t\t\tkind: \"constant\";\n\t\t\tpath: string;\n\t\t\ttype: string;\n\t\t\tvalue: unknown;\n\t\t\tdeclaration?: SourceRange;\n\t };\n\n/** Roots or constants that share a prefix, such as `program` above `program.diverters`. */\ninterface GroupPath {\n\tkind: \"group\";\n\tpath: string;\n}\n\n/** A child in the instance tree. */\nexport type PathNode = { name: string } & (ResolvedPath | GroupPath);\n\n/** In a pattern, every index of an array. */\nconst anyIndex = \"[*]\";\n\n/** Names and indexes as the controller's symbol paths split them, and `[*]` in patterns. */\nexport const pathSegments = (path: string) =>\n\tpath\n\t\t.split(anyIndex)\n\t\t.flatMap((part, index) => [\n\t\t\t...(index > 0 ? [anyIndex] : []),\n\t\t\t...symbolPathSegments(part),\n\t\t]);\n\nexport const joinPath = (prefix: string, segment: string) =>\n\tsegment.startsWith(\"[\") || !prefix\n\t\t? `${prefix}${segment}`\n\t\t: `${prefix}.${segment}`;\n\n/** Scalar type ids are their IEC names, and may have no entry in `types`. */\nexport const typeOf = (manifest: DebugManifest, id: string): DebugType =>\n\tmanifest.types[id] ?? { kind: \"scalar\", iec: id };\n\nexport const createPaths = (manifest: DebugManifest) => {\n\tconst typeOfId = (id: string) => typeOf(manifest, id);\n\t// I/O bindings are readable symbols with TypeScript paths of their own.\n\tconst bases = new Map<string, Extract<ResolvedPath, { kind: \"target\" }>>([\n\t\t...manifest.roots.map(\n\t\t\t({ path, ...root }) =>\n\t\t\t\t[path, { kind: \"target\" as const, path, ...root }] as const,\n\t\t),\n\t\t...manifest.io.map(\n\t\t\t({ path, target, type }) =>\n\t\t\t\t[\n\t\t\t\t\tpath,\n\t\t\t\t\t{ kind: \"target\" as const, path, target, type, readable: true },\n\t\t\t\t] as const,\n\t\t),\n\t]);\n\tconst constants = new Map(\n\t\tmanifest.constants.map(({ path, ...constant }) => [\n\t\t\tpath,\n\t\t\t{ kind: \"constant\" as const, path, ...constant },\n\t\t]),\n\t);\n\t/** Children of the prefixes of every root and constant path, which are groups unless they resolve. */\n\tconst groups = new Map<string, Set<string>>();\n\tfor (const path of [...bases.keys(), ...constants.keys()]) {\n\t\tlet prefix = \"\";\n\t\tfor (const segment of pathSegments(path)) {\n\t\t\tlet children = groups.get(prefix);\n\t\t\tif (!children) {\n\t\t\t\tgroups.set(prefix, (children = new Set()));\n\t\t\t}\n\t\t\tchildren.add(segment);\n\t\t\tprefix = joinPath(prefix, segment);\n\t\t}\n\t}\n\n\tconst resolve = (path: string): ResolvedPath | undefined => {\n\t\tconst constant = constants.get(path);\n\t\tif (constant) {\n\t\t\treturn constant;\n\t\t}\n\t\tconst segments = pathSegments(path);\n\t\tfor (let length = segments.length; length > 0; length--) {\n\t\t\tconst base = bases.get(segments.slice(0, length).reduce(joinPath, \"\"));\n\t\t\tif (!base) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlet resolved: ResolvedPath = base;\n\t\t\tfor (const segment of segments.slice(length)) {\n\t\t\t\tconst type = typeOfId(resolved.type);\n\t\t\t\tif (type.kind === \"record\") {\n\t\t\t\t\tconst field = type.fields.find(({ name }) => name === segment);\n\t\t\t\t\tif (!field) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tresolved = {\n\t\t\t\t\t\tkind: \"target\",\n\t\t\t\t\t\tpath: joinPath(resolved.path, segment),\n\t\t\t\t\t\ttarget: `${resolved.target}.${field.target}`,\n\t\t\t\t\t\ttype: field.type,\n\t\t\t\t\t\treadable: resolved.readable,\n\t\t\t\t\t\t...(field.generated && { generated: true }),\n\t\t\t\t\t\t...(field.declaration && { declaration: field.declaration }),\n\t\t\t\t\t};\n\t\t\t\t} else if (type.kind === \"array\") {\n\t\t\t\t\tif (!isArrayIndexSegment(segment)) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tconst index = Number(segment.slice(1, -1));\n\t\t\t\t\tif (index < type.lower || index >= type.lower + type.length) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tresolved = {\n\t\t\t\t\t\tkind: \"target\",\n\t\t\t\t\t\tpath: joinPath(resolved.path, segment),\n\t\t\t\t\t\ttarget: `${resolved.target}${segment}`,\n\t\t\t\t\t\ttype: type.element,\n\t\t\t\t\t\treadable: resolved.readable,\n\t\t\t\t\t};\n\t\t\t\t} else {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn resolved;\n\t\t}\n\t};\n\n\tconst typedChildren = (resolved: ResolvedPath | undefined) => {\n\t\tconst type =\n\t\t\tresolved?.kind === \"target\" ? typeOfId(resolved.type) : undefined;\n\t\tif (type?.kind === \"record\") {\n\t\t\treturn type.fields.map(({ name }) => name);\n\t\t}\n\t\tif (type?.kind === \"array\") {\n\t\t\treturn Array.from(\n\t\t\t\t{ length: type.length },\n\t\t\t\t(_, index) => `[${type.lower + index}]`,\n\t\t\t);\n\t\t}\n\t\treturn [];\n\t};\n\n\t/** A path's fields or indexes, and the roots and constants below it. */\n\tconst children = (path: string): PathNode[] => {\n\t\tconst names = [\n\t\t\t...typedChildren(resolve(path)),\n\t\t\t...(groups.get(path) ?? []),\n\t\t];\n\t\treturn [...new Set(names)].map((name) => {\n\t\t\tconst child = joinPath(path, name);\n\t\t\treturn { name, ...(resolve(child) ?? { kind: \"group\", path: child }) };\n\t\t});\n\t};\n\n\tconst isLeaf = (node: ResolvedPath | GroupPath) =>\n\t\tnode.kind === \"constant\" ||\n\t\t(node.kind === \"target\" &&\n\t\t\t[\"scalar\", \"enum\"].includes(typeOfId(node.type).kind));\n\n\t/**\n\t * Leaf paths matching `patterns`, where `*` stands for one segment. A pattern naming a record,\n\t * array or group stands for all its leaves; wildcards and expansion skip compiler-made fields.\n\t * Paths that don't resolve are kept, so callers can report them.\n\t */\n\tconst expand = (patterns: readonly string[], limit = 2000) => {\n\t\tconst leaves = new Set<string>();\n\t\tconst add = (path: string) => {\n\t\t\tleaves.add(path);\n\t\t\tif (leaves.size > limit) {\n\t\t\t\tthrow new RangeError(`More than ${limit} values match`);\n\t\t\t}\n\t\t};\n\t\tconst below = (node: ResolvedPath | GroupPath) => {\n\t\t\tif (isLeaf(node)) {\n\t\t\t\treturn add(node.path);\n\t\t\t}\n\t\t\tfor (const child of children(node.path)) {\n\t\t\t\tif (!(child.kind === \"target\" && child.generated)) {\n\t\t\t\t\tbelow(child);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t\tconst match = (\n\t\t\tprefix: string,\n\t\t\trest: readonly string[],\n\t\t\twildcard: boolean,\n\t\t) => {\n\t\t\tconst [head, ...tail] = rest;\n\t\t\tif (head === undefined) {\n\t\t\t\tconst node: ResolvedPath | GroupPath | undefined =\n\t\t\t\t\tresolve(prefix) ??\n\t\t\t\t\t(groups.has(prefix) ? { kind: \"group\", path: prefix } : undefined);\n\t\t\t\tif (node) {\n\t\t\t\t\tbelow(node);\n\t\t\t\t} else if (!wildcard) {\n\t\t\t\t\tadd(prefix);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (head === \"*\" || head === anyIndex) {\n\t\t\t\tfor (const child of children(prefix)) {\n\t\t\t\t\tif (\n\t\t\t\t\t\t!(child.kind === \"target\" && child.generated) &&\n\t\t\t\t\t\t(head === \"*\" || child.name.startsWith(\"[\"))\n\t\t\t\t\t) {\n\t\t\t\t\t\tmatch(child.path, tail, true);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tmatch(joinPath(prefix, head), tail, wildcard);\n\t\t\t}\n\t\t};\n\t\tfor (const pattern of patterns) {\n\t\t\tmatch(\"\", pathSegments(pattern), false);\n\t\t}\n\t\treturn [...leaves];\n\t};\n\n\t/** A value as TypeScript shows it: enum members by name, BOOL as a boolean. */\n\tconst decode = (typeId: string, raw: unknown) => {\n\t\tconst type = typeOfId(typeId);\n\t\tif (type.kind === \"enum\") {\n\t\t\treturn (\n\t\t\t\tObject.entries(type.members).find(([, value]) => value === raw)?.[0] ??\n\t\t\t\traw\n\t\t\t);\n\t\t}\n\t\tif (\n\t\t\ttype.kind === \"scalar\" &&\n\t\t\ttype.iec === \"BOOL\" &&\n\t\t\ttypeof raw === \"number\"\n\t\t) {\n\t\t\treturn raw !== 0;\n\t\t}\n\t\treturn raw;\n\t};\n\n\treturn { typeOf: typeOfId, resolve, children, isLeaf, expand, decode };\n};\n\nexport type Paths = ReturnType<typeof createPaths>;\n",
6
+ "import type { Quality } from \"../api.ts\";\nimport type { DebugManifest, SourceRange } from \"../manifest.ts\";\nimport { typeOf } from \"../paths.ts\";\n\n/** How a value is shown: each state has its own styling and label, so none passes for live. */\nexport type ValueState =\n\t\"live\" | \"uncertain\" | \"bad\" | \"stale\" | \"unavailable\" | \"constant\";\n\nexport const valueStateLabels: Record<ValueState, string> = {\n\tlive: \"good\",\n\tuncertain: \"uncertain\",\n\tbad: \"bad\",\n\tstale: \"stale\",\n\tunavailable: \"unavailable\",\n\tconstant: \"constant\",\n};\n\n/** A sample is stale when the controller connection is down or the sample is older than three sample intervals. */\nexport const valueState = (\n\tquality: Quality,\n\tageMs: number,\n\tcontext: { connected: boolean; intervalMs: number },\n): ValueState => {\n\tif (quality === \"unavailable\") {\n\t\treturn \"unavailable\";\n\t}\n\tif (!context.connected || ageMs > 3 * context.intervalMs) {\n\t\treturn \"stale\";\n\t}\n\tif (quality === \"bad\") {\n\t\treturn \"bad\";\n\t}\n\tif (quality === \"uncertain\") {\n\t\treturn \"uncertain\";\n\t}\n\treturn \"live\";\n};\n\nexport const formatAge = (ms: number) => {\n\tif (ms < 1000) {\n\t\treturn \"now\";\n\t}\n\tif (ms < 60_000) {\n\t\treturn `${Math.floor(ms / 1000)} s ago`;\n\t}\n\tif (ms < 3_600_000) {\n\t\treturn `${Math.floor(ms / 60_000)} min ago`;\n\t}\n\treturn `${Math.floor(ms / 3_600_000)} h ago`;\n};\n\nexport const formatDuration = (ms: number) => {\n\tif (ms < 1000) {\n\t\treturn `${Math.round(ms)} ms`;\n\t}\n\tif (ms < 60_000) {\n\t\treturn `${(ms / 1000).toFixed(1)} s`;\n\t}\n\treturn `${Math.floor(ms / 60_000)} min ${Math.floor((ms % 60_000) / 1000)} s`;\n};\n\n/** How often values are read: `250 ms`, `1 s`, `1.5 s`. */\nexport const formatInterval = (ms: number) =>\n\tms < 1000 ? `${ms} ms` : `${ms / 1000} s`;\n\n/** Local time of day with milliseconds, for ordering events by eye. */\nexport const formatClock = (iso: string) => {\n\tconst date = new Date(iso);\n\tconst pad = (value: number, width = 2) => String(value).padStart(width, \"0\");\n\treturn `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}.${pad(date.getMilliseconds(), 3)}`;\n};\n\n/** When a value was read, to the second: `read 14:03:27`. */\nexport const formatReadAt = (iso: string) =>\n\t`read ${formatClock(iso).slice(0, 8)}`;\n\n/** A value of unknown type: booleans as IEC literals, strings quoted. */\nexport const formatScalar = (value: unknown): string => {\n\tif (typeof value === \"boolean\") {\n\t\treturn value ? \"TRUE\" : \"FALSE\";\n\t}\n\tif (typeof value === \"number\") {\n\t\treturn Number.isInteger(value)\n\t\t\t? String(value)\n\t\t\t: String(Number(value.toPrecision(6)));\n\t}\n\tif (typeof value === \"string\") {\n\t\treturn JSON.stringify(value);\n\t}\n\tif (value === null || value === undefined) {\n\t\treturn \"—\";\n\t}\n\treturn JSON.stringify(value);\n};\n\n/** A value in the program's terms: enum members by name, whether the host decoded them already or not. */\nexport const formatValue = (\n\tmanifest: DebugManifest,\n\ttypeId: string,\n\tvalue: unknown,\n): string => {\n\tconst type = typeOf(manifest, typeId);\n\tif (type.kind !== \"enum\") {\n\t\treturn formatScalar(value);\n\t}\n\tif (typeof value === \"string\" && Object.hasOwn(type.members, value)) {\n\t\treturn value;\n\t}\n\tif (typeof value !== \"number\") {\n\t\treturn formatScalar(value);\n\t}\n\tconst member = Object.entries(type.members).find(\n\t\t([, number]) => number === value,\n\t)?.[0];\n\treturn member ?? `${value} (not a ${type.name})`;\n};\n\nexport const typeLabel = (manifest: DebugManifest, typeId: string): string => {\n\tconst type = typeOf(manifest, typeId);\n\tswitch (type.kind) {\n\t\tcase \"scalar\":\n\t\t\treturn type.iec;\n\t\tcase \"enum\":\n\t\tcase \"record\":\n\t\t\treturn type.name;\n\t\tcase \"array\":\n\t\t\treturn `${typeLabel(manifest, type.element)}[${type.length}]`;\n\t}\n};\n\nconst PROPERTY_TEXT = 16;\n\n/** A record's properties as `barcode=CON-MD3-884… scanner=3`, long strings cut short. */\nexport const formatProperties = (properties: Record<string, unknown>) =>\n\tObject.entries(properties)\n\t\t.map(([key, value]) => {\n\t\t\tconst text = typeof value === \"string\" ? value : formatScalar(value);\n\t\t\treturn `${key}=${text.length > PROPERTY_TEXT ? `${text.slice(0, PROPERTY_TEXT)}…` : text}`;\n\t\t})\n\t\t.join(\" \");\n\n/** A VS Code link to a project file, when the project root on the debugger host is known. */\nconst fileHref = (\n\tprojectRoot: string | undefined,\n\tfile: string,\n\tline: number,\n\tcolumn: number,\n) => {\n\tif (projectRoot === undefined) {\n\t\treturn undefined;\n\t}\n\tconst root = projectRoot.replaceAll(\"\\\\\", \"/\").replace(/\\/$/, \"\");\n\tconst absolute = `${root}/${file}`.replace(/^\\/?/, \"/\");\n\treturn `vscode://file${absolute}:${line}:${column}`;\n};\n\n/** `file:line`, and a VS Code link when the project root on the debugger host is known. */\nexport const sourceLocation = (\n\tmanifest: DebugManifest,\n\tprojectRoot: string | undefined,\n\trange: SourceRange,\n) => {\n\tconst file = manifest.sources[range.file] ?? `source ${range.file}`;\n\tconst href = fileHref(projectRoot, file, range.line, range.column);\n\treturn { label: `${file}:${range.line}`, ...(href && { href }) };\n};\n\n/** A record's project-relative `file:line` as `basename:line`, with the same VS Code link. */\nexport const recordLocation = (\n\tprojectRoot: string | undefined,\n\tsource: string,\n) => {\n\tconst [, file = source, line] = /^(.*):(\\d+)$/.exec(source) ?? [];\n\tconst label = `${file.slice(file.lastIndexOf(\"/\") + 1)}${line ? `:${line}` : \"\"}`;\n\tconst href =\n\t\tline === undefined\n\t\t\t? undefined\n\t\t\t: fileHref(projectRoot, file, Number(line), 1);\n\treturn { label, ...(href && { href }) };\n};\n"
7
7
  ],
8
- "mappings": "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",
9
- "debugId": "78E800313C3FB5FD64756E2164756E21",
8
+ "mappings": "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",
9
+ "debugId": "2270CEF4083E476B64756E2164756E21",
10
10
  "names": []
11
11
  }