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,116 +1,73 @@
1
+ //#region src/psql/core/syncVars.ts
2
+ const asString = (value) => typeof value === "string" ? value : void 0;
3
+ const asPort = (value) => typeof value === "number" && Number.isFinite(value) ? String(value) : void 0;
1
4
  /**
2
- * Connection-driven psql variable sync.
3
- *
4
- * TypeScript port of `SyncVariables()` in upstream PostgreSQL's
5
- * `src/bin/psql/command.c`. After every successful connection (the initial
6
- * connect in `runPsql` and each `\c` reconnect), psql refreshes the set of
7
- * read-only "connection" variables that scripts interpolate with `:DBNAME`,
8
- * `:USER`, etc. Without this the names interpolate to their literal text
9
- * (`:USER` → `:USER`) instead of the live connection facts.
10
- *
11
- * The variables synced here mirror upstream's `SyncVariables()`:
12
- *
13
- * - `DBNAME` — connection database
14
- * - `USER` — connection user
15
- * - `HOST` — host the client connected to (a socket directory
16
- * for a Unix-domain socket — starts with `/`)
17
- * - `PORT` — connection port (stringified)
18
- * - `ENCODING` — client encoding name (the `client_encoding`
19
- * ParameterStatus). The mainloop keeps this in
20
- * sync on later `SET client_encoding`; we seed it
21
- * here so it is correct from the first prompt.
22
- * - `SERVER_VERSION_NAME`— the `server_version` ParameterStatus (e.g. `18.4`)
23
- * - `SERVER_VERSION_NUM` — the numeric server version (e.g. `180004`),
24
- * stringified from `Connection.serverVersion`
25
- *
26
- * The CLIENT version variables (`VERSION` / `VERSION_NAME` / `VERSION_NUM`)
27
- * are constant for the life of the process and set once at startup — see
28
- * {@link setStartupVars}.
29
- */
30
- const asString = (value) => typeof value === "string" ? value : undefined;
31
- const asPort = (value) => typeof value === "number" && Number.isFinite(value)
32
- ? String(value)
33
- : undefined;
34
- /**
35
- * Refresh the connection variables (`DBNAME`, `USER`, `HOST`, `PORT`,
36
- * `ENCODING`, `SERVER_VERSION_NAME`, `SERVER_VERSION_NUM`) from the live
37
- * connection. Mirrors upstream `SyncVariables()`. Call after a successful
38
- * initial connect and after each `\c` reconnect.
39
- *
40
- * A variable is only set when the connection actually surfaces a value; this
41
- * leaves any user-set value untouched if the connection cannot report one.
42
- */
43
- export const syncConnectionVars = (vars, conn) => {
44
- const target = conn;
45
- const database = asString(target.database);
46
- if (database !== undefined)
47
- vars.set("DBNAME", database);
48
- const user = asString(target.user);
49
- if (user !== undefined)
50
- vars.set("USER", user);
51
- const host = asString(target.host);
52
- if (host !== undefined)
53
- vars.set("HOST", host);
54
- const port = asPort(target.port);
55
- if (port !== undefined)
56
- vars.set("PORT", port);
57
- // ENCODING tracks the server's `client_encoding` ParameterStatus. The
58
- // mainloop refreshes it after each `SET client_encoding`; seed it here so
59
- // it is populated from the very first prompt (and re-seeded after `\c`).
60
- const encoding = conn.parameterStatus("client_encoding");
61
- if (encoding !== undefined)
62
- vars.set("ENCODING", encoding);
63
- const serverVersionName = conn.parameterStatus("server_version");
64
- if (serverVersionName !== undefined) {
65
- vars.set("SERVER_VERSION_NAME", serverVersionName);
66
- }
67
- // `Connection.serverVersion` is the libpq-style integer (e.g. 180004 for
68
- // 18.4); 0 means "not yet reported" — skip it so we don't write a bogus 0.
69
- if (conn.serverVersion > 0) {
70
- vars.set("SERVER_VERSION_NUM", String(conn.serverVersion));
71
- }
5
+ * Refresh the connection variables (`DBNAME`, `USER`, `HOST`, `PORT`,
6
+ * `ENCODING`, `SERVER_VERSION_NAME`, `SERVER_VERSION_NUM`) from the live
7
+ * connection. Mirrors upstream `SyncVariables()`. Call after a successful
8
+ * initial connect and after each `\c` reconnect.
9
+ *
10
+ * A variable is only set when the connection actually surfaces a value; this
11
+ * leaves any user-set value untouched if the connection cannot report one.
12
+ */
13
+ const syncConnectionVars = (vars, conn) => {
14
+ const target = conn;
15
+ const database = asString(target.database);
16
+ if (database !== void 0) vars.set("DBNAME", database);
17
+ const user = asString(target.user);
18
+ if (user !== void 0) vars.set("USER", user);
19
+ const host = asString(target.host);
20
+ if (host !== void 0) vars.set("HOST", host);
21
+ const port = asPort(target.port);
22
+ if (port !== void 0) vars.set("PORT", port);
23
+ const encoding = conn.parameterStatus("client_encoding");
24
+ if (encoding !== void 0) vars.set("ENCODING", encoding);
25
+ const serverVersionName = conn.parameterStatus("server_version");
26
+ if (serverVersionName !== void 0) vars.set("SERVER_VERSION_NAME", serverVersionName);
27
+ if (conn.serverVersion > 0) vars.set("SERVER_VERSION_NUM", String(conn.serverVersion));
72
28
  };
73
29
  /**
74
- * Set the constant CLIENT version variables once at startup, mirroring how
75
- * upstream psql seeds `VERSION` / `VERSION_NAME` / `VERSION_NUM` from its
76
- * compiled-in `PG_VERSION` / `PG_VERSION_NUM`:
77
- *
78
- * - `VERSION` — the full banner string (upstream: `PostgreSQL <ver> …`).
79
- * - `VERSION_NAME` — the bare version number (upstream: e.g. `18.4`).
80
- * - `VERSION_NUM` — the numeric version (upstream: e.g. `180004`).
81
- *
82
- * The embedded psql is not a real PostgreSQL build, so there is no
83
- * `PG_VERSION` to read. We derive the values from neonctl's own package
84
- * version (passed in as {@link clientVersion}, e.g. `2.22.0`) so the client
85
- * identifier is real and traceable to the shipped binary, while keeping
86
- * upstream's variable *shapes*:
87
- *
88
- * - `VERSION` → `psql-ts (neon) <clientVersion>` — a banner that
89
- * names the implementation so users can tell they are on the embedded
90
- * TS port, mirroring the startup banner's `psql-ts (neon, …)` shape.
91
- * - `VERSION_NAME` → `<clientVersion>` (e.g. `2.22.0`).
92
- * - `VERSION_NUM` → the same version mapped into PG's NNMMPP integer form
93
- * (`2.22.0` → `22200`) via {@link clientVersionNum}, so a script doing a
94
- * numeric `:VERSION_NUM` comparison gets a monotonic integer.
95
- */
96
- export const setStartupVars = (vars, clientVersion) => {
97
- vars.set("VERSION", `psql-ts (neon) ${clientVersion}`);
98
- vars.set("VERSION_NAME", clientVersion);
99
- vars.set("VERSION_NUM", String(clientVersionNum(clientVersion)));
30
+ * Set the constant CLIENT version variables once at startup, mirroring how
31
+ * upstream psql seeds `VERSION` / `VERSION_NAME` / `VERSION_NUM` from its
32
+ * compiled-in `PG_VERSION` / `PG_VERSION_NUM`:
33
+ *
34
+ * - `VERSION` — the full banner string (upstream: `PostgreSQL <ver> …`).
35
+ * - `VERSION_NAME` — the bare version number (upstream: e.g. `18.4`).
36
+ * - `VERSION_NUM` — the numeric version (upstream: e.g. `180004`).
37
+ *
38
+ * The embedded psql is not a real PostgreSQL build, so there is no
39
+ * `PG_VERSION` to read. We derive the values from neonctl's own package
40
+ * version (passed in as {@link clientVersion}, e.g. `2.22.0`) so the client
41
+ * identifier is real and traceable to the shipped binary, while keeping
42
+ * upstream's variable *shapes*:
43
+ *
44
+ * - `VERSION` → `psql-ts (neon) <clientVersion>` — a banner that
45
+ * names the implementation so users can tell they are on the embedded
46
+ * TS port, mirroring the startup banner's `psql-ts (neon, …)` shape.
47
+ * - `VERSION_NAME` → `<clientVersion>` (e.g. `2.22.0`).
48
+ * - `VERSION_NUM` → the same version mapped into PG's NNMMPP integer form
49
+ * (`2.22.0` → `22200`) via {@link clientVersionNum}, so a script doing a
50
+ * numeric `:VERSION_NUM` comparison gets a monotonic integer.
51
+ */
52
+ const setStartupVars = (vars, clientVersion) => {
53
+ vars.set("VERSION", `psql-ts (neon) ${clientVersion}`);
54
+ vars.set("VERSION_NAME", clientVersion);
55
+ vars.set("VERSION_NUM", String(clientVersionNum(clientVersion)));
100
56
  };
101
57
  /**
102
- * Map a `MAJOR.MINOR.PATCH` semver-style version string into PG's
103
- * `PG_VERSION_NUM` integer layout (`MAJOR * 10000 + MINOR * 100 + PATCH`,
104
- * e.g. `2.22.0` → `22200`). Missing components default to 0; a non-numeric
105
- * leading component yields `0`. Kept deliberately tolerant — the value only
106
- * needs to be a monotonic integer for `:VERSION_NUM` comparisons.
107
- */
108
- export const clientVersionNum = (version) => {
109
- const m = /^(\d+)(?:\.(\d+))?(?:\.(\d+))?/.exec(version.trim());
110
- if (!m)
111
- return 0;
112
- const major = parseInt(m[1], 10);
113
- const minor = m[2] !== undefined ? parseInt(m[2], 10) : 0;
114
- const patch = m[3] !== undefined ? parseInt(m[3], 10) : 0;
115
- return major * 10000 + minor * 100 + patch;
58
+ * Map a `MAJOR.MINOR.PATCH` semver-style version string into PG's
59
+ * `PG_VERSION_NUM` integer layout (`MAJOR * 10000 + MINOR * 100 + PATCH`,
60
+ * e.g. `2.22.0` → `22200`). Missing components default to 0; a non-numeric
61
+ * leading component yields `0`. Kept deliberately tolerant — the value only
62
+ * needs to be a monotonic integer for `:VERSION_NUM` comparisons.
63
+ */
64
+ const clientVersionNum = (version) => {
65
+ const m = /^(\d+)(?:\.(\d+))?(?:\.(\d+))?/.exec(version.trim());
66
+ if (!m) return 0;
67
+ const major = parseInt(m[1], 10);
68
+ const minor = m[2] !== void 0 ? parseInt(m[2], 10) : 0;
69
+ const patch = m[3] !== void 0 ? parseInt(m[3], 10) : 0;
70
+ return major * 1e4 + minor * 100 + patch;
116
71
  };
72
+ //#endregion
73
+ export { clientVersionNum, setStartupVars, syncConnectionVars };
@@ -1,287 +1,165 @@
1
- /**
2
- * psql Variables store.
3
- *
4
- * TypeScript port of PostgreSQL's `src/bin/psql/variables.c`. Models a psql
5
- * "variable space" — a name → string-value mapping with per-variable
6
- * notification hooks and the parsing helpers psql uses to coerce values to
7
- * booleans, tri-state ("on"/"off"/"auto") values, and integers.
8
- *
9
- * Deviations from upstream that are intentional:
10
- *
11
- * - Storage is a `Map<string, string>` rather than a doubly-linked list.
12
- * Insertion order is preserved by Map (psql sorts alphabetically purely for
13
- * pretty-printing in `\set`; that responsibility belongs to a future
14
- * printer/help WP, not the store).
15
- * - Multiple hooks per name are allowed (upstream has at most one substitute
16
- * + one assign hook per variable). All hooks must return `true` for a
17
- * `set()` to be accepted; if any vetoes, the value is left unchanged.
18
- * - On `addHook()` we synchronously replay the current value (or `null` if
19
- * unset) through the new hook. This matches the upstream behaviour where
20
- * `SetVariableHooks` fires the substitute and assign hooks immediately so
21
- * derived psql state can sync.
22
- * - On `unset()` registered hooks are notified with `null` and remain
23
- * registered. The value is removed from the map (so `has()` returns
24
- * `false`), but a later `set()` will still consult the hooks.
25
- * - Variable names are validated against `[A-Za-z_][A-Za-z0-9_]*` per the
26
- * WP-06 spec. Upstream additionally accepts non-ASCII bytes and a leading
27
- * digit; we deliberately tighten the rule for the TS port.
28
- */
1
+ //#region src/psql/core/variables.ts
29
2
  const VALID_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
30
3
  /** Case-insensitive prefix match: does `value` start with `prefix`? */
31
- const isPrefixOf = (value, prefix) => value.length > 0 &&
32
- value.length <= prefix.length &&
33
- prefix.slice(0, value.length).toLowerCase() === value.toLowerCase();
4
+ const isPrefixOf = (value, prefix) => value.length > 0 && value.length <= prefix.length && prefix.slice(0, value.length).toLowerCase() === value.toLowerCase();
34
5
  /**
35
- * Parse a string the way psql's `ParseVariableBool` does.
36
- *
37
- * Recognised tokens (case-insensitive, with unique-prefix matching for the
38
- * word forms): `true`, `false`, `yes`, `no`, `on`, `off`, `1`, `0`. For `on`
39
- * and `off` we require at least two characters of input — a bare `o` is
40
- * ambiguous and upstream rejects it.
41
- *
42
- * Returns the parsed boolean, or `null` if the string is not recognised.
43
- */
6
+ * Parse a string the way psql's `ParseVariableBool` does.
7
+ *
8
+ * Recognised tokens (case-insensitive, with unique-prefix matching for the
9
+ * word forms): `true`, `false`, `yes`, `no`, `on`, `off`, `1`, `0`. For `on`
10
+ * and `off` we require at least two characters of input — a bare `o` is
11
+ * ambiguous and upstream rejects it.
12
+ *
13
+ * Returns the parsed boolean, or `null` if the string is not recognised.
14
+ */
44
15
  const parseBool = (value) => {
45
- if (value.length === 0)
46
- return null;
47
- if (isPrefixOf(value, "true"))
48
- return true;
49
- if (isPrefixOf(value, "false"))
50
- return false;
51
- if (isPrefixOf(value, "yes"))
52
- return true;
53
- if (isPrefixOf(value, "no"))
54
- return false;
55
- // 'on'/'off' need at least 2 chars; 'o' alone is ambiguous.
56
- if (value.length >= 2) {
57
- const lower = value.toLowerCase();
58
- if ("on".startsWith(lower))
59
- return true;
60
- if ("off".startsWith(lower))
61
- return false;
62
- }
63
- if (value === "1")
64
- return true;
65
- if (value === "0")
66
- return false;
67
- // WP-06 extension: any other strtol-parsable integer is truthy if non-zero,
68
- // falsy if zero. Upstream `ParseVariableBool` rejects "42" outright; we
69
- // accept it so callers don't need a separate code path for numeric flags.
70
- const asNum = parseInt32(value);
71
- if (asNum !== null)
72
- return asNum !== 0;
73
- return null;
16
+ if (value.length === 0) return null;
17
+ if (isPrefixOf(value, "true")) return true;
18
+ if (isPrefixOf(value, "false")) return false;
19
+ if (isPrefixOf(value, "yes")) return true;
20
+ if (isPrefixOf(value, "no")) return false;
21
+ if (value.length >= 2) {
22
+ const lower = value.toLowerCase();
23
+ if ("on".startsWith(lower)) return true;
24
+ if ("off".startsWith(lower)) return false;
25
+ }
26
+ if (value === "1") return true;
27
+ if (value === "0") return false;
28
+ const asNum = parseInt32(value);
29
+ if (asNum !== null) return asNum !== 0;
30
+ return null;
74
31
  };
75
32
  /**
76
- * Parse a string as an integer the way psql's `ParseVariableNum` does
77
- * (base 0, i.e. `0x` and leading-zero octal forms are accepted), and clamp
78
- * to the 32-bit signed range that psql uses (the C cast `numval == (int)
79
- * numval` check).
80
- *
81
- * Returns the integer, or `null` on syntax / range failure.
82
- */
33
+ * Parse a string as an integer the way psql's `ParseVariableNum` does
34
+ * (base 0, i.e. `0x` and leading-zero octal forms are accepted), and clamp
35
+ * to the 32-bit signed range that psql uses (the C cast `numval == (int)
36
+ * numval` check).
37
+ *
38
+ * Returns the integer, or `null` on syntax / range failure.
39
+ */
83
40
  const parseInt32 = (value) => {
84
- if (value.length === 0)
85
- return null;
86
- const trimmed = value.trim();
87
- if (trimmed.length === 0)
88
- return null;
89
- // Match the prefixes strtol(_, _, 0) accepts and pick the matching radix
90
- // explicitly so we can validate the entire string (Number()/parseInt with
91
- // base 0 are not portable enough for this).
92
- let body = trimmed;
93
- let sign = 1;
94
- if (body.startsWith("+")) {
95
- body = body.slice(1);
96
- }
97
- else if (body.startsWith("-")) {
98
- sign = -1;
99
- body = body.slice(1);
100
- }
101
- if (body.length === 0)
102
- return null;
103
- let radix = 10;
104
- if (body.startsWith("0x") || body.startsWith("0X")) {
105
- radix = 16;
106
- body = body.slice(2);
107
- }
108
- else if (body.startsWith("0o") || body.startsWith("0O")) {
109
- radix = 8;
110
- body = body.slice(2);
111
- }
112
- else if (body.length > 1 && body.startsWith("0")) {
113
- // C strtol with base 0 treats a leading 0 as octal. JS users typically
114
- // expect decimal; we follow upstream for behavioural fidelity.
115
- radix = 8;
116
- body = body.slice(1);
117
- }
118
- if (body.length === 0)
119
- return null;
120
- const digitRe = radix === 16 ? /^[0-9a-fA-F]+$/ : radix === 8 ? /^[0-7]+$/ : /^[0-9]+$/;
121
- if (!digitRe.test(body))
122
- return null;
123
- const parsed = sign * parseInt(body, radix);
124
- if (!Number.isFinite(parsed))
125
- return null;
126
- // Match `numval == (int) numval` — 32-bit signed range.
127
- if (parsed < -0x80000000 || parsed > 0x7fffffff)
128
- return null;
129
- return parsed;
41
+ if (value.length === 0) return null;
42
+ const trimmed = value.trim();
43
+ if (trimmed.length === 0) return null;
44
+ let body = trimmed;
45
+ let sign = 1;
46
+ if (body.startsWith("+")) body = body.slice(1);
47
+ else if (body.startsWith("-")) {
48
+ sign = -1;
49
+ body = body.slice(1);
50
+ }
51
+ if (body.length === 0) return null;
52
+ let radix = 10;
53
+ if (body.startsWith("0x") || body.startsWith("0X")) {
54
+ radix = 16;
55
+ body = body.slice(2);
56
+ } else if (body.startsWith("0o") || body.startsWith("0O")) {
57
+ radix = 8;
58
+ body = body.slice(2);
59
+ } else if (body.length > 1 && body.startsWith("0")) {
60
+ radix = 8;
61
+ body = body.slice(1);
62
+ }
63
+ if (body.length === 0) return null;
64
+ if (!(radix === 16 ? /^[0-9a-fA-F]+$/ : radix === 8 ? /^[0-7]+$/ : /^[0-9]+$/).test(body)) return null;
65
+ const parsed = sign * parseInt(body, radix);
66
+ if (!Number.isFinite(parsed)) return null;
67
+ if (parsed < -2147483648 || parsed > 2147483647) return null;
68
+ return parsed;
69
+ };
70
+ var VarStore = class {
71
+ constructor() {
72
+ this.values = /* @__PURE__ */ new Map();
73
+ this.hooks = /* @__PURE__ */ new Map();
74
+ }
75
+ set(name, value) {
76
+ return this.trySet(name, value).ok;
77
+ }
78
+ trySet(name, value) {
79
+ if (!VALID_NAME_RE.test(name)) return {
80
+ ok: false,
81
+ reason: "invalid-name"
82
+ };
83
+ const hooks = this.hooks.get(name);
84
+ let toStore = value;
85
+ if (hooks) for (const hook of hooks) {
86
+ const result = hook(toStore);
87
+ if (result === false) return {
88
+ ok: false,
89
+ reason: "hook-veto"
90
+ };
91
+ if (typeof result === "string") return {
92
+ ok: false,
93
+ reason: "hook-veto",
94
+ error: result
95
+ };
96
+ if (typeof result === "object" && result !== null) toStore = result.substitute;
97
+ }
98
+ this.values.set(name, toStore);
99
+ return { ok: true };
100
+ }
101
+ get(name) {
102
+ return this.values.get(name);
103
+ }
104
+ unset(name) {
105
+ const had = this.values.delete(name);
106
+ const hooks = this.hooks.get(name);
107
+ if (hooks) {
108
+ let substituted = null;
109
+ for (const hook of hooks) {
110
+ const r = hook(null);
111
+ if (typeof r === "object" && r !== null && "substitute" in r) substituted = r.substitute;
112
+ }
113
+ if (substituted !== null) {
114
+ this.values.set(name, substituted);
115
+ for (const hook of hooks) hook(substituted);
116
+ }
117
+ }
118
+ return had;
119
+ }
120
+ has(name) {
121
+ return this.values.has(name);
122
+ }
123
+ hasSubstituteHook(name) {
124
+ const hooks = this.hooks.get(name);
125
+ return hooks !== void 0 && hooks.length > 0;
126
+ }
127
+ addHook(name, hook) {
128
+ if (!VALID_NAME_RE.test(name)) return;
129
+ const existing = this.hooks.get(name);
130
+ if (existing) existing.push(hook);
131
+ else this.hooks.set(name, [hook]);
132
+ const result = hook(this.values.get(name) ?? null);
133
+ if (typeof result === "object" && result !== null && "substitute" in result) {
134
+ this.values.set(name, result.substitute);
135
+ hook(result.substitute);
136
+ }
137
+ }
138
+ entries() {
139
+ return this.values.entries();
140
+ }
141
+ asBool(name, defaultValue = false) {
142
+ const value = this.values.get(name);
143
+ if (value === void 0) return defaultValue;
144
+ return parseBool(value) ?? defaultValue;
145
+ }
146
+ asTriple(name, defaultValue) {
147
+ const value = this.values.get(name);
148
+ if (value === void 0) return defaultValue;
149
+ if (isPrefixOf(value, "auto")) return "auto";
150
+ const parsed = parseBool(value);
151
+ if (parsed === null) return { error: `unrecognized value "${value}" for "${name}": Boolean expected` };
152
+ return parsed ? "on" : "off";
153
+ }
154
+ asInt(name, defaultValue = 0) {
155
+ const value = this.values.get(name);
156
+ if (value === void 0) return defaultValue;
157
+ const parsed = parseInt32(value);
158
+ if (parsed === null) return { error: `invalid value "${value}" for "${name}": integer expected` };
159
+ return parsed;
160
+ }
130
161
  };
131
- export class VarStore {
132
- constructor() {
133
- this.values = new Map();
134
- this.hooks = new Map();
135
- }
136
- set(name, value) {
137
- return this.trySet(name, value).ok;
138
- }
139
- trySet(name, value) {
140
- if (!VALID_NAME_RE.test(name)) {
141
- return { ok: false, reason: "invalid-name" };
142
- }
143
- const hooks = this.hooks.get(name);
144
- let toStore = value;
145
- if (hooks) {
146
- // All hooks must accept the value. Each hook can either:
147
- // - return `true` to accept as-is,
148
- // - return `false` to reject silently (the prior value is kept),
149
- // - return a `string` to reject with that error message
150
- // (cmdSet renders it with the `psql: ` prefix), or
151
- // - return `{ substitute: '<value>' }` to rewrite the stored value
152
- // before subsequent hooks see it.
153
- //
154
- // The substitute return is the collapsed equivalent of upstream's
155
- // separate substitute/assign hook pair (see
156
- // `bool_substitute_hook` + `bool_assign_hook` in `command.c`).
157
- // Hooks are responsible for ensuring their substituted value passes
158
- // their own validation — we do NOT re-run a hook against its own
159
- // substitution.
160
- for (const hook of hooks) {
161
- const result = hook(toStore);
162
- if (result === false) {
163
- return { ok: false, reason: "hook-veto" };
164
- }
165
- if (typeof result === "string") {
166
- return { ok: false, reason: "hook-veto", error: result };
167
- }
168
- if (typeof result === "object" && result !== null) {
169
- toStore = result.substitute;
170
- }
171
- }
172
- }
173
- this.values.set(name, toStore);
174
- return { ok: true };
175
- }
176
- get(name) {
177
- return this.values.get(name);
178
- }
179
- unset(name) {
180
- const had = this.values.delete(name);
181
- const hooks = this.hooks.get(name);
182
- if (hooks) {
183
- // Notify hooks of deletion so they can clear derived state.
184
- // Upstream substitute hooks (e.g. `on_error_rollback_substitute_hook`,
185
- // `bool_substitute_hook`) re-inject a default when `newval == NULL` —
186
- // so `\unset ON_ERROR_ROLLBACK` actually re-stores "off",
187
- // `\unset AUTOCOMMIT` re-stores "on", etc. Honor the substitute by
188
- // re-storing the value the hook returns. Plain `true` / `false` /
189
- // error-string returns mean "no substitute" and the slot stays empty.
190
- let substituted = null;
191
- for (const hook of hooks) {
192
- const r = hook(null);
193
- if (typeof r === "object" && r !== null && "substitute" in r) {
194
- substituted = r.substitute;
195
- }
196
- }
197
- if (substituted !== null) {
198
- this.values.set(name, substituted);
199
- // Re-notify hooks with the substituted value so derived state
200
- // (settings.onErrorRollback, etc.) gets the correct default.
201
- for (const hook of hooks)
202
- hook(substituted);
203
- }
204
- }
205
- return had;
206
- }
207
- has(name) {
208
- return this.values.has(name);
209
- }
210
- hasSubstituteHook(name) {
211
- const hooks = this.hooks.get(name);
212
- return hooks !== undefined && hooks.length > 0;
213
- }
214
- addHook(name, hook) {
215
- if (!VALID_NAME_RE.test(name))
216
- return;
217
- const existing = this.hooks.get(name);
218
- if (existing) {
219
- existing.push(hook);
220
- }
221
- else {
222
- this.hooks.set(name, [hook]);
223
- }
224
- // Replay the current value so the hook can sync immediately, matching
225
- // upstream `SetVariableHooks`:
226
- // if (shook) current->value = (*shook)(current->value);
227
- // if (ahook) (void) (*ahook)(current->value);
228
- // Our hooks combine substitute + assign in a single callback. If the
229
- // initial replay returns a `{substitute}` (the bool_substitute_hook /
230
- // verbosity_substitute_hook etc. pattern), persist that and re-run the
231
- // hook so derived state (settings.verbosity, settings.echo, ...) syncs
232
- // to the substituted value. This is how upstream seeds defaults like
233
- // `ON_ERROR_STOP=off`, `VERBOSITY=default`, `QUIET=off` etc. simply
234
- // from `SetVariableHooks` — no explicit `SetVariable("…", "off")` call
235
- // is needed for variables whose default matches the unset substitute.
236
- const current = this.values.get(name);
237
- const result = hook(current ?? null);
238
- if (typeof result === "object" &&
239
- result !== null &&
240
- "substitute" in result) {
241
- this.values.set(name, result.substitute);
242
- hook(result.substitute);
243
- }
244
- }
245
- entries() {
246
- return this.values.entries();
247
- }
248
- asBool(name, defaultValue = false) {
249
- const value = this.values.get(name);
250
- if (value === undefined)
251
- return defaultValue;
252
- const parsed = parseBool(value);
253
- return parsed ?? defaultValue;
254
- }
255
- asTriple(name, defaultValue) {
256
- const value = this.values.get(name);
257
- if (value === undefined)
258
- return defaultValue;
259
- // "auto" is matched first as a unique prefix, so "a", "au", "aut",
260
- // "auto" all map to 'auto'. psql's actual call site does an
261
- // `pg_strncasecmp(value, "auto", len)` before falling through to
262
- // ParseVariableBool — we do the same.
263
- if (isPrefixOf(value, "auto"))
264
- return "auto";
265
- const parsed = parseBool(value);
266
- if (parsed === null) {
267
- return {
268
- error: `unrecognized value "${value}" for "${name}": Boolean expected`,
269
- };
270
- }
271
- return parsed ? "on" : "off";
272
- }
273
- asInt(name, defaultValue = 0) {
274
- const value = this.values.get(name);
275
- if (value === undefined)
276
- return defaultValue;
277
- const parsed = parseInt32(value);
278
- if (parsed === null) {
279
- return {
280
- error: `invalid value "${value}" for "${name}": integer expected`,
281
- };
282
- }
283
- return parsed;
284
- }
285
- }
286
162
  /** Factory mirroring the upstream `CreateVariableSpace()` entry point. */
287
- export const createVarStore = () => new VarStore();
163
+ const createVarStore = () => new VarStore();
164
+ //#endregion
165
+ export { VarStore, createVarStore };