limina 0.1.1 → 0.1.3

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.
@@ -1,5 +1,4 @@
1
- import { a as __toESM, t as require_picocolors } from "./dep-BPK-6PAr.js";
2
-
1
+ import { a as __toESM, t as require_picocolors } from "./dep-BT-sPH_d.js";
3
2
  //#region ../../node_modules/.pnpm/publint@0.3.17/node_modules/publint/src/shared/utils.js
4
3
  /**
5
4
  * @param {string[]} path
@@ -28,7 +27,6 @@ function replaceLast(str, search, replace) {
28
27
  if (index === -1) return str;
29
28
  return str.slice(0, index) + replace + str.slice(index + search.length);
30
29
  }
31
-
32
30
  //#endregion
33
31
  //#region ../../node_modules/.pnpm/publint@0.3.17/node_modules/publint/src/shared/message.js
34
32
  var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
@@ -189,6 +187,5 @@ function exportsRel(s) {
189
187
  if (s[0] === "/") return "." + s;
190
188
  return "./" + s;
191
189
  }
192
-
193
190
  //#endregion
194
- export { formatMessage, formatMessagePath, getPkgPathValue };
191
+ export { formatMessage, formatMessagePath, getPkgPathValue };
@@ -0,0 +1,240 @@
1
+ //#region src/flow/render-model.ts
2
+ const ANSI_RESET = "\x1B[0m";
3
+ const ANSI_GREEN = "\x1B[32m";
4
+ const ANSI_RED = "\x1B[31m";
5
+ const ANSI_YELLOW = "\x1B[33m";
6
+ const DEFAULT_TERMINAL_COLUMNS = 80;
7
+ const TERMINAL_FRAME_MARGIN_LINES = 1;
8
+ const TERMINAL_FRAME_CONTEXT_LINES = 6;
9
+ const OMITTED_LINES_MARKER = "│ ...";
10
+ const ANSI_ESCAPE = String.fromCodePoint(27);
11
+ const ANSI_PATTERN = new RegExp(String.raw`${ANSI_ESCAPE}\[[\d:;<=>?]*[\u0020-\u002F]*[\u0040-\u007E]`, "gu");
12
+ const SPINNER_FRAMES = [
13
+ "⠋",
14
+ "⠙",
15
+ "⠹",
16
+ "⠸",
17
+ "⠼",
18
+ "⠴",
19
+ "⠦",
20
+ "⠧",
21
+ "⠇",
22
+ "⠏"
23
+ ];
24
+ const FLOW_SYMBOL_BY_STATUS = {
25
+ fail: "✕",
26
+ info: "│",
27
+ pass: "◆",
28
+ planned: "◇",
29
+ skip: "◇",
30
+ start: "◇",
31
+ warn: "▲"
32
+ };
33
+ function colorInteractiveSymbol(status, symbol) {
34
+ if (status === "pass") return `${ANSI_GREEN}${symbol}${ANSI_RESET}`;
35
+ if (status === "fail") return `${ANSI_RED}${symbol}${ANSI_RESET}`;
36
+ if (status === "warn") return `${ANSI_YELLOW}${symbol}${ANSI_RESET}`;
37
+ return symbol;
38
+ }
39
+ function formatElapsedTime(milliseconds) {
40
+ if (milliseconds < 1e3) return `${Math.round(milliseconds)}ms`;
41
+ return `${(milliseconds / 1e3).toFixed(2)}s`;
42
+ }
43
+ function formatMessageWithElapsed(message, elapsedTimeMs) {
44
+ return typeof elapsedTimeMs === "number" ? `${message} (${formatElapsedTime(elapsedTimeMs)})` : message;
45
+ }
46
+ function indentMessage(message, depth) {
47
+ if (depth <= 0) return message;
48
+ return `${" ".repeat(depth)}${message}`;
49
+ }
50
+ function formatInteractiveLine(status, message, depth, spinnerFrameIndex) {
51
+ const renderedMessage = indentMessage(message, depth);
52
+ return `${colorInteractiveSymbol(status, status === "start" ? SPINNER_FRAMES[spinnerFrameIndex % SPINNER_FRAMES.length] : FLOW_SYMBOL_BY_STATUS[status])} ${renderedMessage}`;
53
+ }
54
+ function toTreeFlowStatus(status) {
55
+ switch (status) {
56
+ case "failed": return "fail";
57
+ case "passed": return "pass";
58
+ case "planned": return "planned";
59
+ case "running": return "start";
60
+ case "skipped": return "skip";
61
+ }
62
+ throw new Error(`Unsupported flow tree node status: ${status}`);
63
+ }
64
+ function isTreeNodeTerminal(node) {
65
+ return node.status === "failed" || node.status === "passed" || node.status === "skipped";
66
+ }
67
+ function areTreeNodeDescendantsTerminal(node) {
68
+ return node.children.every((child) => isTreeNodeTerminal(child) && areTreeNodeDescendantsTerminal(child));
69
+ }
70
+ function renderTreeNodeLine(node, spinnerFrameIndex) {
71
+ const elapsedTimeMs = isTreeNodeTerminal(node) && areTreeNodeDescendantsTerminal(node) ? node.elapsedTimeMs : void 0;
72
+ return formatInteractiveLine(toTreeFlowStatus(node.status), formatMessageWithElapsed(node.message, elapsedTimeMs), node.depth, spinnerFrameIndex);
73
+ }
74
+ function renderTreeNodeLines(node, spinnerFrameIndex) {
75
+ return [renderTreeNodeLine(node, spinnerFrameIndex), ...node.children.flatMap((child) => renderTreeNodeLines(child, spinnerFrameIndex))];
76
+ }
77
+ function renderCompactTreeNodeLines(node, spinnerFrameIndex) {
78
+ return [renderTreeNodeLine(node, spinnerFrameIndex), ...node.children.map((child) => renderTreeNodeLine(child, spinnerFrameIndex))];
79
+ }
80
+ function renderSnapshotLines(snapshot, spinnerFrameIndex) {
81
+ const lines = snapshot.entries.flatMap((entry) => {
82
+ if (entry.kind === "line") return [entry.line];
83
+ if (entry.kind === "flow-line") return [formatInteractiveLine(entry.status, formatMessageWithElapsed(entry.message, entry.elapsedTimeMs), entry.depth, spinnerFrameIndex)];
84
+ return snapshot.treeRoots.flatMap((root) => renderTreeNodeLines(root, spinnerFrameIndex));
85
+ });
86
+ return snapshot.outroMessage ? [...lines, `└ ${snapshot.outroMessage}`] : lines;
87
+ }
88
+ function renderCompactSnapshotLines(snapshot, spinnerFrameIndex) {
89
+ const flowLineDepths = snapshot.entries.filter((entry) => entry.kind === "flow-line").map((entry) => entry.depth);
90
+ const maxCompactFlowLineDepth = flowLineDepths.length > 0 ? Math.min(...flowLineDepths) + 1 : Number.POSITIVE_INFINITY;
91
+ const lines = snapshot.entries.flatMap((entry) => {
92
+ if (entry.kind === "line") return [entry.line];
93
+ if (entry.kind === "flow-line") {
94
+ if (entry.depth > maxCompactFlowLineDepth) return [];
95
+ return [formatInteractiveLine(entry.status, formatMessageWithElapsed(entry.message, entry.elapsedTimeMs), entry.depth, spinnerFrameIndex)];
96
+ }
97
+ return snapshot.treeRoots.flatMap((root) => renderCompactTreeNodeLines(root, spinnerFrameIndex));
98
+ });
99
+ return snapshot.outroMessage ? [...lines, `└ ${snapshot.outroMessage}`] : lines;
100
+ }
101
+ function stripControlSequences(text) {
102
+ return text.replaceAll(ANSI_PATTERN, "").replaceAll("\r", "");
103
+ }
104
+ function countRenderedTerminalRows(line, columns) {
105
+ const text = stripControlSequences(line);
106
+ let column = 0;
107
+ let rows = 1;
108
+ for (const char of text) {
109
+ if (char === "\n") {
110
+ rows += 1;
111
+ column = 0;
112
+ continue;
113
+ }
114
+ column += 1;
115
+ if (column >= columns) {
116
+ rows += 1;
117
+ column = 0;
118
+ }
119
+ }
120
+ return rows;
121
+ }
122
+ function countRenderedRows(lines, dimensions) {
123
+ const columns = Math.max(1, dimensions.columns ?? DEFAULT_TERMINAL_COLUMNS);
124
+ return lines.reduce((sum, line) => sum + countRenderedTerminalRows(line, columns), 0);
125
+ }
126
+ function fitsRenderedLines(lines, dimensions, options = {}) {
127
+ if (dimensions.rows === void 0) return true;
128
+ const contextLines = options.reserveContext && dimensions.rows > TERMINAL_FRAME_CONTEXT_LINES * 2 ? TERMINAL_FRAME_CONTEXT_LINES : 0;
129
+ const lineLimit = Math.max(1, dimensions.rows - TERMINAL_FRAME_MARGIN_LINES - contextLines);
130
+ return countRenderedRows(lines, dimensions) <= lineLimit;
131
+ }
132
+ function fitRenderedLinesToTerminal(lines, dimensions, options = {}) {
133
+ if (fitsRenderedLines(lines, dimensions) && !options.omittedLines) return lines;
134
+ if (dimensions.rows === void 0) return options.omittedLines ? addOmittedLinesMarker(lines) : lines;
135
+ const lineLimit = Math.max(1, dimensions.rows - TERMINAL_FRAME_MARGIN_LINES);
136
+ const columns = Math.max(1, dimensions.columns ?? DEFAULT_TERMINAL_COLUMNS);
137
+ const lastLine = lines.at(-1);
138
+ const shouldPreserveOutro = lastLine?.startsWith("└ ") ?? false;
139
+ const bodyLineCount = lines.length - (shouldPreserveOutro ? 1 : 0);
140
+ const bodyLines = lines.slice(0, bodyLineCount);
141
+ const ellipsisRows = countRenderedTerminalRows(OMITTED_LINES_MARKER, columns);
142
+ const reservedRows = shouldPreserveOutro && lastLine ? countRenderedTerminalRows(lastLine, columns) : 0;
143
+ const availableBodyRows = Math.max(0, lineLimit - reservedRows);
144
+ const bodyRows = countRenderedRows(bodyLines, { columns });
145
+ const shouldShowOmissionMarker = (options.omittedLines === true || bodyRows > availableBodyRows) && availableBodyRows >= ellipsisRows;
146
+ const fittedLines = [];
147
+ let remainingRows = availableBodyRows;
148
+ if (shouldShowOmissionMarker) remainingRows -= ellipsisRows;
149
+ for (let index = 0; index < bodyLineCount && remainingRows > 0; index++) {
150
+ const line = lines[index];
151
+ const rowCount = countRenderedTerminalRows(line, columns);
152
+ if (rowCount > remainingRows) break;
153
+ fittedLines.push(line);
154
+ remainingRows -= rowCount;
155
+ }
156
+ if (shouldShowOmissionMarker) fittedLines.push(OMITTED_LINES_MARKER);
157
+ if (shouldPreserveOutro && lastLine && reservedRows <= lineLimit) fittedLines.push(lastLine);
158
+ if (fittedLines.length > 0) return fittedLines;
159
+ return lines.slice(0, 1);
160
+ }
161
+ function addOmittedLinesMarker(lines) {
162
+ if (lines.includes(OMITTED_LINES_MARKER)) return lines;
163
+ const lastLine = lines.at(-1);
164
+ if (lastLine?.startsWith("└ ")) return [
165
+ ...lines.slice(0, -1),
166
+ OMITTED_LINES_MARKER,
167
+ lastLine
168
+ ];
169
+ return [...lines, OMITTED_LINES_MARKER];
170
+ }
171
+ function renderSnapshotLinesForTerminal(snapshot, spinnerFrameIndex, dimensions) {
172
+ const shouldPreferCompact = snapshot.compactMode === "check-flow" && snapshot.outroMessage !== void 0;
173
+ const fullLines = renderSnapshotLines(snapshot, spinnerFrameIndex);
174
+ if (!shouldPreferCompact && fitsRenderedLines(fullLines, dimensions, { reserveContext: true })) return fullLines;
175
+ const compactLines = renderCompactSnapshotLines(snapshot, spinnerFrameIndex);
176
+ return fitRenderedLinesToTerminal(compactLines, dimensions, { omittedLines: compactLines.length < fullLines.length });
177
+ }
178
+ function hasRunningSnapshotWork(snapshot) {
179
+ const hasRunningTreeNode = (node) => node.status === "running" || node.children.some(hasRunningTreeNode);
180
+ return snapshot.entries.some((entry) => entry.kind === "flow-line" && entry.status === "start") || snapshot.treeRoots.some(hasRunningTreeNode);
181
+ }
182
+ function toWritableText(chunk) {
183
+ if (chunk instanceof Uint8Array) return Buffer.from(chunk).toString();
184
+ return chunk;
185
+ }
186
+ //#endregion
187
+ //#region src/flow/terminal-frame.ts
188
+ var TerminalFrameTracker = class {
189
+ #column = 0;
190
+ #lineCount = 0;
191
+ #getColumns;
192
+ constructor(getColumns) {
193
+ this.#getColumns = getColumns;
194
+ }
195
+ get lineCount() {
196
+ return this.#lineCount;
197
+ }
198
+ record(chunk) {
199
+ const text = stripControlSequences(toWritableText(chunk));
200
+ const columns = Math.max(1, this.#getColumns());
201
+ for (const char of text) {
202
+ if (char === "\n") {
203
+ this.#lineCount += 1;
204
+ this.#column = 0;
205
+ continue;
206
+ }
207
+ this.#column += 1;
208
+ if (this.#column >= columns) {
209
+ this.#lineCount += 1;
210
+ this.#column = 0;
211
+ }
212
+ }
213
+ }
214
+ reset() {
215
+ this.#lineCount = 0;
216
+ this.#column = 0;
217
+ }
218
+ setLineCount(lineCount) {
219
+ this.#lineCount = Math.max(0, lineCount);
220
+ this.#column = 0;
221
+ }
222
+ };
223
+ function patchWriteStream(stream, onWrite) {
224
+ if (typeof stream?.write !== "function") return;
225
+ const originalWrite = stream.write;
226
+ const patchedWrite = (...args) => {
227
+ onWrite(args[0]);
228
+ return writeWithFlowArgs(originalWrite, args);
229
+ };
230
+ stream.write = patchedWrite;
231
+ return () => {
232
+ stream.write = originalWrite;
233
+ };
234
+ }
235
+ function writeWithFlowArgs(write, args) {
236
+ if (typeof args[1] === "string") return write(args[0], args[1], args[2]);
237
+ return write(args[0], args[1]);
238
+ }
239
+ //#endregion
240
+ export { formatInteractiveLine as a, renderSnapshotLinesForTerminal as c, SPINNER_FRAMES as i, toTreeFlowStatus as l, patchWriteStream as n, formatMessageWithElapsed as o, writeWithFlowArgs as r, hasRunningSnapshotWork as s, TerminalFrameTracker as t, toWritableText as u };