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.
- package/README.md +54 -0
- package/dist/_shared/auth_selection.js +76 -79
- package/dist/_shared/credentials.js +166 -187
- package/dist/_shared/env-core/env.js +395 -0
- package/dist/_shared/env-core/reuse-secrets.js +179 -0
- package/dist/_shared/paths.js +129 -126
- package/dist/_shared/profiles.js +192 -242
- package/dist/_shared/secure_file.js +36 -38
- package/dist/_virtual/_rolldown/runtime.js +13 -0
- package/dist/analytics.js +163 -207
- package/dist/api.js +815 -758
- package/dist/auth.js +121 -141
- package/dist/auth_context.js +39 -53
- package/dist/cli.js +4 -7
- package/dist/commands/api.js +220 -250
- package/dist/commands/api_keys.js +251 -314
- package/dist/commands/auth.js +283 -328
- package/dist/commands/bootstrap.js +372 -437
- package/dist/commands/branches.js +304 -455
- package/dist/commands/bucket.js +374 -514
- package/dist/commands/checkout.js +213 -298
- package/dist/commands/config.js +575 -658
- package/dist/commands/connection_string.js +137 -165
- package/dist/commands/data_api.js +238 -260
- package/dist/commands/databases.js +67 -76
- package/dist/commands/deploy.js +31 -25
- package/dist/commands/dev.js +642 -681
- package/dist/commands/diff.js +156 -200
- package/dist/commands/env.js +243 -303
- package/dist/commands/functions.js +275 -341
- package/dist/commands/index.js +70 -65
- package/dist/commands/init.js +84 -119
- package/dist/commands/inspect.js +55 -55
- package/dist/commands/ip_allow.js +88 -120
- package/dist/commands/link.js +874 -1019
- package/dist/commands/logs.js +291 -0
- package/dist/commands/neon_auth.js +725 -933
- package/dist/commands/operations.js +34 -25
- package/dist/commands/orgs.js +28 -18
- package/dist/commands/profile.js +614 -845
- package/dist/commands/projects.js +313 -373
- package/dist/commands/psql.js +60 -58
- package/dist/commands/roles.js +55 -58
- package/dist/commands/schema_diff.js +87 -131
- package/dist/commands/set_context.js +34 -26
- package/dist/commands/snapshots.js +288 -413
- package/dist/commands/status.js +41 -37
- package/dist/commands/user.js +21 -10
- package/dist/commands/vpc_endpoints.js +85 -113
- package/dist/config.js +7 -6
- package/dist/config_format.js +50 -66
- package/dist/config_template.js +128 -157
- package/dist/context.js +183 -235
- package/dist/current_branch_fast_path.js +40 -49
- package/dist/dev/env.js +313 -394
- package/dist/dev/functions.js +54 -64
- package/dist/dev/inputs.js +46 -58
- package/dist/dev/runtime.js +135 -164
- package/dist/dev/websocket.js +766 -959
- package/dist/env.js +27 -33
- package/dist/env_file.js +118 -132
- package/dist/env_services.js +36 -38
- package/dist/errors.js +57 -68
- package/dist/functions_api.js +45 -43
- package/dist/help.js +189 -140
- package/dist/index.js +182 -257
- package/dist/init/agents.js +137 -118
- package/dist/init/auth.js +58 -68
- package/dist/init/bootstrap.js +325 -396
- package/dist/init/build_config.js +4 -2
- package/dist/init/detect_agent.js +56 -101
- package/dist/init/editors.js +35 -52
- package/dist/init/enrich_output.js +51 -66
- package/dist/init/extension.js +134 -171
- package/dist/init/inspect.js +179 -266
- package/dist/init/interactive.js +510 -622
- package/dist/init/neonctl.js +117 -168
- package/dist/init/orchestrate.js +157 -173
- package/dist/init/phases/auth.js +188 -202
- package/dist/init/phases/cleanup.js +23 -23
- package/dist/init/phases/db.js +251 -277
- package/dist/init/phases/getting_started.js +213 -223
- package/dist/init/phases/mcp.js +174 -224
- package/dist/init/phases/migrations.js +247 -248
- package/dist/init/phases/neon_auth.js +114 -133
- package/dist/init/phases/setup.js +546 -703
- package/dist/init/phases/skills.js +75 -86
- package/dist/init/phases/status.js +72 -67
- package/dist/init/resolve_context.js +102 -99
- package/dist/init/route_command.js +91 -98
- package/dist/init/skills.js +174 -218
- package/dist/init/vsix.js +77 -99
- package/dist/log.js +17 -16
- package/dist/neon_services.js +104 -129
- package/dist/parameters.gen.js +481 -471
- package/dist/pkg.js +17 -19
- package/dist/profile_keys.js +44 -47
- package/dist/psql/cli.js +44 -47
- package/dist/psql/command/cmd_cond.js +231 -406
- package/dist/psql/command/cmd_connect.js +557 -764
- package/dist/psql/command/cmd_copy.js +727 -983
- package/dist/psql/command/cmd_describe.js +1499 -1688
- package/dist/psql/command/cmd_format.js +733 -905
- package/dist/psql/command/cmd_io.js +1293 -2082
- package/dist/psql/command/cmd_lo.js +297 -359
- package/dist/psql/command/cmd_meta.js +727 -878
- package/dist/psql/command/cmd_misc.js +138 -172
- package/dist/psql/command/cmd_pipeline.js +547 -1099
- package/dist/psql/command/cmd_restrict.js +119 -155
- package/dist/psql/command/cmd_show.js +529 -688
- package/dist/psql/command/dispatch.js +261 -325
- package/dist/psql/command/inputQueue.js +35 -33
- package/dist/psql/command/shared.js +49 -63
- package/dist/psql/complete/filenames.js +90 -133
- package/dist/psql/complete/index.js +59 -97
- package/dist/psql/complete/matcher.js +236 -300
- package/dist/psql/complete/psqlVars.js +218 -223
- package/dist/psql/complete/queries.js +159 -177
- package/dist/psql/complete/rules.js +1493 -2299
- package/dist/psql/core/common.js +762 -1180
- package/dist/psql/core/help.js +456 -546
- package/dist/psql/core/mainloop.js +692 -1302
- package/dist/psql/core/prompt.js +391 -408
- package/dist/psql/core/settings.js +429 -644
- package/dist/psql/core/sqlHelp.js +480 -554
- package/dist/psql/core/startup.js +626 -815
- package/dist/psql/core/syncVars.js +67 -110
- package/dist/psql/core/variables.js +156 -278
- package/dist/psql/describe/formatters.js +884 -1285
- package/dist/psql/describe/processNamePattern.js +173 -260
- package/dist/psql/describe/queries.js +1368 -2403
- package/dist/psql/describe/versionGate.js +32 -41
- package/dist/psql/index.js +1414 -1927
- package/dist/psql/io/history.js +232 -271
- package/dist/psql/io/input.js +103 -108
- package/dist/psql/io/lineEditor/buffer.js +238 -319
- package/dist/psql/io/lineEditor/complete.js +135 -213
- package/dist/psql/io/lineEditor/filename.js +139 -148
- package/dist/psql/io/lineEditor/index.js +653 -870
- package/dist/psql/io/lineEditor/keymap.js +544 -702
- package/dist/psql/io/lineEditor/vt100.js +294 -341
- package/dist/psql/io/pgpass.js +158 -187
- package/dist/psql/io/pgservice.js +146 -183
- package/dist/psql/io/psqlrc.js +328 -403
- package/dist/psql/print/aligned.js +1020 -1683
- package/dist/psql/print/asciidoc.js +180 -214
- package/dist/psql/print/crosstab.js +281 -442
- package/dist/psql/print/csv.js +48 -70
- package/dist/psql/print/html.js +195 -226
- package/dist/psql/print/json.js +75 -88
- package/dist/psql/print/latex.js +291 -364
- package/dist/psql/print/pager.js +171 -242
- package/dist/psql/print/troff.js +194 -226
- package/dist/psql/print/unaligned.js +69 -95
- package/dist/psql/print/units.js +167 -169
- package/dist/psql/scanner/slash.js +428 -483
- package/dist/psql/scanner/sql.js +445 -889
- package/dist/psql/scanner/stringutils.js +309 -379
- package/dist/psql/types/index.js +2 -7
- package/dist/psql/types/scanner.js +25 -22
- package/dist/psql/wire/connection.js +2042 -2803
- package/dist/psql/wire/copy.js +84 -100
- package/dist/psql/wire/notify.js +39 -59
- package/dist/psql/wire/pipeline.js +305 -518
- package/dist/psql/wire/protocol.js +349 -417
- package/dist/psql/wire/sasl.js +180 -265
- package/dist/psql/wire/tls.js +400 -561
- package/dist/storage_api.js +115 -129
- package/dist/test_utils/fixtures.js +94 -113
- package/dist/test_utils/oauth_server.js +10 -7
- package/dist/test_utils/project_dir.js +33 -0
- package/dist/utils/ai_gateway_notice.js +131 -162
- package/dist/utils/api_enums.js +21 -28
- package/dist/utils/auth.js +10 -4
- package/dist/utils/branch_notice.js +20 -19
- package/dist/utils/branch_picker.js +83 -89
- package/dist/utils/cli_name.js +15 -12
- package/dist/utils/compute_units.js +20 -27
- package/dist/utils/config_diff.js +127 -158
- package/dist/utils/enrichers.js +95 -148
- package/dist/utils/esbuild.js +133 -147
- package/dist/utils/flags.js +35 -47
- package/dist/utils/formats.js +8 -15
- package/dist/utils/git_diff.js +69 -80
- package/dist/utils/inspect_db.js +101 -143
- package/dist/utils/inspect_queries.js +179 -142
- package/dist/utils/middlewares.js +37 -44
- package/dist/utils/openapi.js +87 -99
- package/dist/utils/package_manager.js +312 -110
- package/dist/utils/point_in_time.js +49 -53
- package/dist/utils/psql.js +89 -106
- package/dist/utils/service_picker.js +55 -58
- package/dist/utils/string.js +5 -5
- package/dist/utils/ui.js +38 -55
- package/dist/utils/write_sync.js +26 -35
- package/dist/utils/zip.js +4 -3
- package/dist/writer.js +67 -87
- 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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
-
|
|
163
|
+
const createVarStore = () => new VarStore();
|
|
164
|
+
//#endregion
|
|
165
|
+
export { VarStore, createVarStore };
|