@evolu/nodejs 3.2.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/Fs.d.ts +63 -0
- package/dist/src/Fs.d.ts.map +1 -0
- package/dist/src/Fs.js +217 -0
- package/dist/src/Task.d.ts +51 -38
- package/dist/src/Task.d.ts.map +1 -1
- package/dist/src/Task.js +2 -2
- package/dist/src/TestJSDoc.d.ts +4 -1
- package/dist/src/TestJSDoc.d.ts.map +1 -1
- package/dist/src/TestJSDoc.js +1 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -0
- package/dist/src/local-first/Relay.d.ts +15 -27
- package/dist/src/local-first/Relay.d.ts.map +1 -1
- package/dist/src/local-first/Relay.js +10 -25
- package/package.json +4 -4
- package/src/Fs.test.ts +1019 -0
- package/src/Fs.ts +300 -0
- package/src/Task.ts +62 -45
- package/src/TestJSDoc.ts +7 -1
- package/src/index.ts +1 -0
- package/src/local-first/Relay.ts +15 -26
package/dist/src/Fs.d.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File system operations using Node.js.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { type Fs } from "@evolu/common";
|
|
7
|
+
/**
|
|
8
|
+
* Creates a {@link Fs} backed by `node:fs/promises`.
|
|
9
|
+
*
|
|
10
|
+
* `readFile` and `writeFile` pass the Run's abort signal to Node and propagate
|
|
11
|
+
* the Run's abort reason if Node rejects after cancellation. Cancellation can
|
|
12
|
+
* leave a write partially completed. Successful operations return their values
|
|
13
|
+
* even if an abort was requested. Other operations run to completion and return
|
|
14
|
+
* their results once started, so callers also receive created temporary
|
|
15
|
+
* directories and can dispose them.
|
|
16
|
+
*
|
|
17
|
+
* Temporary directory parents are resolved through the file system, preserving
|
|
18
|
+
* the meaning of symbolic links followed by `..`. Returned paths are absolute,
|
|
19
|
+
* so cleanup still targets the created directory after a change to the
|
|
20
|
+
* process's working directory.
|
|
21
|
+
*
|
|
22
|
+
* ### Example
|
|
23
|
+
*
|
|
24
|
+
* ```ts
|
|
25
|
+
* import {
|
|
26
|
+
* assertEqual,
|
|
27
|
+
* assertFalse,
|
|
28
|
+
* ok,
|
|
29
|
+
* type FsDep,
|
|
30
|
+
* type FsError,
|
|
31
|
+
* type Task,
|
|
32
|
+
* } from "@evolu/common";
|
|
33
|
+
* import { createNodeFs, runMain } from "@evolu/nodejs";
|
|
34
|
+
* import { join } from "node:path";
|
|
35
|
+
*
|
|
36
|
+
* const main: Task<void, FsError, FsDep> = async (run) => {
|
|
37
|
+
* const { fs } = run.deps;
|
|
38
|
+
* const temp = await run(fs.createTempDirectory({ prefix: "evolu-" }));
|
|
39
|
+
* if (!temp.ok) return temp;
|
|
40
|
+
*
|
|
41
|
+
* {
|
|
42
|
+
* await using directory = temp.value;
|
|
43
|
+
* const path = join(directory.path, "config.json");
|
|
44
|
+
*
|
|
45
|
+
* const result = await run(fs.writeFile(path, '{ "port": 4000 }'));
|
|
46
|
+
* if (!result.ok) return result;
|
|
47
|
+
*
|
|
48
|
+
* const text = await run(fs.readFile(path, "utf8"));
|
|
49
|
+
* if (!text.ok) return text;
|
|
50
|
+
* assertEqual(text.value, '{ "port": 4000 }');
|
|
51
|
+
* }
|
|
52
|
+
*
|
|
53
|
+
* const exists = await run(fs.exists(temp.value.path));
|
|
54
|
+
* if (!exists.ok) return exists;
|
|
55
|
+
* assertFalse(exists.value);
|
|
56
|
+
* return ok();
|
|
57
|
+
* };
|
|
58
|
+
*
|
|
59
|
+
* await runMain({ fs: createNodeFs() }, { mode: "command" })(main);
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export declare const createNodeFs: () => Fs;
|
|
63
|
+
//# sourceMappingURL=Fs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Fs.d.ts","sourceRoot":"","sources":["../../src/Fs.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAOL,KAAK,EAAE,EAQR,MAAM,eAAe,CAAC;AAmBvB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AACH,eAAO,MAAM,YAAY,QAAO,EAgI/B,CAAC"}
|
package/dist/src/Fs.js
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File system operations using Node.js.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { assert, ByteLength, disposable, err, ok, tryAsync, } from "@evolu/common";
|
|
7
|
+
import { constants } from "node:fs";
|
|
8
|
+
import { access, copyFile, cp, mkdir, mkdtemp, readdir, readFile, realpath, rename, rm, stat, writeFile, } from "node:fs/promises";
|
|
9
|
+
import { tmpdir } from "node:os";
|
|
10
|
+
import { join, sep } from "node:path";
|
|
11
|
+
/**
|
|
12
|
+
* Creates a {@link Fs} backed by `node:fs/promises`.
|
|
13
|
+
*
|
|
14
|
+
* `readFile` and `writeFile` pass the Run's abort signal to Node and propagate
|
|
15
|
+
* the Run's abort reason if Node rejects after cancellation. Cancellation can
|
|
16
|
+
* leave a write partially completed. Successful operations return their values
|
|
17
|
+
* even if an abort was requested. Other operations run to completion and return
|
|
18
|
+
* their results once started, so callers also receive created temporary
|
|
19
|
+
* directories and can dispose them.
|
|
20
|
+
*
|
|
21
|
+
* Temporary directory parents are resolved through the file system, preserving
|
|
22
|
+
* the meaning of symbolic links followed by `..`. Returned paths are absolute,
|
|
23
|
+
* so cleanup still targets the created directory after a change to the
|
|
24
|
+
* process's working directory.
|
|
25
|
+
*
|
|
26
|
+
* ### Example
|
|
27
|
+
*
|
|
28
|
+
* ```ts
|
|
29
|
+
* import {
|
|
30
|
+
* assertEqual,
|
|
31
|
+
* assertFalse,
|
|
32
|
+
* ok,
|
|
33
|
+
* type FsDep,
|
|
34
|
+
* type FsError,
|
|
35
|
+
* type Task,
|
|
36
|
+
* } from "@evolu/common";
|
|
37
|
+
* import { createNodeFs, runMain } from "@evolu/nodejs";
|
|
38
|
+
* import { join } from "node:path";
|
|
39
|
+
*
|
|
40
|
+
* const main: Task<void, FsError, FsDep> = async (run) => {
|
|
41
|
+
* const { fs } = run.deps;
|
|
42
|
+
* const temp = await run(fs.createTempDirectory({ prefix: "evolu-" }));
|
|
43
|
+
* if (!temp.ok) return temp;
|
|
44
|
+
*
|
|
45
|
+
* {
|
|
46
|
+
* await using directory = temp.value;
|
|
47
|
+
* const path = join(directory.path, "config.json");
|
|
48
|
+
*
|
|
49
|
+
* const result = await run(fs.writeFile(path, '{ "port": 4000 }'));
|
|
50
|
+
* if (!result.ok) return result;
|
|
51
|
+
*
|
|
52
|
+
* const text = await run(fs.readFile(path, "utf8"));
|
|
53
|
+
* if (!text.ok) return text;
|
|
54
|
+
* assertEqual(text.value, '{ "port": 4000 }');
|
|
55
|
+
* }
|
|
56
|
+
*
|
|
57
|
+
* const exists = await run(fs.exists(temp.value.path));
|
|
58
|
+
* if (!exists.ok) return exists;
|
|
59
|
+
* assertFalse(exists.value);
|
|
60
|
+
* return ok();
|
|
61
|
+
* };
|
|
62
|
+
*
|
|
63
|
+
* await runMain({ fs: createNodeFs() }, { mode: "command" })(main);
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export const createNodeFs = () => {
|
|
67
|
+
function readFileTask(path, encoding) {
|
|
68
|
+
return async (run) => {
|
|
69
|
+
const { signal } = run;
|
|
70
|
+
const result = await tryAsync(() => encoding === undefined
|
|
71
|
+
? readFile(path, { signal })
|
|
72
|
+
: readFile(path, { ...toEncodingOptions(encoding), signal }));
|
|
73
|
+
if (result.ok)
|
|
74
|
+
return result;
|
|
75
|
+
signal.throwIfAborted();
|
|
76
|
+
return err(createFsError("readFile", path, result.error));
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
readFile: readFileTask,
|
|
81
|
+
writeFile: (path, data, options) => async (run) => {
|
|
82
|
+
const { signal } = run;
|
|
83
|
+
const result = await tryAsync(() => writeFile(path, data, { ...options, signal }));
|
|
84
|
+
if (result.ok)
|
|
85
|
+
return result;
|
|
86
|
+
signal.throwIfAborted();
|
|
87
|
+
return err(createFsError("writeFile", path, result.error));
|
|
88
|
+
},
|
|
89
|
+
readDirectory: (path, options) => async () => {
|
|
90
|
+
const result = await tryAsync(() => readdir(path, options));
|
|
91
|
+
return result.ok
|
|
92
|
+
? result
|
|
93
|
+
: err(createFsError("readDirectory", path, result.error));
|
|
94
|
+
},
|
|
95
|
+
createDirectory: (path, options) => async () => {
|
|
96
|
+
const result = await tryAsync(async () => {
|
|
97
|
+
await mkdir(path, options);
|
|
98
|
+
});
|
|
99
|
+
return result.ok
|
|
100
|
+
? ok()
|
|
101
|
+
: err(createFsError("createDirectory", path, result.error));
|
|
102
|
+
},
|
|
103
|
+
copy: (source, destination, options) => async () => {
|
|
104
|
+
const result = await tryAsync(() => cp(source, destination, { ...options, recursive: true }));
|
|
105
|
+
return result.ok
|
|
106
|
+
? ok()
|
|
107
|
+
: err(createFsError("copy", source, result.error, destination));
|
|
108
|
+
},
|
|
109
|
+
copyFile: (source, destination, { overwrite = false } = {}) => async () => {
|
|
110
|
+
const result = await tryAsync(() => copyFile(source, destination, overwrite ? 0 : constants.COPYFILE_EXCL));
|
|
111
|
+
return result.ok
|
|
112
|
+
? ok()
|
|
113
|
+
: err(createFsError("copyFile", source, result.error, destination));
|
|
114
|
+
},
|
|
115
|
+
rename: (source, destination) => async () => {
|
|
116
|
+
const result = await tryAsync(() => rename(source, destination));
|
|
117
|
+
return result.ok
|
|
118
|
+
? ok()
|
|
119
|
+
: err(createFsError("rename", source, result.error, destination));
|
|
120
|
+
},
|
|
121
|
+
remove: (path, options) => async () => {
|
|
122
|
+
const result = await tryAsync(() => rm(path, options));
|
|
123
|
+
return result.ok
|
|
124
|
+
? ok()
|
|
125
|
+
: err(createFsError("remove", path, result.error));
|
|
126
|
+
},
|
|
127
|
+
getMetadata: (path) => async () => {
|
|
128
|
+
const result = await tryAsync(() => stat(path));
|
|
129
|
+
return result.ok
|
|
130
|
+
? ok(statsToFsMetadata(result.value))
|
|
131
|
+
: err(createFsError("getMetadata", path, result.error));
|
|
132
|
+
},
|
|
133
|
+
exists: (path) => async () => {
|
|
134
|
+
const result = await tryAsync(() => access(path));
|
|
135
|
+
if (result.ok)
|
|
136
|
+
return ok(true);
|
|
137
|
+
const error = createFsError("exists", path, result.error);
|
|
138
|
+
return error.reason === "NotFound" ? ok(false) : err(error);
|
|
139
|
+
},
|
|
140
|
+
createTempDirectory: ({ directory, prefix = "" } = {}) => async () => {
|
|
141
|
+
const parent = directory ?? tmpdir();
|
|
142
|
+
const resolvedParent = await tryAsync(() => realpath(parent || "."), (error) => createFsError("createTempDirectory", parent, error));
|
|
143
|
+
if (!resolvedParent.ok)
|
|
144
|
+
return resolvedParent;
|
|
145
|
+
// Keep the parent separator even when the name prefix is empty.
|
|
146
|
+
const pathPrefix = join(resolvedParent.value, sep) + prefix;
|
|
147
|
+
const result = await tryAsync(() => mkdtemp(pathPrefix), (error) => createFsError("createTempDirectory", pathPrefix, error));
|
|
148
|
+
if (!result.ok)
|
|
149
|
+
return result;
|
|
150
|
+
const path = result.value;
|
|
151
|
+
const disposer = new AsyncDisposableStack();
|
|
152
|
+
disposer.defer(() => rm(path, { recursive: true, force: true }));
|
|
153
|
+
return ok(disposable({ path }, disposer));
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
};
|
|
157
|
+
const toEncodingOptions = (encoding) => typeof encoding === "string" ? { encoding } : encoding;
|
|
158
|
+
const fsErrorReasonByCode = {
|
|
159
|
+
ENOENT: "NotFound",
|
|
160
|
+
EEXIST: "AlreadyExists",
|
|
161
|
+
EACCES: "PermissionDenied",
|
|
162
|
+
EPERM: "PermissionDenied",
|
|
163
|
+
EISDIR: "IsDirectory",
|
|
164
|
+
ERR_FS_EISDIR: "IsDirectory",
|
|
165
|
+
ERR_FS_CP_EEXIST: "AlreadyExists",
|
|
166
|
+
ERR_FS_CP_NON_DIR_TO_DIR: "IsDirectory",
|
|
167
|
+
ERR_FS_CP_DIR_TO_NON_DIR: "NotDirectory",
|
|
168
|
+
ENOTDIR: "NotDirectory",
|
|
169
|
+
ENOTEMPTY: "NotEmpty",
|
|
170
|
+
EBUSY: "Busy",
|
|
171
|
+
};
|
|
172
|
+
const createFsError = (method, path, error, destination) => {
|
|
173
|
+
assert(error instanceof Error, "Node fs rejects with an Error.");
|
|
174
|
+
const { code, syscall } = error;
|
|
175
|
+
return {
|
|
176
|
+
type: "FsError",
|
|
177
|
+
reason: fsErrorReasonByCode[String(code)] ?? "Unknown",
|
|
178
|
+
path: typeof path === "string" ? path : path.href,
|
|
179
|
+
...(destination === undefined
|
|
180
|
+
? {}
|
|
181
|
+
: {
|
|
182
|
+
destination: typeof destination === "string" ? destination : destination.href,
|
|
183
|
+
}),
|
|
184
|
+
syscall: syscall ?? method,
|
|
185
|
+
message: error.message,
|
|
186
|
+
};
|
|
187
|
+
};
|
|
188
|
+
const fsEntryTypeByMode = {
|
|
189
|
+
[constants.S_IFREG]: "File",
|
|
190
|
+
[constants.S_IFDIR]: "Directory",
|
|
191
|
+
[constants.S_IFLNK]: "SymbolicLink",
|
|
192
|
+
[constants.S_IFBLK]: "BlockDevice",
|
|
193
|
+
[constants.S_IFCHR]: "CharacterDevice",
|
|
194
|
+
[constants.S_IFIFO]: "FIFO",
|
|
195
|
+
[constants.S_IFSOCK]: "Socket",
|
|
196
|
+
};
|
|
197
|
+
const statsToFsMetadata = (stats) => ({
|
|
198
|
+
type: fsEntryTypeByMode[stats.mode & constants.S_IFMT] ?? "Unknown",
|
|
199
|
+
dev: stats.dev,
|
|
200
|
+
ino: stats.ino,
|
|
201
|
+
mode: stats.mode,
|
|
202
|
+
nlink: stats.nlink,
|
|
203
|
+
uid: stats.uid,
|
|
204
|
+
gid: stats.gid,
|
|
205
|
+
rdev: stats.rdev,
|
|
206
|
+
size: ByteLength.orThrow(stats.size),
|
|
207
|
+
blksize: stats.blksize,
|
|
208
|
+
blocks: stats.blocks,
|
|
209
|
+
atimeMs: stats.atimeMs,
|
|
210
|
+
mtimeMs: stats.mtimeMs,
|
|
211
|
+
ctimeMs: stats.ctimeMs,
|
|
212
|
+
birthtimeMs: stats.birthtimeMs,
|
|
213
|
+
atime: stats.atime,
|
|
214
|
+
mtime: stats.mtime,
|
|
215
|
+
ctime: stats.ctime,
|
|
216
|
+
birthtime: stats.birthtime,
|
|
217
|
+
});
|
package/dist/src/Task.d.ts
CHANGED
|
@@ -22,69 +22,82 @@ export interface RunMainOptions {
|
|
|
22
22
|
* How termination signals affect the process exit status.
|
|
23
23
|
*
|
|
24
24
|
* Services treat a gracefully handled signal as a successful shutdown.
|
|
25
|
-
* Commands use the conventional `128 + signal number` exit status unless
|
|
26
|
-
*
|
|
25
|
+
* Commands use the conventional `128 + signal number` exit status unless
|
|
26
|
+
* `process.exitCode` is already set.
|
|
27
27
|
*
|
|
28
28
|
* @default "service"
|
|
29
29
|
*/
|
|
30
30
|
readonly mode?: RunMainMode;
|
|
31
31
|
}
|
|
32
32
|
/**
|
|
33
|
-
* Runs the main Task
|
|
33
|
+
* Runs the main {@link Task} of a Node.js command or service.
|
|
34
34
|
*
|
|
35
|
-
*
|
|
35
|
+
* A command does a finite job, such as importing data. A service handles
|
|
36
|
+
* ongoing work, such as accepting connections. Choose a mode for how the
|
|
37
|
+
* program reports an interruption:
|
|
36
38
|
*
|
|
37
|
-
* - `
|
|
38
|
-
*
|
|
39
|
-
*
|
|
39
|
+
* - `"command"`: A termination signal means the job was interrupted. After
|
|
40
|
+
* cleanup, use the conventional signal exit status, such as 130 for Ctrl-C,
|
|
41
|
+
* unless `process.exitCode` is already set.
|
|
42
|
+
* - `"service"` (default): A termination signal is an expected way to stop the
|
|
43
|
+
* service. Graceful shutdown does not set a failure exit status.
|
|
40
44
|
*
|
|
41
|
-
*
|
|
42
|
-
* the main Task
|
|
43
|
-
* immediately with its conventional signal status, abandoning cleanup. A signal
|
|
44
|
-
* received during final cleanup still applies signal shutdown behavior.
|
|
45
|
+
* Both modes create one root {@link Run} and wait for cleanup. The mode controls
|
|
46
|
+
* signal exit status; the main Task controls how long `runMain` waits.
|
|
45
47
|
*
|
|
46
|
-
*
|
|
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.
|
|
48
|
+
* ### Lifetime
|
|
51
49
|
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
50
|
+
* When the main Task returns `void`, `runMain` finishes after the Task and root
|
|
51
|
+
* Run cleanup. It does not force the Node.js process to exit.
|
|
52
|
+
*
|
|
53
|
+
* A service can return a live {@link Resource}, such as the relay created by
|
|
54
|
+
* {@link createRelay}. This transfers ownership to `runMain`, which waits for
|
|
55
|
+
* shutdown and then disposes the Resource. It must remain usable after the
|
|
56
|
+
* creating Task finishes; do not dispose it before returning it.
|
|
57
|
+
*
|
|
58
|
+
* Alternatively, the main Task can own its resources with `using` or `await
|
|
59
|
+
* using` while awaiting {@link waitForAbort} with `run(waitForAbort)`. Waiting
|
|
60
|
+
* for abort does not itself keep Node.js running: the service needs active
|
|
61
|
+
* work, such as a listening server.
|
|
62
|
+
*
|
|
63
|
+
* ### Shutdown and errors
|
|
64
|
+
*
|
|
65
|
+
* Handles `SIGINT` (Ctrl-C), `SIGTERM` (termination by the OS or a service
|
|
66
|
+
* host), and `SIGBREAK` (Ctrl-Break on Windows). The first signal logs shutdown
|
|
67
|
+
* progress, aborts the root Run, and waits for cleanup. A second signal exits
|
|
68
|
+
* immediately with its conventional signal status, abandoning cleanup. Signals
|
|
69
|
+
* are still handled during final cleanup.
|
|
70
|
+
*
|
|
71
|
+
* An error returned by the main Task is fatal. {@link getOrThrow} preserves it
|
|
72
|
+
* in `Error.cause`; the Run reports the failure and finishes cleanup. Every
|
|
73
|
+
* reported defect sets `process.exitCode` to 1, including an observer defect
|
|
74
|
+
* that does not abort the Run. The default reporter logs to the configured
|
|
56
75
|
* Evolu console.
|
|
57
76
|
*
|
|
58
77
|
* Escaped uncaught exceptions and unhandled rejections remain under Node.js
|
|
59
78
|
* native reporting and termination.
|
|
60
79
|
*
|
|
61
|
-
* ###
|
|
80
|
+
* ### Example
|
|
62
81
|
*
|
|
63
|
-
*
|
|
64
|
-
* const deps = { ...createRelayDeps(), console: createConsole() };
|
|
65
|
-
*
|
|
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:
|
|
82
|
+
* A command finishes when its main Task completes:
|
|
71
83
|
*
|
|
72
84
|
* ```ts
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* return await run(waitForAbort);
|
|
76
|
-
* });
|
|
77
|
-
* ```
|
|
85
|
+
* import { assertTrue, ok, type Task } from "@evolu/common";
|
|
86
|
+
* import { runMain } from "@evolu/nodejs";
|
|
78
87
|
*
|
|
79
|
-
*
|
|
88
|
+
* let completed = false;
|
|
89
|
+
* const command: Task<void> = () => {
|
|
90
|
+
* completed = true;
|
|
91
|
+
* return ok();
|
|
92
|
+
* };
|
|
80
93
|
*
|
|
81
|
-
* ```ts
|
|
82
94
|
* await runMain(command, { mode: "command" });
|
|
95
|
+
* assertTrue(completed);
|
|
83
96
|
* ```
|
|
84
97
|
*
|
|
85
98
|
* @group Node.js Task
|
|
86
99
|
*/
|
|
87
|
-
export declare function runMain<T extends void | Resource>(main: Task<T>, options?: RunMainOptions): Promise<void>;
|
|
100
|
+
export declare function runMain<T extends void | Resource, E = never>(main: Task<T, E>, options?: RunMainOptions): Promise<void>;
|
|
88
101
|
/** With custom dependencies. */
|
|
89
|
-
export declare function runMain<D extends object>(deps: RunCustomDeps<D>, options?: RunMainOptions): <T extends void | Resource>(main: Task<T,
|
|
102
|
+
export declare function runMain<D extends object>(deps: RunCustomDeps<D>, options?: RunMainOptions): <T extends void | Resource, E = never>(main: Task<T, E, D>) => Promise<void>;
|
|
90
103
|
//# sourceMappingURL=Task.d.ts.map
|
package/dist/src/Task.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Task.d.ts","sourceRoot":"","sources":["../../src/Task.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"Task.d.ts","sourceRoot":"","sources":["../../src/Task.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EASL,KAAK,QAAQ,EAEb,KAAK,aAAa,EAClB,KAAK,IAAI,EACT,KAAK,KAAK,EACX,MAAM,eAAe,CAAC;AAGvB;;;;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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmEG;AACH,wBAAgB,OAAO,CAAC,CAAC,SAAS,IAAI,GAAG,QAAQ,EAAE,CAAC,GAAG,KAAK,EAC1D,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,EAChB,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,CAAC,GAAG,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC"}
|
package/dist/src/Task.js
CHANGED
|
@@ -55,7 +55,7 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
|
|
|
55
55
|
var e = new Error(message);
|
|
56
56
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
57
57
|
});
|
|
58
|
-
import { createConsole, createRun, isDisposable, ok, waitForAbort, } from "@evolu/common";
|
|
58
|
+
import { createConsole, createRun, getOrThrow, isDisposable, ok, waitForAbort, } from "@evolu/common";
|
|
59
59
|
export function runMain(mainOrDeps, { mode = "service" } = {}) {
|
|
60
60
|
return typeof mainOrDeps === "function"
|
|
61
61
|
? runMainInternal(mainOrDeps, {}, mode)
|
|
@@ -106,7 +106,7 @@ const runMainInternal = async (main, deps, mode) => {
|
|
|
106
106
|
await run(async (run) => {
|
|
107
107
|
const env_2 = { stack: [], error: void 0, hasError: false };
|
|
108
108
|
try {
|
|
109
|
-
const resource = await run
|
|
109
|
+
const resource = getOrThrow(await run(main));
|
|
110
110
|
if (!isDisposable(resource))
|
|
111
111
|
return ok();
|
|
112
112
|
const _resource = __addDisposableResource(env_2, resource, true);
|
package/dist/src/TestJSDoc.d.ts
CHANGED
|
@@ -14,7 +14,10 @@ import { type ReadonlyRecord } from "@evolu/common";
|
|
|
14
14
|
export interface TestJSDocExamplesOptions {
|
|
15
15
|
/** Source files or glob patterns resolved from `cwd`. */
|
|
16
16
|
readonly include: string | ReadonlyArray<string>;
|
|
17
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* Package imports redirected to absolute TypeScript entry paths. Type
|
|
19
|
+
* checking also applies these aliases to imports within those modules.
|
|
20
|
+
*/
|
|
18
21
|
readonly aliases?: ReadonlyRecord<string, string>;
|
|
19
22
|
/** Directory used for globbing and package resolution. */
|
|
20
23
|
readonly cwd?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TestJSDoc.d.ts","sourceRoot":"","sources":["../../src/TestJSDoc.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAY,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAiB9D;;;GAGG;AACH,MAAM,WAAW,wBAAwB;IACvC,yDAAyD;IACzD,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IACjD
|
|
1
|
+
{"version":3,"file":"TestJSDoc.d.ts","sourceRoot":"","sources":["../../src/TestJSDoc.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAY,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAiB9D;;;GAGG;AACH,MAAM,WAAW,wBAAwB;IACvC,yDAAyD;IACzD,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IACjD;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClD,0DAA0D;IAC1D,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,+DAA+D;IAC/D,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;CACrC;AA+CD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;AACH,eAAO,MAAM,iBAAiB,kDAK3B,wBAAwB,KAAG,OAAO,CAAC,IAAI,CAmKzC,CAAC"}
|
package/dist/src/TestJSDoc.js
CHANGED
|
@@ -113,6 +113,7 @@ export const testJSDocExamples = async ({ include, aliases = {}, cwd = process.c
|
|
|
113
113
|
noEmit: true,
|
|
114
114
|
noUnusedLocals: false,
|
|
115
115
|
noUnusedParameters: false,
|
|
116
|
+
paths: Object.fromEntries(Object.entries(aliases).map(([name, target]) => [name, [target]])),
|
|
116
117
|
target: "es2022",
|
|
117
118
|
types: ["node"],
|
|
118
119
|
},
|
package/dist/src/index.d.ts
CHANGED
package/dist/src/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,wBAAwB,CAAC;AACvC,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,wBAAwB,CAAC;AACvC,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC"}
|
package/dist/src/index.js
CHANGED
|
@@ -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
|
-
/**
|
|
5
|
-
|
|
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
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* const
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
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
|
-
*
|
|
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
|
|
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
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
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
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
* }),
|
|
80
|
+
* const main = createRelay({
|
|
81
|
+
* port: Port.orThrow(4000),
|
|
82
|
+
* isOwnerWithinQuota: (_ownerId, requiredBytes) =>
|
|
83
|
+
* requiredBytes <= 1024 * 1024,
|
|
85
84
|
* });
|
|
86
85
|
*
|
|
87
|
-
*
|
|
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
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "Evolu for Node.js",
|
|
5
5
|
"author": "Daniel Steigerwald <daniel@steigerwald.cz>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"ws": "^8.21.3"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
|
-
"@evolu/common": "8.
|
|
63
|
+
"@evolu/common": "8.10.0",
|
|
64
64
|
"@evolu/oxlint-config": "0.2.0",
|
|
65
65
|
"@evolu/typescript-config": "0.1.1",
|
|
66
66
|
"@types/better-sqlite3": "^9.6.0",
|
|
@@ -73,9 +73,9 @@
|
|
|
73
73
|
"webpack": "^5.109.2"
|
|
74
74
|
},
|
|
75
75
|
"peerDependencies": {
|
|
76
|
-
"@evolu/common": "^8.
|
|
76
|
+
"@evolu/common": "^8.10.0",
|
|
77
77
|
"@evolu/oxlint-config": "^0.2.0",
|
|
78
|
-
"@evolu/typescript-config": "^0.1.
|
|
78
|
+
"@evolu/typescript-config": "^0.1.1",
|
|
79
79
|
"oxlint": "^1.79.0",
|
|
80
80
|
"oxlint-tsgolint": "7.0.2001",
|
|
81
81
|
"typescript": ">=7.0.0",
|