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,315 +1,251 @@
1
+ //#region src/psql/complete/matcher.ts
1
2
  /**
2
- * Matches / TailMatches / HeadMatches DSL.
3
- *
4
- * TypeScript port of the pattern-matching helpers in psql's tab-complete.in.c.
5
- * The C source uses a varargs macro layered on top of `word_matches()` to
6
- * decide whether a sequence of "previous words" matches a pattern. We
7
- * reproduce the same shape with plain functions because (a) JS has no
8
- * preprocessor macros and (b) array spread is the natural varargs analogue.
9
- *
10
- * Matches('SELECT', MatchAny, 'FROM') — exact word-count match.
11
- * TailMatches('SELECT', MatchAny, 'FROM') — match the last N words.
12
- * HeadMatches('ALTER', 'TABLE') — match the first N words.
13
- *
14
- * Wildcards and special tokens (mirroring the upstream constants):
15
- *
16
- * MatchAny — any single word (`NULL` upstream).
17
- * MatchAnyExcept(x) — any word that doesn't match `x` (upstream `"!x"`).
18
- * '*' inside a pattern — wildcard within a single word (e.g. `'pg_*'`).
19
- * '|' inside a pattern — alternation (e.g. `'TABLE|VIEW'`).
20
- *
21
- * Comparisons are case-insensitive by default (SQL keywords); pass
22
- * `caseSensitive: true` to the DSL helpers when matching strictly.
23
- *
24
- * The tokenizer is exported separately because the `index.ts` entry point
25
- * needs to do the same word-split-then-match dance.
26
- */
27
- export const MatchAny = null;
28
- export const MatchAnyExcept = (pattern) => "!" + pattern;
3
+ * Matches / TailMatches / HeadMatches DSL.
4
+ *
5
+ * TypeScript port of the pattern-matching helpers in psql's tab-complete.in.c.
6
+ * The C source uses a varargs macro layered on top of `word_matches()` to
7
+ * decide whether a sequence of "previous words" matches a pattern. We
8
+ * reproduce the same shape with plain functions because (a) JS has no
9
+ * preprocessor macros and (b) array spread is the natural varargs analogue.
10
+ *
11
+ * Matches('SELECT', MatchAny, 'FROM') — exact word-count match.
12
+ * TailMatches('SELECT', MatchAny, 'FROM') — match the last N words.
13
+ * HeadMatches('ALTER', 'TABLE') — match the first N words.
14
+ *
15
+ * Wildcards and special tokens (mirroring the upstream constants):
16
+ *
17
+ * MatchAny — any single word (`NULL` upstream).
18
+ * MatchAnyExcept(x) — any word that doesn't match `x` (upstream `"!x"`).
19
+ * '*' inside a pattern — wildcard within a single word (e.g. `'pg_*'`).
20
+ * '|' inside a pattern — alternation (e.g. `'TABLE|VIEW'`).
21
+ *
22
+ * Comparisons are case-insensitive by default (SQL keywords); pass
23
+ * `caseSensitive: true` to the DSL helpers when matching strictly.
24
+ *
25
+ * The tokenizer is exported separately because the `index.ts` entry point
26
+ * needs to do the same word-split-then-match dance.
27
+ */
28
+ const MatchAny = null;
29
+ const MatchAnyExcept = (pattern) => "!" + pattern;
29
30
  const cimatch = (s1, s2, n, caseSensitive) => {
30
- if (s1.length < n || s2.length < n)
31
- return false;
32
- const a = s1.slice(0, n);
33
- const b = s2.slice(0, n);
34
- if (caseSensitive)
35
- return a === b;
36
- return a.toLowerCase() === b.toLowerCase();
31
+ if (s1.length < n || s2.length < n) return false;
32
+ const a = s1.slice(0, n);
33
+ const b = s2.slice(0, n);
34
+ if (caseSensitive) return a === b;
35
+ return a.toLowerCase() === b.toLowerCase();
37
36
  };
38
37
  /**
39
- * Return true iff `word` matches `pattern`. `null`/`MatchAny` matches
40
- * everything. A leading `!` inverts the match. `|` separates alternatives.
41
- * `*` is a single-word wildcard.
42
- */
43
- export const wordMatches = (pattern, word, caseSensitive = false) => {
44
- if (pattern === null)
45
- return true;
46
- if (pattern.startsWith("!")) {
47
- return !wordMatches(pattern.slice(1), word, caseSensitive);
48
- }
49
- const wordlen = word.length;
50
- let cursor = pattern;
51
- for (;;) {
52
- let starIdx = -1;
53
- let i = 0;
54
- while (i < cursor.length && cursor[i] !== "|") {
55
- if (cursor[i] === "*")
56
- starIdx = i;
57
- i++;
58
- }
59
- if (starIdx >= 0) {
60
- const beforeLen = starIdx;
61
- const afterLen = i - starIdx - 1;
62
- if (wordlen >= beforeLen + afterLen &&
63
- cimatch(word, cursor, beforeLen, caseSensitive) &&
64
- cimatch(word.slice(wordlen - afterLen), cursor.slice(starIdx + 1), afterLen, caseSensitive)) {
65
- return true;
66
- }
67
- }
68
- else {
69
- if (wordlen === i &&
70
- cimatch(word, cursor, wordlen, caseSensitive)) {
71
- return true;
72
- }
73
- }
74
- if (i >= cursor.length)
75
- break;
76
- cursor = cursor.slice(i + 1);
77
- }
78
- return false;
38
+ * Return true iff `word` matches `pattern`. `null`/`MatchAny` matches
39
+ * everything. A leading `!` inverts the match. `|` separates alternatives.
40
+ * `*` is a single-word wildcard.
41
+ */
42
+ const wordMatches = (pattern, word, caseSensitive = false) => {
43
+ if (pattern === null) return true;
44
+ if (pattern.startsWith("!")) return !wordMatches(pattern.slice(1), word, caseSensitive);
45
+ const wordlen = word.length;
46
+ let cursor = pattern;
47
+ for (;;) {
48
+ let starIdx = -1;
49
+ let i = 0;
50
+ while (i < cursor.length && cursor[i] !== "|") {
51
+ if (cursor[i] === "*") starIdx = i;
52
+ i++;
53
+ }
54
+ if (starIdx >= 0) {
55
+ const beforeLen = starIdx;
56
+ const afterLen = i - starIdx - 1;
57
+ if (wordlen >= beforeLen + afterLen && cimatch(word, cursor, beforeLen, caseSensitive) && cimatch(word.slice(wordlen - afterLen), cursor.slice(starIdx + 1), afterLen, caseSensitive)) return true;
58
+ } else if (wordlen === i && cimatch(word, cursor, wordlen, caseSensitive)) return true;
59
+ if (i >= cursor.length) break;
60
+ cursor = cursor.slice(i + 1);
61
+ }
62
+ return false;
79
63
  };
80
64
  /**
81
- * Exact-length match: every word in `words` must match the corresponding
82
- * pattern. `words` here is "previous words" — i.e. the words BEFORE the
83
- * current (in-progress) word.
84
- *
85
- * The upstream uses `previous_words_count + 1 == narg` because the C array
86
- * includes both prev words AND the current. Our representation only stores
87
- * prev words; the caller passes the in-progress word separately.
88
- *
89
- * prev = ['SELECT'], pattern = ['SELECT'] → true (1 == 1)
90
- * prev = ['SELECT', 'a'], pattern = ['SELECT'] → false (2 != 1)
91
- */
92
- export const Matches = (prev, patterns, caseSensitive = false) => {
93
- if (prev.length !== patterns.length)
94
- return false;
95
- for (let k = 0; k < patterns.length; k++) {
96
- if (!wordMatches(patterns[k], prev[k], caseSensitive))
97
- return false;
98
- }
99
- return true;
65
+ * Exact-length match: every word in `words` must match the corresponding
66
+ * pattern. `words` here is "previous words" — i.e. the words BEFORE the
67
+ * current (in-progress) word.
68
+ *
69
+ * The upstream uses `previous_words_count + 1 == narg` because the C array
70
+ * includes both prev words AND the current. Our representation only stores
71
+ * prev words; the caller passes the in-progress word separately.
72
+ *
73
+ * prev = ['SELECT'], pattern = ['SELECT'] → true (1 == 1)
74
+ * prev = ['SELECT', 'a'], pattern = ['SELECT'] → false (2 != 1)
75
+ */
76
+ const Matches = (prev, patterns, caseSensitive = false) => {
77
+ if (prev.length !== patterns.length) return false;
78
+ for (let k = 0; k < patterns.length; k++) if (!wordMatches(patterns[k], prev[k], caseSensitive)) return false;
79
+ return true;
100
80
  };
101
81
  /**
102
- * Tail match: the LAST N words of `prev` must match the patterns.
103
- *
104
- * prev = ['SELECT', 'a', 'FROM'], patterns = ['FROM'] → true
105
- * prev = ['SELECT'], patterns = ['SELECT', 'FROM'] → false (too few)
106
- */
107
- export const TailMatches = (prev, patterns, caseSensitive = false) => {
108
- if (prev.length < patterns.length)
109
- return false;
110
- const offset = prev.length - patterns.length;
111
- for (let k = 0; k < patterns.length; k++) {
112
- if (!wordMatches(patterns[k], prev[offset + k], caseSensitive))
113
- return false;
114
- }
115
- return true;
82
+ * Tail match: the LAST N words of `prev` must match the patterns.
83
+ *
84
+ * prev = ['SELECT', 'a', 'FROM'], patterns = ['FROM'] → true
85
+ * prev = ['SELECT'], patterns = ['SELECT', 'FROM'] → false (too few)
86
+ */
87
+ const TailMatches = (prev, patterns, caseSensitive = false) => {
88
+ if (prev.length < patterns.length) return false;
89
+ const offset = prev.length - patterns.length;
90
+ for (let k = 0; k < patterns.length; k++) if (!wordMatches(patterns[k], prev[offset + k], caseSensitive)) return false;
91
+ return true;
116
92
  };
117
93
  /**
118
- * Head match: the FIRST N words of `prev` must match.
119
- *
120
- * prev = ['ALTER', 'TABLE', 'foo'], patterns = ['ALTER', 'TABLE'] → true
121
- */
122
- export const HeadMatches = (prev, patterns, caseSensitive = false) => {
123
- if (prev.length < patterns.length)
124
- return false;
125
- for (let k = 0; k < patterns.length; k++) {
126
- if (!wordMatches(patterns[k], prev[k], caseSensitive))
127
- return false;
128
- }
129
- return true;
94
+ * Head match: the FIRST N words of `prev` must match.
95
+ *
96
+ * prev = ['ALTER', 'TABLE', 'foo'], patterns = ['ALTER', 'TABLE'] → true
97
+ */
98
+ const HeadMatches = (prev, patterns, caseSensitive = false) => {
99
+ if (prev.length < patterns.length) return false;
100
+ for (let k = 0; k < patterns.length; k++) if (!wordMatches(patterns[k], prev[k], caseSensitive)) return false;
101
+ return true;
130
102
  };
131
103
  /**
132
- * Split `input` into psql-style words for completion purposes.
133
- *
134
- * - Whitespace is the basic separator.
135
- * - "..." treated as ONE word (quoted identifier).
136
- * - '...' treated as ONE word (string literal).
137
- * - \backslash... treated as ONE word (the backslash command).
138
- * - Punctuation (commas, parens, semicolons) is broken into its own word.
139
- * - Embedded `.` (schema-qualifier) keeps the dotted name as a single word
140
- * so completion sees `pg_catalog.pg_class` whole.
141
- *
142
- * The tokenizer is intentionally pragmatic — it doesn't validate SQL, it
143
- * just slices well enough for the rule body to inspect previous words.
144
- */
145
- export const tokenize = (input) => {
146
- const out = [];
147
- let i = 0;
148
- while (i < input.length) {
149
- const ch = input[i];
150
- // Whitespace.
151
- if (ch === " " || ch === "\t" || ch === "\n" || ch === "\r") {
152
- i++;
153
- continue;
154
- }
155
- // Backslash command. Capture the entire backslash word as one token.
156
- if (ch === "\\") {
157
- const start = i;
158
- i++;
159
- // Single-char commands like `\!` and `\?` are valid; otherwise read
160
- // letters until we hit whitespace or punctuation.
161
- if (i < input.length && /[!?]/.test(input[i])) {
162
- i++;
163
- }
164
- else {
165
- while (i < input.length && /[A-Za-z_]/.test(input[i]))
166
- i++;
167
- // Fold a trailing `+` run (the verbose suffix) into the command word
168
- // so `\dt+ <TAB>` tokenizes as a single word and the describe rules
169
- // that key on `prevWords.length === 1` still fire. The
170
- // `S` (system-objects) suffix is already a letter, so it's consumed
171
- // above; only `+` needs explicit handling here.
172
- while (i < input.length && input[i] === "+")
173
- i++;
174
- }
175
- out.push({
176
- text: input.slice(start, i),
177
- raw: input.slice(start, i),
178
- start,
179
- end: i,
180
- });
181
- continue;
182
- }
183
- // Double-quoted identifier.
184
- if (ch === '"') {
185
- const start = i;
186
- i++;
187
- while (i < input.length) {
188
- if (input[i] === '"') {
189
- // "" inside quotes is an escaped quote.
190
- if (input[i + 1] === '"') {
191
- i += 2;
192
- continue;
193
- }
194
- i++;
195
- break;
196
- }
197
- i++;
198
- }
199
- out.push({
200
- text: input.slice(start, i),
201
- raw: input.slice(start, i),
202
- start,
203
- end: i,
204
- });
205
- continue;
206
- }
207
- // Single-quoted string literal.
208
- if (ch === "'") {
209
- const start = i;
210
- i++;
211
- while (i < input.length) {
212
- if (input[i] === "\\" && i + 1 < input.length) {
213
- i += 2;
214
- continue;
215
- }
216
- if (input[i] === "'") {
217
- if (input[i + 1] === "'") {
218
- i += 2;
219
- continue;
220
- }
221
- i++;
222
- break;
223
- }
224
- i++;
225
- }
226
- out.push({
227
- text: input.slice(start, i),
228
- raw: input.slice(start, i),
229
- start,
230
- end: i,
231
- });
232
- continue;
233
- }
234
- // Punctuation that splits words.
235
- if (ch === "," ||
236
- ch === ";" ||
237
- ch === "(" ||
238
- ch === ")" ||
239
- ch === "[" ||
240
- ch === "]") {
241
- out.push({ text: ch, raw: ch, start: i, end: i + 1 });
242
- i++;
243
- continue;
244
- }
245
- // Bareword: letters, digits, underscore, `.` (schema qualifier), `:` (var
246
- // expansion marker is kept inside the word), `$` (positional param).
247
- const start = i;
248
- while (i < input.length) {
249
- const c = input[i];
250
- if (c === " " ||
251
- c === "\t" ||
252
- c === "\n" ||
253
- c === "\r" ||
254
- c === "," ||
255
- c === ";" ||
256
- c === "(" ||
257
- c === ")" ||
258
- c === "[" ||
259
- c === "]" ||
260
- c === '"' ||
261
- c === "'" ||
262
- c === "\\") {
263
- break;
264
- }
265
- i++;
266
- }
267
- out.push({
268
- text: input.slice(start, i),
269
- raw: input.slice(start, i),
270
- start,
271
- end: i,
272
- });
273
- }
274
- return out;
104
+ * Split `input` into psql-style words for completion purposes.
105
+ *
106
+ * - Whitespace is the basic separator.
107
+ * - "..." treated as ONE word (quoted identifier).
108
+ * - '...' treated as ONE word (string literal).
109
+ * - \backslash... treated as ONE word (the backslash command).
110
+ * - Punctuation (commas, parens, semicolons) is broken into its own word.
111
+ * - Embedded `.` (schema-qualifier) keeps the dotted name as a single word
112
+ * so completion sees `pg_catalog.pg_class` whole.
113
+ *
114
+ * The tokenizer is intentionally pragmatic — it doesn't validate SQL, it
115
+ * just slices well enough for the rule body to inspect previous words.
116
+ */
117
+ const tokenize = (input) => {
118
+ const out = [];
119
+ let i = 0;
120
+ while (i < input.length) {
121
+ const ch = input[i];
122
+ if (ch === " " || ch === " " || ch === "\n" || ch === "\r") {
123
+ i++;
124
+ continue;
125
+ }
126
+ if (ch === "\\") {
127
+ const start = i;
128
+ i++;
129
+ if (i < input.length && /[!?]/.test(input[i])) i++;
130
+ else {
131
+ while (i < input.length && /[A-Za-z_]/.test(input[i])) i++;
132
+ while (i < input.length && input[i] === "+") i++;
133
+ }
134
+ out.push({
135
+ text: input.slice(start, i),
136
+ raw: input.slice(start, i),
137
+ start,
138
+ end: i
139
+ });
140
+ continue;
141
+ }
142
+ if (ch === "\"") {
143
+ const start = i;
144
+ i++;
145
+ while (i < input.length) {
146
+ if (input[i] === "\"") {
147
+ if (input[i + 1] === "\"") {
148
+ i += 2;
149
+ continue;
150
+ }
151
+ i++;
152
+ break;
153
+ }
154
+ i++;
155
+ }
156
+ out.push({
157
+ text: input.slice(start, i),
158
+ raw: input.slice(start, i),
159
+ start,
160
+ end: i
161
+ });
162
+ continue;
163
+ }
164
+ if (ch === "'") {
165
+ const start = i;
166
+ i++;
167
+ while (i < input.length) {
168
+ if (input[i] === "\\" && i + 1 < input.length) {
169
+ i += 2;
170
+ continue;
171
+ }
172
+ if (input[i] === "'") {
173
+ if (input[i + 1] === "'") {
174
+ i += 2;
175
+ continue;
176
+ }
177
+ i++;
178
+ break;
179
+ }
180
+ i++;
181
+ }
182
+ out.push({
183
+ text: input.slice(start, i),
184
+ raw: input.slice(start, i),
185
+ start,
186
+ end: i
187
+ });
188
+ continue;
189
+ }
190
+ if (ch === "," || ch === ";" || ch === "(" || ch === ")" || ch === "[" || ch === "]") {
191
+ out.push({
192
+ text: ch,
193
+ raw: ch,
194
+ start: i,
195
+ end: i + 1
196
+ });
197
+ i++;
198
+ continue;
199
+ }
200
+ const start = i;
201
+ while (i < input.length) {
202
+ const c = input[i];
203
+ if (c === " " || c === " " || c === "\n" || c === "\r" || c === "," || c === ";" || c === "(" || c === ")" || c === "[" || c === "]" || c === "\"" || c === "'" || c === "\\") break;
204
+ i++;
205
+ }
206
+ out.push({
207
+ text: input.slice(start, i),
208
+ raw: input.slice(start, i),
209
+ start,
210
+ end: i
211
+ });
212
+ }
213
+ return out;
275
214
  };
276
215
  /**
277
- * Slice `input` at `cursor`, then determine what's being completed:
278
- *
279
- * - `prevWords`: the completed words ENTIRELY before the cursor.
280
- * - `currentWord`: the partial word the cursor is sitting in (may be '').
281
- * - `replaceLength`: number of code points to chop off the input before
282
- * inserting the completion (= length of currentWord in code points).
283
- *
284
- * The tokenizer is run on `input.slice(0, cursor)` and the LAST token, if
285
- * it ends exactly at cursor and didn't start with whitespace/punctuation,
286
- * becomes the current word. Otherwise the current word is empty (the user
287
- * just typed a space and is starting a new word).
288
- */
289
- export const splitForCompletion = (input, cursor) => {
290
- const head = input.slice(0, cursor);
291
- const tokens = tokenize(head);
292
- if (tokens.length === 0) {
293
- return { prevWords: [], currentWord: "", replaceLength: 0 };
294
- }
295
- const last = tokens[tokens.length - 1];
296
- // The cursor is sitting inside the last token if (a) it ends exactly at
297
- // cursor AND (b) the last char before cursor isn't whitespace.
298
- const charBefore = head[head.length - 1];
299
- const inWhitespace = charBefore === " " ||
300
- charBefore === "\t" ||
301
- charBefore === "\n" ||
302
- charBefore === "\r";
303
- if (last.end === head.length && !inWhitespace) {
304
- return {
305
- prevWords: tokens.slice(0, -1).map((t) => t.text),
306
- currentWord: last.text,
307
- replaceLength: Array.from(last.text).length,
308
- };
309
- }
310
- return {
311
- prevWords: tokens.map((t) => t.text),
312
- currentWord: "",
313
- replaceLength: 0,
314
- };
216
+ * Slice `input` at `cursor`, then determine what's being completed:
217
+ *
218
+ * - `prevWords`: the completed words ENTIRELY before the cursor.
219
+ * - `currentWord`: the partial word the cursor is sitting in (may be '').
220
+ * - `replaceLength`: number of code points to chop off the input before
221
+ * inserting the completion (= length of currentWord in code points).
222
+ *
223
+ * The tokenizer is run on `input.slice(0, cursor)` and the LAST token, if
224
+ * it ends exactly at cursor and didn't start with whitespace/punctuation,
225
+ * becomes the current word. Otherwise the current word is empty (the user
226
+ * just typed a space and is starting a new word).
227
+ */
228
+ const splitForCompletion = (input, cursor) => {
229
+ const head = input.slice(0, cursor);
230
+ const tokens = tokenize(head);
231
+ if (tokens.length === 0) return {
232
+ prevWords: [],
233
+ currentWord: "",
234
+ replaceLength: 0
235
+ };
236
+ const last = tokens[tokens.length - 1];
237
+ const charBefore = head[head.length - 1];
238
+ const inWhitespace = charBefore === " " || charBefore === " " || charBefore === "\n" || charBefore === "\r";
239
+ if (last.end === head.length && !inWhitespace) return {
240
+ prevWords: tokens.slice(0, -1).map((t) => t.text),
241
+ currentWord: last.text,
242
+ replaceLength: Array.from(last.text).length
243
+ };
244
+ return {
245
+ prevWords: tokens.map((t) => t.text),
246
+ currentWord: "",
247
+ replaceLength: 0
248
+ };
315
249
  };
250
+ //#endregion
251
+ export { HeadMatches, MatchAny, MatchAnyExcept, Matches, TailMatches, splitForCompletion, tokenize, wordMatches };