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,343 +1,279 @@
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 { cmdA, cmdC, cmdEncoding, cmdF, cmdH, cmdPset, cmdT, cmdTitleAttr, cmdX } from "./cmd_format.js";
6
+ import { registerIoCommands } from "./cmd_io.js";
7
+ import { registerPipelineCommands } from "./cmd_pipeline.js";
35
8
  import { registerCopyCommands } from "./cmd_copy.js";
36
9
  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
10
  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
11
  import { registerMiscCommands } from "./cmd_misc.js";
42
- import { registerPipelineCommands } from "./cmd_pipeline.js";
43
- import { isCommandRestricted, registerRestrictCommands, wrapRestrictedCommands, } from "./cmd_restrict.js";
12
+ import { isCommandRestricted, registerRestrictCommands, wrapRestrictedCommands } from "./cmd_restrict.js";
44
13
  import { registerShowCommands } from "./cmd_show.js";
45
- import { writeErr } from "./shared.js";
14
+ //#region src/psql/command/dispatch.ts
46
15
  /**
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
- }
16
+ * Backslash command dispatch.
17
+ *
18
+ * TypeScript port of the top half of PostgreSQL's `src/bin/psql/command.c`:
19
+ * specifically the `exec_command()` entry point that, given a parsed slash
20
+ * command name, looks up the right handler and runs it with a small per-call
21
+ * context (`BackslashContext`).
22
+ *
23
+ * The upstream is a giant switch statement keyed off the first one-or-two
24
+ * letters of the command. We replace that with a registry of typed
25
+ * `BackslashCmdSpec` records keyed by primary name with a separate alias map.
26
+ * Commands are added by `register()` at construction time; the default
27
+ * registry returned by {@link defaultRegistry} is pre-populated with every
28
+ * command implemented in this WP (meta + format). Later WPs (I/O, connection,
29
+ * describe, large object, pipeline, misc) add their own commands by calling
30
+ * `registry.register(...)` on top.
31
+ *
32
+ * The `BackslashContext` carries:
33
+ *
34
+ * - the parsed command name (without the leading backslash),
35
+ * - the raw post-name remainder of the input line (`rawArgs`),
36
+ * - the current SQL query buffer (`queryBuf`), and
37
+ * - a small `nextArg(mode)` / `restOfLine()` pair backed by
38
+ * `scanSlashArgs()` from the WP-07 scanner. Each call to `nextArg` returns
39
+ * the next lexed argument under the requested {@link SlashArgMode}, or
40
+ * `null` once the buffer is exhausted. Mixing modes across calls is
41
+ * supported: each call rescans the tail starting at the current cursor.
42
+ *
43
+ * Variable substitution: the scanner is given a `varLookup` callback that
44
+ * delegates to `settings.vars`. Modes that disable substitution (`no-vars`)
45
+ * naturally fall through to the scanner's existing behaviour.
46
+ */
47
+ /**
48
+ * Concrete `BackslashRegistry`: a primary-name → spec map plus a parallel
49
+ * alias → primary-name map so lookups stay O(1).
50
+ *
51
+ * Re-registering the same primary name overwrites the existing spec; this
52
+ * matches the upstream behaviour that doesn't multi-register and gives
53
+ * downstream WPs a clean way to override a default if they need to.
54
+ */
55
+ var Registry = class {
56
+ constructor() {
57
+ this.specs = /* @__PURE__ */ new Map();
58
+ this.aliases = /* @__PURE__ */ new Map();
59
+ }
60
+ register(spec) {
61
+ this.specs.set(spec.name, spec);
62
+ if (spec.aliases) for (const alias of spec.aliases) this.aliases.set(alias, spec.name);
63
+ }
64
+ lookup(name) {
65
+ const direct = this.specs.get(name);
66
+ if (direct) return direct;
67
+ const aliased = this.aliases.get(name);
68
+ if (aliased) return this.specs.get(aliased);
69
+ }
70
+ all() {
71
+ return this.specs.values();
72
+ }
73
+ };
80
74
  /** Construct a fresh, empty registry. */
81
- export const createBackslashRegistry = () => new Registry();
75
+ const createBackslashRegistry = () => new Registry();
82
76
  /**
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
- };
77
+ * Build a {@link BackslashContext} from inputs the REPL has on hand at
78
+ * dispatch time.
79
+ *
80
+ * The context's `nextArg(mode)` is built on top of `scanSlashArgs`. We
81
+ * maintain a small internal byte cursor that tracks how much of `rawArgs`
82
+ * has been consumed so far; each call rescans the remaining tail in the
83
+ * requested mode and advances the cursor past the first arg's source
84
+ * extent. `restOfLine()` returns the unconsumed tail verbatim (with leading
85
+ * whitespace trimmed, matching `whole-line` semantics) and advances the
86
+ * cursor to the end.
87
+ *
88
+ * The tracking is conservative: because `scanSlashArgs` does not directly
89
+ * report per-arg source spans, we estimate the consumed span by re-lexing
90
+ * with a 1-arg cap in `whole-line` mode to find the boundary. This is an
91
+ * over-approximation only when adjacent quoted runs collapse to fewer
92
+ * characters in the parsed output — in practice every command in this WP
93
+ * either reads args in order or reads the whole tail with `restOfLine()`,
94
+ * so the cursor is never observed to lag in the calls we ship.
95
+ */
96
+ const makeContext = (opts) => {
97
+ let cursor = 0;
98
+ const rawArgs = opts.rawArgs;
99
+ const varLookup = (name) => opts.settings.vars.get(name);
100
+ const nextArg = (mode = "normal") => {
101
+ let i = cursor;
102
+ while (i < rawArgs.length && /[\s]/.test(rawArgs[i])) i++;
103
+ if (i >= rawArgs.length) return null;
104
+ if (mode === "whole-line") {
105
+ const tail = rawArgs.slice(i);
106
+ cursor = rawArgs.length;
107
+ return tail;
108
+ }
109
+ const tail = rawArgs.slice(i);
110
+ const args = scanSlashArgs(tail, mode, varLookup);
111
+ if (args.length === 0) {
112
+ cursor = rawArgs.length;
113
+ return null;
114
+ }
115
+ const first = args[0];
116
+ const span = consumedSpan(tail, mode, varLookup);
117
+ cursor = i + span;
118
+ return first;
119
+ };
120
+ const restOfLine = () => {
121
+ let i = cursor;
122
+ while (i < rawArgs.length && /[\s]/.test(rawArgs[i])) i++;
123
+ const tail = rawArgs.slice(i);
124
+ cursor = rawArgs.length;
125
+ return tail;
126
+ };
127
+ return {
128
+ settings: opts.settings,
129
+ cmdName: opts.cmdName,
130
+ queryBuf: opts.queryBuf,
131
+ rawArgs,
132
+ nextArg,
133
+ restOfLine
134
+ };
154
135
  };
155
136
  /**
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
- */
137
+ * Compute how many bytes of `tail` were consumed lexing the first arg. We
138
+ * walk the same quoting/escape rules as the scanner so the cursor advances
139
+ * past the *source* extent, not the post-expansion length.
140
+ *
141
+ * Stops at whitespace or backslash. Quoted runs (`'…'`, `"…"`, `` `…` ``)
142
+ * are consumed to their closing delimiter. `:var` substitutions advance
143
+ * past the original `:name` form regardless of expansion size.
144
+ */
164
145
  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;
146
+ if (mode === "whole-line") return tail.length;
147
+ let i = 0;
148
+ while (i < tail.length && /[\s]/.test(tail[i])) i++;
149
+ if (mode === "filepipe" && tail[i] === "|") return tail.length;
150
+ while (i < tail.length) {
151
+ const c = tail[i];
152
+ if (/[\s]/.test(c) || c === "\\") break;
153
+ if (c === "'") {
154
+ i++;
155
+ while (i < tail.length) {
156
+ if (tail[i] === "\\" && i + 1 < tail.length) {
157
+ i += 2;
158
+ continue;
159
+ }
160
+ if (tail[i] === "'") {
161
+ if (tail[i + 1] === "'") {
162
+ i += 2;
163
+ continue;
164
+ }
165
+ i++;
166
+ break;
167
+ }
168
+ i++;
169
+ }
170
+ continue;
171
+ }
172
+ if (c === "\"") {
173
+ i++;
174
+ while (i < tail.length && tail[i] !== "\"") i++;
175
+ if (i < tail.length) i++;
176
+ continue;
177
+ }
178
+ if (c === "`") {
179
+ i++;
180
+ while (i < tail.length && tail[i] !== "`") i++;
181
+ if (i < tail.length) i++;
182
+ continue;
183
+ }
184
+ if (c === ":" && mode !== "no-vars") {
185
+ const next = tail[i + 1];
186
+ if (next === "\"" || next === "'") {
187
+ let j = i + 2;
188
+ while (j < tail.length && /[A-Za-z0-9_\x80-\xff]/.test(tail[j])) j++;
189
+ if (j > i + 2 && tail[j] === next) {
190
+ i = j + 1;
191
+ continue;
192
+ }
193
+ }
194
+ if (next && /[A-Za-z0-9_\x80-\xff]/.test(next)) {
195
+ let j = i + 1;
196
+ while (j < tail.length && /[A-Za-z0-9_\x80-\xff]/.test(tail[j])) j++;
197
+ i = j;
198
+ continue;
199
+ }
200
+ }
201
+ i++;
202
+ }
203
+ return i;
239
204
  };
240
205
  /**
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);
206
+ * Top-level dispatch entry. Looks the command up by name (and falls back to
207
+ * registered aliases), runs it, and returns the result.
208
+ *
209
+ * Unknown commands return `{ status: 'error' }` so the mainloop can emit
210
+ * the upstream-style `"invalid command \…"` diagnostic. We deliberately
211
+ * don't print here; the caller owns stderr.
212
+ */
213
+ const dispatchBackslash = async (registry, cmdName, ctx) => {
214
+ const spec = registry.lookup(cmdName);
215
+ if (!spec) return { status: "error" };
216
+ if (isCommandRestricted(ctx.settings, spec.name)) {
217
+ writeErr(`\\${cmdName}: command is not allowed in restricted mode; use \\unrestrict to leave restricted mode\n`);
218
+ return { status: "error" };
219
+ }
220
+ return spec.run(ctx);
261
221
  };
262
222
  /**
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;
223
+ * Return a fresh registry pre-populated with every backslash command this
224
+ * WP implements: meta (`\q`, `\r`/`\reset`, `\!`, `\cd`, `\echo`, `\qecho`,
225
+ * `\warn`, `\prompt`, `\set`, `\unset`, `\getenv`, `\setenv`, `\errverbose`,
226
+ * `\timing`) and format (`\a`, `\C`, `\f`, `\H`, `\t`, `\T`, `\x`,
227
+ * `\pset`, `\encoding`).
228
+ *
229
+ * Other WPs (15/17/20/21/22/23) extend this set by calling `register()` on
230
+ * the returned registry — see the plan for the full mapping.
231
+ */
232
+ const defaultRegistry = () => {
233
+ const r = createBackslashRegistry();
234
+ r.register(cmdQuit);
235
+ r.register(cmdReset);
236
+ r.register(cmdShell);
237
+ r.register(cmdCd);
238
+ r.register(cmdEcho);
239
+ r.register(cmdQecho);
240
+ r.register(cmdWarn);
241
+ r.register(cmdPrompt);
242
+ r.register(cmdSet);
243
+ r.register(cmdUnset);
244
+ r.register(cmdGetenv);
245
+ r.register(cmdSetenv);
246
+ r.register(cmdErrverbose);
247
+ r.register(cmdTiming);
248
+ r.register(cmdCopyright);
249
+ r.register(cmdHelpSQL);
250
+ r.register(cmdSlashHelp);
251
+ r.register(cmdEdit);
252
+ r.register(cmdS);
253
+ r.register({
254
+ name: "html",
255
+ run: () => Promise.resolve({ status: "ok" })
256
+ });
257
+ r.register(cmdA);
258
+ r.register(cmdC);
259
+ r.register(cmdF);
260
+ r.register(cmdH);
261
+ r.register(cmdT);
262
+ r.register(cmdTitleAttr);
263
+ r.register(cmdX);
264
+ r.register(cmdEncoding);
265
+ r.register(cmdPset);
266
+ registerIoCommands(r);
267
+ registerConnectCommands(r);
268
+ registerCopyCommands(r);
269
+ registerDescribeCommands(r);
270
+ registerPipelineCommands(r);
271
+ registerMiscCommands(r);
272
+ registerLargeObjectCommands(r);
273
+ registerShowCommands(r);
274
+ registerRestrictCommands(r);
275
+ wrapRestrictedCommands(r);
276
+ return r;
343
277
  };
278
+ //#endregion
279
+ export { createBackslashRegistry, defaultRegistry, dispatchBackslash, makeContext };