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,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 };