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,437 +1,262 @@
1
- /**
2
- * psql conditional backslash commands: `\if`, `\elif`, `\else`, `\endif`.
3
- *
4
- * TypeScript port of `exec_command_if`/`exec_command_elif`/`exec_command_else`/
5
- * `exec_command_endif` in `src/bin/psql/command.c`, plus the `ConditionalStack`
6
- * machinery from `src/fe_utils/conditional.c`.
7
- *
8
- * Semantics (mirroring upstream exactly):
9
- *
10
- * - `\if <expr>`
11
- * • If outer branch is active, push a new frame whose state is `TRUE` or
12
- * `FALSE` depending on the parsed expression value.
13
- * • Otherwise push `IGNORED`: every nested branch is suppressed regardless
14
- * of expression. This is how upstream achieves transitive suppression
15
- * without `conditional_active()` itself being transitive.
16
- * - `\elif <expr>`
17
- * • Top is `TRUE` → branch already taken; skip rest until `\endif`
18
- * (poke to `IGNORED`).
19
- * • Top is `FALSE` → first true branch wins; evaluate expression.
20
- * • Top is `IGNORED` → leave untouched, ignore expression.
21
- * • Top is `ELSE_*` → error: `\elif: cannot occur after \else`.
22
- * • Top is `NONE` (empty) → error: `\elif: no matching \if`.
23
- * - `\else`
24
- * • Top is `TRUE` → poke `ELSE_FALSE`.
25
- * • Top is `FALSE` → poke `ELSE_TRUE`.
26
- * • Top is `IGNORED` → poke `ELSE_FALSE` (still suppressed).
27
- * • Top is `ELSE_*` → error: `\else: cannot occur after \else`.
28
- * • Top is `NONE` → error: `\else: no matching \if`.
29
- * - `\endif`
30
- * • Pop the top frame.
31
- * • Top was `NONE` → error: `\endif: no matching \if`.
32
- *
33
- * Expression evaluation: upstream `read_boolean_expression` reads tokens
34
- * with `OT_NORMAL` (which expands `:vars` and backticks) and concatenates
35
- * them with single spaces; the assembled string is passed through
36
- * `ParseVariableBool`. We mirror that pipeline in {@link collectExpr} +
37
- * {@link parseBool}: collect every `nextArg('normal')` token, join with
38
- * spaces, parse. Unrecognised tokens emit the upstream
39
- * `unrecognized value "<tok>" for "\<cmd> expression": Boolean expected`
40
- * diagnostic and evaluate to false.
41
- *
42
- * Inactive branches: when the surrounding scope is suppressed, upstream
43
- * `ignore_boolean_expression` drops the argument tokens WITHOUT running
44
- * `:var` / backtick expansion (regress psql.sql ~line 1028 covers this).
45
- * We achieve the same by NOT calling `nextArg` at all — the
46
- * BackslashContext factory only invokes the slash scanner lazily on the
47
- * first `nextArg` request, so leaving the args queue untouched is
48
- * equivalent to upstream's "discard without expansion".
49
- *
50
- * Diagnostic format: cond commands emit their errors BARE (no
51
- * `psql: ERROR:` prefix). This mirrors `expected/psql.out` (e.g.
52
- * `\endif: no matching \if` on a single line). We use `writeErr` directly
53
- * and return `errorWritten: true` so the mainloop's `psql: ERROR:` fallback
54
- * is suppressed.
55
- *
56
- * Note on the "transitive suppression" requirement: upstream's
57
- * `conditional_active()` only inspects the top frame, but the resulting state
58
- * machine *is* transitive because `\if` inside an inactive outer always pushes
59
- * `IGNORED`, and `IGNORED` never transitions to `TRUE`. So `isActive()`
60
- * inspecting just the top frame produces the right answer.
61
- */
62
1
  import { writeErr } from "./shared.js";
63
- // ---------------------------------------------------------------------------
64
- // Conditional stack
65
- // ---------------------------------------------------------------------------
66
- const INACTIVE_STATES = ["false", "else-false", "ignored"];
2
+ //#region src/psql/command/cmd_cond.ts
3
+ const INACTIVE_STATES = [
4
+ "false",
5
+ "else-false",
6
+ "ignored"
7
+ ];
67
8
  /**
68
- * Build an empty {@link CondStack}.
69
- *
70
- * Frames are stored in an array; `top()` is the last element. `branchTaken`
71
- * records whether a `TRUE`/`ELSE_TRUE` branch has been seen at this level —
72
- * mainloop and the elif/else commands use it implicitly via the state-machine
73
- * transitions described above (we don't expose it through the public API, but
74
- * it's part of the frame shape declared in `types/repl.ts`).
75
- */
76
- export const createCondStack = () => {
77
- const frames = [];
78
- const branchTakenForInitial = (state) => state === "true" || state === "else-true";
79
- return {
80
- push(state, savedQueryBufLen = 0) {
81
- frames.push({
82
- state,
83
- branchTaken: branchTakenForInitial(state),
84
- savedQueryBufLen,
85
- });
86
- },
87
- pop() {
88
- return frames.pop();
89
- },
90
- top() {
91
- return frames.length === 0 ? undefined : frames[frames.length - 1];
92
- },
93
- isActive() {
94
- // Upstream `conditional_active()`: top is NONE/TRUE/ELSE_TRUE → active.
95
- // The transitive suppression is encoded by `\if` pushing `IGNORED` when
96
- // its surrounding context is inactive — see cmdIf below.
97
- if (frames.length === 0)
98
- return true;
99
- return !INACTIVE_STATES.includes(frames[frames.length - 1].state);
100
- },
101
- setState(state) {
102
- if (frames.length === 0)
103
- return;
104
- const top = frames[frames.length - 1];
105
- top.state = state;
106
- if (state === "true" || state === "else-true")
107
- top.branchTaken = true;
108
- },
109
- setSavedQueryBufLen(len) {
110
- if (frames.length === 0)
111
- return;
112
- frames[frames.length - 1].savedQueryBufLen = len;
113
- },
114
- depth() {
115
- return frames.length;
116
- },
117
- };
9
+ * Build an empty {@link CondStack}.
10
+ *
11
+ * Frames are stored in an array; `top()` is the last element. `branchTaken`
12
+ * records whether a `TRUE`/`ELSE_TRUE` branch has been seen at this level —
13
+ * mainloop and the elif/else commands use it implicitly via the state-machine
14
+ * transitions described above (we don't expose it through the public API, but
15
+ * it's part of the frame shape declared in `types/repl.ts`).
16
+ */
17
+ const createCondStack = () => {
18
+ const frames = [];
19
+ const branchTakenForInitial = (state) => state === "true" || state === "else-true";
20
+ return {
21
+ push(state, savedQueryBufLen = 0) {
22
+ frames.push({
23
+ state,
24
+ branchTaken: branchTakenForInitial(state),
25
+ savedQueryBufLen
26
+ });
27
+ },
28
+ pop() {
29
+ return frames.pop();
30
+ },
31
+ top() {
32
+ return frames.length === 0 ? void 0 : frames[frames.length - 1];
33
+ },
34
+ isActive() {
35
+ if (frames.length === 0) return true;
36
+ return !INACTIVE_STATES.includes(frames[frames.length - 1].state);
37
+ },
38
+ setState(state) {
39
+ if (frames.length === 0) return;
40
+ const top = frames[frames.length - 1];
41
+ top.state = state;
42
+ if (state === "true" || state === "else-true") top.branchTaken = true;
43
+ },
44
+ setSavedQueryBufLen(len) {
45
+ if (frames.length === 0) return;
46
+ frames[frames.length - 1].savedQueryBufLen = len;
47
+ },
48
+ depth() {
49
+ return frames.length;
50
+ }
51
+ };
118
52
  };
119
- // ---------------------------------------------------------------------------
120
- // ParseVariableBool — mirrors variables.c::ParseVariableBool.
121
- //
122
- // We re-implement it here (rather than importing from core/variables.ts) so
123
- // the slash-cmd modules stay decoupled from the var-store implementation;
124
- // they just need a value-parser. Recognised forms are case-insensitive with
125
- // unique-prefix matching for word forms:
126
- //
127
- // true / false / yes / no (unique-prefix accepted)
128
- // on / off (need at least 2 chars; bare 'o' ambiguous)
129
- // 1 / 0 (literal)
130
- //
131
- // Anything else is an error (upstream prints a warning and returns false). We
132
- // follow upstream by treating unrecognised tokens as `false` while pushing
133
- // the frame.
134
- // ---------------------------------------------------------------------------
135
- const isPrefixOf = (value, prefix) => value.length > 0 &&
136
- value.length <= prefix.length &&
137
- prefix.slice(0, value.length).toLowerCase() === value.toLowerCase();
53
+ const isPrefixOf = (value, prefix) => value.length > 0 && value.length <= prefix.length && prefix.slice(0, value.length).toLowerCase() === value.toLowerCase();
138
54
  /** Returns the parsed boolean, or `null` if the string was not recognised. */
139
- export const parseBool = (value) => {
140
- if (value.length === 0)
141
- return null;
142
- if (isPrefixOf(value, "true"))
143
- return true;
144
- if (isPrefixOf(value, "false"))
145
- return false;
146
- if (isPrefixOf(value, "yes"))
147
- return true;
148
- if (isPrefixOf(value, "no"))
149
- return false;
150
- if (value.length >= 2) {
151
- const lower = value.toLowerCase();
152
- if ("on".startsWith(lower))
153
- return true;
154
- if ("off".startsWith(lower))
155
- return false;
156
- }
157
- if (value === "1")
158
- return true;
159
- if (value === "0")
160
- return false;
161
- return null;
55
+ const parseBool = (value) => {
56
+ if (value.length === 0) return null;
57
+ if (isPrefixOf(value, "true")) return true;
58
+ if (isPrefixOf(value, "false")) return false;
59
+ if (isPrefixOf(value, "yes")) return true;
60
+ if (isPrefixOf(value, "no")) return false;
61
+ if (value.length >= 2) {
62
+ const lower = value.toLowerCase();
63
+ if ("on".startsWith(lower)) return true;
64
+ if ("off".startsWith(lower)) return false;
65
+ }
66
+ if (value === "1") return true;
67
+ if (value === "0") return false;
68
+ return null;
162
69
  };
163
- // ---------------------------------------------------------------------------
164
- // Backslash command implementations
165
- // ---------------------------------------------------------------------------
166
70
  /**
167
- * Read every remaining `'normal'`-mode argument off the BackslashContext and
168
- * concatenate them with single spaces. Mirrors upstream
169
- * `read_boolean_expression`, which calls `psql_scan_slash_option(OT_NORMAL)`
170
- * in a loop and joins tokens with `" "`. Returns the empty string when no
171
- * args follow the command name — upstream's "missing expression" path
172
- * surfaces the same `unrecognized value ""...` diagnostic as any other
173
- * unparseable token, then evaluates to false.
174
- *
175
- * After joining, we resolve the `:{?varname}` "defined-variable" substitution
176
- * form. Upstream's slash lexer recognises `:{?name}` directly and emits
177
- * `TRUE` / `FALSE` depending on whether the named variable is currently
178
- * set; we re-create that pass here because the underlying scanner only
179
- * handles the plain `:name`, `:'name'`, `:"name"` forms (the `:{?name}`
180
- * form lives in a separate `xslashdefined` flex rule upstream). Doing it
181
- * after the join keeps the rule local to the conditional-expression
182
- * pipeline — every other call site of the slash scanner has its own
183
- * variable-expansion needs that we don't want to disturb.
184
- */
71
+ * Read every remaining `'normal'`-mode argument off the BackslashContext and
72
+ * concatenate them with single spaces. Mirrors upstream
73
+ * `read_boolean_expression`, which calls `psql_scan_slash_option(OT_NORMAL)`
74
+ * in a loop and joins tokens with `" "`. Returns the empty string when no
75
+ * args follow the command name — upstream's "missing expression" path
76
+ * surfaces the same `unrecognized value ""...` diagnostic as any other
77
+ * unparseable token, then evaluates to false.
78
+ *
79
+ * After joining, we resolve the `:{?varname}` "defined-variable" substitution
80
+ * form. Upstream's slash lexer recognises `:{?name}` directly and emits
81
+ * `TRUE` / `FALSE` depending on whether the named variable is currently
82
+ * set; we re-create that pass here because the underlying scanner only
83
+ * handles the plain `:name`, `:'name'`, `:"name"` forms (the `:{?name}`
84
+ * form lives in a separate `xslashdefined` flex rule upstream). Doing it
85
+ * after the join keeps the rule local to the conditional-expression
86
+ * pipeline — every other call site of the slash scanner has its own
87
+ * variable-expansion needs that we don't want to disturb.
88
+ */
185
89
  const DEFINED_VAR_RE = /:\{\?([A-Za-z_][A-Za-z0-9_]*)\}/g;
186
90
  const expandDefinedVar = (text, isDefined) => text.replace(DEFINED_VAR_RE, (_match, name) => isDefined(name) ? "TRUE" : "FALSE");
187
91
  const collectExpr = (ctx) => {
188
- const parts = [];
189
- for (;;) {
190
- const arg = ctx.nextArg("normal");
191
- if (arg === null)
192
- break;
193
- parts.push(arg);
194
- }
195
- return expandDefinedVar(parts.join(" "), (name) => ctx.settings.vars.has(name));
196
- };
197
- /**
198
- * Marker call indicating "discard the expression without evaluating it".
199
- * Mirrors upstream `ignore_boolean_expression` — when we're already inside
200
- * an inactive branch, `\if` / `\elif` arguments are dropped without
201
- * expanding `:vars` or running backticks. We achieve this by simply NOT
202
- * calling `nextArg`: the BackslashContext factory in `mainloop.ts` only
203
- * invokes the slash scanner lazily on the first arg request, so leaving
204
- * the queue untouched skips all expansion. The unconsumed `rawArgs` are
205
- * dropped after the cmd returns.
206
- *
207
- * Kept as a named no-op so call sites read intent-fully ("dropExpr") and
208
- * future refactors can replace the body without touching every caller.
209
- */
210
- const dropExpr = () => {
211
- // Intentionally empty — see the doc comment.
92
+ const parts = [];
93
+ for (;;) {
94
+ const arg = ctx.nextArg("normal");
95
+ if (arg === null) break;
96
+ parts.push(arg);
97
+ }
98
+ return expandDefinedVar(parts.join(" "), (name) => ctx.settings.vars.has(name));
212
99
  };
213
100
  /**
214
- * Evaluate the joined expression against {@link parseBool}. Unrecognised
215
- * tokens surface `unrecognized value "<tok>" for "\<cmd> expression":
216
- * Boolean expected` to stderr (bare, no `psql: ERROR:` prefix — upstream
217
- * `psql_error` shape) and evaluate to false. The caller is responsible for
218
- * setting the stack state to `'false'` on this path. `cmdName` is the
219
- * caller's command identifier (`'if'` / `'elif'`) so the diagnostic matches
220
- * upstream verbatim.
221
- */
101
+ * Evaluate the joined expression against {@link parseBool}. Unrecognised
102
+ * tokens surface `unrecognized value "<tok>" for "\<cmd> expression":
103
+ * Boolean expected` to stderr (bare, no `psql: ERROR:` prefix — upstream
104
+ * `psql_error` shape) and evaluate to false. The caller is responsible for
105
+ * setting the stack state to `'false'` on this path. `cmdName` is the
106
+ * caller's command identifier (`'if'` / `'elif'`) so the diagnostic matches
107
+ * upstream verbatim.
108
+ */
222
109
  const evalExpr = (ctx, cmdName) => {
223
- const raw = collectExpr(ctx);
224
- const parsed = parseBool(raw);
225
- if (parsed === null) {
226
- const message = `unrecognized value "${raw}" for "\\${cmdName} expression": Boolean expected`;
227
- ctx.settings.lastErrorResult = { message };
228
- writeErr(`${message}\n`);
229
- return false;
230
- }
231
- return parsed;
110
+ const raw = collectExpr(ctx);
111
+ const parsed = parseBool(raw);
112
+ if (parsed === null) {
113
+ const message = `unrecognized value "${raw}" for "\\${cmdName} expression": Boolean expected`;
114
+ ctx.settings.lastErrorResult = { message };
115
+ writeErr(`${message}\n`);
116
+ return false;
117
+ }
118
+ return parsed;
232
119
  };
233
120
  /**
234
- * Marker symbol on BackslashContext.cmdName so the mainloop can recognise the
235
- * cond commands without an interface-pollution argument. We instead attach the
236
- * CondStack via a well-known field on the `settings` object — see {@link
237
- * attachCondStack} / {@link getCondStack}.
238
- *
239
- * The mainloop is the sole owner of the CondStack, and it threads it onto the
240
- * BackslashContext via this helper pair so command modules don't have to know
241
- * about REPLContext.
242
- */
121
+ * Marker symbol on BackslashContext.cmdName so the mainloop can recognise the
122
+ * cond commands without an interface-pollution argument. We instead attach the
123
+ * CondStack via a well-known field on the `settings` object — see {@link
124
+ * attachCondStack} / {@link getCondStack}.
125
+ *
126
+ * The mainloop is the sole owner of the CondStack, and it threads it onto the
127
+ * BackslashContext via this helper pair so command modules don't have to know
128
+ * about REPLContext.
129
+ */
243
130
  const COND_STACK_KEY = Symbol.for("neonctl.psql.condStack");
244
- export const attachCondStack = (ctx, cond) => {
245
- const settings = ctx.settings;
246
- settings[COND_STACK_KEY] = cond;
131
+ const attachCondStack = (ctx, cond) => {
132
+ const settings = ctx.settings;
133
+ settings[COND_STACK_KEY] = cond;
247
134
  };
248
- export const getCondStack = (ctx) => {
249
- const settings = ctx.settings;
250
- const stack = settings[COND_STACK_KEY];
251
- if (stack === undefined) {
252
- throw new Error("cond stack not attached; cmd_cond commands must be dispatched via runMainLoop");
253
- }
254
- return stack;
135
+ const getCondStack = (ctx) => {
136
+ const stack = ctx.settings[COND_STACK_KEY];
137
+ if (stack === void 0) throw new Error("cond stack not attached; cmd_cond commands must be dispatched via runMainLoop");
138
+ return stack;
255
139
  };
256
140
  const errResult = (ctx, message) => {
257
- ctx.settings.lastErrorResult = { message };
258
- // Upstream emits cond diagnostics bare via `psql_error("%s\n", ...)`: no
259
- // `psql: ERROR:` prefix, no `\<cmd>:` prefix on top of the message (the
260
- // message already includes it). We mirror that exactly so the regress
261
- // expected output (`\elif: cannot occur after \else`) matches verbatim.
262
- // The `errorWritten` flag tells the mainloop not to add its own
263
- // `psql: ERROR: <msg>` fallback line.
264
- writeErr(`${message}\n`);
265
- return { status: "error", errorWritten: true };
141
+ ctx.settings.lastErrorResult = { message };
142
+ writeErr(`${message}\n`);
143
+ return {
144
+ status: "error",
145
+ errorWritten: true
146
+ };
266
147
  };
267
148
  const okResult = () => ({ status: "ok" });
268
149
  /**
269
- * Build an `{ status: 'ok' }` result that also asks the mainloop to truncate
270
- * `queryBuf` back to `len`. Mirrors upstream `discard_query_text` — called
271
- * by `\elif` / `\else` / `\endif` when the just-completed branch was
272
- * INACTIVE, so the SQL text the skipped branch accumulated doesn't bleed
273
- * into the surrounding statement.
274
- */
150
+ * Build an `{ status: 'ok' }` result that also asks the mainloop to truncate
151
+ * `queryBuf` back to `len`. Mirrors upstream `discard_query_text` — called
152
+ * by `\elif` / `\else` / `\endif` when the just-completed branch was
153
+ * INACTIVE, so the SQL text the skipped branch accumulated doesn't bleed
154
+ * into the surrounding statement.
155
+ */
275
156
  const truncResult = (len) => ({
276
- status: "ok",
277
- truncateBufTo: len,
157
+ status: "ok",
158
+ truncateBufTo: len
278
159
  });
279
160
  /**
280
- * `true` when {@link IfState} corresponds to a branch that was skipping
281
- * statements. Upstream `conditional_active` returns false for these. Used
282
- * by the elif/else/endif commands to decide whether the
283
- * `discard_query_text` step should fire on transition out of the branch.
284
- */
161
+ * `true` when {@link IfState} corresponds to a branch that was skipping
162
+ * statements. Upstream `conditional_active` returns false for these. Used
163
+ * by the elif/else/endif commands to decide whether the
164
+ * `discard_query_text` step should fire on transition out of the branch.
165
+ */
285
166
  const isInactiveState = (state) => state === "false" || state === "else-false" || state === "ignored";
286
- export const cmdIf = {
287
- name: "if",
288
- argMode: "lex",
289
- async run(ctx) {
290
- const cond = getCondStack(ctx);
291
- // Save the queryBuf state at the point the `\if` was encountered. The
292
- // mainloop dispatches the cond command AFTER folding any preceding
293
- // text into `queryBuf` (e.g. the "select" in `select \if true 42 ...`),
294
- // so `ctx.queryBuf.length` is exactly upstream `save_query_text_state`'s
295
- // snapshot. Inactive branches will accumulate scan-time SQL we'll later
296
- // roll back via `discard_query_text` (the truncate-to-saved step in the
297
- // matching `\elif`/`\else`/`\endif`).
298
- const savedLen = ctx.queryBuf.length;
299
- if (!cond.isActive()) {
300
- // Suppressed by outer: push IGNORED and drop the expression WITHOUT
301
- // expanding it. Upstream `ignore_boolean_expression` calls the lexer
302
- // with backticks/vars disabled (psql.sql:1028 covers this with
303
- // `\if false { \if \`nosuchcommand\` ... }`).
304
- dropExpr();
305
- cond.push("ignored", savedLen);
306
- return Promise.resolve(okResult());
307
- }
308
- const truthy = evalExpr(ctx, "if");
309
- cond.push(truthy ? "true" : "false", savedLen);
310
- return Promise.resolve(okResult());
311
- },
167
+ const cmdIf = {
168
+ name: "if",
169
+ argMode: "lex",
170
+ async run(ctx) {
171
+ const cond = getCondStack(ctx);
172
+ const savedLen = ctx.queryBuf.length;
173
+ if (!cond.isActive()) {
174
+ cond.push("ignored", savedLen);
175
+ return Promise.resolve(okResult());
176
+ }
177
+ const truthy = evalExpr(ctx, "if");
178
+ cond.push(truthy ? "true" : "false", savedLen);
179
+ return Promise.resolve(okResult());
180
+ }
312
181
  };
313
- export const cmdElif = {
314
- name: "elif",
315
- argMode: "lex",
316
- async run(ctx) {
317
- const cond = getCondStack(ctx);
318
- const top = cond.top();
319
- if (top === undefined) {
320
- dropExpr();
321
- return Promise.resolve(errResult(ctx, "\\elif: no matching \\if"));
322
- }
323
- // If the branch we're leaving was INACTIVE, anything its body added to
324
- // `queryBuf` is scan-time accumulation we don't want — discard back to
325
- // the snapshot captured at the matching `\if`/`\elif`. The truncate is
326
- // applied by the mainloop; we just report the target length.
327
- const wasInactive = isInactiveState(top.state);
328
- const savedLen = top.savedQueryBufLen;
329
- switch (top.state) {
330
- case "true": {
331
- // Branch already taken — flip to IGNORED. Drop the expression
332
- // without expansion (regress suite: `\if true \elif \`bad\` ...`).
333
- dropExpr();
334
- cond.setState("ignored");
335
- // Active branch — keep buffer text. Re-anchor savedQueryBufLen
336
- // to the start of the new (ignored) branch so a later `\else`/
337
- // `\endif` discards just this branch's additions.
338
- cond.setSavedQueryBufLen(ctx.queryBuf.length);
339
- return Promise.resolve(okResult());
340
- }
341
- case "false": {
342
- // Have not yet found a true branch — evaluate this one.
343
- // evalExpr emits its own `unrecognized value` diagnostic on failure
344
- // and falls through to false, mirroring upstream.
345
- const truthy = evalExpr(ctx, "elif");
346
- cond.setState(truthy ? "true" : "false");
347
- // Apply the discard (was INACTIVE) and re-anchor at the rolled-back
348
- // length so the new branch's bookkeeping starts fresh.
349
- cond.setSavedQueryBufLen(savedLen);
350
- return wasInactive
351
- ? Promise.resolve(truncResult(savedLen))
352
- : Promise.resolve(okResult());
353
- }
354
- case "ignored": {
355
- // Outer is suppressed — stay ignored, drop args without expanding.
356
- // Still discard accumulated buffer text and re-anchor.
357
- dropExpr();
358
- cond.setSavedQueryBufLen(savedLen);
359
- return wasInactive
360
- ? Promise.resolve(truncResult(savedLen))
361
- : Promise.resolve(okResult());
362
- }
363
- case "else-true":
364
- case "else-false":
365
- dropExpr();
366
- return Promise.resolve(errResult(ctx, "\\elif: cannot occur after \\else"));
367
- case "none":
368
- dropExpr();
369
- return Promise.resolve(errResult(ctx, "\\elif: no matching \\if"));
370
- }
371
- },
182
+ const cmdElif = {
183
+ name: "elif",
184
+ argMode: "lex",
185
+ async run(ctx) {
186
+ const cond = getCondStack(ctx);
187
+ const top = cond.top();
188
+ if (top === void 0) return Promise.resolve(errResult(ctx, "\\elif: no matching \\if"));
189
+ const wasInactive = isInactiveState(top.state);
190
+ const savedLen = top.savedQueryBufLen;
191
+ switch (top.state) {
192
+ case "true":
193
+ cond.setState("ignored");
194
+ cond.setSavedQueryBufLen(ctx.queryBuf.length);
195
+ return Promise.resolve(okResult());
196
+ case "false": {
197
+ const truthy = evalExpr(ctx, "elif");
198
+ cond.setState(truthy ? "true" : "false");
199
+ cond.setSavedQueryBufLen(savedLen);
200
+ return wasInactive ? Promise.resolve(truncResult(savedLen)) : Promise.resolve(okResult());
201
+ }
202
+ case "ignored":
203
+ cond.setSavedQueryBufLen(savedLen);
204
+ return wasInactive ? Promise.resolve(truncResult(savedLen)) : Promise.resolve(okResult());
205
+ case "else-true":
206
+ case "else-false": return Promise.resolve(errResult(ctx, "\\elif: cannot occur after \\else"));
207
+ case "none": return Promise.resolve(errResult(ctx, "\\elif: no matching \\if"));
208
+ }
209
+ }
372
210
  };
373
- export const cmdElse = {
374
- name: "else",
375
- argMode: "lex",
376
- async run(ctx) {
377
- const cond = getCondStack(ctx);
378
- const top = cond.top();
379
- if (top === undefined) {
380
- return Promise.resolve(errResult(ctx, "\\else: no matching \\if"));
381
- }
382
- const wasInactive = isInactiveState(top.state);
383
- const savedLen = top.savedQueryBufLen;
384
- switch (top.state) {
385
- case "true":
386
- cond.setState("else-false");
387
- // Was ACTIVE — keep buffer text. Re-anchor at the current length so
388
- // the upcoming else-false branch's additions can be discarded by
389
- // the matching `\endif`.
390
- cond.setSavedQueryBufLen(ctx.queryBuf.length);
391
- return Promise.resolve(okResult());
392
- case "false":
393
- cond.setState("else-true");
394
- cond.setSavedQueryBufLen(savedLen);
395
- return wasInactive
396
- ? Promise.resolve(truncResult(savedLen))
397
- : Promise.resolve(okResult());
398
- case "ignored":
399
- cond.setState("else-false");
400
- cond.setSavedQueryBufLen(savedLen);
401
- return wasInactive
402
- ? Promise.resolve(truncResult(savedLen))
403
- : Promise.resolve(okResult());
404
- case "else-true":
405
- case "else-false":
406
- return Promise.resolve(errResult(ctx, "\\else: cannot occur after \\else"));
407
- case "none":
408
- return Promise.resolve(errResult(ctx, "\\else: no matching \\if"));
409
- }
410
- },
211
+ const cmdElse = {
212
+ name: "else",
213
+ argMode: "lex",
214
+ async run(ctx) {
215
+ const cond = getCondStack(ctx);
216
+ const top = cond.top();
217
+ if (top === void 0) return Promise.resolve(errResult(ctx, "\\else: no matching \\if"));
218
+ const wasInactive = isInactiveState(top.state);
219
+ const savedLen = top.savedQueryBufLen;
220
+ switch (top.state) {
221
+ case "true":
222
+ cond.setState("else-false");
223
+ cond.setSavedQueryBufLen(ctx.queryBuf.length);
224
+ return Promise.resolve(okResult());
225
+ case "false":
226
+ cond.setState("else-true");
227
+ cond.setSavedQueryBufLen(savedLen);
228
+ return wasInactive ? Promise.resolve(truncResult(savedLen)) : Promise.resolve(okResult());
229
+ case "ignored":
230
+ cond.setState("else-false");
231
+ cond.setSavedQueryBufLen(savedLen);
232
+ return wasInactive ? Promise.resolve(truncResult(savedLen)) : Promise.resolve(okResult());
233
+ case "else-true":
234
+ case "else-false": return Promise.resolve(errResult(ctx, "\\else: cannot occur after \\else"));
235
+ case "none": return Promise.resolve(errResult(ctx, "\\else: no matching \\if"));
236
+ }
237
+ }
411
238
  };
412
- export const cmdEndif = {
413
- name: "endif",
414
- argMode: "lex",
415
- async run(ctx) {
416
- const cond = getCondStack(ctx);
417
- const top = cond.top();
418
- if (top === undefined) {
419
- return Promise.resolve(errResult(ctx, "\\endif: no matching \\if"));
420
- }
421
- const wasInactive = isInactiveState(top.state);
422
- const savedLen = top.savedQueryBufLen;
423
- cond.pop();
424
- return wasInactive
425
- ? Promise.resolve(truncResult(savedLen))
426
- : Promise.resolve(okResult());
427
- },
239
+ const cmdEndif = {
240
+ name: "endif",
241
+ argMode: "lex",
242
+ async run(ctx) {
243
+ const cond = getCondStack(ctx);
244
+ const top = cond.top();
245
+ if (top === void 0) return Promise.resolve(errResult(ctx, "\\endif: no matching \\if"));
246
+ const wasInactive = isInactiveState(top.state);
247
+ const savedLen = top.savedQueryBufLen;
248
+ cond.pop();
249
+ return wasInactive ? Promise.resolve(truncResult(savedLen)) : Promise.resolve(okResult());
250
+ }
428
251
  };
429
252
  /** Names of the conditional commands — the mainloop dispatches these
430
- * unconditionally (i.e. ignoring `cond.isActive()`) so an `\if false` block
431
- * can still be closed by `\endif`. */
432
- export const COND_COMMAND_NAMES = new Set([
433
- "if",
434
- "elif",
435
- "else",
436
- "endif",
253
+ * unconditionally (i.e. ignoring `cond.isActive()`) so an `\if false` block
254
+ * can still be closed by `\endif`. */
255
+ const COND_COMMAND_NAMES = /* @__PURE__ */ new Set([
256
+ "if",
257
+ "elif",
258
+ "else",
259
+ "endif"
437
260
  ]);
261
+ //#endregion
262
+ export { COND_COMMAND_NAMES, attachCondStack, cmdElif, cmdElse, cmdEndif, cmdIf, createCondStack, getCondStack, parseBool };