@yydb/yyds 0.0.0 → 0.0.2

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/bin/yyds.mjs ADDED
@@ -0,0 +1,114 @@
1
+ #!/usr/bin/env node
2
+ import { loadYydsNative } from "../src/node/load.ts";
3
+ import { parseStartOptions, runNodeStart } from "../src/node/runtime.ts";
4
+
5
+ function usage() {
6
+ return "usage: yyds <init|start|status|stop> --data-dir DIR [--cluster-id ID --node-id ID] [--redis-port PORT] [--pgsql-port PORT] [--mysql-port PORT] [--wire-port PORT]";
7
+ }
8
+
9
+ function parseCommonArgs(command, args) {
10
+ const options = { dataDir: "", clusterId: "", nodeId: "", redisPort: 6379, pgsqlPort: undefined, mysqlPort: undefined, wirePort: undefined };
11
+ for (let index = 0; index < args.length; index += 1) {
12
+ const flag = args[index];
13
+ const value = args[index + 1];
14
+ if (!value || value.startsWith("--")) throw new Error(`missing value for ${flag}`);
15
+ switch (flag) {
16
+ case "--data-dir":
17
+ options.dataDir = value;
18
+ index += 1;
19
+ break;
20
+ case "--cluster-id":
21
+ options.clusterId = value;
22
+ index += 1;
23
+ break;
24
+ case "--node-id":
25
+ options.nodeId = value;
26
+ index += 1;
27
+ break;
28
+ case "--redis-port":
29
+ if (command !== "start") throw new Error(`option ${flag} is not valid for this command`);
30
+ options.redisPort = Number(value);
31
+ index += 1;
32
+ break;
33
+ case "--pgsql-port":
34
+ if (command !== "start") throw new Error(`option ${flag} is not valid for this command`);
35
+ options.pgsqlPort = Number(value);
36
+ index += 1;
37
+ break;
38
+ case "--mysql-port":
39
+ if (command !== "start") throw new Error(`option ${flag} is not valid for this command`);
40
+ options.mysqlPort = Number(value);
41
+ index += 1;
42
+ break;
43
+ case "--wire-port":
44
+ if (command !== "start") throw new Error(`option ${flag} is not valid for this command`);
45
+ options.wirePort = Number(value);
46
+ index += 1;
47
+ break;
48
+ default:
49
+ throw new Error(`unknown option '${flag}'\n${usage()}`);
50
+ }
51
+ }
52
+ if (!options.dataDir) throw new Error(usage());
53
+ if ((command === "init" || command === "start") && (!options.clusterId || !options.nodeId)) {
54
+ throw new Error(usage());
55
+ }
56
+ return options;
57
+ }
58
+
59
+ async function main(args) {
60
+ const command = args[0];
61
+ const rest = args.slice(1);
62
+ const native = loadYydsNative();
63
+ const options = parseCommonArgs(command, rest);
64
+
65
+ switch (command) {
66
+ case "init": {
67
+ const identity = new native.NodeIdentityHandle(options.clusterId, options.nodeId);
68
+ native.nodeInit(options.dataDir, identity);
69
+ console.log(`initialized ${options.dataDir}`);
70
+ return;
71
+ }
72
+ case "start":
73
+ await runNodeStart({
74
+ dataDir: options.dataDir,
75
+ clusterId: options.clusterId,
76
+ nodeId: options.nodeId,
77
+ redisPort: options.redisPort,
78
+ pgsqlPort: options.pgsqlPort,
79
+ mysqlPort: options.mysqlPort,
80
+ wirePort: options.wirePort,
81
+ });
82
+ return;
83
+ case "status": {
84
+ const snapshot = native.nodeInspect(options.dataDir);
85
+ console.log(JSON.stringify({
86
+ directory: snapshot.directory,
87
+ identity: JSON.parse(snapshot.identity),
88
+ ownership: snapshot.ownership,
89
+ runtime: snapshot.runtime ? JSON.parse(snapshot.runtime) : null,
90
+ health: snapshot.health,
91
+ }));
92
+ return;
93
+ }
94
+ case "stop": {
95
+ const snapshot = native.nodeInspect(options.dataDir);
96
+ if (snapshot.ownership === "available") throw new Error("node is not running");
97
+ const runtime = snapshot.runtime ? JSON.parse(snapshot.runtime) : null;
98
+ if (!runtime) throw new Error("node owner has not published runtime status");
99
+ native.nodeRequestStop(options.dataDir, runtime.generation);
100
+ native.nodeWaitUntilStopped(options.dataDir, runtime.generation, 10_000);
101
+ console.log("stopped");
102
+ return;
103
+ }
104
+ default:
105
+ throw new Error(command ? `unknown command '${command}'` : usage());
106
+ }
107
+ }
108
+
109
+ try {
110
+ await main(process.argv.slice(2));
111
+ } catch (error) {
112
+ console.error(`yyds: ${error.message}`);
113
+ process.exitCode = 1;
114
+ }
package/package.json CHANGED
@@ -1,63 +1,72 @@
1
1
  {
2
- "bugs": {
3
- "url": "https://github.com/yy-database/yyds/issues"
4
- },
5
- "description": "YYDS scripting runtime for TypeScript and Node.js",
6
- "devDependencies": {
7
- "@types/node": "^22.15.0",
8
- "typescript": "^5.9.2"
9
- },
10
- "engines": {
11
- "node": ">=18"
12
- },
2
+ "name": "@yydb/yyds",
3
+ "version": "0.0.2",
4
+ "description": "Node.js facade, CLI, and native bindings for local YYDS nodes.",
5
+ "license": "MPL-2.0",
6
+ "type": "module",
7
+ "sideEffects": false,
13
8
  "exports": {
14
9
  ".": {
15
- "default": "./src/node/unsupported.ts",
10
+ "types": "./src/node/index.ts",
16
11
  "node": "./src/node/index.ts",
17
- "types": "./src/node/index.ts"
12
+ "default": "./src/node/unsupported.ts"
18
13
  },
19
14
  "./node": {
20
- "default": "./src/node/unsupported.ts",
15
+ "types": "./src/node/index.ts",
21
16
  "node": "./src/node/index.ts",
22
- "types": "./src/node/index.ts"
17
+ "default": "./src/node/unsupported.ts"
23
18
  },
24
19
  "./node/cluster": {
25
- "default": "./src/node/unsupported.ts",
20
+ "types": "./src/node/cluster.ts",
26
21
  "node": "./src/node/cluster.ts",
27
- "types": "./src/node/cluster.ts"
22
+ "default": "./src/node/unsupported.ts"
23
+ },
24
+ "./node/runtime": {
25
+ "types": "./src/node/runtime.ts",
26
+ "node": "./src/node/runtime.ts",
27
+ "default": "./src/node/unsupported.ts"
28
28
  }
29
29
  },
30
+ "bin": {
31
+ "yyds": "./bin/yyds.mjs"
32
+ },
30
33
  "files": [
34
+ "bin",
31
35
  "src"
32
36
  ],
37
+ "scripts": {
38
+ "typecheck": "tsc --noEmit -p tsconfig.json",
39
+ "test": "node --experimental-strip-types --test \"./tests/**/*.test.ts\""
40
+ },
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "git+https://github.com/yy-database/yyds.git",
44
+ "directory": "projects/packages/yyds"
45
+ },
33
46
  "homepage": "https://github.com/yy-database/yyds/tree/dev/projects/packages/yyds#readme",
47
+ "bugs": {
48
+ "url": "https://github.com/yy-database/yyds/issues"
49
+ },
34
50
  "keywords": [
35
51
  "yyds",
36
52
  "scripting",
37
53
  "typescript",
38
54
  "napi"
39
55
  ],
40
- "license": "MPL-2.0",
41
- "name": "@yydb/yyds",
42
- "optionalDependencies": {
43
- "@yydb/yyds-darwin-arm64": "0.0.0",
44
- "@yydb/yyds-darwin-x64": "0.0.0",
45
- "@yydb/yyds-linux-x64": "0.0.0",
46
- "@yydb/yyds-win32-x64": "0.0.0"
47
- },
48
56
  "publishConfig": {
49
57
  "access": "public"
50
58
  },
51
- "repository": {
52
- "directory": "projects/packages/yyds",
53
- "type": "git",
54
- "url": "git+https://github.com/yy-database/yyds.git"
59
+ "optionalDependencies": {
60
+ "@yydb/yyds-win32-x64": "0.0.2",
61
+ "@yydb/yyds-linux-x64": "0.0.2",
62
+ "@yydb/yyds-darwin-x64": "0.0.2",
63
+ "@yydb/yyds-darwin-arm64": "0.0.2"
55
64
  },
56
- "scripts": {
57
- "test": "node --experimental-strip-types --test \"./tests/**/*.test.ts\"",
58
- "typecheck": "tsc --noEmit -p tsconfig.json"
65
+ "devDependencies": {
66
+ "@types/node": "^22.15.0",
67
+ "typescript": "^5.9.2"
59
68
  },
60
- "sideEffects": false,
61
- "type": "module",
62
- "version": "0.0.0"
69
+ "engines": {
70
+ "node": ">=18"
71
+ }
63
72
  }
package/readme.md CHANGED
@@ -1,13 +1,39 @@
1
- # @yyds/yyds
2
-
3
- TypeScript facade for the YYDS scripting runtime.
4
-
5
- - Browser / Worker: default export (`@yyds/yyds`) or `@yyds/yyds/wasm`
6
- - Node.js: `@yyds/yyds/node`
7
-
8
- Build native and WASM artifacts from the repo root:
9
-
10
- ```bash
11
- pnpm build:napi
12
- pnpm build:wasm
13
- ```
1
+ # @yydb/yyds
2
+
3
+ Node.js facade for running a local YYDS node: native lifecycle blocks, a small CLI, and helpers used by disguise server entrypoints.
4
+
5
+ ## Exports
6
+
7
+ | Subpath | Use |
8
+ |---------|-----|
9
+ | `@yydb/yyds` / `@yydb/yyds/node` | Load native bindings, start or inspect a node |
10
+ | `@yydb/yyds/node/cluster` | `launchYydsServer` for `redis-server`, `mysqld`, `postgres` |
11
+ | `@yydb/yyds/node/runtime` | `runNodeStart`, `parseStartOptions` |
12
+
13
+ The `yyds` binary provides `init`, `start`, `status`, and `stop`. Native addons for your platform are pulled in automatically as optional dependencies.
14
+
15
+ ## Example
16
+
17
+ ```ts
18
+ import { runNodeStart } from "@yydb/yyds/node/runtime";
19
+
20
+ await runNodeStart({
21
+ dataDir: "./node-data",
22
+ clusterId: "local",
23
+ nodeId: "node-1",
24
+ redisPort: 6379,
25
+ });
26
+ ```
27
+
28
+ ```bash
29
+ yyds init --data-dir ./node-data --cluster-id local --node-id node-1
30
+ yyds start --data-dir ./node-data --cluster-id local --node-id node-1
31
+ yyds status --data-dir ./node-data
32
+ yyds stop --data-dir ./node-data
33
+ ```
34
+
35
+ ## Related packages
36
+
37
+ - [`@yydb/redis`](https://github.com/yy-database/yyds/tree/dev/projects/packages/redis) — Redis protocol disguise
38
+ - [`@yydb/mysql`](https://github.com/yy-database/yyds/tree/dev/projects/packages/mysql) — MySQL protocol disguise
39
+ - [`@yydb/postgresql`](https://github.com/yy-database/yyds/tree/dev/projects/packages/postgresql) — PostgreSQL protocol disguise
package/src/bindings.ts CHANGED
@@ -1,5 +1,41 @@
1
- /** Shared Node and WASM binding surface for `@yydb/yyds`. */
2
- export interface YydsBindings {
3
- yydsVersion(): string;
4
- ping(): string;
5
- }
1
+ /** Shared Node and WASM binding surface for `@yydb/yyds`. */
2
+ export interface YydsBindings {
3
+ yydsVersion(): string;
4
+ ping(): string;
5
+ NodeIdentityHandle: new (clusterId: string, nodeId: string) => unknown;
6
+ NodeLeaseHandle: {
7
+ acquire(dataDir: string, identity: unknown): unknown;
8
+ };
9
+ LocalExecutorHandle: new (dataDir: string) => unknown;
10
+ RedisServiceHandle: new (lease: unknown, port: number, executor: unknown) => RedisServiceBinding;
11
+ PgsqlServiceHandle: new (lease: unknown, port: number) => PgsqlServiceBinding;
12
+ MysqlServiceHandle: new (lease: unknown, port: number) => MysqlServiceBinding;
13
+ WireServerHandle: new (port: number, executor: unknown) => WireServiceBinding;
14
+ nodeInit(dataDir: string, identity: unknown): void;
15
+ nodeInspect(dataDir: string): NodeStatusBinding;
16
+ nodeReadRuntimeStatus(dataDir: string): string | null;
17
+ nodeWriteRuntimeStatus(dataDir: string, statusJson: string): void;
18
+ nodeRequestStop(dataDir: string, generation: string): void;
19
+ nodeWaitUntilStopped(dataDir: string, generation: string, timeoutMs: number): void;
20
+ nodeStopRequested(dataDir: string, generation: string): boolean;
21
+ nodeClearRuntimeArtifacts(dataDir: string, generation: string): void;
22
+ }
23
+
24
+ export interface ServiceBinding {
25
+ address(): string;
26
+ isFinished(): boolean;
27
+ stop(): void;
28
+ }
29
+
30
+ export type RedisServiceBinding = ServiceBinding;
31
+ export type PgsqlServiceBinding = ServiceBinding;
32
+ export type MysqlServiceBinding = ServiceBinding;
33
+ export type WireServiceBinding = ServiceBinding;
34
+
35
+ export interface NodeStatusBinding {
36
+ directory: string;
37
+ ownership: string;
38
+ health: string;
39
+ identity: string;
40
+ runtime: string | null;
41
+ }
@@ -1,56 +1,53 @@
1
- import { spawn } from "node:child_process";
2
-
3
- export type YydsProtocol = "redis" | "mysql" | "postgresql";
4
-
5
- const portFlags: Record<YydsProtocol, string> = {
6
- redis: "--redis-port",
7
- mysql: "--mysql-port",
8
- postgresql: "--pgsql-port",
9
- };
10
- const defaultPorts: Partial<Record<YydsProtocol, number>> = { mysql: 3306, postgresql: 5432 };
11
-
12
- export function yydsStartArguments(protocol: YydsProtocol, args: string[]): string[] {
13
- const result = ["start"];
14
- let protocolPortProvided = false;
15
- for (let index = 0; index < args.length; index += 1) {
16
- const argument = args[index];
17
- if (["--port", "-p", "-P"].includes(argument)) {
18
- const value = args[++index];
19
- if (value === undefined || value.startsWith("-"))
20
- throw new Error(`missing port after ${argument}`);
21
- result.push(portFlags[protocol], value);
22
- protocolPortProvided = true;
23
- } else {
24
- result.push(argument);
25
- if (argument === portFlags[protocol]) protocolPortProvided = true;
26
- }
27
- }
28
- const defaultPort = defaultPorts[protocol];
29
- if (!protocolPortProvided && defaultPort !== undefined)
30
- result.push(portFlags[protocol], String(defaultPort));
31
- return result;
32
- }
33
-
34
- export function launchYydsServer(protocol: YydsProtocol, args: string[]): Promise<number> {
35
- const executable = process.env.YYDS_CLI_PATH || "yyds";
36
- const child = spawn(executable, yydsStartArguments(protocol, args), {
37
- stdio: "inherit",
38
- windowsHide: true,
39
- });
40
- const forward = (signal: NodeJS.Signals) => child.kill(signal);
41
- const onInterrupt = () => forward("SIGINT");
42
- const onTerminate = () => forward("SIGTERM");
43
- process.once("SIGINT", onInterrupt);
44
- process.once("SIGTERM", onTerminate);
45
-
46
- return new Promise((resolve, reject) => {
47
- child.once("error", (error) =>
48
- reject(new Error(`cannot start '${executable}': ${error.message}`)),
49
- );
50
- child.once("close", (code, signal) => {
51
- process.removeListener("SIGINT", onInterrupt);
52
- process.removeListener("SIGTERM", onTerminate);
53
- resolve(code ?? (signal ? 1 : 0));
54
- });
55
- });
56
- }
1
+ import type { StartNodeOptions } from "./runtime.ts";
2
+ import { parseStartOptions, runNodeStart } from "./runtime.ts";
3
+
4
+ export type YydsProtocol = "redis" | "mysql" | "postgresql";
5
+
6
+ /** Maps disguise-server `--port` flags to protocol-specific yyds start flags. */
7
+ export function yydsStartArguments(protocol: YydsProtocol, args: string[]): string[] {
8
+ const portFlags: Record<YydsProtocol, string> = {
9
+ redis: "--redis-port",
10
+ mysql: "--mysql-port",
11
+ postgresql: "--pgsql-port",
12
+ };
13
+ const defaultPorts: Partial<Record<YydsProtocol, number>> = {
14
+ mysql: 3306,
15
+ postgresql: 5432,
16
+ };
17
+ const normalized: string[] = [];
18
+ let sawPort = false;
19
+
20
+ for (let index = 0; index < args.length; index += 1) {
21
+ const argument = args[index];
22
+ if (["--port", "-p", "-P"].includes(argument)) {
23
+ const value = args[index + 1];
24
+ if (value === undefined || value.startsWith("--")) {
25
+ throw new Error(`missing port after ${argument}`);
26
+ }
27
+ normalized.push(portFlags[protocol], value);
28
+ sawPort = true;
29
+ index += 1;
30
+ continue;
31
+ }
32
+ normalized.push(argument);
33
+ }
34
+
35
+ const defaultPort = defaultPorts[protocol];
36
+ if (!sawPort && defaultPort !== undefined) {
37
+ normalized.push(portFlags[protocol], String(defaultPort));
38
+ }
39
+ return normalized;
40
+ }
41
+
42
+ /** Runs a node lifecycle in-process through orthogonal native blocks. */
43
+ export async function launchYydsServer(protocol: YydsProtocol, args: string[]): Promise<number> {
44
+ try {
45
+ await runNodeStart(parseStartOptions(protocol, yydsStartArguments(protocol, args)));
46
+ return 0;
47
+ } catch (error) {
48
+ const message = error instanceof Error ? error.message : String(error);
49
+ throw new Error(message);
50
+ }
51
+ }
52
+
53
+ export type { StartNodeOptions };
package/src/node/index.ts CHANGED
@@ -1,20 +1,22 @@
1
- /**
2
- * `@yydb/yyds/node` — Node N-API facade.
3
- *
4
- * Browser code must use the default `@yydb/yyds` entry or `@yydb/yyds/wasm`.
5
- */
6
-
7
- export type { YydsBindings } from "../bindings.ts";
8
- export type { YydsProtocol } from "./cluster.ts";
9
- export { launchYydsServer, yydsStartArguments } from "./cluster.ts";
10
- export { isYydsNativeInstalled, loadYydsNative, loadYydsSqliteNative } from "./load.ts";
11
- export type {
12
- SqliteConnectionBinding,
13
- SqliteQueryResultBinding,
14
- SqliteReadLimits,
15
- SqliteResultValueBinding,
16
- SqliteSchemaObject,
17
- SqliteSnapshotBinding,
18
- SqliteTableRow,
19
- YydsSqliteBindings,
20
- } from "./sqlite-bindings.ts";
1
+ /**
2
+ * `@yydb/yyds/node` — Node N-API facade.
3
+ *
4
+ * Browser hosts are not supported in this package yet.
5
+ */
6
+
7
+ export type { YydsBindings, NodeStatusBinding, ServiceBinding } from "../bindings.ts";
8
+ export type { YydsProtocol } from "./cluster.ts";
9
+ export { launchYydsServer, yydsStartArguments } from "./cluster.ts";
10
+ export { isYydsNativeInstalled, loadYydsNative, loadYydsSqliteNative } from "./load.ts";
11
+ export type { StartNodeOptions } from "./runtime.ts";
12
+ export { parseStartOptions, runNodeStart } from "./runtime.ts";
13
+ export type {
14
+ SqliteDatabaseBinding,
15
+ SqliteDecodedRow,
16
+ SqliteReadLimits,
17
+ SqliteRecordValue,
18
+ SqliteSchemaObject,
19
+ SqliteSnapshotBinding,
20
+ SqliteTableRow,
21
+ YydsSqliteBindings,
22
+ } from "./sqlite-bindings.ts";
package/src/node/load.ts CHANGED
@@ -1,50 +1,48 @@
1
- import { createRequire } from "node:module";
2
-
3
- import type { YydsBindings } from "../bindings.ts";
4
- import { resolvePlatformPackage } from "./platform-packages.ts";
5
- import type { YydsSqliteBindings } from "./sqlite-bindings.ts";
6
-
7
- const require = createRequire(import.meta.url);
8
-
9
- function normalizeBinding(module: Record<string, unknown>): YydsBindings {
10
- const binding = (module.default ?? module) as Record<string, unknown>;
11
- return {
12
- yydsVersion: () => String((binding.yydsVersion as () => string)()),
13
- ping: () => String((binding.ping as () => string)()),
14
- };
15
- }
16
-
17
- /** Load the platform-specific Node-API binding. */
18
- export function loadYydsNative(): YydsBindings {
19
- const specifier = resolvePlatformPackage();
20
- return normalizeBinding(require(specifier) as Record<string, unknown>);
21
- }
22
-
23
- /** Load the independent SQLite snapshot reader, not a YYDS execution session. */
24
- export function loadYydsSqliteNative(): YydsSqliteBindings {
25
- const module = require(resolvePlatformPackage()) as Record<string, unknown>;
26
- const binding = (module.default ?? module) as Record<string, unknown>;
27
- if (
28
- typeof binding.SqliteSnapshot !== "function" ||
29
- typeof binding.SqliteConnection !== "function"
30
- ) {
31
- throw new Error(
32
- "Native addon lacks the SQLite snapshot reader. Rebuild the native package.",
33
- );
34
- }
35
- return {
36
- SqliteSnapshot: binding.SqliteSnapshot as YydsSqliteBindings["SqliteSnapshot"],
37
- SqliteConnection: binding.SqliteConnection as YydsSqliteBindings["SqliteConnection"],
38
- };
39
- }
40
-
41
- /** Whether the current platform optional dependency is installed. */
42
- export function isYydsNativeInstalled(): boolean {
43
- try {
44
- resolvePlatformPackage();
45
- require.resolve(resolvePlatformPackage());
46
- return true;
47
- } catch {
48
- return false;
49
- }
50
- }
1
+ import { createRequire } from "node:module";
2
+
3
+ import type { YydsBindings } from "../bindings.ts";
4
+ import { resolvePlatformPackage } from "./platform-packages.ts";
5
+ import type { YydsSqliteBindings } from "./sqlite-bindings.ts";
6
+
7
+ const require = createRequire(import.meta.url);
8
+
9
+ function bindingModule(): Record<string, unknown> {
10
+ const module = require(resolvePlatformPackage()) as Record<string, unknown>;
11
+ return (module.default ?? module) as Record<string, unknown>;
12
+ }
13
+
14
+ /** Load the platform-specific Node-API binding. */
15
+ export function loadYydsNative(): YydsBindings {
16
+ const binding = bindingModule();
17
+ return binding as unknown as YydsBindings;
18
+ }
19
+
20
+ /** Load the independent SQLite snapshot reader, not a YYDS execution session. */
21
+ export function loadYydsSqliteNative(): YydsSqliteBindings {
22
+ const binding = bindingModule();
23
+ if (typeof binding.SqliteSnapshot !== "function") {
24
+ throw new Error("Native addon lacks the SQLite snapshot reader. Rebuild the native package.");
25
+ }
26
+ if (typeof binding.SqliteDatabase !== "function") {
27
+ throw new Error("Native addon lacks the SqliteDatabase class. Rebuild the native package.");
28
+ }
29
+ if (typeof binding.decodeRecord !== "function") {
30
+ throw new Error("Native addon lacks decodeRecord. Rebuild the native package.");
31
+ }
32
+ return {
33
+ SqliteSnapshot: binding.SqliteSnapshot as YydsSqliteBindings["SqliteSnapshot"],
34
+ SqliteDatabase: binding.SqliteDatabase as unknown as YydsSqliteBindings["SqliteDatabase"],
35
+ decodeRecord: binding.decodeRecord as YydsSqliteBindings["decodeRecord"],
36
+ };
37
+ }
38
+
39
+ /** Whether the current platform optional dependency is installed. */
40
+ export function isYydsNativeInstalled(): boolean {
41
+ try {
42
+ resolvePlatformPackage();
43
+ require.resolve(resolvePlatformPackage());
44
+ return true;
45
+ } catch {
46
+ return false;
47
+ }
48
+ }
@@ -1,15 +1,15 @@
1
- const PLATFORM_PACKAGES: Record<string, string> = {
2
- "win32-x64": "@yydb/yyds-win32-x64",
3
- "darwin-x64": "@yydb/yyds-darwin-x64",
4
- "darwin-arm64": "@yydb/yyds-darwin-arm64",
5
- "linux-x64": "@yydb/yyds-linux-x64",
6
- };
7
-
8
- export function resolvePlatformPackage(): string {
9
- const key = `${process.platform}-${process.arch}`;
10
- const specifier = PLATFORM_PACKAGES[key];
11
- if (!specifier) {
12
- throw new Error(`@yydb/yyds/node: unsupported platform ${key}`);
13
- }
14
- return specifier;
15
- }
1
+ const PLATFORM_PACKAGES: Record<string, string> = {
2
+ "win32-x64": "@yydb/yyds-win32-x64",
3
+ "darwin-x64": "@yydb/yyds-darwin-x64",
4
+ "darwin-arm64": "@yydb/yyds-darwin-arm64",
5
+ "linux-x64": "@yydb/yyds-linux-x64",
6
+ };
7
+
8
+ export function resolvePlatformPackage(): string {
9
+ const key = `${process.platform}-${process.arch}`;
10
+ const specifier = PLATFORM_PACKAGES[key];
11
+ if (!specifier) {
12
+ throw new Error(`@yydb/yyds/node: unsupported platform ${key}`);
13
+ }
14
+ return specifier;
15
+ }
@@ -0,0 +1,127 @@
1
+ import { setTimeout as sleep } from "node:timers/promises";
2
+
3
+ import type { YydsProtocol } from "./cluster.ts";
4
+ import { loadYydsNative } from "./load.ts";
5
+
6
+ const DEFAULT_REDIS_PORT = 6379;
7
+
8
+ export interface StartNodeOptions {
9
+ dataDir: string;
10
+ clusterId: string;
11
+ nodeId: string;
12
+ redisPort?: number;
13
+ pgsqlPort?: number;
14
+ mysqlPort?: number;
15
+ wirePort?: number;
16
+ }
17
+
18
+ export function parseStartOptions(protocol: YydsProtocol, args: string[]): StartNodeOptions {
19
+ const options: StartNodeOptions = {
20
+ dataDir: "",
21
+ clusterId: "",
22
+ nodeId: "",
23
+ redisPort: DEFAULT_REDIS_PORT,
24
+ };
25
+ const portFlags: Record<YydsProtocol, string> = {
26
+ redis: "--redis-port",
27
+ mysql: "--mysql-port",
28
+ postgresql: "--pgsql-port",
29
+ };
30
+ const defaultPorts: Partial<Record<YydsProtocol, number>> = { mysql: 3306, postgresql: 5432 };
31
+
32
+ for (let index = 0; index < args.length; index += 1) {
33
+ const argument = args[index];
34
+ if (["--port", "-p", "-P"].includes(argument)) {
35
+ const value = Number(args[++index]);
36
+ if (!Number.isInteger(value) || value < 0) throw new Error(`missing port after ${argument}`);
37
+ if (protocol === "redis") options.redisPort = value;
38
+ if (protocol === "mysql") options.mysqlPort = value;
39
+ if (protocol === "postgresql") options.pgsqlPort = value;
40
+ continue;
41
+ }
42
+ if (argument === "--data-dir") {
43
+ options.dataDir = args[++index] ?? "";
44
+ continue;
45
+ }
46
+ if (argument === "--cluster-id") {
47
+ options.clusterId = args[++index] ?? "";
48
+ continue;
49
+ }
50
+ if (argument === "--node-id") {
51
+ options.nodeId = args[++index] ?? "";
52
+ continue;
53
+ }
54
+ if (argument === "--redis-port") {
55
+ options.redisPort = Number(args[++index]);
56
+ continue;
57
+ }
58
+ if (argument === "--pgsql-port") {
59
+ options.pgsqlPort = Number(args[++index]);
60
+ continue;
61
+ }
62
+ if (argument === "--mysql-port") {
63
+ options.mysqlPort = Number(args[++index]);
64
+ continue;
65
+ }
66
+ if (argument === "--wire-port") {
67
+ options.wirePort = Number(args[++index]);
68
+ continue;
69
+ }
70
+ throw new Error(`unknown option '${argument}'`);
71
+ }
72
+
73
+ if (!options.dataDir || !options.clusterId || !options.nodeId) {
74
+ throw new Error("start requires --data-dir, --cluster-id, and --node-id");
75
+ }
76
+ const defaultPort = defaultPorts[protocol];
77
+ if (protocol === "mysql" && options.mysqlPort === undefined && defaultPort !== undefined) {
78
+ options.mysqlPort = defaultPort;
79
+ }
80
+ if (protocol === "postgresql" && options.pgsqlPort === undefined && defaultPort !== undefined) {
81
+ options.pgsqlPort = defaultPort;
82
+ }
83
+ return options;
84
+ }
85
+
86
+ /** Starts protocol listeners for one node directory and blocks until stop is requested. */
87
+ export async function runNodeStart(options: StartNodeOptions): Promise<void> {
88
+ const native = loadYydsNative();
89
+ const identity = new native.NodeIdentityHandle(options.clusterId, options.nodeId);
90
+ const lease = native.NodeLeaseHandle.acquire(options.dataDir, identity);
91
+ const executor = new native.LocalExecutorHandle(options.dataDir);
92
+ const redis = new native.RedisServiceHandle(lease, options.redisPort ?? DEFAULT_REDIS_PORT, executor);
93
+ const pgsql = options.pgsqlPort === undefined ? undefined : new native.PgsqlServiceHandle(lease, options.pgsqlPort);
94
+ const mysql = options.mysqlPort === undefined ? undefined : new native.MysqlServiceHandle(lease, options.mysqlPort);
95
+ const wire = options.wirePort === undefined ? undefined : new native.WireServerHandle(options.wirePort, executor);
96
+
97
+ const runtime = {
98
+ version: 1,
99
+ generation: crypto.randomUUID(),
100
+ pid: process.pid,
101
+ startedAtUnixMs: Date.now(),
102
+ redisAddress: redis.address(),
103
+ pgsqlAddress: pgsql?.address(),
104
+ mysqlAddress: mysql?.address(),
105
+ wireAddress: wire?.address(),
106
+ };
107
+ native.nodeWriteRuntimeStatus(options.dataDir, JSON.stringify(runtime));
108
+
109
+ process.stdout.write(
110
+ `yyds running pid=${runtime.pid} redis=${runtime.redisAddress} pgsql=${runtime.pgsqlAddress ?? "null"} mysql=${runtime.mysqlAddress ?? "null"} wire=${runtime.wireAddress ?? "null"}\n`,
111
+ );
112
+
113
+ while (true) {
114
+ if (native.nodeStopRequested(options.dataDir, runtime.generation)) break;
115
+ if (redis.isFinished()) break;
116
+ if (pgsql?.isFinished()) break;
117
+ if (mysql?.isFinished()) break;
118
+ if (wire?.isFinished()) break;
119
+ await sleep(50);
120
+ }
121
+
122
+ redis.stop();
123
+ pgsql?.stop();
124
+ mysql?.stop();
125
+ wire?.stop();
126
+ native.nodeClearRuntimeArtifacts(options.dataDir, runtime.generation);
127
+ }
@@ -1,64 +1,70 @@
1
- /** Bounds apply independently to each schema or table scan. */
2
- export interface SqliteReadLimits {
3
- maxPages?: number;
4
- maxRows?: number;
5
- maxPayloadBytes?: number;
6
- maxTotalPayloadBytes?: number;
7
- }
8
-
9
- /** SQL source is opaque. No CREATE statement is parsed or executed. */
10
- export interface SqliteSchemaObject {
11
- kind: "table" | "index" | "view" | "trigger";
12
- name: string;
13
- tableName: string;
14
- rootPage?: number;
15
- sql?: string;
16
- }
17
-
18
- /** Raw storage values, not SQL results or INTEGER PRIMARY KEY expansion. */
19
- export interface SqliteTableRow {
20
- rowid: bigint;
21
- payload: Buffer;
22
- }
23
-
24
- export interface SqliteSnapshotBinding {
25
- schema(limits?: SqliteReadLimits): SqliteSchemaObject[];
26
- table(name: string, limits?: SqliteReadLimits): SqliteTableRow[];
27
- }
28
-
29
- /** An independent SQLite reader hosted in the same native addon. */
30
- export interface YydsSqliteBindings {
31
- SqliteSnapshot: new (bytes: Buffer, maxSnapshotBytes?: number) => SqliteSnapshotBinding;
32
- SqliteConnection: new (path: string, readOnly?: boolean) => SqliteConnectionBinding;
33
- }
34
-
35
- export interface SqliteConnectionBinding {
36
- execute(sql: string): SqliteQueryResultBinding;
37
- executeWithParameters(sql: string, parameters: SqliteParameterBinding[]): SqliteQueryResultBinding;
38
- executeBatch(sql: string): void;
39
- sourceId(): string;
40
- }
41
-
42
- export interface SqliteParameterBinding {
43
- kind: "null" | "integer" | "real" | "text" | "blob";
44
- integer?: string;
45
- real?: number;
46
- text?: Buffer;
47
- blob?: Buffer;
48
- }
49
-
50
- export interface SqliteQueryResultBinding {
51
- columns: string[];
52
- rows: SqliteResultValueBinding[][];
53
- changes: string;
54
- lastInsertRowid: string;
55
- }
56
-
57
- export interface SqliteResultValueBinding {
58
- kind: "null" | "integer" | "real" | "text" | "blob";
59
- integer?: string;
60
- real?: number;
61
- text?: string;
62
- textBytes?: Buffer;
63
- blob?: Buffer;
64
- }
1
+ /** Bounds apply independently to each schema or table scan. */
2
+ export interface SqliteReadLimits {
3
+ maxPages?: number;
4
+ maxRows?: number;
5
+ maxPayloadBytes?: number;
6
+ maxTotalPayloadBytes?: number;
7
+ }
8
+
9
+ /** SQL source is opaque. No CREATE statement is parsed or executed. */
10
+ export interface SqliteSchemaObject {
11
+ kind: "table" | "index" | "view" | "trigger";
12
+ name: string;
13
+ tableName: string;
14
+ rootPage?: number;
15
+ sql?: string;
16
+ }
17
+
18
+ /** Raw storage values, not SQL results or INTEGER PRIMARY KEY expansion. */
19
+ export interface SqliteTableRow {
20
+ rowid: bigint;
21
+ payload: Buffer;
22
+ }
23
+
24
+ /** One decoded SQLite storage-class field. Exactly one `value*` is populated. */
25
+ export interface SqliteRecordValue {
26
+ kind: "null" | "integer" | "real" | "text" | "blob";
27
+ valueInteger?: bigint;
28
+ valueReal?: number;
29
+ valueText?: string;
30
+ valueBlob?: Buffer;
31
+ }
32
+
33
+ /** A decoded SQLite row with storage-class typed columns. */
34
+ export interface SqliteDecodedRow {
35
+ rowid: bigint;
36
+ columns: SqliteRecordValue[];
37
+ }
38
+
39
+ /** Binding surface for SqliteSnapshot (read-only main-file buffer). */
40
+ export interface SqliteSnapshotBinding {
41
+ schema(limits?: SqliteReadLimits): SqliteSchemaObject[];
42
+ table(name: string, limits?: SqliteReadLimits): SqliteTableRow[];
43
+ tableDecoded(name: string, limits?: SqliteReadLimits): SqliteDecodedRow[];
44
+ sqliteVersion(): string;
45
+ encoding(): string;
46
+ ping(): number;
47
+ pages(): Buffer;
48
+ }
49
+
50
+ /** Binding surface for SqliteDatabase (file-backed or in-memory handle). */
51
+ export interface SqliteDatabaseBinding {
52
+ path(): string | null;
53
+ ping(): number;
54
+ sqliteVersion(): string;
55
+ encoding(): string;
56
+ schema(limits?: SqliteReadLimits): SqliteSchemaObject[];
57
+ table(name: string, limits?: SqliteReadLimits): SqliteDecodedRow[];
58
+ tableRaw(name: string, limits?: SqliteReadLimits): SqliteTableRow[];
59
+ pages(): Buffer;
60
+ }
61
+
62
+ /** Combined SQLite native addon surface. */
63
+ export interface YydsSqliteBindings {
64
+ SqliteSnapshot: new (bytes: Buffer, maxSnapshotBytes?: number) => SqliteSnapshotBinding;
65
+ SqliteDatabase: {
66
+ openInMemory(): SqliteDatabaseBinding;
67
+ open(path: string): SqliteDatabaseBinding;
68
+ };
69
+ decodeRecord(payload: Buffer, encoding?: string): SqliteRecordValue[];
70
+ }
@@ -1 +1 @@
1
- throw new Error("@yydb/yyds/node is only available in Node.js runtimes");
1
+ throw new Error("@yydb/yyds/node is only available in Node.js runtimes");
@@ -1,7 +0,0 @@
1
- /**
2
- * `@yydb/yyds` default export — browser / Worker facade (WASM inside).
3
- *
4
- * Node apps use `@yydb/yyds/node`. Low-level WASM loader: `@yydb/yyds/wasm`.
5
- */
6
-
7
- export { initWasm, ping, yydsVersion, type InitWasmOptions, type WasmInitInput } from "../wasm/index.ts";
package/src/wasm/index.ts DELETED
@@ -1,8 +0,0 @@
1
- export {
2
- initWasm,
3
- ping,
4
- resetWasmBindingForTests,
5
- yydsVersion,
6
- type InitWasmOptions,
7
- type WasmInitInput,
8
- } from "@yydb/yyds-unknown-wasm32";