@evolu/nodejs 3.0.0-next.2 → 3.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.
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Abort-aware CLI process utilities.
3
+ *
4
+ * @module
5
+ */
6
+ import { type Task, type Typed } from "@evolu/common";
7
+ /** Runs a command with inherited stdio. */
8
+ export type Spawn = (file: string, args: ReadonlyArray<string>, options?: {
9
+ readonly cwd?: string | URL;
10
+ }) => Task<void, SpawnError>;
11
+ /** Failure to start a command or an unsuccessful command exit. */
12
+ export interface SpawnError extends Typed<"SpawnError"> {
13
+ readonly command: string;
14
+ readonly exitCode: number | null;
15
+ readonly signal: NodeJS.Signals | null;
16
+ readonly message: string;
17
+ }
18
+ /** Dependency wrapper for {@link spawn}. */
19
+ export interface SpawnDep {
20
+ readonly spawn: Spawn;
21
+ }
22
+ /**
23
+ * Runs a command with inherited stdio and aborts it with the current Run.
24
+ *
25
+ * A zero exit code succeeds. A start failure, non-zero exit code, or signal
26
+ * exit returns {@link SpawnError}.
27
+ */
28
+ export declare const spawn: Spawn;
29
+ //# sourceMappingURL=Cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Cli.d.ts","sourceRoot":"","sources":["../../src/Cli.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAqB,KAAK,IAAI,EAAE,KAAK,KAAK,EAAE,MAAM,eAAe,CAAC;AAGzE,2CAA2C;AAC3C,MAAM,MAAM,KAAK,GAAG,CAClB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,EAC3B,OAAO,CAAC,EAAE;IACR,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,GAAG,CAAC;CAC7B,KACE,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;AAE5B,kEAAkE;AAClE,MAAM,WAAW,UAAW,SAAQ,KAAK,CAAC,YAAY,CAAC;IACrD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,4CAA4C;AAC5C,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;CACvB;AAED;;;;;GAKG;AACH,eAAO,MAAM,KAAK,EAAE,KAwCnB,CAAC"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Abort-aware CLI process utilities.
3
+ *
4
+ * @module
5
+ */
6
+ import { callback, err, ok } from "@evolu/common";
7
+ import { spawn as nodeSpawn } from "node:child_process";
8
+ /**
9
+ * Runs a command with inherited stdio and aborts it with the current Run.
10
+ *
11
+ * A zero exit code succeeds. A start failure, non-zero exit code, or signal
12
+ * exit returns {@link SpawnError}.
13
+ */
14
+ export const spawn = (file, args, { cwd } = {}) => {
15
+ const command = [file, ...args].join(" ");
16
+ return callback(({ run, resolve }) => {
17
+ const child = nodeSpawn(file, args, {
18
+ cwd,
19
+ signal: run.signal,
20
+ stdio: "inherit",
21
+ });
22
+ child.once("error", (error) => {
23
+ if (run.signal.aborted)
24
+ return;
25
+ resolve(err({
26
+ type: "SpawnError",
27
+ command,
28
+ exitCode: null,
29
+ signal: null,
30
+ message: `Failed to start ${command}: ${error.message}`,
31
+ }));
32
+ });
33
+ child.once("close", (exitCode, signal) => {
34
+ if (run.signal.aborted)
35
+ return;
36
+ resolve(exitCode === 0
37
+ ? ok()
38
+ : err({
39
+ type: "SpawnError",
40
+ command,
41
+ exitCode,
42
+ signal,
43
+ message: signal == null
44
+ ? `${command} exited with code ${exitCode}.`
45
+ : `${command} exited from ${signal}.`,
46
+ }));
47
+ });
48
+ });
49
+ };
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Node.js platform utilities.
3
+ *
4
+ * @module
5
+ */
6
+ import { PositiveInt } from "@evolu/common";
7
+ /** Returns the recommended amount of parallelism available to this process. */
8
+ export type AvailableParallelism = () => PositiveInt;
9
+ /** Dependency wrapper for {@link availableParallelism}. */
10
+ export interface AvailableParallelismDep {
11
+ readonly availableParallelism: AvailableParallelism;
12
+ }
13
+ /** Returns the recommended amount of parallelism available to this process. */
14
+ export declare const availableParallelism: AvailableParallelism;
15
+ //# sourceMappingURL=Platform.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Platform.d.ts","sourceRoot":"","sources":["../../src/Platform.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAG5C,+EAA+E;AAC/E,MAAM,MAAM,oBAAoB,GAAG,MAAM,WAAW,CAAC;AAErD,2DAA2D;AAC3D,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,oBAAoB,EAAE,oBAAoB,CAAC;CACrD;AAED,+EAA+E;AAC/E,eAAO,MAAM,oBAAoB,EAAE,oBACc,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Node.js platform utilities.
3
+ *
4
+ * @module
5
+ */
6
+ import { PositiveInt } from "@evolu/common";
7
+ import { availableParallelism as nodeAvailableParallelism } from "node:os";
8
+ /** Returns the recommended amount of parallelism available to this process. */
9
+ export const availableParallelism = () => PositiveInt.orThrow(nodeAvailableParallelism());
@@ -1 +1 @@
1
- {"version":3,"file":"Sqlite.d.ts","sourceRoot":"","sources":["../../src/Sqlite.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,kBAAkB,EAGxB,MAAM,eAAe,CAAC;AAGvB,eAAO,MAAM,wBAAwB,EAAE,kBAiDpC,CAAC"}
1
+ {"version":3,"file":"Sqlite.d.ts","sourceRoot":"","sources":["../../src/Sqlite.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,kBAAkB,EAExB,MAAM,eAAe,CAAC;AAIvB,eAAO,MAAM,wBAAwB,EAAE,kBAoEpC,CAAC"}
@@ -1,38 +1,128 @@
1
- import { createPreparedStatementsCache, lazyVoid, ok, } from "@evolu/common";
2
- import BetterSQLite, {} from "better-sqlite3";
3
- export const createBetterSqliteDriver = (name, options) => () => {
4
- const filename = options?.mode === "memory" ? ":memory:" : `${name}.db`;
5
- const stack = new globalThis.DisposableStack();
6
- const db = stack.adopt(new BetterSQLite(filename), (db) => {
7
- db.close();
8
- });
9
- const cache = stack.use(createPreparedStatementsCache((sql) => db.prepare(sql),
10
- // Not needed.
11
- // https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-statement
12
- lazyVoid));
13
- const driver = {
14
- exec: (query) => {
15
- // Always prepare is recommended for better-sqlite3
16
- const prepared = cache.get(query, true);
17
- if (prepared.reader) {
18
- const rows = prepared.all(query.parameters);
19
- return { rows, changes: 0 };
20
- }
21
- const changes = prepared.run(query.parameters).changes;
22
- return { rows: [], changes };
23
- },
24
- export: () => {
25
- const file = db.serialize();
26
- const { buffer } = file;
27
- if (buffer instanceof ArrayBuffer) {
28
- return new Uint8Array(buffer, file.byteOffset, file.byteLength);
1
+ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
2
+ if (value !== null && value !== void 0) {
3
+ if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
4
+ var dispose, inner;
5
+ if (async) {
6
+ if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
7
+ dispose = value[Symbol.asyncDispose];
8
+ }
9
+ if (dispose === void 0) {
10
+ if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
11
+ dispose = value[Symbol.dispose];
12
+ if (async) inner = dispose;
13
+ }
14
+ if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
15
+ if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
16
+ env.stack.push({ value: value, dispose: dispose, async: async });
17
+ }
18
+ else if (async) {
19
+ env.stack.push({ async: true });
20
+ }
21
+ return value;
22
+ };
23
+ var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
24
+ return function (env) {
25
+ function fail(e) {
26
+ env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
27
+ env.hasError = true;
28
+ }
29
+ var r, s = 0;
30
+ function next() {
31
+ while (r = env.stack.pop()) {
32
+ try {
33
+ if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
34
+ if (r.dispose) {
35
+ var result = r.dispose.call(r.value);
36
+ if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
37
+ }
38
+ else s |= 1;
39
+ }
40
+ catch (e) {
41
+ fail(e);
42
+ }
29
43
  }
30
- // Ensure export uses transferable ArrayBuffer backing.
31
- return new Uint8Array(file);
32
- },
33
- [Symbol.dispose]: () => {
34
- stack.dispose();
35
- },
44
+ if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
45
+ if (env.hasError) throw env.error;
46
+ }
47
+ return next();
36
48
  };
37
- return ok(driver);
49
+ })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
50
+ var e = new Error(message);
51
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
52
+ });
53
+ import { constVoid, createPreparedStatementsCache, ok, } from "@evolu/common";
54
+ import BetterSQLite, {} from "better-sqlite3";
55
+ import { rmSync } from "fs";
56
+ export const createBetterSqliteDriver = (name, options) => () => {
57
+ const env_1 = { stack: [], error: void 0, hasError: false };
58
+ try {
59
+ const filename = options?.mode === "memory" ? ":memory:" : `${name}.db`;
60
+ const filenamesToDelete = options?.mode === "memory"
61
+ ? []
62
+ : [
63
+ filename,
64
+ `${filename}-shm`,
65
+ `${filename}-wal`,
66
+ `${filename}-journal`,
67
+ ];
68
+ const disposer = __addDisposableResource(env_1, new DisposableStack(), false);
69
+ const db = disposer.adopt(new BetterSQLite(filename), (db) => {
70
+ db.close();
71
+ });
72
+ const cache = disposer.use(createPreparedStatementsCache((sql) => db.prepare(sql),
73
+ // Not needed.
74
+ // https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-statement
75
+ constVoid));
76
+ const disposables = disposer.move();
77
+ return ok({
78
+ exec: (query) => {
79
+ // Always prepare is recommended for better-sqlite3
80
+ const prepared = cache.get(query, true);
81
+ if (prepared.reader) {
82
+ const rows = prepared.all(query.parameters);
83
+ return { rows, changes: 0 };
84
+ }
85
+ const changes = prepared.run(query.parameters).changes;
86
+ return { rows: [], changes };
87
+ },
88
+ export: () => {
89
+ const file = db.serialize();
90
+ const { buffer } = file;
91
+ if (buffer instanceof ArrayBuffer) {
92
+ return new Uint8Array(buffer, file.byteOffset, file.byteLength);
93
+ }
94
+ // Ensure export uses transferable ArrayBuffer backing.
95
+ return new Uint8Array(file);
96
+ },
97
+ deleteDatabase: () => {
98
+ const env_2 = { stack: [], error: void 0, hasError: false };
99
+ try {
100
+ const deleteDisposer = __addDisposableResource(env_2, new DisposableStack(), false);
101
+ for (const filename of filenamesToDelete) {
102
+ deleteDisposer.defer(() => {
103
+ rmSync(filename, { force: true });
104
+ });
105
+ }
106
+ deleteDisposer.use(disposables);
107
+ }
108
+ catch (e_2) {
109
+ env_2.error = e_2;
110
+ env_2.hasError = true;
111
+ }
112
+ finally {
113
+ __disposeResources(env_2);
114
+ }
115
+ },
116
+ [Symbol.dispose]: () => {
117
+ disposables.dispose();
118
+ },
119
+ });
120
+ }
121
+ catch (e_1) {
122
+ env_1.error = e_1;
123
+ env_1.hasError = true;
124
+ }
125
+ finally {
126
+ __disposeResources(env_1);
127
+ }
38
128
  };
@@ -3,43 +3,88 @@
3
3
  *
4
4
  * @module
5
5
  */
6
- import { type CreateRun, type RunDeps } from "@evolu/common";
6
+ import { type Resource, type RunCustomDeps, type Task, type Typed } from "@evolu/common";
7
7
  /**
8
- * A promise that resolves when a termination signal is received.
8
+ * An abort requested by a Node.js termination signal.
9
9
  *
10
- * Resolves on `SIGINT` (Ctrl-C), `SIGTERM` (OS/k8s/Docker termination),
11
- * `SIGHUP` (console close/terminal disconnect), or `SIGBREAK` (Windows
12
- * Ctrl-Break).
13
- *
14
- * @group Node.js Run
10
+ * @group Node.js Task
15
11
  */
16
- export type Shutdown = Promise<void>;
17
- export interface ShutdownDep {
18
- readonly shutdown: Shutdown;
12
+ export interface NodeSignalAbortReason extends Typed<"NodeSignalAbortReason"> {
13
+ readonly signal: NodeSignal;
14
+ }
15
+ /** A Node.js termination signal handled by {@link runMain}. */
16
+ export type NodeSignal = "SIGINT" | "SIGTERM" | "SIGBREAK";
17
+ /** Process lifecycle behavior for {@link runMain}. */
18
+ export type RunMainMode = "service" | "command";
19
+ /** Options for {@link runMain}. */
20
+ export interface RunMainOptions {
21
+ /**
22
+ * How termination signals affect the process exit status.
23
+ *
24
+ * Services treat a gracefully handled signal as a successful shutdown.
25
+ * Commands use the conventional `128 + signal number` exit status unless a
26
+ * reported defect has already set a failure status.
27
+ *
28
+ * @default "service"
29
+ */
30
+ readonly mode?: RunMainMode;
19
31
  }
20
32
  /**
21
- * Creates {@link Run} for Node.js with global error handling and graceful
22
- * shutdown.
33
+ * Runs the main Task as the Node.js program lifecycle.
34
+ *
35
+ * Creates one root {@link Run} and aborts it on:
36
+ *
37
+ * - `SIGINT`: Ctrl-C on all platforms.
38
+ * - `SIGTERM`: OS, service, Docker, or Kubernetes termination on Unix.
39
+ * - `SIGBREAK`: Ctrl-Break on Windows.
40
+ *
41
+ * The first signal logs shutdown progress, aborts the root Run, and waits for
42
+ * the main Task and structured cleanup to finish. A subsequent signal exits
43
+ * immediately with its conventional signal status, abandoning cleanup. A signal
44
+ * received during final cleanup still applies signal shutdown behavior.
45
+ *
46
+ * A main Task returning {@link Resource} keeps the program running until a
47
+ * termination signal and is disposed during shutdown. A main Task returning
48
+ * `void` completes the program immediately. A Resource result transfers
49
+ * ownership of a live resource that must remain valid after its creating Task
50
+ * settles.
51
+ *
52
+ * Service mode treats graceful signal shutdown as successful. Command mode
53
+ * preserves conventional signal exit statuses. Every defect reported through
54
+ * `reportDefect`, including an observer defect that does not abort the Run,
55
+ * sets `process.exitCode` to 1. The default reporter logs to the configured
56
+ * Evolu console.
23
57
  *
24
- * Registers `uncaughtException` and `unhandledRejection` handlers that log
25
- * errors and initiate graceful shutdown. Adds a `shutdown` promise to deps that
26
- * resolves on termination signals (`SIGINT`, `SIGTERM`, `SIGHUP`). Handlers are
27
- * removed when the Run is disposed.
58
+ * Escaped uncaught exceptions and unhandled rejections remain under Node.js
59
+ * native reporting and termination.
28
60
  *
29
- * ### Example
61
+ * ### Service Example
30
62
  *
31
63
  * ```ts
32
- * const deps = { ...createRelayDeps(), console };
64
+ * const deps = { ...createRelayDeps(), console: createConsole() };
33
65
  *
34
- * await using run = createRun(deps);
35
- * await using stack = new AsyncDisposableStack();
66
+ * await runMain(deps)(createRelay({ port: 4000 }));
67
+ * ```
68
+ *
69
+ * A Task returning `void` can keep a service alive explicitly when no Resource
70
+ * owns its lifetime:
71
+ *
72
+ * ```ts
73
+ * await runMain(deps)(async (run) => {
74
+ * void run(processMessages);
75
+ * return await run(waitForAbort);
76
+ * });
77
+ * ```
36
78
  *
37
- * stack.use(await run.orThrow(startRelay({ port: 4000 })));
79
+ * ### Command Example
38
80
  *
39
- * await run.deps.shutdown;
81
+ * ```ts
82
+ * await runMain(command, { mode: "command" });
40
83
  * ```
41
84
  *
42
- * @group Node.js Run
85
+ * @group Node.js Task
43
86
  */
44
- export declare const createRun: CreateRun<RunDeps & ShutdownDep>;
87
+ export declare function runMain<T extends void | Resource>(main: Task<T>, options?: RunMainOptions): Promise<void>;
88
+ /** With custom dependencies. */
89
+ export declare function runMain<D extends object>(deps: RunCustomDeps<D>, options?: RunMainOptions): <T extends void | Resource>(main: Task<T, never, D>) => Promise<void>;
45
90
  //# sourceMappingURL=Task.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Task.d.ts","sourceRoot":"","sources":["../../src/Task.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAGL,KAAK,SAAS,EAEd,KAAK,OAAO,EACb,MAAM,eAAe,CAAC;AAEvB;;;;;;;;GAQG;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAErC,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,SAAS,EAAE,SAAS,CAAC,OAAO,GAAG,WAAW,CAuCtD,CAAC"}
1
+ {"version":3,"file":"Task.d.ts","sourceRoot":"","sources":["../../src/Task.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAQL,KAAK,QAAQ,EAEb,KAAK,aAAa,EAClB,KAAK,IAAI,EACT,KAAK,KAAK,EACX,MAAM,eAAe,CAAC;AAEvB;;;;GAIG;AACH,MAAM,WAAW,qBAAsB,SAAQ,KAAK,CAAC,uBAAuB,CAAC;IAC3E,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;CAC7B;AAED,+DAA+D;AAC/D,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,SAAS,GAAG,UAAU,CAAC;AAE3D,sDAAsD;AACtD,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,SAAS,CAAC;AAEhD,mCAAmC;AACnC,MAAM,WAAW,cAAc;IAC7B;;;;;;;;OAQG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,WAAW,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AACH,wBAAgB,OAAO,CAAC,CAAC,SAAS,IAAI,GAAG,QAAQ,EAC/C,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,EACb,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,IAAI,CAAC,CAAC;AACjB,gCAAgC;AAChC,wBAAgB,OAAO,CAAC,CAAC,SAAS,MAAM,EACtC,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC,EACtB,OAAO,CAAC,EAAE,cAAc,GACvB,CAAC,CAAC,SAAS,IAAI,GAAG,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC"}
package/dist/src/Task.js CHANGED
@@ -3,57 +3,153 @@
3
3
  *
4
4
  * @module
5
5
  */
6
- import { createRun as createCommonRun, createUnknownError, } from "@evolu/common";
7
- /**
8
- * Creates {@link Run} for Node.js with global error handling and graceful
9
- * shutdown.
10
- *
11
- * Registers `uncaughtException` and `unhandledRejection` handlers that log
12
- * errors and initiate graceful shutdown. Adds a `shutdown` promise to deps that
13
- * resolves on termination signals (`SIGINT`, `SIGTERM`, `SIGHUP`). Handlers are
14
- * removed when the Run is disposed.
15
- *
16
- * ### Example
17
- *
18
- * ```ts
19
- * const deps = { ...createRelayDeps(), console };
20
- *
21
- * await using run = createRun(deps);
22
- * await using stack = new AsyncDisposableStack();
23
- *
24
- * stack.use(await run.orThrow(startRelay({ port: 4000 })));
25
- *
26
- * await run.deps.shutdown;
27
- * ```
28
- *
29
- * @group Node.js Run
30
- */
31
- export const createRun = (deps) => {
32
- const { promise: shutdown, resolve: resolveShutdown } = Promise.withResolvers();
33
- const run = createCommonRun({ ...deps, shutdown });
34
- const console = run.deps.console.child("global");
35
- const handleError = (source) => (error) => {
36
- console.error(source, createUnknownError(error));
37
- process.exitCode = 1;
38
- // Resolve shutdown so `await run.deps.shutdown` unblocks
39
- // and allows the stack to be disposed.
40
- resolveShutdown();
6
+ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
7
+ if (value !== null && value !== void 0) {
8
+ if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
9
+ var dispose, inner;
10
+ if (async) {
11
+ if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
12
+ dispose = value[Symbol.asyncDispose];
13
+ }
14
+ if (dispose === void 0) {
15
+ if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
16
+ dispose = value[Symbol.dispose];
17
+ if (async) inner = dispose;
18
+ }
19
+ if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
20
+ if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
21
+ env.stack.push({ value: value, dispose: dispose, async: async });
22
+ }
23
+ else if (async) {
24
+ env.stack.push({ async: true });
25
+ }
26
+ return value;
27
+ };
28
+ var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
29
+ return function (env) {
30
+ function fail(e) {
31
+ env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
32
+ env.hasError = true;
33
+ }
34
+ var r, s = 0;
35
+ function next() {
36
+ while (r = env.stack.pop()) {
37
+ try {
38
+ if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
39
+ if (r.dispose) {
40
+ var result = r.dispose.call(r.value);
41
+ if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
42
+ }
43
+ else s |= 1;
44
+ }
45
+ catch (e) {
46
+ fail(e);
47
+ }
48
+ }
49
+ if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
50
+ if (env.hasError) throw env.error;
51
+ }
52
+ return next();
41
53
  };
42
- const handleUncaughtException = handleError("uncaughtException");
43
- const handleUnhandledRejection = handleError("unhandledRejection");
44
- process.on("uncaughtException", handleUncaughtException);
45
- process.on("unhandledRejection", handleUnhandledRejection);
46
- process.on("SIGINT", resolveShutdown); // Ctrl-C (all platforms)
47
- process.on("SIGTERM", resolveShutdown); // OS/k8s/Docker termination (Unix)
48
- process.on("SIGHUP", resolveShutdown); // Console close (Windows), terminal disconnect (Unix)
49
- process.on("SIGBREAK", resolveShutdown); // Ctrl-Break (Windows)
50
- run.onAbort(() => {
51
- process.off("uncaughtException", handleUncaughtException);
52
- process.off("unhandledRejection", handleUnhandledRejection);
53
- process.off("SIGINT", resolveShutdown);
54
- process.off("SIGTERM", resolveShutdown);
55
- process.off("SIGHUP", resolveShutdown);
56
- process.off("SIGBREAK", resolveShutdown);
57
- });
58
- return run;
54
+ })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
55
+ var e = new Error(message);
56
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
57
+ });
58
+ import { createConsole, createRun, isDisposable, ok, waitForAbort, } from "@evolu/common";
59
+ export function runMain(mainOrDeps, { mode = "service" } = {}) {
60
+ return typeof mainOrDeps === "function"
61
+ ? runMainInternal(mainOrDeps, {}, mode)
62
+ : (main) => runMainInternal(main, mainOrDeps, mode);
63
+ }
64
+ const commandExitCodeBySignal = {
65
+ SIGINT: 130,
66
+ SIGTERM: 143,
67
+ SIGBREAK: 149,
68
+ };
69
+ const runMainInternal = async (main, deps, mode) => {
70
+ const env_1 = { stack: [], error: void 0, hasError: false };
71
+ try {
72
+ const console = deps.console ?? createConsole();
73
+ const mainConsole = console.child("main");
74
+ let defectReported = false;
75
+ let receivedSignal = null;
76
+ const disposer = __addDisposableResource(env_1, new AsyncDisposableStack(), true);
77
+ const run = disposer.use(createRun({
78
+ ...deps,
79
+ console,
80
+ reportDefect: (reported) => {
81
+ defectReported = true;
82
+ process.exitCode = 1;
83
+ if (deps.reportDefect)
84
+ deps.reportDefect(reported);
85
+ else
86
+ console.error(reported);
87
+ },
88
+ }));
89
+ ["SIGINT", "SIGTERM", "SIGBREAK"].forEach((signal) => {
90
+ const handleSignal = () => {
91
+ if (receivedSignal !== null) {
92
+ mainConsole.warn("Forcing shutdown...");
93
+ process.exit(commandExitCodeBySignal[signal]);
94
+ return;
95
+ }
96
+ receivedSignal = signal;
97
+ mainConsole.info("Shutting down...");
98
+ run.abort({ type: "NodeSignalAbortReason", signal });
99
+ };
100
+ process.on(signal, handleSignal);
101
+ run.defer(() => {
102
+ process.off(signal, handleSignal);
103
+ });
104
+ });
105
+ try {
106
+ await run(async (run) => {
107
+ const env_2 = { stack: [], error: void 0, hasError: false };
108
+ try {
109
+ const resource = await run.ok(main);
110
+ if (!isDisposable(resource))
111
+ return ok();
112
+ const _resource = __addDisposableResource(env_2, resource, true);
113
+ return await run(waitForAbort);
114
+ }
115
+ catch (e_2) {
116
+ env_2.error = e_2;
117
+ env_2.hasError = true;
118
+ }
119
+ finally {
120
+ const result_2 = __disposeResources(env_2);
121
+ if (result_2)
122
+ await result_2;
123
+ }
124
+ });
125
+ }
126
+ catch {
127
+ // Aborts are control flow; defects are already handled by reportDefect.
128
+ }
129
+ // Move ownership out of the await-using setup safety net so an already
130
+ // reported finalizer defect can be suppressed during explicit disposal.
131
+ try {
132
+ await disposer.move().disposeAsync();
133
+ }
134
+ catch {
135
+ // Finalizer defects are already handled by reportDefect.
136
+ }
137
+ if (receivedSignal !== null) {
138
+ if (defectReported)
139
+ mainConsole.warn("Shutdown finished with errors");
140
+ else
141
+ mainConsole.info("Shutdown complete");
142
+ if (mode === "command")
143
+ process.exitCode ??= commandExitCodeBySignal[receivedSignal];
144
+ }
145
+ }
146
+ catch (e_1) {
147
+ env_1.error = e_1;
148
+ env_1.hasError = true;
149
+ }
150
+ finally {
151
+ const result_1 = __disposeResources(env_1);
152
+ if (result_1)
153
+ await result_1;
154
+ }
59
155
  };