@yydb/yydb 0.0.12 → 0.0.16

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.
@@ -43,6 +43,38 @@ export interface MigrateOptions {
43
43
  toField: string;
44
44
  }>;
45
45
  }
46
+ /** One consistency finding from {@link Database.doctor}. */
47
+ export interface DoctorIssue {
48
+ severity: 'warn' | 'error';
49
+ code: string;
50
+ message: string;
51
+ key_hint?: string | null;
52
+ }
53
+ /** Per-namespace usage snapshot from {@link Database.doctor}. */
54
+ export interface DoctorNamespaceStats {
55
+ prefix: string;
56
+ record_count: number;
57
+ bytes_used: number;
58
+ object_bytes: number;
59
+ quota_max_bytes?: number | null;
60
+ ttl_expired_pending: number;
61
+ }
62
+ /** Read-only consistency report. */
63
+ export interface DoctorReport {
64
+ file_path?: string | null;
65
+ last_commit_sequence: number;
66
+ last_checkpoint_sequence: number;
67
+ checkpoint_lag_records: number;
68
+ wal_bytes_uncheckpointed: number;
69
+ orphan_object_count: number;
70
+ namespaces: DoctorNamespaceStats[];
71
+ issues: DoctorIssue[];
72
+ }
73
+ /** Summary of a v0 `.yydx` compaction pass. */
74
+ export interface CompactReport {
75
+ checkpointed: boolean;
76
+ orphans_reclaimed: number;
77
+ }
46
78
  /**
47
79
  * Transaction-local handle passed to {@link Database.transaction} callbacks.
48
80
  * All CRUD methods forward to the underlying native handle while the
@@ -108,6 +140,14 @@ export declare class Database {
108
140
  compareExchange(key: string, expected: Uint8Array | string | null, replacement: Uint8Array | string | null): Promise<boolean>;
109
141
  /** Fold the WAL into the main file and truncate the sidecar. */
110
142
  checkpoint(): Promise<void>;
143
+ /** Checkpoint then copy a self-contained `.yydb` main file to `dest`. */
144
+ backup(dest: string): Promise<void>;
145
+ /** Copy main + WAL + SHM without checkpoint; opening `dest` replays the WAL tail. */
146
+ backupWithWal(dest: string): Promise<void>;
147
+ /** Run read-only consistency probes against the database. */
148
+ doctor(): Promise<DoctorReport>;
149
+ /** Fold WAL + reclaim orphan blobs (`.yydx` layouts only). */
150
+ compactYydx(): Promise<CompactReport>;
111
151
  /**
112
152
  * Run `fn` inside an explicit transaction. The transaction commits on
113
153
  * normal return and rolls back on throw. Nested calls are not supported.
@@ -150,6 +150,22 @@ export class Database {
150
150
  async checkpoint() {
151
151
  this.native.checkpoint();
152
152
  }
153
+ /** Checkpoint then copy a self-contained `.yydb` main file to `dest`. */
154
+ async backup(dest) {
155
+ this.native.backup(dest);
156
+ }
157
+ /** Copy main + WAL + SHM without checkpoint; opening `dest` replays the WAL tail. */
158
+ async backupWithWal(dest) {
159
+ this.native.backupWithWal(dest);
160
+ }
161
+ /** Run read-only consistency probes against the database. */
162
+ async doctor() {
163
+ return JSON.parse(this.native.doctor());
164
+ }
165
+ /** Fold WAL + reclaim orphan blobs (`.yydx` layouts only). */
166
+ async compactYydx() {
167
+ return JSON.parse(this.native.compactYydx());
168
+ }
153
169
  /**
154
170
  * Run `fn` inside an explicit transaction. The transaction commits on
155
171
  * normal return and rolls back on throw. Nested calls are not supported.
package/dist/cli/cli.js CHANGED
@@ -1,16 +1,22 @@
1
- import { dirname, join } from 'node:path';
1
+ import fs from 'node:fs';
2
+ import { dirname, join, resolve } from 'node:path';
2
3
  import { fileURLToPath } from 'node:url';
3
4
  import { createCli } from '@vmz/commander';
5
+ import { formatDiagnostic } from '@vmz/diagnostic';
6
+ import { YydbError } from '../shared/errors.js';
4
7
  import { registerInfoCommand } from './info-cmd.js';
5
8
  import { registerInitCommand } from './init-cmd.js';
6
9
  import { registerServeCommand } from './serve-cmd.js';
10
+ import { registerVerifyCommand } from './verify-cmd.js';
7
11
  import { registerVersionCommand } from './version-cmd.js';
12
+ const CLI_CATALOG = JSON.parse(fs.readFileSync(resolve(import.meta.dirname, '../../locales/en-US/cli.json'), 'utf8'));
8
13
  function buildYydbCli() {
9
14
  const localesRoot = join(dirname(fileURLToPath(import.meta.url)), '../../locales');
10
15
  const cli = createCli('yydb').locales(localesRoot).intro('cli.intro');
11
16
  registerVersionCommand(cli);
12
17
  registerInitCommand(cli);
13
18
  registerInfoCommand(cli);
19
+ registerVerifyCommand(cli);
14
20
  registerServeCommand(cli);
15
21
  return cli;
16
22
  }
@@ -19,6 +25,27 @@ export async function runCli(argv) {
19
25
  return await buildYydbCli().parse(argv);
20
26
  }
21
27
  catch (error) {
28
+ const parsed = error instanceof Error
29
+ ? YydbError.fromDiagnosticJson(error.message)
30
+ : null;
31
+ if (parsed) {
32
+ // Structured engine error from N-API / wire / WASM envelope.
33
+ const severity = parsed.retryable ? 'warning' : 'error';
34
+ const text = formatDiagnostic({
35
+ path: '',
36
+ severity,
37
+ code: parsed.code,
38
+ args: parsed.params,
39
+ message: error instanceof Error ? error.message : String(error),
40
+ }, {
41
+ locale: 'en-US',
42
+ catalog: CLI_CATALOG,
43
+ style: 'compact',
44
+ });
45
+ console.error(text);
46
+ return parsed.retryable ? 13 : 1;
47
+ }
48
+ // Plain text fallback (commander parse failures, unknown errors).
22
49
  console.error(`error: ${error instanceof Error ? error.message : String(error)}`);
23
50
  return 1;
24
51
  }
@@ -1,10 +1,17 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { formatDiagnostic } from '@vmz/diagnostic';
1
4
  import { loadNative } from '../native/binding.js';
2
5
  import { requiredPositional } from './options.js';
6
+ const CLI_CATALOG = JSON.parse(fs.readFileSync(path.resolve(import.meta.dirname, '../../locales/en-US/cli.json'), 'utf8'));
3
7
  export function registerInfoCommand(cli) {
4
- cli.command('info', 'cli.cmd.info').action((options) => cmdInfo(options));
8
+ cli.command('info', 'cli.cmd.info')
9
+ .option('--doctor', 'cli.opt.doctor')
10
+ .action((options) => cmdInfo(options));
5
11
  }
6
12
  export function cmdInfo(options) {
7
13
  const dbPath = requiredPositional(options, 'info');
14
+ const wantDoctor = Boolean(options.doctor);
8
15
  const database = loadNative().Database.open(dbPath);
9
16
  const lines = [`path=${database.path() ?? '<memory>'}`];
10
17
  const schemaVersion = database.schemaVersion();
@@ -25,6 +32,28 @@ export function cmdInfo(options) {
25
32
  lines.push(`shm=${shm}`);
26
33
  lines.push(`wal_frames=${database.walFrameCount()}`);
27
34
  console.log(lines.join('\n'));
35
+ if (wantDoctor) {
36
+ const report = JSON.parse(database.doctor());
37
+ if (report.issues.length === 0) {
38
+ console.log(`doctor=clean orphan_objects=${report.orphan_object_count}`);
39
+ }
40
+ else {
41
+ for (const issue of report.issues) {
42
+ const severity = issue.severity === 'error' ? 'error' : 'warning';
43
+ console.error(formatDiagnostic({
44
+ path: dbPath,
45
+ severity,
46
+ code: issue.code,
47
+ message: issue.message,
48
+ }, {
49
+ locale: 'en-US',
50
+ catalog: CLI_CATALOG,
51
+ style: 'compact',
52
+ }));
53
+ }
54
+ console.error(`doctor=issues(${report.issues.length}) orphans=${report.orphan_object_count}`);
55
+ }
56
+ }
28
57
  return 0;
29
58
  }
30
59
  function summarizeDocument(document) {
@@ -0,0 +1,11 @@
1
+ import type { Cli, ParsedOptions } from '@vmz/commander';
2
+ export declare function registerVerifyCommand(cli: Cli): void;
3
+ /**
4
+ * Run read-only consistency probes against a YYDB database file.
5
+ *
6
+ * Exit codes:
7
+ * 0 — clean (no issues)
8
+ * 1 — doctor found at least one error (database may be corrupt)
9
+ * 13 — only warnings present (readable but should be inspected)
10
+ */
11
+ export declare function cmdVerify(options: ParsedOptions): number;
@@ -0,0 +1,73 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { formatDiagnostic } from '@vmz/diagnostic';
4
+ import { loadNative } from '../native/binding.js';
5
+ import { requiredPositional } from './options.js';
6
+ /**
7
+ * Inline CLI catalog — commander's `.locales()` resolution is not exposed
8
+ * outside the CLI builder, so we ship a static copy here. Only one locale is
9
+ * loaded at runtime; future locale awareness should resolve the chosen locale
10
+ * from commander internals once they surface it.
11
+ */
12
+ const CLI_CATALOG = JSON.parse(fs.readFileSync(path.resolve(import.meta.dirname, '../../locales/en-US/cli.json'), 'utf8'));
13
+ const DIAGNOSTIC_STYLE = 'compact';
14
+ const DIAGNOSTIC_LOCALE = 'en-US';
15
+ export function registerVerifyCommand(cli) {
16
+ cli.command('verify', 'cli.cmd.verify').action((options) => cmdVerify(options));
17
+ }
18
+ /**
19
+ * Run read-only consistency probes against a YYDB database file.
20
+ *
21
+ * Exit codes:
22
+ * 0 — clean (no issues)
23
+ * 1 — doctor found at least one error (database may be corrupt)
24
+ * 13 — only warnings present (readable but should be inspected)
25
+ */
26
+ export function cmdVerify(options) {
27
+ const dbPath = requiredPositional(options, 'verify');
28
+ let report;
29
+ try {
30
+ const native = loadNative();
31
+ const db = native.Database.open(dbPath);
32
+ report = JSON.parse(db.doctor());
33
+ }
34
+ catch (err) {
35
+ const message = err instanceof Error ? err.message : String(err);
36
+ console.error(formatDiagnostic({
37
+ path: dbPath,
38
+ severity: 'error',
39
+ code: 'cli.err.open_failed',
40
+ args: { path: dbPath, reason: message },
41
+ }, {
42
+ locale: DIAGNOSTIC_LOCALE,
43
+ catalog: CLI_CATALOG,
44
+ style: DIAGNOSTIC_STYLE,
45
+ }));
46
+ return 1;
47
+ }
48
+ for (const issue of report.issues) {
49
+ const severity = issue.severity === 'error' ? 'error' : 'warning';
50
+ console.error(formatDiagnostic({
51
+ path: dbPath,
52
+ severity,
53
+ code: issue.code,
54
+ message: issue.message,
55
+ }, {
56
+ locale: DIAGNOSTIC_LOCALE,
57
+ catalog: CLI_CATALOG,
58
+ style: DIAGNOSTIC_STYLE,
59
+ }));
60
+ }
61
+ const errCount = report.issues.filter((i) => i.severity === 'error').length;
62
+ const warnCount = report.issues.length - errCount;
63
+ if (errCount > 0) {
64
+ console.log(`exit=1 errors=${errCount} warnings=${warnCount}`);
65
+ return 1;
66
+ }
67
+ if (warnCount > 0) {
68
+ console.log(`exit=13 errors=0 warnings=${warnCount}`);
69
+ return 13;
70
+ }
71
+ console.log(`exit=0 clean last_commit=${report.last_commit_sequence}`);
72
+ return 0;
73
+ }
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { Database, type OpenOptions, type SchemaVersion } from './api/database.js';
2
- export { YydbError, YydbErrorCodes, isRetryableYydbCode, isYydbError } from './shared/errors.js';
2
+ export { YydbError, YydbErrorCodes, YydbClientErrorCodes, isYydbError } from './shared/errors.js';
3
3
  export type { YydbErrorCode, YydbErrorOptions } from './shared/errors.js';
4
4
  export { defineMicro, type DefinedMicro, type MicroHandle, type TsMicroDefinitionInput, MicroSessionRegistry } from './shared/micro.js';
5
5
  export { type InferUdfArgs, type InferUdfReturn, type MicroEffect, type MicroInvocationMode, type UdfScalarDescriptor, type UdfScalarKind, } from './shared/udf-types.js';
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export { Database } from './api/database.js';
2
- export { YydbError, YydbErrorCodes, isRetryableYydbCode, isYydbError } from './shared/errors.js';
2
+ export { YydbError, YydbErrorCodes, YydbClientErrorCodes, isYydbError } from './shared/errors.js';
3
3
  export { defineMicro, MicroSessionRegistry } from './shared/micro.js';
4
4
  export { nativeBindingName, platformKey, resolveYydbBinary, resolveYydbCli } from './native/resolve.js';
5
5
  export * from './shared/vos.js';
@@ -38,6 +38,14 @@ export interface NativeDatabase {
38
38
  executeVosWrite(source: string): void;
39
39
  /** Publish a new schema version, optionally with rename map. */
40
40
  migrateSchema(document: string, renamesJson?: string | null): void;
41
+ /** Checkpoint + copy self-contained `.yydb` main file (no WAL sidecars). */
42
+ backup(dest: string): void;
43
+ /** Copy main + WAL + SHM without checkpoint (opening replays the tail). */
44
+ backupWithWal(dest: string): void;
45
+ /** Read-only consistency probe; returns DoctorReport JSON. */
46
+ doctor(): string;
47
+ /** Fold WAL + reclaim orphan blobs (`.yydx` only); returns CompactReport JSON. */
48
+ compactYydx(): string;
41
49
  registerMicro(name: string, version: number, args: string[], returns: string, hostId: number, functionId: string, handleVersion: number, fingerprint: Uint8Array): void;
42
50
  callScalar(name: string, version: number, argsJson: string): string;
43
51
  }
@@ -2,12 +2,57 @@
2
2
  * Host-facing YYDB error surface.
3
3
  *
4
4
  * Every public failure carries a stable `code`: engine codes come from the
5
- * Rust core (`yydb.schema.invalid`, …) over N-API or the wire, client codes
6
- * are minted locally with the `yydb.client.` prefix. Branch on `code` —
7
- * `message` is display text only.
5
+ * Rust core (`yydb.schema.invalid`, …) over N-API or the wire; client codes
6
+ * are minted locally with the `yydb.client.` prefix. **Branch on `code`** —
7
+ * `message` is display text only and must never be used for retry decisions.
8
+ *
9
+ * ## Where codes live
10
+ *
11
+ * Engine codes live in Rust (`yydb_types::Error` variants → DiagnosticEnvelope).
12
+ * The TS side no longer keeps a hardcoded engine-code table — when Rust gains a
13
+ * new variant, hosts pick up the new code string via {@link fromDiagnosticJson}
14
+ * without any TS change. Client codes (`yydb.client.*`) are minted only on the
15
+ * host side and remain available as constants below.
16
+ */
17
+ /**
18
+ * Client-side error codes that never originate from Rust.
19
+ *
20
+ * Engine codes (`yydb.schema.*`, `yydb.udf.*`, `yydb.io`, …) are emitted by
21
+ * Rust and should be matched via string literals or from the diagnostic JSON
22
+ * envelope — they are intentionally not duplicated here.
23
+ */
24
+ export declare const YydbClientErrorCodes: {
25
+ /** serve() endpoint is empty or invalid. */
26
+ readonly clientInvalidEndpoint: "yydb.client.invalid_endpoint";
27
+ /** connect() socket / transport layer failure. */
28
+ readonly clientTransportFailure: "yydb.client.transport_failure";
29
+ /** Server replied with a request id we did not send. */
30
+ readonly clientRequestIdMismatch: "yydb.client.request_id_mismatch";
31
+ /** Server replied with a msg_type we did not request. */
32
+ readonly clientUnexpectedResponse: "yydb.client.unexpected_response";
33
+ /** Could not parse an Error frame body from the wire. */
34
+ readonly clientDecodeFailure: "yydb.client.decode_failure";
35
+ /** The YYDB session is already closed. */
36
+ readonly sessionClosed: "yydb.session.closed";
37
+ };
38
+ /**
39
+ * @deprecated Engine codes moved to Rust — use string literals or match on
40
+ * {@link YydbError.code} returned from {@link fromDiagnosticJson}. This table
41
+ * exists only for transition; new engine error variants will **not** be added here.
8
42
  */
9
- /** Stable codes known to the TypeScript hosts. */
10
43
  export declare const YydbErrorCodes: {
44
+ /** serve() endpoint is empty or invalid. */
45
+ readonly clientInvalidEndpoint: "yydb.client.invalid_endpoint";
46
+ /** connect() socket / transport layer failure. */
47
+ readonly clientTransportFailure: "yydb.client.transport_failure";
48
+ /** Server replied with a request id we did not send. */
49
+ readonly clientRequestIdMismatch: "yydb.client.request_id_mismatch";
50
+ /** Server replied with a msg_type we did not request. */
51
+ readonly clientUnexpectedResponse: "yydb.client.unexpected_response";
52
+ /** Could not parse an Error frame body from the wire. */
53
+ readonly clientDecodeFailure: "yydb.client.decode_failure";
54
+ /** The YYDB session is already closed. */
55
+ readonly sessionClosed: "yydb.session.closed";
11
56
  readonly io: "yydb.io";
12
57
  readonly corrupt: "yydb.corrupt";
13
58
  readonly schemaConflict: "yydb.schema.conflict";
@@ -25,21 +70,23 @@ export declare const YydbErrorCodes: {
25
70
  readonly fencingMismatch: "yydb.fencing.mismatch";
26
71
  readonly leaseExpired: "yydb.lease.expired";
27
72
  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
73
  };
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;
74
+ /** Any stable YYDB error code string — engine, client, or future variant. */
75
+ export type YydbErrorCode = string;
39
76
  /** Options carried by a {@link YydbError}. */
40
77
  export interface YydbErrorOptions {
41
78
  /** Operation that failed (`schemaGet`, `kvPut`, `query`, …). */
42
79
  operation?: string;
80
+ /**
81
+ * Whether the caller may retry with the same arguments.
82
+ *
83
+ * When omitted, constructor defaults to `false` (conservative). Callers
84
+ * that mint their own errors (especially client-local retryable ones like
85
+ * `yydb.client.transport_failure`) **must** set this explicitly. Engine
86
+ * errors coming through {@link fromDiagnosticJson} always carry the Rust-
87
+ * authoritative `retryable` value and override the default.
88
+ */
89
+ retryable?: boolean;
43
90
  /** Structured parameters for catalog templating (when present). */
44
91
  params?: Readonly<Record<string, string>>;
45
92
  /** Underlying failure when this error wraps one. */
@@ -47,7 +94,7 @@ export interface YydbErrorOptions {
47
94
  }
48
95
  /** YYDB failure with a stable machine-readable {@link YydbError.code}. */
49
96
  export declare class YydbError extends Error {
50
- /** Stable code — engine code or `yydb.client.*`. */
97
+ /** Stable code — engine code (from Rust) or `yydb.client.*`. */
51
98
  readonly code: string;
52
99
  /** Operation that failed. */
53
100
  readonly operation: string;
@@ -2,12 +2,46 @@
2
2
  * Host-facing YYDB error surface.
3
3
  *
4
4
  * Every public failure carries a stable `code`: engine codes come from the
5
- * Rust core (`yydb.schema.invalid`, …) over N-API or the wire, client codes
6
- * are minted locally with the `yydb.client.` prefix. Branch on `code` —
7
- * `message` is display text only.
5
+ * Rust core (`yydb.schema.invalid`, …) over N-API or the wire; client codes
6
+ * are minted locally with the `yydb.client.` prefix. **Branch on `code`** —
7
+ * `message` is display text only and must never be used for retry decisions.
8
+ *
9
+ * ## Where codes live
10
+ *
11
+ * Engine codes live in Rust (`yydb_types::Error` variants → DiagnosticEnvelope).
12
+ * The TS side no longer keeps a hardcoded engine-code table — when Rust gains a
13
+ * new variant, hosts pick up the new code string via {@link fromDiagnosticJson}
14
+ * without any TS change. Client codes (`yydb.client.*`) are minted only on the
15
+ * host side and remain available as constants below.
16
+ */
17
+ /**
18
+ * Client-side error codes that never originate from Rust.
19
+ *
20
+ * Engine codes (`yydb.schema.*`, `yydb.udf.*`, `yydb.io`, …) are emitted by
21
+ * Rust and should be matched via string literals or from the diagnostic JSON
22
+ * envelope — they are intentionally not duplicated here.
23
+ */
24
+ export const YydbClientErrorCodes = {
25
+ /** serve() endpoint is empty or invalid. */
26
+ clientInvalidEndpoint: 'yydb.client.invalid_endpoint',
27
+ /** connect() socket / transport layer failure. */
28
+ clientTransportFailure: 'yydb.client.transport_failure',
29
+ /** Server replied with a request id we did not send. */
30
+ clientRequestIdMismatch: 'yydb.client.request_id_mismatch',
31
+ /** Server replied with a msg_type we did not request. */
32
+ clientUnexpectedResponse: 'yydb.client.unexpected_response',
33
+ /** Could not parse an Error frame body from the wire. */
34
+ clientDecodeFailure: 'yydb.client.decode_failure',
35
+ /** The YYDB session is already closed. */
36
+ sessionClosed: 'yydb.session.closed',
37
+ };
38
+ /**
39
+ * @deprecated Engine codes moved to Rust — use string literals or match on
40
+ * {@link YydbError.code} returned from {@link fromDiagnosticJson}. This table
41
+ * exists only for transition; new engine error variants will **not** be added here.
8
42
  */
9
- /** Stable codes known to the TypeScript hosts. */
10
43
  export const YydbErrorCodes = {
44
+ // Engine codes (deprecated)
11
45
  io: 'yydb.io',
12
46
  corrupt: 'yydb.corrupt',
13
47
  schemaConflict: 'yydb.schema.conflict',
@@ -25,30 +59,12 @@ export const YydbErrorCodes = {
25
59
  fencingMismatch: 'yydb.fencing.mismatch',
26
60
  leaseExpired: 'yydb.lease.expired',
27
61
  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',
62
+ // Client codes (still canonical for client-local errors)
63
+ ...YydbClientErrorCodes,
34
64
  };
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
65
  /** YYDB failure with a stable machine-readable {@link YydbError.code}. */
50
66
  export class YydbError extends Error {
51
- /** Stable code — engine code or `yydb.client.*`. */
67
+ /** Stable code — engine code (from Rust) or `yydb.client.*`. */
52
68
  code;
53
69
  /** Operation that failed. */
54
70
  operation;
@@ -61,8 +77,10 @@ export class YydbError extends Error {
61
77
  this.name = 'YydbError';
62
78
  this.code = code;
63
79
  this.operation = options.operation ?? 'unknown';
64
- this.retryable = isRetryableYydbCode(code);
65
- this.params = options.params;
80
+ this.retryable = options.retryable ?? false;
81
+ if (options.params !== undefined) {
82
+ this.params = options.params;
83
+ }
66
84
  }
67
85
  /**
68
86
  * Parse the diagnostic JSON that N-API and WASM surfaces embed in their
@@ -106,23 +124,28 @@ export class YydbError extends Error {
106
124
  else {
107
125
  return null;
108
126
  }
109
- const code = typeof obj.code === 'string' ? obj.code : null;
110
- const message = typeof obj.message === 'string' ? obj.message : '';
127
+ // WASM envelopes nest the diagnostic under `{ ok, error: {...} }`.
128
+ let diag = obj;
129
+ if (typeof obj['ok'] === 'boolean' && !obj['ok'] && obj['error'] && typeof obj['error'] === 'object') {
130
+ diag = obj['error'];
131
+ }
132
+ const code = typeof diag['code'] === 'string' ? diag['code'] : null;
133
+ const message = typeof diag['message'] === 'string' ? diag['message'] : '';
111
134
  if (!code)
112
135
  return null;
113
- const operation = typeof obj.operation === 'string' ? obj.operation : undefined;
114
- const retryable = typeof obj.retryable === 'boolean' ? obj.retryable : undefined;
115
- const params = obj.params && typeof obj.params === 'object'
116
- ? obj.params
136
+ const operation = typeof diag['operation'] === 'string' ? diag['operation'] : undefined;
137
+ const retryable = typeof diag['retryable'] === 'boolean' ? diag['retryable'] : undefined;
138
+ const params = diag['params'] && typeof diag['params'] === 'object'
139
+ ? diag['params']
117
140
  : undefined;
118
- const err = new YydbError(code, message, {
119
- operation,
120
- params,
121
- });
122
- if (typeof retryable === 'boolean') {
123
- err.retryable = retryable;
124
- }
125
- return err;
141
+ const opts = {};
142
+ if (operation !== undefined)
143
+ opts.operation = operation;
144
+ if (retryable !== undefined)
145
+ opts.retryable = retryable;
146
+ if (params !== undefined)
147
+ opts.params = params;
148
+ return new YydbError(code, message, opts);
126
149
  }
127
150
  }
128
151
  /** Narrowing helper so callers can branch without `instanceof` imports. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yydb/yydb",
3
- "version": "0.0.12",
3
+ "version": "0.0.16",
4
4
  "description": "YYDB Node embedded engine and CLI",
5
5
  "license": "MPL-2.0",
6
6
  "type": "module",
@@ -59,9 +59,9 @@
59
59
  "node": ">=18"
60
60
  },
61
61
  "optionalDependencies": {
62
- "@yydb/yydb-darwin-arm64": "0.0.12",
63
- "@yydb/yydb-darwin-x64": "0.0.12",
64
- "@yydb/yydb-linux-x64": "0.0.12",
65
- "@yydb/yydb-win32-x64": "0.0.12"
62
+ "@yydb/yydb-darwin-arm64": "0.0.16",
63
+ "@yydb/yydb-darwin-x64": "0.0.16",
64
+ "@yydb/yydb-linux-x64": "0.0.16",
65
+ "@yydb/yydb-win32-x64": "0.0.16"
66
66
  }
67
67
  }