neon 3.0.0 → 3.1.1

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 (207) hide show
  1. package/README.md +70 -5
  2. package/dist/_chunks/auth_selection-DGgq6ifc.js +83 -0
  3. package/dist/_chunks/cmd_pipeline-CUbBO9U_.js +2818 -0
  4. package/dist/_chunks/credentials-MYdHdKah.js +188 -0
  5. package/dist/_chunks/env-NbA61JR3.js +585 -0
  6. package/dist/_chunks/env_services-Tz9G4JeT.js +531 -0
  7. package/dist/_chunks/paths-DMq0Lt7a.js +151 -0
  8. package/dist/_chunks/profiles-Ir29rqns.js +217 -0
  9. package/dist/_chunks/psql-DWH-kc69.js +2169 -0
  10. package/dist/_chunks/rolldown-runtime-D7D4PA-g.js +13 -0
  11. package/dist/_chunks/secure_file-BucZj4yQ.js +39 -0
  12. package/dist/analytics.js +163 -207
  13. package/dist/api.js +815 -758
  14. package/dist/auth.js +121 -141
  15. package/dist/auth_context.js +39 -53
  16. package/dist/cli.js +4 -7
  17. package/dist/commands/api.js +220 -250
  18. package/dist/commands/api_keys.js +251 -314
  19. package/dist/commands/auth.js +283 -328
  20. package/dist/commands/bootstrap.js +372 -437
  21. package/dist/commands/branches.js +304 -455
  22. package/dist/commands/bucket.js +374 -514
  23. package/dist/commands/checkout.js +213 -298
  24. package/dist/commands/config.js +573 -690
  25. package/dist/commands/connection_string.js +137 -165
  26. package/dist/commands/data_api.js +238 -260
  27. package/dist/commands/databases.js +67 -76
  28. package/dist/commands/deploy.js +31 -25
  29. package/dist/commands/dev.js +639 -719
  30. package/dist/commands/diff.js +156 -200
  31. package/dist/commands/env.js +255 -305
  32. package/dist/commands/functions.js +275 -355
  33. package/dist/commands/index.js +70 -65
  34. package/dist/commands/init.js +84 -119
  35. package/dist/commands/inspect.js +55 -55
  36. package/dist/commands/ip_allow.js +88 -120
  37. package/dist/commands/link.js +874 -1019
  38. package/dist/commands/logs.js +291 -0
  39. package/dist/commands/neon_auth.js +725 -933
  40. package/dist/commands/operations.js +34 -25
  41. package/dist/commands/orgs.js +28 -18
  42. package/dist/commands/profile.js +615 -846
  43. package/dist/commands/projects.js +313 -373
  44. package/dist/commands/psql.js +60 -58
  45. package/dist/commands/roles.js +55 -58
  46. package/dist/commands/schema_diff.js +87 -131
  47. package/dist/commands/set_context.js +34 -26
  48. package/dist/commands/snapshots.js +288 -413
  49. package/dist/commands/status.js +41 -37
  50. package/dist/commands/user.js +21 -10
  51. package/dist/commands/vpc_endpoints.js +85 -113
  52. package/dist/config.js +7 -6
  53. package/dist/config_format.js +50 -66
  54. package/dist/config_template.js +128 -157
  55. package/dist/context.js +183 -235
  56. package/dist/current_branch_fast_path.js +40 -49
  57. package/dist/dev/env.js +2 -446
  58. package/dist/dev/functions.js +54 -68
  59. package/dist/dev/inputs.js +46 -58
  60. package/dist/dev/runtime.js +135 -164
  61. package/dist/dev/websocket.js +766 -959
  62. package/dist/env.js +27 -33
  63. package/dist/env_file.js +118 -132
  64. package/dist/env_services.js +2 -51
  65. package/dist/errors.js +57 -68
  66. package/dist/functions_api.js +45 -43
  67. package/dist/help.js +189 -140
  68. package/dist/index.js +182 -257
  69. package/dist/init/agents.js +137 -118
  70. package/dist/init/auth.js +58 -68
  71. package/dist/init/bootstrap.js +325 -396
  72. package/dist/init/build_config.js +4 -2
  73. package/dist/init/detect_agent.js +56 -101
  74. package/dist/init/editors.js +35 -52
  75. package/dist/init/enrich_output.js +51 -66
  76. package/dist/init/extension.js +134 -171
  77. package/dist/init/inspect.js +179 -266
  78. package/dist/init/interactive.js +510 -622
  79. package/dist/init/neonctl.js +117 -168
  80. package/dist/init/orchestrate.js +157 -173
  81. package/dist/init/phases/auth.js +188 -202
  82. package/dist/init/phases/cleanup.js +23 -23
  83. package/dist/init/phases/db.js +251 -277
  84. package/dist/init/phases/getting_started.js +213 -223
  85. package/dist/init/phases/mcp.js +174 -224
  86. package/dist/init/phases/migrations.js +247 -248
  87. package/dist/init/phases/neon_auth.js +114 -133
  88. package/dist/init/phases/setup.js +546 -703
  89. package/dist/init/phases/skills.js +75 -86
  90. package/dist/init/phases/status.js +72 -67
  91. package/dist/init/resolve_context.js +102 -99
  92. package/dist/init/route_command.js +91 -98
  93. package/dist/init/skills.js +174 -218
  94. package/dist/init/vsix.js +77 -99
  95. package/dist/log.js +17 -16
  96. package/dist/neon_services.js +104 -129
  97. package/dist/parameters.gen.js +481 -471
  98. package/dist/pkg.js +17 -19
  99. package/dist/profile_keys.js +44 -47
  100. package/dist/psql/cli.js +44 -47
  101. package/dist/psql/command/cmd_cond.js +231 -406
  102. package/dist/psql/command/cmd_connect.js +557 -764
  103. package/dist/psql/command/cmd_copy.js +728 -984
  104. package/dist/psql/command/cmd_describe.js +1499 -1688
  105. package/dist/psql/command/cmd_format.js +733 -905
  106. package/dist/psql/command/cmd_io.js +2 -2193
  107. package/dist/psql/command/cmd_lo.js +297 -359
  108. package/dist/psql/command/cmd_meta.js +727 -878
  109. package/dist/psql/command/cmd_misc.js +138 -172
  110. package/dist/psql/command/cmd_pipeline.js +2 -1148
  111. package/dist/psql/command/cmd_restrict.js +119 -155
  112. package/dist/psql/command/cmd_show.js +529 -688
  113. package/dist/psql/command/dispatch.js +260 -325
  114. package/dist/psql/command/inputQueue.js +35 -33
  115. package/dist/psql/command/shared.js +49 -63
  116. package/dist/psql/complete/filenames.js +90 -133
  117. package/dist/psql/complete/index.js +59 -97
  118. package/dist/psql/complete/matcher.js +236 -300
  119. package/dist/psql/complete/psqlVars.js +218 -223
  120. package/dist/psql/complete/queries.js +159 -177
  121. package/dist/psql/complete/rules.js +1493 -2299
  122. package/dist/psql/core/common.js +2 -1253
  123. package/dist/psql/core/help.js +456 -546
  124. package/dist/psql/core/mainloop.js +692 -1303
  125. package/dist/psql/core/prompt.js +391 -408
  126. package/dist/psql/core/settings.js +429 -644
  127. package/dist/psql/core/sqlHelp.js +480 -554
  128. package/dist/psql/core/startup.js +2 -846
  129. package/dist/psql/core/syncVars.js +67 -110
  130. package/dist/psql/core/variables.js +156 -278
  131. package/dist/psql/describe/formatters.js +884 -1285
  132. package/dist/psql/describe/processNamePattern.js +173 -260
  133. package/dist/psql/describe/queries.js +1368 -2403
  134. package/dist/psql/describe/versionGate.js +32 -41
  135. package/dist/psql/index.js +2 -2030
  136. package/dist/psql/io/history.js +232 -271
  137. package/dist/psql/io/input.js +103 -108
  138. package/dist/psql/io/lineEditor/buffer.js +238 -319
  139. package/dist/psql/io/lineEditor/complete.js +135 -213
  140. package/dist/psql/io/lineEditor/filename.js +139 -148
  141. package/dist/psql/io/lineEditor/index.js +653 -870
  142. package/dist/psql/io/lineEditor/keymap.js +544 -702
  143. package/dist/psql/io/lineEditor/vt100.js +294 -341
  144. package/dist/psql/io/pgpass.js +158 -187
  145. package/dist/psql/io/pgservice.js +146 -183
  146. package/dist/psql/io/psqlrc.js +328 -403
  147. package/dist/psql/print/aligned.js +1020 -1683
  148. package/dist/psql/print/asciidoc.js +180 -214
  149. package/dist/psql/print/crosstab.js +281 -442
  150. package/dist/psql/print/csv.js +48 -70
  151. package/dist/psql/print/html.js +195 -226
  152. package/dist/psql/print/json.js +75 -88
  153. package/dist/psql/print/latex.js +291 -364
  154. package/dist/psql/print/pager.js +171 -242
  155. package/dist/psql/print/troff.js +194 -226
  156. package/dist/psql/print/unaligned.js +69 -95
  157. package/dist/psql/print/units.js +167 -169
  158. package/dist/psql/scanner/slash.js +428 -483
  159. package/dist/psql/scanner/sql.js +445 -889
  160. package/dist/psql/scanner/stringutils.js +309 -379
  161. package/dist/psql/types/index.js +8 -7
  162. package/dist/psql/types/scanner.js +25 -22
  163. package/dist/psql/wire/connection.js +2042 -2803
  164. package/dist/psql/wire/copy.js +84 -100
  165. package/dist/psql/wire/notify.js +39 -59
  166. package/dist/psql/wire/pipeline.js +305 -518
  167. package/dist/psql/wire/protocol.js +349 -417
  168. package/dist/psql/wire/sasl.js +180 -265
  169. package/dist/psql/wire/tls.js +400 -561
  170. package/dist/storage_api.js +115 -129
  171. package/dist/test_utils/fixtures.js +94 -113
  172. package/dist/test_utils/oauth_server.js +10 -7
  173. package/dist/test_utils/project_dir.js +33 -0
  174. package/dist/utils/ai_gateway_notice.js +131 -162
  175. package/dist/utils/api_enums.js +21 -28
  176. package/dist/utils/auth.js +10 -4
  177. package/dist/utils/branch_notice.js +20 -19
  178. package/dist/utils/branch_picker.js +83 -89
  179. package/dist/utils/cli_name.js +15 -12
  180. package/dist/utils/compute_units.js +20 -27
  181. package/dist/utils/config_diff.js +127 -158
  182. package/dist/utils/enrichers.js +95 -148
  183. package/dist/utils/esbuild.js +130 -189
  184. package/dist/utils/flags.js +35 -47
  185. package/dist/utils/formats.js +8 -15
  186. package/dist/utils/git_diff.js +69 -80
  187. package/dist/utils/inspect_db.js +101 -143
  188. package/dist/utils/inspect_queries.js +179 -142
  189. package/dist/utils/middlewares.js +39 -45
  190. package/dist/utils/openapi.js +87 -99
  191. package/dist/utils/package_manager.js +312 -110
  192. package/dist/utils/point_in_time.js +49 -53
  193. package/dist/utils/psql.js +89 -106
  194. package/dist/utils/service_picker.js +55 -58
  195. package/dist/utils/string.js +5 -5
  196. package/dist/utils/ui.js +38 -55
  197. package/dist/utils/write_sync.js +26 -35
  198. package/dist/utils/zip.js +4 -3
  199. package/dist/writer.js +67 -87
  200. package/package.json +11 -6
  201. package/dist/_shared/auth_selection.js +0 -86
  202. package/dist/_shared/credentials.js +0 -209
  203. package/dist/_shared/env-core/env.js +0 -558
  204. package/dist/_shared/env-core/reuse-secrets.js +0 -223
  205. package/dist/_shared/paths.js +0 -148
  206. package/dist/_shared/profiles.js +0 -276
  207. package/dist/_shared/secure_file.js +0 -43
@@ -1,267 +1,196 @@
1
- import { accessSync, constants as fsConstants } from "node:fs";
1
+ import { accessSync, constants } from "node:fs";
2
2
  import { spawn } from "child_process";
3
3
  import { basename, delimiter, isAbsolute, join } from "path";
4
+ //#region src/psql/print/pager.ts
4
5
  /**
5
- * Can `command` be spawned with `shell:false`? A bare name is searched on
6
- * `$PATH`; a path is checked directly. Used to avoid `spawn(missingBinary,
7
- * { shell:false })`, which does NOT throw synchronously (our try/catch never
8
- * fires) but emits an async ENOENT — leaving writes to the dead child's stdin
9
- * to vanish into a blank terminal (review item #19).
10
- */
6
+ * Can `command` be spawned with `shell:false`? A bare name is searched on
7
+ * `$PATH`; a path is checked directly. Used to avoid `spawn(missingBinary,
8
+ * { shell:false })`, which does NOT throw synchronously (our try/catch never
9
+ * fires) but emits an async ENOENT — leaving writes to the dead child's stdin
10
+ * to vanish into a blank terminal (review item #19).
11
+ */
11
12
  const pagerCommandResolvable = (command) => {
12
- const ok = (p) => {
13
- try {
14
- accessSync(p, fsConstants.X_OK);
15
- return true;
16
- }
17
- catch {
18
- // On Windows X_OK is not meaningful and a bare name omits `.exe`; fall
19
- // back to a plain existence probe with common executable extensions.
20
- if (process.platform === "win32") {
21
- for (const ext of ["", ".exe", ".cmd", ".bat"]) {
22
- try {
23
- accessSync(p + ext, fsConstants.F_OK);
24
- return true;
25
- }
26
- catch {
27
- /* keep trying */
28
- }
29
- }
30
- }
31
- return false;
32
- }
33
- };
34
- if (command.includes("/") ||
35
- command.includes("\\") ||
36
- isAbsolute(command)) {
37
- return ok(command);
38
- }
39
- return (process.env.PATH ?? "")
40
- .split(delimiter)
41
- .filter((d) => d.length > 0)
42
- .some((dir) => ok(join(dir, command)));
13
+ const ok = (p) => {
14
+ try {
15
+ accessSync(p, constants.X_OK);
16
+ return true;
17
+ } catch {
18
+ if (process.platform === "win32") for (const ext of [
19
+ "",
20
+ ".exe",
21
+ ".cmd",
22
+ ".bat"
23
+ ]) try {
24
+ accessSync(p + ext, constants.F_OK);
25
+ return true;
26
+ } catch {}
27
+ return false;
28
+ }
29
+ };
30
+ if (command.includes("/") || command.includes("\\") || isAbsolute(command)) return ok(command);
31
+ return (process.env.PATH ?? "").split(delimiter).filter((d) => d.length > 0).some((dir) => ok(join(dir, command)));
43
32
  };
44
33
  /**
45
- * Resolve the pager command string. Mirrors upstream:
46
- * PSQL_PAGER → PAGER → DEFAULT_PAGER (`less` on POSIX, none on Windows).
47
- *
48
- * Empty / whitespace-only env values FALL THROUGH to the next candidate. This
49
- * deliberately diverges from strict upstream (where `PSQL_PAGER=''` would
50
- * disable the pager outright) because in Node a spawned child cannot easily
51
- * "unset" an inherited env var — tests have to override it with the empty
52
- * string. Treating empty values as "unset" matches the conformance spec
53
- * (`tests/psql-conformance/tap/030_pager.spec.ts`) and lets `PSQL_PAGER=''`
54
- * fall through to PAGER, which is the user-friendly interpretation.
55
- *
56
- * Users who want to disable the pager unconditionally should set
57
- * `\pset pager off` (preferred) or unset both env vars before launch.
58
- */
34
+ * Resolve the pager command string. Mirrors upstream:
35
+ * PSQL_PAGER → PAGER → DEFAULT_PAGER (`less` on POSIX, none on Windows).
36
+ *
37
+ * Empty / whitespace-only env values FALL THROUGH to the next candidate. This
38
+ * deliberately diverges from strict upstream (where `PSQL_PAGER=''` would
39
+ * disable the pager outright) because in Node a spawned child cannot easily
40
+ * "unset" an inherited env var — tests have to override it with the empty
41
+ * string. Treating empty values as "unset" matches the conformance spec
42
+ * (`tests/psql-conformance/tap/030_pager.spec.ts`) and lets `PSQL_PAGER=''`
43
+ * fall through to PAGER, which is the user-friendly interpretation.
44
+ *
45
+ * Users who want to disable the pager unconditionally should set
46
+ * `\pset pager off` (preferred) or unset both env vars before launch.
47
+ */
59
48
  const resolvePagerCmd = (opts) => {
60
- const env = opts.env ?? process.env;
61
- const candidates = [
62
- opts.pagerCmd,
63
- env.PSQL_PAGER,
64
- env.PAGER,
65
- ];
66
- for (const c of candidates) {
67
- if (c === undefined)
68
- continue;
69
- // Empty or whitespace-only → treat as "not set" and try the next slot.
70
- if (/^\s*$/.test(c))
71
- continue;
72
- return c;
73
- }
74
- // DEFAULT_PAGER: `less` on POSIX; nothing on Windows.
75
- if (process.platform === "win32")
76
- return "";
77
- return "less";
49
+ const env = opts.env ?? process.env;
50
+ const candidates = [
51
+ opts.pagerCmd,
52
+ env.PSQL_PAGER,
53
+ env.PAGER
54
+ ];
55
+ for (const c of candidates) {
56
+ if (c === void 0) continue;
57
+ if (/^\s*$/.test(c)) continue;
58
+ return c;
59
+ }
60
+ if (process.platform === "win32") return "";
61
+ return "less";
78
62
  };
79
63
  const getIsTty = (opts) => {
80
- if (opts.isTty !== undefined)
81
- return opts.isTty;
82
- const stream = opts.stdout ?? process.stdout;
83
- // Some test streams won't have isTTY.
84
- const tty = stream.isTTY;
85
- return Boolean(tty);
64
+ if (opts.isTty !== void 0) return opts.isTty;
65
+ const tty = (opts.stdout ?? process.stdout).isTTY;
66
+ return Boolean(tty);
86
67
  };
87
68
  const getTerminalHeight = (opts) => {
88
- if (opts.terminalHeight !== undefined)
89
- return opts.terminalHeight;
90
- const stream = opts.stdout ?? process.stdout;
91
- const rows = stream.rows;
92
- // If we can't tell, fall back to 24 (classic VT100 default).
93
- return typeof rows === "number" && rows > 0 ? rows : 24;
69
+ if (opts.terminalHeight !== void 0) return opts.terminalHeight;
70
+ const rows = (opts.stdout ?? process.stdout).rows;
71
+ return typeof rows === "number" && rows > 0 ? rows : 24;
94
72
  };
95
73
  /** Standalone helper to determine whether a pager is needed at all. */
96
- export const isPagerNeeded = (opts) => {
97
- if (opts.pager === "off")
98
- return false;
99
- const cmd = resolvePagerCmd(opts);
100
- if (cmd === "")
101
- return false;
102
- if (opts.pager === "always")
103
- return true;
104
- // pager === 'on'
105
- if (!getIsTty(opts))
106
- return false;
107
- if (opts.lines === undefined)
108
- return false;
109
- const threshold = Math.max(opts.pagerMinLines, getTerminalHeight(opts));
110
- return opts.lines >= threshold;
74
+ const isPagerNeeded = (opts) => {
75
+ if (opts.pager === "off") return false;
76
+ if (resolvePagerCmd(opts) === "") return false;
77
+ if (opts.pager === "always") return true;
78
+ if (!getIsTty(opts)) return false;
79
+ if (opts.lines === void 0) return false;
80
+ const threshold = Math.max(opts.pagerMinLines, getTerminalHeight(opts));
81
+ return opts.lines >= threshold;
111
82
  };
112
83
  /**
113
- * Decide whether a result of `rowCount` rows by `colCount` columns should be
114
- * routed through the pager when written to `output`.
115
- *
116
- * NOTE on `pager === 'always'`: the TTY check is INTENTIONALLY skipped in
117
- * this case. Upstream's `\pset pager always` is a user-explicit "force the
118
- * pager on" override; honouring it on a pipe matches the integration test
119
- * harness contract (see `tests/psql-conformance/tap/030_pager.spec.ts`) where
120
- * the child process has no controlling TTY but the spec still requires the
121
- * configured PAGER to be invoked. For `pager === 'on'` (auto mode) we keep
122
- * the TTY guard so non-interactive runs don't spuriously spawn `less`.
123
- */
124
- export const shouldPage = (opts) => {
125
- if (opts.pager === "off")
126
- return false;
127
- if (opts.redirectedOutput)
128
- return false;
129
- // Rough heuristic for "lines" — header (3) + rows + footer (1). Matches
130
- // upstream `IsPagerNeeded` which counts rendered table lines.
131
- const HEADER_LINES = 3;
132
- const FOOTER_LINES = 1;
133
- const estimatedLines = HEADER_LINES + Math.max(0, opts.rowCount) + FOOTER_LINES;
134
- // TTY check: explicit override wins, else inspect the stream's own isTTY.
135
- const isTty = opts.isTty !== undefined
136
- ? opts.isTty
137
- : Boolean(opts.output.isTTY);
138
- // `pager === 'always'` bypasses the TTY guard — see the docstring above.
139
- // The remaining gates (resolved cmd, redirected output, off) are enforced
140
- // inside `isPagerNeeded`.
141
- if (opts.pager !== "always" && !isTty)
142
- return false;
143
- return isPagerNeeded({
144
- pager: opts.pager,
145
- pagerMinLines: opts.pagerMinLines,
146
- pagerCmd: opts.pagerCmd,
147
- env: opts.env,
148
- stdout: opts.output,
149
- isTty,
150
- terminalHeight: opts.terminalHeight,
151
- lines: estimatedLines,
152
- });
84
+ * Decide whether a result of `rowCount` rows by `colCount` columns should be
85
+ * routed through the pager when written to `output`.
86
+ *
87
+ * NOTE on `pager === 'always'`: the TTY check is INTENTIONALLY skipped in
88
+ * this case. Upstream's `\pset pager always` is a user-explicit "force the
89
+ * pager on" override; honouring it on a pipe matches the integration test
90
+ * harness contract (see `tests/psql-conformance/tap/030_pager.spec.ts`) where
91
+ * the child process has no controlling TTY but the spec still requires the
92
+ * configured PAGER to be invoked. For `pager === 'on'` (auto mode) we keep
93
+ * the TTY guard so non-interactive runs don't spuriously spawn `less`.
94
+ */
95
+ const shouldPage = (opts) => {
96
+ if (opts.pager === "off") return false;
97
+ if (opts.redirectedOutput) return false;
98
+ const estimatedLines = 3 + Math.max(0, opts.rowCount) + 1;
99
+ const isTty = opts.isTty !== void 0 ? opts.isTty : Boolean(opts.output.isTTY);
100
+ if (opts.pager !== "always" && !isTty) return false;
101
+ return isPagerNeeded({
102
+ pager: opts.pager,
103
+ pagerMinLines: opts.pagerMinLines,
104
+ pagerCmd: opts.pagerCmd,
105
+ env: opts.env,
106
+ stdout: opts.output,
107
+ isTty,
108
+ terminalHeight: opts.terminalHeight,
109
+ lines: estimatedLines
110
+ });
153
111
  };
154
112
  const SHELL_META = /[\s|;><]/;
155
113
  const parsePagerCmd = (cmd) => {
156
- // Match upstream behavior: when the value looks shell-y, hand it off to
157
- // /bin/sh -c. Otherwise treat it as a direct argv[0].
158
- if (SHELL_META.test(cmd)) {
159
- return { command: cmd, args: [], shell: true };
160
- }
161
- return { command: cmd, args: [], shell: false };
114
+ if (SHELL_META.test(cmd)) return {
115
+ command: cmd,
116
+ args: [],
117
+ shell: true
118
+ };
119
+ return {
120
+ command: cmd,
121
+ args: [],
122
+ shell: false
123
+ };
162
124
  };
163
125
  const buildPagerEnv = (cmd, baseEnv) => {
164
- const env = { ...baseEnv };
165
- // psql sets LESS=FRX by default; mirror it when the resolved pager is
166
- // `less` and the caller hasn't already set LESS.
167
- if (env.LESS === undefined) {
168
- // Pull out the first whitespace-separated token to detect `less` even
169
- // when args follow (e.g. "less -S").
170
- const firstToken = cmd.trim().split(/\s+/, 1)[0] ?? "";
171
- const program = basename(firstToken);
172
- if (program === "less") {
173
- env.LESS = "FRX";
174
- }
175
- }
176
- return env;
126
+ const env = { ...baseEnv };
127
+ if (env.LESS === void 0) {
128
+ const firstToken = cmd.trim().split(/\s+/, 1)[0] ?? "";
129
+ if (basename(firstToken) === "less") env.LESS = "FRX";
130
+ }
131
+ return env;
177
132
  };
178
133
  const noOpHandle = (out) => ({
179
- out,
180
- spawned: false,
181
- close: () => Promise.resolve(0),
134
+ out,
135
+ spawned: false,
136
+ close: () => Promise.resolve(0)
182
137
  });
183
138
  /**
184
- * Returns a PagerHandle. Caller writes data to `out`, then calls `close()`.
185
- * If no pager spawned (pager='off', not a TTY, or fewer lines than threshold),
186
- * `out` is `stdout`.
187
- */
188
- export const openPager = (opts) => {
189
- const stdout = opts.stdout ?? process.stdout;
190
- if (!isPagerNeeded(opts)) {
191
- return noOpHandle(stdout);
192
- }
193
- const cmd = resolvePagerCmd(opts);
194
- // isPagerNeeded already verified cmd is non-empty, but guard for safety.
195
- if (cmd === "") {
196
- return noOpHandle(stdout);
197
- }
198
- const { command, shell } = parsePagerCmd(cmd);
199
- // A missing pager binary with shell:false emits an async ENOENT that the
200
- // try/catch below cannot catch, and the result would be silently-discarded
201
- // output. Pre-check and fall back to stdout instead (review item #19).
202
- // shell:true goes through `sh -c`, which always exists.
203
- if (!shell && !pagerCommandResolvable(command)) {
204
- return noOpHandle(stdout);
205
- }
206
- const baseEnv = opts.env ?? process.env;
207
- const childEnv = buildPagerEnv(cmd, baseEnv);
208
- let child;
209
- try {
210
- child = spawn(command, [], {
211
- stdio: ["pipe", "inherit", "inherit"],
212
- shell,
213
- env: childEnv,
214
- });
215
- }
216
- catch {
217
- // If the pager fails to spawn, fall back to stdout (matches upstream:
218
- // `if (pagerpipe) return pagerpipe; restore_sigpipe_trap(); ... return stdout`).
219
- return noOpHandle(stdout);
220
- }
221
- const stdin = child.stdin;
222
- if (stdin === null) {
223
- // Should not happen given stdio: ['pipe', ...], but be defensive.
224
- return noOpHandle(stdout);
225
- }
226
- // Swallow EPIPE: the user can quit the pager early, after which any
227
- // pending writes will fail with EPIPE. Upstream relies on SIGPIPE being
228
- // ignored to short-circuit the write loop; we just drop the error.
229
- stdin.on("error", (err) => {
230
- if (err.code !== "EPIPE") {
231
- // Re-throw anything unexpected.
232
- throw err;
233
- }
234
- });
235
- const exitPromise = new Promise((resolve) => {
236
- const settle = (code) => {
237
- resolve(code ?? 0);
238
- };
239
- child.once("exit", (code) => {
240
- settle(code);
241
- });
242
- child.once("error", () => {
243
- // If the child errored (e.g. ENOENT), surface a non-zero exit code
244
- // but don't throw — the caller's writes will have hit EPIPE which
245
- // we already swallow.
246
- settle(127);
247
- });
248
- });
249
- return {
250
- out: stdin,
251
- spawned: true,
252
- close: () => {
253
- // End stdin then wait for the pager to drain & exit.
254
- if (!stdin.writableEnded) {
255
- try {
256
- stdin.end();
257
- }
258
- catch (err) {
259
- const e = err;
260
- if (e.code !== "EPIPE")
261
- throw err;
262
- }
263
- }
264
- return exitPromise;
265
- },
266
- };
139
+ * Returns a PagerHandle. Caller writes data to `out`, then calls `close()`.
140
+ * If no pager spawned (pager='off', not a TTY, or fewer lines than threshold),
141
+ * `out` is `stdout`.
142
+ */
143
+ const openPager = (opts) => {
144
+ const stdout = opts.stdout ?? process.stdout;
145
+ if (!isPagerNeeded(opts)) return noOpHandle(stdout);
146
+ const cmd = resolvePagerCmd(opts);
147
+ if (cmd === "") return noOpHandle(stdout);
148
+ const { command, shell } = parsePagerCmd(cmd);
149
+ if (!shell && !pagerCommandResolvable(command)) return noOpHandle(stdout);
150
+ const baseEnv = opts.env ?? process.env;
151
+ const childEnv = buildPagerEnv(cmd, baseEnv);
152
+ let child;
153
+ try {
154
+ child = spawn(command, [], {
155
+ stdio: [
156
+ "pipe",
157
+ "inherit",
158
+ "inherit"
159
+ ],
160
+ shell,
161
+ env: childEnv
162
+ });
163
+ } catch {
164
+ return noOpHandle(stdout);
165
+ }
166
+ const stdin = child.stdin;
167
+ if (stdin === null) return noOpHandle(stdout);
168
+ stdin.on("error", (err) => {
169
+ if (err.code !== "EPIPE") throw err;
170
+ });
171
+ const exitPromise = new Promise((resolve) => {
172
+ const settle = (code) => {
173
+ resolve(code ?? 0);
174
+ };
175
+ child.once("exit", (code) => {
176
+ settle(code);
177
+ });
178
+ child.once("error", () => {
179
+ settle(127);
180
+ });
181
+ });
182
+ return {
183
+ out: stdin,
184
+ spawned: true,
185
+ close: () => {
186
+ if (!stdin.writableEnded) try {
187
+ stdin.end();
188
+ } catch (err) {
189
+ if (err.code !== "EPIPE") throw err;
190
+ }
191
+ return exitPromise;
192
+ }
193
+ };
267
194
  };
195
+ //#endregion
196
+ export { isPagerNeeded, openPager, shouldPage };