neon 2.47.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/README.md +54 -0
  2. package/dist/_shared/auth_selection.js +76 -79
  3. package/dist/_shared/credentials.js +166 -187
  4. package/dist/_shared/env-core/env.js +395 -0
  5. package/dist/_shared/env-core/reuse-secrets.js +179 -0
  6. package/dist/_shared/paths.js +129 -126
  7. package/dist/_shared/profiles.js +192 -242
  8. package/dist/_shared/secure_file.js +36 -38
  9. package/dist/_virtual/_rolldown/runtime.js +13 -0
  10. package/dist/analytics.js +163 -207
  11. package/dist/api.js +815 -758
  12. package/dist/auth.js +121 -141
  13. package/dist/auth_context.js +39 -53
  14. package/dist/cli.js +4 -7
  15. package/dist/commands/api.js +220 -250
  16. package/dist/commands/api_keys.js +251 -314
  17. package/dist/commands/auth.js +283 -328
  18. package/dist/commands/bootstrap.js +372 -437
  19. package/dist/commands/branches.js +304 -455
  20. package/dist/commands/bucket.js +374 -514
  21. package/dist/commands/checkout.js +213 -298
  22. package/dist/commands/config.js +575 -658
  23. package/dist/commands/connection_string.js +137 -165
  24. package/dist/commands/data_api.js +238 -260
  25. package/dist/commands/databases.js +67 -76
  26. package/dist/commands/deploy.js +31 -25
  27. package/dist/commands/dev.js +642 -681
  28. package/dist/commands/diff.js +156 -200
  29. package/dist/commands/env.js +243 -303
  30. package/dist/commands/functions.js +275 -341
  31. package/dist/commands/index.js +70 -65
  32. package/dist/commands/init.js +84 -119
  33. package/dist/commands/inspect.js +55 -55
  34. package/dist/commands/ip_allow.js +88 -120
  35. package/dist/commands/link.js +874 -1019
  36. package/dist/commands/logs.js +291 -0
  37. package/dist/commands/neon_auth.js +725 -933
  38. package/dist/commands/operations.js +34 -25
  39. package/dist/commands/orgs.js +28 -18
  40. package/dist/commands/profile.js +614 -845
  41. package/dist/commands/projects.js +313 -373
  42. package/dist/commands/psql.js +60 -58
  43. package/dist/commands/roles.js +55 -58
  44. package/dist/commands/schema_diff.js +87 -131
  45. package/dist/commands/set_context.js +34 -26
  46. package/dist/commands/snapshots.js +288 -413
  47. package/dist/commands/status.js +41 -37
  48. package/dist/commands/user.js +21 -10
  49. package/dist/commands/vpc_endpoints.js +85 -113
  50. package/dist/config.js +7 -6
  51. package/dist/config_format.js +50 -66
  52. package/dist/config_template.js +128 -157
  53. package/dist/context.js +183 -235
  54. package/dist/current_branch_fast_path.js +40 -49
  55. package/dist/dev/env.js +313 -394
  56. package/dist/dev/functions.js +54 -64
  57. package/dist/dev/inputs.js +46 -58
  58. package/dist/dev/runtime.js +135 -164
  59. package/dist/dev/websocket.js +766 -959
  60. package/dist/env.js +27 -33
  61. package/dist/env_file.js +118 -132
  62. package/dist/env_services.js +36 -38
  63. package/dist/errors.js +57 -68
  64. package/dist/functions_api.js +45 -43
  65. package/dist/help.js +189 -140
  66. package/dist/index.js +182 -257
  67. package/dist/init/agents.js +137 -118
  68. package/dist/init/auth.js +58 -68
  69. package/dist/init/bootstrap.js +325 -396
  70. package/dist/init/build_config.js +4 -2
  71. package/dist/init/detect_agent.js +56 -101
  72. package/dist/init/editors.js +35 -52
  73. package/dist/init/enrich_output.js +51 -66
  74. package/dist/init/extension.js +134 -171
  75. package/dist/init/inspect.js +179 -266
  76. package/dist/init/interactive.js +510 -622
  77. package/dist/init/neonctl.js +117 -168
  78. package/dist/init/orchestrate.js +157 -173
  79. package/dist/init/phases/auth.js +188 -202
  80. package/dist/init/phases/cleanup.js +23 -23
  81. package/dist/init/phases/db.js +251 -277
  82. package/dist/init/phases/getting_started.js +213 -223
  83. package/dist/init/phases/mcp.js +174 -224
  84. package/dist/init/phases/migrations.js +247 -248
  85. package/dist/init/phases/neon_auth.js +114 -133
  86. package/dist/init/phases/setup.js +546 -703
  87. package/dist/init/phases/skills.js +75 -86
  88. package/dist/init/phases/status.js +72 -67
  89. package/dist/init/resolve_context.js +102 -99
  90. package/dist/init/route_command.js +91 -98
  91. package/dist/init/skills.js +174 -218
  92. package/dist/init/vsix.js +77 -99
  93. package/dist/log.js +17 -16
  94. package/dist/neon_services.js +104 -129
  95. package/dist/parameters.gen.js +481 -471
  96. package/dist/pkg.js +17 -19
  97. package/dist/profile_keys.js +44 -47
  98. package/dist/psql/cli.js +44 -47
  99. package/dist/psql/command/cmd_cond.js +231 -406
  100. package/dist/psql/command/cmd_connect.js +557 -764
  101. package/dist/psql/command/cmd_copy.js +727 -983
  102. package/dist/psql/command/cmd_describe.js +1499 -1688
  103. package/dist/psql/command/cmd_format.js +733 -905
  104. package/dist/psql/command/cmd_io.js +1293 -2082
  105. package/dist/psql/command/cmd_lo.js +297 -359
  106. package/dist/psql/command/cmd_meta.js +727 -878
  107. package/dist/psql/command/cmd_misc.js +138 -172
  108. package/dist/psql/command/cmd_pipeline.js +547 -1099
  109. package/dist/psql/command/cmd_restrict.js +119 -155
  110. package/dist/psql/command/cmd_show.js +529 -688
  111. package/dist/psql/command/dispatch.js +261 -325
  112. package/dist/psql/command/inputQueue.js +35 -33
  113. package/dist/psql/command/shared.js +49 -63
  114. package/dist/psql/complete/filenames.js +90 -133
  115. package/dist/psql/complete/index.js +59 -97
  116. package/dist/psql/complete/matcher.js +236 -300
  117. package/dist/psql/complete/psqlVars.js +218 -223
  118. package/dist/psql/complete/queries.js +159 -177
  119. package/dist/psql/complete/rules.js +1493 -2299
  120. package/dist/psql/core/common.js +762 -1180
  121. package/dist/psql/core/help.js +456 -546
  122. package/dist/psql/core/mainloop.js +692 -1302
  123. package/dist/psql/core/prompt.js +391 -408
  124. package/dist/psql/core/settings.js +429 -644
  125. package/dist/psql/core/sqlHelp.js +480 -554
  126. package/dist/psql/core/startup.js +626 -815
  127. package/dist/psql/core/syncVars.js +67 -110
  128. package/dist/psql/core/variables.js +156 -278
  129. package/dist/psql/describe/formatters.js +884 -1285
  130. package/dist/psql/describe/processNamePattern.js +173 -260
  131. package/dist/psql/describe/queries.js +1368 -2403
  132. package/dist/psql/describe/versionGate.js +32 -41
  133. package/dist/psql/index.js +1414 -1927
  134. package/dist/psql/io/history.js +232 -271
  135. package/dist/psql/io/input.js +103 -108
  136. package/dist/psql/io/lineEditor/buffer.js +238 -319
  137. package/dist/psql/io/lineEditor/complete.js +135 -213
  138. package/dist/psql/io/lineEditor/filename.js +139 -148
  139. package/dist/psql/io/lineEditor/index.js +653 -870
  140. package/dist/psql/io/lineEditor/keymap.js +544 -702
  141. package/dist/psql/io/lineEditor/vt100.js +294 -341
  142. package/dist/psql/io/pgpass.js +158 -187
  143. package/dist/psql/io/pgservice.js +146 -183
  144. package/dist/psql/io/psqlrc.js +328 -403
  145. package/dist/psql/print/aligned.js +1020 -1683
  146. package/dist/psql/print/asciidoc.js +180 -214
  147. package/dist/psql/print/crosstab.js +281 -442
  148. package/dist/psql/print/csv.js +48 -70
  149. package/dist/psql/print/html.js +195 -226
  150. package/dist/psql/print/json.js +75 -88
  151. package/dist/psql/print/latex.js +291 -364
  152. package/dist/psql/print/pager.js +171 -242
  153. package/dist/psql/print/troff.js +194 -226
  154. package/dist/psql/print/unaligned.js +69 -95
  155. package/dist/psql/print/units.js +167 -169
  156. package/dist/psql/scanner/slash.js +428 -483
  157. package/dist/psql/scanner/sql.js +445 -889
  158. package/dist/psql/scanner/stringutils.js +309 -379
  159. package/dist/psql/types/index.js +2 -7
  160. package/dist/psql/types/scanner.js +25 -22
  161. package/dist/psql/wire/connection.js +2042 -2803
  162. package/dist/psql/wire/copy.js +84 -100
  163. package/dist/psql/wire/notify.js +39 -59
  164. package/dist/psql/wire/pipeline.js +305 -518
  165. package/dist/psql/wire/protocol.js +349 -417
  166. package/dist/psql/wire/sasl.js +180 -265
  167. package/dist/psql/wire/tls.js +400 -561
  168. package/dist/storage_api.js +115 -129
  169. package/dist/test_utils/fixtures.js +94 -113
  170. package/dist/test_utils/oauth_server.js +10 -7
  171. package/dist/test_utils/project_dir.js +33 -0
  172. package/dist/utils/ai_gateway_notice.js +131 -162
  173. package/dist/utils/api_enums.js +21 -28
  174. package/dist/utils/auth.js +10 -4
  175. package/dist/utils/branch_notice.js +20 -19
  176. package/dist/utils/branch_picker.js +83 -89
  177. package/dist/utils/cli_name.js +15 -12
  178. package/dist/utils/compute_units.js +20 -27
  179. package/dist/utils/config_diff.js +127 -158
  180. package/dist/utils/enrichers.js +95 -148
  181. package/dist/utils/esbuild.js +133 -147
  182. package/dist/utils/flags.js +35 -47
  183. package/dist/utils/formats.js +8 -15
  184. package/dist/utils/git_diff.js +69 -80
  185. package/dist/utils/inspect_db.js +101 -143
  186. package/dist/utils/inspect_queries.js +179 -142
  187. package/dist/utils/middlewares.js +37 -44
  188. package/dist/utils/openapi.js +87 -99
  189. package/dist/utils/package_manager.js +312 -110
  190. package/dist/utils/point_in_time.js +49 -53
  191. package/dist/utils/psql.js +89 -106
  192. package/dist/utils/service_picker.js +55 -58
  193. package/dist/utils/string.js +5 -5
  194. package/dist/utils/ui.js +38 -55
  195. package/dist/utils/write_sync.js +26 -35
  196. package/dist/utils/zip.js +4 -3
  197. package/dist/writer.js +67 -87
  198. package/package.json +9 -7
@@ -1,363 +1,316 @@
1
- /**
2
- * VT100/xterm input decoder and CSI escape helpers.
3
- *
4
- * Input side: a streaming decoder that consumes raw bytes from stdin and
5
- * emits `KeyEvent`s. Handles:
6
- *
7
- * - Printable ASCII (0x20..0x7e)
8
- * - Control bytes (0x00..0x1f, plus 0x7f → backspace)
9
- * - CSI sequences (`\x1b[...`): arrow keys, Home/End, Delete, PageUp/Dn,
10
- * bracketed paste markers
11
- * - SS3 sequences (`\x1bO<x>`) for application-mode arrows
12
- * - Alt-X sequences (`\x1b<letter>`) for Meta keystrokes
13
- * - Multi-byte UTF-8 codepoints (accumulated until complete)
14
- *
15
- * Output side: small helpers wrapping CSI escapes used by the renderer.
16
- *
17
- * The decoder is allocation-light: input chunks are appended to a private
18
- * buffer, then drained from the head. We never throw on malformed input —
19
- * leftover bytes that can't be decoded become a single `KeyEvent` with
20
- * `key: 'unknown'` so the editor can ring the bell instead of dying.
21
- */
1
+ //#region src/psql/io/lineEditor/vt100.ts
22
2
  /** Convenience: build a control-character event for byte `b` in 0x01..0x1f. */
23
3
  const controlEvent = (b) => {
24
- // ^A == 0x01 maps back to 'a'. Lowercase keeps things consistent.
25
- const letter = String.fromCharCode(b + 0x60);
26
- return { key: "char", char: letter, ctrl: true };
4
+ return {
5
+ key: "char",
6
+ char: String.fromCharCode(b + 96),
7
+ ctrl: true
8
+ };
27
9
  };
28
10
  /** Streaming decoder. Owns a small pending-byte buffer. */
29
- export class Vt100Decoder {
30
- constructor(opts = {}) {
31
- this.pending = [];
32
- /** UTF-8 continuation accumulator. */
33
- this.utf8Bytes = [];
34
- this.utf8Expect = 0;
35
- /** Active bare-Esc timer, if one is pending. */
36
- this.escTimer = null;
37
- /** True while we're sitting on a buffered Esc waiting for follow-on. */
38
- this.escPending = false;
39
- this.escTimeoutMs = opts.escTimeoutMs ?? 0;
40
- this.onTimeoutEvent = opts.onTimeoutEvent;
41
- }
42
- /** Reset internal state. Useful before re-entering raw mode after a fork. */
43
- reset() {
44
- this.pending.length = 0;
45
- this.utf8Bytes.length = 0;
46
- this.utf8Expect = 0;
47
- this.clearEscTimer();
48
- this.escPending = false;
49
- }
50
- clearEscTimer() {
51
- if (this.escTimer !== null) {
52
- clearTimeout(this.escTimer);
53
- this.escTimer = null;
54
- }
55
- }
56
- /**
57
- * Feed a chunk of input. Returns zero or more `KeyEvent`s; bytes that
58
- * form an incomplete sequence are buffered until the next call.
59
- */
60
- push(chunk) {
61
- // A follow-on byte arrived: cancel any pending Esc timer; the standard
62
- // sequence consumption path will see the Esc + byte together.
63
- if (chunk.length > 0 && this.escPending) {
64
- this.clearEscTimer();
65
- this.escPending = false;
66
- }
67
- for (const b of chunk)
68
- this.pending.push(b);
69
- const out = [];
70
- // Drain as long as we can make progress.
71
- for (;;) {
72
- const before = this.pending.length;
73
- const ev = this.tryConsume();
74
- if (ev === null) {
75
- // Need more bytes.
76
- if (this.pending.length !== before) {
77
- // Bytes were consumed without emitting an event (UTF-8 prefix).
78
- // Loop again to try further.
79
- continue;
80
- }
81
- break;
82
- }
83
- out.push(ev);
84
- if (this.pending.length === 0)
85
- break;
86
- }
87
- return out;
88
- }
89
- /**
90
- * Try to consume one key sequence from the head of `pending`. Returns
91
- * the event, or `null` if more input is needed. May consume bytes
92
- * without emitting (UTF-8 lead byte → continuation).
93
- */
94
- tryConsume() {
95
- if (this.pending.length === 0)
96
- return null;
97
- const b = this.pending[0];
98
- // Mid-UTF-8 sequence: accumulate continuation bytes.
99
- if (this.utf8Expect > 0) {
100
- if ((b & 0xc0) !== 0x80) {
101
- // Invalid continuation: drop the lead and continuations, emit unknown.
102
- this.utf8Bytes.length = 0;
103
- this.utf8Expect = 0;
104
- this.pending.shift();
105
- return { key: "unknown", raw: new Uint8Array([b]) };
106
- }
107
- this.utf8Bytes.push(b);
108
- this.pending.shift();
109
- this.utf8Expect--;
110
- if (this.utf8Expect === 0) {
111
- const bytes = Uint8Array.from(this.utf8Bytes);
112
- this.utf8Bytes.length = 0;
113
- const decoded = utf8Decode(bytes);
114
- return { key: "char", char: decoded };
115
- }
116
- return null;
117
- }
118
- // ASCII printable.
119
- if (b >= 0x20 && b <= 0x7e) {
120
- this.pending.shift();
121
- return { key: "char", char: String.fromCharCode(b) };
122
- }
123
- // Common controls.
124
- if (b === 0x09) {
125
- this.pending.shift();
126
- return { key: "tab" };
127
- }
128
- if (b === 0x0a || b === 0x0d) {
129
- this.pending.shift();
130
- return { key: "enter" };
131
- }
132
- if (b === 0x7f || b === 0x08) {
133
- this.pending.shift();
134
- return { key: "backspace" };
135
- }
136
- if (b === 0x1b) {
137
- // Escape: maybe alone, maybe the lead of a CSI/SS3/Meta sequence.
138
- return this.consumeEscape();
139
- }
140
- if (b < 0x20) {
141
- this.pending.shift();
142
- return controlEvent(b);
143
- }
144
- // UTF-8 lead byte.
145
- if ((b & 0xe0) === 0xc0) {
146
- this.utf8Expect = 1;
147
- this.utf8Bytes = [b];
148
- this.pending.shift();
149
- return null;
150
- }
151
- if ((b & 0xf0) === 0xe0) {
152
- this.utf8Expect = 2;
153
- this.utf8Bytes = [b];
154
- this.pending.shift();
155
- return null;
156
- }
157
- if ((b & 0xf8) === 0xf0) {
158
- this.utf8Expect = 3;
159
- this.utf8Bytes = [b];
160
- this.pending.shift();
161
- return null;
162
- }
163
- // Stray high byte. Emit unknown.
164
- this.pending.shift();
165
- return { key: "unknown", raw: new Uint8Array([b]) };
166
- }
167
- /**
168
- * Called when the head byte is 0x1b. Tries to consume an escape
169
- * sequence; returns `null` if more bytes are needed.
170
- *
171
- * When only the Esc byte sits in the buffer we have two strategies:
172
- *
173
- * 1) `escTimeoutMs === 0` (default for non-LineEditor callers): emit
174
- * the bare `escape` immediately. Matches the pre-polish behaviour.
175
- * 2) `escTimeoutMs > 0`: park the Esc byte, arm a `setTimeout`. If a
176
- * follow-on byte arrives within the window, `push()` cancels the
177
- * timer and the normal Esc-prefix path runs. Otherwise the timer
178
- * fires and we synthesise an `escape` event into the host queue.
179
- */
180
- consumeEscape() {
181
- if (this.pending.length === 1) {
182
- if (this.escTimeoutMs === 0) {
183
- this.pending.shift();
184
- return { key: "escape" };
185
- }
186
- // Already waiting? Don't re-arm the timer.
187
- if (this.escPending)
188
- return null;
189
- this.escPending = true;
190
- this.escTimer = setTimeout(() => {
191
- this.escTimer = null;
192
- // If the buffer head is still a lone Esc, drain it as a bare escape.
193
- if (this.escPending && this.pending[0] === 0x1b) {
194
- this.pending.shift();
195
- this.escPending = false;
196
- this.onTimeoutEvent?.({ key: "escape" });
197
- }
198
- else {
199
- this.escPending = false;
200
- }
201
- }, this.escTimeoutMs);
202
- return null;
203
- }
204
- const b1 = this.pending[1];
205
- // Esc [ ... — CSI sequence
206
- if (b1 === 0x5b /* '[' */) {
207
- return this.consumeCsi();
208
- }
209
- // Esc O X — SS3 (application-mode arrows on many terminals)
210
- if (b1 === 0x4f /* 'O' */) {
211
- if (this.pending.length < 3)
212
- return null;
213
- const b2 = this.pending[2];
214
- this.pending.splice(0, 3);
215
- return ss3ToEvent(b2);
216
- }
217
- // Esc <byte> — Alt/Meta combination.
218
- if (b1 < 0x20) {
219
- // Esc + control byte. Treat as Alt-Ctrl-X; we don't currently bind any.
220
- this.pending.splice(0, 2);
221
- const inner = controlEvent(b1);
222
- inner.meta = true;
223
- return inner;
224
- }
225
- if (b1 === 0x7f) {
226
- this.pending.splice(0, 2);
227
- return { key: "backspace", meta: true };
228
- }
229
- if (b1 >= 0x20 && b1 <= 0x7e) {
230
- this.pending.splice(0, 2);
231
- return { key: "char", char: String.fromCharCode(b1), meta: true };
232
- }
233
- // Unknown Esc-X sequence; consume both and emit unknown.
234
- const raw = Uint8Array.from(this.pending.slice(0, 2));
235
- this.pending.splice(0, 2);
236
- return { key: "unknown", raw };
237
- }
238
- consumeCsi() {
239
- // Format: ESC [ (parameter bytes 0x30..0x3f)* (intermediate bytes 0x20..0x2f)* (final byte 0x40..0x7e)
240
- // We start at pending[2] (after ESC '[').
241
- let i = 2;
242
- while (i < this.pending.length) {
243
- const b = this.pending[i];
244
- if (b >= 0x40 && b <= 0x7e)
245
- break;
246
- i++;
247
- }
248
- if (i === this.pending.length)
249
- return null; // need more bytes
250
- // Examine parameter bytes between pending[2] and pending[i-1].
251
- const params = this.pending.slice(2, i);
252
- const final = this.pending[i];
253
- const seqLen = i + 1;
254
- const consume = () => {
255
- this.pending.splice(0, seqLen);
256
- };
257
- // Bracketed paste markers: ESC [ 200 ~ and ESC [ 201 ~
258
- if (final === 0x7e /* '~' */) {
259
- const paramStr = String.fromCharCode(...params);
260
- consume();
261
- switch (paramStr) {
262
- case "1":
263
- case "7":
264
- return { key: "home" };
265
- case "2":
266
- return { key: "unknown" }; // Insert; we don't handle it.
267
- case "3":
268
- return { key: "delete" };
269
- case "4":
270
- case "8":
271
- return { key: "end" };
272
- case "5":
273
- return { key: "pageup" };
274
- case "6":
275
- return { key: "pagedown" };
276
- case "200":
277
- return { key: "paste-start" };
278
- case "201":
279
- return { key: "paste-end" };
280
- default:
281
- return { key: "unknown" };
282
- }
283
- }
284
- // Letter finals (A/B/C/D/H/F).
285
- // Param string may carry modifiers: e.g. "1;3" → Alt-arrow.
286
- const paramStr = String.fromCharCode(...params);
287
- consume();
288
- const meta = paramStr.endsWith(";3") || paramStr.endsWith(";7");
289
- switch (final) {
290
- case 0x41 /* 'A' */:
291
- return meta ? { key: "up", meta: true } : { key: "up" };
292
- case 0x42 /* 'B' */:
293
- return meta ? { key: "down", meta: true } : { key: "down" };
294
- case 0x43 /* 'C' */:
295
- return meta ? { key: "right", meta: true } : { key: "right" };
296
- case 0x44 /* 'D' */:
297
- return meta ? { key: "left", meta: true } : { key: "left" };
298
- case 0x48 /* 'H' */:
299
- return { key: "home" };
300
- case 0x46 /* 'F' */:
301
- return { key: "end" };
302
- case 0x5a /* 'Z' */:
303
- // Shift-Tab; treat as plain Tab for now (no reverse cycling yet).
304
- return { key: "tab", meta: true };
305
- default:
306
- return {
307
- key: "unknown",
308
- raw: Uint8Array.from(this.pending.slice(0, seqLen)),
309
- };
310
- }
311
- }
312
- }
11
+ var Vt100Decoder = class {
12
+ constructor(opts = {}) {
13
+ this.pending = [];
14
+ this.utf8Bytes = [];
15
+ this.utf8Expect = 0;
16
+ this.escTimer = null;
17
+ this.escPending = false;
18
+ this.escTimeoutMs = opts.escTimeoutMs ?? 0;
19
+ this.onTimeoutEvent = opts.onTimeoutEvent;
20
+ }
21
+ /** Reset internal state. Useful before re-entering raw mode after a fork. */
22
+ reset() {
23
+ this.pending.length = 0;
24
+ this.utf8Bytes.length = 0;
25
+ this.utf8Expect = 0;
26
+ this.clearEscTimer();
27
+ this.escPending = false;
28
+ }
29
+ clearEscTimer() {
30
+ if (this.escTimer !== null) {
31
+ clearTimeout(this.escTimer);
32
+ this.escTimer = null;
33
+ }
34
+ }
35
+ /**
36
+ * Feed a chunk of input. Returns zero or more `KeyEvent`s; bytes that
37
+ * form an incomplete sequence are buffered until the next call.
38
+ */
39
+ push(chunk) {
40
+ if (chunk.length > 0 && this.escPending) {
41
+ this.clearEscTimer();
42
+ this.escPending = false;
43
+ }
44
+ for (const b of chunk) this.pending.push(b);
45
+ const out = [];
46
+ for (;;) {
47
+ const before = this.pending.length;
48
+ const ev = this.tryConsume();
49
+ if (ev === null) {
50
+ if (this.pending.length !== before) continue;
51
+ break;
52
+ }
53
+ out.push(ev);
54
+ if (this.pending.length === 0) break;
55
+ }
56
+ return out;
57
+ }
58
+ /**
59
+ * Try to consume one key sequence from the head of `pending`. Returns
60
+ * the event, or `null` if more input is needed. May consume bytes
61
+ * without emitting (UTF-8 lead byte → continuation).
62
+ */
63
+ tryConsume() {
64
+ if (this.pending.length === 0) return null;
65
+ const b = this.pending[0];
66
+ if (this.utf8Expect > 0) {
67
+ if ((b & 192) !== 128) {
68
+ this.utf8Bytes.length = 0;
69
+ this.utf8Expect = 0;
70
+ this.pending.shift();
71
+ return {
72
+ key: "unknown",
73
+ raw: new Uint8Array([b])
74
+ };
75
+ }
76
+ this.utf8Bytes.push(b);
77
+ this.pending.shift();
78
+ this.utf8Expect--;
79
+ if (this.utf8Expect === 0) {
80
+ const bytes = Uint8Array.from(this.utf8Bytes);
81
+ this.utf8Bytes.length = 0;
82
+ return {
83
+ key: "char",
84
+ char: utf8Decode(bytes)
85
+ };
86
+ }
87
+ return null;
88
+ }
89
+ if (b >= 32 && b <= 126) {
90
+ this.pending.shift();
91
+ return {
92
+ key: "char",
93
+ char: String.fromCharCode(b)
94
+ };
95
+ }
96
+ if (b === 9) {
97
+ this.pending.shift();
98
+ return { key: "tab" };
99
+ }
100
+ if (b === 10 || b === 13) {
101
+ this.pending.shift();
102
+ return { key: "enter" };
103
+ }
104
+ if (b === 127 || b === 8) {
105
+ this.pending.shift();
106
+ return { key: "backspace" };
107
+ }
108
+ if (b === 27) return this.consumeEscape();
109
+ if (b < 32) {
110
+ this.pending.shift();
111
+ return controlEvent(b);
112
+ }
113
+ if ((b & 224) === 192) {
114
+ this.utf8Expect = 1;
115
+ this.utf8Bytes = [b];
116
+ this.pending.shift();
117
+ return null;
118
+ }
119
+ if ((b & 240) === 224) {
120
+ this.utf8Expect = 2;
121
+ this.utf8Bytes = [b];
122
+ this.pending.shift();
123
+ return null;
124
+ }
125
+ if ((b & 248) === 240) {
126
+ this.utf8Expect = 3;
127
+ this.utf8Bytes = [b];
128
+ this.pending.shift();
129
+ return null;
130
+ }
131
+ this.pending.shift();
132
+ return {
133
+ key: "unknown",
134
+ raw: new Uint8Array([b])
135
+ };
136
+ }
137
+ /**
138
+ * Called when the head byte is 0x1b. Tries to consume an escape
139
+ * sequence; returns `null` if more bytes are needed.
140
+ *
141
+ * When only the Esc byte sits in the buffer we have two strategies:
142
+ *
143
+ * 1) `escTimeoutMs === 0` (default for non-LineEditor callers): emit
144
+ * the bare `escape` immediately. Matches the pre-polish behaviour.
145
+ * 2) `escTimeoutMs > 0`: park the Esc byte, arm a `setTimeout`. If a
146
+ * follow-on byte arrives within the window, `push()` cancels the
147
+ * timer and the normal Esc-prefix path runs. Otherwise the timer
148
+ * fires and we synthesise an `escape` event into the host queue.
149
+ */
150
+ consumeEscape() {
151
+ if (this.pending.length === 1) {
152
+ if (this.escTimeoutMs === 0) {
153
+ this.pending.shift();
154
+ return { key: "escape" };
155
+ }
156
+ if (this.escPending) return null;
157
+ this.escPending = true;
158
+ this.escTimer = setTimeout(() => {
159
+ this.escTimer = null;
160
+ if (this.escPending && this.pending[0] === 27) {
161
+ this.pending.shift();
162
+ this.escPending = false;
163
+ this.onTimeoutEvent?.({ key: "escape" });
164
+ } else this.escPending = false;
165
+ }, this.escTimeoutMs);
166
+ return null;
167
+ }
168
+ const b1 = this.pending[1];
169
+ if (b1 === 91) return this.consumeCsi();
170
+ if (b1 === 79) {
171
+ if (this.pending.length < 3) return null;
172
+ const b2 = this.pending[2];
173
+ this.pending.splice(0, 3);
174
+ return ss3ToEvent(b2);
175
+ }
176
+ if (b1 < 32) {
177
+ this.pending.splice(0, 2);
178
+ const inner = controlEvent(b1);
179
+ inner.meta = true;
180
+ return inner;
181
+ }
182
+ if (b1 === 127) {
183
+ this.pending.splice(0, 2);
184
+ return {
185
+ key: "backspace",
186
+ meta: true
187
+ };
188
+ }
189
+ if (b1 >= 32 && b1 <= 126) {
190
+ this.pending.splice(0, 2);
191
+ return {
192
+ key: "char",
193
+ char: String.fromCharCode(b1),
194
+ meta: true
195
+ };
196
+ }
197
+ const raw = Uint8Array.from(this.pending.slice(0, 2));
198
+ this.pending.splice(0, 2);
199
+ return {
200
+ key: "unknown",
201
+ raw
202
+ };
203
+ }
204
+ consumeCsi() {
205
+ let i = 2;
206
+ while (i < this.pending.length) {
207
+ const b = this.pending[i];
208
+ if (b >= 64 && b <= 126) break;
209
+ i++;
210
+ }
211
+ if (i === this.pending.length) return null;
212
+ const params = this.pending.slice(2, i);
213
+ const final = this.pending[i];
214
+ const seqLen = i + 1;
215
+ const consume = () => {
216
+ this.pending.splice(0, seqLen);
217
+ };
218
+ if (final === 126) {
219
+ const paramStr = String.fromCharCode(...params);
220
+ consume();
221
+ switch (paramStr) {
222
+ case "1":
223
+ case "7": return { key: "home" };
224
+ case "2": return { key: "unknown" };
225
+ case "3": return { key: "delete" };
226
+ case "4":
227
+ case "8": return { key: "end" };
228
+ case "5": return { key: "pageup" };
229
+ case "6": return { key: "pagedown" };
230
+ case "200": return { key: "paste-start" };
231
+ case "201": return { key: "paste-end" };
232
+ default: return { key: "unknown" };
233
+ }
234
+ }
235
+ const paramStr = String.fromCharCode(...params);
236
+ consume();
237
+ const meta = paramStr.endsWith(";3") || paramStr.endsWith(";7");
238
+ switch (final) {
239
+ case 65: return meta ? {
240
+ key: "up",
241
+ meta: true
242
+ } : { key: "up" };
243
+ case 66: return meta ? {
244
+ key: "down",
245
+ meta: true
246
+ } : { key: "down" };
247
+ case 67: return meta ? {
248
+ key: "right",
249
+ meta: true
250
+ } : { key: "right" };
251
+ case 68: return meta ? {
252
+ key: "left",
253
+ meta: true
254
+ } : { key: "left" };
255
+ case 72: return { key: "home" };
256
+ case 70: return { key: "end" };
257
+ case 90: return {
258
+ key: "tab",
259
+ meta: true
260
+ };
261
+ default: return {
262
+ key: "unknown",
263
+ raw: Uint8Array.from(this.pending.slice(0, seqLen))
264
+ };
265
+ }
266
+ }
267
+ };
313
268
  /** Decode a small UTF-8 byte sequence (1..4 bytes) into a string. */
314
269
  const utf8Decode = (bytes) => {
315
- // Node provides TextDecoder; available on all supported runtimes (Node 18+).
316
- return new TextDecoder("utf-8", { fatal: false }).decode(bytes);
270
+ return new TextDecoder("utf-8", { fatal: false }).decode(bytes);
317
271
  };
318
272
  /** Translate an SS3 final byte (after `ESC O`) into a key event. */
319
273
  const ss3ToEvent = (b) => {
320
- switch (b) {
321
- case 0x41:
322
- return { key: "up" };
323
- case 0x42:
324
- return { key: "down" };
325
- case 0x43:
326
- return { key: "right" };
327
- case 0x44:
328
- return { key: "left" };
329
- case 0x48:
330
- return { key: "home" };
331
- case 0x46:
332
- return { key: "end" };
333
- default:
334
- return { key: "unknown", raw: new Uint8Array([0x1b, 0x4f, b]) };
335
- }
274
+ switch (b) {
275
+ case 65: return { key: "up" };
276
+ case 66: return { key: "down" };
277
+ case 67: return { key: "right" };
278
+ case 68: return { key: "left" };
279
+ case 72: return { key: "home" };
280
+ case 70: return { key: "end" };
281
+ default: return {
282
+ key: "unknown",
283
+ raw: new Uint8Array([
284
+ 27,
285
+ 79,
286
+ b
287
+ ])
288
+ };
289
+ }
336
290
  };
337
- // ---------------------------------------------------------------------------
338
- // CSI output helpers
339
- // ---------------------------------------------------------------------------
340
291
  /** Move cursor up N rows. */
341
- export const csiUp = (n) => (n > 0 ? `\x1b[${n}A` : "");
292
+ const csiUp = (n) => n > 0 ? `\x1b[${n}A` : "";
342
293
  /** Move cursor down N rows. */
343
- export const csiDown = (n) => (n > 0 ? `\x1b[${n}B` : "");
294
+ const csiDown = (n) => n > 0 ? `\x1b[${n}B` : "";
344
295
  /** Move cursor right N columns. */
345
- export const csiRight = (n) => (n > 0 ? `\x1b[${n}C` : "");
296
+ const csiRight = (n) => n > 0 ? `\x1b[${n}C` : "";
346
297
  /** Move cursor left N columns. */
347
- export const csiLeft = (n) => (n > 0 ? `\x1b[${n}D` : "");
298
+ const csiLeft = (n) => n > 0 ? `\x1b[${n}D` : "";
348
299
  /** Move cursor to column N (1-based). */
349
- export const csiToColumn = (col) => `\x1b[${col}G`;
300
+ const csiToColumn = (col) => `\x1b[${col}G`;
350
301
  /** Erase from cursor to end-of-line. */
351
- export const csiEraseToEol = () => "\x1b[K";
302
+ const csiEraseToEol = () => "\x1B[K";
352
303
  /** Erase entire screen and move cursor to home. */
353
- export const csiClearScreen = () => "\x1b[2J\x1b[H";
304
+ const csiClearScreen = () => "\x1B[2J\x1B[H";
354
305
  /** Carriage return: move to column 1 without writing a newline. */
355
- export const CR = "\r";
306
+ const CR = "\r";
356
307
  /** Newline (LF). */
357
- export const LF = "\n";
308
+ const LF = "\n";
358
309
  /** Enable bracketed paste mode (DEC private mode 2004). */
359
- export const enableBracketedPaste = () => "\x1b[?2004h";
310
+ const enableBracketedPaste = () => "\x1B[?2004h";
360
311
  /** Disable bracketed paste mode. */
361
- export const disableBracketedPaste = () => "\x1b[?2004l";
312
+ const disableBracketedPaste = () => "\x1B[?2004l";
362
313
  /** Audible bell. */
363
- export const BEL = "\x07";
314
+ const BEL = "\x07";
315
+ //#endregion
316
+ export { BEL, CR, LF, Vt100Decoder, csiClearScreen, csiDown, csiEraseToEol, csiLeft, csiRight, csiToColumn, csiUp, disableBracketedPaste, enableBracketedPaste };