neon 3.0.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 +354 -517
  5. package/dist/_shared/env-core/reuse-secrets.js +159 -203
  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 +573 -690
  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 +639 -719
  28. package/dist/commands/diff.js +156 -200
  29. package/dist/commands/env.js +243 -303
  30. package/dist/commands/functions.js +275 -355
  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 -68
  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 +130 -189
  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 +7 -5
@@ -1,76 +1,54 @@
1
1
  import { boolDisplayOf, renderCellValue } from "./units.js";
2
+ //#region src/psql/print/csv.ts
2
3
  /**
3
- * RFC 4180 CSV printer.
4
- *
5
- * Mirrors print.c `print_csv_text` / `print_csv_vertical`.
6
- *
7
- * - Field separator defaults to `,`; honors `topt.csvFieldSep`. The
8
- * separator must be a single character and may not be `"`, `\n`, or
9
- * `\r` (matches the psql `\pset csv_fieldsep` check).
10
- * - Line ending is always `\n` regardless of `topt.recordSep`, since
11
- * `print_csv_text` writes `'\n'` literally.
12
- * - Header row is printed only when `startTable && !tuplesOnly`
13
- * (matches `print_csv_text` — see the `start_table && !tuples_only`
14
- * guard upstream). `\pset tuples_only true` suppresses the header.
15
- * - No footer in CSV.
16
- * - Expanded mode (`print_csv_vertical`) prints `name,value` lines and
17
- * never emits a standalone header row.
18
- *
19
- * Quoting rule (from `csv_print_field`): wrap in double quotes if the
20
- * value contains the separator, a `"`, `\n`, or `\r`; inside quotes,
21
- * double the embedded `"`. We additionally quote `\.` (psql's COPY
22
- * sentinel) when the separator is `\` or `.`, matching upstream.
23
- */
24
- export const csvPrinter = {
25
- format: "csv",
26
- printQuery(rs, opts, out) {
27
- const topt = opts.topt;
28
- const sep = topt.csvFieldSep !== undefined && topt.csvFieldSep !== ""
29
- ? topt.csvFieldSep
30
- : ",";
31
- if (sep.length !== 1 || sep === '"' || sep === "\n" || sep === "\r") {
32
- throw new RangeError(`csv_fieldsep must be a single character other than '"', '\\n', or '\\r' (got ${JSON.stringify(sep)})`);
33
- }
34
- const nullPrint = opts.nullPrint !== "" ? opts.nullPrint : topt.nullPrint;
35
- const expanded = topt.expanded === "on";
36
- const headers = rs.fields.map((f) => f.name);
37
- const boolDisplay = boolDisplayOf(topt);
38
- const cells = rs.rows.map((row) => row.map((cell, i) => renderCell(cell, nullPrint, topt.numericLocale, rs.fields[i]?.dataTypeID, boolDisplay)));
39
- let outBuf = "";
40
- if (expanded) {
41
- for (const row of cells) {
42
- row.forEach((value, colIdx) => {
43
- outBuf +=
44
- csvField(headers[colIdx], sep) +
45
- sep +
46
- csvField(value, sep) +
47
- "\n";
48
- });
49
- }
50
- }
51
- else {
52
- // Header is gated on startTable && !tuplesOnly (cf. print.c).
53
- if (topt.startTable && !topt.tuplesOnly) {
54
- outBuf += headers.map((h) => csvField(h, sep)).join(sep) + "\n";
55
- }
56
- for (const row of cells) {
57
- outBuf += row.map((c) => csvField(c, sep)).join(sep) + "\n";
58
- }
59
- }
60
- out.write(outBuf);
61
- return Promise.resolve();
62
- },
4
+ * RFC 4180 CSV printer.
5
+ *
6
+ * Mirrors print.c `print_csv_text` / `print_csv_vertical`.
7
+ *
8
+ * - Field separator defaults to `,`; honors `topt.csvFieldSep`. The
9
+ * separator must be a single character and may not be `"`, `\n`, or
10
+ * `\r` (matches the psql `\pset csv_fieldsep` check).
11
+ * - Line ending is always `\n` regardless of `topt.recordSep`, since
12
+ * `print_csv_text` writes `'\n'` literally.
13
+ * - Header row is printed only when `startTable && !tuplesOnly`
14
+ * (matches `print_csv_text` — see the `start_table && !tuples_only`
15
+ * guard upstream). `\pset tuples_only true` suppresses the header.
16
+ * - No footer in CSV.
17
+ * - Expanded mode (`print_csv_vertical`) prints `name,value` lines and
18
+ * never emits a standalone header row.
19
+ *
20
+ * Quoting rule (from `csv_print_field`): wrap in double quotes if the
21
+ * value contains the separator, a `"`, `\n`, or `\r`; inside quotes,
22
+ * double the embedded `"`. We additionally quote `\.` (psql's COPY
23
+ * sentinel) when the separator is `\` or `.`, matching upstream.
24
+ */
25
+ const csvPrinter = {
26
+ format: "csv",
27
+ printQuery(rs, opts, out) {
28
+ const topt = opts.topt;
29
+ const sep = topt.csvFieldSep !== void 0 && topt.csvFieldSep !== "" ? topt.csvFieldSep : ",";
30
+ if (sep.length !== 1 || sep === "\"" || sep === "\n" || sep === "\r") throw new RangeError(`csv_fieldsep must be a single character other than '"', '\\n', or '\\r' (got ${JSON.stringify(sep)})`);
31
+ const nullPrint = opts.nullPrint !== "" ? opts.nullPrint : topt.nullPrint;
32
+ const expanded = topt.expanded === "on";
33
+ const headers = rs.fields.map((f) => f.name);
34
+ const boolDisplay = boolDisplayOf(topt);
35
+ const cells = rs.rows.map((row) => row.map((cell, i) => renderCell(cell, nullPrint, topt.numericLocale, rs.fields[i]?.dataTypeID, boolDisplay)));
36
+ let outBuf = "";
37
+ if (expanded) for (const row of cells) row.forEach((value, colIdx) => {
38
+ outBuf += csvField(headers[colIdx], sep) + sep + csvField(value, sep) + "\n";
39
+ });
40
+ else {
41
+ if (topt.startTable && !topt.tuplesOnly) outBuf += headers.map((h) => csvField(h, sep)).join(sep) + "\n";
42
+ for (const row of cells) outBuf += row.map((c) => csvField(c, sep)).join(sep) + "\n";
43
+ }
44
+ out.write(outBuf);
45
+ return Promise.resolve();
46
+ }
63
47
  };
64
48
  const renderCell = (cell, nullPrint, numericLocale, dataTypeID, boolDisplay) => renderCellValue(cell, nullPrint, numericLocale, dataTypeID, boolDisplay);
65
49
  const csvField = (value, sep) => {
66
- const needsQuote = value.includes(sep) ||
67
- value.includes('"') ||
68
- value.includes("\n") ||
69
- value.includes("\r") ||
70
- value === "\\." ||
71
- sep === "\\" ||
72
- sep === ".";
73
- if (!needsQuote)
74
- return value;
75
- return `"${value.replace(/"/g, '""')}"`;
50
+ if (!(value.includes(sep) || value.includes("\"") || value.includes("\n") || value.includes("\r") || value === "\\." || sep === "\\" || sep === ".")) return value;
51
+ return `"${value.replace(/"/g, "\"\"")}"`;
76
52
  };
53
+ //#endregion
54
+ export { csvPrinter };
@@ -1,240 +1,209 @@
1
1
  import { boolDisplayOf, renderCellValue } from "./units.js";
2
+ //#region src/psql/print/html.ts
2
3
  /**
3
- * HTML printer.
4
- *
5
- * Mirrors print.c `print_html_text` and `print_html_vertical`.
6
- *
7
- * Output shape (flat):
8
- * <table border="1">
9
- * <caption>title</caption>
10
- * <tr>
11
- * <th align="center">col</th>
12
- * ...
13
- * </tr>
14
- * <tr valign="top">
15
- * <td align="left">val</td>
16
- * ...
17
- * </tr>
18
- * ...
19
- * </table>
20
- * <p>(N rows)<br />
21
- * footer<br />
22
- * </p>
23
- *
24
- * Output shape (expanded, `\pset expanded on`):
25
- * <table border="1">
26
- * <caption>title</caption>
27
- *
28
- * <tr><td colspan="2" align="center">Record 1</td></tr>
29
- * <tr valign="top">
30
- * <th>col</th>
31
- * <td align="left">val</td>
32
- * </tr>
33
- * ...
34
- * </table>
35
- * <p>user footer<br />
36
- * </p>
37
- *
38
- * - `topt.tableAttr` is appended to the opening `<table>` tag.
39
- * - `topt.border` becomes the `border="..."` value.
40
- * - `topt.startTable` / `stopTable` gate the prologue/epilogue, allowing
41
- * chunked streaming. For this WP both default to true, so the full
42
- * document is emitted in one call.
43
- * - Right-align numeric columns based on the PG type-OID heuristic
44
- * (`NUMERIC_OIDS`), matching how `print_aligned_text` derives the
45
- * `aligns` array upstream.
46
- * - Cells that are whitespace-only render `&nbsp; ` so the cell still
47
- * takes layout space (verbatim upstream behavior).
48
- * - Expanded mode renders a `<tr><td colspan="2" align="center">Record
49
- * N</td></tr>` separator before each row (or `&nbsp;` placeholder
50
- * when `tuplesOnly`); it never emits the default `(N rows)` footer.
51
- */
52
- // INT2, INT4, INT8, FLOAT4, FLOAT8, NUMERIC, INTERVAL.
53
- const NUMERIC_OIDS = new Set([21, 23, 20, 700, 701, 1700, 1186]);
54
- export const htmlPrinter = {
55
- format: "html",
56
- printQuery(rs, opts, out) {
57
- const topt = opts.topt;
58
- if (topt.expanded === "on") {
59
- return printExpanded(rs, opts, out);
60
- }
61
- return printFlat(rs, opts, out);
62
- },
4
+ * HTML printer.
5
+ *
6
+ * Mirrors print.c `print_html_text` and `print_html_vertical`.
7
+ *
8
+ * Output shape (flat):
9
+ * <table border="1">
10
+ * <caption>title</caption>
11
+ * <tr>
12
+ * <th align="center">col</th>
13
+ * ...
14
+ * </tr>
15
+ * <tr valign="top">
16
+ * <td align="left">val</td>
17
+ * ...
18
+ * </tr>
19
+ * ...
20
+ * </table>
21
+ * <p>(N rows)<br />
22
+ * footer<br />
23
+ * </p>
24
+ *
25
+ * Output shape (expanded, `\pset expanded on`):
26
+ * <table border="1">
27
+ * <caption>title</caption>
28
+ *
29
+ * <tr><td colspan="2" align="center">Record 1</td></tr>
30
+ * <tr valign="top">
31
+ * <th>col</th>
32
+ * <td align="left">val</td>
33
+ * </tr>
34
+ * ...
35
+ * </table>
36
+ * <p>user footer<br />
37
+ * </p>
38
+ *
39
+ * - `topt.tableAttr` is appended to the opening `<table>` tag.
40
+ * - `topt.border` becomes the `border="..."` value.
41
+ * - `topt.startTable` / `stopTable` gate the prologue/epilogue, allowing
42
+ * chunked streaming. For this WP both default to true, so the full
43
+ * document is emitted in one call.
44
+ * - Right-align numeric columns based on the PG type-OID heuristic
45
+ * (`NUMERIC_OIDS`), matching how `print_aligned_text` derives the
46
+ * `aligns` array upstream.
47
+ * - Cells that are whitespace-only render `&nbsp; ` so the cell still
48
+ * takes layout space (verbatim upstream behavior).
49
+ * - Expanded mode renders a `<tr><td colspan="2" align="center">Record
50
+ * N</td></tr>` separator before each row (or `&nbsp;` placeholder
51
+ * when `tuplesOnly`); it never emits the default `(N rows)` footer.
52
+ */
53
+ const NUMERIC_OIDS = /* @__PURE__ */ new Set([
54
+ 21,
55
+ 23,
56
+ 20,
57
+ 700,
58
+ 701,
59
+ 1700,
60
+ 1186
61
+ ]);
62
+ const htmlPrinter = {
63
+ format: "html",
64
+ printQuery(rs, opts, out) {
65
+ if (opts.topt.expanded === "on") return printExpanded(rs, opts, out);
66
+ return printFlat(rs, opts, out);
67
+ }
63
68
  };
64
69
  const printFlat = (rs, opts, out) => {
65
- const topt = opts.topt;
66
- const tuplesOnly = topt.tuplesOnly;
67
- const startTable = topt.startTable;
68
- const stopTable = topt.stopTable;
69
- const nullPrint = opts.nullPrint !== "" ? opts.nullPrint : topt.nullPrint;
70
- const title = opts.title ?? topt.title;
71
- const footers = opts.footers ?? topt.footers;
72
- const headers = rs.fields.map((f) => f.name);
73
- const aligns = rs.fields.map((f) => NUMERIC_OIDS.has(f.dataTypeID) ? "right" : "left");
74
- const boolDisplay = boolDisplayOf(topt);
75
- const cells = rs.rows.map((row) => row.map((cell, i) => renderCell(cell, nullPrint, topt.numericLocale, rs.fields[i]?.dataTypeID, boolDisplay)));
76
- let buf = "";
77
- if (startTable) {
78
- buf += `<table border="${String(topt.border)}"`;
79
- if (topt.tableAttr)
80
- buf += ` ${topt.tableAttr}`;
81
- buf += ">\n";
82
- if (!tuplesOnly && title) {
83
- buf += " <caption>" + escapeHtml(title) + "</caption>\n";
84
- }
85
- if (!tuplesOnly) {
86
- buf += " <tr>\n";
87
- for (const h of headers) {
88
- buf += ' <th align="center">' + escapeHtml(h) + "</th>\n";
89
- }
90
- buf += " </tr>\n";
91
- }
92
- }
93
- for (const row of cells) {
94
- buf += ' <tr valign="top">\n';
95
- row.forEach((value, idx) => {
96
- buf += ` <td align="${aligns[idx]}">`;
97
- if (isWhitespaceOnly(value)) {
98
- buf += "&nbsp; ";
99
- }
100
- else {
101
- buf += escapeHtml(value);
102
- }
103
- buf += "</td>\n";
104
- });
105
- buf += " </tr>\n";
106
- }
107
- if (stopTable) {
108
- buf += "</table>\n";
109
- if (!tuplesOnly) {
110
- const effective = effectiveFooters(rs, topt, footers);
111
- if (effective.length > 0) {
112
- buf += "<p>";
113
- for (const f of effective) {
114
- buf += escapeHtml(f) + "<br />\n";
115
- }
116
- buf += "</p>";
117
- }
118
- }
119
- // Upstream `print_html_text` finishes with an unconditional
120
- // `fputc('\n', fout)` AFTER the (optional) `<p>...</p>` block. The
121
- // trailing newline appears regardless of footers / tuplesOnly.
122
- buf += "\n";
123
- }
124
- out.write(buf);
125
- return Promise.resolve();
70
+ const topt = opts.topt;
71
+ const tuplesOnly = topt.tuplesOnly;
72
+ const startTable = topt.startTable;
73
+ const stopTable = topt.stopTable;
74
+ const nullPrint = opts.nullPrint !== "" ? opts.nullPrint : topt.nullPrint;
75
+ const title = opts.title ?? topt.title;
76
+ const footers = opts.footers ?? topt.footers;
77
+ const headers = rs.fields.map((f) => f.name);
78
+ const aligns = rs.fields.map((f) => NUMERIC_OIDS.has(f.dataTypeID) ? "right" : "left");
79
+ const boolDisplay = boolDisplayOf(topt);
80
+ const cells = rs.rows.map((row) => row.map((cell, i) => renderCell(cell, nullPrint, topt.numericLocale, rs.fields[i]?.dataTypeID, boolDisplay)));
81
+ let buf = "";
82
+ if (startTable) {
83
+ buf += `<table border="${String(topt.border)}"`;
84
+ if (topt.tableAttr) buf += ` ${topt.tableAttr}`;
85
+ buf += ">\n";
86
+ if (!tuplesOnly && title) buf += " <caption>" + escapeHtml(title) + "</caption>\n";
87
+ if (!tuplesOnly) {
88
+ buf += " <tr>\n";
89
+ for (const h of headers) buf += " <th align=\"center\">" + escapeHtml(h) + "</th>\n";
90
+ buf += " </tr>\n";
91
+ }
92
+ }
93
+ for (const row of cells) {
94
+ buf += " <tr valign=\"top\">\n";
95
+ row.forEach((value, idx) => {
96
+ buf += ` <td align="${aligns[idx]}">`;
97
+ if (isWhitespaceOnly(value)) buf += "&nbsp; ";
98
+ else buf += escapeHtml(value);
99
+ buf += "</td>\n";
100
+ });
101
+ buf += " </tr>\n";
102
+ }
103
+ if (stopTable) {
104
+ buf += "</table>\n";
105
+ if (!tuplesOnly) {
106
+ const effective = effectiveFooters(rs, topt, footers);
107
+ if (effective.length > 0) {
108
+ buf += "<p>";
109
+ for (const f of effective) buf += escapeHtml(f) + "<br />\n";
110
+ buf += "</p>";
111
+ }
112
+ }
113
+ buf += "\n";
114
+ }
115
+ out.write(buf);
116
+ return Promise.resolve();
126
117
  };
127
118
  const printExpanded = (rs, opts, out) => {
128
- const topt = opts.topt;
129
- const tuplesOnly = topt.tuplesOnly;
130
- const startTable = topt.startTable;
131
- const stopTable = topt.stopTable;
132
- const nullPrint = opts.nullPrint !== "" ? opts.nullPrint : topt.nullPrint;
133
- const title = opts.title ?? topt.title;
134
- const footers = opts.footers ?? topt.footers;
135
- const headers = rs.fields.map((f) => f.name);
136
- const aligns = rs.fields.map((f) => NUMERIC_OIDS.has(f.dataTypeID) ? "right" : "left");
137
- const boolDisplay = boolDisplayOf(topt);
138
- const cells = rs.rows.map((row) => row.map((cell, i) => renderCell(cell, nullPrint, topt.numericLocale, rs.fields[i]?.dataTypeID, boolDisplay)));
139
- let buf = "";
140
- if (startTable) {
141
- buf += `<table border="${String(topt.border)}"`;
142
- if (topt.tableAttr)
143
- buf += ` ${topt.tableAttr}`;
144
- buf += ">\n";
145
- if (!tuplesOnly && title) {
146
- buf += " <caption>" + escapeHtml(title) + "</caption>\n";
147
- }
148
- }
149
- let record = topt.prior + 1;
150
- cells.forEach((row) => {
151
- if (!tuplesOnly) {
152
- buf += `\n <tr><td colspan="2" align="center">Record ${String(record)}</td></tr>\n`;
153
- record += 1;
154
- }
155
- else {
156
- buf += '\n <tr><td colspan="2">&nbsp;</td></tr>\n';
157
- }
158
- row.forEach((value, idx) => {
159
- buf += ' <tr valign="top">\n';
160
- buf += " <th>" + escapeHtml(headers[idx]) + "</th>\n";
161
- buf += ` <td align="${aligns[idx]}">`;
162
- if (isWhitespaceOnly(value)) {
163
- buf += "&nbsp; ";
164
- }
165
- else {
166
- buf += escapeHtml(value);
167
- }
168
- buf += "</td>\n";
169
- buf += " </tr>\n";
170
- });
171
- });
172
- if (stopTable) {
173
- buf += "</table>\n";
174
- // Expanded mode never emits the default "(N rows)" footer; only
175
- // user-supplied footers appear (matches print_html_vertical).
176
- if (!tuplesOnly && footers && footers.length > 0) {
177
- buf += "<p>";
178
- for (const f of footers) {
179
- buf += escapeHtml(f) + "<br />\n";
180
- }
181
- buf += "</p>";
182
- }
183
- buf += "\n";
184
- }
185
- out.write(buf);
186
- return Promise.resolve();
119
+ const topt = opts.topt;
120
+ const tuplesOnly = topt.tuplesOnly;
121
+ const startTable = topt.startTable;
122
+ const stopTable = topt.stopTable;
123
+ const nullPrint = opts.nullPrint !== "" ? opts.nullPrint : topt.nullPrint;
124
+ const title = opts.title ?? topt.title;
125
+ const footers = opts.footers ?? topt.footers;
126
+ const headers = rs.fields.map((f) => f.name);
127
+ const aligns = rs.fields.map((f) => NUMERIC_OIDS.has(f.dataTypeID) ? "right" : "left");
128
+ const boolDisplay = boolDisplayOf(topt);
129
+ const cells = rs.rows.map((row) => row.map((cell, i) => renderCell(cell, nullPrint, topt.numericLocale, rs.fields[i]?.dataTypeID, boolDisplay)));
130
+ let buf = "";
131
+ if (startTable) {
132
+ buf += `<table border="${String(topt.border)}"`;
133
+ if (topt.tableAttr) buf += ` ${topt.tableAttr}`;
134
+ buf += ">\n";
135
+ if (!tuplesOnly && title) buf += " <caption>" + escapeHtml(title) + "</caption>\n";
136
+ }
137
+ let record = topt.prior + 1;
138
+ cells.forEach((row) => {
139
+ if (!tuplesOnly) {
140
+ buf += `\n <tr><td colspan="2" align="center">Record ${String(record)}</td></tr>\n`;
141
+ record += 1;
142
+ } else buf += "\n <tr><td colspan=\"2\">&nbsp;</td></tr>\n";
143
+ row.forEach((value, idx) => {
144
+ buf += " <tr valign=\"top\">\n";
145
+ buf += " <th>" + escapeHtml(headers[idx]) + "</th>\n";
146
+ buf += ` <td align="${aligns[idx]}">`;
147
+ if (isWhitespaceOnly(value)) buf += "&nbsp; ";
148
+ else buf += escapeHtml(value);
149
+ buf += "</td>\n";
150
+ buf += " </tr>\n";
151
+ });
152
+ });
153
+ if (stopTable) {
154
+ buf += "</table>\n";
155
+ if (!tuplesOnly && footers && footers.length > 0) {
156
+ buf += "<p>";
157
+ for (const f of footers) buf += escapeHtml(f) + "<br />\n";
158
+ buf += "</p>";
159
+ }
160
+ buf += "\n";
161
+ }
162
+ out.write(buf);
163
+ return Promise.resolve();
187
164
  };
188
165
  const effectiveFooters = (rs, topt, footers) => {
189
- if (footers && footers.length > 0)
190
- return footers;
191
- if (topt.defaultFooter) {
192
- const n = rs.rows.length;
193
- return [`(${String(n)} ${n === 1 ? "row" : "rows"})`];
194
- }
195
- return [];
166
+ if (footers && footers.length > 0) return footers;
167
+ if (topt.defaultFooter) {
168
+ const n = rs.rows.length;
169
+ return [`(${String(n)} ${n === 1 ? "row" : "rows"})`];
170
+ }
171
+ return [];
196
172
  };
197
173
  const isWhitespaceOnly = (s) => {
198
- if (s.length === 0)
199
- return true;
200
- for (const ch of s) {
201
- if (ch !== " " && ch !== "\t")
202
- return false;
203
- }
204
- return true;
174
+ if (s.length === 0) return true;
175
+ for (const ch of s) if (ch !== " " && ch !== " ") return false;
176
+ return true;
205
177
  };
206
178
  const escapeHtml = (input) => {
207
- // Upstream `html_escaped_print` walks the string byte-by-byte and
208
- // converts leading spaces (per line) to `&nbsp;` so EXPLAIN output
209
- // stays indented. We replicate that with a stateful pass.
210
- let out = "";
211
- let leadingSpace = true;
212
- for (const ch of input) {
213
- switch (ch) {
214
- case "&":
215
- out += "&amp;";
216
- break;
217
- case "<":
218
- out += "&lt;";
219
- break;
220
- case ">":
221
- out += "&gt;";
222
- break;
223
- case '"':
224
- out += "&quot;";
225
- break;
226
- case "\n":
227
- out += "<br />\n";
228
- break;
229
- case " ":
230
- out += leadingSpace ? "&nbsp;" : " ";
231
- break;
232
- default:
233
- out += ch;
234
- }
235
- if (ch !== " ")
236
- leadingSpace = false;
237
- }
238
- return out;
179
+ let out = "";
180
+ let leadingSpace = true;
181
+ for (const ch of input) {
182
+ switch (ch) {
183
+ case "&":
184
+ out += "&amp;";
185
+ break;
186
+ case "<":
187
+ out += "&lt;";
188
+ break;
189
+ case ">":
190
+ out += "&gt;";
191
+ break;
192
+ case "\"":
193
+ out += "&quot;";
194
+ break;
195
+ case "\n":
196
+ out += "<br />\n";
197
+ break;
198
+ case " ":
199
+ out += leadingSpace ? "&nbsp;" : " ";
200
+ break;
201
+ default: out += ch;
202
+ }
203
+ if (ch !== " ") leadingSpace = false;
204
+ }
205
+ return out;
239
206
  };
240
207
  const renderCell = (cell, nullPrint, numericLocale, dataTypeID, boolDisplay) => renderCellValue(cell, nullPrint, numericLocale, dataTypeID, boolDisplay);
208
+ //#endregion
209
+ export { htmlPrinter };