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,394 +1,324 @@
|
|
|
1
|
+
//#region src/psql/scanner/stringutils.ts
|
|
1
2
|
/**
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
3
|
+
* psql string utilities.
|
|
4
|
+
*
|
|
5
|
+
* TypeScript port of PostgreSQL's `src/bin/psql/stringutils.c`. Three pure
|
|
6
|
+
* helpers used by the slash-command scanner and by code that constructs
|
|
7
|
+
* round-trippable argument strings (notably tab-completion output).
|
|
8
|
+
*
|
|
9
|
+
* - {@link strtokx} — upstream `strtokx()`, a tokenizer with
|
|
10
|
+
* configurable whitespace, delimiter, quote and
|
|
11
|
+
* escape sets. Implemented as a pure function that
|
|
12
|
+
* returns `{ token, rest }` rather than the upstream
|
|
13
|
+
* re-entrant-via-static-variables style.
|
|
14
|
+
* - {@link quoteIfNeeded} — upstream `quote_if_needed()`. Returns the value
|
|
15
|
+
* unchanged when it contains no characters that
|
|
16
|
+
* require quoting; otherwise wraps in `quote`,
|
|
17
|
+
* doubling any embedded `quote` characters.
|
|
18
|
+
* - {@link dequote} — small companion to `quote_if_needed()`. Strips a
|
|
19
|
+
* single surrounding `quote` and undoubles any
|
|
20
|
+
* embedded occurrences (the inverse of the wrap
|
|
21
|
+
* done by `quoteIfNeeded`).
|
|
22
|
+
* - {@link tryConsumeVarSubstitution} — `:NAME`/`:'NAME'`/`:"NAME"`
|
|
23
|
+
* substitution helper used by the SQL scanner.
|
|
24
|
+
* (The slash-arg scanner has its own local copy of
|
|
25
|
+
* the same logic; future work can collapse them.)
|
|
26
|
+
*
|
|
27
|
+
* Deviations from upstream that are intentional:
|
|
28
|
+
*
|
|
29
|
+
* - All inputs are JS strings, processed as UTF-16 code units. Upstream uses
|
|
30
|
+
* `PQmblenBounded()` to advance one multibyte character at a time; for the
|
|
31
|
+
* purposes of `strtokx`/`quote_if_needed`/`strip_quotes` the only thing
|
|
32
|
+
* that matters is matching ASCII delimiter / quote / escape bytes, which
|
|
33
|
+
* are guaranteed not to be the middle byte of a multibyte sequence in any
|
|
34
|
+
* PostgreSQL-supported encoding. We therefore safely walk the string one
|
|
35
|
+
* code unit at a time.
|
|
36
|
+
* - The `encoding` argument is accepted for API parity but is currently
|
|
37
|
+
* unused. The slash scanner passes it through; documenting it lets us add
|
|
38
|
+
* encoding-aware handling later without a signature break.
|
|
39
|
+
* - `strtokx` returns `{ token, rest }`. The caller iterates by passing
|
|
40
|
+
* `rest` back in; this is friendlier to TS than threading a hidden static.
|
|
41
|
+
*/
|
|
41
42
|
/**
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
|
|
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
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
// 4. Bareword: scan to next whitespace, delim, or quote.
|
|
151
|
-
const start = p;
|
|
152
|
-
while (p < n) {
|
|
153
|
-
const c = input[p];
|
|
154
|
-
if (whitespace.includes(c))
|
|
155
|
-
break;
|
|
156
|
-
if (delim.length > 0 && delim.includes(c))
|
|
157
|
-
break;
|
|
158
|
-
if (quote.length > 0 && quote.includes(c))
|
|
159
|
-
break;
|
|
160
|
-
p++;
|
|
161
|
-
}
|
|
162
|
-
const token = input.slice(start, p);
|
|
163
|
-
// Always skip trailing whitespace so the next call lands on the next
|
|
164
|
-
// non-blank character. When `atEol` is `false` we additionally consume a
|
|
165
|
-
// single trailing delim — the caller has told us delims are line-internal
|
|
166
|
-
// separators rather than significant tokens.
|
|
167
|
-
while (p < n && whitespace.includes(input[p]))
|
|
168
|
-
p++;
|
|
169
|
-
if (!atEol && p < n && delim.length > 0 && delim.includes(input[p])) {
|
|
170
|
-
p++;
|
|
171
|
-
while (p < n && whitespace.includes(input[p]))
|
|
172
|
-
p++;
|
|
173
|
-
}
|
|
174
|
-
return { token, rest: input.slice(p) };
|
|
43
|
+
* Tokenizer used by the psql slash-command scanner.
|
|
44
|
+
*
|
|
45
|
+
* Behaviour, matching upstream `strtokx()`:
|
|
46
|
+
* 1. Skip any characters in `whitespace`.
|
|
47
|
+
* 2. If the cursor sits on a character in `delim`, return that single
|
|
48
|
+
* character as the token (the delimiter itself is a token).
|
|
49
|
+
* 3. If the cursor sits on a quote character, scan until the matching quote.
|
|
50
|
+
* Doubled quotes are kept verbatim in the returned token (the caller can
|
|
51
|
+
* post-process by passing the result through {@link dequote}). The
|
|
52
|
+
* `escape` character, when set, lets the next character be taken
|
|
53
|
+
* literally — including a quote that would otherwise close the token.
|
|
54
|
+
* 4. Otherwise scan until the next whitespace, delim, or quote character and
|
|
55
|
+
* return everything consumed.
|
|
56
|
+
*
|
|
57
|
+
* @param input remaining input string
|
|
58
|
+
* @param whitespace characters treated as whitespace (any sequence is
|
|
59
|
+
* a single separator and is consumed without
|
|
60
|
+
* emitting a token)
|
|
61
|
+
* @param delim characters returned as standalone single-char
|
|
62
|
+
* tokens (use `""` to disable)
|
|
63
|
+
* @param quote characters that open a quoted token (use `""` to
|
|
64
|
+
* disable)
|
|
65
|
+
* @param escape character that lets the next char be taken
|
|
66
|
+
* literally inside a quoted token (use `""` to
|
|
67
|
+
* disable)
|
|
68
|
+
* @param eAcceptInUnquoted optional set of "E-string" prefixes — letters that
|
|
69
|
+
* when followed by a single quote start a quoted
|
|
70
|
+
* token with backslash escaping enabled. Pass `"Ee"`
|
|
71
|
+
* to mirror upstream's `e_strings = true`. `null`
|
|
72
|
+
* disables the behaviour.
|
|
73
|
+
* @param atEol when `true`, a trailing delim character is left in
|
|
74
|
+
* the remainder for the next call. When `false`,
|
|
75
|
+
* trailing whitespace and any single trailing delim
|
|
76
|
+
* are consumed before returning.
|
|
77
|
+
* @param encoding accepted for API parity; unused.
|
|
78
|
+
*
|
|
79
|
+
* @returns `{ token, rest }` where `token` is `null` at end of input.
|
|
80
|
+
*/
|
|
81
|
+
const strtokx = (input, whitespace, delim, quote, escape, eAcceptInUnquoted, atEol, encoding) => {
|
|
82
|
+
let i = 0;
|
|
83
|
+
const n = input.length;
|
|
84
|
+
while (i < n && whitespace.includes(input[i])) i++;
|
|
85
|
+
if (i >= n) return {
|
|
86
|
+
token: null,
|
|
87
|
+
rest: ""
|
|
88
|
+
};
|
|
89
|
+
if (delim.length > 0 && delim.includes(input[i])) {
|
|
90
|
+
const token = input[i];
|
|
91
|
+
i++;
|
|
92
|
+
if (!atEol) while (i < n && whitespace.includes(input[i])) i++;
|
|
93
|
+
return {
|
|
94
|
+
token,
|
|
95
|
+
rest: input.slice(i)
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
let p = i;
|
|
99
|
+
let effectiveQuote = quote;
|
|
100
|
+
let effectiveEscape = escape;
|
|
101
|
+
if (eAcceptInUnquoted && p + 1 < n && eAcceptInUnquoted.includes(input[p]) && input[p + 1] === "'") {
|
|
102
|
+
effectiveQuote = "'";
|
|
103
|
+
effectiveEscape = "\\";
|
|
104
|
+
p++;
|
|
105
|
+
}
|
|
106
|
+
if (effectiveQuote.length > 0 && effectiveQuote.includes(input[p])) {
|
|
107
|
+
const thisQuote = input[p];
|
|
108
|
+
const start = p;
|
|
109
|
+
p++;
|
|
110
|
+
while (p < n) {
|
|
111
|
+
const c = input[p];
|
|
112
|
+
if (effectiveEscape.length > 0 && c === effectiveEscape && p + 1 < n) {
|
|
113
|
+
p += 2;
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (c === thisQuote && input[p + 1] === thisQuote) {
|
|
117
|
+
p += 2;
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if (c === thisQuote) {
|
|
121
|
+
p++;
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
p++;
|
|
125
|
+
}
|
|
126
|
+
const token = input.slice(start, p);
|
|
127
|
+
if (!atEol) while (p < n && whitespace.includes(input[p])) p++;
|
|
128
|
+
return {
|
|
129
|
+
token,
|
|
130
|
+
rest: input.slice(p)
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
const start = p;
|
|
134
|
+
while (p < n) {
|
|
135
|
+
const c = input[p];
|
|
136
|
+
if (whitespace.includes(c)) break;
|
|
137
|
+
if (delim.length > 0 && delim.includes(c)) break;
|
|
138
|
+
if (quote.length > 0 && quote.includes(c)) break;
|
|
139
|
+
p++;
|
|
140
|
+
}
|
|
141
|
+
const token = input.slice(start, p);
|
|
142
|
+
while (p < n && whitespace.includes(input[p])) p++;
|
|
143
|
+
if (!atEol && p < n && delim.length > 0 && delim.includes(input[p])) {
|
|
144
|
+
p++;
|
|
145
|
+
while (p < n && whitespace.includes(input[p])) p++;
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
token,
|
|
149
|
+
rest: input.slice(p)
|
|
150
|
+
};
|
|
175
151
|
};
|
|
176
152
|
/**
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
needsQuotes = true;
|
|
202
|
-
escaped += c;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
if (!needsQuotes)
|
|
206
|
-
return value;
|
|
207
|
-
return quote + escaped + quote;
|
|
153
|
+
* Wrap `value` in `quote` if it contains any character in `escapeChars`,
|
|
154
|
+
* `quote` itself, or is otherwise ambiguous; embedded occurrences of `quote`
|
|
155
|
+
* are doubled to escape them. If no quoting is needed the original `value` is
|
|
156
|
+
* returned verbatim (so this is a no-op for already-clean tokens).
|
|
157
|
+
*
|
|
158
|
+
* @param value string to (possibly) quote
|
|
159
|
+
* @param escapeChars characters whose presence in `value` triggers quoting
|
|
160
|
+
* (typically the same character set passed as `whitespace`
|
|
161
|
+
* / `delim` to {@link strtokx})
|
|
162
|
+
* @param quote quote character to wrap with (e.g. `'` or `"`)
|
|
163
|
+
*/
|
|
164
|
+
const quoteIfNeeded = (value, escapeChars, quote) => {
|
|
165
|
+
if (quote.length !== 1) throw new Error("quoteIfNeeded: quote must be exactly one character");
|
|
166
|
+
let needsQuotes = false;
|
|
167
|
+
let escaped = "";
|
|
168
|
+
for (const c of value) if (c === quote) {
|
|
169
|
+
needsQuotes = true;
|
|
170
|
+
escaped += quote + quote;
|
|
171
|
+
} else {
|
|
172
|
+
if (escapeChars.includes(c)) needsQuotes = true;
|
|
173
|
+
escaped += c;
|
|
174
|
+
}
|
|
175
|
+
if (!needsQuotes) return value;
|
|
176
|
+
return quote + escaped + quote;
|
|
208
177
|
};
|
|
209
178
|
/**
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
if (inner[i] === quote && inner[i + 1] === quote) {
|
|
232
|
-
out += quote;
|
|
233
|
-
i += 2;
|
|
234
|
-
}
|
|
235
|
-
else {
|
|
236
|
-
out += inner[i];
|
|
237
|
-
i++;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
return out;
|
|
179
|
+
* Inverse of {@link quoteIfNeeded}. If `value` is wrapped in `quote`, strip
|
|
180
|
+
* the outer quotes and undouble any embedded `quote` occurrences. If `value`
|
|
181
|
+
* is not wrapped in `quote`, it is returned unchanged.
|
|
182
|
+
*
|
|
183
|
+
* @param value any string (quoted or bare)
|
|
184
|
+
* @param quote quote character used to wrap (e.g. `'` or `"`)
|
|
185
|
+
*/
|
|
186
|
+
const dequote = (value, quote) => {
|
|
187
|
+
if (quote.length !== 1) throw new Error("dequote: quote must be exactly one character");
|
|
188
|
+
if (value.length < 2 || !value.startsWith(quote) || !value.endsWith(quote)) return value;
|
|
189
|
+
const inner = value.slice(1, -1);
|
|
190
|
+
let out = "";
|
|
191
|
+
let i = 0;
|
|
192
|
+
while (i < inner.length) if (inner[i] === quote && inner[i + 1] === quote) {
|
|
193
|
+
out += quote;
|
|
194
|
+
i += 2;
|
|
195
|
+
} else {
|
|
196
|
+
out += inner[i];
|
|
197
|
+
i++;
|
|
198
|
+
}
|
|
199
|
+
return out;
|
|
241
200
|
};
|
|
242
201
|
/**
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
}
|
|
261
|
-
else {
|
|
262
|
-
inner += c;
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
return needsEscape ? `E'${inner}'` : `'${inner}'`;
|
|
202
|
+
* SQL-literal-quote a value for the `:'varname'` substitution form.
|
|
203
|
+
*
|
|
204
|
+
* Mirrors libpq's `PQescapeLiteral` for the common case: wrap in `'…'`,
|
|
205
|
+
* double any embedded `'`, and backslash-escape any embedded `\`. Upstream
|
|
206
|
+
* additionally emits an `E` prefix when the value contains backslashes; we
|
|
207
|
+
* preserve that behaviour for compatibility with code that round-trips
|
|
208
|
+
* through the SQL parser.
|
|
209
|
+
*/
|
|
210
|
+
const quoteSqlLiteral = (value) => {
|
|
211
|
+
let needsEscape = false;
|
|
212
|
+
let inner = "";
|
|
213
|
+
for (const c of value) if (c === "'") inner += "''";
|
|
214
|
+
else if (c === "\\") {
|
|
215
|
+
inner += "\\\\";
|
|
216
|
+
needsEscape = true;
|
|
217
|
+
} else inner += c;
|
|
218
|
+
return needsEscape ? `E'${inner}'` : `'${inner}'`;
|
|
266
219
|
};
|
|
267
220
|
/**
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
}
|
|
276
|
-
return `"${inner}"`;
|
|
221
|
+
* SQL-identifier-quote a value for the `:"varname"` substitution form.
|
|
222
|
+
* Wraps the value in `"…"` and doubles any embedded `"`.
|
|
223
|
+
*/
|
|
224
|
+
const quoteSqlIdent = (value) => {
|
|
225
|
+
let inner = "";
|
|
226
|
+
for (const c of value) inner += c === "\"" ? "\"\"" : c;
|
|
227
|
+
return `"${inner}"`;
|
|
277
228
|
};
|
|
278
|
-
// Variable-name character class. Upstream's `variable_char` flex rule matches
|
|
279
|
-
// `[A-Za-z0-9_\x80-\xff]+`. We do not allow a leading digit (matches
|
|
280
|
-
// `VarStore`'s `[A-Za-z_][A-Za-z0-9_]*` validation rule) — a token like `:1`
|
|
281
|
-
// is not a substitution. The high-byte range `\x80-\xff` is spelled with
|
|
282
|
-
// explicit `\x..` escapes so the `_` and the range stay separated; an earlier
|
|
283
|
-
// `[A-Za-z0-9_-ÿ]` form silently included `{`, `|`, `}`, `~` via the
|
|
284
|
-
// dash-range bridge between `_` (0x5F) and `ÿ` (0xFF), which broke the
|
|
285
|
-
// `:{?NAME}` brace-form lookup whose closing `}` would be eaten as part of
|
|
286
|
-
// the name.
|
|
287
229
|
const VAR_NAME_CONT_RE = /[A-Za-z0-9_\x80-\xff]/;
|
|
288
230
|
const VAR_NAME_START_RE = /[A-Za-z_\x80-\xff]/;
|
|
289
|
-
const isVarNameStart = (c) => c !==
|
|
290
|
-
const isVarNameCont = (c) => c !==
|
|
231
|
+
const isVarNameStart = (c) => c !== void 0 && VAR_NAME_START_RE.test(c);
|
|
232
|
+
const isVarNameCont = (c) => c !== void 0 && VAR_NAME_CONT_RE.test(c);
|
|
291
233
|
/**
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
if (isVarNameStart(next)) {
|
|
381
|
-
let j = i + 1;
|
|
382
|
-
while (j < s.length && isVarNameCont(s[j]))
|
|
383
|
-
j++;
|
|
384
|
-
const name = s.slice(i + 1, j);
|
|
385
|
-
const value = varLookup(name);
|
|
386
|
-
if (value === undefined) {
|
|
387
|
-
// Unset → emit literally so it stays visible. Upstream ECHOes the
|
|
388
|
-
// entire `:name` text in this case.
|
|
389
|
-
return { end: j, text: s.slice(i, j) };
|
|
390
|
-
}
|
|
391
|
-
return { end: j, text: value };
|
|
392
|
-
}
|
|
393
|
-
return null;
|
|
234
|
+
* Attempt to consume one of the `:NAME`, `:'NAME'`, `:"NAME"` variable
|
|
235
|
+
* substitution forms at position `i` in `s`. Returns the new index plus the
|
|
236
|
+
* substituted text, or `null` if no recognised form is present (i.e. the
|
|
237
|
+
* caller should emit `s[i]` verbatim and advance one char).
|
|
238
|
+
*
|
|
239
|
+
* When `varLookup` is `undefined`, substitution is disabled outright (the
|
|
240
|
+
* function returns `null` for every call).
|
|
241
|
+
*
|
|
242
|
+
* Unknown variables: upstream still ECHOes the raw `:NAME` token rather than
|
|
243
|
+
* substituting an empty string, so a misspelled reference stays visible to
|
|
244
|
+
* the user. We mirror that for all three forms.
|
|
245
|
+
*
|
|
246
|
+
* Edge cases handled here so callers don't repeat them:
|
|
247
|
+
*
|
|
248
|
+
* - `::` (PostgreSQL cast operator): when `s[i+1] === ':'`, we treat the
|
|
249
|
+
* sequence as not-a-substitution and return `null`. The caller is then
|
|
250
|
+
* responsible for advancing past both colons (the SQL scanner does that
|
|
251
|
+
* in a dedicated branch before calling us).
|
|
252
|
+
* - `:` followed by a non-identifier char: returns `null`. The caller emits
|
|
253
|
+
* the literal `:` and continues.
|
|
254
|
+
* - `:'` or `:"` with no matching closing quote or empty name: returns
|
|
255
|
+
* `null`. We require at least one identifier character and a properly
|
|
256
|
+
* closed quote, matching upstream's flex rules.
|
|
257
|
+
*/
|
|
258
|
+
const tryConsumeVarSubstitution = (s, i, varLookup) => {
|
|
259
|
+
if (varLookup === void 0) return null;
|
|
260
|
+
if (s[i] !== ":") return null;
|
|
261
|
+
const next = s[i + 1];
|
|
262
|
+
if (next === void 0) return null;
|
|
263
|
+
if (next === ":") return null;
|
|
264
|
+
if (next === "{" && s[i + 2] === "?") {
|
|
265
|
+
let j = i + 3;
|
|
266
|
+
while (j < s.length && isVarNameCont(s[j])) j++;
|
|
267
|
+
if (j > i + 3 && s[j] === "}") {
|
|
268
|
+
const value = varLookup(s.slice(i + 3, j));
|
|
269
|
+
return {
|
|
270
|
+
end: j + 1,
|
|
271
|
+
text: value !== void 0 ? "TRUE" : "FALSE"
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
return null;
|
|
275
|
+
}
|
|
276
|
+
if (next === "\"") {
|
|
277
|
+
let j = i + 2;
|
|
278
|
+
while (j < s.length && isVarNameCont(s[j])) j++;
|
|
279
|
+
if (j > i + 2 && s[j] === "\"") {
|
|
280
|
+
const value = varLookup(s.slice(i + 2, j));
|
|
281
|
+
if (value === void 0) return {
|
|
282
|
+
end: j + 1,
|
|
283
|
+
text: s.slice(i, j + 1)
|
|
284
|
+
};
|
|
285
|
+
return {
|
|
286
|
+
end: j + 1,
|
|
287
|
+
text: quoteSqlIdent(value)
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
return null;
|
|
291
|
+
}
|
|
292
|
+
if (next === "'") {
|
|
293
|
+
let j = i + 2;
|
|
294
|
+
while (j < s.length && isVarNameCont(s[j])) j++;
|
|
295
|
+
if (j > i + 2 && s[j] === "'") {
|
|
296
|
+
const value = varLookup(s.slice(i + 2, j));
|
|
297
|
+
if (value === void 0) return {
|
|
298
|
+
end: j + 1,
|
|
299
|
+
text: s.slice(i, j + 1)
|
|
300
|
+
};
|
|
301
|
+
return {
|
|
302
|
+
end: j + 1,
|
|
303
|
+
text: quoteSqlLiteral(value)
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
if (isVarNameStart(next)) {
|
|
309
|
+
let j = i + 1;
|
|
310
|
+
while (j < s.length && isVarNameCont(s[j])) j++;
|
|
311
|
+
const value = varLookup(s.slice(i + 1, j));
|
|
312
|
+
if (value === void 0) return {
|
|
313
|
+
end: j,
|
|
314
|
+
text: s.slice(i, j)
|
|
315
|
+
};
|
|
316
|
+
return {
|
|
317
|
+
end: j,
|
|
318
|
+
text: value
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
return null;
|
|
394
322
|
};
|
|
323
|
+
//#endregion
|
|
324
|
+
export { dequote, quoteIfNeeded, quoteSqlIdent, quoteSqlLiteral, strtokx, tryConsumeVarSubstitution };
|