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,422 +1,347 @@
1
- /**
2
- * `.psqlrc` autoload.
3
- *
4
- * TypeScript port of `process_psqlrc()` / `process_psqlrc_file()` in
5
- * `src/bin/psql/startup.c`. Two responsibilities:
6
- *
7
- * 1. `defaultPsqlrcPath(env?)` — pure function returning the path psql
8
- * *would* read in the absence of a `$PSQLRC` override. Resolves to
9
- * `$HOME/.psqlrc` on POSIX and `%APPDATA%/postgresql/psqlrc.conf` on
10
- * Windows (the upstream layout).
11
- *
12
- * 2. `loadPsqlrc(ctx)` — discover and execute the rc files in upstream
13
- * order:
14
- * a. `$PGSYSCONFDIR/psqlrc-VERSION` and `$PGSYSCONFDIR/psqlrc`
15
- * b. `$PSQLRC` if non-empty (overrides $HOME path)
16
- * c. else `$HOME/.psqlrc-VERSION` then `$HOME/.psqlrc`
17
- * Each existing file's contents are split into statements via the same
18
- * `scanSql` boundary detector the mainloop uses, then dispatched
19
- * through `connection.execSimple` (for SQL) or the backslash registry
20
- * (for `\…` commands).
21
- *
22
- * Duplication note: we deliberately re-implement a minimal scan-and-dispatch
23
- * loop here instead of routing through `core/mainloop.ts`. The mainloop is
24
- * locked-down in WP-12 and adding an `executeInputString` helper to it would
25
- * touch a file we can't modify in this WP. The two loops share `scanSql` and
26
- * `dispatchBackslash` so the divergence is structural, not behavioural.
27
- *
28
- * Follow-up issue: refactor `mainloop.ts` to expose an `executeInputString`
29
- * primitive and delete this duplication. Tracked TODO at the bottom of this
30
- * file.
31
- */
32
- import { promises as fs } from "node:fs";
33
- import * as path from "node:path";
34
- import { attachCondStack, COND_COMMAND_NAMES } from "../command/cmd_cond.js";
35
- import { dispatchBackslash } from "../command/dispatch.js";
36
- import { sendQuery } from "../core/common.js";
1
+ import { COND_COMMAND_NAMES, attachCondStack } from "../command/cmd_cond.js";
37
2
  import { scanSlashArgs } from "../scanner/slash.js";
38
- import { scanSql } from "../scanner/sql.js";
3
+ import { sendQuery } from "../core/common.js";
4
+ import { dispatchBackslash } from "../command/dispatch.js";
39
5
  import { initialScanState } from "../types/scanner.js";
40
- // ---------------------------------------------------------------------------
41
- // Path discovery.
42
- // ---------------------------------------------------------------------------
6
+ import { scanSql } from "../scanner/sql.js";
7
+ import { promises } from "node:fs";
8
+ import * as path from "node:path";
9
+ //#region src/psql/io/psqlrc.ts
10
+ /**
11
+ * `.psqlrc` autoload.
12
+ *
13
+ * TypeScript port of `process_psqlrc()` / `process_psqlrc_file()` in
14
+ * `src/bin/psql/startup.c`. Two responsibilities:
15
+ *
16
+ * 1. `defaultPsqlrcPath(env?)` — pure function returning the path psql
17
+ * *would* read in the absence of a `$PSQLRC` override. Resolves to
18
+ * `$HOME/.psqlrc` on POSIX and `%APPDATA%/postgresql/psqlrc.conf` on
19
+ * Windows (the upstream layout).
20
+ *
21
+ * 2. `loadPsqlrc(ctx)` — discover and execute the rc files in upstream
22
+ * order:
23
+ * a. `$PGSYSCONFDIR/psqlrc-VERSION` and `$PGSYSCONFDIR/psqlrc`
24
+ * b. `$PSQLRC` if non-empty (overrides $HOME path)
25
+ * c. else `$HOME/.psqlrc-VERSION` then `$HOME/.psqlrc`
26
+ * Each existing file's contents are split into statements via the same
27
+ * `scanSql` boundary detector the mainloop uses, then dispatched
28
+ * through `connection.execSimple` (for SQL) or the backslash registry
29
+ * (for `\…` commands).
30
+ *
31
+ * Duplication note: we deliberately re-implement a minimal scan-and-dispatch
32
+ * loop here instead of routing through `core/mainloop.ts`. The mainloop is
33
+ * locked-down in WP-12 and adding an `executeInputString` helper to it would
34
+ * touch a file we can't modify in this WP. The two loops share `scanSql` and
35
+ * `dispatchBackslash` so the divergence is structural, not behavioural.
36
+ *
37
+ * Follow-up issue: refactor `mainloop.ts` to expose an `executeInputString`
38
+ * primitive and delete this duplication. Tracked TODO at the bottom of this
39
+ * file.
40
+ */
43
41
  const isWindows = process.platform === "win32";
44
42
  const baseRcName = isWindows ? "psqlrc.conf" : ".psqlrc";
45
43
  /**
46
- * Return the default user-level psqlrc path. The path is *not* verified to
47
- * exist — callers stat it lazily. The Windows layout follows upstream:
48
- * `%APPDATA%/postgresql/psqlrc.conf`. On POSIX we use `$HOME/.psqlrc`.
49
- */
50
- export const defaultPsqlrcPath = (env = process.env) => {
51
- if (isWindows) {
52
- const appData = env.APPDATA;
53
- if (appData && appData.length > 0) {
54
- return path.join(appData, "postgresql", baseRcName);
55
- }
56
- return path.join(env.USERPROFILE ?? "", "postgresql", baseRcName);
57
- }
58
- const home = env.HOME ?? "";
59
- return path.join(home, baseRcName);
44
+ * Return the default user-level psqlrc path. The path is *not* verified to
45
+ * exist — callers stat it lazily. The Windows layout follows upstream:
46
+ * `%APPDATA%/postgresql/psqlrc.conf`. On POSIX we use `$HOME/.psqlrc`.
47
+ */
48
+ const defaultPsqlrcPath = (env = process.env) => {
49
+ if (isWindows) {
50
+ const appData = env.APPDATA;
51
+ if (appData && appData.length > 0) return path.join(appData, "postgresql", baseRcName);
52
+ return path.join(env.USERPROFILE ?? "", "postgresql", baseRcName);
53
+ }
54
+ const home = env.HOME ?? "";
55
+ return path.join(home, baseRcName);
60
56
  };
61
57
  const versionSuffix = (serverVersion) => {
62
- if (!serverVersion || serverVersion <= 0)
63
- return null;
64
- // PG numeric version is e.g. 170002 (17.2). Upstream uses both
65
- // `psqlrc-PG_VERSION` (major.minor) and `psqlrc-PG_MAJORVERSION`. We use
66
- // the major (e.g. 17) — the bigger of the two backwards-compat targets.
67
- const major = Math.floor(serverVersion / 10000);
68
- return String(major);
58
+ if (!serverVersion || serverVersion <= 0) return null;
59
+ const major = Math.floor(serverVersion / 1e4);
60
+ return String(major);
69
61
  };
70
- export const psqlrcCandidates = (env, serverVersion) => {
71
- const out = [];
72
- const suffix = versionSuffix(serverVersion);
73
- // System-wide.
74
- const sysDir = env.PGSYSCONFDIR;
75
- if (sysDir && sysDir.length > 0) {
76
- const sysBase = isWindows ? "psqlrc.conf" : "psqlrc";
77
- if (suffix) {
78
- out.push({
79
- path: path.join(sysDir, `${sysBase}-${suffix}`),
80
- description: "system psqlrc (versioned)",
81
- });
82
- }
83
- out.push({
84
- path: path.join(sysDir, sysBase),
85
- description: "system psqlrc",
86
- });
87
- }
88
- // PSQLRC override suppresses HOME-based discovery (matches upstream).
89
- const envRc = env.PSQLRC;
90
- if (envRc !== undefined && envRc !== "") {
91
- out.push({ path: expandTilde(envRc, env), description: "$PSQLRC" });
92
- return out;
93
- }
94
- // HOME-based.
95
- const home = env.HOME ?? "";
96
- if (isWindows) {
97
- const appData = env.APPDATA;
98
- const dir = appData && appData.length > 0
99
- ? path.join(appData, "postgresql")
100
- : path.join(env.USERPROFILE ?? "", "postgresql");
101
- if (suffix) {
102
- out.push({
103
- path: path.join(dir, `${baseRcName}-${suffix}`),
104
- description: "user psqlrc (versioned)",
105
- });
106
- }
107
- out.push({
108
- path: path.join(dir, baseRcName),
109
- description: "user psqlrc",
110
- });
111
- }
112
- else if (home.length > 0) {
113
- if (suffix) {
114
- out.push({
115
- path: path.join(home, `${baseRcName}-${suffix}`),
116
- description: "user psqlrc (versioned)",
117
- });
118
- }
119
- out.push({
120
- path: path.join(home, baseRcName),
121
- description: "user psqlrc",
122
- });
123
- }
124
- return out;
62
+ const psqlrcCandidates = (env, serverVersion) => {
63
+ const out = [];
64
+ const suffix = versionSuffix(serverVersion);
65
+ const sysDir = env.PGSYSCONFDIR;
66
+ if (sysDir && sysDir.length > 0) {
67
+ const sysBase = isWindows ? "psqlrc.conf" : "psqlrc";
68
+ if (suffix) out.push({
69
+ path: path.join(sysDir, `${sysBase}-${suffix}`),
70
+ description: "system psqlrc (versioned)"
71
+ });
72
+ out.push({
73
+ path: path.join(sysDir, sysBase),
74
+ description: "system psqlrc"
75
+ });
76
+ }
77
+ const envRc = env.PSQLRC;
78
+ if (envRc !== void 0 && envRc !== "") {
79
+ out.push({
80
+ path: expandTilde(envRc, env),
81
+ description: "$PSQLRC"
82
+ });
83
+ return out;
84
+ }
85
+ const home = env.HOME ?? "";
86
+ if (isWindows) {
87
+ const appData = env.APPDATA;
88
+ const dir = appData && appData.length > 0 ? path.join(appData, "postgresql") : path.join(env.USERPROFILE ?? "", "postgresql");
89
+ if (suffix) out.push({
90
+ path: path.join(dir, `${baseRcName}-${suffix}`),
91
+ description: "user psqlrc (versioned)"
92
+ });
93
+ out.push({
94
+ path: path.join(dir, baseRcName),
95
+ description: "user psqlrc"
96
+ });
97
+ } else if (home.length > 0) {
98
+ if (suffix) out.push({
99
+ path: path.join(home, `${baseRcName}-${suffix}`),
100
+ description: "user psqlrc (versioned)"
101
+ });
102
+ out.push({
103
+ path: path.join(home, baseRcName),
104
+ description: "user psqlrc"
105
+ });
106
+ }
107
+ return out;
125
108
  };
126
109
  const expandTilde = (p, env) => {
127
- if (p.startsWith("~/") && env.HOME) {
128
- return path.join(env.HOME, p.slice(2));
129
- }
130
- if (p === "~" && env.HOME) {
131
- return env.HOME;
132
- }
133
- return p;
110
+ if (p.startsWith("~/") && env.HOME) return path.join(env.HOME, p.slice(2));
111
+ if (p === "~" && env.HOME) return env.HOME;
112
+ return p;
134
113
  };
135
- // ---------------------------------------------------------------------------
136
- // File read + dispatch.
137
- // ---------------------------------------------------------------------------
138
114
  const readIfExists = async (file) => {
139
- try {
140
- return await fs.readFile(file, "utf8");
141
- }
142
- catch (err) {
143
- const code = err.code;
144
- if (code === "ENOENT" || code === "ENOTDIR")
145
- return null;
146
- // Permission / I/O errors propagate — they're worth surfacing because
147
- // they signal a likely misconfiguration. Mirror upstream which prints
148
- // a warning but doesn't abort the session; we use a noop for now.
149
- return null;
150
- }
115
+ try {
116
+ return await promises.readFile(file, "utf8");
117
+ } catch (err) {
118
+ const code = err.code;
119
+ if (code === "ENOENT" || code === "ENOTDIR") return null;
120
+ return null;
121
+ }
151
122
  };
152
123
  const makeBackslashContext = (ctx, cmdName, rawArgs, queryBuf) => {
153
- const varLookup = (name) => ctx.settings.vars.get(name);
154
- const buffered = new Map();
155
- const cursors = new Map();
156
- const argsFor = (mode) => {
157
- const cached = buffered.get(mode);
158
- if (cached)
159
- return cached;
160
- const parsed = scanSlashArgs(rawArgs, mode, varLookup);
161
- buffered.set(mode, parsed);
162
- return parsed;
163
- };
164
- const bctx = {
165
- settings: ctx.settings,
166
- cmdName,
167
- queryBuf,
168
- rawArgs,
169
- nextArg(mode = "normal") {
170
- const av = argsFor(mode);
171
- const idx = cursors.get(mode) ?? 0;
172
- if (idx >= av.length)
173
- return null;
174
- cursors.set(mode, idx + 1);
175
- return av[idx];
176
- },
177
- restOfLine() {
178
- return rawArgs;
179
- },
180
- };
181
- attachCondStack(bctx, ctx.cond);
182
- return bctx;
124
+ const varLookup = (name) => ctx.settings.vars.get(name);
125
+ const buffered = /* @__PURE__ */ new Map();
126
+ const cursors = /* @__PURE__ */ new Map();
127
+ const argsFor = (mode) => {
128
+ const cached = buffered.get(mode);
129
+ if (cached) return cached;
130
+ const parsed = scanSlashArgs(rawArgs, mode, varLookup);
131
+ buffered.set(mode, parsed);
132
+ return parsed;
133
+ };
134
+ const bctx = {
135
+ settings: ctx.settings,
136
+ cmdName,
137
+ queryBuf,
138
+ rawArgs,
139
+ nextArg(mode = "normal") {
140
+ const av = argsFor(mode);
141
+ const idx = cursors.get(mode) ?? 0;
142
+ if (idx >= av.length) return null;
143
+ cursors.set(mode, idx + 1);
144
+ return av[idx];
145
+ },
146
+ restOfLine() {
147
+ return rawArgs;
148
+ }
149
+ };
150
+ attachCondStack(bctx, ctx.cond);
151
+ return bctx;
183
152
  };
184
153
  /**
185
- * Execute the supplied input string against the running REPL context. Used
186
- * by `loadPsqlrc` (and exported for tests). This is a minimal cousin of
187
- * `runMainLoop`'s processChunk — see the module header for the duplication
188
- * rationale.
189
- *
190
- * Returns when EOF is reached; `\q` and other exit commands inside an rc
191
- * file are treated as "stop reading this file" (the REPL itself continues).
192
- *
193
- * The returned outcome lets `runPsql`'s `-c`/`-f` loop apply upstream's
194
- * per-switch exit-code semantics (see comment above).
195
- */
196
- export const executeInputString = async (input, ctx, opts = {}) => {
197
- let working = input;
198
- let queryBuf = "";
199
- let scanState = initialScanState();
200
- let hadError = false;
201
- let stoppedOnError = false;
202
- let connectionLost = false;
203
- const print = opts.print ?? false;
204
- const noteConnectionLost = () => {
205
- if (ctx.settings.db?.isClosed()) {
206
- ctx.stderr.write("psql: error: connection to server was lost\n");
207
- connectionLost = true;
208
- return true;
209
- }
210
- return false;
211
- };
212
- // Inline `:NAME` substitution wired through SQL bodies (handled by
213
- // scanSql). Slash-arg bodies are already substituted via
214
- // `makeBackslashContext` above. Mirror the mainloop's wiring so `-c`/`-f`
215
- // / .psqlrc inputs expand the same way as interactive input.
216
- const sqlVarLookup = (name) => ctx.settings.vars.get(name);
217
- while (working.length > 0) {
218
- const r = scanSql(working, scanState, sqlVarLookup);
219
- scanState = r.nextState;
220
- if (r.kind === "semicolon") {
221
- // Use the substituted text rather than the raw slice so `:NAME`
222
- // expansions land in the executed SQL.
223
- const sqlText = queryBuf + r.sql;
224
- queryBuf = "";
225
- working = working.slice(r.consumed);
226
- scanState = initialScanState();
227
- if (!ctx.cond.isActive())
228
- continue;
229
- const trimmed = sqlText.trim();
230
- if (trimmed.length === 0)
231
- continue;
232
- if (!ctx.settings.db)
233
- continue;
234
- if (print) {
235
- // `-c` / `-f`: route through the full SendQuery pipeline so SELECT
236
- // tuples, NOTICE messages and `\timing` land on the output streams.
237
- const stats = await sendQuery(ctx, sqlText);
238
- // LATCH: a multi-statement input must report an error if ANY statement
239
- // failed, not just the last one — otherwise `-c "SELECT 1; SELECT 1/0;
240
- // SELECT 2"` exits 0 and silently passes CI. The
241
- // -f/psqlrc callers ignore this aggregate (they only act on
242
- // stoppedOnError), so latching is safe across all callers.
243
- if (stats.hadError)
244
- hadError = true;
245
- if (noteConnectionLost()) {
246
- return { hadError, stoppedOnError, connectionLost };
247
- }
248
- if (stats.hadError && ctx.settings.onErrorStop) {
249
- stoppedOnError = true;
250
- return { hadError, stoppedOnError, connectionLost };
251
- }
252
- }
253
- else {
254
- try {
255
- await ctx.settings.db.execSimple(sqlText);
256
- }
257
- catch (err) {
258
- const msg = err instanceof Error ? err.message : String(err);
259
- ctx.stderr.write(`psql: ERROR: ${msg}\n`);
260
- hadError = true;
261
- if (noteConnectionLost()) {
262
- return { hadError, stoppedOnError, connectionLost };
263
- }
264
- if (ctx.settings.onErrorStop) {
265
- stoppedOnError = true;
266
- return { hadError, stoppedOnError, connectionLost };
267
- }
268
- }
269
- }
270
- continue;
271
- }
272
- if (r.kind === "backslash") {
273
- // Use `r.sql` — the scanner's substituted/processed text that
274
- // *preceded* the backslash boundary. The raw consumed slice would
275
- // also include the backslash command itself and any separators
276
- // (like the `\\` end-of-args marker), which we don't want in the
277
- // query buffer. `r.sql` excludes both. Mirrors the mainloop's
278
- // `queryBuf += result.sql` path.
279
- queryBuf += r.sql;
280
- working = working.slice(r.consumed);
281
- const cmdName = r.cmd;
282
- // Skip cond-commands inside rc — they require the full mainloop state
283
- // machine to be useful; we treat them as no-ops here. Other commands
284
- // run through the standard registry.
285
- if (COND_COMMAND_NAMES.has(cmdName))
286
- continue;
287
- if (!ctx.cond.isActive())
288
- continue;
289
- const bctx = makeBackslashContext(ctx, cmdName, r.rest, queryBuf);
290
- let res;
291
- try {
292
- res = await dispatchBackslash(ctx.registry, cmdName, bctx);
293
- }
294
- catch (err) {
295
- const msg = err instanceof Error ? err.message : String(err);
296
- ctx.stderr.write(`psql: ERROR: ${msg}\n`);
297
- hadError = true;
298
- if (ctx.settings.onErrorStop) {
299
- stoppedOnError = true;
300
- return { hadError, stoppedOnError, connectionLost };
301
- }
302
- continue;
303
- }
304
- if (res.status === "exit")
305
- return { hadError, stoppedOnError, connectionLost };
306
- if (res.status === "reset-buf") {
307
- queryBuf = res.newBuf ?? "";
308
- scanState = initialScanState();
309
- }
310
- // Mirror upstream `mainloop.c`: a backslash command that errored
311
- // drops the query buffer and resets the scanner. Without this the
312
- // residue would re-execute via the tail-dispatch at EOF (or get
313
- // glued onto the next dispatched statement), masking the failure.
314
- if (res.status === "error") {
315
- queryBuf = "";
316
- scanState = initialScanState();
317
- }
318
- if (res.status === "error")
319
- hadError = true; // latch
320
- if (noteConnectionLost()) {
321
- return { hadError, stoppedOnError, connectionLost };
322
- }
323
- if (res.status === "error" && ctx.settings.onErrorStop) {
324
- stoppedOnError = true;
325
- return { hadError, stoppedOnError, connectionLost };
326
- }
327
- continue;
328
- }
329
- // eof / incomplete: stash residue and stop. Use `r.sql` so any `:NAME`
330
- // tokens fully consumed in this chunk make it into the buffer in
331
- // substituted form. (Same caveat as the mainloop: cross-chunk colon
332
- // references fall back to the literal.)
333
- queryBuf += r.sql;
334
- working = "";
335
- }
336
- // Tail dispatch: if the file ended mid-statement (no trailing `;`), run
337
- // the residue. This matches `process_file` behaviour in upstream.
338
- const tail = queryBuf.trim();
339
- if (tail.length > 0 && ctx.settings.db && ctx.cond.isActive()) {
340
- if (print) {
341
- const stats = await sendQuery(ctx, queryBuf);
342
- // LATCH, same as the semicolon branch — a trailing statement (no `;`)
343
- // that succeeds must not clear an earlier error, or `-c "bad; ok"`
344
- // wrongly exits 0. The function's latch invariant applies
345
- // to the tail path too.
346
- if (stats.hadError)
347
- hadError = true;
348
- noteConnectionLost();
349
- }
350
- else {
351
- try {
352
- await ctx.settings.db.execSimple(queryBuf);
353
- }
354
- catch (err) {
355
- const msg = err instanceof Error ? err.message : String(err);
356
- ctx.stderr.write(`psql: ERROR: ${msg}\n`);
357
- hadError = true;
358
- noteConnectionLost();
359
- }
360
- }
361
- }
362
- return { hadError, stoppedOnError, connectionLost };
154
+ * Execute the supplied input string against the running REPL context. Used
155
+ * by `loadPsqlrc` (and exported for tests). This is a minimal cousin of
156
+ * `runMainLoop`'s processChunk — see the module header for the duplication
157
+ * rationale.
158
+ *
159
+ * Returns when EOF is reached; `\q` and other exit commands inside an rc
160
+ * file are treated as "stop reading this file" (the REPL itself continues).
161
+ *
162
+ * The returned outcome lets `runPsql`'s `-c`/`-f` loop apply upstream's
163
+ * per-switch exit-code semantics (see comment above).
164
+ */
165
+ const executeInputString = async (input, ctx, opts = {}) => {
166
+ let working = input;
167
+ let queryBuf = "";
168
+ let scanState = initialScanState();
169
+ let hadError = false;
170
+ let stoppedOnError = false;
171
+ let connectionLost = false;
172
+ const print = opts.print ?? false;
173
+ const noteConnectionLost = () => {
174
+ if (ctx.settings.db?.isClosed()) {
175
+ ctx.stderr.write("psql: error: connection to server was lost\n");
176
+ connectionLost = true;
177
+ return true;
178
+ }
179
+ return false;
180
+ };
181
+ const sqlVarLookup = (name) => ctx.settings.vars.get(name);
182
+ while (working.length > 0) {
183
+ const r = scanSql(working, scanState, sqlVarLookup);
184
+ scanState = r.nextState;
185
+ if (r.kind === "semicolon") {
186
+ const sqlText = queryBuf + r.sql;
187
+ queryBuf = "";
188
+ working = working.slice(r.consumed);
189
+ scanState = initialScanState();
190
+ if (!ctx.cond.isActive()) continue;
191
+ if (sqlText.trim().length === 0) continue;
192
+ if (!ctx.settings.db) continue;
193
+ if (print) {
194
+ const stats = await sendQuery(ctx, sqlText);
195
+ if (stats.hadError) hadError = true;
196
+ if (noteConnectionLost()) return {
197
+ hadError,
198
+ stoppedOnError,
199
+ connectionLost
200
+ };
201
+ if (stats.hadError && ctx.settings.onErrorStop) {
202
+ stoppedOnError = true;
203
+ return {
204
+ hadError,
205
+ stoppedOnError,
206
+ connectionLost
207
+ };
208
+ }
209
+ } else try {
210
+ await ctx.settings.db.execSimple(sqlText);
211
+ } catch (err) {
212
+ const msg = err instanceof Error ? err.message : String(err);
213
+ ctx.stderr.write(`psql: ERROR: ${msg}\n`);
214
+ hadError = true;
215
+ if (noteConnectionLost()) return {
216
+ hadError,
217
+ stoppedOnError,
218
+ connectionLost
219
+ };
220
+ if (ctx.settings.onErrorStop) {
221
+ stoppedOnError = true;
222
+ return {
223
+ hadError,
224
+ stoppedOnError,
225
+ connectionLost
226
+ };
227
+ }
228
+ }
229
+ continue;
230
+ }
231
+ if (r.kind === "backslash") {
232
+ queryBuf += r.sql;
233
+ working = working.slice(r.consumed);
234
+ const cmdName = r.cmd;
235
+ if (COND_COMMAND_NAMES.has(cmdName)) continue;
236
+ if (!ctx.cond.isActive()) continue;
237
+ const bctx = makeBackslashContext(ctx, cmdName, r.rest, queryBuf);
238
+ let res;
239
+ try {
240
+ res = await dispatchBackslash(ctx.registry, cmdName, bctx);
241
+ } catch (err) {
242
+ const msg = err instanceof Error ? err.message : String(err);
243
+ ctx.stderr.write(`psql: ERROR: ${msg}\n`);
244
+ hadError = true;
245
+ if (ctx.settings.onErrorStop) {
246
+ stoppedOnError = true;
247
+ return {
248
+ hadError,
249
+ stoppedOnError,
250
+ connectionLost
251
+ };
252
+ }
253
+ continue;
254
+ }
255
+ if (res.status === "exit") return {
256
+ hadError,
257
+ stoppedOnError,
258
+ connectionLost
259
+ };
260
+ if (res.status === "reset-buf") {
261
+ queryBuf = res.newBuf ?? "";
262
+ scanState = initialScanState();
263
+ }
264
+ if (res.status === "error") {
265
+ queryBuf = "";
266
+ scanState = initialScanState();
267
+ }
268
+ if (res.status === "error") hadError = true;
269
+ if (noteConnectionLost()) return {
270
+ hadError,
271
+ stoppedOnError,
272
+ connectionLost
273
+ };
274
+ if (res.status === "error" && ctx.settings.onErrorStop) {
275
+ stoppedOnError = true;
276
+ return {
277
+ hadError,
278
+ stoppedOnError,
279
+ connectionLost
280
+ };
281
+ }
282
+ continue;
283
+ }
284
+ queryBuf += r.sql;
285
+ working = "";
286
+ }
287
+ if (queryBuf.trim().length > 0 && ctx.settings.db && ctx.cond.isActive()) if (print) {
288
+ if ((await sendQuery(ctx, queryBuf)).hadError) hadError = true;
289
+ noteConnectionLost();
290
+ } else try {
291
+ await ctx.settings.db.execSimple(queryBuf);
292
+ } catch (err) {
293
+ const msg = err instanceof Error ? err.message : String(err);
294
+ ctx.stderr.write(`psql: ERROR: ${msg}\n`);
295
+ hadError = true;
296
+ noteConnectionLost();
297
+ }
298
+ return {
299
+ hadError,
300
+ stoppedOnError,
301
+ connectionLost
302
+ };
363
303
  };
364
304
  /**
365
- * Discover and execute `.psqlrc` files against `ctx`. Honors the `-X` flag
366
- * via `opts.skip = true`. Safe to call before the connection is ready —
367
- * commands that need `db` will simply no-op (see `executeInputString`).
368
- */
369
- export const loadPsqlrc = async (ctx, opts = {}) => {
370
- if (opts.skip)
371
- return;
372
- const env = opts.env ?? process.env;
373
- // Single-path mode (test ergonomics / explicit override).
374
- if (opts.path !== undefined) {
375
- const content = await readIfExists(opts.path);
376
- if (content === null)
377
- return;
378
- const prevSource = ctx.settings.curCmdSource;
379
- ctx.settings.curCmdSource = "rcfile";
380
- try {
381
- await executeInputString(content, ctx);
382
- }
383
- finally {
384
- ctx.settings.curCmdSource = prevSource;
385
- }
386
- return;
387
- }
388
- const serverVersion = ctx.settings.db?.serverVersion;
389
- const candidates = psqlrcCandidates(env, serverVersion);
390
- // Upstream `process_psqlrc()` is first-match-wins PER LOCATION: it reads at
391
- // most ONE system file (versioned preferred over unversioned) and then at
392
- // most ONE user file. Running both the versioned AND unversioned file in a
393
- // location double-executes side effects and lets the unversioned file
394
- // clobber the versioned one.
395
- let systemDone = false;
396
- for (const c of candidates) {
397
- const isSystem = c.description.startsWith("system");
398
- if (isSystem && systemDone)
399
- continue; // first system file already ran
400
- const content = await readIfExists(c.path);
401
- if (content === null)
402
- continue;
403
- const prevSource = ctx.settings.curCmdSource;
404
- ctx.settings.curCmdSource = "rcfile";
405
- try {
406
- await executeInputString(content, ctx);
407
- }
408
- finally {
409
- ctx.settings.curCmdSource = prevSource;
410
- }
411
- if (isSystem) {
412
- systemDone = true; // consumed the system location; move to the user one
413
- continue;
414
- }
415
- // First user / $PSQLRC match wins; nothing further to read.
416
- return;
417
- }
305
+ * Discover and execute `.psqlrc` files against `ctx`. Honors the `-X` flag
306
+ * via `opts.skip = true`. Safe to call before the connection is ready —
307
+ * commands that need `db` will simply no-op (see `executeInputString`).
308
+ */
309
+ const loadPsqlrc = async (ctx, opts = {}) => {
310
+ if (opts.skip) return;
311
+ const env = opts.env ?? process.env;
312
+ if (opts.path !== void 0) {
313
+ const content = await readIfExists(opts.path);
314
+ if (content === null) return;
315
+ const prevSource = ctx.settings.curCmdSource;
316
+ ctx.settings.curCmdSource = "rcfile";
317
+ try {
318
+ await executeInputString(content, ctx);
319
+ } finally {
320
+ ctx.settings.curCmdSource = prevSource;
321
+ }
322
+ return;
323
+ }
324
+ const serverVersion = ctx.settings.db?.serverVersion;
325
+ const candidates = psqlrcCandidates(env, serverVersion);
326
+ let systemDone = false;
327
+ for (const c of candidates) {
328
+ const isSystem = c.description.startsWith("system");
329
+ if (isSystem && systemDone) continue;
330
+ const content = await readIfExists(c.path);
331
+ if (content === null) continue;
332
+ const prevSource = ctx.settings.curCmdSource;
333
+ ctx.settings.curCmdSource = "rcfile";
334
+ try {
335
+ await executeInputString(content, ctx);
336
+ } finally {
337
+ ctx.settings.curCmdSource = prevSource;
338
+ }
339
+ if (isSystem) {
340
+ systemDone = true;
341
+ continue;
342
+ }
343
+ return;
344
+ }
418
345
  };
419
- // TODO(WP-26-followup): Refactor `core/mainloop.ts` to export an
420
- // `executeInputString(input, ctx)` primitive, then have this module call it
421
- // instead of duplicating the scan-and-dispatch loop. The duplication today is
422
- // roughly 60 LOC; the refactor would shrink this file by half.
346
+ //#endregion
347
+ export { defaultPsqlrcPath, executeInputString, loadPsqlrc, psqlrcCandidates };