@yydb/yydb-client 0.0.5 → 0.0.7
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 +1 -1
- package/dist/client.d.ts +8 -2
- package/dist/client.js +72 -25
- package/dist/errors.d.ts +57 -0
- package/dist/errors.js +68 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +2 -1
- package/dist/node.d.ts +5 -2
- package/dist/node.js +13 -3
- package/dist/wire.d.ts +11 -0
- package/dist/wire.js +16 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ browser, WebUI, Electron renderer, or a thin Node process.
|
|
|
14
14
|
| `@yydb/yydb-client` | WebSocket `/wire` | Browsers and WebUI |
|
|
15
15
|
| `@yydb/yydb-client/node` | TCP or WebSocket | Node and Electron |
|
|
16
16
|
|
|
17
|
-
Frame helpers (`encodeFrame`, `
|
|
17
|
+
Frame helpers (`encodeFrame`, `MessageType`, …) are exported for tools and tests.
|
|
18
18
|
|
|
19
19
|
## Example
|
|
20
20
|
|
package/dist/client.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { MicroRegisterPayload, SchemaVersion, WireUdfScalarValue } from './wire.js';
|
|
2
2
|
import type { MicroHostHandler, Transport } from './transport.js';
|
|
3
|
-
export type { Frame, MicroHostInvokePayload, MicroRegisterPayload, MsgTypeCode, SchemaVersion, WireUdfScalarKind, WireUdfScalarValue, } from './wire.js';
|
|
3
|
+
export type { Frame, MicroHostInvokePayload, MicroRegisterPayload, MsgTypeCode, SchemaVersion, WireErrorBody, WireUdfScalarKind, WireUdfScalarValue, } from './wire.js';
|
|
4
4
|
export type { MicroHostHandler } from './transport.js';
|
|
5
|
-
export { MsgType, encodeFrame, decodeFrame } from './wire.js';
|
|
5
|
+
export { MsgType, encodeFrame, decodeFrame, encodeErrorBody, decodeErrorBody } from './wire.js';
|
|
6
|
+
export { YydbError, YydbErrorCodes, isRetryableYydbCode, isYydbError } from './errors.js';
|
|
7
|
+
export type { YydbErrorCode, YydbErrorOptions } from './errors.js';
|
|
6
8
|
export interface ConnectOptions {
|
|
7
9
|
/** `ws://127.0.0.1:7700/wire` or `127.0.0.1:7700` (upgraded to `/wire`). */
|
|
8
10
|
endpoint: string;
|
|
@@ -16,12 +18,16 @@ export declare class Client {
|
|
|
16
18
|
readonly endpoint: string;
|
|
17
19
|
private transport;
|
|
18
20
|
private nextId;
|
|
21
|
+
private closed;
|
|
19
22
|
private constructor();
|
|
20
23
|
/** Construct from an already-open transport (used by Node TCP entry). */
|
|
21
24
|
static fromTransport(endpoint: string, transport: Transport): Client;
|
|
22
25
|
/** Open WebSocket to `yydb serve` `/wire` and Hello-handshake. */
|
|
23
26
|
static connect(options: ConnectOptions | string): Promise<Client>;
|
|
27
|
+
/** Close the transport. Idempotent — further calls fail with `yydb.session.closed`. */
|
|
24
28
|
close(): void;
|
|
29
|
+
/** Whether {@link close} already ran. */
|
|
30
|
+
isClosed(): boolean;
|
|
25
31
|
hello(): Promise<string>;
|
|
26
32
|
serverVersion(): Promise<string>;
|
|
27
33
|
info(): Promise<string>;
|
package/dist/client.js
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
|
-
import { decodeKvGetOk, decodeScalarCallOk, decodeSchemaGetOk, encodeKvGet, encodeKvPut, encodeMicroRegister, encodeScalarCall, encodeSchemaEnsure, MsgType, } from './wire.js';
|
|
1
|
+
import { decodeErrorBody, decodeKvGetOk, decodeScalarCallOk, decodeSchemaGetOk, encodeKvGet, encodeKvPut, encodeMicroRegister, encodeScalarCall, encodeSchemaEnsure, MsgType, } from './wire.js';
|
|
2
2
|
import { openWebSocketTransport } from './ws.js';
|
|
3
|
-
|
|
3
|
+
import { YydbError, YydbErrorCodes } from './errors.js';
|
|
4
|
+
export { MsgType, encodeFrame, decodeFrame, encodeErrorBody, decodeErrorBody } from './wire.js';
|
|
5
|
+
export { YydbError, YydbErrorCodes, isRetryableYydbCode, isYydbError } from './errors.js';
|
|
6
|
+
/** Expected reply type and operation label per request type. */
|
|
7
|
+
const REQUESTS = {
|
|
8
|
+
[MsgType.Hello]: { operation: 'hello', ok: MsgType.HelloOk },
|
|
9
|
+
[MsgType.Info]: { operation: 'info', ok: MsgType.InfoOk },
|
|
10
|
+
[MsgType.SchemaGet]: { operation: 'schemaGet', ok: MsgType.SchemaGetOk },
|
|
11
|
+
[MsgType.SchemaEnsure]: { operation: 'schemaEnsure', ok: MsgType.SchemaEnsureOk },
|
|
12
|
+
[MsgType.KvGet]: { operation: 'kvGet', ok: MsgType.KvGetOk },
|
|
13
|
+
[MsgType.KvPut]: { operation: 'kvPut', ok: MsgType.KvPutOk },
|
|
14
|
+
[MsgType.MicroRegister]: { operation: 'microRegister', ok: MsgType.MicroRegisterOk },
|
|
15
|
+
[MsgType.ScalarCall]: { operation: 'scalarCall', ok: MsgType.ScalarCallOk },
|
|
16
|
+
};
|
|
4
17
|
/**
|
|
5
18
|
* TypeScript client for YY wire (browser = WebSocket `/wire`).
|
|
6
19
|
* Accepts product magic `YYDB` or `YYDS` as equivalent self-claims (same wire);
|
|
@@ -10,6 +23,7 @@ export class Client {
|
|
|
10
23
|
endpoint;
|
|
11
24
|
transport;
|
|
12
25
|
nextId = 1;
|
|
26
|
+
closed = false;
|
|
13
27
|
constructor(endpoint, transport) {
|
|
14
28
|
this.endpoint = endpoint;
|
|
15
29
|
this.transport = transport;
|
|
@@ -22,16 +36,36 @@ export class Client {
|
|
|
22
36
|
static async connect(options) {
|
|
23
37
|
const endpoint = typeof options === 'string' ? options : options.endpoint;
|
|
24
38
|
if (!endpoint.trim()) {
|
|
25
|
-
throw new
|
|
39
|
+
throw new YydbError(YydbErrorCodes.clientInvalidEndpoint, 'empty serve endpoint', {
|
|
40
|
+
operation: 'connect',
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
let transport;
|
|
44
|
+
try {
|
|
45
|
+
transport = await openWebSocketTransport(endpoint);
|
|
46
|
+
}
|
|
47
|
+
catch (cause) {
|
|
48
|
+
throw new YydbError(YydbErrorCodes.clientTransportFailure, `connect ${endpoint}: transport failed`, {
|
|
49
|
+
operation: 'connect',
|
|
50
|
+
cause,
|
|
51
|
+
});
|
|
26
52
|
}
|
|
27
|
-
const transport = await openWebSocketTransport(endpoint);
|
|
28
53
|
const client = new Client(endpoint, transport);
|
|
29
54
|
await client.hello();
|
|
30
55
|
return client;
|
|
31
56
|
}
|
|
57
|
+
/** Close the transport. Idempotent — further calls fail with `yydb.session.closed`. */
|
|
32
58
|
close() {
|
|
59
|
+
if (this.closed) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
this.closed = true;
|
|
33
63
|
this.transport.close();
|
|
34
64
|
}
|
|
65
|
+
/** Whether {@link close} already ran. */
|
|
66
|
+
isClosed() {
|
|
67
|
+
return this.closed;
|
|
68
|
+
}
|
|
35
69
|
async hello() {
|
|
36
70
|
const response = await this.roundtrip(MsgType.Hello, new Uint8Array());
|
|
37
71
|
return new TextDecoder().decode(response.body);
|
|
@@ -72,33 +106,46 @@ export class Client {
|
|
|
72
106
|
return decodeScalarCallOk(response.body);
|
|
73
107
|
}
|
|
74
108
|
async roundtrip(msgType, body) {
|
|
109
|
+
const request = REQUESTS[msgType];
|
|
110
|
+
const operation = request?.operation ?? `msgType(${msgType})`;
|
|
111
|
+
if (this.closed) {
|
|
112
|
+
throw new YydbError(YydbErrorCodes.sessionClosed, `${operation}: YYDB session is closed`, { operation });
|
|
113
|
+
}
|
|
75
114
|
const requestId = this.nextId++;
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
115
|
+
let response;
|
|
116
|
+
try {
|
|
117
|
+
response = await this.transport.send({
|
|
118
|
+
msgType,
|
|
119
|
+
flags: 0,
|
|
120
|
+
requestId,
|
|
121
|
+
body,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
catch (cause) {
|
|
125
|
+
throw new YydbError(YydbErrorCodes.clientTransportFailure, `${operation}: transport failed`, { operation, cause });
|
|
126
|
+
}
|
|
82
127
|
if (response.requestId !== requestId) {
|
|
83
|
-
throw new
|
|
128
|
+
throw new YydbError(YydbErrorCodes.clientRequestIdMismatch, `${operation}: wire response request_id mismatch (got ${response.requestId}, want ${requestId})`, { operation });
|
|
84
129
|
}
|
|
85
130
|
if (response.msgType === MsgType.Error) {
|
|
86
|
-
throw
|
|
131
|
+
throw remoteError(response.body, operation);
|
|
87
132
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
[MsgType.SchemaEnsure]: MsgType.SchemaEnsureOk,
|
|
93
|
-
[MsgType.KvGet]: MsgType.KvGetOk,
|
|
94
|
-
[MsgType.KvPut]: MsgType.KvPutOk,
|
|
95
|
-
[MsgType.MicroRegister]: MsgType.MicroRegisterOk,
|
|
96
|
-
[MsgType.ScalarCall]: MsgType.ScalarCallOk,
|
|
97
|
-
};
|
|
98
|
-
const want = expectedOk[msgType];
|
|
99
|
-
if (want !== undefined && response.msgType !== want) {
|
|
100
|
-
throw new Error(`unexpected wire response type ${response.msgType}`);
|
|
133
|
+
if (request && response.msgType !== request.ok) {
|
|
134
|
+
throw new YydbError(YydbErrorCodes.clientUnexpectedResponse, `${operation}: unexpected wire response type ${response.msgType}`, {
|
|
135
|
+
operation,
|
|
136
|
+
});
|
|
101
137
|
}
|
|
102
138
|
return response;
|
|
103
139
|
}
|
|
104
140
|
}
|
|
141
|
+
/** Turn an `Error` frame into a {@link YydbError}, keeping the engine code. */
|
|
142
|
+
function remoteError(body, operation) {
|
|
143
|
+
let decoded;
|
|
144
|
+
try {
|
|
145
|
+
decoded = decodeErrorBody(body);
|
|
146
|
+
}
|
|
147
|
+
catch (cause) {
|
|
148
|
+
return new YydbError(YydbErrorCodes.clientDecodeFailure, `${operation}: malformed wire error frame`, { operation, cause });
|
|
149
|
+
}
|
|
150
|
+
return new YydbError(decoded.code, decoded.message, { operation });
|
|
151
|
+
}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host-facing YYDB error surface.
|
|
3
|
+
*
|
|
4
|
+
* Every public failure carries a stable `code`: engine codes come from the
|
|
5
|
+
* Rust core (`yydb.schema.invalid`, …) over the wire, client codes are minted
|
|
6
|
+
* locally with the `yydb.client.` prefix. Branch on `code` — `message` is
|
|
7
|
+
* display text only.
|
|
8
|
+
*/
|
|
9
|
+
/** Stable codes known to the TypeScript hosts. */
|
|
10
|
+
export declare const YydbErrorCodes: {
|
|
11
|
+
readonly io: "yydb.io";
|
|
12
|
+
readonly corrupt: "yydb.corrupt";
|
|
13
|
+
readonly schemaConflict: "yydb.schema.conflict";
|
|
14
|
+
readonly schemaInvalid: "yydb.schema.invalid";
|
|
15
|
+
readonly udfNotFound: "yydb.udf.not_found";
|
|
16
|
+
readonly udfArity: "yydb.udf.arity";
|
|
17
|
+
readonly udfFailed: "yydb.udf.failed";
|
|
18
|
+
readonly udfVersionMismatch: "yydb.udf.version_mismatch";
|
|
19
|
+
readonly objectNotFound: "yydb.object.not_found";
|
|
20
|
+
readonly objectCorrupt: "yydb.object.corrupt";
|
|
21
|
+
readonly unsupported: "yydb.unsupported";
|
|
22
|
+
readonly protocol: "yydb.protocol";
|
|
23
|
+
readonly casConflict: "yydb.cas.conflict";
|
|
24
|
+
readonly leaseUnavailable: "yydb.lease.unavailable";
|
|
25
|
+
readonly fencingMismatch: "yydb.fencing.mismatch";
|
|
26
|
+
readonly leaseExpired: "yydb.lease.expired";
|
|
27
|
+
readonly quotaExceeded: "yydb.quota.exceeded";
|
|
28
|
+
readonly sessionClosed: "yydb.session.closed";
|
|
29
|
+
readonly clientInvalidEndpoint: "yydb.client.invalid_endpoint";
|
|
30
|
+
readonly clientTransportFailure: "yydb.client.transport_failure";
|
|
31
|
+
readonly clientRequestIdMismatch: "yydb.client.request_id_mismatch";
|
|
32
|
+
readonly clientUnexpectedResponse: "yydb.client.unexpected_response";
|
|
33
|
+
readonly clientDecodeFailure: "yydb.client.decode_failure";
|
|
34
|
+
};
|
|
35
|
+
/** A known code, or any future code the engine introduces. */
|
|
36
|
+
export type YydbErrorCode = (typeof YydbErrorCodes)[keyof typeof YydbErrorCodes] | (string & {});
|
|
37
|
+
/** Whether `code` may be retried with the same arguments. */
|
|
38
|
+
export declare function isRetryableYydbCode(code: string): boolean;
|
|
39
|
+
/** Options carried by a {@link YydbError}. */
|
|
40
|
+
export interface YydbErrorOptions {
|
|
41
|
+
/** Operation that failed (`schemaGet`, `kvPut`, `query`, …). */
|
|
42
|
+
operation?: string;
|
|
43
|
+
/** Underlying failure when this error wraps one. */
|
|
44
|
+
cause?: unknown;
|
|
45
|
+
}
|
|
46
|
+
/** YYDB failure with a stable machine-readable {@link YydbError.code}. */
|
|
47
|
+
export declare class YydbError extends Error {
|
|
48
|
+
/** Stable code — engine code or `yydb.client.*`. */
|
|
49
|
+
readonly code: string;
|
|
50
|
+
/** Operation that failed. */
|
|
51
|
+
readonly operation: string;
|
|
52
|
+
/** Whether the same call may be retried. */
|
|
53
|
+
readonly retryable: boolean;
|
|
54
|
+
constructor(code: YydbErrorCode, message: string, options?: YydbErrorOptions);
|
|
55
|
+
}
|
|
56
|
+
/** Narrowing helper so callers can branch without `instanceof` imports. */
|
|
57
|
+
export declare function isYydbError(value: unknown): value is YydbError;
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host-facing YYDB error surface.
|
|
3
|
+
*
|
|
4
|
+
* Every public failure carries a stable `code`: engine codes come from the
|
|
5
|
+
* Rust core (`yydb.schema.invalid`, …) over the wire, client codes are minted
|
|
6
|
+
* locally with the `yydb.client.` prefix. Branch on `code` — `message` is
|
|
7
|
+
* display text only.
|
|
8
|
+
*/
|
|
9
|
+
/** Stable codes known to the TypeScript hosts. */
|
|
10
|
+
export const YydbErrorCodes = {
|
|
11
|
+
io: 'yydb.io',
|
|
12
|
+
corrupt: 'yydb.corrupt',
|
|
13
|
+
schemaConflict: 'yydb.schema.conflict',
|
|
14
|
+
schemaInvalid: 'yydb.schema.invalid',
|
|
15
|
+
udfNotFound: 'yydb.udf.not_found',
|
|
16
|
+
udfArity: 'yydb.udf.arity',
|
|
17
|
+
udfFailed: 'yydb.udf.failed',
|
|
18
|
+
udfVersionMismatch: 'yydb.udf.version_mismatch',
|
|
19
|
+
objectNotFound: 'yydb.object.not_found',
|
|
20
|
+
objectCorrupt: 'yydb.object.corrupt',
|
|
21
|
+
unsupported: 'yydb.unsupported',
|
|
22
|
+
protocol: 'yydb.protocol',
|
|
23
|
+
casConflict: 'yydb.cas.conflict',
|
|
24
|
+
leaseUnavailable: 'yydb.lease.unavailable',
|
|
25
|
+
fencingMismatch: 'yydb.fencing.mismatch',
|
|
26
|
+
leaseExpired: 'yydb.lease.expired',
|
|
27
|
+
quotaExceeded: 'yydb.quota.exceeded',
|
|
28
|
+
sessionClosed: 'yydb.session.closed',
|
|
29
|
+
clientInvalidEndpoint: 'yydb.client.invalid_endpoint',
|
|
30
|
+
clientTransportFailure: 'yydb.client.transport_failure',
|
|
31
|
+
clientRequestIdMismatch: 'yydb.client.request_id_mismatch',
|
|
32
|
+
clientUnexpectedResponse: 'yydb.client.unexpected_response',
|
|
33
|
+
clientDecodeFailure: 'yydb.client.decode_failure',
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Codes where retrying the same operation can legitimately succeed: a socket or
|
|
37
|
+
* lease hiccup, or a compare-and-set that lost a benign race. Every other code
|
|
38
|
+
* needs the caller to change input, schema, or wiring first.
|
|
39
|
+
*/
|
|
40
|
+
const RETRYABLE_CODES = new Set([
|
|
41
|
+
YydbErrorCodes.clientTransportFailure,
|
|
42
|
+
YydbErrorCodes.casConflict,
|
|
43
|
+
YydbErrorCodes.leaseUnavailable,
|
|
44
|
+
]);
|
|
45
|
+
/** Whether `code` may be retried with the same arguments. */
|
|
46
|
+
export function isRetryableYydbCode(code) {
|
|
47
|
+
return RETRYABLE_CODES.has(code);
|
|
48
|
+
}
|
|
49
|
+
/** YYDB failure with a stable machine-readable {@link YydbError.code}. */
|
|
50
|
+
export class YydbError extends Error {
|
|
51
|
+
/** Stable code — engine code or `yydb.client.*`. */
|
|
52
|
+
code;
|
|
53
|
+
/** Operation that failed. */
|
|
54
|
+
operation;
|
|
55
|
+
/** Whether the same call may be retried. */
|
|
56
|
+
retryable;
|
|
57
|
+
constructor(code, message, options = {}) {
|
|
58
|
+
super(message, { cause: options.cause });
|
|
59
|
+
this.name = 'YydbError';
|
|
60
|
+
this.code = code;
|
|
61
|
+
this.operation = options.operation ?? 'unknown';
|
|
62
|
+
this.retryable = isRetryableYydbCode(code);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/** Narrowing helper so callers can branch without `instanceof` imports. */
|
|
66
|
+
export function isYydbError(value) {
|
|
67
|
+
return value instanceof YydbError;
|
|
68
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
/** Browser-safe entry (WebSocket). For Node TCP use `@yydb/yydb-client/node`. */
|
|
2
2
|
export { Client } from './client.js';
|
|
3
3
|
export type { ConnectOptions } from './client.js';
|
|
4
|
-
export { MAGIC, MAGIC_YYDB, MAGIC_YYDS, WIRE_VERSION, WIRE_VERSION_NEXT, HEADER_LEN, MsgType, encodeFrame, decodeFrame, encodeSchemaEnsure, encodeKvGet, encodeKvPut, encodeMicroRegister, encodeScalarCall, decodeScalarCallOk, decodeSchemaGetOk, decodeKvGetOk, } from './wire.js';
|
|
5
|
-
export type { Frame, MicroRegisterPayload, MsgTypeCode, ProductMagic, SchemaVersion, WireUdfScalarKind, WireUdfScalarValue } from './wire.js';
|
|
4
|
+
export { MAGIC, MAGIC_YYDB, MAGIC_YYDS, WIRE_VERSION, WIRE_VERSION_NEXT, HEADER_LEN, MsgType, encodeFrame, decodeFrame, encodeSchemaEnsure, encodeKvGet, encodeKvPut, encodeMicroRegister, encodeScalarCall, encodeErrorBody, decodeErrorBody, decodeScalarCallOk, decodeSchemaGetOk, decodeKvGetOk, } from './wire.js';
|
|
5
|
+
export type { Frame, MicroRegisterPayload, MsgTypeCode, ProductMagic, SchemaVersion, WireErrorBody, WireUdfScalarKind, WireUdfScalarValue, } from './wire.js';
|
|
6
|
+
export { YydbError, YydbErrorCodes, isRetryableYydbCode, isYydbError } from './errors.js';
|
|
7
|
+
export type { YydbErrorCode, YydbErrorOptions } from './errors.js';
|
|
8
|
+
export type { MicroHostHandler, Transport } from './transport.js';
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
/** Browser-safe entry (WebSocket). For Node TCP use `@yydb/yydb-client/node`. */
|
|
2
2
|
export { Client } from './client.js';
|
|
3
|
-
export { MAGIC, MAGIC_YYDB, MAGIC_YYDS, WIRE_VERSION, WIRE_VERSION_NEXT, HEADER_LEN, MsgType, encodeFrame, decodeFrame, encodeSchemaEnsure, encodeKvGet, encodeKvPut, encodeMicroRegister, encodeScalarCall, decodeScalarCallOk, decodeSchemaGetOk, decodeKvGetOk, } from './wire.js';
|
|
3
|
+
export { MAGIC, MAGIC_YYDB, MAGIC_YYDS, WIRE_VERSION, WIRE_VERSION_NEXT, HEADER_LEN, MsgType, encodeFrame, decodeFrame, encodeSchemaEnsure, encodeKvGet, encodeKvPut, encodeMicroRegister, encodeScalarCall, encodeErrorBody, decodeErrorBody, decodeScalarCallOk, decodeSchemaGetOk, decodeKvGetOk, } from './wire.js';
|
|
4
|
+
export { YydbError, YydbErrorCodes, isRetryableYydbCode, isYydbError } from './errors.js';
|
package/dist/node.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { Client, type ConnectOptions } from './client.js';
|
|
2
2
|
export { Client } from './client.js';
|
|
3
3
|
export type { ConnectOptions } from './client.js';
|
|
4
|
-
export { MsgType, encodeFrame, decodeFrame, encodeSchemaEnsure, encodeKvGet, encodeKvPut, decodeSchemaGetOk, decodeKvGetOk } from './wire.js';
|
|
5
|
-
export type { Frame, MsgTypeCode, SchemaVersion } from './wire.js';
|
|
4
|
+
export { MsgType, encodeFrame, decodeFrame, encodeSchemaEnsure, encodeKvGet, encodeKvPut, decodeSchemaGetOk, decodeKvGetOk, encodeErrorBody, decodeErrorBody, } from './wire.js';
|
|
5
|
+
export type { Frame, MsgTypeCode, SchemaVersion, WireErrorBody } from './wire.js';
|
|
6
|
+
export { YydbError, YydbErrorCodes, isRetryableYydbCode, isYydbError } from './errors.js';
|
|
7
|
+
export type { YydbErrorCode, YydbErrorOptions } from './errors.js';
|
|
8
|
+
export type { MicroHostHandler, Transport } from './transport.js';
|
|
6
9
|
/** Node entry: TCP by default; `ws://` uses WebSocket. */
|
|
7
10
|
export declare function connect(options: ConnectOptions | string): Promise<Client>;
|
package/dist/node.js
CHANGED
|
@@ -2,8 +2,10 @@ import { Client } from './client.js';
|
|
|
2
2
|
import { openTcpTransport } from './tcp.js';
|
|
3
3
|
import { openWebSocketTransport } from './ws.js';
|
|
4
4
|
import { normalizeWsUrl } from './transport.js';
|
|
5
|
+
import { YydbError, YydbErrorCodes } from './errors.js';
|
|
5
6
|
export { Client } from './client.js';
|
|
6
|
-
export { MsgType, encodeFrame, decodeFrame, encodeSchemaEnsure, encodeKvGet, encodeKvPut, decodeSchemaGetOk, decodeKvGetOk } from './wire.js';
|
|
7
|
+
export { MsgType, encodeFrame, decodeFrame, encodeSchemaEnsure, encodeKvGet, encodeKvPut, decodeSchemaGetOk, decodeKvGetOk, encodeErrorBody, decodeErrorBody, } from './wire.js';
|
|
8
|
+
export { YydbError, YydbErrorCodes, isRetryableYydbCode, isYydbError } from './errors.js';
|
|
7
9
|
function wantsWebSocket(endpoint) {
|
|
8
10
|
const trimmed = endpoint.trim();
|
|
9
11
|
return trimmed.startsWith('ws://') || trimmed.startsWith('wss://');
|
|
@@ -12,9 +14,17 @@ function wantsWebSocket(endpoint) {
|
|
|
12
14
|
export async function connect(options) {
|
|
13
15
|
const endpoint = typeof options === 'string' ? options : options.endpoint;
|
|
14
16
|
if (!endpoint.trim()) {
|
|
15
|
-
throw new
|
|
17
|
+
throw new YydbError(YydbErrorCodes.clientInvalidEndpoint, 'empty serve endpoint', {
|
|
18
|
+
operation: 'connect',
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
let transport;
|
|
22
|
+
try {
|
|
23
|
+
transport = wantsWebSocket(endpoint) ? await openWebSocketTransport(endpoint) : await openTcpTransport(endpoint);
|
|
24
|
+
}
|
|
25
|
+
catch (cause) {
|
|
26
|
+
throw new YydbError(YydbErrorCodes.clientTransportFailure, `connect ${endpoint}: transport failed`, { operation: 'connect', cause });
|
|
16
27
|
}
|
|
17
|
-
const transport = wantsWebSocket(endpoint) ? await openWebSocketTransport(endpoint) : await openTcpTransport(endpoint);
|
|
18
28
|
const client = Client.fromTransport(wantsWebSocket(endpoint) ? normalizeWsUrl(endpoint) : endpoint, transport);
|
|
19
29
|
await client.hello();
|
|
20
30
|
return client;
|
package/dist/wire.d.ts
CHANGED
|
@@ -87,3 +87,14 @@ export interface SchemaVersion {
|
|
|
87
87
|
}
|
|
88
88
|
export declare function decodeSchemaGetOk(body: Uint8Array): SchemaVersion | null;
|
|
89
89
|
export declare function decodeKvGetOk(body: Uint8Array): Uint8Array | null;
|
|
90
|
+
/** Structured `Error` frame body: stable code plus display message. */
|
|
91
|
+
export interface WireErrorBody {
|
|
92
|
+
/** Stable error code (`yydb.schema.invalid`, …). */
|
|
93
|
+
code: string;
|
|
94
|
+
/** Display message; hosts must not branch on this text. */
|
|
95
|
+
message: string;
|
|
96
|
+
}
|
|
97
|
+
/** Encode an `Error` frame body. */
|
|
98
|
+
export declare function encodeErrorBody(error: WireErrorBody): Uint8Array;
|
|
99
|
+
/** Decode an `Error` frame body. */
|
|
100
|
+
export declare function decodeErrorBody(body: Uint8Array): WireErrorBody;
|
package/dist/wire.js
CHANGED
|
@@ -329,3 +329,19 @@ export function decodeKvGetOk(body) {
|
|
|
329
329
|
const valLen = readU32At(body, offset);
|
|
330
330
|
return readBytesAt(body, offset, valLen);
|
|
331
331
|
}
|
|
332
|
+
/** Encode an `Error` frame body. */
|
|
333
|
+
export function encodeErrorBody(error) {
|
|
334
|
+
const out = [];
|
|
335
|
+
pushBytes(out, new TextEncoder().encode(error.code));
|
|
336
|
+
pushBytes(out, new TextEncoder().encode(error.message));
|
|
337
|
+
return Uint8Array.from(out);
|
|
338
|
+
}
|
|
339
|
+
/** Decode an `Error` frame body. */
|
|
340
|
+
export function decodeErrorBody(body) {
|
|
341
|
+
const offset = { n: 0 };
|
|
342
|
+
const codeLen = readU32At(body, offset);
|
|
343
|
+
const code = new TextDecoder().decode(readBytesAt(body, offset, codeLen));
|
|
344
|
+
const messageLen = readU32At(body, offset);
|
|
345
|
+
const message = new TextDecoder().decode(readBytesAt(body, offset, messageLen));
|
|
346
|
+
return { code, message };
|
|
347
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yydb/yydb-client",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"description": "TypeScript client for the YYDB WebSocket and TCP wire protocol",
|
|
5
5
|
"license": "MPL-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
],
|
|
23
23
|
"scripts": {
|
|
24
24
|
"build": "tsc -p tsconfig.json",
|
|
25
|
-
"test": "node --experimental-strip-types --test src
|
|
25
|
+
"test": "pnpm run build && node --experimental-strip-types --test src/**/*.test.ts"
|
|
26
26
|
},
|
|
27
27
|
"repository": {
|
|
28
28
|
"type": "git",
|