neonctl 2.22.2 → 2.23.1

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 (113) hide show
  1. package/README.md +84 -0
  2. package/analytics.js +5 -2
  3. package/commands/branches.js +9 -1
  4. package/commands/connection_string.js +9 -1
  5. package/commands/functions.js +268 -0
  6. package/commands/index.js +4 -0
  7. package/commands/neon_auth.js +1013 -0
  8. package/commands/projects.js +9 -1
  9. package/commands/psql.js +6 -1
  10. package/functions_api.js +43 -0
  11. package/package.json +15 -5
  12. package/psql/cli.js +51 -0
  13. package/psql/command/cmd_cond.js +437 -0
  14. package/psql/command/cmd_connect.js +815 -0
  15. package/psql/command/cmd_copy.js +1025 -0
  16. package/psql/command/cmd_describe.js +1810 -0
  17. package/psql/command/cmd_format.js +909 -0
  18. package/psql/command/cmd_io.js +2187 -0
  19. package/psql/command/cmd_lo.js +385 -0
  20. package/psql/command/cmd_meta.js +970 -0
  21. package/psql/command/cmd_misc.js +187 -0
  22. package/psql/command/cmd_pipeline.js +1141 -0
  23. package/psql/command/cmd_restrict.js +171 -0
  24. package/psql/command/cmd_show.js +751 -0
  25. package/psql/command/dispatch.js +343 -0
  26. package/psql/command/inputQueue.js +42 -0
  27. package/psql/command/shared.js +71 -0
  28. package/psql/complete/filenames.js +139 -0
  29. package/psql/complete/index.js +104 -0
  30. package/psql/complete/matcher.js +314 -0
  31. package/psql/complete/psqlVars.js +247 -0
  32. package/psql/complete/queries.js +491 -0
  33. package/psql/complete/rules.js +2387 -0
  34. package/psql/core/common.js +1250 -0
  35. package/psql/core/help.js +576 -0
  36. package/psql/core/mainloop.js +1353 -0
  37. package/psql/core/prompt.js +437 -0
  38. package/psql/core/settings.js +684 -0
  39. package/psql/core/sqlHelp.js +1066 -0
  40. package/psql/core/startup.js +840 -0
  41. package/psql/core/syncVars.js +116 -0
  42. package/psql/core/variables.js +287 -0
  43. package/psql/describe/formatters.js +1277 -0
  44. package/psql/describe/processNamePattern.js +270 -0
  45. package/psql/describe/queries.js +2373 -0
  46. package/psql/describe/versionGate.js +43 -0
  47. package/psql/index.js +2005 -0
  48. package/psql/io/history.js +299 -0
  49. package/psql/io/input.js +120 -0
  50. package/psql/io/lineEditor/buffer.js +323 -0
  51. package/psql/io/lineEditor/complete.js +227 -0
  52. package/psql/io/lineEditor/filename.js +159 -0
  53. package/psql/io/lineEditor/index.js +891 -0
  54. package/psql/io/lineEditor/keymap.js +738 -0
  55. package/psql/io/lineEditor/vt100.js +363 -0
  56. package/psql/io/pgpass.js +202 -0
  57. package/psql/io/pgservice.js +194 -0
  58. package/psql/io/psqlrc.js +422 -0
  59. package/psql/print/aligned.js +1756 -0
  60. package/psql/print/asciidoc.js +248 -0
  61. package/psql/print/crosstab.js +460 -0
  62. package/psql/print/csv.js +92 -0
  63. package/psql/print/html.js +258 -0
  64. package/psql/print/json.js +96 -0
  65. package/psql/print/latex.js +396 -0
  66. package/psql/print/pager.js +265 -0
  67. package/psql/print/troff.js +258 -0
  68. package/psql/print/unaligned.js +118 -0
  69. package/psql/print/units.js +135 -0
  70. package/psql/scanner/slash.js +513 -0
  71. package/psql/scanner/sql.js +910 -0
  72. package/psql/scanner/stringutils.js +390 -0
  73. package/psql/types/backslash.js +1 -0
  74. package/psql/types/connection.js +1 -0
  75. package/psql/types/index.js +7 -0
  76. package/psql/types/printer.js +1 -0
  77. package/psql/types/repl.js +1 -0
  78. package/psql/types/scanner.js +24 -0
  79. package/psql/types/settings.js +1 -0
  80. package/psql/types/variables.js +1 -0
  81. package/psql/wire/connection.js +2844 -0
  82. package/psql/wire/copy.js +108 -0
  83. package/psql/wire/notify.js +59 -0
  84. package/psql/wire/pipeline.js +519 -0
  85. package/psql/wire/protocol.js +466 -0
  86. package/psql/wire/sasl.js +296 -0
  87. package/psql/wire/tls.js +596 -0
  88. package/test_utils/fixtures.js +1 -0
  89. package/utils/esbuild.js +147 -0
  90. package/utils/psql.js +107 -11
  91. package/utils/zip.js +4 -0
  92. package/writer.js +1 -1
  93. package/commands/auth.test.js +0 -211
  94. package/commands/branches.test.js +0 -460
  95. package/commands/checkout.test.js +0 -170
  96. package/commands/connection_string.test.js +0 -196
  97. package/commands/data_api.test.js +0 -169
  98. package/commands/databases.test.js +0 -39
  99. package/commands/help.test.js +0 -9
  100. package/commands/init.test.js +0 -56
  101. package/commands/ip_allow.test.js +0 -59
  102. package/commands/link.test.js +0 -381
  103. package/commands/operations.test.js +0 -7
  104. package/commands/orgs.test.js +0 -7
  105. package/commands/projects.test.js +0 -144
  106. package/commands/psql.test.js +0 -49
  107. package/commands/roles.test.js +0 -37
  108. package/commands/set_context.test.js +0 -159
  109. package/commands/vpc_endpoints.test.js +0 -69
  110. package/context.test.js +0 -119
  111. package/env.test.js +0 -55
  112. package/utils/formats.test.js +0 -32
  113. package/writer.test.js +0 -104
@@ -0,0 +1,466 @@
1
+ /**
2
+ * PostgreSQL wire protocol message codec — thin adapter over `pg-protocol`.
3
+ *
4
+ * Background (per the plan): the original module was a hand-rolled wire codec
5
+ * for protocol v3.0. We've now swapped the bytes for `pg-protocol@1.14`
6
+ * (node-postgres' upstream codec) and this file is the seam:
7
+ *
8
+ * - Frontend builders (`Query`, `StartupMessage`, `Parse`, `Bind`, …) are
9
+ * re-exports of `serialize.*` adapted to our existing call signatures, so
10
+ * `connection.ts` and `pipeline.ts` keep working unchanged.
11
+ * - `MessageParser` wraps pg-protocol's synchronous `Parser` and reshapes
12
+ * each parsed message into our existing `BackendMessage` union — so the
13
+ * connection-state-machine switches on `.type` ('RowDescription', 'DataRow',
14
+ * …) and reads the same field names ('values', 'tag', 'fields', …) as
15
+ * before.
16
+ *
17
+ * Exceptions (kept hand-rolled):
18
+ *
19
+ * - `CancelRequest`: pg-protocol exposes one as `serialize.cancel`, and it
20
+ * is layout-compatible with our previous output. We delegate to it.
21
+ * - `SSLRequest`: pg-protocol *does* expose this as `serialize.requestSsl`;
22
+ * we re-export it for symmetry. No hand-roll needed.
23
+ * - `StartupMessage`: pg-protocol's `serialize.startup` unconditionally
24
+ * appends `client_encoding=UTF8` to whatever the caller supplies. We
25
+ * defensively filter `client_encoding` from the caller-provided map
26
+ * before passing through, since our connect layer always sets it to UTF8
27
+ * anyway. If it's set to something else, that's a caller bug — assert.
28
+ *
29
+ * Why this shim layer (instead of using pg-protocol directly):
30
+ *
31
+ * - Field-name mapping. pg-protocol's parser emits camelCase names
32
+ * (`'rowDescription'`, `'dataRow'`, …) and uses different field names
33
+ * (`fields` vs `values`, `text` vs `tag`, `processID` vs `processId`).
34
+ * Translating in one place keeps `connection.ts` minimal.
35
+ * - DataRow values. pg-protocol parses every value as a UTF-8 string;
36
+ * our connection layer expects `(Buffer | null)[]` (so binary-format
37
+ * columns can keep their bytes intact later). We re-buffer here.
38
+ * - ErrorResponse / NoticeResponse fields. pg-protocol attaches the parsed
39
+ * fields as named properties on the Error/Notice instance. We re-pack
40
+ * them into the Map<tag,value> shape our `fieldsToNotice` helper consumes.
41
+ */
42
+ import { Buffer } from 'node:buffer';
43
+ // pg-protocol@1.14 ships two entry points via its `exports` map:
44
+ // - `./esm/index.js` (the `"import"` key) — a thin ESM wrapper that does
45
+ // `import * as protocol from '../dist/index.js'`.
46
+ // - `./dist/index.js` (the `"require"` / `"default"` keys) — the actual
47
+ // CJS implementation.
48
+ // On Node 20, resolving `'pg-protocol'` from an ESM context picks the
49
+ // `esm/index.js` wrapper, BUT the surrounding `package.json` has no
50
+ // `"type": "module"` field. Node 20 still parses the wrapper as CJS, hits
51
+ // the `import` syntax, and dies with `SyntaxError: Cannot use import
52
+ // statement outside a module`. Node 22+ honours the `"import"` exports
53
+ // key as a forced-ESM hint and avoids this. To stay portable across both,
54
+ // we import the CJS implementation directly — same pattern node-postgres
55
+ // uses for the `Parser` subpath, and what we already do on the next line.
56
+ import pgProtocol from 'pg-protocol/dist/index.js';
57
+ const { serialize } = pgProtocol;
58
+ import { Parser } from 'pg-protocol/dist/parser.js';
59
+ export class ProtocolError extends Error {
60
+ constructor(message) {
61
+ super(message);
62
+ this.name = 'ProtocolError';
63
+ }
64
+ }
65
+ // ---------------------------------------------------------------------------
66
+ // Frontend message encoders
67
+ //
68
+ // These are thin wrappers over `pg-protocol`'s `serialize.*`. The signatures
69
+ // mirror our previous hand-rolled API so callers in `connection.ts` and
70
+ // `pipeline.ts` don't need to change.
71
+ // ---------------------------------------------------------------------------
72
+ /**
73
+ * StartupMessage. pg-protocol always appends `client_encoding=UTF8`; we
74
+ * strip any incoming `client_encoding` to avoid duplicating it on the wire.
75
+ * Our connect layer always sets UTF8 anyway, so this is a no-op in practice.
76
+ */
77
+ export function StartupMessage(params) {
78
+ const filtered = {};
79
+ for (const [key, value] of Object.entries(params)) {
80
+ if (value === undefined)
81
+ continue;
82
+ if (key === 'client_encoding')
83
+ continue;
84
+ filtered[key] = value;
85
+ }
86
+ return serialize.startup(filtered);
87
+ }
88
+ /** SSLRequest — pg-protocol exposes this as `requestSsl`. */
89
+ export function SSLRequest() {
90
+ return serialize.requestSsl();
91
+ }
92
+ /** CancelRequest — pg-protocol's `cancel` is layout-compatible. */
93
+ export function CancelRequest(processId, secretKey) {
94
+ return serialize.cancel(processId, secretKey);
95
+ }
96
+ /** Query: 'Q' + cstring(sql). */
97
+ export function Query(sql) {
98
+ return serialize.query(sql);
99
+ }
100
+ /** Terminate: 'X' + 4-byte length. */
101
+ export function Terminate() {
102
+ return serialize.end();
103
+ }
104
+ /** Sync: 'S' + 4-byte length. */
105
+ export function Sync() {
106
+ return serialize.sync();
107
+ }
108
+ /** Flush: 'H' + 4-byte length. */
109
+ export function Flush() {
110
+ return serialize.flush();
111
+ }
112
+ /** Parse: 'P' + cstring(name) + cstring(sql) + Int16 nparam + {Int32 oid}*. */
113
+ export function Parse(name, sql, paramTypes) {
114
+ return serialize.parse({ name, text: sql, types: paramTypes });
115
+ }
116
+ /**
117
+ * Bind: 'B' + cstring(portal) + cstring(stmt) + per-value param formats +
118
+ * value array + result formats.
119
+ *
120
+ * Note on parameter formats: pg-protocol's `serialize.bind` auto-detects the
121
+ * format byte per value (`Buffer → 1`, `string/null → 0`); the caller-supplied
122
+ * `paramFormats` arg is therefore ignored. This matches the hand-rolled
123
+ * codec's effective behaviour because every caller passes either an empty
124
+ * array (= "all text", which is what pg-protocol picks for strings) or a
125
+ * format list that already agrees with the value's type.
126
+ *
127
+ * Note on result formats: we want every column in *text* format (`[0]`) for
128
+ * `psql`-style printing. pg-protocol forces a single shared result-format
129
+ * byte (`binary: boolean`); we pass `binary: false` to get text columns
130
+ * regardless of the caller-supplied `resultFormats` array.
131
+ */
132
+ export function Bind(portal, stmt, paramFormats, params, resultFormats) {
133
+ // `paramFormats` and `resultFormats` are read for the rare future caller
134
+ // that might want explicit control; pg-protocol's `serialize.bind` doesn't
135
+ // expose either, so we intentionally drop them on the floor for now.
136
+ void paramFormats;
137
+ void resultFormats;
138
+ return serialize.bind({
139
+ portal,
140
+ statement: stmt,
141
+ values: params,
142
+ binary: false,
143
+ });
144
+ }
145
+ /** Describe: 'D' + byte('S'|'P') + cstring(name). */
146
+ export function Describe(target, name) {
147
+ return serialize.describe({ type: target, name });
148
+ }
149
+ /** Execute: 'E' + cstring(portal) + Int32 maxRows. */
150
+ export function Execute(portal, maxRows) {
151
+ return serialize.execute({ portal, rows: maxRows });
152
+ }
153
+ /** Close: 'C' + byte('S'|'P') + cstring(name). */
154
+ export function Close(target, name) {
155
+ return serialize.close({ type: target, name });
156
+ }
157
+ /** PasswordMessage: 'p' + cstring(password). */
158
+ export function PasswordMessage(password) {
159
+ return serialize.password(password);
160
+ }
161
+ /**
162
+ * SASLInitialResponse: 'p' + cstring(mechanism) + Int32(bodyLen) + body.
163
+ *
164
+ * pg-protocol takes the initial response as a *string*; our SCRAM client
165
+ * hands back a Buffer. SCRAM-SHA-256 messages are ASCII so this round-trips
166
+ * losslessly via UTF-8.
167
+ */
168
+ export function SASLInitialResponse(mechanism, response) {
169
+ return serialize.sendSASLInitialResponseMessage(mechanism, response.toString('utf8'));
170
+ }
171
+ /** SASLResponse: 'p' + opaque body (no NUL terminator). */
172
+ export function SASLResponse(response) {
173
+ return serialize.sendSCRAMClientFinalMessage(response.toString('utf8'));
174
+ }
175
+ /** CopyData: 'd' + opaque bytes. */
176
+ export function CopyData(data) {
177
+ return serialize.copyData(data);
178
+ }
179
+ /** CopyDone: 'c' + 4-byte length. */
180
+ export function CopyDone() {
181
+ return serialize.copyDone();
182
+ }
183
+ /** CopyFail: 'f' + cstring(reason). */
184
+ export function CopyFail(message) {
185
+ return serialize.copyFail(message);
186
+ }
187
+ // ---------------------------------------------------------------------------
188
+ // Backend message parser
189
+ // ---------------------------------------------------------------------------
190
+ /**
191
+ * Streaming framer with a synchronous `feed()` API.
192
+ *
193
+ * Internally delegates to `pg-protocol`'s `Parser` (which exposes a
194
+ * `parse(buffer, callback)` method that emits each completed backend message
195
+ * via callback). On each `feed()` call we drain the parser and collect the
196
+ * emitted messages into an array, reshaping each pg-protocol message into our
197
+ * `BackendMessage` union (so `connection.ts`'s switch arms keep working).
198
+ *
199
+ * pg-protocol's `Parser.parse` calls the callback synchronously for every
200
+ * complete message and keeps incomplete trailing bytes buffered internally.
201
+ * That's a near-perfect match for our previous hand-rolled `MessageParser`.
202
+ */
203
+ export class MessageParser {
204
+ constructor() {
205
+ this.inner = new Parser();
206
+ /** Bytes buffered inside `inner` but not yet emitted. Diagnostic only. */
207
+ this.buffered = 0;
208
+ }
209
+ feed(chunk) {
210
+ const out = [];
211
+ try {
212
+ this.inner.parse(chunk, (msg) => {
213
+ out.push(adaptBackendMessage(msg));
214
+ });
215
+ }
216
+ catch (err) {
217
+ // pg-protocol throws plain `Error` on bad input (unknown auth
218
+ // subtype, truncated frames, …). Normalize to our type.
219
+ throw err instanceof ProtocolError
220
+ ? err
221
+ : new ProtocolError(err instanceof Error ? err.message : String(err));
222
+ }
223
+ // Probe the parser's leftover length via its internal field. Used by a
224
+ // handful of tests for diagnostics; not relied on in production code.
225
+ const probe = this.inner;
226
+ this.buffered = probe.bufferLength ?? 0;
227
+ return out;
228
+ }
229
+ /**
230
+ * Number of bytes buffered but not yet emitted (e.g. partial trailing
231
+ * message). Exposed for tests / diagnostics.
232
+ */
233
+ get bufferedBytes() {
234
+ return this.buffered;
235
+ }
236
+ }
237
+ function adaptBackendMessage(raw) {
238
+ const msg = raw;
239
+ switch (msg.name) {
240
+ case 'authenticationOk':
241
+ return { type: 'AuthenticationOk' };
242
+ case 'authenticationCleartextPassword':
243
+ return { type: 'AuthenticationCleartextPassword' };
244
+ case 'authenticationMD5Password':
245
+ return {
246
+ type: 'AuthenticationMD5Password',
247
+ salt: Buffer.from(msg.salt),
248
+ };
249
+ case 'authenticationSASL':
250
+ return {
251
+ type: 'AuthenticationSASL',
252
+ mechanisms: msg.mechanisms,
253
+ };
254
+ case 'authenticationSASLContinue':
255
+ return {
256
+ type: 'AuthenticationSASLContinue',
257
+ data: Buffer.from(msg.data, 'utf8'),
258
+ };
259
+ case 'authenticationSASLFinal':
260
+ return {
261
+ type: 'AuthenticationSASLFinal',
262
+ data: Buffer.from(msg.data, 'utf8'),
263
+ };
264
+ case 'parameterStatus':
265
+ return {
266
+ type: 'ParameterStatus',
267
+ name: msg.parameterName,
268
+ value: msg.parameterValue,
269
+ };
270
+ case 'backendKeyData':
271
+ return {
272
+ type: 'BackendKeyData',
273
+ processId: msg.processID,
274
+ secretKey: msg.secretKey,
275
+ };
276
+ case 'readyForQuery': {
277
+ const status = msg.status;
278
+ if (status !== 'I' && status !== 'T' && status !== 'E') {
279
+ throw new ProtocolError(`ReadyForQuery: unexpected status ${JSON.stringify(status)}`);
280
+ }
281
+ return { type: 'ReadyForQuery', status };
282
+ }
283
+ case 'rowDescription': {
284
+ const fields = msg.fields.map((f) => ({
285
+ name: f.name,
286
+ tableID: f.tableID,
287
+ columnID: f.columnID,
288
+ dataTypeID: f.dataTypeID,
289
+ dataTypeSize: f.dataTypeSize,
290
+ dataTypeModifier: f.dataTypeModifier,
291
+ format: f.format === 'binary' ? 1 : 0,
292
+ }));
293
+ return { type: 'RowDescription', fields };
294
+ }
295
+ case 'dataRow': {
296
+ // pg-protocol always parses values as UTF-8 strings (or null for SQL
297
+ // NULL). Our connection layer expects (Buffer | null)[] so it can hand
298
+ // text-format columns through `.toString('utf8')` and pass binary
299
+ // columns through unchanged. Re-encode here.
300
+ const fields = msg.fields;
301
+ const values = new Array(fields.length);
302
+ for (let i = 0; i < fields.length; i++) {
303
+ const v = fields[i];
304
+ values[i] = v === null ? null : Buffer.from(v, 'utf8');
305
+ }
306
+ return { type: 'DataRow', values };
307
+ }
308
+ case 'commandComplete':
309
+ return { type: 'CommandComplete', tag: msg.text };
310
+ case 'emptyQuery':
311
+ return { type: 'EmptyQueryResponse' };
312
+ case 'error':
313
+ return { type: 'ErrorResponse', fields: errorOrNoticeFields(msg) };
314
+ case 'notice':
315
+ return { type: 'NoticeResponse', fields: errorOrNoticeFields(msg) };
316
+ case 'notification':
317
+ return {
318
+ type: 'NotificationResponse',
319
+ processId: msg.processId,
320
+ channel: msg.channel,
321
+ payload: msg.payload,
322
+ };
323
+ case 'copyInResponse':
324
+ return adaptCopyResponse('CopyInResponse', msg);
325
+ case 'copyOutResponse':
326
+ return adaptCopyResponse('CopyOutResponse', msg);
327
+ case 'replicationStart':
328
+ // pg-protocol emits a bare marker for the 'W' (CopyBothResponse) byte
329
+ // — its `Parser` recognises the message code but does not decode the
330
+ // payload (overall format + per-column format bytes). The body shape
331
+ // is identical to CopyInResponse / CopyOutResponse but we don't need
332
+ // those fields: the connection layer reacts to CopyBothResponse by
333
+ // raising a clean diagnostic because this client does not implement
334
+ // CopyBoth streaming.
335
+ return { type: 'CopyBothResponse' };
336
+ case 'copyData':
337
+ return { type: 'CopyData', data: Buffer.from(msg.chunk) };
338
+ case 'copyDone':
339
+ return { type: 'CopyDone' };
340
+ case 'noData':
341
+ return { type: 'NoData' };
342
+ case 'parseComplete':
343
+ return { type: 'ParseComplete' };
344
+ case 'bindComplete':
345
+ return { type: 'BindComplete' };
346
+ case 'closeComplete':
347
+ return { type: 'CloseComplete' };
348
+ case 'portalSuspended':
349
+ return { type: 'PortalSuspended' };
350
+ case 'parameterDescription':
351
+ return {
352
+ type: 'ParameterDescription',
353
+ oids: msg.dataTypeIDs,
354
+ };
355
+ default:
356
+ throw new ProtocolError(`Unknown backend message: ${String(msg.name)}`);
357
+ }
358
+ }
359
+ function adaptCopyResponse(type, msg) {
360
+ const binary = msg.binary;
361
+ const columnTypes = msg.columnTypes;
362
+ const columnFormats = columnTypes.map((f) => (f === 1 ? 1 : 0));
363
+ return {
364
+ type,
365
+ overallFormat: binary ? 1 : 0,
366
+ columnFormats,
367
+ };
368
+ }
369
+ /**
370
+ * Re-pack pg-protocol's flattened Error/Notice fields (named properties like
371
+ * `severity`, `code`, `detail`, …) into our previous `Map<tag, value>` shape
372
+ * keyed by the on-wire single-letter tag. The map is consumed by
373
+ * `fieldsToNotice` below.
374
+ */
375
+ function errorOrNoticeFields(msg) {
376
+ const out = new Map();
377
+ // The on-wire tag → field-name mapping is per PG docs §53.8. pg-protocol
378
+ // stores `severity` from BOTH `S` and `V` (it overwrites with `V` if present)
379
+ // — to preserve our previous behaviour (where the map carried both raw
380
+ // tags), we copy S = V = severity when severity is defined. Consumers like
381
+ // `fieldsToNotice` look at V then S so this matches.
382
+ const set = (tag, value) => {
383
+ if (typeof value === 'string')
384
+ out.set(tag, value);
385
+ };
386
+ set('S', msg.severity);
387
+ set('V', msg.severity);
388
+ set('C', msg.code);
389
+ set('M', msg.message);
390
+ set('D', msg.detail);
391
+ set('H', msg.hint);
392
+ set('P', msg.position);
393
+ set('p', msg.internalPosition);
394
+ set('q', msg.internalQuery);
395
+ set('W', msg.where);
396
+ set('s', msg.schema);
397
+ set('t', msg.table);
398
+ set('c', msg.column);
399
+ set('d', msg.dataType);
400
+ set('n', msg.constraint);
401
+ set('F', msg.file);
402
+ set('L', msg.line);
403
+ set('R', msg.routine);
404
+ return out;
405
+ }
406
+ // ---------------------------------------------------------------------------
407
+ // Notice helper (unchanged; consumed by connection.ts)
408
+ // ---------------------------------------------------------------------------
409
+ /**
410
+ * Convert a notice/error field map into a Notice-shaped object. The wire
411
+ * tags are PG's single-letter codes; we promote the ones the Connection
412
+ * interface exposes.
413
+ */
414
+ export function fieldsToNotice(fields) {
415
+ // Per PG docs: V is the non-localized severity (preferred when present),
416
+ // S is the localized severity (always present). Message M is mandatory.
417
+ const severity = fields.get('V') ?? fields.get('S') ?? '';
418
+ const message = fields.get('M') ?? '';
419
+ const out = { severity, message };
420
+ const code = fields.get('C');
421
+ if (code !== undefined)
422
+ out.code = code;
423
+ const detail = fields.get('D');
424
+ if (detail !== undefined)
425
+ out.detail = detail;
426
+ const hint = fields.get('H');
427
+ if (hint !== undefined)
428
+ out.hint = hint;
429
+ const position = fields.get('P');
430
+ if (position !== undefined)
431
+ out.position = position;
432
+ const internalPosition = fields.get('p');
433
+ if (internalPosition !== undefined)
434
+ out.internalPosition = internalPosition;
435
+ const internalQuery = fields.get('q');
436
+ if (internalQuery !== undefined)
437
+ out.internalQuery = internalQuery;
438
+ const where = fields.get('W');
439
+ if (where !== undefined)
440
+ out.where = where;
441
+ const schema = fields.get('s');
442
+ if (schema !== undefined)
443
+ out.schema = schema;
444
+ const table = fields.get('t');
445
+ if (table !== undefined)
446
+ out.table = table;
447
+ const column = fields.get('c');
448
+ if (column !== undefined)
449
+ out.column = column;
450
+ const dataType = fields.get('d');
451
+ if (dataType !== undefined)
452
+ out.dataType = dataType;
453
+ const constraint = fields.get('n');
454
+ if (constraint !== undefined)
455
+ out.constraint = constraint;
456
+ const file = fields.get('F');
457
+ if (file !== undefined)
458
+ out.file = file;
459
+ const line = fields.get('L');
460
+ if (line !== undefined)
461
+ out.line = line;
462
+ const routine = fields.get('R');
463
+ if (routine !== undefined)
464
+ out.routine = routine;
465
+ return out;
466
+ }