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,343 +1,278 @@
1
- /**
2
- * Backslash command dispatch.
3
- *
4
- * TypeScript port of the top half of PostgreSQL's `src/bin/psql/command.c`:
5
- * specifically the `exec_command()` entry point that, given a parsed slash
6
- * command name, looks up the right handler and runs it with a small per-call
7
- * context (`BackslashContext`).
8
- *
9
- * The upstream is a giant switch statement keyed off the first one-or-two
10
- * letters of the command. We replace that with a registry of typed
11
- * `BackslashCmdSpec` records keyed by primary name with a separate alias map.
12
- * Commands are added by `register()` at construction time; the default
13
- * registry returned by {@link defaultRegistry} is pre-populated with every
14
- * command implemented in this WP (meta + format). Later WPs (I/O, connection,
15
- * describe, large object, pipeline, misc) add their own commands by calling
16
- * `registry.register(...)` on top.
17
- *
18
- * The `BackslashContext` carries:
19
- *
20
- * - the parsed command name (without the leading backslash),
21
- * - the raw post-name remainder of the input line (`rawArgs`),
22
- * - the current SQL query buffer (`queryBuf`), and
23
- * - a small `nextArg(mode)` / `restOfLine()` pair backed by
24
- * `scanSlashArgs()` from the WP-07 scanner. Each call to `nextArg` returns
25
- * the next lexed argument under the requested {@link SlashArgMode}, or
26
- * `null` once the buffer is exhausted. Mixing modes across calls is
27
- * supported: each call rescans the tail starting at the current cursor.
28
- *
29
- * Variable substitution: the scanner is given a `varLookup` callback that
30
- * delegates to `settings.vars`. Modes that disable substitution (`no-vars`)
31
- * naturally fall through to the scanner's existing behaviour.
32
- */
1
+ import { writeErr } from "./shared.js";
33
2
  import { scanSlashArgs } from "../scanner/slash.js";
34
3
  import { registerConnectCommands } from "./cmd_connect.js";
4
+ import { cmdCd, cmdCopyright, cmdEcho, cmdEdit, cmdErrverbose, cmdGetenv, cmdHelpSQL, cmdPrompt, cmdQecho, cmdQuit, cmdReset, cmdS, cmdSet, cmdSetenv, cmdShell, cmdSlashHelp, cmdTiming, cmdUnset, cmdWarn } from "./cmd_meta.js";
5
+ import { F as registerIoCommands, g as registerPipelineCommands } from "../../_chunks/cmd_pipeline-CUbBO9U_.js";
6
+ import { cmdA, cmdC, cmdEncoding, cmdF, cmdH, cmdPset, cmdT, cmdTitleAttr, cmdX } from "./cmd_format.js";
35
7
  import { registerCopyCommands } from "./cmd_copy.js";
36
8
  import { registerDescribeCommands } from "./cmd_describe.js";
37
- import { cmdA, cmdC, cmdEncoding, cmdF, cmdH, cmdPset, cmdT, cmdTitleAttr, cmdX, } from "./cmd_format.js";
38
- import { registerIoCommands } from "./cmd_io.js";
39
9
  import { registerLargeObjectCommands } from "./cmd_lo.js";
40
- import { cmdCd, cmdCopyright, cmdEcho, cmdEdit, cmdErrverbose, cmdGetenv, cmdHelpSQL, cmdPrompt, cmdQecho, cmdQuit, cmdReset, cmdS, cmdSet, cmdSetenv, cmdShell, cmdSlashHelp, cmdTiming, cmdUnset, cmdWarn, } from "./cmd_meta.js";
41
10
  import { registerMiscCommands } from "./cmd_misc.js";
42
- import { registerPipelineCommands } from "./cmd_pipeline.js";
43
- import { isCommandRestricted, registerRestrictCommands, wrapRestrictedCommands, } from "./cmd_restrict.js";
11
+ import { isCommandRestricted, registerRestrictCommands, wrapRestrictedCommands } from "./cmd_restrict.js";
44
12
  import { registerShowCommands } from "./cmd_show.js";
45
- import { writeErr } from "./shared.js";
13
+ //#region src/psql/command/dispatch.ts
46
14
  /**
47
- * Concrete `BackslashRegistry`: a primary-name → spec map plus a parallel
48
- * alias → primary-name map so lookups stay O(1).
49
- *
50
- * Re-registering the same primary name overwrites the existing spec; this
51
- * matches the upstream behaviour that doesn't multi-register and gives
52
- * downstream WPs a clean way to override a default if they need to.
53
- */
54
- class Registry {
55
- constructor() {
56
- this.specs = new Map();
57
- this.aliases = new Map();
58
- }
59
- register(spec) {
60
- this.specs.set(spec.name, spec);
61
- if (spec.aliases) {
62
- for (const alias of spec.aliases) {
63
- this.aliases.set(alias, spec.name);
64
- }
65
- }
66
- }
67
- lookup(name) {
68
- const direct = this.specs.get(name);
69
- if (direct)
70
- return direct;
71
- const aliased = this.aliases.get(name);
72
- if (aliased)
73
- return this.specs.get(aliased);
74
- return undefined;
75
- }
76
- all() {
77
- return this.specs.values();
78
- }
79
- }
15
+ * Backslash command dispatch.
16
+ *
17
+ * TypeScript port of the top half of PostgreSQL's `src/bin/psql/command.c`:
18
+ * specifically the `exec_command()` entry point that, given a parsed slash
19
+ * command name, looks up the right handler and runs it with a small per-call
20
+ * context (`BackslashContext`).
21
+ *
22
+ * The upstream is a giant switch statement keyed off the first one-or-two
23
+ * letters of the command. We replace that with a registry of typed
24
+ * `BackslashCmdSpec` records keyed by primary name with a separate alias map.
25
+ * Commands are added by `register()` at construction time; the default
26
+ * registry returned by {@link defaultRegistry} is pre-populated with every
27
+ * command implemented in this WP (meta + format). Later WPs (I/O, connection,
28
+ * describe, large object, pipeline, misc) add their own commands by calling
29
+ * `registry.register(...)` on top.
30
+ *
31
+ * The `BackslashContext` carries:
32
+ *
33
+ * - the parsed command name (without the leading backslash),
34
+ * - the raw post-name remainder of the input line (`rawArgs`),
35
+ * - the current SQL query buffer (`queryBuf`), and
36
+ * - a small `nextArg(mode)` / `restOfLine()` pair backed by
37
+ * `scanSlashArgs()` from the WP-07 scanner. Each call to `nextArg` returns
38
+ * the next lexed argument under the requested {@link SlashArgMode}, or
39
+ * `null` once the buffer is exhausted. Mixing modes across calls is
40
+ * supported: each call rescans the tail starting at the current cursor.
41
+ *
42
+ * Variable substitution: the scanner is given a `varLookup` callback that
43
+ * delegates to `settings.vars`. Modes that disable substitution (`no-vars`)
44
+ * naturally fall through to the scanner's existing behaviour.
45
+ */
46
+ /**
47
+ * Concrete `BackslashRegistry`: a primary-name → spec map plus a parallel
48
+ * alias → primary-name map so lookups stay O(1).
49
+ *
50
+ * Re-registering the same primary name overwrites the existing spec; this
51
+ * matches the upstream behaviour that doesn't multi-register and gives
52
+ * downstream WPs a clean way to override a default if they need to.
53
+ */
54
+ var Registry = class {
55
+ constructor() {
56
+ this.specs = /* @__PURE__ */ new Map();
57
+ this.aliases = /* @__PURE__ */ new Map();
58
+ }
59
+ register(spec) {
60
+ this.specs.set(spec.name, spec);
61
+ if (spec.aliases) for (const alias of spec.aliases) this.aliases.set(alias, spec.name);
62
+ }
63
+ lookup(name) {
64
+ const direct = this.specs.get(name);
65
+ if (direct) return direct;
66
+ const aliased = this.aliases.get(name);
67
+ if (aliased) return this.specs.get(aliased);
68
+ }
69
+ all() {
70
+ return this.specs.values();
71
+ }
72
+ };
80
73
  /** Construct a fresh, empty registry. */
81
- export const createBackslashRegistry = () => new Registry();
74
+ const createBackslashRegistry = () => new Registry();
82
75
  /**
83
- * Build a {@link BackslashContext} from inputs the REPL has on hand at
84
- * dispatch time.
85
- *
86
- * The context's `nextArg(mode)` is built on top of `scanSlashArgs`. We
87
- * maintain a small internal byte cursor that tracks how much of `rawArgs`
88
- * has been consumed so far; each call rescans the remaining tail in the
89
- * requested mode and advances the cursor past the first arg's source
90
- * extent. `restOfLine()` returns the unconsumed tail verbatim (with leading
91
- * whitespace trimmed, matching `whole-line` semantics) and advances the
92
- * cursor to the end.
93
- *
94
- * The tracking is conservative: because `scanSlashArgs` does not directly
95
- * report per-arg source spans, we estimate the consumed span by re-lexing
96
- * with a 1-arg cap in `whole-line` mode to find the boundary. This is an
97
- * over-approximation only when adjacent quoted runs collapse to fewer
98
- * characters in the parsed output — in practice every command in this WP
99
- * either reads args in order or reads the whole tail with `restOfLine()`,
100
- * so the cursor is never observed to lag in the calls we ship.
101
- */
102
- export const makeContext = (opts) => {
103
- let cursor = 0;
104
- const rawArgs = opts.rawArgs;
105
- const varLookup = (name) => opts.settings.vars.get(name);
106
- const nextArg = (mode = "normal") => {
107
- // Find the next non-whitespace byte from the cursor; we use it both to
108
- // know whether anything remains and as the basis for span tracking.
109
- let i = cursor;
110
- while (i < rawArgs.length && /[\s]/.test(rawArgs[i]))
111
- i++;
112
- if (i >= rawArgs.length)
113
- return null;
114
- if (mode === "whole-line") {
115
- const tail = rawArgs.slice(i);
116
- cursor = rawArgs.length;
117
- return tail;
118
- }
119
- // Scan just the tail and pick the first arg. The scanner consumes one
120
- // arg's worth of input; we need to advance `cursor` past it so the next
121
- // call sees the remaining tail. We do that by rescanning the tail again
122
- // with a one-token cap and comparing lengths.
123
- const tail = rawArgs.slice(i);
124
- const args = scanSlashArgs(tail, mode, varLookup);
125
- if (args.length === 0) {
126
- cursor = rawArgs.length;
127
- return null;
128
- }
129
- const first = args[0];
130
- // Compute the consumed span by scanning the original tail in normal
131
- // mode and finding where the second arg would start. We don't have a
132
- // direct API for that, so we walk character-by-character using the
133
- // same termination rules as the scanner.
134
- const span = consumedSpan(tail, mode, varLookup);
135
- cursor = i + span;
136
- return first;
137
- };
138
- const restOfLine = () => {
139
- let i = cursor;
140
- while (i < rawArgs.length && /[\s]/.test(rawArgs[i]))
141
- i++;
142
- const tail = rawArgs.slice(i);
143
- cursor = rawArgs.length;
144
- return tail;
145
- };
146
- return {
147
- settings: opts.settings,
148
- cmdName: opts.cmdName,
149
- queryBuf: opts.queryBuf,
150
- rawArgs,
151
- nextArg,
152
- restOfLine,
153
- };
76
+ * Build a {@link BackslashContext} from inputs the REPL has on hand at
77
+ * dispatch time.
78
+ *
79
+ * The context's `nextArg(mode)` is built on top of `scanSlashArgs`. We
80
+ * maintain a small internal byte cursor that tracks how much of `rawArgs`
81
+ * has been consumed so far; each call rescans the remaining tail in the
82
+ * requested mode and advances the cursor past the first arg's source
83
+ * extent. `restOfLine()` returns the unconsumed tail verbatim (with leading
84
+ * whitespace trimmed, matching `whole-line` semantics) and advances the
85
+ * cursor to the end.
86
+ *
87
+ * The tracking is conservative: because `scanSlashArgs` does not directly
88
+ * report per-arg source spans, we estimate the consumed span by re-lexing
89
+ * with a 1-arg cap in `whole-line` mode to find the boundary. This is an
90
+ * over-approximation only when adjacent quoted runs collapse to fewer
91
+ * characters in the parsed output — in practice every command in this WP
92
+ * either reads args in order or reads the whole tail with `restOfLine()`,
93
+ * so the cursor is never observed to lag in the calls we ship.
94
+ */
95
+ const makeContext = (opts) => {
96
+ let cursor = 0;
97
+ const rawArgs = opts.rawArgs;
98
+ const varLookup = (name) => opts.settings.vars.get(name);
99
+ const nextArg = (mode = "normal") => {
100
+ let i = cursor;
101
+ while (i < rawArgs.length && /[\s]/.test(rawArgs[i])) i++;
102
+ if (i >= rawArgs.length) return null;
103
+ if (mode === "whole-line") {
104
+ const tail = rawArgs.slice(i);
105
+ cursor = rawArgs.length;
106
+ return tail;
107
+ }
108
+ const tail = rawArgs.slice(i);
109
+ const args = scanSlashArgs(tail, mode, varLookup);
110
+ if (args.length === 0) {
111
+ cursor = rawArgs.length;
112
+ return null;
113
+ }
114
+ const first = args[0];
115
+ const span = consumedSpan(tail, mode, varLookup);
116
+ cursor = i + span;
117
+ return first;
118
+ };
119
+ const restOfLine = () => {
120
+ let i = cursor;
121
+ while (i < rawArgs.length && /[\s]/.test(rawArgs[i])) i++;
122
+ const tail = rawArgs.slice(i);
123
+ cursor = rawArgs.length;
124
+ return tail;
125
+ };
126
+ return {
127
+ settings: opts.settings,
128
+ cmdName: opts.cmdName,
129
+ queryBuf: opts.queryBuf,
130
+ rawArgs,
131
+ nextArg,
132
+ restOfLine
133
+ };
154
134
  };
155
135
  /**
156
- * Compute how many bytes of `tail` were consumed lexing the first arg. We
157
- * walk the same quoting/escape rules as the scanner so the cursor advances
158
- * past the *source* extent, not the post-expansion length.
159
- *
160
- * Stops at whitespace or backslash. Quoted runs (`'…'`, `"…"`, `` `…` ``)
161
- * are consumed to their closing delimiter. `:var` substitutions advance
162
- * past the original `:name` form regardless of expansion size.
163
- */
136
+ * Compute how many bytes of `tail` were consumed lexing the first arg. We
137
+ * walk the same quoting/escape rules as the scanner so the cursor advances
138
+ * past the *source* extent, not the post-expansion length.
139
+ *
140
+ * Stops at whitespace or backslash. Quoted runs (`'…'`, `"…"`, `` `…` ``)
141
+ * are consumed to their closing delimiter. `:var` substitutions advance
142
+ * past the original `:name` form regardless of expansion size.
143
+ */
164
144
  const consumedSpan = (tail, mode, varLookup) => {
165
- if (mode === "whole-line")
166
- return tail.length;
167
- let i = 0;
168
- // Skip leading whitespace inside the tail (already trimmed by caller, but
169
- // safe to repeat).
170
- while (i < tail.length && /[\s]/.test(tail[i]))
171
- i++;
172
- // filepipe special: a leading `|` slurps to EOL.
173
- if (mode === "filepipe" && tail[i] === "|")
174
- return tail.length;
175
- while (i < tail.length) {
176
- const c = tail[i];
177
- if (/[\s]/.test(c) || c === "\\")
178
- break;
179
- if (c === "'") {
180
- i++;
181
- while (i < tail.length) {
182
- if (tail[i] === "\\" && i + 1 < tail.length) {
183
- i += 2;
184
- continue;
185
- }
186
- if (tail[i] === "'") {
187
- if (tail[i + 1] === "'") {
188
- i += 2;
189
- continue;
190
- }
191
- i++;
192
- break;
193
- }
194
- i++;
195
- }
196
- continue;
197
- }
198
- if (c === '"') {
199
- i++;
200
- while (i < tail.length && tail[i] !== '"')
201
- i++;
202
- if (i < tail.length)
203
- i++;
204
- continue;
205
- }
206
- if (c === "`") {
207
- i++;
208
- while (i < tail.length && tail[i] !== "`")
209
- i++;
210
- if (i < tail.length)
211
- i++;
212
- continue;
213
- }
214
- if (c === ":" && mode !== "no-vars") {
215
- // :"name" / :'name' / :name — advance past the source form. We don't
216
- // actually call varLookup here; we just measure the lexical span.
217
- void varLookup;
218
- const next = tail[i + 1];
219
- if (next === '"' || next === "'") {
220
- let j = i + 2;
221
- while (j < tail.length && /[A-Za-z0-9_\x80-\xff]/.test(tail[j]))
222
- j++;
223
- if (j > i + 2 && tail[j] === next) {
224
- i = j + 1;
225
- continue;
226
- }
227
- }
228
- if (next && /[A-Za-z0-9_\x80-\xff]/.test(next)) {
229
- let j = i + 1;
230
- while (j < tail.length && /[A-Za-z0-9_\x80-\xff]/.test(tail[j]))
231
- j++;
232
- i = j;
233
- continue;
234
- }
235
- }
236
- i++;
237
- }
238
- return i;
145
+ if (mode === "whole-line") return tail.length;
146
+ let i = 0;
147
+ while (i < tail.length && /[\s]/.test(tail[i])) i++;
148
+ if (mode === "filepipe" && tail[i] === "|") return tail.length;
149
+ while (i < tail.length) {
150
+ const c = tail[i];
151
+ if (/[\s]/.test(c) || c === "\\") break;
152
+ if (c === "'") {
153
+ i++;
154
+ while (i < tail.length) {
155
+ if (tail[i] === "\\" && i + 1 < tail.length) {
156
+ i += 2;
157
+ continue;
158
+ }
159
+ if (tail[i] === "'") {
160
+ if (tail[i + 1] === "'") {
161
+ i += 2;
162
+ continue;
163
+ }
164
+ i++;
165
+ break;
166
+ }
167
+ i++;
168
+ }
169
+ continue;
170
+ }
171
+ if (c === "\"") {
172
+ i++;
173
+ while (i < tail.length && tail[i] !== "\"") i++;
174
+ if (i < tail.length) i++;
175
+ continue;
176
+ }
177
+ if (c === "`") {
178
+ i++;
179
+ while (i < tail.length && tail[i] !== "`") i++;
180
+ if (i < tail.length) i++;
181
+ continue;
182
+ }
183
+ if (c === ":" && mode !== "no-vars") {
184
+ const next = tail[i + 1];
185
+ if (next === "\"" || next === "'") {
186
+ let j = i + 2;
187
+ while (j < tail.length && /[A-Za-z0-9_\x80-\xff]/.test(tail[j])) j++;
188
+ if (j > i + 2 && tail[j] === next) {
189
+ i = j + 1;
190
+ continue;
191
+ }
192
+ }
193
+ if (next && /[A-Za-z0-9_\x80-\xff]/.test(next)) {
194
+ let j = i + 1;
195
+ while (j < tail.length && /[A-Za-z0-9_\x80-\xff]/.test(tail[j])) j++;
196
+ i = j;
197
+ continue;
198
+ }
199
+ }
200
+ i++;
201
+ }
202
+ return i;
239
203
  };
240
204
  /**
241
- * Top-level dispatch entry. Looks the command up by name (and falls back to
242
- * registered aliases), runs it, and returns the result.
243
- *
244
- * Unknown commands return `{ status: 'error' }` so the mainloop can emit
245
- * the upstream-style `"invalid command \…"` diagnostic. We deliberately
246
- * don't print here; the caller owns stderr.
247
- */
248
- export const dispatchBackslash = async (registry, cmdName, ctx) => {
249
- const spec = registry.lookup(cmdName);
250
- if (!spec)
251
- return { status: "error" };
252
- // PG 18: refuse shell/filesystem-touching commands while restricted.
253
- // We check against the resolved *primary* name so aliases like
254
- // `\write` → `w` are caught.
255
- if (isCommandRestricted(ctx.settings, spec.name)) {
256
- writeErr(`\\${cmdName}: command is not allowed in restricted mode; ` +
257
- `use \\unrestrict to leave restricted mode\n`);
258
- return { status: "error" };
259
- }
260
- return spec.run(ctx);
205
+ * Top-level dispatch entry. Looks the command up by name (and falls back to
206
+ * registered aliases), runs it, and returns the result.
207
+ *
208
+ * Unknown commands return `{ status: 'error' }` so the mainloop can emit
209
+ * the upstream-style `"invalid command \…"` diagnostic. We deliberately
210
+ * don't print here; the caller owns stderr.
211
+ */
212
+ const dispatchBackslash = async (registry, cmdName, ctx) => {
213
+ const spec = registry.lookup(cmdName);
214
+ if (!spec) return { status: "error" };
215
+ if (isCommandRestricted(ctx.settings, spec.name)) {
216
+ writeErr(`\\${cmdName}: command is not allowed in restricted mode; use \\unrestrict to leave restricted mode\n`);
217
+ return { status: "error" };
218
+ }
219
+ return spec.run(ctx);
261
220
  };
262
221
  /**
263
- * Return a fresh registry pre-populated with every backslash command this
264
- * WP implements: meta (`\q`, `\r`/`\reset`, `\!`, `\cd`, `\echo`, `\qecho`,
265
- * `\warn`, `\prompt`, `\set`, `\unset`, `\getenv`, `\setenv`, `\errverbose`,
266
- * `\timing`) and format (`\a`, `\C`, `\f`, `\H`, `\t`, `\T`, `\x`,
267
- * `\pset`, `\encoding`).
268
- *
269
- * Other WPs (15/17/20/21/22/23) extend this set by calling `register()` on
270
- * the returned registry — see the plan for the full mapping.
271
- */
272
- export const defaultRegistry = () => {
273
- const r = createBackslashRegistry();
274
- // Meta.
275
- r.register(cmdQuit);
276
- r.register(cmdReset);
277
- r.register(cmdShell);
278
- r.register(cmdCd);
279
- r.register(cmdEcho);
280
- r.register(cmdQecho);
281
- r.register(cmdWarn);
282
- r.register(cmdPrompt);
283
- r.register(cmdSet);
284
- r.register(cmdUnset);
285
- r.register(cmdGetenv);
286
- r.register(cmdSetenv);
287
- r.register(cmdErrverbose);
288
- r.register(cmdTiming);
289
- r.register(cmdCopyright);
290
- r.register(cmdHelpSQL);
291
- // `\?` (backslash-command help), `\e`/`\edit` (edit query buffer in an
292
- // external editor), and `\s` (print/save command history) are full
293
- // implementations living in `cmd_meta.ts`. They previously sat here as
294
- // no-op stubs only so the `\if false ... <cmd> ... \endif` inactive-branch
295
- // enumeration didn't emit spurious "invalid command" diagnostics; now that
296
- // they do real work the inactive-branch guard still skips them (it only
297
- // checks that the name is registered).
298
- r.register(cmdSlashHelp);
299
- r.register(cmdEdit);
300
- r.register(cmdS);
301
- // `\html` is NOT a real psql command (HTML output is the `\H` toggle in
302
- // `cmd_format.ts`) — but it MUST stay registered as a recognized no-op.
303
- // Upstream `psql.sql`'s inactive-branch enumeration test (`\if false …
304
- // \html … \endif`, regress line 1062) requires every backslash name in
305
- // that dump to be recognized: our inactive-branch guard skips a command
306
- // only when its name is registered, and silently emits "invalid command"
307
- // otherwise. An unregistered `\html` therefore breaks the regress diff.
308
- // Upstream skips ALL commands (known or not) in a false branch; until our
309
- // mainloop matches that, the recognized-name stub is the load-bearing
310
- // shim. (In an active branch this makes `\html` a silent no-op rather than
311
- // upstream's "invalid command", but no test exercises that path.)
312
- r.register({
313
- name: "html",
314
- run: () => Promise.resolve({ status: "ok" }),
315
- });
316
- // Format.
317
- r.register(cmdA);
318
- r.register(cmdC);
319
- r.register(cmdF);
320
- r.register(cmdH);
321
- r.register(cmdT);
322
- r.register(cmdTitleAttr);
323
- r.register(cmdX);
324
- r.register(cmdEncoding);
325
- r.register(cmdPset);
326
- // I/O & control (WP-15).
327
- registerIoCommands(r);
328
- registerConnectCommands(r);
329
- registerCopyCommands(r);
330
- registerDescribeCommands(r);
331
- registerPipelineCommands(r);
332
- registerMiscCommands(r);
333
- registerLargeObjectCommands(r);
334
- registerShowCommands(r);
335
- registerRestrictCommands(r);
336
- // Must run after every other `register*` call so the wrappers see the
337
- // final specs for the restricted command names (e.g. `\!`, `\cd`, `\copy`,
338
- // `\setenv`, `\w`). Without this, the REPL mainloop's direct
339
- // `spec.run(ctx)` invocation bypasses the gate that lives in
340
- // `dispatchBackslash`.
341
- wrapRestrictedCommands(r);
342
- return r;
222
+ * Return a fresh registry pre-populated with every backslash command this
223
+ * WP implements: meta (`\q`, `\r`/`\reset`, `\!`, `\cd`, `\echo`, `\qecho`,
224
+ * `\warn`, `\prompt`, `\set`, `\unset`, `\getenv`, `\setenv`, `\errverbose`,
225
+ * `\timing`) and format (`\a`, `\C`, `\f`, `\H`, `\t`, `\T`, `\x`,
226
+ * `\pset`, `\encoding`).
227
+ *
228
+ * Other WPs (15/17/20/21/22/23) extend this set by calling `register()` on
229
+ * the returned registry — see the plan for the full mapping.
230
+ */
231
+ const defaultRegistry = () => {
232
+ const r = createBackslashRegistry();
233
+ r.register(cmdQuit);
234
+ r.register(cmdReset);
235
+ r.register(cmdShell);
236
+ r.register(cmdCd);
237
+ r.register(cmdEcho);
238
+ r.register(cmdQecho);
239
+ r.register(cmdWarn);
240
+ r.register(cmdPrompt);
241
+ r.register(cmdSet);
242
+ r.register(cmdUnset);
243
+ r.register(cmdGetenv);
244
+ r.register(cmdSetenv);
245
+ r.register(cmdErrverbose);
246
+ r.register(cmdTiming);
247
+ r.register(cmdCopyright);
248
+ r.register(cmdHelpSQL);
249
+ r.register(cmdSlashHelp);
250
+ r.register(cmdEdit);
251
+ r.register(cmdS);
252
+ r.register({
253
+ name: "html",
254
+ run: () => Promise.resolve({ status: "ok" })
255
+ });
256
+ r.register(cmdA);
257
+ r.register(cmdC);
258
+ r.register(cmdF);
259
+ r.register(cmdH);
260
+ r.register(cmdT);
261
+ r.register(cmdTitleAttr);
262
+ r.register(cmdX);
263
+ r.register(cmdEncoding);
264
+ r.register(cmdPset);
265
+ registerIoCommands(r);
266
+ registerConnectCommands(r);
267
+ registerCopyCommands(r);
268
+ registerDescribeCommands(r);
269
+ registerPipelineCommands(r);
270
+ registerMiscCommands(r);
271
+ registerLargeObjectCommands(r);
272
+ registerShowCommands(r);
273
+ registerRestrictCommands(r);
274
+ wrapRestrictedCommands(r);
275
+ return r;
343
276
  };
277
+ //#endregion
278
+ export { createBackslashRegistry, defaultRegistry, dispatchBackslash, makeContext };