tubeless 0.2.12 → 0.2.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 (195) hide show
  1. package/dist/core/child-execution.d.ts +0 -23
  2. package/dist/core/child-execution.js +10 -12
  3. package/dist/core/child-execution.js.map +1 -1
  4. package/dist/core/child-progress.js +7 -2
  5. package/dist/core/child-progress.js.map +1 -1
  6. package/dist/core/iteration.d.ts +27 -0
  7. package/dist/core/iteration.js +111 -0
  8. package/dist/core/iteration.js.map +1 -0
  9. package/dist/core/lifecycle.d.ts +7 -5
  10. package/dist/core/lifecycle.js +36 -4
  11. package/dist/core/lifecycle.js.map +1 -1
  12. package/dist/core/pipeline-artifacts.d.ts +11 -0
  13. package/dist/core/pipeline-artifacts.js +2 -0
  14. package/dist/core/pipeline-artifacts.js.map +1 -0
  15. package/dist/core/pipeline-compiler.d.ts +1 -0
  16. package/dist/core/pipeline-compiler.js +1 -0
  17. package/dist/core/pipeline-compiler.js.map +1 -1
  18. package/dist/core/pipeline-definition-identity.d.ts +2 -2
  19. package/dist/core/pipeline-definition-identity.js +28 -5
  20. package/dist/core/pipeline-definition-identity.js.map +1 -1
  21. package/dist/core/pipeline-execute.d.ts +1 -6
  22. package/dist/core/pipeline-execute.js +28 -215
  23. package/dist/core/pipeline-execute.js.map +1 -1
  24. package/dist/core/pipeline-execution-error.d.ts +35 -0
  25. package/dist/core/pipeline-execution-error.js +195 -0
  26. package/dist/core/pipeline-execution-error.js.map +1 -0
  27. package/dist/core/pipeline-identity.d.ts +1 -0
  28. package/dist/core/pipeline-identity.js +1 -0
  29. package/dist/core/pipeline-identity.js.map +1 -1
  30. package/dist/core/pipeline-mermaid.js +4 -1
  31. package/dist/core/pipeline-mermaid.js.map +1 -1
  32. package/dist/core/pipeline-run-state.d.ts +4 -4
  33. package/dist/core/pipeline-run-state.js +10 -16
  34. package/dist/core/pipeline-run-state.js.map +1 -1
  35. package/dist/core/pipeline-step-executor.d.ts +2 -0
  36. package/dist/core/pipeline-step-executor.js +11 -3
  37. package/dist/core/pipeline-step-executor.js.map +1 -1
  38. package/dist/core/pipeline-steps.d.ts +20 -0
  39. package/dist/core/pipeline-steps.js +62 -0
  40. package/dist/core/pipeline-steps.js.map +1 -1
  41. package/dist/core/pipeline-types.d.ts +15 -2
  42. package/dist/core/pipeline-types.js.map +1 -1
  43. package/dist/core/pipeline.d.ts +4 -1
  44. package/dist/core/pipeline.js +18 -3
  45. package/dist/core/pipeline.js.map +1 -1
  46. package/dist/project/project-compiler.d.ts +3 -31
  47. package/dist/project/project-compiler.js +11 -180
  48. package/dist/project/project-compiler.js.map +1 -1
  49. package/dist/project/project-registry.d.ts +44 -0
  50. package/dist/project/project-registry.js +107 -0
  51. package/dist/project/project-registry.js.map +1 -0
  52. package/dist/project/project-step-graph.d.ts +9 -0
  53. package/dist/project/project-step-graph.js +99 -0
  54. package/dist/project/project-step-graph.js.map +1 -0
  55. package/dist/render/render.js +1 -1
  56. package/dist/render/render.js.map +1 -1
  57. package/dist/reporter/interactive-reporter.js +6 -3
  58. package/dist/reporter/interactive-reporter.js.map +1 -1
  59. package/dist/reporter/live-ticker-frame.d.ts +21 -0
  60. package/dist/reporter/live-ticker-frame.js +196 -0
  61. package/dist/reporter/live-ticker-frame.js.map +1 -0
  62. package/dist/reporter/live-ticker-protocol.d.ts +41 -0
  63. package/dist/reporter/live-ticker-protocol.js +59 -0
  64. package/dist/reporter/live-ticker-protocol.js.map +1 -0
  65. package/dist/reporter/live-ticker-worker.js +11 -25
  66. package/dist/reporter/live-ticker-worker.js.map +1 -1
  67. package/dist/reporter/live-ticker.d.ts +0 -15
  68. package/dist/reporter/live-ticker.js +11 -216
  69. package/dist/reporter/live-ticker.js.map +1 -1
  70. package/dist/reporter/reporter.js +7 -6
  71. package/dist/reporter/reporter.js.map +1 -1
  72. package/dist/run-store/definition-projection.d.ts +8 -0
  73. package/dist/run-store/definition-projection.js +89 -0
  74. package/dist/run-store/definition-projection.js.map +1 -0
  75. package/dist/run-store/run-projection.d.ts +15 -0
  76. package/dist/run-store/run-projection.js +203 -0
  77. package/dist/run-store/run-projection.js.map +1 -0
  78. package/dist/run-store/run-store-codec.d.ts +2 -0
  79. package/dist/run-store/run-store-codec.js +11 -0
  80. package/dist/run-store/run-store-codec.js.map +1 -1
  81. package/dist/run-store/run-store-event-decoder.js +3 -0
  82. package/dist/run-store/run-store-event-decoder.js.map +1 -1
  83. package/dist/run-store/run-store-reader.d.ts +7 -0
  84. package/dist/run-store/run-store-reader.js +31 -0
  85. package/dist/run-store/run-store-reader.js.map +1 -0
  86. package/dist/run-store/run-store-sqlite.js +23 -7
  87. package/dist/run-store/run-store-sqlite.js.map +1 -1
  88. package/dist/run-store/run-store.d.ts +16 -4
  89. package/dist/run-store/run-store.js +14 -263
  90. package/dist/run-store/run-store.js.map +1 -1
  91. package/dist/studio/run-store-ui-api.d.ts +13 -0
  92. package/dist/studio/run-store-ui-api.js +163 -0
  93. package/dist/studio/run-store-ui-api.js.map +1 -0
  94. package/dist/studio/run-store-ui-client-browser.js +2 -2
  95. package/dist/studio/run-store-ui-client-transport.js +28 -3
  96. package/dist/studio/run-store-ui-client-transport.js.map +1 -1
  97. package/dist/studio/run-store-ui-client.d.ts +2 -1
  98. package/dist/studio/run-store-ui-client.js +15 -9
  99. package/dist/studio/run-store-ui-client.js.map +1 -1
  100. package/dist/studio/run-store-ui-http.d.ts +11 -0
  101. package/dist/studio/run-store-ui-http.js +52 -0
  102. package/dist/studio/run-store-ui-http.js.map +1 -1
  103. package/dist/studio/run-store-ui-state.d.ts +1 -1
  104. package/dist/studio/run-store-ui-state.js +10 -28
  105. package/dist/studio/run-store-ui-state.js.map +1 -1
  106. package/dist/studio/run-store-ui.d.ts +5 -11
  107. package/dist/studio/run-store-ui.js +5 -203
  108. package/dist/studio/run-store-ui.js.map +1 -1
  109. package/dist/testing/testing.d.ts +18 -2
  110. package/dist/testing/testing.js +35 -2
  111. package/dist/testing/testing.js.map +1 -1
  112. package/dist/tracing/artifact-metadata.d.ts +28 -0
  113. package/dist/tracing/artifact-metadata.js +105 -0
  114. package/dist/tracing/artifact-metadata.js.map +1 -0
  115. package/dist/tracing/tracing-codec.d.ts +1 -1
  116. package/dist/tracing/tracing-codec.js +1 -1
  117. package/dist/tracing/tracing-codec.js.map +1 -1
  118. package/dist/tracing/tracing-constants.d.ts +1 -1
  119. package/dist/tracing/tracing-constants.js +1 -1
  120. package/dist/tracing/tracing-constants.js.map +1 -1
  121. package/dist/tracing/tracing-contracts.d.ts +3 -1
  122. package/dist/tracing/tracing-contracts.js.map +1 -1
  123. package/dist/tracing/tracing-internal.d.ts +2 -0
  124. package/dist/tracing/tracing-internal.js +36 -13
  125. package/dist/tracing/tracing-internal.js.map +1 -1
  126. package/dist/tracing/tracing-schema.d.ts +330 -49
  127. package/dist/tracing/tracing-schema.js +102 -10
  128. package/dist/tracing/tracing-schema.js.map +1 -1
  129. package/dist/workbench/workbench-command-execution.d.ts +6 -0
  130. package/dist/workbench/workbench-command-execution.js +48 -0
  131. package/dist/workbench/workbench-command-execution.js.map +1 -0
  132. package/dist/workbench/workbench-history.js +11 -18
  133. package/dist/workbench/workbench-history.js.map +1 -1
  134. package/dist/workbench/workbench-launch-session.d.ts +2 -2
  135. package/dist/workbench/workbench-launch-session.js.map +1 -1
  136. package/dist/workbench/workbench-run.d.ts +0 -4
  137. package/dist/workbench/workbench-run.js +3 -47
  138. package/dist/workbench/workbench-run.js.map +1 -1
  139. package/dist/workbench/workbench-studio-launcher.d.ts +27 -0
  140. package/dist/workbench/workbench-studio-launcher.js +95 -0
  141. package/dist/workbench/workbench-studio-launcher.js.map +1 -0
  142. package/dist/workbench/workbench-ui.js +10 -83
  143. package/dist/workbench/workbench-ui.js.map +1 -1
  144. package/docs/README.md +13 -7
  145. package/docs/agent-guide.md +69 -6
  146. package/docs/airflow.md +187 -0
  147. package/docs/api-reference.md +87 -76
  148. package/docs/api-report.json +17 -6
  149. package/docs/artifacts.md +173 -0
  150. package/docs/child-pipeline-composition.md +65 -2
  151. package/docs/comparison.md +20 -0
  152. package/docs/concepts.md +70 -2
  153. package/docs/dagster.md +161 -0
  154. package/docs/inngest.md +163 -0
  155. package/docs/llms.txt +30 -1
  156. package/docs/recipes.md +82 -33
  157. package/docs/remote-step-composition.md +6 -0
  158. package/docs/step-functions.md +190 -0
  159. package/docs/studio.md +31 -12
  160. package/docs/temporal.md +183 -0
  161. package/examples/airflow/contract.ts +46 -0
  162. package/examples/airflow/hosted.ts +70 -0
  163. package/examples/airflow/remote.ts +175 -0
  164. package/examples/airflow/tubeless_example.py +49 -0
  165. package/examples/airflow/worker.ts +24 -0
  166. package/examples/artifact-lineage.ts +70 -0
  167. package/examples/dagster/definitions.py +59 -0
  168. package/examples/dagster/hosted.ts +78 -0
  169. package/examples/dagster/pipeline.ts +62 -0
  170. package/examples/dagster/test_definitions.py +47 -0
  171. package/examples/dagster/tsconfig.build.json +10 -0
  172. package/examples/dagster/worker.ts +22 -0
  173. package/examples/inngest/client.ts +4 -0
  174. package/examples/inngest/functions.ts +65 -0
  175. package/examples/inngest/pipeline.ts +41 -0
  176. package/examples/inngest/send.ts +11 -0
  177. package/examples/inngest/server.ts +17 -0
  178. package/examples/inngest/tsconfig.build.json +10 -0
  179. package/examples/iteration.ts +52 -0
  180. package/examples/parameterized-steps.ts +96 -0
  181. package/examples/project/tubeless.project.ts +46 -0
  182. package/examples/step-functions/handler.ts +110 -0
  183. package/examples/step-functions/input.json +1 -0
  184. package/examples/step-functions/pipeline.ts +41 -0
  185. package/examples/step-functions/state-machine.asl.json +41 -0
  186. package/examples/step-functions/template.json +42 -0
  187. package/examples/step-output-overrides.ts +41 -0
  188. package/examples/temporal/activities.ts +94 -0
  189. package/examples/temporal/client.ts +19 -0
  190. package/examples/temporal/pipeline.ts +41 -0
  191. package/examples/temporal/tsconfig.build.json +10 -0
  192. package/examples/temporal/worker.ts +21 -0
  193. package/examples/temporal/workflows.ts +16 -0
  194. package/package.json +13 -4
  195. package/skills/tubeless/SKILL.md +30 -3
@@ -0,0 +1,196 @@
1
+ import { formatDurationMs } from "./reporter.js";
2
+ export const SPINNER_TOKEN = "\u0001";
3
+ const ELAPSED_TOKEN_START = "\u0002";
4
+ const ELAPSED_TOKEN_END = "\u0003";
5
+ const SHIMMER_TOKEN_START = "\u0004";
6
+ const SHIMMER_TOKEN_END = "\u0005";
7
+ export const TICKER_ANSI = {
8
+ clearDown: "\u001B[J",
9
+ hideCursor: "\u001B[?25l",
10
+ reset: "\u001B[0m",
11
+ showCursor: "\u001B[?25h",
12
+ };
13
+ const SHIMMER_BAND_COLUMNS = 3;
14
+ const SHIMMER_BRIGHT = "\u001B[0;1;36m";
15
+ const SHIMMER_DIM = "\u001B[0;2;36m";
16
+ const SHIMMER_FRAME_MS = 80;
17
+ const ANSI_STYLE = /\u001B\[[0-9;]*m/g;
18
+ const ANSI_STYLE_PREFIX = /^\u001B\[[0-9;]*m/;
19
+ const COMBINING_MARK = /\p{Mark}/u;
20
+ const EMOJI = /\p{Extended_Pictographic}/u;
21
+ const SPINNERS = {
22
+ ascii: ["-", "\\", "|", "/"],
23
+ unicode: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
24
+ };
25
+ function characterWidth(character) {
26
+ if (COMBINING_MARK.test(character) || character === "\u200D")
27
+ return 0;
28
+ if (EMOJI.test(character))
29
+ return 2;
30
+ const codePoint = character.codePointAt(0) ?? 0;
31
+ return codePoint >= 0x1100 &&
32
+ (codePoint <= 0x115f ||
33
+ codePoint === 0x2329 ||
34
+ codePoint === 0x232a ||
35
+ (codePoint >= 0x2e80 && codePoint <= 0xa4cf) ||
36
+ (codePoint >= 0xac00 && codePoint <= 0xd7a3) ||
37
+ (codePoint >= 0xf900 && codePoint <= 0xfaff) ||
38
+ (codePoint >= 0xfe10 && codePoint <= 0xfe6f) ||
39
+ (codePoint >= 0xff00 && codePoint <= 0xff60) ||
40
+ (codePoint >= 0xffe0 && codePoint <= 0xffe6) ||
41
+ (codePoint >= 0x20000 && codePoint <= 0x3fffd))
42
+ ? 2
43
+ : 1;
44
+ }
45
+ function visibleWidth(value) {
46
+ return [...value].reduce((width, character) => width + characterWidth(character), 0);
47
+ }
48
+ function fitLine(value, columns, unicode) {
49
+ if (!columns || columns <= 1)
50
+ return value;
51
+ const maxWidth = columns - 1;
52
+ const plain = value.replace(ANSI_STYLE, "");
53
+ if (visibleWidth(plain) <= maxWidth)
54
+ return value;
55
+ const ellipsis = unicode ? "…" : ".".repeat(Math.min(3, maxWidth));
56
+ const targetWidth = Math.max(0, maxWidth - visibleWidth(ellipsis));
57
+ let width = 0;
58
+ let truncated = "";
59
+ let index = 0;
60
+ let hasStyle = false;
61
+ while (index < value.length) {
62
+ const style = value.slice(index).match(ANSI_STYLE_PREFIX)?.[0];
63
+ if (style) {
64
+ truncated += style;
65
+ index += style.length;
66
+ hasStyle = true;
67
+ continue;
68
+ }
69
+ const character = String.fromCodePoint(value.codePointAt(index) ?? 0);
70
+ const nextWidth = width + characterWidth(character);
71
+ if (nextWidth > targetWidth)
72
+ break;
73
+ truncated += character;
74
+ width = nextWidth;
75
+ index += character.length;
76
+ }
77
+ return `${truncated}${hasStyle ? TICKER_ANSI.reset : ""}${ellipsis}`;
78
+ }
79
+ export function elapsedToken(startedAtMs) {
80
+ return `${ELAPSED_TOKEN_START}${startedAtMs}${ELAPSED_TOKEN_END}`;
81
+ }
82
+ export function shimmerToken(value) {
83
+ return `${SHIMMER_TOKEN_START}${value}${SHIMMER_TOKEN_END}`;
84
+ }
85
+ function paintShimmerText(text, nowMs) {
86
+ const characters = [...text];
87
+ if (characters.length === 0)
88
+ return text;
89
+ const widths = characters.map(characterWidth);
90
+ const totalWidth = visibleWidth(text);
91
+ const period = Math.max(1, totalWidth + SHIMMER_BAND_COLUMNS);
92
+ const head = Math.floor(nowMs / SHIMMER_FRAME_MS) % period;
93
+ const bandStart = head - SHIMMER_BAND_COLUMNS + 1;
94
+ let column = 0;
95
+ let output = TICKER_ANSI.reset;
96
+ let last;
97
+ for (const [index, character] of characters.entries()) {
98
+ const width = widths[index] ?? 0;
99
+ const inBand = width === 0 ? last === "bright" : column <= head && column + width - 1 >= bandStart;
100
+ const style = inBand ? "bright" : "dim";
101
+ if (style !== last) {
102
+ output += style === "bright" ? SHIMMER_BRIGHT : SHIMMER_DIM;
103
+ last = style;
104
+ }
105
+ output += character;
106
+ column += width;
107
+ }
108
+ return `${output}${TICKER_ANSI.reset}`;
109
+ }
110
+ function replaceLiveTokens(line, spinner, nowMs, color) {
111
+ return line
112
+ .replaceAll(SPINNER_TOKEN, spinner)
113
+ .replace(new RegExp(`${ELAPSED_TOKEN_START}(\\d+)${ELAPSED_TOKEN_END}`, "g"), (_match, start) => formatDurationMs(Math.max(0, nowMs - Number(start))))
114
+ .replace(new RegExp(`${SHIMMER_TOKEN_START}(.*?)${SHIMMER_TOKEN_END}`, "g"), (_match, text) => (color ? paintShimmerText(text, nowMs) : text));
115
+ }
116
+ export function paintLiveLines(lines, spinner, nowMs, columns, color = false, unicode = true, logPane) {
117
+ const painted = lines.map((line) => replaceLiveTokens(line, spinner, nowMs, color));
118
+ if (!logPane || !columns || columns < 120) {
119
+ return painted.map((line) => fitLine(line, columns, unicode));
120
+ }
121
+ const paneWidth = Math.min(60, Math.floor(columns * 0.4));
122
+ const leftWidth = columns - paneWidth - 3;
123
+ const horizontal = unicode ? "─" : "-";
124
+ const vertical = unicode ? "│" : "|";
125
+ const pad = (line, width) => {
126
+ const fitted = fitLine(line, width + 1, unicode);
127
+ return fitted + " ".repeat(Math.max(0, width - visibleWidth(fitted.replace(ANSI_STYLE, ""))));
128
+ };
129
+ const pane = [
130
+ `${unicode ? "╭" : "+"}${horizontal} Logs ${horizontal.repeat(paneWidth - 9)}${unicode ? "╮" : "+"}`,
131
+ ...logPane.map((line) => `${vertical} ${pad(line, paneWidth - 4)} ${vertical}`),
132
+ `${unicode ? "╰" : "+"}${horizontal.repeat(paneWidth - 2)}${unicode ? "╯" : "+"}`,
133
+ ];
134
+ return Array.from({ length: Math.max(painted.length, pane.length) }, (_, index) => pane[index] === undefined
135
+ ? fitLine(painted[index] ?? "", leftWidth + 1, unicode)
136
+ : `${pad(painted[index] ?? "", leftWidth)} ${pane[index]}`);
137
+ }
138
+ export class TickerFrame {
139
+ write;
140
+ paintedLines;
141
+ cursorHidden;
142
+ constructor(write, paintedLines = []) {
143
+ this.write = write;
144
+ this.paintedLines = paintedLines;
145
+ this.cursorHidden = paintedLines.length > 0;
146
+ }
147
+ showCursor() {
148
+ if (!this.cursorHidden)
149
+ return;
150
+ this.write(TICKER_ANSI.showCursor);
151
+ this.cursorHidden = false;
152
+ }
153
+ clear(columns) {
154
+ if (this.paintedLines.length === 0)
155
+ return;
156
+ // Resizing can reflow each previously painted line across several rows.
157
+ // Count those rows at the current width before moving back to the frame start.
158
+ let rows = this.paintedLines.length;
159
+ if (columns !== undefined && columns > 0) {
160
+ rows = 0;
161
+ for (const line of this.paintedLines) {
162
+ rows += 1;
163
+ let column = 0;
164
+ for (const character of line.replace(ANSI_STYLE, "")) {
165
+ const width = characterWidth(character);
166
+ if (column + width > columns) {
167
+ rows += 1;
168
+ column = 0;
169
+ }
170
+ column += width;
171
+ }
172
+ }
173
+ }
174
+ this.write(`\u001B[${rows}F${TICKER_ANSI.clearDown}`);
175
+ this.paintedLines = [];
176
+ }
177
+ redraw(lines, columns) {
178
+ if (!this.cursorHidden) {
179
+ this.write(TICKER_ANSI.hideCursor);
180
+ this.cursorHidden = true;
181
+ }
182
+ this.clear(columns);
183
+ if (lines.length === 0)
184
+ return;
185
+ this.write(`${lines.join("\n")}\n`);
186
+ this.paintedLines = lines;
187
+ }
188
+ }
189
+ export function currentSpinner(unicode, refreshIntervalMs) {
190
+ const frames = unicode ? SPINNERS.unicode : SPINNERS.ascii;
191
+ return frames[Math.floor(Date.now() / refreshIntervalMs) % frames.length] ?? frames[0];
192
+ }
193
+ export function hasLiveAnimation(lines) {
194
+ return lines.some((line) => line.includes(SPINNER_TOKEN) || line.includes(SHIMMER_TOKEN_START));
195
+ }
196
+ //# sourceMappingURL=live-ticker-frame.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"live-ticker-frame.js","sourceRoot":"","sources":["../../src/reporter/live-ticker-frame.ts"],"names":[],"mappings":"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","sourcesContent":["import { formatDurationMs } from \"./reporter.js\";\n\nexport const SPINNER_TOKEN = \"\\u0001\";\nconst ELAPSED_TOKEN_START = \"\\u0002\";\nconst ELAPSED_TOKEN_END = \"\\u0003\";\nconst SHIMMER_TOKEN_START = \"\\u0004\";\nconst SHIMMER_TOKEN_END = \"\\u0005\";\n\nexport const TICKER_ANSI = {\n clearDown: \"\\u001B[J\",\n hideCursor: \"\\u001B[?25l\",\n reset: \"\\u001B[0m\",\n showCursor: \"\\u001B[?25h\",\n} as const;\n\nconst SHIMMER_BAND_COLUMNS = 3;\nconst SHIMMER_BRIGHT = \"\\u001B[0;1;36m\";\nconst SHIMMER_DIM = \"\\u001B[0;2;36m\";\nconst SHIMMER_FRAME_MS = 80;\n\nconst ANSI_STYLE = /\\u001B\\[[0-9;]*m/g;\nconst ANSI_STYLE_PREFIX = /^\\u001B\\[[0-9;]*m/;\nconst COMBINING_MARK = /\\p{Mark}/u;\nconst EMOJI = /\\p{Extended_Pictographic}/u;\n\nconst SPINNERS = {\n ascii: [\"-\", \"\\\\\", \"|\", \"/\"],\n unicode: [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"],\n} as const;\n\nfunction characterWidth(character: string): number {\n if (COMBINING_MARK.test(character) || character === \"\\u200D\") return 0;\n if (EMOJI.test(character)) return 2;\n const codePoint = character.codePointAt(0) ?? 0;\n return codePoint >= 0x1100 &&\n (codePoint <= 0x115f ||\n codePoint === 0x2329 ||\n codePoint === 0x232a ||\n (codePoint >= 0x2e80 && codePoint <= 0xa4cf) ||\n (codePoint >= 0xac00 && codePoint <= 0xd7a3) ||\n (codePoint >= 0xf900 && codePoint <= 0xfaff) ||\n (codePoint >= 0xfe10 && codePoint <= 0xfe6f) ||\n (codePoint >= 0xff00 && codePoint <= 0xff60) ||\n (codePoint >= 0xffe0 && codePoint <= 0xffe6) ||\n (codePoint >= 0x20000 && codePoint <= 0x3fffd))\n ? 2\n : 1;\n}\n\nfunction visibleWidth(value: string): number {\n return [...value].reduce((width, character) => width + characterWidth(character), 0);\n}\n\nfunction fitLine(value: string, columns: number | undefined, unicode: boolean): string {\n if (!columns || columns <= 1) return value;\n const maxWidth = columns - 1;\n const plain = value.replace(ANSI_STYLE, \"\");\n if (visibleWidth(plain) <= maxWidth) return value;\n const ellipsis = unicode ? \"…\" : \".\".repeat(Math.min(3, maxWidth));\n const targetWidth = Math.max(0, maxWidth - visibleWidth(ellipsis));\n let width = 0;\n let truncated = \"\";\n let index = 0;\n let hasStyle = false;\n while (index < value.length) {\n const style = value.slice(index).match(ANSI_STYLE_PREFIX)?.[0];\n if (style) {\n truncated += style;\n index += style.length;\n hasStyle = true;\n continue;\n }\n const character = String.fromCodePoint(value.codePointAt(index) ?? 0);\n const nextWidth = width + characterWidth(character);\n if (nextWidth > targetWidth) break;\n truncated += character;\n width = nextWidth;\n index += character.length;\n }\n return `${truncated}${hasStyle ? TICKER_ANSI.reset : \"\"}${ellipsis}`;\n}\n\nexport function elapsedToken(startedAtMs: number): string {\n return `${ELAPSED_TOKEN_START}${startedAtMs}${ELAPSED_TOKEN_END}`;\n}\n\nexport function shimmerToken(value: string): string {\n return `${SHIMMER_TOKEN_START}${value}${SHIMMER_TOKEN_END}`;\n}\n\nfunction paintShimmerText(text: string, nowMs: number): string {\n const characters = [...text];\n if (characters.length === 0) return text;\n const widths = characters.map(characterWidth);\n const totalWidth = visibleWidth(text);\n const period = Math.max(1, totalWidth + SHIMMER_BAND_COLUMNS);\n const head = Math.floor(nowMs / SHIMMER_FRAME_MS) % period;\n const bandStart = head - SHIMMER_BAND_COLUMNS + 1;\n let column = 0;\n let output = TICKER_ANSI.reset;\n let last: \"bright\" | \"dim\" | undefined;\n for (const [index, character] of characters.entries()) {\n const width = widths[index] ?? 0;\n const inBand =\n width === 0 ? last === \"bright\" : column <= head && column + width - 1 >= bandStart;\n const style = inBand ? \"bright\" : \"dim\";\n if (style !== last) {\n output += style === \"bright\" ? SHIMMER_BRIGHT : SHIMMER_DIM;\n last = style;\n }\n output += character;\n column += width;\n }\n return `${output}${TICKER_ANSI.reset}`;\n}\n\nfunction replaceLiveTokens(line: string, spinner: string, nowMs: number, color: boolean): string {\n return line\n .replaceAll(SPINNER_TOKEN, spinner)\n .replace(\n new RegExp(`${ELAPSED_TOKEN_START}(\\\\d+)${ELAPSED_TOKEN_END}`, \"g\"),\n (_match, start: string) => formatDurationMs(Math.max(0, nowMs - Number(start)))\n )\n .replace(\n new RegExp(`${SHIMMER_TOKEN_START}(.*?)${SHIMMER_TOKEN_END}`, \"g\"),\n (_match, text: string) => (color ? paintShimmerText(text, nowMs) : text)\n );\n}\n\nexport function paintLiveLines(\n lines: readonly string[],\n spinner: string,\n nowMs: number,\n columns?: number,\n color = false,\n unicode = true,\n logPane?: readonly string[]\n): string[] {\n const painted = lines.map((line) => replaceLiveTokens(line, spinner, nowMs, color));\n if (!logPane || !columns || columns < 120) {\n return painted.map((line) => fitLine(line, columns, unicode));\n }\n const paneWidth = Math.min(60, Math.floor(columns * 0.4));\n const leftWidth = columns - paneWidth - 3;\n const horizontal = unicode ? \"─\" : \"-\";\n const vertical = unicode ? \"│\" : \"|\";\n const pad = (line: string, width: number): string => {\n const fitted = fitLine(line, width + 1, unicode);\n return fitted + \" \".repeat(Math.max(0, width - visibleWidth(fitted.replace(ANSI_STYLE, \"\"))));\n };\n const pane = [\n `${unicode ? \"╭\" : \"+\"}${horizontal} Logs ${horizontal.repeat(paneWidth - 9)}${unicode ? \"╮\" : \"+\"}`,\n ...logPane.map((line) => `${vertical} ${pad(line, paneWidth - 4)} ${vertical}`),\n `${unicode ? \"╰\" : \"+\"}${horizontal.repeat(paneWidth - 2)}${unicode ? \"╯\" : \"+\"}`,\n ];\n return Array.from({ length: Math.max(painted.length, pane.length) }, (_, index) =>\n pane[index] === undefined\n ? fitLine(painted[index] ?? \"\", leftWidth + 1, unicode)\n : `${pad(painted[index] ?? \"\", leftWidth)} ${pane[index]}`\n );\n}\n\nexport class TickerFrame {\n private cursorHidden: boolean;\n\n constructor(\n private readonly write: (chunk: string) => void,\n private paintedLines: readonly string[] = []\n ) {\n this.cursorHidden = paintedLines.length > 0;\n }\n\n showCursor(): void {\n if (!this.cursorHidden) return;\n this.write(TICKER_ANSI.showCursor);\n this.cursorHidden = false;\n }\n\n clear(columns?: number): void {\n if (this.paintedLines.length === 0) return;\n // Resizing can reflow each previously painted line across several rows.\n // Count those rows at the current width before moving back to the frame start.\n let rows = this.paintedLines.length;\n if (columns !== undefined && columns > 0) {\n rows = 0;\n for (const line of this.paintedLines) {\n rows += 1;\n let column = 0;\n for (const character of line.replace(ANSI_STYLE, \"\")) {\n const width = characterWidth(character);\n if (column + width > columns) {\n rows += 1;\n column = 0;\n }\n column += width;\n }\n }\n }\n this.write(`\\u001B[${rows}F${TICKER_ANSI.clearDown}`);\n this.paintedLines = [];\n }\n\n redraw(lines: readonly string[], columns?: number): void {\n if (!this.cursorHidden) {\n this.write(TICKER_ANSI.hideCursor);\n this.cursorHidden = true;\n }\n this.clear(columns);\n if (lines.length === 0) return;\n this.write(`${lines.join(\"\\n\")}\\n`);\n this.paintedLines = lines;\n }\n}\n\nexport function currentSpinner(unicode: boolean, refreshIntervalMs: number): string {\n const frames = unicode ? SPINNERS.unicode : SPINNERS.ascii;\n return frames[Math.floor(Date.now() / refreshIntervalMs) % frames.length] ?? frames[0]!;\n}\n\nexport function hasLiveAnimation(lines: readonly string[]): boolean {\n return lines.some((line) => line.includes(SPINNER_TOKEN) || line.includes(SHIMMER_TOKEN_START));\n}\n"]}
@@ -0,0 +1,41 @@
1
+ import type { MessagePort } from "node:worker_threads";
2
+ export interface LiveTickerWorkerData {
3
+ color: boolean;
4
+ columns?: number;
5
+ fd: number;
6
+ framePort: MessagePort;
7
+ refreshIntervalMs: number;
8
+ stateBuffer: SharedArrayBuffer;
9
+ unicode: boolean;
10
+ }
11
+ export type TickerWorkerMessage = {
12
+ columns?: number;
13
+ lines: string[];
14
+ logPane?: readonly string[];
15
+ type: "lines";
16
+ } | {
17
+ columns?: number;
18
+ text: string;
19
+ type: "log";
20
+ } | {
21
+ columns?: number;
22
+ lines: string[];
23
+ logPane?: readonly string[];
24
+ type: "stop";
25
+ };
26
+ /** Shared output ownership and acknowledgements, viewed from either thread. */
27
+ export declare class TickerWorkerState {
28
+ #private;
29
+ readonly buffer: SharedArrayBuffer;
30
+ constructor(buffer?: SharedArrayBuffer);
31
+ get acceptedLogs(): number;
32
+ acknowledgeLog(): void;
33
+ get inlineRequested(): boolean;
34
+ markStopped(): void;
35
+ waitForStop(timeoutMs: number): boolean;
36
+ withWorkerOutput(write: () => void): void;
37
+ /** Revoke worker writes, then acquire exclusive output ownership within the deadline. */
38
+ claimOutput(timeoutMs: number): boolean;
39
+ /** Only an exit event guarantees that an abandoned lock no longer protects a write. */
40
+ releaseOutputAfterWorkerExit(): void;
41
+ }
@@ -0,0 +1,59 @@
1
+ const STOPPED = 0;
2
+ const ACCEPTED_LOGS = 1;
3
+ const INLINE_REQUESTED = 2;
4
+ const OUTPUT_LOCK = 3;
5
+ /** Shared output ownership and acknowledgements, viewed from either thread. */
6
+ export class TickerWorkerState {
7
+ buffer;
8
+ #state;
9
+ constructor(buffer = new SharedArrayBuffer(4 * Int32Array.BYTES_PER_ELEMENT)) {
10
+ this.buffer = buffer;
11
+ this.#state = new Int32Array(buffer);
12
+ }
13
+ get acceptedLogs() {
14
+ return Atomics.load(this.#state, ACCEPTED_LOGS);
15
+ }
16
+ acknowledgeLog() {
17
+ Atomics.add(this.#state, ACCEPTED_LOGS, 1);
18
+ }
19
+ get inlineRequested() {
20
+ return Atomics.load(this.#state, INLINE_REQUESTED) === 1;
21
+ }
22
+ markStopped() {
23
+ Atomics.store(this.#state, STOPPED, 1);
24
+ Atomics.notify(this.#state, STOPPED);
25
+ }
26
+ waitForStop(timeoutMs) {
27
+ return Atomics.wait(this.#state, STOPPED, 0, timeoutMs) !== "timed-out";
28
+ }
29
+ withWorkerOutput(write) {
30
+ while (Atomics.compareExchange(this.#state, OUTPUT_LOCK, 0, 1) !== 0) {
31
+ Atomics.wait(this.#state, OUTPUT_LOCK, 1);
32
+ }
33
+ try {
34
+ if (!this.inlineRequested)
35
+ write();
36
+ }
37
+ finally {
38
+ Atomics.store(this.#state, OUTPUT_LOCK, 0);
39
+ Atomics.notify(this.#state, OUTPUT_LOCK);
40
+ }
41
+ }
42
+ /** Revoke worker writes, then acquire exclusive output ownership within the deadline. */
43
+ claimOutput(timeoutMs) {
44
+ Atomics.store(this.#state, INLINE_REQUESTED, 1);
45
+ const deadline = Date.now() + timeoutMs;
46
+ while (Atomics.compareExchange(this.#state, OUTPUT_LOCK, 0, 1) !== 0) {
47
+ const remainingMs = deadline - Date.now();
48
+ if (remainingMs <= 0)
49
+ return false;
50
+ Atomics.wait(this.#state, OUTPUT_LOCK, 1, remainingMs);
51
+ }
52
+ return true;
53
+ }
54
+ /** Only an exit event guarantees that an abandoned lock no longer protects a write. */
55
+ releaseOutputAfterWorkerExit() {
56
+ Atomics.store(this.#state, OUTPUT_LOCK, 0);
57
+ }
58
+ }
59
+ //# sourceMappingURL=live-ticker-protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"live-ticker-protocol.js","sourceRoot":"","sources":["../../src/reporter/live-ticker-protocol.ts"],"names":[],"mappings":"AAiBA,MAAM,OAAO,GAAG,CAAC,CAAC;AAClB,MAAM,aAAa,GAAG,CAAC,CAAC;AACxB,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAC3B,MAAM,WAAW,GAAG,CAAC,CAAC;AAEtB,+EAA+E;AAC/E,MAAM,OAAO,iBAAiB;IAGP,MAAM;IAFlB,MAAM,CAAa;IAE5B,YAAqB,MAAM,GAAG,IAAI,iBAAiB,CAAC,CAAC,GAAG,UAAU,CAAC,iBAAiB,CAAC;sBAAhE,MAAM;QACzB,IAAI,CAAC,MAAM,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;IACvC,CAAC;IAED,IAAI,YAAY;QACd,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAClD,CAAC;IAED,cAAc;QACZ,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC;IAC7C,CAAC;IAED,IAAI,eAAe;QACjB,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,gBAAgB,CAAC,KAAK,CAAC,CAAC;IAC3D,CAAC;IAED,WAAW;QACT,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;QACvC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,CAAC;IAED,WAAW,CAAC,SAAiB;QAC3B,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,SAAS,CAAC,KAAK,WAAW,CAAC;IAC1E,CAAC;IAED,gBAAgB,CAAC,KAAiB;QAChC,OAAO,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;YACrE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC;QAC5C,CAAC;QACD,IAAI,CAAC;YACH,IAAI,CAAC,IAAI,CAAC,eAAe;gBAAE,KAAK,EAAE,CAAC;QACrC,CAAC;gBAAS,CAAC;YACT,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC;YAC3C,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC;IAED,yFAAyF;IACzF,WAAW,CAAC,SAAiB;QAC3B,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC,CAAC;QAChD,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;QACxC,OAAO,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;YACrE,MAAM,WAAW,GAAG,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAC1C,IAAI,WAAW,IAAI,CAAC;gBAAE,OAAO,KAAK,CAAC;YACnC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,EAAE,WAAW,CAAC,CAAC;QACzD,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,uFAAuF;IACvF,4BAA4B;QAC1B,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC;IAC7C,CAAC;CACF","sourcesContent":["import type { MessagePort } from \"node:worker_threads\";\n\nexport interface LiveTickerWorkerData {\n color: boolean;\n columns?: number;\n fd: number;\n framePort: MessagePort;\n refreshIntervalMs: number;\n stateBuffer: SharedArrayBuffer;\n unicode: boolean;\n}\n\nexport type TickerWorkerMessage =\n | { columns?: number; lines: string[]; logPane?: readonly string[]; type: \"lines\" }\n | { columns?: number; text: string; type: \"log\" }\n | { columns?: number; lines: string[]; logPane?: readonly string[]; type: \"stop\" };\n\nconst STOPPED = 0;\nconst ACCEPTED_LOGS = 1;\nconst INLINE_REQUESTED = 2;\nconst OUTPUT_LOCK = 3;\n\n/** Shared output ownership and acknowledgements, viewed from either thread. */\nexport class TickerWorkerState {\n readonly #state: Int32Array;\n\n constructor(readonly buffer = new SharedArrayBuffer(4 * Int32Array.BYTES_PER_ELEMENT)) {\n this.#state = new Int32Array(buffer);\n }\n\n get acceptedLogs(): number {\n return Atomics.load(this.#state, ACCEPTED_LOGS);\n }\n\n acknowledgeLog(): void {\n Atomics.add(this.#state, ACCEPTED_LOGS, 1);\n }\n\n get inlineRequested(): boolean {\n return Atomics.load(this.#state, INLINE_REQUESTED) === 1;\n }\n\n markStopped(): void {\n Atomics.store(this.#state, STOPPED, 1);\n Atomics.notify(this.#state, STOPPED);\n }\n\n waitForStop(timeoutMs: number): boolean {\n return Atomics.wait(this.#state, STOPPED, 0, timeoutMs) !== \"timed-out\";\n }\n\n withWorkerOutput(write: () => void): void {\n while (Atomics.compareExchange(this.#state, OUTPUT_LOCK, 0, 1) !== 0) {\n Atomics.wait(this.#state, OUTPUT_LOCK, 1);\n }\n try {\n if (!this.inlineRequested) write();\n } finally {\n Atomics.store(this.#state, OUTPUT_LOCK, 0);\n Atomics.notify(this.#state, OUTPUT_LOCK);\n }\n }\n\n /** Revoke worker writes, then acquire exclusive output ownership within the deadline. */\n claimOutput(timeoutMs: number): boolean {\n Atomics.store(this.#state, INLINE_REQUESTED, 1);\n const deadline = Date.now() + timeoutMs;\n while (Atomics.compareExchange(this.#state, OUTPUT_LOCK, 0, 1) !== 0) {\n const remainingMs = deadline - Date.now();\n if (remainingMs <= 0) return false;\n Atomics.wait(this.#state, OUTPUT_LOCK, 1, remainingMs);\n }\n return true;\n }\n\n /** Only an exit event guarantees that an abandoned lock no longer protects a write. */\n releaseOutputAfterWorkerExit(): void {\n Atomics.store(this.#state, OUTPUT_LOCK, 0);\n }\n}\n"]}
@@ -1,61 +1,48 @@
1
1
  import { writeSync } from "node:fs";
2
2
  import { parentPort, workerData } from "node:worker_threads";
3
- import { currentSpinner, paintLiveLines, SHIMMER_TOKEN_START, SPINNER_TOKEN, TickerFrame, } from "./live-ticker.js";
3
+ import { currentSpinner, paintLiveLines, hasLiveAnimation, TickerFrame, } from "./live-ticker-frame.js";
4
+ import { TickerWorkerState, } from "./live-ticker-protocol.js";
4
5
  const port = parentPort;
5
6
  if (port === null)
6
7
  throw new Error("live-ticker-worker must run in a worker thread");
7
8
  // SAFETY: createWorkerTicker owns the workerData shape.
8
9
  const data = workerData;
9
- // [0] stopped, [1] accepted logs, [2] inline owns output, [3] output lock
10
- const state = new Int32Array(data.stateBuffer);
10
+ const state = new TickerWorkerState(data.stateBuffer);
11
11
  const frame = new TickerFrame((chunk) => writeSync(data.fd, chunk));
12
12
  let columns = data.columns;
13
13
  let lines = [];
14
14
  let logPane;
15
- function withOutputLock(write) {
16
- while (Atomics.compareExchange(state, 3, 0, 1) !== 0) {
17
- Atomics.wait(state, 3, 1);
18
- }
19
- try {
20
- if (Atomics.load(state, 2) === 0)
21
- write();
22
- }
23
- finally {
24
- Atomics.store(state, 3, 0);
25
- Atomics.notify(state, 3);
26
- }
27
- }
28
15
  function paintFrame() {
29
16
  const painted = paintLiveLines(lines, currentSpinner(data.unicode, data.refreshIntervalMs), Date.now(), columns, data.color, data.unicode, logPane);
30
17
  frame.redraw(painted, columns);
31
18
  data.framePort.postMessage(painted);
32
19
  }
33
20
  function redraw() {
34
- withOutputLock(paintFrame);
21
+ state.withWorkerOutput(paintFrame);
35
22
  }
36
23
  const timer = setInterval(() => {
37
- if (Atomics.load(state, 2) === 1) {
24
+ if (state.inlineRequested) {
38
25
  clearInterval(timer);
39
26
  port.close();
40
27
  return;
41
28
  }
42
- if (lines.some((line) => line.includes(SPINNER_TOKEN) || line.includes(SHIMMER_TOKEN_START))) {
29
+ if (hasLiveAnimation(lines)) {
43
30
  redraw();
44
31
  }
45
32
  }, data.refreshIntervalMs);
46
33
  port.on("message", (message) => {
47
- if (Atomics.load(state, 2) === 1) {
34
+ if (state.inlineRequested) {
48
35
  clearInterval(timer);
49
36
  port.close();
50
37
  return;
51
38
  }
52
39
  columns = message.columns ?? columns;
53
40
  if (message.type === "log") {
54
- withOutputLock(() => {
41
+ state.withWorkerOutput(() => {
55
42
  frame.clear(columns);
56
43
  data.framePort.postMessage([]);
57
44
  writeSync(data.fd, message.text);
58
- Atomics.add(state, 1, 1);
45
+ state.acknowledgeLog();
59
46
  });
60
47
  return;
61
48
  }
@@ -67,12 +54,11 @@ port.on("message", (message) => {
67
54
  }
68
55
  clearInterval(timer);
69
56
  try {
70
- withOutputLock(() => {
57
+ state.withWorkerOutput(() => {
71
58
  paintFrame();
72
59
  frame.showCursor();
73
60
  });
74
- Atomics.store(state, 0, 1);
75
- Atomics.notify(state, 0);
61
+ state.markStopped();
76
62
  }
77
63
  finally {
78
64
  port.close();
@@ -1 +1 @@
1
- {"version":3,"file":"live-ticker-worker.js","sourceRoot":"","sources":["../../src/reporter/live-ticker-worker.ts"],"names":[],"mappings":"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","sourcesContent":["import { writeSync } from \"node:fs\";\nimport { parentPort, workerData, type MessagePort } from \"node:worker_threads\";\nimport {\n currentSpinner,\n paintLiveLines,\n SHIMMER_TOKEN_START,\n SPINNER_TOKEN,\n TickerFrame,\n} from \"./live-ticker.js\";\n\ninterface LiveTickerWorkerData {\n color: boolean;\n columns?: number;\n fd: number;\n framePort: MessagePort;\n refreshIntervalMs: number;\n stateBuffer: SharedArrayBuffer;\n unicode: boolean;\n}\n\ntype TickerWorkerMessage =\n | { columns?: number; lines: string[]; logPane?: readonly string[]; type: \"lines\" }\n | { columns?: number; text: string; type: \"log\" }\n | { columns?: number; lines: string[]; logPane?: readonly string[]; type: \"stop\" };\n\nconst port = parentPort;\nif (port === null) throw new Error(\"live-ticker-worker must run in a worker thread\");\n\n// SAFETY: createWorkerTicker owns the workerData shape.\nconst data = workerData as LiveTickerWorkerData;\n// [0] stopped, [1] accepted logs, [2] inline owns output, [3] output lock\nconst state = new Int32Array(data.stateBuffer);\nconst frame = new TickerFrame((chunk) => writeSync(data.fd, chunk));\nlet columns = data.columns;\nlet lines: string[] = [];\nlet logPane: readonly string[] | undefined;\n\nfunction withOutputLock(write: () => void): void {\n while (Atomics.compareExchange(state, 3, 0, 1) !== 0) {\n Atomics.wait(state, 3, 1);\n }\n try {\n if (Atomics.load(state, 2) === 0) write();\n } finally {\n Atomics.store(state, 3, 0);\n Atomics.notify(state, 3);\n }\n}\n\nfunction paintFrame(): void {\n const painted = paintLiveLines(\n lines,\n currentSpinner(data.unicode, data.refreshIntervalMs),\n Date.now(),\n columns,\n data.color,\n data.unicode,\n logPane\n );\n frame.redraw(painted, columns);\n data.framePort.postMessage(painted);\n}\n\nfunction redraw(): void {\n withOutputLock(paintFrame);\n}\n\nconst timer = setInterval(() => {\n if (Atomics.load(state, 2) === 1) {\n clearInterval(timer);\n port.close();\n return;\n }\n if (lines.some((line) => line.includes(SPINNER_TOKEN) || line.includes(SHIMMER_TOKEN_START))) {\n redraw();\n }\n}, data.refreshIntervalMs);\n\nport.on(\"message\", (message: TickerWorkerMessage) => {\n if (Atomics.load(state, 2) === 1) {\n clearInterval(timer);\n port.close();\n return;\n }\n columns = message.columns ?? columns;\n if (message.type === \"log\") {\n withOutputLock(() => {\n frame.clear(columns);\n data.framePort.postMessage([]);\n writeSync(data.fd, message.text);\n Atomics.add(state, 1, 1);\n });\n return;\n }\n lines = message.lines;\n logPane = message.logPane;\n if (message.type === \"lines\") {\n redraw();\n return;\n }\n clearInterval(timer);\n try {\n withOutputLock(() => {\n paintFrame();\n frame.showCursor();\n });\n Atomics.store(state, 0, 1);\n Atomics.notify(state, 0);\n } finally {\n port.close();\n }\n});\n"]}
1
+ {"version":3,"file":"live-ticker-worker.js","sourceRoot":"","sources":["../../src/reporter/live-ticker-worker.ts"],"names":[],"mappings":"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","sourcesContent":["import { writeSync } from \"node:fs\";\nimport { parentPort, workerData } from \"node:worker_threads\";\nimport {\n currentSpinner,\n paintLiveLines,\n hasLiveAnimation,\n TickerFrame,\n} from \"./live-ticker-frame.js\";\nimport {\n TickerWorkerState,\n type LiveTickerWorkerData,\n type TickerWorkerMessage,\n} from \"./live-ticker-protocol.js\";\n\nconst port = parentPort;\nif (port === null) throw new Error(\"live-ticker-worker must run in a worker thread\");\n\n// SAFETY: createWorkerTicker owns the workerData shape.\nconst data = workerData as LiveTickerWorkerData;\nconst state = new TickerWorkerState(data.stateBuffer);\nconst frame = new TickerFrame((chunk) => writeSync(data.fd, chunk));\nlet columns = data.columns;\nlet lines: string[] = [];\nlet logPane: readonly string[] | undefined;\n\nfunction paintFrame(): void {\n const painted = paintLiveLines(\n lines,\n currentSpinner(data.unicode, data.refreshIntervalMs),\n Date.now(),\n columns,\n data.color,\n data.unicode,\n logPane\n );\n frame.redraw(painted, columns);\n data.framePort.postMessage(painted);\n}\n\nfunction redraw(): void {\n state.withWorkerOutput(paintFrame);\n}\n\nconst timer = setInterval(() => {\n if (state.inlineRequested) {\n clearInterval(timer);\n port.close();\n return;\n }\n if (hasLiveAnimation(lines)) {\n redraw();\n }\n}, data.refreshIntervalMs);\n\nport.on(\"message\", (message: TickerWorkerMessage) => {\n if (state.inlineRequested) {\n clearInterval(timer);\n port.close();\n return;\n }\n columns = message.columns ?? columns;\n if (message.type === \"log\") {\n state.withWorkerOutput(() => {\n frame.clear(columns);\n data.framePort.postMessage([]);\n writeSync(data.fd, message.text);\n state.acknowledgeLog();\n });\n return;\n }\n lines = message.lines;\n logPane = message.logPane;\n if (message.type === \"lines\") {\n redraw();\n return;\n }\n clearInterval(timer);\n try {\n state.withWorkerOutput(() => {\n paintFrame();\n frame.showCursor();\n });\n state.markStopped();\n } finally {\n port.close();\n }\n});\n"]}
@@ -1,8 +1,3 @@
1
- export declare const SPINNER_TOKEN = "\u0001";
2
- export declare const SHIMMER_TOKEN_START = "\u0004";
3
- export declare function elapsedToken(startedAtMs: number): string;
4
- export declare function shimmerToken(value: string): string;
5
- export declare function paintLiveLines(lines: readonly string[], spinner: string, nowMs: number, columns?: number, color?: boolean, unicode?: boolean, logPane?: readonly string[]): string[];
6
1
  export interface LiveTicker {
7
2
  setLines(lines: readonly string[], logPane?: readonly string[]): void;
8
3
  writeLog(text: string): void;
@@ -18,14 +13,4 @@ export interface LiveTickerOptions {
18
13
  write(chunk: string): void;
19
14
  workerUrl?: URL;
20
15
  }
21
- export declare class TickerFrame {
22
- private readonly write;
23
- private paintedLines;
24
- private cursorHidden;
25
- constructor(write: (chunk: string) => void, paintedLines?: readonly string[]);
26
- showCursor(): void;
27
- clear(columns?: number): void;
28
- redraw(lines: readonly string[], columns?: number): void;
29
- }
30
- export declare function currentSpinner(unicode: boolean, refreshIntervalMs: number): string;
31
16
  export declare function createLiveTicker(options: LiveTickerOptions): LiveTicker;