neon 3.0.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 +354 -517
  5. package/dist/_shared/env-core/reuse-secrets.js +159 -203
  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 +573 -690
  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 +639 -719
  28. package/dist/commands/diff.js +156 -200
  29. package/dist/commands/env.js +243 -303
  30. package/dist/commands/functions.js +275 -355
  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 -68
  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 +130 -189
  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 +7 -5
@@ -1,846 +1,657 @@
1
- /**
2
- * psql startup-args parser + applier.
3
- *
4
- * TypeScript port of `parse_psql_options()` / the top of `main()` in
5
- * `src/bin/psql/startup.c`. Two responsibilities:
6
- *
7
- * 1. `parseStartupArgs(argv)` — hand-written getopt_long-equivalent that
8
- * walks the argv array once, decoding short (`-c`) and long (`--command`)
9
- * options, and accumulates an ordered list of `-c`/`-f` actions plus a
10
- * bag of settings flags. Positional arguments follow the upstream
11
- * `[DBNAME [USERNAME]]` rule.
12
- *
13
- * We hand-roll the parser instead of pulling in `yargs` (or similar)
14
- * because upstream psql has subtle precedence rules — actions are
15
- * ordered, `-v NAME` without `=` deletes the variable, `-X` interacts
16
- * with `.psqlrc` discovery, etc. The parser stays under ~250 LOC and is
17
- * fully covered by unit tests.
18
- *
19
- * 2. `applyStartupArgs(args, settings, baseConnect)` — mutates `settings`
20
- * (and its var store) in-place to reflect parsed flags, then returns
21
- * the connect options the caller should use plus the ordered list of
22
- * pre-REPL actions to execute (currently just `-c` / `-f`).
23
- *
24
- * Out-of-scope for WP-26 but the table mentions them so they're listed below:
25
- * - `-1` / `--single-transaction` — we accept and record on the args but
26
- * do not wire its semantics in the caller. Tracked for a follow-up.
27
- * - `-C` — accepted as a no-op (since-PG17 connection-check skip).
28
- * - `-l` / `--list` — recorded; the caller may use it later to short-circuit
29
- * the REPL with a `\l` and exit. Not wired here.
30
- * - `-L logfile`, `-o output` — recorded on args. Routing into the printer
31
- * is owned by other WPs (logfile streaming and output redirection).
32
- * - `--help[=topic]` — returns a ParseError with `kind: 'help'` and the
33
- * help text already rendered as `message`. Caller writes and exits 0.
34
- * - `-V` / `--version` — returns `kind: 'version'` with the version string.
35
- * Caller writes and exits 0.
36
- *
37
- * Help/version text is rendered through `./help.ts` so the formatting stays
38
- * in lock-step with `\?` output (WP-18). The version string mirrors upstream
39
- * `psql (PostgreSQL) PG_VERSION` and is currently hard-coded — the embedded
40
- * psql doesn't have a build-system PG_VERSION constant. A follow-up could
41
- * surface the real PG version from a const, but for now we expose the
42
- * embedded build's version label.
43
- */
44
- import { envConnectionDefaults, libpqConnectionDefaults, looksLikeConnectionString, mergeConnectOptions, parseConnectionUriPartial, parseConninfo, serviceEntryToConnectOptions, } from "../index.js";
45
- import { lookupPgPass } from "../io/pgpass.js";
46
- import { helpVariables, slashUsage, usage } from "./help.js";
47
1
  import { setStartupVars } from "./syncVars.js";
48
- // ---------------------------------------------------------------------------
49
- // Internal helpers.
50
- // ---------------------------------------------------------------------------
2
+ import { helpVariables, slashUsage, usage } from "./help.js";
3
+ import { lookupPgPass } from "../io/pgpass.js";
4
+ import { envConnectionDefaults, libpqConnectionDefaults, looksLikeConnectionString, mergeConnectOptions, parseConnectionUriPartial, parseConninfo, serviceEntryToConnectOptions } from "../index.js";
5
+ //#region src/psql/core/startup.ts
6
+ /**
7
+ * psql startup-args parser + applier.
8
+ *
9
+ * TypeScript port of `parse_psql_options()` / the top of `main()` in
10
+ * `src/bin/psql/startup.c`. Two responsibilities:
11
+ *
12
+ * 1. `parseStartupArgs(argv)` — hand-written getopt_long-equivalent that
13
+ * walks the argv array once, decoding short (`-c`) and long (`--command`)
14
+ * options, and accumulates an ordered list of `-c`/`-f` actions plus a
15
+ * bag of settings flags. Positional arguments follow the upstream
16
+ * `[DBNAME [USERNAME]]` rule.
17
+ *
18
+ * We hand-roll the parser instead of pulling in `yargs` (or similar)
19
+ * because upstream psql has subtle precedence rules — actions are
20
+ * ordered, `-v NAME` without `=` deletes the variable, `-X` interacts
21
+ * with `.psqlrc` discovery, etc. The parser stays under ~250 LOC and is
22
+ * fully covered by unit tests.
23
+ *
24
+ * 2. `applyStartupArgs(args, settings, baseConnect)` — mutates `settings`
25
+ * (and its var store) in-place to reflect parsed flags, then returns
26
+ * the connect options the caller should use plus the ordered list of
27
+ * pre-REPL actions to execute (currently just `-c` / `-f`).
28
+ *
29
+ * Out-of-scope for WP-26 but the table mentions them so they're listed below:
30
+ * - `-1` / `--single-transaction` — we accept and record on the args but
31
+ * do not wire its semantics in the caller. Tracked for a follow-up.
32
+ * - `-C` — accepted as a no-op (since-PG17 connection-check skip).
33
+ * - `-l` / `--list` — recorded; the caller may use it later to short-circuit
34
+ * the REPL with a `\l` and exit. Not wired here.
35
+ * - `-L logfile`, `-o output` — recorded on args. Routing into the printer
36
+ * is owned by other WPs (logfile streaming and output redirection).
37
+ * - `--help[=topic]` — returns a ParseError with `kind: 'help'` and the
38
+ * help text already rendered as `message`. Caller writes and exits 0.
39
+ * - `-V` / `--version` — returns `kind: 'version'` with the version string.
40
+ * Caller writes and exits 0.
41
+ *
42
+ * Help/version text is rendered through `./help.ts` so the formatting stays
43
+ * in lock-step with `\?` output (WP-18). The version string mirrors upstream
44
+ * `psql (PostgreSQL) PG_VERSION` and is currently hard-coded — the embedded
45
+ * psql doesn't have a build-system PG_VERSION constant. A follow-up could
46
+ * surface the real PG version from a const, but for now we expose the
47
+ * embedded build's version label.
48
+ */
51
49
  /** Short options that consume the next argv slot (or attached value). */
52
- const SHORT_WITH_ARG = new Set([
53
- "c",
54
- "d",
55
- "f",
56
- "F",
57
- "h",
58
- "L",
59
- "o",
60
- "p",
61
- "P",
62
- "R",
63
- "T",
64
- "U",
65
- "v",
50
+ const SHORT_WITH_ARG = /* @__PURE__ */ new Set([
51
+ "c",
52
+ "d",
53
+ "f",
54
+ "F",
55
+ "h",
56
+ "L",
57
+ "o",
58
+ "p",
59
+ "P",
60
+ "R",
61
+ "T",
62
+ "U",
63
+ "v"
66
64
  ]);
67
65
  /** Short flags (no argument). */
68
- const SHORT_NO_ARG = new Set([
69
- "a",
70
- "A",
71
- "b",
72
- "C",
73
- "e",
74
- "E",
75
- "H",
76
- "l",
77
- "n",
78
- "q",
79
- "s",
80
- "S",
81
- "t",
82
- "V",
83
- "w",
84
- "W",
85
- "x",
86
- "X",
87
- "z",
88
- "0",
89
- "1",
90
- "?",
66
+ const SHORT_NO_ARG = /* @__PURE__ */ new Set([
67
+ "a",
68
+ "A",
69
+ "b",
70
+ "C",
71
+ "e",
72
+ "E",
73
+ "H",
74
+ "l",
75
+ "n",
76
+ "q",
77
+ "s",
78
+ "S",
79
+ "t",
80
+ "V",
81
+ "w",
82
+ "W",
83
+ "x",
84
+ "X",
85
+ "z",
86
+ "0",
87
+ "1",
88
+ "?"
91
89
  ]);
92
90
  /** Long option → canonical short equivalent (or sentinel string). */
93
91
  const LONG_MAP = {
94
- "echo-all": "a",
95
- "no-align": "A",
96
- command: "c",
97
- dbname: "d",
98
- "echo-queries": "e",
99
- "echo-errors": "b",
100
- "echo-hidden": "E",
101
- file: "f",
102
- "field-separator": "F",
103
- "field-separator-zero": "z",
104
- host: "h",
105
- html: "H",
106
- list: "l",
107
- "log-file": "L",
108
- "no-readline": "n",
109
- "single-transaction": "1",
110
- output: "o",
111
- port: "p",
112
- pset: "P",
113
- quiet: "q",
114
- "record-separator": "R",
115
- "record-separator-zero": "0",
116
- "single-step": "s",
117
- "single-line": "S",
118
- "tuples-only": "t",
119
- "table-attr": "T",
120
- username: "U",
121
- set: "v",
122
- variable: "v",
123
- version: "V",
124
- "no-password": "w",
125
- password: "W",
126
- expanded: "x",
127
- "no-psqlrc": "X",
128
- "no-pager": "__no_pager__",
129
- "on-error-stop": "__on_error_stop__",
130
- help: "__help__",
131
- csv: "__csv__",
92
+ "echo-all": "a",
93
+ "no-align": "A",
94
+ command: "c",
95
+ dbname: "d",
96
+ "echo-queries": "e",
97
+ "echo-errors": "b",
98
+ "echo-hidden": "E",
99
+ file: "f",
100
+ "field-separator": "F",
101
+ "field-separator-zero": "z",
102
+ host: "h",
103
+ html: "H",
104
+ list: "l",
105
+ "log-file": "L",
106
+ "no-readline": "n",
107
+ "single-transaction": "1",
108
+ output: "o",
109
+ port: "p",
110
+ pset: "P",
111
+ quiet: "q",
112
+ "record-separator": "R",
113
+ "record-separator-zero": "0",
114
+ "single-step": "s",
115
+ "single-line": "S",
116
+ "tuples-only": "t",
117
+ "table-attr": "T",
118
+ username: "U",
119
+ set: "v",
120
+ variable: "v",
121
+ version: "V",
122
+ "no-password": "w",
123
+ password: "W",
124
+ expanded: "x",
125
+ "no-psqlrc": "X",
126
+ "no-pager": "__no_pager__",
127
+ "on-error-stop": "__on_error_stop__",
128
+ help: "__help__",
129
+ csv: "__csv__"
132
130
  };
133
131
  /** Long options that REQUIRE an attached value (or one in the next argv). */
134
- const LONG_WITH_ARG = new Set([
135
- "command",
136
- "dbname",
137
- "file",
138
- "field-separator",
139
- "host",
140
- "log-file",
141
- "output",
142
- "port",
143
- "pset",
144
- "record-separator",
145
- "table-attr",
146
- "username",
147
- "set",
148
- "variable",
132
+ const LONG_WITH_ARG = /* @__PURE__ */ new Set([
133
+ "command",
134
+ "dbname",
135
+ "file",
136
+ "field-separator",
137
+ "host",
138
+ "log-file",
139
+ "output",
140
+ "port",
141
+ "pset",
142
+ "record-separator",
143
+ "table-attr",
144
+ "username",
145
+ "set",
146
+ "variable"
149
147
  ]);
150
148
  /** Long options whose value is optional (only attached with `=`). */
151
- const LONG_OPTIONAL_ARG = new Set(["help", "echo-hidden"]);
149
+ const LONG_OPTIONAL_ARG = /* @__PURE__ */ new Set(["help", "echo-hidden"]);
152
150
  const blankArgs = () => ({
153
- actions: [],
154
- variables: [],
155
- noPsqlrc: false,
156
- noReadline: false,
157
- echoAll: false,
158
- echoHidden: "off",
159
- echoErrors: false,
160
- echoQueries: false,
161
- quiet: false,
162
- singleline: false,
163
- singlestep: false,
164
- noAlign: false,
165
- noPager: false,
166
- htmlMode: false,
167
- tuplesOnly: false,
168
- expanded: false,
169
- csvOutput: false,
170
- onErrorStop: false,
171
- fieldSepZero: false,
172
- recordSepZero: false,
173
- pset: [],
174
- list: false,
175
- singleTransaction: false,
176
- help: false,
177
- version: false,
178
- positional: [],
151
+ actions: [],
152
+ variables: [],
153
+ noPsqlrc: false,
154
+ noReadline: false,
155
+ echoAll: false,
156
+ echoHidden: "off",
157
+ echoErrors: false,
158
+ echoQueries: false,
159
+ quiet: false,
160
+ singleline: false,
161
+ singlestep: false,
162
+ noAlign: false,
163
+ noPager: false,
164
+ htmlMode: false,
165
+ tuplesOnly: false,
166
+ expanded: false,
167
+ csvOutput: false,
168
+ onErrorStop: false,
169
+ fieldSepZero: false,
170
+ recordSepZero: false,
171
+ pset: [],
172
+ list: false,
173
+ singleTransaction: false,
174
+ help: false,
175
+ version: false,
176
+ positional: []
179
177
  });
180
178
  const renderToString = (fn) => {
181
- const chunks = [];
182
- const sink = {
183
- write(chunk) {
184
- chunks.push(typeof chunk === "string"
185
- ? chunk
186
- : Buffer.from(chunk).toString("utf8"));
187
- return true;
188
- },
189
- };
190
- fn(sink);
191
- return chunks.join("");
179
+ const chunks = [];
180
+ fn({ write(chunk) {
181
+ chunks.push(typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8"));
182
+ return true;
183
+ } });
184
+ return chunks.join("");
192
185
  };
193
186
  const renderHelp = (topic) => {
194
- if (topic === undefined || topic === "options") {
195
- return renderToString((out) => {
196
- usage(out);
197
- });
198
- }
199
- if (topic === "commands") {
200
- return renderToString((out) => {
201
- slashUsage(out, false);
202
- });
203
- }
204
- if (topic === "variables") {
205
- return renderToString((out) => {
206
- helpVariables(out);
207
- });
208
- }
209
- return "";
187
+ if (topic === void 0 || topic === "options") return renderToString((out) => {
188
+ usage(out);
189
+ });
190
+ if (topic === "commands") return renderToString((out) => {
191
+ slashUsage(out, false);
192
+ });
193
+ if (topic === "variables") return renderToString((out) => {
194
+ helpVariables(out);
195
+ });
196
+ return "";
210
197
  };
211
198
  /**
212
- * Version label for the embedded TypeScript psql. This is the "client"
213
- * identifier — the analogue of upstream psql's compiled-in `PG_VERSION`.
214
- * The embedded psql is not a real PostgreSQL build, so we track our own
215
- * implementation version here. It seeds the `VERSION_NAME` psql var (and,
216
- * via {@link setStartupVars}, the numeric `VERSION_NUM`); keep it as a
217
- * `MAJOR.MINOR.PATCH` string so the numeric mapping stays meaningful. Bump
218
- * alongside meaningful behaviour changes to the embedded psql.
219
- */
220
- export const CLIENT_VERSION = "1.0.0";
221
- // `psql --version` / `-V` output. Mirrors upstream's `psql (PostgreSQL)
222
- // <PG_VERSION>` SHAPE (tools parse the leading `psql (PostgreSQL)`), with an
223
- // `embedded-ts` label in place of a real PG build number since this is a
224
- // reimplementation, not a compiled psql. Distinct from the `:VERSION` psql
225
- // variable (the client-identity string seeded from CLIENT_VERSION).
199
+ * Version label for the embedded TypeScript psql. This is the "client"
200
+ * identifier — the analogue of upstream psql's compiled-in `PG_VERSION`.
201
+ * The embedded psql is not a real PostgreSQL build, so we track our own
202
+ * implementation version here. It seeds the `VERSION_NAME` psql var (and,
203
+ * via {@link setStartupVars}, the numeric `VERSION_NUM`); keep it as a
204
+ * `MAJOR.MINOR.PATCH` string so the numeric mapping stays meaningful. Bump
205
+ * alongside meaningful behaviour changes to the embedded psql.
206
+ */
207
+ const CLIENT_VERSION = "1.0.0";
226
208
  const VERSION_STRING = `psql (PostgreSQL) embedded-ts (neon ${CLIENT_VERSION})`;
227
209
  const pushAction = (acts, a) => {
228
- acts.push(a);
210
+ acts.push(a);
229
211
  };
230
212
  const pushVariable = (vars, raw) => {
231
- const eq = raw.indexOf("=");
232
- if (eq < 0) {
233
- // psql treats `-v NAME` (no value) as "delete this variable". We surface
234
- // an empty value; the caller decides whether to set or unset. Tests
235
- // verify both shapes.
236
- vars.push({ name: raw, value: "" });
237
- return;
238
- }
239
- vars.push({ name: raw.slice(0, eq), value: raw.slice(eq + 1) });
213
+ const eq = raw.indexOf("=");
214
+ if (eq < 0) {
215
+ vars.push({
216
+ name: raw,
217
+ value: ""
218
+ });
219
+ return;
220
+ }
221
+ vars.push({
222
+ name: raw.slice(0, eq),
223
+ value: raw.slice(eq + 1)
224
+ });
240
225
  };
241
226
  /**
242
- * `-d VALUE` resolution. libpq accepts three shapes for the value:
243
- *
244
- * 1. Bare database name (no `=`, no `postgres[ql]://` prefix) — set
245
- * `args.database` directly. This is by far the common case.
246
- * 2. Connection URI (`postgresql://…` / `postgres://…`) — parse it and
247
- * lift the fields it specifies onto `args` (host, port, user, etc.).
248
- * 3. Conninfo key=value pairs separated by whitespace — same, but parsed
249
- * via `parseConninfo`.
250
- *
251
- * The test corpus for upstream `001_basic.pl` uses shape (3) to open a
252
- * walsender connection: `-d "dbname=postgres replication=database"`. We
253
- * fold any conninfo-only fields (currently only `replication`) onto a
254
- * dedicated `args.replication` slot that `applyStartupArgs` threads into
255
- * `ConnectOptions.replication`.
256
- */
227
+ * `-d VALUE` resolution. libpq accepts three shapes for the value:
228
+ *
229
+ * 1. Bare database name (no `=`, no `postgres[ql]://` prefix) — set
230
+ * `args.database` directly. This is by far the common case.
231
+ * 2. Connection URI (`postgresql://…` / `postgres://…`) — parse it and
232
+ * lift the fields it specifies onto `args` (host, port, user, etc.).
233
+ * 3. Conninfo key=value pairs separated by whitespace — same, but parsed
234
+ * via `parseConninfo`.
235
+ *
236
+ * The test corpus for upstream `001_basic.pl` uses shape (3) to open a
237
+ * walsender connection: `-d "dbname=postgres replication=database"`. We
238
+ * fold any conninfo-only fields (currently only `replication`) onto a
239
+ * dedicated `args.replication` slot that `applyStartupArgs` threads into
240
+ * `ConnectOptions.replication`.
241
+ */
257
242
  const applyDashD = (args, value) => {
258
- if (!looksLikeConnectionString(value)) {
259
- args.database = value;
260
- return;
261
- }
262
- // Use the *partial* URI parser, NOT parseConnectionUri: the latter applies
263
- // libpq's lowest-priority OS-user fallback (`user = $USER`, `database =
264
- // user`) eagerly, and those non-undefined defaults would then land in the
265
- // top-priority argv layer and override `PGUSER` / `PGDATABASE` (review #7).
266
- // The partial parser returns only the keys the URI actually specified.
267
- const parsed = value.startsWith("postgresql://") || value.startsWith("postgres://")
268
- ? parseConnectionUriPartial(value)
269
- : parseConninfo(value);
270
- // Keep the whole partial (incl. ssl*/channelBinding/hostaddr/requireAuth)
271
- // so the connection layer carries it (review #6).
272
- args.dashD = { ...(args.dashD ?? {}), ...parsed };
273
- // Continue to mirror the core target fields onto the flag slots so the
274
- // positional-arg / dbname-username precedence logic below sees them. Only
275
- // when not already set by an explicit flag (flags win over -d).
276
- if (parsed.host !== undefined && args.host === undefined) {
277
- args.host = parsed.host;
278
- }
279
- if (parsed.port !== undefined && args.port === undefined) {
280
- args.port = parsed.port;
281
- }
282
- if (parsed.user !== undefined && args.user === undefined) {
283
- args.user = parsed.user;
284
- }
285
- if (parsed.password !== undefined && args.password === undefined) {
286
- args.password = parsed.password;
287
- }
288
- if (parsed.database !== undefined && args.database === undefined) {
289
- args.database = parsed.database;
290
- }
291
- if (parsed.replication !== undefined) {
292
- args.replication = parsed.replication;
293
- }
243
+ if (!looksLikeConnectionString(value)) {
244
+ args.database = value;
245
+ return;
246
+ }
247
+ const parsed = value.startsWith("postgresql://") || value.startsWith("postgres://") ? parseConnectionUriPartial(value) : parseConninfo(value);
248
+ args.dashD = {
249
+ ...args.dashD ?? {},
250
+ ...parsed
251
+ };
252
+ if (parsed.host !== void 0 && args.host === void 0) args.host = parsed.host;
253
+ if (parsed.port !== void 0 && args.port === void 0) args.port = parsed.port;
254
+ if (parsed.user !== void 0 && args.user === void 0) args.user = parsed.user;
255
+ if (parsed.password !== void 0 && args.password === void 0) args.password = parsed.password;
256
+ if (parsed.database !== void 0 && args.database === void 0) args.database = parsed.database;
257
+ if (parsed.replication !== void 0) args.replication = parsed.replication;
294
258
  };
295
- // ---------------------------------------------------------------------------
296
- // parseStartupArgs.
297
- // ---------------------------------------------------------------------------
298
- export const parseStartupArgs = (argv) => {
299
- const args = blankArgs();
300
- const actions = [];
301
- const variables = [];
302
- const pset = [];
303
- let i = 0;
304
- const n = argv.length;
305
- const needValue = (flagDisplay) => {
306
- if (i + 1 >= n) {
307
- return {
308
- kind: "missing-arg",
309
- message: `option requires an argument -- ${flagDisplay}`,
310
- };
311
- }
312
- i += 1;
313
- return argv[i];
314
- };
315
- const applyShort = (letter, attachedValue, flagDisplay) => {
316
- // Resolve the value: either attached (already next to the flag) or the
317
- // next argv slot. If the flag does not take an argument, attachedValue
318
- // must be undefined.
319
- const takesArg = SHORT_WITH_ARG.has(letter);
320
- let value;
321
- if (takesArg) {
322
- if (attachedValue !== undefined) {
323
- value = attachedValue;
324
- }
325
- else {
326
- const v = needValue(flagDisplay);
327
- if (typeof v !== "string")
328
- return v;
329
- value = v;
330
- }
331
- }
332
- else if (attachedValue !== undefined) {
333
- // A value was attached to a short flag that doesn't accept one. Treat
334
- // this as an invalid-option error (matches `getopt_long` behaviour
335
- // for clusters like `-Xy` where `X` doesn't take an argument — flex
336
- // continues to `y`; we keep it strict for clarity).
337
- return {
338
- kind: "invalid-option",
339
- message: `option does not take an argument -- ${letter}`,
340
- };
341
- }
342
- switch (letter) {
343
- case "a":
344
- args.echoAll = true;
345
- return null;
346
- case "A":
347
- args.noAlign = true;
348
- return null;
349
- case "b":
350
- args.echoErrors = true;
351
- return null;
352
- case "C":
353
- // Since-PG17 "skip connection check" — no-op for our use.
354
- return null;
355
- case "c":
356
- pushAction(actions, { kind: "command", sql: value });
357
- return null;
358
- case "d":
359
- applyDashD(args, value);
360
- return null;
361
- case "e":
362
- args.echoQueries = true;
363
- return null;
364
- case "E":
365
- args.echoHidden = "on";
366
- return null;
367
- case "f":
368
- pushAction(actions, { kind: "file", path: value });
369
- return null;
370
- case "F":
371
- args.fieldSep = value;
372
- args.fieldSepZero = false;
373
- return null;
374
- case "h":
375
- args.host = value;
376
- return null;
377
- case "H":
378
- args.htmlMode = true;
379
- return null;
380
- case "l":
381
- args.list = true;
382
- return null;
383
- case "L":
384
- args.log = value;
385
- return null;
386
- case "n":
387
- args.noReadline = true;
388
- return null;
389
- case "o":
390
- args.output = value;
391
- return null;
392
- case "p": {
393
- const p = Number.parseInt(value, 10);
394
- if (!Number.isFinite(p) || p <= 0 || p > 65535) {
395
- return {
396
- kind: "invalid-value",
397
- message: `invalid port number: "${value}"`,
398
- };
399
- }
400
- args.port = p;
401
- return null;
402
- }
403
- case "P":
404
- pset.push(value);
405
- return null;
406
- case "q":
407
- args.quiet = true;
408
- return null;
409
- case "R":
410
- args.recordSep = value;
411
- args.recordSepZero = false;
412
- return null;
413
- case "s":
414
- args.singlestep = true;
415
- return null;
416
- case "S":
417
- args.singleline = true;
418
- return null;
419
- case "t":
420
- args.tuplesOnly = true;
421
- return null;
422
- case "T":
423
- // -T TEXT → push a synthetic pset directive so it routes through
424
- // the same `\pset tableattr` plumbing.
425
- pset.push(`tableattr=${value}`);
426
- return null;
427
- case "U":
428
- args.user = value;
429
- return null;
430
- case "v":
431
- pushVariable(variables, value);
432
- return null;
433
- case "V":
434
- return {
435
- kind: "version",
436
- message: VERSION_STRING,
437
- };
438
- case "w":
439
- args.promptPassword = false;
440
- return null;
441
- case "W":
442
- args.promptPassword = true;
443
- return null;
444
- case "x":
445
- args.expanded = true;
446
- return null;
447
- case "X":
448
- args.noPsqlrc = true;
449
- return null;
450
- case "z":
451
- args.fieldSepZero = true;
452
- return null;
453
- case "0":
454
- args.recordSepZero = true;
455
- return null;
456
- case "1":
457
- args.singleTransaction = true;
458
- return null;
459
- case "?":
460
- return {
461
- kind: "help",
462
- message: renderHelp(undefined),
463
- };
464
- default:
465
- return {
466
- kind: "invalid-option",
467
- message: `invalid option -- ${letter}`,
468
- };
469
- }
470
- };
471
- // ---- Main loop. -------------------------------------------------------
472
- while (i < n) {
473
- const tok = argv[i];
474
- if (tok === "--") {
475
- // End of options. Everything after is positional.
476
- i += 1;
477
- while (i < n) {
478
- args.positional.push(argv[i]);
479
- i += 1;
480
- }
481
- break;
482
- }
483
- if (tok.startsWith("--")) {
484
- // Long option: `--name` or `--name=value`.
485
- const eq = tok.indexOf("=");
486
- const name = eq < 0 ? tok.slice(2) : tok.slice(2, eq);
487
- const attached = eq < 0 ? undefined : tok.slice(eq + 1);
488
- // Special-case the long-only synthetic options first.
489
- if (name === "csv") {
490
- if (attached !== undefined) {
491
- return {
492
- kind: "invalid-option",
493
- message: `option does not take an argument: --${name}`,
494
- };
495
- }
496
- args.csvOutput = true;
497
- i += 1;
498
- continue;
499
- }
500
- if (name === "no-pager") {
501
- if (attached !== undefined) {
502
- return {
503
- kind: "invalid-option",
504
- message: `option does not take an argument: --${name}`,
505
- };
506
- }
507
- args.noPager = true;
508
- i += 1;
509
- continue;
510
- }
511
- if (name === "on-error-stop") {
512
- if (attached !== undefined) {
513
- return {
514
- kind: "invalid-option",
515
- message: `option does not take an argument: --${name}`,
516
- };
517
- }
518
- args.onErrorStop = true;
519
- variables.push({ name: "ON_ERROR_STOP", value: "on" });
520
- i += 1;
521
- continue;
522
- }
523
- if (name === "help") {
524
- const topic = attached;
525
- if (topic !== undefined &&
526
- topic !== "options" &&
527
- topic !== "commands" &&
528
- topic !== "variables") {
529
- return {
530
- kind: "invalid-option",
531
- message: `unrecognized help topic: ${topic}`,
532
- };
533
- }
534
- return { kind: "help", message: renderHelp(topic) };
535
- }
536
- if (name === "echo-hidden") {
537
- if (attached === undefined) {
538
- args.echoHidden = "on";
539
- }
540
- else if (attached === "noexec") {
541
- args.echoHidden = "noexec";
542
- }
543
- else if (attached === "" || attached === "on") {
544
- args.echoHidden = "on";
545
- }
546
- else {
547
- return {
548
- kind: "invalid-value",
549
- message: `invalid value for --echo-hidden: "${attached}"`,
550
- };
551
- }
552
- i += 1;
553
- continue;
554
- }
555
- const short = LONG_MAP[name];
556
- if (short === undefined) {
557
- return {
558
- kind: "invalid-option",
559
- message: `unrecognized option: --${name}`,
560
- };
561
- }
562
- const requiresArg = LONG_WITH_ARG.has(name);
563
- const optionalArg = LONG_OPTIONAL_ARG.has(name);
564
- let value = attached;
565
- if (requiresArg && value === undefined) {
566
- if (i + 1 >= n) {
567
- return {
568
- kind: "missing-arg",
569
- message: `option requires an argument: --${name}`,
570
- };
571
- }
572
- i += 1;
573
- value = argv[i];
574
- }
575
- else if (!requiresArg && !optionalArg && value !== undefined) {
576
- return {
577
- kind: "invalid-option",
578
- message: `option does not take an argument: --${name}`,
579
- };
580
- }
581
- const err = applyShort(short, value, `--${name}`);
582
- if (err)
583
- return err;
584
- i += 1;
585
- continue;
586
- }
587
- if (tok.startsWith("-") && tok.length > 1) {
588
- // Short option cluster. Each char might consume the rest of the token
589
- // (or the next argv) as its value.
590
- let k = 1;
591
- while (k < tok.length) {
592
- const letter = tok[k];
593
- if (!SHORT_WITH_ARG.has(letter) && !SHORT_NO_ARG.has(letter)) {
594
- return {
595
- kind: "invalid-option",
596
- message: `invalid option -- ${letter}`,
597
- };
598
- }
599
- if (SHORT_WITH_ARG.has(letter)) {
600
- // Consume the rest of this token as the value if anything remains,
601
- // otherwise pull from the next argv.
602
- const remainder = tok.slice(k + 1);
603
- const value = remainder.length > 0 ? remainder : undefined;
604
- const err = applyShort(letter, value, letter);
605
- if (err)
606
- return err;
607
- k = tok.length; // Consumed the rest.
608
- break;
609
- }
610
- const err = applyShort(letter, undefined, letter);
611
- if (err)
612
- return err;
613
- k += 1;
614
- }
615
- i += 1;
616
- continue;
617
- }
618
- // Positional.
619
- args.positional.push(tok);
620
- i += 1;
621
- }
622
- // Upstream consumes only DBNAME and USERNAME from positionals; extras are
623
- // warned about and ignored. We surface them all on `positional` so the
624
- // caller can warn/log; applyStartupArgs takes care of the first two.
625
- return {
626
- ...args,
627
- actions,
628
- variables,
629
- pset,
630
- };
259
+ const parseStartupArgs = (argv) => {
260
+ const args = blankArgs();
261
+ const actions = [];
262
+ const variables = [];
263
+ const pset = [];
264
+ let i = 0;
265
+ const n = argv.length;
266
+ const needValue = (flagDisplay) => {
267
+ if (i + 1 >= n) return {
268
+ kind: "missing-arg",
269
+ message: `option requires an argument -- ${flagDisplay}`
270
+ };
271
+ i += 1;
272
+ return argv[i];
273
+ };
274
+ const applyShort = (letter, attachedValue, flagDisplay) => {
275
+ const takesArg = SHORT_WITH_ARG.has(letter);
276
+ let value;
277
+ if (takesArg) if (attachedValue !== void 0) value = attachedValue;
278
+ else {
279
+ const v = needValue(flagDisplay);
280
+ if (typeof v !== "string") return v;
281
+ value = v;
282
+ }
283
+ else if (attachedValue !== void 0) return {
284
+ kind: "invalid-option",
285
+ message: `option does not take an argument -- ${letter}`
286
+ };
287
+ switch (letter) {
288
+ case "a":
289
+ args.echoAll = true;
290
+ return null;
291
+ case "A":
292
+ args.noAlign = true;
293
+ return null;
294
+ case "b":
295
+ args.echoErrors = true;
296
+ return null;
297
+ case "C": return null;
298
+ case "c":
299
+ pushAction(actions, {
300
+ kind: "command",
301
+ sql: value
302
+ });
303
+ return null;
304
+ case "d":
305
+ applyDashD(args, value);
306
+ return null;
307
+ case "e":
308
+ args.echoQueries = true;
309
+ return null;
310
+ case "E":
311
+ args.echoHidden = "on";
312
+ return null;
313
+ case "f":
314
+ pushAction(actions, {
315
+ kind: "file",
316
+ path: value
317
+ });
318
+ return null;
319
+ case "F":
320
+ args.fieldSep = value;
321
+ args.fieldSepZero = false;
322
+ return null;
323
+ case "h":
324
+ args.host = value;
325
+ return null;
326
+ case "H":
327
+ args.htmlMode = true;
328
+ return null;
329
+ case "l":
330
+ args.list = true;
331
+ return null;
332
+ case "L":
333
+ args.log = value;
334
+ return null;
335
+ case "n":
336
+ args.noReadline = true;
337
+ return null;
338
+ case "o":
339
+ args.output = value;
340
+ return null;
341
+ case "p": {
342
+ const p = Number.parseInt(value, 10);
343
+ if (!Number.isFinite(p) || p <= 0 || p > 65535) return {
344
+ kind: "invalid-value",
345
+ message: `invalid port number: "${value}"`
346
+ };
347
+ args.port = p;
348
+ return null;
349
+ }
350
+ case "P":
351
+ pset.push(value);
352
+ return null;
353
+ case "q":
354
+ args.quiet = true;
355
+ return null;
356
+ case "R":
357
+ args.recordSep = value;
358
+ args.recordSepZero = false;
359
+ return null;
360
+ case "s":
361
+ args.singlestep = true;
362
+ return null;
363
+ case "S":
364
+ args.singleline = true;
365
+ return null;
366
+ case "t":
367
+ args.tuplesOnly = true;
368
+ return null;
369
+ case "T":
370
+ pset.push(`tableattr=${value}`);
371
+ return null;
372
+ case "U":
373
+ args.user = value;
374
+ return null;
375
+ case "v":
376
+ pushVariable(variables, value);
377
+ return null;
378
+ case "V": return {
379
+ kind: "version",
380
+ message: VERSION_STRING
381
+ };
382
+ case "w":
383
+ args.promptPassword = false;
384
+ return null;
385
+ case "W":
386
+ args.promptPassword = true;
387
+ return null;
388
+ case "x":
389
+ args.expanded = true;
390
+ return null;
391
+ case "X":
392
+ args.noPsqlrc = true;
393
+ return null;
394
+ case "z":
395
+ args.fieldSepZero = true;
396
+ return null;
397
+ case "0":
398
+ args.recordSepZero = true;
399
+ return null;
400
+ case "1":
401
+ args.singleTransaction = true;
402
+ return null;
403
+ case "?": return {
404
+ kind: "help",
405
+ message: renderHelp(void 0)
406
+ };
407
+ default: return {
408
+ kind: "invalid-option",
409
+ message: `invalid option -- ${letter}`
410
+ };
411
+ }
412
+ };
413
+ while (i < n) {
414
+ const tok = argv[i];
415
+ if (tok === "--") {
416
+ i += 1;
417
+ while (i < n) {
418
+ args.positional.push(argv[i]);
419
+ i += 1;
420
+ }
421
+ break;
422
+ }
423
+ if (tok.startsWith("--")) {
424
+ const eq = tok.indexOf("=");
425
+ const name = eq < 0 ? tok.slice(2) : tok.slice(2, eq);
426
+ const attached = eq < 0 ? void 0 : tok.slice(eq + 1);
427
+ if (name === "csv") {
428
+ if (attached !== void 0) return {
429
+ kind: "invalid-option",
430
+ message: `option does not take an argument: --${name}`
431
+ };
432
+ args.csvOutput = true;
433
+ i += 1;
434
+ continue;
435
+ }
436
+ if (name === "no-pager") {
437
+ if (attached !== void 0) return {
438
+ kind: "invalid-option",
439
+ message: `option does not take an argument: --${name}`
440
+ };
441
+ args.noPager = true;
442
+ i += 1;
443
+ continue;
444
+ }
445
+ if (name === "on-error-stop") {
446
+ if (attached !== void 0) return {
447
+ kind: "invalid-option",
448
+ message: `option does not take an argument: --${name}`
449
+ };
450
+ args.onErrorStop = true;
451
+ variables.push({
452
+ name: "ON_ERROR_STOP",
453
+ value: "on"
454
+ });
455
+ i += 1;
456
+ continue;
457
+ }
458
+ if (name === "help") {
459
+ const topic = attached;
460
+ if (topic !== void 0 && topic !== "options" && topic !== "commands" && topic !== "variables") return {
461
+ kind: "invalid-option",
462
+ message: `unrecognized help topic: ${topic}`
463
+ };
464
+ return {
465
+ kind: "help",
466
+ message: renderHelp(topic)
467
+ };
468
+ }
469
+ if (name === "echo-hidden") {
470
+ if (attached === void 0) args.echoHidden = "on";
471
+ else if (attached === "noexec") args.echoHidden = "noexec";
472
+ else if (attached === "" || attached === "on") args.echoHidden = "on";
473
+ else return {
474
+ kind: "invalid-value",
475
+ message: `invalid value for --echo-hidden: "${attached}"`
476
+ };
477
+ i += 1;
478
+ continue;
479
+ }
480
+ const short = LONG_MAP[name];
481
+ if (short === void 0) return {
482
+ kind: "invalid-option",
483
+ message: `unrecognized option: --${name}`
484
+ };
485
+ const requiresArg = LONG_WITH_ARG.has(name);
486
+ const optionalArg = LONG_OPTIONAL_ARG.has(name);
487
+ let value = attached;
488
+ if (requiresArg && value === void 0) {
489
+ if (i + 1 >= n) return {
490
+ kind: "missing-arg",
491
+ message: `option requires an argument: --${name}`
492
+ };
493
+ i += 1;
494
+ value = argv[i];
495
+ } else if (!requiresArg && !optionalArg && value !== void 0) return {
496
+ kind: "invalid-option",
497
+ message: `option does not take an argument: --${name}`
498
+ };
499
+ const err = applyShort(short, value, `--${name}`);
500
+ if (err) return err;
501
+ i += 1;
502
+ continue;
503
+ }
504
+ if (tok.startsWith("-") && tok.length > 1) {
505
+ let k = 1;
506
+ while (k < tok.length) {
507
+ const letter = tok[k];
508
+ if (!SHORT_WITH_ARG.has(letter) && !SHORT_NO_ARG.has(letter)) return {
509
+ kind: "invalid-option",
510
+ message: `invalid option -- ${letter}`
511
+ };
512
+ if (SHORT_WITH_ARG.has(letter)) {
513
+ const remainder = tok.slice(k + 1);
514
+ const err = applyShort(letter, remainder.length > 0 ? remainder : void 0, letter);
515
+ if (err) return err;
516
+ k = tok.length;
517
+ break;
518
+ }
519
+ const err = applyShort(letter, void 0, letter);
520
+ if (err) return err;
521
+ k += 1;
522
+ }
523
+ i += 1;
524
+ continue;
525
+ }
526
+ args.positional.push(tok);
527
+ i += 1;
528
+ }
529
+ return {
530
+ ...args,
531
+ actions,
532
+ variables,
533
+ pset
534
+ };
631
535
  };
632
- export const applyStartupArgs = (args, settings, baseConnectOpts, resolution) => {
633
- // -------- 0) Constant client-version vars. -----------------------------
634
- // Seed VERSION / VERSION_NAME / VERSION_NUM once (upstream seeds these from
635
- // its compiled-in PG_VERSION before reading psqlrc / CLI vars). Done before
636
- // the `-v` loop so a user-supplied `-v VERSION=…` still wins.
637
- setStartupVars(settings.vars, CLIENT_VERSION);
638
- // -------- 1) Variable assignments. -------------------------------------
639
- for (const v of args.variables) {
640
- if (v.value === "") {
641
- // Mirror upstream `-v NAME` (no `=`): DeleteVariable.
642
- settings.vars.unset(v.name);
643
- }
644
- else {
645
- settings.vars.set(v.name, v.value);
646
- }
647
- }
648
- // -------- 2) Settings flags. -------------------------------------------
649
- if (args.echoAll)
650
- settings.vars.set("ECHO", "all");
651
- if (args.echoErrors)
652
- settings.vars.set("ECHO", "errors");
653
- if (args.echoQueries)
654
- settings.vars.set("ECHO", "queries");
655
- if (args.echoHidden !== "off") {
656
- settings.echoHidden = args.echoHidden;
657
- settings.vars.set("ECHO_HIDDEN", args.echoHidden === "noexec" ? "noexec" : "on");
658
- }
659
- if (args.quiet) {
660
- settings.quiet = true;
661
- settings.vars.set("QUIET", "on");
662
- }
663
- if (args.singleline) {
664
- settings.singleline = true;
665
- settings.vars.set("SINGLELINE", "on");
666
- }
667
- if (args.singlestep) {
668
- settings.singlestep = true;
669
- settings.vars.set("SINGLESTEP", "on");
670
- }
671
- if (args.onErrorStop) {
672
- settings.onErrorStop = true;
673
- settings.vars.set("ON_ERROR_STOP", "on");
674
- }
675
- // -------- 3) Output formatting. ----------------------------------------
676
- if (args.noAlign) {
677
- settings.popt.topt.format = "unaligned";
678
- }
679
- if (args.htmlMode) {
680
- settings.popt.topt.format = "html";
681
- }
682
- if (args.csvOutput) {
683
- settings.popt.topt.format = "csv";
684
- }
685
- if (args.tuplesOnly) {
686
- settings.popt.topt.tuplesOnly = true;
687
- }
688
- if (args.expanded) {
689
- settings.popt.topt.expanded = "on";
690
- }
691
- if (args.fieldSepZero) {
692
- settings.popt.topt.fieldSep = "\0";
693
- }
694
- else if (args.fieldSep !== undefined) {
695
- settings.popt.topt.fieldSep = args.fieldSep;
696
- }
697
- if (args.recordSepZero) {
698
- settings.popt.topt.recordSep = "\0";
699
- }
700
- else if (args.recordSep !== undefined) {
701
- settings.popt.topt.recordSep = args.recordSep;
702
- }
703
- if (args.noPager) {
704
- settings.popt.topt.pager = "off";
705
- }
706
- // -------- 4) pset directives. ------------------------------------------
707
- // We don't have a parsed `do_pset` here yet (lives in command/cmd_format),
708
- // so we surface them as raw strings and let the integration layer feed
709
- // them through once parsed. For tableattr (the most common -T use), apply
710
- // directly.
711
- for (const directive of args.pset) {
712
- const eq = directive.indexOf("=");
713
- if (eq >= 0) {
714
- const key = directive.slice(0, eq);
715
- const val = directive.slice(eq + 1);
716
- if (key === "tableattr" || key === "T") {
717
- settings.popt.topt.tableAttr = val.length > 0 ? val : null;
718
- }
719
- else if (key === "pager") {
720
- if (val === "on" || val === "off" || val === "always") {
721
- settings.popt.topt.pager = val;
722
- }
723
- }
724
- // Other directives left to the broader -P plumbing.
725
- }
726
- }
727
- // -------- 5) Connection overrides. -------------------------------------
728
- // Positional args follow upstream: positional[0] → dbname, positional[1] →
729
- // username (if not already set by a flag).
730
- let dbname = args.database;
731
- let username = args.user;
732
- if (args.positional[0] && !dbname)
733
- dbname = args.positional[0];
734
- if (args.positional[1] && !username)
735
- username = args.positional[1];
736
- // The argv flag layer mirrors the highest-priority CLI inputs (`-h`/`-p`
737
- // / positional DBNAME / etc). Only populated entries end up in the layer
738
- // so they don't accidentally clobber URI/env values.
739
- // Seed with the full `-d` partial (carries ssl*/channelBinding/hostaddr/
740
- // requireAuth — review #6), then overlay the explicit flag/positional
741
- // values so `-h`/`-p`/`-U`/`-W`/positional still take precedence over `-d`.
742
- const argvLayer = { ...(args.dashD ?? {}) };
743
- if (args.host !== undefined)
744
- argvLayer.host = args.host;
745
- if (args.port !== undefined)
746
- argvLayer.port = args.port;
747
- if (username !== undefined)
748
- argvLayer.user = username;
749
- if (dbname !== undefined)
750
- argvLayer.database = dbname;
751
- if (args.password !== undefined)
752
- argvLayer.password = args.password;
753
- if (args.replication !== undefined)
754
- argvLayer.replication = args.replication;
755
- // Two modes:
756
- // - LEGACY: no `resolution` provided → behave exactly like before, with
757
- // `baseConnectOpts` supplying every default and argv overriding it.
758
- // - LAYERED: `resolution` provided → run the full vanilla-psql chain.
759
- let connect;
760
- if (resolution === undefined) {
761
- const base = baseConnectOpts ?? {
762
- host: "localhost",
763
- port: 5432,
764
- user: "",
765
- database: "",
766
- ssl: "prefer",
767
- };
768
- connect = { ...base, ...argvLayer };
769
- }
770
- else {
771
- connect = resolveLayeredConnect(argvLayer, resolution, baseConnectOpts);
772
- }
773
- return { connect, preActions: args.actions };
536
+ const applyStartupArgs = (args, settings, baseConnectOpts, resolution) => {
537
+ setStartupVars(settings.vars, CLIENT_VERSION);
538
+ for (const v of args.variables) if (v.value === "") settings.vars.unset(v.name);
539
+ else settings.vars.set(v.name, v.value);
540
+ if (args.echoAll) settings.vars.set("ECHO", "all");
541
+ if (args.echoErrors) settings.vars.set("ECHO", "errors");
542
+ if (args.echoQueries) settings.vars.set("ECHO", "queries");
543
+ if (args.echoHidden !== "off") {
544
+ settings.echoHidden = args.echoHidden;
545
+ settings.vars.set("ECHO_HIDDEN", args.echoHidden === "noexec" ? "noexec" : "on");
546
+ }
547
+ if (args.quiet) {
548
+ settings.quiet = true;
549
+ settings.vars.set("QUIET", "on");
550
+ }
551
+ if (args.singleline) {
552
+ settings.singleline = true;
553
+ settings.vars.set("SINGLELINE", "on");
554
+ }
555
+ if (args.singlestep) {
556
+ settings.singlestep = true;
557
+ settings.vars.set("SINGLESTEP", "on");
558
+ }
559
+ if (args.onErrorStop) {
560
+ settings.onErrorStop = true;
561
+ settings.vars.set("ON_ERROR_STOP", "on");
562
+ }
563
+ if (args.noAlign) settings.popt.topt.format = "unaligned";
564
+ if (args.htmlMode) settings.popt.topt.format = "html";
565
+ if (args.csvOutput) settings.popt.topt.format = "csv";
566
+ if (args.tuplesOnly) settings.popt.topt.tuplesOnly = true;
567
+ if (args.expanded) settings.popt.topt.expanded = "on";
568
+ if (args.fieldSepZero) settings.popt.topt.fieldSep = "\0";
569
+ else if (args.fieldSep !== void 0) settings.popt.topt.fieldSep = args.fieldSep;
570
+ if (args.recordSepZero) settings.popt.topt.recordSep = "\0";
571
+ else if (args.recordSep !== void 0) settings.popt.topt.recordSep = args.recordSep;
572
+ if (args.noPager) settings.popt.topt.pager = "off";
573
+ for (const directive of args.pset) {
574
+ const eq = directive.indexOf("=");
575
+ if (eq >= 0) {
576
+ const key = directive.slice(0, eq);
577
+ const val = directive.slice(eq + 1);
578
+ if (key === "tableattr" || key === "T") settings.popt.topt.tableAttr = val.length > 0 ? val : null;
579
+ else if (key === "pager") {
580
+ if (val === "on" || val === "off" || val === "always") settings.popt.topt.pager = val;
581
+ }
582
+ }
583
+ }
584
+ let dbname = args.database;
585
+ let username = args.user;
586
+ if (args.positional[0] && !dbname) dbname = args.positional[0];
587
+ if (args.positional[1] && !username) username = args.positional[1];
588
+ const argvLayer = { ...args.dashD ?? {} };
589
+ if (args.host !== void 0) argvLayer.host = args.host;
590
+ if (args.port !== void 0) argvLayer.port = args.port;
591
+ if (username !== void 0) argvLayer.user = username;
592
+ if (dbname !== void 0) argvLayer.database = dbname;
593
+ if (args.password !== void 0) argvLayer.password = args.password;
594
+ if (args.replication !== void 0) argvLayer.replication = args.replication;
595
+ let connect;
596
+ if (resolution === void 0) connect = {
597
+ ...baseConnectOpts ?? {
598
+ host: "localhost",
599
+ port: 5432,
600
+ user: "",
601
+ database: "",
602
+ ssl: "prefer"
603
+ },
604
+ ...argvLayer
605
+ };
606
+ else connect = resolveLayeredConnect(argvLayer, resolution, baseConnectOpts);
607
+ return {
608
+ connect,
609
+ preActions: args.actions
610
+ };
774
611
  };
775
612
  /**
776
- * Run the layered connection-parameter merge.
777
- *
778
- * argv > URI partial > PG* env > .pgpass (password only) > service > libpq defaults
779
- *
780
- * `baseConnectOpts` (the legacy `ConnectOptions` argument) is treated as a
781
- * pre-baked URI partial when the resolution path is used WITHOUT an explicit
782
- * `uriPartial`. This keeps the door open for callers that still want to
783
- * pass a fully-defaulted URI shape from `parseConnectionUri`.
784
- */
613
+ * Run the layered connection-parameter merge.
614
+ *
615
+ * argv > URI partial > PG* env > .pgpass (password only) > service > libpq defaults
616
+ *
617
+ * `baseConnectOpts` (the legacy `ConnectOptions` argument) is treated as a
618
+ * pre-baked URI partial when the resolution path is used WITHOUT an explicit
619
+ * `uriPartial`. This keeps the door open for callers that still want to
620
+ * pass a fully-defaulted URI shape from `parseConnectionUri`.
621
+ */
785
622
  const resolveLayeredConnect = (argvLayer, resolution, legacyBase) => {
786
- const env = resolution.env ?? {};
787
- const uriPartial = resolution.uriPartial ??
788
- (legacyBase !== undefined
789
- ? legacyBase
790
- : {});
791
- // Pick the service entry: explicit `serviceName` argument first, else fall
792
- // back to `$PGSERVICE`. The URI parser threads `?service=` through to its
793
- // caller via this same field, so callers should populate `serviceName`
794
- // from there before invoking us.
795
- const serviceName = resolution.serviceName ?? env.PGSERVICE;
796
- let serviceEntry;
797
- if (serviceName !== undefined && serviceName !== "") {
798
- serviceEntry = resolution.services?.get(serviceName);
799
- if (serviceEntry === undefined) {
800
- // Mirror libpq: if the user explicitly named a service (via
801
- // `?service=`, conninfo `service=`, or `$PGSERVICE`) and it isn't
802
- // found in any loaded service file, fail fast with the exact
803
- // upstream wording. Without this the empty service layer would
804
- // silently degrade to a different connection target — surprising
805
- // and very hard to debug.
806
- throw new Error(`definition of service "${serviceName}" not found`);
807
- }
808
- }
809
- const serviceLayer = serviceEntry !== undefined
810
- ? serviceEntryToConnectOptions(serviceEntry)
811
- : {};
812
- // Build the layer stack (highest priority first).
813
- const envLayer = envConnectionDefaults(env);
814
- const defaults = libpqConnectionDefaults(env);
815
- const layers = [
816
- argvLayer,
817
- uriPartial,
818
- envLayer,
819
- serviceLayer,
820
- ];
821
- // First-pass merge (without pgpass) so we know what host/port/db/user
822
- // ended up as. pgpass is a password-only layer that depends on the
823
- // resolved target.
824
- let merged = mergeConnectOptions(layers, defaults);
825
- if (merged.password === undefined) {
826
- const pgpassPwd = lookupPgPass(resolution.pgpassEntries ?? [], {
827
- host: merged.host,
828
- port: merged.port,
829
- database: merged.database !== "" ? merged.database : merged.user,
830
- user: merged.user,
831
- });
832
- if (pgpassPwd !== undefined) {
833
- // Re-merge with pgpass slotted in just above libpq defaults so any
834
- // explicit `password=` in env / service still wins.
835
- const pgpassLayer = {
836
- password: pgpassPwd,
837
- };
838
- merged = mergeConnectOptions([...layers, pgpassLayer], defaults);
839
- }
840
- }
841
- return merged;
623
+ const env = resolution.env ?? {};
624
+ const uriPartial = resolution.uriPartial ?? (legacyBase !== void 0 ? legacyBase : {});
625
+ const serviceName = resolution.serviceName ?? env.PGSERVICE;
626
+ let serviceEntry;
627
+ if (serviceName !== void 0 && serviceName !== "") {
628
+ serviceEntry = resolution.services?.get(serviceName);
629
+ if (serviceEntry === void 0) throw new Error(`definition of service "${serviceName}" not found`);
630
+ }
631
+ const serviceLayer = serviceEntry !== void 0 ? serviceEntryToConnectOptions(serviceEntry) : {};
632
+ const envLayer = envConnectionDefaults(env);
633
+ const defaults = libpqConnectionDefaults(env);
634
+ const layers = [
635
+ argvLayer,
636
+ uriPartial,
637
+ envLayer,
638
+ serviceLayer
639
+ ];
640
+ let merged = mergeConnectOptions(layers, defaults);
641
+ if (merged.password === void 0) {
642
+ const pgpassPwd = lookupPgPass(resolution.pgpassEntries ?? [], {
643
+ host: merged.host,
644
+ port: merged.port,
645
+ database: merged.database !== "" ? merged.database : merged.user,
646
+ user: merged.user
647
+ });
648
+ if (pgpassPwd !== void 0) {
649
+ const pgpassLayer = { password: pgpassPwd };
650
+ merged = mergeConnectOptions([...layers, pgpassLayer], defaults);
651
+ }
652
+ }
653
+ return merged;
842
654
  };
843
- // ---------------------------------------------------------------------------
844
- // Error-formatting helper for the integration layer.
845
- // ---------------------------------------------------------------------------
846
- export const formatParseError = (err) => err.message;
655
+ const formatParseError = (err) => err.message;
656
+ //#endregion
657
+ export { CLIENT_VERSION, applyStartupArgs, formatParseError, parseStartupArgs };