@zvndev/powdb-client 0.12.0 → 0.13.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 +96 -6
- package/dist/cjs/index.d.ts +87 -3
- package/dist/cjs/index.js +192 -2
- package/dist/cjs/protocol.d.ts +55 -0
- package/dist/cjs/protocol.js +405 -33
- package/dist/index.d.ts +87 -3
- package/dist/index.js +190 -1
- package/dist/protocol.d.ts +55 -0
- package/dist/protocol.js +404 -32
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -22,7 +22,28 @@ import { PowDBError, PowDBScriptError, isPowDBError } from "./errors.js";
|
|
|
22
22
|
import { splitStatements } from "./script.js";
|
|
23
23
|
import { coerceRows, } from "./typed.js";
|
|
24
24
|
/** Client library version. Compared to the server's reported version. */
|
|
25
|
-
export const CLIENT_VERSION = "0.
|
|
25
|
+
export const CLIENT_VERSION = "0.13.0";
|
|
26
|
+
/**
|
|
27
|
+
* The maximum catalog format version this client can read. State this as the
|
|
28
|
+
* `catalogVersion` in sync pull requests: the server accepts any replica whose
|
|
29
|
+
* maximum is at least its active catalog format and rejects an older replica.
|
|
30
|
+
* When validating a server-reported catalog version, accept anything at or
|
|
31
|
+
* below this and reject anything newer (the client cannot read it).
|
|
32
|
+
*/
|
|
33
|
+
export const SUPPORTED_CATALOG_VERSION = 6;
|
|
34
|
+
/**
|
|
35
|
+
* Throw when a server-reported catalog format is newer than this client can
|
|
36
|
+
* read. Accepts `serverCatalogVersion <= SUPPORTED_CATALOG_VERSION`; rejects a
|
|
37
|
+
* newer server, which requires upgrading the client.
|
|
38
|
+
*/
|
|
39
|
+
export function assertServerCatalogVersionSupported(serverCatalogVersion, clientMax = SUPPORTED_CATALOG_VERSION) {
|
|
40
|
+
if (!Number.isInteger(serverCatalogVersion) || serverCatalogVersion < 1) {
|
|
41
|
+
throw new Error(`invalid server catalog version ${serverCatalogVersion}`);
|
|
42
|
+
}
|
|
43
|
+
if (serverCatalogVersion > clientMax) {
|
|
44
|
+
throw new Error(`server catalog format v${serverCatalogVersion} is newer than this client supports (max v${clientMax}); upgrade the client`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
26
47
|
/** Transaction-control statements a `transactional` script may not contain. */
|
|
27
48
|
const TX_CONTROL_RE = /^(begin|commit|rollback)\b/i;
|
|
28
49
|
/**
|
|
@@ -54,6 +75,67 @@ function toWireParam(p) {
|
|
|
54
75
|
throw new PowDBError(`unsupported query parameter type: ${typeof p}`, "protocol_error");
|
|
55
76
|
}
|
|
56
77
|
}
|
|
78
|
+
function fromWireValue(value) {
|
|
79
|
+
switch (value.type) {
|
|
80
|
+
case "empty":
|
|
81
|
+
return null;
|
|
82
|
+
case "int":
|
|
83
|
+
return value.value >= BigInt(Number.MIN_SAFE_INTEGER) &&
|
|
84
|
+
value.value <= BigInt(Number.MAX_SAFE_INTEGER)
|
|
85
|
+
? Number(value.value)
|
|
86
|
+
: value.value;
|
|
87
|
+
case "float":
|
|
88
|
+
case "bool":
|
|
89
|
+
case "str":
|
|
90
|
+
return value.value;
|
|
91
|
+
case "datetime":
|
|
92
|
+
return value.value;
|
|
93
|
+
case "uuid": {
|
|
94
|
+
const hex = Array.from(value.value, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
95
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
96
|
+
}
|
|
97
|
+
case "bytes":
|
|
98
|
+
return new Uint8Array(value.value);
|
|
99
|
+
case "json":
|
|
100
|
+
return value.value;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function nativeQueryResult(reply) {
|
|
104
|
+
switch (reply.type) {
|
|
105
|
+
case "ResultRowsNative":
|
|
106
|
+
return {
|
|
107
|
+
kind: "rows",
|
|
108
|
+
columns: reply.columns,
|
|
109
|
+
rows: reply.rows.map((row) => row.map(fromWireValue)),
|
|
110
|
+
};
|
|
111
|
+
case "ResultScalarNative":
|
|
112
|
+
return { kind: "scalar", value: fromWireValue(reply.value) };
|
|
113
|
+
case "ResultOk":
|
|
114
|
+
return { kind: "ok", affected: reply.affected };
|
|
115
|
+
case "ResultMessage":
|
|
116
|
+
return { kind: "message", message: reply.message };
|
|
117
|
+
case "Error":
|
|
118
|
+
throw new PowDBError(`query failed: ${reply.message}`, "query_failed");
|
|
119
|
+
default:
|
|
120
|
+
throw new PowDBError(`unexpected reply to native query: ${reply.type}`, "protocol_error");
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function rawNativeQueryResult(reply) {
|
|
124
|
+
switch (reply.type) {
|
|
125
|
+
case "ResultRowsNative":
|
|
126
|
+
return { kind: "rows", columns: reply.columns, rows: reply.rows };
|
|
127
|
+
case "ResultScalarNative":
|
|
128
|
+
return { kind: "scalar", value: reply.value };
|
|
129
|
+
case "ResultOk":
|
|
130
|
+
return { kind: "ok", affected: reply.affected };
|
|
131
|
+
case "ResultMessage":
|
|
132
|
+
return { kind: "message", message: reply.message };
|
|
133
|
+
case "Error":
|
|
134
|
+
throw new PowDBError(`query failed: ${reply.message}`, "query_failed");
|
|
135
|
+
default:
|
|
136
|
+
throw new PowDBError(`unexpected reply to native query: ${reply.type}`, "protocol_error");
|
|
137
|
+
}
|
|
138
|
+
}
|
|
57
139
|
function toU64(value, label) {
|
|
58
140
|
if (typeof value === "bigint") {
|
|
59
141
|
if (value < 0n || value > 0xffffffffffffffffn) {
|
|
@@ -289,6 +371,90 @@ export class Client extends EventEmitter {
|
|
|
289
371
|
throw err;
|
|
290
372
|
}
|
|
291
373
|
}
|
|
374
|
+
/**
|
|
375
|
+
* Run PowQL over the lossless typed wire surface. Unlike {@link query},
|
|
376
|
+
* cells are not stringified: bytes remain bytes, JSON is recursive data,
|
|
377
|
+
* and unsafe integers remain bigint. This method never retries as a legacy
|
|
378
|
+
* query, because replaying a mutation after an ambiguous response is unsafe.
|
|
379
|
+
*/
|
|
380
|
+
async queryNative(query, paramsOrOpts, maybeOpts) {
|
|
381
|
+
const hasParams = Array.isArray(paramsOrOpts);
|
|
382
|
+
const params = hasParams ? paramsOrOpts : undefined;
|
|
383
|
+
const opts = hasParams
|
|
384
|
+
? maybeOpts
|
|
385
|
+
: paramsOrOpts;
|
|
386
|
+
const start = Date.now();
|
|
387
|
+
try {
|
|
388
|
+
const request = params === undefined
|
|
389
|
+
? { type: "QueryNative", query }
|
|
390
|
+
: {
|
|
391
|
+
type: "QueryWithParamsNative",
|
|
392
|
+
query,
|
|
393
|
+
params: params.map(toWireParam),
|
|
394
|
+
};
|
|
395
|
+
const result = nativeQueryResult(await this.send(request, opts));
|
|
396
|
+
this.emit("query", {
|
|
397
|
+
query,
|
|
398
|
+
durationMs: Date.now() - start,
|
|
399
|
+
ok: true,
|
|
400
|
+
kind: result.kind,
|
|
401
|
+
});
|
|
402
|
+
return result;
|
|
403
|
+
}
|
|
404
|
+
catch (err) {
|
|
405
|
+
this.emit("query", {
|
|
406
|
+
query,
|
|
407
|
+
durationMs: Date.now() - start,
|
|
408
|
+
ok: false,
|
|
409
|
+
error: err,
|
|
410
|
+
});
|
|
411
|
+
throw err;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Like {@link queryNative}, but returns every cell as the raw
|
|
416
|
+
* {@link WireValue} tagged union with no conversion to {@link NativeValue}.
|
|
417
|
+
*
|
|
418
|
+
* Reach for this only when the convenience conversion would erase something
|
|
419
|
+
* you need: the raw PJ1 bytes of a JSON cell (`pj1`), or the distinction
|
|
420
|
+
* between an absent value (`{ type: "empty" }`), the string `"null"`, and a
|
|
421
|
+
* JSON null (all three of which {@link queryNative} maps to `null`). For
|
|
422
|
+
* ordinary reads, {@link queryNative} is friendlier.
|
|
423
|
+
*/
|
|
424
|
+
async queryNativeRaw(query, paramsOrOpts, maybeOpts) {
|
|
425
|
+
const hasParams = Array.isArray(paramsOrOpts);
|
|
426
|
+
const params = hasParams ? paramsOrOpts : undefined;
|
|
427
|
+
const opts = hasParams
|
|
428
|
+
? maybeOpts
|
|
429
|
+
: paramsOrOpts;
|
|
430
|
+
const start = Date.now();
|
|
431
|
+
try {
|
|
432
|
+
const request = params === undefined
|
|
433
|
+
? { type: "QueryNative", query }
|
|
434
|
+
: {
|
|
435
|
+
type: "QueryWithParamsNative",
|
|
436
|
+
query,
|
|
437
|
+
params: params.map(toWireParam),
|
|
438
|
+
};
|
|
439
|
+
const result = rawNativeQueryResult(await this.send(request, opts));
|
|
440
|
+
this.emit("query", {
|
|
441
|
+
query,
|
|
442
|
+
durationMs: Date.now() - start,
|
|
443
|
+
ok: true,
|
|
444
|
+
kind: result.kind,
|
|
445
|
+
});
|
|
446
|
+
return result;
|
|
447
|
+
}
|
|
448
|
+
catch (err) {
|
|
449
|
+
this.emit("query", {
|
|
450
|
+
query,
|
|
451
|
+
durationMs: Date.now() - start,
|
|
452
|
+
ok: false,
|
|
453
|
+
error: err,
|
|
454
|
+
});
|
|
455
|
+
throw err;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
292
458
|
/**
|
|
293
459
|
* Run a SQL statement through the server-side SQL frontend. The plain
|
|
294
460
|
* {@link query} method remains PowQL for wire compatibility.
|
|
@@ -334,6 +500,29 @@ export class Client extends EventEmitter {
|
|
|
334
500
|
throw err;
|
|
335
501
|
}
|
|
336
502
|
}
|
|
503
|
+
/** Run SQL through the lossless typed wire surface without legacy replay. */
|
|
504
|
+
async querySqlNative(query, opts) {
|
|
505
|
+
const start = Date.now();
|
|
506
|
+
try {
|
|
507
|
+
const result = nativeQueryResult(await this.send({ type: "QuerySqlNative", query }, opts));
|
|
508
|
+
this.emit("query", {
|
|
509
|
+
query,
|
|
510
|
+
durationMs: Date.now() - start,
|
|
511
|
+
ok: true,
|
|
512
|
+
kind: result.kind,
|
|
513
|
+
});
|
|
514
|
+
return result;
|
|
515
|
+
}
|
|
516
|
+
catch (err) {
|
|
517
|
+
this.emit("query", {
|
|
518
|
+
query,
|
|
519
|
+
durationMs: Date.now() - start,
|
|
520
|
+
ok: false,
|
|
521
|
+
error: err,
|
|
522
|
+
});
|
|
523
|
+
throw err;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
337
526
|
/**
|
|
338
527
|
* Fetch primary-side sync status for one embedded replica cursor.
|
|
339
528
|
*
|
package/dist/protocol.d.ts
CHANGED
|
@@ -32,6 +32,11 @@ export declare const MSG_RESULT_MSG = 11;
|
|
|
32
32
|
export declare const MSG_DISCONNECT = 16;
|
|
33
33
|
export declare const MSG_PING = 17;
|
|
34
34
|
export declare const MSG_PONG = 18;
|
|
35
|
+
export declare const MSG_QUERY_NATIVE = 19;
|
|
36
|
+
export declare const MSG_QUERY_PARAMS_NATIVE = 20;
|
|
37
|
+
export declare const MSG_QUERY_SQL_NATIVE = 21;
|
|
38
|
+
export declare const MSG_RESULT_ROWS_NATIVE = 22;
|
|
39
|
+
export declare const MSG_RESULT_SCALAR_NATIVE = 23;
|
|
35
40
|
/** Maximum payload size accepted from the wire (64 MB). */
|
|
36
41
|
export declare const MAX_PAYLOAD_SIZE: number;
|
|
37
42
|
/** Maximum number of rows allowed in a single result message. */
|
|
@@ -70,6 +75,39 @@ export type WireParam = {
|
|
|
70
75
|
tag: "str";
|
|
71
76
|
value: string;
|
|
72
77
|
};
|
|
78
|
+
/** Recursively decoded PJ1 JSON. Integers outside JS's safe range are bigint. */
|
|
79
|
+
export type NativeJson = null | boolean | number | bigint | string | NativeJson[] | {
|
|
80
|
+
[key: string]: NativeJson;
|
|
81
|
+
};
|
|
82
|
+
/** Lossless typed cell used internally by the native result protocol. */
|
|
83
|
+
export type WireValue = {
|
|
84
|
+
type: "empty";
|
|
85
|
+
} | {
|
|
86
|
+
type: "int";
|
|
87
|
+
value: bigint;
|
|
88
|
+
} | {
|
|
89
|
+
type: "float";
|
|
90
|
+
value: number;
|
|
91
|
+
} | {
|
|
92
|
+
type: "bool";
|
|
93
|
+
value: boolean;
|
|
94
|
+
} | {
|
|
95
|
+
type: "str";
|
|
96
|
+
value: string;
|
|
97
|
+
} | {
|
|
98
|
+
type: "datetime";
|
|
99
|
+
value: bigint;
|
|
100
|
+
} | {
|
|
101
|
+
type: "uuid";
|
|
102
|
+
value: Uint8Array;
|
|
103
|
+
} | {
|
|
104
|
+
type: "bytes";
|
|
105
|
+
value: Uint8Array;
|
|
106
|
+
} | {
|
|
107
|
+
type: "json";
|
|
108
|
+
value: NativeJson;
|
|
109
|
+
pj1: Uint8Array;
|
|
110
|
+
};
|
|
73
111
|
export type SyncRepairAction = "none" | "pull" | "awaitArchive" | "rebootstrap";
|
|
74
112
|
export interface WireSyncStatus {
|
|
75
113
|
replicaId: string;
|
|
@@ -116,6 +154,16 @@ export type Message = {
|
|
|
116
154
|
type: "QueryWithParams";
|
|
117
155
|
query: string;
|
|
118
156
|
params: WireParam[];
|
|
157
|
+
} | {
|
|
158
|
+
type: "QueryNative";
|
|
159
|
+
query: string;
|
|
160
|
+
} | {
|
|
161
|
+
type: "QueryWithParamsNative";
|
|
162
|
+
query: string;
|
|
163
|
+
params: WireParam[];
|
|
164
|
+
} | {
|
|
165
|
+
type: "QuerySqlNative";
|
|
166
|
+
query: string;
|
|
119
167
|
} | {
|
|
120
168
|
type: "SyncStatus";
|
|
121
169
|
replicaId: string;
|
|
@@ -157,6 +205,13 @@ export type Message = {
|
|
|
157
205
|
} | {
|
|
158
206
|
type: "ResultScalar";
|
|
159
207
|
value: string;
|
|
208
|
+
} | {
|
|
209
|
+
type: "ResultRowsNative";
|
|
210
|
+
columns: string[];
|
|
211
|
+
rows: WireValue[][];
|
|
212
|
+
} | {
|
|
213
|
+
type: "ResultScalarNative";
|
|
214
|
+
value: WireValue;
|
|
160
215
|
} | {
|
|
161
216
|
type: "ResultOk";
|
|
162
217
|
affected: bigint;
|