@evolu/nodejs 3.1.0 → 4.0.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/dist/src/index.js CHANGED
@@ -4,6 +4,7 @@
4
4
  */
5
5
  export * from "./Cli.js";
6
6
  export * from "./Crypto.js";
7
+ export * from "./Fs.js";
7
8
  export * from "./local-first/Relay.js";
8
9
  export * from "./Platform.js";
9
10
  export * from "./Sqlite.js";
@@ -1,8 +1,11 @@
1
- import { type CreateSqliteDriverDep, type RandomDep, type Task, type TimingSafeEqualDep } from "@evolu/common";
1
+ import { type CreateSqliteDriverDep, Port, type RandomDep, type Task, type TimingSafeEqualDep } from "@evolu/common";
2
2
  import { type Relay, type RelayConfig } from "@evolu/common/local-first";
3
3
  export interface NodeJsRelayConfig extends RelayConfig {
4
- /** The port number for the HTTP server. */
5
- readonly port?: number;
4
+ /**
5
+ * The HTTP server's {@link Port}. Zero requests an automatically assigned
6
+ * port.
7
+ */
8
+ readonly port?: Port;
6
9
  }
7
10
  export type RelayDeps = CreateSqliteDriverDep & RandomDep & TimingSafeEqualDep;
8
11
  /** Dependencies for {@link createRelay} using better-sqlite3. */
@@ -16,32 +19,17 @@ export declare const createRelayDeps: () => RelayDeps;
16
19
  * ### Example
17
20
  *
18
21
  * ```ts
19
- * // Ensure the database is created in a predictable location for Docker.
20
- * mkdirSync("data", { recursive: true });
21
- * process.chdir("data");
22
- *
23
- * const console = createConsole({
24
- * // level: "debug",
25
- * formatter: createConsoleFormatter()({
26
- * timestampFormat: "relative",
27
- * }),
22
+ * import { assertType, Port, type Task } from "@evolu/common";
23
+ * import type { Relay } from "@evolu/common/local-first";
24
+ * import { createRelay, type RelayDeps } from "@evolu/nodejs";
25
+ *
26
+ * const main = createRelay({
27
+ * port: Port.orThrow(4000),
28
+ * isOwnerWithinQuota: (_ownerId, requiredBytes) =>
29
+ * requiredBytes <= 1024 * 1024,
28
30
  * });
29
31
  *
30
- * const deps = { ...createRelayDeps(), console };
31
- *
32
- * await runMain(deps)(
33
- * createRelay({
34
- * port: 4000,
35
- *
36
- * // Note: Relay requires URL in format ws://host:port?ownerId=<ownerId>
37
- * // isOwnerAllowed: (_ownerId, { signal: _signal }) => true,
38
- *
39
- * isOwnerWithinQuota: (_ownerId, requiredBytes) => {
40
- * const maxBytes = 1024 * 1024; // 1MB
41
- * return requiredBytes <= maxBytes;
42
- * },
43
- * }),
44
- * );
32
+ * assertType<typeof main, Task<Relay, never, RelayDeps>>();
45
33
  * ```
46
34
  */
47
35
  export declare const createRelay: ({ port, name, isOwnerAllowed, isOwnerWithinQuota, }: NodeJsRelayConfig) => Task<Relay, never, RelayDeps>;
@@ -1 +1 @@
1
- {"version":3,"file":"Relay.d.ts","sourceRoot":"","sources":["../../../src/local-first/Relay.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,qBAAqB,EAK1B,KAAK,SAAS,EACd,KAAK,IAAI,EACT,KAAK,kBAAkB,EAGxB,MAAM,eAAe,CAAC;AACvB,OAAO,EAQL,KAAK,KAAK,EACV,KAAK,WAAW,EACjB,MAAM,2BAA2B,CAAC;AAQnC,MAAM,WAAW,iBAAkB,SAAQ,WAAW;IACpD,2CAA2C;IAC3C,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,MAAM,SAAS,GAAG,qBAAqB,GAAG,SAAS,GAAG,kBAAkB,CAAC;AAE/E,iEAAiE;AACjE,eAAO,MAAM,eAAe,QAAO,SAIjC,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,eAAO,MAAM,WAAW,wDAMnB,iBAAiB,KAAG,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,CAkNlD,CAAC"}
1
+ {"version":3,"file":"Relay.d.ts","sourceRoot":"","sources":["../../../src/local-first/Relay.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,qBAAqB,EAK1B,IAAI,EACJ,KAAK,SAAS,EACd,KAAK,IAAI,EACT,KAAK,kBAAkB,EAGxB,MAAM,eAAe,CAAC;AACvB,OAAO,EAQL,KAAK,KAAK,EACV,KAAK,WAAW,EACjB,MAAM,2BAA2B,CAAC;AAQnC,MAAM,WAAW,iBAAkB,SAAQ,WAAW;IACpD;;;OAGG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC;CACtB;AAED,MAAM,MAAM,SAAS,GAAG,qBAAqB,GAAG,SAAS,GAAG,kBAAkB,CAAC;AAE/E,iEAAiE;AACjE,eAAO,MAAM,eAAe,QAAO,SAIjC,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,WAAW,wDAMnB,iBAAiB,KAAG,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,CAkNlD,CAAC"}
@@ -50,7 +50,7 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
50
50
  var e = new Error(message);
51
51
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
52
52
  });
53
- import { assert, createRandom, createRelation, createSqlite, daemon, Name, ok, OwnerId, tryAsync, Uint8Array, } from "@evolu/common";
53
+ import { assert, createRandom, createRelation, createSqlite, daemon, Name, ok, OwnerId, Port, tryAsync, Uint8Array, } from "@evolu/common";
54
54
  import { applyProtocolMessageAsRelay, createBaseSqliteStorageTables, createRelaySqliteStorage, createRelayStorageTables, defaultProtocolMessageMaxSize, parseOwnerIdFromOwnerWebSocketTransportUrl, } from "@evolu/common/local-first";
55
55
  import { once } from "events";
56
56
  import { existsSync } from "fs";
@@ -73,35 +73,20 @@ export const createRelayDeps = () => ({
73
73
  * ### Example
74
74
  *
75
75
  * ```ts
76
- * // Ensure the database is created in a predictable location for Docker.
77
- * mkdirSync("data", { recursive: true });
78
- * process.chdir("data");
76
+ * import { assertType, Port, type Task } from "@evolu/common";
77
+ * import type { Relay } from "@evolu/common/local-first";
78
+ * import { createRelay, type RelayDeps } from "@evolu/nodejs";
79
79
  *
80
- * const console = createConsole({
81
- * // level: "debug",
82
- * formatter: createConsoleFormatter()({
83
- * timestampFormat: "relative",
84
- * }),
80
+ * const main = createRelay({
81
+ * port: Port.orThrow(4000),
82
+ * isOwnerWithinQuota: (_ownerId, requiredBytes) =>
83
+ * requiredBytes <= 1024 * 1024,
85
84
  * });
86
85
  *
87
- * const deps = { ...createRelayDeps(), console };
88
- *
89
- * await runMain(deps)(
90
- * createRelay({
91
- * port: 4000,
92
- *
93
- * // Note: Relay requires URL in format ws://host:port?ownerId=<ownerId>
94
- * // isOwnerAllowed: (_ownerId, { signal: _signal }) => true,
95
- *
96
- * isOwnerWithinQuota: (_ownerId, requiredBytes) => {
97
- * const maxBytes = 1024 * 1024; // 1MB
98
- * return requiredBytes <= maxBytes;
99
- * },
100
- * }),
101
- * );
86
+ * assertType<typeof main, Task<Relay, never, RelayDeps>>();
102
87
  * ```
103
88
  */
104
- export const createRelay = ({ port = 443, name = Name.orThrow("evolu-relay"), isOwnerAllowed, isOwnerWithinQuota, }) => async (run) => {
89
+ export const createRelay = ({ port = Port.orThrow(443), name = Name.orThrow("evolu-relay"), isOwnerAllowed, isOwnerWithinQuota, }) => async (run) => {
105
90
  const env_1 = { stack: [], error: void 0, hasError: false };
106
91
  try {
107
92
  const disposer = __addDisposableResource(env_1, new AsyncDisposableStack(), true);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evolu/nodejs",
3
- "version": "3.1.0",
3
+ "version": "4.0.0",
4
4
  "description": "Evolu for Node.js",
5
5
  "author": "Daniel Steigerwald <daniel@steigerwald.cz>",
6
6
  "license": "MIT",
@@ -29,6 +29,11 @@
29
29
  "types": "./dist/src/TestJSDoc.d.ts",
30
30
  "import": "./dist/src/TestJSDoc.js",
31
31
  "default": "./dist/src/TestJSDoc.js"
32
+ },
33
+ "./TestOverviewReporter": {
34
+ "types": "./dist/src/TestOverviewReporter.d.ts",
35
+ "import": "./dist/src/TestOverviewReporter.js",
36
+ "default": "./dist/src/TestOverviewReporter.js"
32
37
  }
33
38
  },
34
39
  "typesVersions": {
@@ -38,11 +43,15 @@
38
43
  ],
39
44
  "TestJSDoc": [
40
45
  "./dist/src/TestJSDoc.d.ts"
46
+ ],
47
+ "TestOverviewReporter": [
48
+ "./dist/src/TestOverviewReporter.d.ts"
41
49
  ]
42
50
  }
43
51
  },
44
52
  "files": [
45
53
  "dist/src/**",
54
+ "!dist/**/*.test.*",
46
55
  "src/**",
47
56
  "README.md"
48
57
  ],
@@ -51,9 +60,9 @@
51
60
  "ws": "^8.21.3"
52
61
  },
53
62
  "devDependencies": {
54
- "@evolu/common": "8.6.0",
55
- "@evolu/oxlint-config": "0.1.0",
56
- "@evolu/typescript-config": "0.1.0",
63
+ "@evolu/common": "8.10.0",
64
+ "@evolu/oxlint-config": "0.2.0",
65
+ "@evolu/typescript-config": "0.1.1",
57
66
  "@types/better-sqlite3": "^9.6.0",
58
67
  "@types/node": "^24.10.9",
59
68
  "@types/ws": "^8.18.1",
@@ -64,9 +73,9 @@
64
73
  "webpack": "^5.109.2"
65
74
  },
66
75
  "peerDependencies": {
67
- "@evolu/common": "^8.0.0",
68
- "@evolu/oxlint-config": "^0.1.0",
69
- "@evolu/typescript-config": "^0.1.0",
76
+ "@evolu/common": "^8.10.0",
77
+ "@evolu/oxlint-config": "^0.2.0",
78
+ "@evolu/typescript-config": "^0.1.1",
70
79
  "oxlint": "^1.79.0",
71
80
  "oxlint-tsgolint": "7.0.2001",
72
81
  "typescript": ">=7.0.0",
@@ -97,7 +106,7 @@
97
106
  }
98
107
  },
99
108
  "engines": {
100
- "node": ">=24.0.0"
109
+ "node": ">=24.20.0"
101
110
  },
102
111
  "publishConfig": {
103
112
  "access": "public"
@@ -0,0 +1,90 @@
1
+ import {
2
+ assertEqual,
3
+ assertErr,
4
+ assertOk,
5
+ assertTrue,
6
+ testAbortError,
7
+ testAbortReason,
8
+ testCreateRun,
9
+ } from "@evolu/common";
10
+ import { test } from "node:test";
11
+ import { spawn } from "./Cli.ts";
12
+
13
+ test("spawn succeeds with inherited stdio and a configured cwd", async () => {
14
+ await using run = testCreateRun();
15
+ const scriptsDirectory = new URL("../../../scripts/", import.meta.url);
16
+ const result = await run(
17
+ spawn(
18
+ process.execPath,
19
+ ["-e", 'process.exit(process.cwd().endsWith("scripts") ? 0 : 1)'],
20
+ { cwd: scriptsDirectory },
21
+ ),
22
+ );
23
+
24
+ assertOk(result);
25
+ });
26
+
27
+ test("spawn returns a start error", async () => {
28
+ await using run = testCreateRun();
29
+ const result = await run(spawn("evolu-command-that-does-not-exist", []));
30
+
31
+ assertErr(result);
32
+ const { message } = result.error;
33
+ assertTrue(
34
+ message.startsWith("Failed to start evolu-command-that-does-not-exist:"),
35
+ );
36
+ assertEqual(result, {
37
+ ok: false,
38
+ error: {
39
+ type: "SpawnError",
40
+ command: "evolu-command-that-does-not-exist",
41
+ exitCode: null,
42
+ signal: null,
43
+ message,
44
+ },
45
+ });
46
+ });
47
+
48
+ test("spawn returns a non-zero exit error", async () => {
49
+ await using run = testCreateRun();
50
+ const result = await run(spawn(process.execPath, ["-e", "process.exit(7)"]));
51
+ const command = `${process.execPath} -e process.exit(7)`;
52
+
53
+ assertErr(result, {
54
+ type: "SpawnError",
55
+ command,
56
+ exitCode: 7,
57
+ signal: null,
58
+ message: `${command} exited with code 7.`,
59
+ });
60
+ });
61
+
62
+ test("spawn returns a signal exit error", async () => {
63
+ await using run = testCreateRun();
64
+ const result = await run(
65
+ spawn(process.execPath, ["-e", 'process.kill(process.pid, "SIGTERM")']),
66
+ );
67
+ const command = `${process.execPath} -e process.kill(process.pid, "SIGTERM")`;
68
+
69
+ assertErr(result, {
70
+ type: "SpawnError",
71
+ command,
72
+ exitCode: null,
73
+ signal: "SIGTERM",
74
+ message: `${command} exited from SIGTERM.`,
75
+ });
76
+ });
77
+
78
+ test("spawn aborts its child process with the Run", async () => {
79
+ await using run = testCreateRun();
80
+ const fiber = run.abortable(
81
+ spawn(process.execPath, ["-e", "setInterval(() => {}, 1000)"]),
82
+ );
83
+
84
+ await new Promise<void>((resolve) => {
85
+ setImmediate(resolve);
86
+ });
87
+ fiber.abort(testAbortReason);
88
+
89
+ assertErr(await fiber, testAbortError);
90
+ });