@zvndev/powdb-client 0.8.1 → 0.10.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 +124 -2
- package/dist/cjs/errors.d.ts +70 -0
- package/dist/cjs/errors.js +68 -0
- package/dist/cjs/escape.d.ts +54 -0
- package/dist/cjs/escape.js +131 -0
- package/dist/cjs/index.d.ts +393 -0
- package/dist/cjs/index.js +1046 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/pool.d.ts +140 -0
- package/dist/cjs/pool.js +335 -0
- package/dist/cjs/protocol.d.ts +185 -0
- package/dist/cjs/protocol.js +645 -0
- package/dist/cjs/script.d.ts +25 -0
- package/dist/cjs/script.js +69 -0
- package/dist/cjs/typed.d.ts +49 -0
- package/dist/cjs/typed.js +105 -0
- package/dist/errors.d.ts +27 -0
- package/dist/errors.js +30 -0
- package/dist/index.d.ts +137 -4
- package/dist/index.js +255 -77
- package/dist/pool.d.ts +18 -1
- package/dist/pool.js +18 -1
- package/dist/script.d.ts +25 -0
- package/dist/script.js +66 -0
- package/package.json +18 -6
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Statement-aware PowQL script splitting.
|
|
4
|
+
*
|
|
5
|
+
* Mirrors the server-side splitter (`powdb_query::lexer::split_statements`)
|
|
6
|
+
* exactly, so a script file behaves the same whether it is fed to the CLI's
|
|
7
|
+
* `--exec` / `.powql` path or to {@link Client.execScript} over the wire:
|
|
8
|
+
*
|
|
9
|
+
* - `;` splits statements only at the top level.
|
|
10
|
+
* - `;` inside a `"..."` string literal never splits. A backslash inside a
|
|
11
|
+
* string consumes the next character unconditionally (mirroring the
|
|
12
|
+
* PowQL lexer), so `\"` and `\;` stay inside the string.
|
|
13
|
+
* - `#` starts a comment that runs to end-of-line; a `;` inside a comment
|
|
14
|
+
* never splits.
|
|
15
|
+
* - Segments are trimmed; empty segments (leading/doubled/trailing `;`,
|
|
16
|
+
* blank lines) are dropped.
|
|
17
|
+
* - Never errors: an unterminated string simply makes the rest of the
|
|
18
|
+
* input the final segment.
|
|
19
|
+
*/
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.splitStatements = splitStatements;
|
|
22
|
+
/**
|
|
23
|
+
* Split a PowQL script into individual statements.
|
|
24
|
+
*
|
|
25
|
+
* String-literal- and `#`-comment-aware `;` splitting with the exact
|
|
26
|
+
* semantics of the CLI/server splitter (see module docs above).
|
|
27
|
+
*/
|
|
28
|
+
function splitStatements(input) {
|
|
29
|
+
const out = [];
|
|
30
|
+
let start = 0;
|
|
31
|
+
let state = "normal";
|
|
32
|
+
for (let i = 0; i < input.length; i++) {
|
|
33
|
+
const c = input[i];
|
|
34
|
+
switch (state) {
|
|
35
|
+
case "normal":
|
|
36
|
+
if (c === ";") {
|
|
37
|
+
const seg = input.slice(start, i).trim();
|
|
38
|
+
if (seg.length > 0)
|
|
39
|
+
out.push(seg);
|
|
40
|
+
start = i + 1;
|
|
41
|
+
}
|
|
42
|
+
else if (c === '"') {
|
|
43
|
+
state = "in-string";
|
|
44
|
+
}
|
|
45
|
+
else if (c === "#") {
|
|
46
|
+
state = "in-comment";
|
|
47
|
+
}
|
|
48
|
+
break;
|
|
49
|
+
case "in-string":
|
|
50
|
+
// Mirror the lexer: a backslash consumes the next char
|
|
51
|
+
// unconditionally, so `\"` and `\;` stay inside the string.
|
|
52
|
+
if (c === "\\") {
|
|
53
|
+
i++;
|
|
54
|
+
}
|
|
55
|
+
else if (c === '"') {
|
|
56
|
+
state = "normal";
|
|
57
|
+
}
|
|
58
|
+
break;
|
|
59
|
+
case "in-comment":
|
|
60
|
+
if (c === "\n")
|
|
61
|
+
state = "normal";
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const seg = input.slice(start).trim();
|
|
66
|
+
if (seg.length > 0)
|
|
67
|
+
out.push(seg);
|
|
68
|
+
return out;
|
|
69
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional row-level type coercion for `queryTyped`.
|
|
3
|
+
*
|
|
4
|
+
* The server's wire format serialises every value to a string (see
|
|
5
|
+
* `crates/server/src/handler.rs::value_to_display`):
|
|
6
|
+
* Int → decimal digits, e.g. "42" or "-7"
|
|
7
|
+
* Float → Rust `{}` format, e.g. "3.14"
|
|
8
|
+
* Bool → "true" / "false"
|
|
9
|
+
* Str → raw UTF-8 (may contain anything)
|
|
10
|
+
* DateTime → microseconds since Unix epoch as decimal, e.g. "1718928000000000"
|
|
11
|
+
* Uuid → canonical "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
|
12
|
+
* Bytes → "<N bytes>" (LOSSY — see note below)
|
|
13
|
+
*
|
|
14
|
+
* `queryTyped` takes a user-supplied schema and coerces each column string
|
|
15
|
+
* into the corresponding JS value. Rationale for requiring the caller to
|
|
16
|
+
* supply the schema (rather than introspecting): PowDB has no `describe`
|
|
17
|
+
* statement yet, and going via `select` off a system catalog is one extra
|
|
18
|
+
* round trip per typed query. When schema introspection lands, a helper
|
|
19
|
+
* that builds the schema object can be added without changing this API.
|
|
20
|
+
*
|
|
21
|
+
* Bytes caveat: the current wire format is lossy for byte columns (renders
|
|
22
|
+
* `<N bytes>`). Until the wire protocol grows a binary column type, this
|
|
23
|
+
* module throws on `bytes` columns rather than guessing — use `str` in the
|
|
24
|
+
* schema if you just want the `<N bytes>` placeholder.
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* Supported column types. Mirrors the server's `DataType` enum minus the
|
|
28
|
+
* `Bytes` variant, which is intentionally unsupported here.
|
|
29
|
+
*/
|
|
30
|
+
export type ColumnType = "int" | "float" | "bool" | "str" | "datetime" | "uuid";
|
|
31
|
+
/** Map of column name → declared type. Columns not in the map pass through as strings. */
|
|
32
|
+
export type TypedSchema = Record<string, ColumnType>;
|
|
33
|
+
/** Coerced value type. `int` may return `bigint` if the value exceeds Number.MAX_SAFE_INTEGER. */
|
|
34
|
+
export type Coerced = number | bigint | boolean | string | Date | null;
|
|
35
|
+
/** A row of coerced values keyed by column name. */
|
|
36
|
+
export type TypedRow = Record<string, Coerced>;
|
|
37
|
+
/**
|
|
38
|
+
* Coerce a single string value to the JS type declared in `schema`. `null`
|
|
39
|
+
* is returned for the conventional null sentinel — see note at call site.
|
|
40
|
+
* Unknown column types fall through as strings.
|
|
41
|
+
*/
|
|
42
|
+
export declare function coerceValue(column: string, raw: string, type: ColumnType | undefined): Coerced;
|
|
43
|
+
/**
|
|
44
|
+
* Coerce a single result row (a string tuple) into a typed object keyed by
|
|
45
|
+
* column name.
|
|
46
|
+
*/
|
|
47
|
+
export declare function coerceRow(columns: string[], values: string[], schema: TypedSchema): TypedRow;
|
|
48
|
+
/** Coerce every row in a rows-result into typed objects. */
|
|
49
|
+
export declare function coerceRows(columns: string[], rows: string[][], schema: TypedSchema): TypedRow[];
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Optional row-level type coercion for `queryTyped`.
|
|
4
|
+
*
|
|
5
|
+
* The server's wire format serialises every value to a string (see
|
|
6
|
+
* `crates/server/src/handler.rs::value_to_display`):
|
|
7
|
+
* Int → decimal digits, e.g. "42" or "-7"
|
|
8
|
+
* Float → Rust `{}` format, e.g. "3.14"
|
|
9
|
+
* Bool → "true" / "false"
|
|
10
|
+
* Str → raw UTF-8 (may contain anything)
|
|
11
|
+
* DateTime → microseconds since Unix epoch as decimal, e.g. "1718928000000000"
|
|
12
|
+
* Uuid → canonical "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
|
13
|
+
* Bytes → "<N bytes>" (LOSSY — see note below)
|
|
14
|
+
*
|
|
15
|
+
* `queryTyped` takes a user-supplied schema and coerces each column string
|
|
16
|
+
* into the corresponding JS value. Rationale for requiring the caller to
|
|
17
|
+
* supply the schema (rather than introspecting): PowDB has no `describe`
|
|
18
|
+
* statement yet, and going via `select` off a system catalog is one extra
|
|
19
|
+
* round trip per typed query. When schema introspection lands, a helper
|
|
20
|
+
* that builds the schema object can be added without changing this API.
|
|
21
|
+
*
|
|
22
|
+
* Bytes caveat: the current wire format is lossy for byte columns (renders
|
|
23
|
+
* `<N bytes>`). Until the wire protocol grows a binary column type, this
|
|
24
|
+
* module throws on `bytes` columns rather than guessing — use `str` in the
|
|
25
|
+
* schema if you just want the `<N bytes>` placeholder.
|
|
26
|
+
*/
|
|
27
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
|
+
exports.coerceValue = coerceValue;
|
|
29
|
+
exports.coerceRow = coerceRow;
|
|
30
|
+
exports.coerceRows = coerceRows;
|
|
31
|
+
const errors_js_1 = require("./errors.js");
|
|
32
|
+
/**
|
|
33
|
+
* Coerce a single string value to the JS type declared in `schema`. `null`
|
|
34
|
+
* is returned for the conventional null sentinel — see note at call site.
|
|
35
|
+
* Unknown column types fall through as strings.
|
|
36
|
+
*/
|
|
37
|
+
function coerceValue(column, raw, type) {
|
|
38
|
+
// The server's wire format distinguishes NULL from empty string by the
|
|
39
|
+
// literal "null" bareword in scalar displays. For typed columns we treat
|
|
40
|
+
// the single token "null" as NULL; empty string remains empty string for
|
|
41
|
+
// `str` columns (can be an intentional empty value).
|
|
42
|
+
if (type !== "str" && raw === "null")
|
|
43
|
+
return null;
|
|
44
|
+
switch (type) {
|
|
45
|
+
case undefined:
|
|
46
|
+
case "str":
|
|
47
|
+
return raw;
|
|
48
|
+
case "int": {
|
|
49
|
+
if (!/^-?\d+$/.test(raw)) {
|
|
50
|
+
throw new errors_js_1.PowDBError(`queryTyped: column "${column}" declared int but got ${JSON.stringify(raw)}`, "type_coercion_failed");
|
|
51
|
+
}
|
|
52
|
+
// Promote to BigInt when the value doesn't round-trip through Number.
|
|
53
|
+
const n = Number(raw);
|
|
54
|
+
if (Number.isSafeInteger(n))
|
|
55
|
+
return n;
|
|
56
|
+
return BigInt(raw);
|
|
57
|
+
}
|
|
58
|
+
case "float": {
|
|
59
|
+
const n = Number(raw);
|
|
60
|
+
if (!Number.isFinite(n)) {
|
|
61
|
+
throw new errors_js_1.PowDBError(`queryTyped: column "${column}" declared float but got ${JSON.stringify(raw)}`, "type_coercion_failed");
|
|
62
|
+
}
|
|
63
|
+
return n;
|
|
64
|
+
}
|
|
65
|
+
case "bool":
|
|
66
|
+
if (raw === "true")
|
|
67
|
+
return true;
|
|
68
|
+
if (raw === "false")
|
|
69
|
+
return false;
|
|
70
|
+
throw new errors_js_1.PowDBError(`queryTyped: column "${column}" declared bool but got ${JSON.stringify(raw)}`, "type_coercion_failed");
|
|
71
|
+
case "datetime": {
|
|
72
|
+
if (!/^-?\d+$/.test(raw)) {
|
|
73
|
+
throw new errors_js_1.PowDBError(`queryTyped: column "${column}" declared datetime but got ${JSON.stringify(raw)} (expected microseconds since epoch)`, "type_coercion_failed");
|
|
74
|
+
}
|
|
75
|
+
// Server sends microseconds since epoch. JS Date uses milliseconds.
|
|
76
|
+
// BigInt division avoids float precision loss for far-future dates.
|
|
77
|
+
const micros = BigInt(raw);
|
|
78
|
+
const millis = Number(micros / 1000n);
|
|
79
|
+
return new Date(millis);
|
|
80
|
+
}
|
|
81
|
+
case "uuid": {
|
|
82
|
+
// Canonical 8-4-4-4-12 hex, lowercase (per server format).
|
|
83
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(raw)) {
|
|
84
|
+
throw new errors_js_1.PowDBError(`queryTyped: column "${column}" declared uuid but got ${JSON.stringify(raw)}`, "type_coercion_failed");
|
|
85
|
+
}
|
|
86
|
+
return raw;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Coerce a single result row (a string tuple) into a typed object keyed by
|
|
92
|
+
* column name.
|
|
93
|
+
*/
|
|
94
|
+
function coerceRow(columns, values, schema) {
|
|
95
|
+
const out = {};
|
|
96
|
+
for (let i = 0; i < columns.length; i++) {
|
|
97
|
+
const col = columns[i];
|
|
98
|
+
out[col] = coerceValue(col, values[i] ?? "null", schema[col]);
|
|
99
|
+
}
|
|
100
|
+
return out;
|
|
101
|
+
}
|
|
102
|
+
/** Coerce every row in a rows-result into typed objects. */
|
|
103
|
+
function coerceRows(columns, rows, schema) {
|
|
104
|
+
return rows.map((r) => coerceRow(columns, r, schema));
|
|
105
|
+
}
|
package/dist/errors.d.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* is intentionally small — new codes are added only when a caller has a
|
|
7
7
|
* legitimate reason to handle them differently.
|
|
8
8
|
*/
|
|
9
|
+
import type { QueryResult } from "./index.js";
|
|
9
10
|
export type PowDBErrorCode =
|
|
10
11
|
/** TCP/TLS connect, DNS, connect timeout. Transient — safe to retry. */
|
|
11
12
|
"connect_failed"
|
|
@@ -41,3 +42,29 @@ export declare class PowDBError extends Error {
|
|
|
41
42
|
* to branch on `.code` but TypeScript sees `unknown`.
|
|
42
43
|
*/
|
|
43
44
|
export declare function isPowDBError(err: unknown): err is PowDBError;
|
|
45
|
+
/**
|
|
46
|
+
* Failure of one statement inside `client.execScript(...)` (fail-fast mode).
|
|
47
|
+
*
|
|
48
|
+
* `code` mirrors the failing statement's error code (`"query_failed"` for a
|
|
49
|
+
* server Error frame, `"aborted"` for a fired AbortSignal, ...), so the usual
|
|
50
|
+
* `.code` branching keeps working; `cause` carries the underlying error.
|
|
51
|
+
* `statementIndex`/`statement` identify the failing statement within the
|
|
52
|
+
* split script, and `results` holds the successful results of every
|
|
53
|
+
* statement before it, in order.
|
|
54
|
+
*/
|
|
55
|
+
export declare class PowDBScriptError extends PowDBError {
|
|
56
|
+
/** Zero-based index of the failing statement within the split script. */
|
|
57
|
+
readonly statementIndex: number;
|
|
58
|
+
/** Text of the failing statement (as split, trimmed). */
|
|
59
|
+
readonly statement: string;
|
|
60
|
+
/** Results of the statements before the failing one, in order. */
|
|
61
|
+
readonly results: QueryResult[];
|
|
62
|
+
constructor(message: string, code: PowDBErrorCode, details: {
|
|
63
|
+
statementIndex: number;
|
|
64
|
+
statement: string;
|
|
65
|
+
results: QueryResult[];
|
|
66
|
+
cause?: unknown;
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
/** Narrow `unknown` to a PowDBScriptError. */
|
|
70
|
+
export declare function isPowDBScriptError(err: unknown): err is PowDBScriptError;
|
package/dist/errors.js
CHANGED
|
@@ -29,3 +29,33 @@ export class PowDBError extends Error {
|
|
|
29
29
|
export function isPowDBError(err) {
|
|
30
30
|
return err instanceof PowDBError;
|
|
31
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Failure of one statement inside `client.execScript(...)` (fail-fast mode).
|
|
34
|
+
*
|
|
35
|
+
* `code` mirrors the failing statement's error code (`"query_failed"` for a
|
|
36
|
+
* server Error frame, `"aborted"` for a fired AbortSignal, ...), so the usual
|
|
37
|
+
* `.code` branching keeps working; `cause` carries the underlying error.
|
|
38
|
+
* `statementIndex`/`statement` identify the failing statement within the
|
|
39
|
+
* split script, and `results` holds the successful results of every
|
|
40
|
+
* statement before it, in order.
|
|
41
|
+
*/
|
|
42
|
+
export class PowDBScriptError extends PowDBError {
|
|
43
|
+
/** Zero-based index of the failing statement within the split script. */
|
|
44
|
+
statementIndex;
|
|
45
|
+
/** Text of the failing statement (as split, trimmed). */
|
|
46
|
+
statement;
|
|
47
|
+
/** Results of the statements before the failing one, in order. */
|
|
48
|
+
results;
|
|
49
|
+
constructor(message, code, details) {
|
|
50
|
+
super(message, code, { cause: details.cause });
|
|
51
|
+
this.name = "PowDBScriptError";
|
|
52
|
+
this.statementIndex = details.statementIndex;
|
|
53
|
+
this.statement = details.statement;
|
|
54
|
+
this.results = details.results;
|
|
55
|
+
Object.setPrototypeOf(this, PowDBScriptError.prototype);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/** Narrow `unknown` to a PowDBScriptError. */
|
|
59
|
+
export function isPowDBScriptError(err) {
|
|
60
|
+
return err instanceof PowDBScriptError;
|
|
61
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -19,7 +19,7 @@ import { EventEmitter } from "node:events";
|
|
|
19
19
|
import { type SyncRepairAction, type WireRetainedUnit, type WireSyncStatus } from "./protocol.js";
|
|
20
20
|
import { type TypedRow, type TypedSchema } from "./typed.js";
|
|
21
21
|
/** Client library version. Compared to the server's reported version. */
|
|
22
|
-
export declare const CLIENT_VERSION = "0.
|
|
22
|
+
export declare const CLIENT_VERSION = "0.10.0";
|
|
23
23
|
export type QueryResult = {
|
|
24
24
|
kind: "rows";
|
|
25
25
|
columns: string[];
|
|
@@ -43,6 +43,43 @@ export type QueryResult = {
|
|
|
43
43
|
* binds as an int; `null` binds PowQL `null`.
|
|
44
44
|
*/
|
|
45
45
|
export type QueryParam = string | number | bigint | boolean | null;
|
|
46
|
+
/** Options for {@link Client.execScript} / {@link Pool.execScript}. */
|
|
47
|
+
export interface ExecScriptOptions {
|
|
48
|
+
/**
|
|
49
|
+
* When `true`, keep dispatching statements after one fails and return a
|
|
50
|
+
* per-statement outcome array instead of throwing. Defaults to `false`
|
|
51
|
+
* (fail-fast: stop dispatching and reject with a {@link PowDBScriptError}
|
|
52
|
+
* carrying the failing statement's index and the results so far).
|
|
53
|
+
*/
|
|
54
|
+
continueOnError?: boolean;
|
|
55
|
+
/**
|
|
56
|
+
* When `true`, run the whole script atomically: `execScript` opens a
|
|
57
|
+
* transaction before dispatching and sends `commit` only after EVERY
|
|
58
|
+
* statement's reply has arrived successfully — on any failure it sends
|
|
59
|
+
* `rollback` instead, so no statement's effect survives. This is the only
|
|
60
|
+
* all-or-nothing mode: embedding your own `begin`/`commit` in a pipelined
|
|
61
|
+
* script is NOT safe (a trailing `commit` is already on the wire when an
|
|
62
|
+
* earlier error reply arrives, so partial work would commit), and a
|
|
63
|
+
* transactional script containing its own transaction-control statements
|
|
64
|
+
* is rejected up front. Mutually exclusive with `continueOnError`.
|
|
65
|
+
*/
|
|
66
|
+
transactional?: boolean;
|
|
67
|
+
/** Abort the remaining statements (see {@link Client.query}). */
|
|
68
|
+
signal?: AbortSignal;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Per-statement outcome from `execScript(script, { continueOnError: true })`.
|
|
72
|
+
* Array order matches statement order in the script.
|
|
73
|
+
*/
|
|
74
|
+
export type ScriptStatementOutcome = {
|
|
75
|
+
statement: string;
|
|
76
|
+
ok: true;
|
|
77
|
+
result: QueryResult;
|
|
78
|
+
} | {
|
|
79
|
+
statement: string;
|
|
80
|
+
ok: false;
|
|
81
|
+
error: Error;
|
|
82
|
+
};
|
|
46
83
|
/** Unsigned 64-bit sync protocol value. Numbers must be safe non-negative integers. */
|
|
47
84
|
export type SyncU64 = bigint | number;
|
|
48
85
|
/**
|
|
@@ -107,6 +144,21 @@ export interface ClientOptions {
|
|
|
107
144
|
* `tls.connect(port, host, options)`. Defaults to plain TCP.
|
|
108
145
|
*/
|
|
109
146
|
tls?: boolean | tls.ConnectionOptions;
|
|
147
|
+
/**
|
|
148
|
+
* Pipelined ("eager") connect. When `true`, {@link Client.connect}
|
|
149
|
+
* resolves as soon as the socket is open and the Connect frame has been
|
|
150
|
+
* written — it does NOT wait for the server's ConnectOk. Queries issued
|
|
151
|
+
* immediately are queued and written right behind the Connect frame (the
|
|
152
|
+
* server reads frames sequentially, so this is valid on the wire), saving
|
|
153
|
+
* a full round trip on every fresh connection.
|
|
154
|
+
*
|
|
155
|
+
* If the handshake then fails (bad password, protocol mismatch), every
|
|
156
|
+
* queued query rejects with the handshake error and the client closes.
|
|
157
|
+
* `serverVersion` is `""` until the ConnectOk arrives; await
|
|
158
|
+
* {@link Client.ready} if you need the handshake settled. Defaults to
|
|
159
|
+
* `false` (connect blocks until ConnectOk, exactly as before).
|
|
160
|
+
*/
|
|
161
|
+
eager?: boolean;
|
|
110
162
|
}
|
|
111
163
|
/**
|
|
112
164
|
* Event map for {@link Client}. Typed so `client.on("query", ...)` gets
|
|
@@ -156,10 +208,41 @@ export declare class Client extends EventEmitter<ClientEvents> {
|
|
|
156
208
|
private readonly pending;
|
|
157
209
|
private closed;
|
|
158
210
|
private closeError;
|
|
159
|
-
|
|
211
|
+
/** Settled once the Connect→ConnectOk handshake completes (or fails). */
|
|
212
|
+
private handshake;
|
|
213
|
+
/** True once ConnectOk has been received. */
|
|
214
|
+
private handshakeComplete;
|
|
215
|
+
private _serverVersion;
|
|
216
|
+
/**
|
|
217
|
+
* Server version from the ConnectOk frame. For a client opened with
|
|
218
|
+
* `eager: true` this is `""` until the handshake reply arrives (await
|
|
219
|
+
* {@link ready} to guarantee it is populated).
|
|
220
|
+
*/
|
|
221
|
+
get serverVersion(): string;
|
|
160
222
|
private constructor();
|
|
161
|
-
/**
|
|
223
|
+
/**
|
|
224
|
+
* Open a connection, send Connect, and wait for ConnectOk.
|
|
225
|
+
*
|
|
226
|
+
* With `eager: true`, resolve as soon as the Connect frame is written
|
|
227
|
+
* instead — queries may be issued immediately and are pipelined behind
|
|
228
|
+
* the handshake (see {@link ClientOptions.eager} and {@link ready}).
|
|
229
|
+
*/
|
|
162
230
|
static connect(opts: ClientOptions): Promise<Client>;
|
|
231
|
+
/**
|
|
232
|
+
* Resolves once the Connect→ConnectOk handshake has completed; rejects
|
|
233
|
+
* with the handshake error if it failed. For non-eager clients this has
|
|
234
|
+
* already settled by the time {@link connect} returns. Eager callers can
|
|
235
|
+
* await it to learn the handshake outcome without issuing a query.
|
|
236
|
+
*/
|
|
237
|
+
ready(): Promise<void>;
|
|
238
|
+
/**
|
|
239
|
+
* Write the Connect frame and register the handshake as the first entry
|
|
240
|
+
* in the pending queue. The reply-matching machinery is strictly FIFO, so
|
|
241
|
+
* the ConnectOk (or Error) frame is matched to the handshake before any
|
|
242
|
+
* pipelined query sees a reply. On failure, every queued query is
|
|
243
|
+
* rejected with the handshake error and the socket is torn down.
|
|
244
|
+
*/
|
|
245
|
+
private startHandshake;
|
|
163
246
|
/**
|
|
164
247
|
* Run a PowQL statement and return the typed result.
|
|
165
248
|
*
|
|
@@ -216,6 +299,49 @@ export declare class Client extends EventEmitter<ClientEvents> {
|
|
|
216
299
|
queryTyped(query: string, schema: TypedSchema, opts?: {
|
|
217
300
|
signal?: AbortSignal;
|
|
218
301
|
}): Promise<TypedRow[]>;
|
|
302
|
+
/**
|
|
303
|
+
* Execute a multi-statement PowQL script on this connection, pipelined.
|
|
304
|
+
*
|
|
305
|
+
* The script is split with the same statement-aware semantics as the CLI
|
|
306
|
+
* (see {@link splitStatements}: `;` inside string literals and `#`
|
|
307
|
+
* comments never splits; empty statements are dropped). All statements
|
|
308
|
+
* are then written down the single connection back-to-back WITHOUT
|
|
309
|
+
* waiting for each reply — the server reads frames sequentially, so an
|
|
310
|
+
* N-statement script costs one round trip instead of N. Results are
|
|
311
|
+
* collected in statement order.
|
|
312
|
+
*
|
|
313
|
+
* Error handling:
|
|
314
|
+
* - Default (fail-fast): resolve with `QueryResult[]` (one entry per
|
|
315
|
+
* statement). On the first failed statement, stop dispatching further
|
|
316
|
+
* statements and reject with a {@link PowDBScriptError} carrying the
|
|
317
|
+
* failing `statementIndex`, the `statement` text, and the successful
|
|
318
|
+
* `results` so far. NOTE: because dispatch is pipelined, statements
|
|
319
|
+
* already written when the error reply arrives (typically the whole
|
|
320
|
+
* script) still execute server-side. If you need all-or-nothing
|
|
321
|
+
* behavior use `transactional: true` — do NOT embed `begin`/`commit`
|
|
322
|
+
* in the script yourself: the trailing `commit` is already on the
|
|
323
|
+
* wire when an error reply arrives, so it commits the partial work.
|
|
324
|
+
* - `transactional: true`: `execScript` opens the transaction itself,
|
|
325
|
+
* waits for every statement's reply, and only then sends `commit`
|
|
326
|
+
* (or `rollback` if any statement failed), so no statement's effect
|
|
327
|
+
* survives a failure. On failure the {@link PowDBScriptError}'s
|
|
328
|
+
* `results` are the replies received before rollback — their effects
|
|
329
|
+
* are NOT persisted.
|
|
330
|
+
* - `continueOnError: true`: dispatch every statement regardless of
|
|
331
|
+
* failures and resolve with a dense {@link ScriptStatementOutcome}
|
|
332
|
+
* array recording each statement's result or error.
|
|
333
|
+
*
|
|
334
|
+
* Each statement emits the usual `"query"` event.
|
|
335
|
+
*/
|
|
336
|
+
execScript(script: string, opts?: {
|
|
337
|
+
continueOnError?: false;
|
|
338
|
+
transactional?: boolean;
|
|
339
|
+
signal?: AbortSignal;
|
|
340
|
+
}): Promise<QueryResult[]>;
|
|
341
|
+
execScript(script: string, opts: {
|
|
342
|
+
continueOnError: true;
|
|
343
|
+
signal?: AbortSignal;
|
|
344
|
+
}): Promise<ScriptStatementOutcome[]>;
|
|
219
345
|
/**
|
|
220
346
|
* Poll a query on an interval and invoke `onRows` for every successful
|
|
221
347
|
* run. Returns an unsubscribe function. Does NOT deduplicate results —
|
|
@@ -241,6 +367,12 @@ export declare class Client extends EventEmitter<ClientEvents> {
|
|
|
241
367
|
*/
|
|
242
368
|
close(): Promise<void>;
|
|
243
369
|
private send;
|
|
370
|
+
/**
|
|
371
|
+
* Resolve once the socket's write buffer has drained. Also resolves if
|
|
372
|
+
* the client closes while waiting, so a dead peer cannot hang a
|
|
373
|
+
* backpressured `execScript` dispatch loop forever.
|
|
374
|
+
*/
|
|
375
|
+
private drained;
|
|
244
376
|
private onData;
|
|
245
377
|
/** Collapse the chunk queue into a single Buffer. */
|
|
246
378
|
private coalesce;
|
|
@@ -254,7 +386,8 @@ export { MAX_PAYLOAD_SIZE, MAX_ROWS, MAX_COLUMNS, MAX_PARAMS, MAX_SYNC_UNITS, MA
|
|
|
254
386
|
export { escapeLiteral, escapeIdent, ident, powql, PowqlIdent, } from "./escape.js";
|
|
255
387
|
export { Pool } from "./pool.js";
|
|
256
388
|
export type { PoolOptions } from "./pool.js";
|
|
257
|
-
export {
|
|
389
|
+
export { splitStatements } from "./script.js";
|
|
390
|
+
export { PowDBError, isPowDBError, PowDBScriptError, isPowDBScriptError, } from "./errors.js";
|
|
258
391
|
export type { PowDBErrorCode } from "./errors.js";
|
|
259
392
|
export { coerceValue, coerceRow, coerceRows, } from "./typed.js";
|
|
260
393
|
export type { ColumnType, TypedSchema, TypedRow, Coerced, } from "./typed.js";
|