@evolu/nodejs 3.0.0-next.2 → 3.0.0-next.3
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/Sqlite.d.ts.map +1 -1
- package/dist/src/Sqlite.js +124 -34
- package/dist/src/Task.d.ts +4 -4
- package/dist/src/Task.d.ts.map +1 -1
- package/dist/src/Task.js +2 -2
- package/dist/src/Worker.d.ts +3 -1
- package/dist/src/Worker.d.ts.map +1 -1
- package/dist/src/Worker.js +94 -1
- 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 +2 -2
- package/dist/src/local-first/Relay.d.ts.map +1 -1
- package/dist/src/local-first/Relay.js +12 -11
- package/package.json +2 -2
- package/src/Sqlite.ts +28 -9
- package/src/Task.ts +5 -5
- package/src/Worker.ts +42 -0
- package/src/index.ts +1 -0
- package/src/local-first/Relay.ts +13 -12
package/dist/src/Sqlite.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Sqlite.d.ts","sourceRoot":"","sources":["../../src/Sqlite.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,kBAAkB,
|
|
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"}
|
package/dist/src/Sqlite.js
CHANGED
|
@@ -1,38 +1,128 @@
|
|
|
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
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
|
|
45
|
+
if (env.hasError) throw env.error;
|
|
46
|
+
}
|
|
47
|
+
return next();
|
|
48
|
+
};
|
|
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
|
+
});
|
|
1
53
|
import { createPreparedStatementsCache, lazyVoid, ok, } from "@evolu/common";
|
|
2
54
|
import BetterSQLite, {} from "better-sqlite3";
|
|
55
|
+
import { rmSync } from "fs";
|
|
3
56
|
export const createBetterSqliteDriver = (name, options) => () => {
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
+
lazyVoid));
|
|
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
|
};
|
package/dist/src/Task.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
|
-
import { type CreateRun, type
|
|
6
|
+
import { type CreateRun, type RunDefaultDeps } from "@evolu/common";
|
|
7
7
|
/**
|
|
8
8
|
* A promise that resolves when a termination signal is received.
|
|
9
9
|
*
|
|
@@ -32,14 +32,14 @@ export interface ShutdownDep {
|
|
|
32
32
|
* const deps = { ...createRelayDeps(), console };
|
|
33
33
|
*
|
|
34
34
|
* await using run = createRun(deps);
|
|
35
|
-
* await using
|
|
35
|
+
* await using disposer = new AsyncDisposableStack();
|
|
36
36
|
*
|
|
37
|
-
*
|
|
37
|
+
* disposer.use(await run.orThrow(startRelay({ port: 4000 })));
|
|
38
38
|
*
|
|
39
39
|
* await run.deps.shutdown;
|
|
40
40
|
* ```
|
|
41
41
|
*
|
|
42
42
|
* @group Node.js Run
|
|
43
43
|
*/
|
|
44
|
-
export declare const createRun: CreateRun<
|
|
44
|
+
export declare const createRun: CreateRun<RunDefaultDeps & ShutdownDep>;
|
|
45
45
|
//# 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,EAGL,KAAK,SAAS,EAEd,KAAK,
|
|
1
|
+
{"version":3,"file":"Task.d.ts","sourceRoot":"","sources":["../../src/Task.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAGL,KAAK,SAAS,EAEd,KAAK,cAAc,EACpB,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,cAAc,GAAG,WAAW,CAuC7D,CAAC"}
|
package/dist/src/Task.js
CHANGED
|
@@ -19,9 +19,9 @@ import { createRun as createCommonRun, createUnknownError, } from "@evolu/common
|
|
|
19
19
|
* const deps = { ...createRelayDeps(), console };
|
|
20
20
|
*
|
|
21
21
|
* await using run = createRun(deps);
|
|
22
|
-
* await using
|
|
22
|
+
* await using disposer = new AsyncDisposableStack();
|
|
23
23
|
*
|
|
24
|
-
*
|
|
24
|
+
* disposer.use(await run.orThrow(startRelay({ port: 4000 })));
|
|
25
25
|
*
|
|
26
26
|
* await run.deps.shutdown;
|
|
27
27
|
* ```
|
package/dist/src/Worker.d.ts
CHANGED
package/dist/src/Worker.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Worker.d.ts","sourceRoot":"","sources":["../../src/Worker.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"Worker.d.ts","sourceRoot":"","sources":["../../src/Worker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAoB,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAG9E,0EAA0E;AAC1E,eAAO,MAAM,sBAAsB,EAAE,sBAoCpC,CAAC"}
|
package/dist/src/Worker.js
CHANGED
|
@@ -1,4 +1,97 @@
|
|
|
1
|
-
|
|
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
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
|
|
45
|
+
if (env.hasError) throw env.error;
|
|
46
|
+
}
|
|
47
|
+
return next();
|
|
48
|
+
};
|
|
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 { assertNotDisposed } from "@evolu/common";
|
|
54
|
+
/** Creates a {@link BroadcastChannel} from a Node.js BroadcastChannel. */
|
|
55
|
+
export const createBroadcastChannel = (name) => {
|
|
56
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
57
|
+
try {
|
|
58
|
+
const nativeBroadcastChannel = new globalThis.BroadcastChannel(name);
|
|
59
|
+
const disposer = __addDisposableResource(env_1, new DisposableStack(), false);
|
|
60
|
+
disposer.defer(() => {
|
|
61
|
+
nativeBroadcastChannel.onmessage = null;
|
|
62
|
+
nativeBroadcastChannel.close();
|
|
63
|
+
});
|
|
64
|
+
const disposables = disposer.move();
|
|
65
|
+
let onMessageHandler = null;
|
|
66
|
+
return {
|
|
67
|
+
postMessage: (message) => {
|
|
68
|
+
assertNotDisposed(disposables);
|
|
69
|
+
nativeBroadcastChannel.postMessage(message);
|
|
70
|
+
},
|
|
71
|
+
get onMessage() {
|
|
72
|
+
return disposables.disposed ? null : onMessageHandler;
|
|
73
|
+
},
|
|
74
|
+
set onMessage(fn) {
|
|
75
|
+
if (disposables.disposed)
|
|
76
|
+
return;
|
|
77
|
+
onMessageHandler = fn;
|
|
78
|
+
nativeBroadcastChannel.onmessage = fn
|
|
79
|
+
? (event) => {
|
|
80
|
+
fn(event.data);
|
|
81
|
+
}
|
|
82
|
+
: null;
|
|
83
|
+
},
|
|
84
|
+
[Symbol.dispose]: () => disposables.dispose(),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
catch (e_1) {
|
|
88
|
+
env_1.error = e_1;
|
|
89
|
+
env_1.hasError = true;
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
__disposeResources(env_1);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
2
95
|
// TODO: Implement Node.js Worker API
|
|
3
96
|
//
|
|
4
97
|
// This module should provide Node.js implementations of the common Worker API:
|
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,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,wBAAwB,CAAC;AACvC,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,wBAAwB,CAAC;AACvC,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC"}
|
package/dist/src/index.js
CHANGED
|
@@ -30,9 +30,9 @@ export declare const createRelayDeps: () => RelayDeps;
|
|
|
30
30
|
* const deps = { ...createRelayDeps(), console };
|
|
31
31
|
*
|
|
32
32
|
* await using run = createRun(deps);
|
|
33
|
-
* await using
|
|
33
|
+
* await using disposer = new AsyncDisposableStack();
|
|
34
34
|
*
|
|
35
|
-
*
|
|
35
|
+
* disposer.use(
|
|
36
36
|
* await run.orThrow(
|
|
37
37
|
* startRelay({
|
|
38
38
|
* port: 4000,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Relay.d.ts","sourceRoot":"","sources":["../../../src/local-first/Relay.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,qBAAqB,EAI1B,KAAK,SAAS,EACd,KAAK,IAAI,EACT,KAAK,kBAAkB,EAExB,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,gEAAgE;AAChE,eAAO,MAAM,eAAe,QAAO,SAIjC,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,eAAO,MAAM,UAAU,GACpB,qDAKE,iBAAiB,KAAG,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"Relay.d.ts","sourceRoot":"","sources":["../../../src/local-first/Relay.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,qBAAqB,EAI1B,KAAK,SAAS,EACd,KAAK,IAAI,EACT,KAAK,kBAAkB,EAExB,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,gEAAgE;AAChE,eAAO,MAAM,eAAe,QAAO,SAIjC,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,eAAO,MAAM,UAAU,GACpB,qDAKE,iBAAiB,KAAG,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,CA8MlD,CAAC"}
|
|
@@ -87,9 +87,9 @@ export const createRelayDeps = () => ({
|
|
|
87
87
|
* const deps = { ...createRelayDeps(), console };
|
|
88
88
|
*
|
|
89
89
|
* await using run = createRun(deps);
|
|
90
|
-
* await using
|
|
90
|
+
* await using disposer = new AsyncDisposableStack();
|
|
91
91
|
*
|
|
92
|
-
*
|
|
92
|
+
* disposer.use(
|
|
93
93
|
* await run.orThrow(
|
|
94
94
|
* startRelay({
|
|
95
95
|
* port: 4000,
|
|
@@ -111,23 +111,23 @@ export const createRelayDeps = () => ({
|
|
|
111
111
|
export const startRelay = ({ port = 443, name = Name.orThrow("evolu-relay"), isOwnerAllowed, isOwnerWithinQuota, }) => async (run) => {
|
|
112
112
|
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
113
113
|
try {
|
|
114
|
-
const
|
|
114
|
+
const disposer = __addDisposableResource(env_1, new AsyncDisposableStack(), true);
|
|
115
115
|
const console = run.deps.console.child("relay");
|
|
116
|
-
|
|
116
|
+
disposer.defer(() => {
|
|
117
117
|
console.info("Shutdown complete");
|
|
118
118
|
});
|
|
119
119
|
const dbFileExists = existsSync(`${name}.db`);
|
|
120
|
-
const sqlite =
|
|
120
|
+
const sqlite = disposer.use(await run.orThrow(createSqlite(name)));
|
|
121
121
|
const deps = { ...run.deps, sqlite };
|
|
122
122
|
if (!dbFileExists) {
|
|
123
123
|
createBaseSqliteStorageTables(deps);
|
|
124
124
|
createRelayStorageTables(deps);
|
|
125
125
|
}
|
|
126
|
-
const server =
|
|
126
|
+
const server = disposer.use(createServer());
|
|
127
127
|
server.once("close", () => {
|
|
128
128
|
console.info("HTTP server closed");
|
|
129
129
|
});
|
|
130
|
-
const wss =
|
|
130
|
+
const wss = disposer.adopt(new WebSocketServer({
|
|
131
131
|
maxPayload: defaultProtocolMessageMaxSize,
|
|
132
132
|
noServer: true,
|
|
133
133
|
}), (wss) => new Promise((resolve) => {
|
|
@@ -137,7 +137,8 @@ export const startRelay = ({ port = 443, name = Name.orThrow("evolu-relay"), isO
|
|
|
137
137
|
});
|
|
138
138
|
}));
|
|
139
139
|
const ownerSocketRelation = createRelation();
|
|
140
|
-
const relayRun =
|
|
140
|
+
const relayRun = disposer.use(run.create({
|
|
141
|
+
...run.deps,
|
|
141
142
|
storage: createRelaySqliteStorage(deps)({
|
|
142
143
|
isOwnerWithinQuota,
|
|
143
144
|
}),
|
|
@@ -234,7 +235,7 @@ export const startRelay = ({ port = 443, name = Name.orThrow("evolu-relay"), isO
|
|
|
234
235
|
console.debug("ws close", wss.clients.size);
|
|
235
236
|
});
|
|
236
237
|
});
|
|
237
|
-
|
|
238
|
+
disposer.defer(() => {
|
|
238
239
|
console.info("Shutting down...");
|
|
239
240
|
for (const client of wss.clients) {
|
|
240
241
|
if (client.readyState === WebSocket.OPEN) {
|
|
@@ -246,11 +247,11 @@ export const startRelay = ({ port = 443, name = Name.orThrow("evolu-relay"), isO
|
|
|
246
247
|
await once(server, "listening");
|
|
247
248
|
const address = server.address();
|
|
248
249
|
assert(address && typeof address !== "string", "Expected TCP address");
|
|
249
|
-
const
|
|
250
|
+
const disposables = disposer.move();
|
|
250
251
|
console.info(`Started on port ${address.port}`);
|
|
251
252
|
return ok({
|
|
252
253
|
port: address.port,
|
|
253
|
-
[Symbol.asyncDispose]: () =>
|
|
254
|
+
[Symbol.asyncDispose]: () => disposables.disposeAsync(),
|
|
254
255
|
});
|
|
255
256
|
}
|
|
256
257
|
catch (e_1) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evolu/nodejs",
|
|
3
|
-
"version": "3.0.0-next.
|
|
3
|
+
"version": "3.0.0-next.3",
|
|
4
4
|
"description": "Evolu for Node.js",
|
|
5
5
|
"author": "Daniel Steigerwald <daniel@steigerwald.cz>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"@types/node": "^24.10.9",
|
|
36
36
|
"@types/ws": "^8.18.1",
|
|
37
37
|
"typescript": "^6",
|
|
38
|
-
"@evolu/common": "8.0.0-next.
|
|
38
|
+
"@evolu/common": "8.0.0-next.5",
|
|
39
39
|
"@evolu/typescript-config": "0.0.2"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
package/src/Sqlite.ts
CHANGED
|
@@ -3,20 +3,29 @@ import {
|
|
|
3
3
|
lazyVoid,
|
|
4
4
|
ok,
|
|
5
5
|
type CreateSqliteDriver,
|
|
6
|
-
type SqliteDriver,
|
|
7
6
|
type SqliteRow,
|
|
8
7
|
} from "@evolu/common";
|
|
9
8
|
import BetterSQLite, { type Statement } from "better-sqlite3";
|
|
9
|
+
import { rmSync } from "fs";
|
|
10
10
|
|
|
11
11
|
export const createBetterSqliteDriver: CreateSqliteDriver =
|
|
12
12
|
(name, options) => () => {
|
|
13
13
|
const filename = options?.mode === "memory" ? ":memory:" : `${name}.db`;
|
|
14
|
-
const
|
|
15
|
-
|
|
14
|
+
const filenamesToDelete =
|
|
15
|
+
options?.mode === "memory"
|
|
16
|
+
? []
|
|
17
|
+
: [
|
|
18
|
+
filename,
|
|
19
|
+
`${filename}-shm`,
|
|
20
|
+
`${filename}-wal`,
|
|
21
|
+
`${filename}-journal`,
|
|
22
|
+
];
|
|
23
|
+
using disposer = new DisposableStack();
|
|
24
|
+
const db = disposer.adopt(new BetterSQLite(filename), (db) => {
|
|
16
25
|
db.close();
|
|
17
26
|
});
|
|
18
27
|
|
|
19
|
-
const cache =
|
|
28
|
+
const cache = disposer.use(
|
|
20
29
|
createPreparedStatementsCache<Statement>(
|
|
21
30
|
(sql) => db.prepare(sql),
|
|
22
31
|
// Not needed.
|
|
@@ -25,7 +34,9 @@ export const createBetterSqliteDriver: CreateSqliteDriver =
|
|
|
25
34
|
),
|
|
26
35
|
);
|
|
27
36
|
|
|
28
|
-
const
|
|
37
|
+
const disposables = disposer.move();
|
|
38
|
+
|
|
39
|
+
return ok({
|
|
29
40
|
exec: (query) => {
|
|
30
41
|
// Always prepare is recommended for better-sqlite3
|
|
31
42
|
const prepared = cache.get(query, true);
|
|
@@ -51,10 +62,18 @@ export const createBetterSqliteDriver: CreateSqliteDriver =
|
|
|
51
62
|
return new Uint8Array(file);
|
|
52
63
|
},
|
|
53
64
|
|
|
54
|
-
|
|
55
|
-
|
|
65
|
+
deleteDatabase: () => {
|
|
66
|
+
using deleteDisposer = new DisposableStack();
|
|
67
|
+
for (const filename of filenamesToDelete) {
|
|
68
|
+
deleteDisposer.defer(() => {
|
|
69
|
+
rmSync(filename, { force: true });
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
deleteDisposer.use(disposables);
|
|
56
73
|
},
|
|
57
|
-
};
|
|
58
74
|
|
|
59
|
-
|
|
75
|
+
[Symbol.dispose]: () => {
|
|
76
|
+
disposables.dispose();
|
|
77
|
+
},
|
|
78
|
+
});
|
|
60
79
|
};
|
package/src/Task.ts
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
createUnknownError,
|
|
10
10
|
type CreateRun,
|
|
11
11
|
type Run,
|
|
12
|
-
type
|
|
12
|
+
type RunDefaultDeps,
|
|
13
13
|
} from "@evolu/common";
|
|
14
14
|
|
|
15
15
|
/**
|
|
@@ -42,18 +42,18 @@ export interface ShutdownDep {
|
|
|
42
42
|
* const deps = { ...createRelayDeps(), console };
|
|
43
43
|
*
|
|
44
44
|
* await using run = createRun(deps);
|
|
45
|
-
* await using
|
|
45
|
+
* await using disposer = new AsyncDisposableStack();
|
|
46
46
|
*
|
|
47
|
-
*
|
|
47
|
+
* disposer.use(await run.orThrow(startRelay({ port: 4000 })));
|
|
48
48
|
*
|
|
49
49
|
* await run.deps.shutdown;
|
|
50
50
|
* ```
|
|
51
51
|
*
|
|
52
52
|
* @group Node.js Run
|
|
53
53
|
*/
|
|
54
|
-
export const createRun: CreateRun<
|
|
54
|
+
export const createRun: CreateRun<RunDefaultDeps & ShutdownDep> = <D>(
|
|
55
55
|
deps?: D,
|
|
56
|
-
): Run<
|
|
56
|
+
): Run<RunDefaultDeps & ShutdownDep & D> => {
|
|
57
57
|
const { promise: shutdown, resolve: resolveShutdown } =
|
|
58
58
|
Promise.withResolvers<void>();
|
|
59
59
|
|
package/src/Worker.ts
CHANGED
|
@@ -1,3 +1,45 @@
|
|
|
1
|
+
import type { BroadcastChannel, CreateBroadcastChannel } from "@evolu/common";
|
|
2
|
+
import { assertNotDisposed } from "@evolu/common";
|
|
3
|
+
|
|
4
|
+
/** Creates a {@link BroadcastChannel} from a Node.js BroadcastChannel. */
|
|
5
|
+
export const createBroadcastChannel: CreateBroadcastChannel = <
|
|
6
|
+
Input,
|
|
7
|
+
Output = Input,
|
|
8
|
+
>(
|
|
9
|
+
name: string,
|
|
10
|
+
): BroadcastChannel<Input, Output> => {
|
|
11
|
+
const nativeBroadcastChannel = new globalThis.BroadcastChannel(name);
|
|
12
|
+
using disposer = new DisposableStack();
|
|
13
|
+
|
|
14
|
+
disposer.defer(() => {
|
|
15
|
+
nativeBroadcastChannel.onmessage = null;
|
|
16
|
+
nativeBroadcastChannel.close();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const disposables = disposer.move();
|
|
20
|
+
let onMessageHandler: ((message: Output) => void) | null = null;
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
postMessage: (message) => {
|
|
24
|
+
assertNotDisposed(disposables);
|
|
25
|
+
nativeBroadcastChannel.postMessage(message);
|
|
26
|
+
},
|
|
27
|
+
get onMessage() {
|
|
28
|
+
return disposables.disposed ? null : onMessageHandler;
|
|
29
|
+
},
|
|
30
|
+
set onMessage(fn) {
|
|
31
|
+
if (disposables.disposed) return;
|
|
32
|
+
onMessageHandler = fn;
|
|
33
|
+
nativeBroadcastChannel.onmessage = fn
|
|
34
|
+
? (event: MessageEvent<Output>) => {
|
|
35
|
+
fn(event.data);
|
|
36
|
+
}
|
|
37
|
+
: null;
|
|
38
|
+
},
|
|
39
|
+
[Symbol.dispose]: () => disposables.dispose(),
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
|
|
1
43
|
// TODO: Implement Node.js Worker API
|
|
2
44
|
//
|
|
3
45
|
// This module should provide Node.js implementations of the common Worker API:
|
package/src/index.ts
CHANGED
package/src/local-first/Relay.ts
CHANGED
|
@@ -69,9 +69,9 @@ export const createRelayDeps = (): RelayDeps => ({
|
|
|
69
69
|
* const deps = { ...createRelayDeps(), console };
|
|
70
70
|
*
|
|
71
71
|
* await using run = createRun(deps);
|
|
72
|
-
* await using
|
|
72
|
+
* await using disposer = new AsyncDisposableStack();
|
|
73
73
|
*
|
|
74
|
-
*
|
|
74
|
+
* disposer.use(
|
|
75
75
|
* await run.orThrow(
|
|
76
76
|
* startRelay({
|
|
77
77
|
* port: 4000,
|
|
@@ -98,15 +98,15 @@ export const startRelay =
|
|
|
98
98
|
isOwnerWithinQuota,
|
|
99
99
|
}: NodeJsRelayConfig): Task<Relay, never, RelayDeps> =>
|
|
100
100
|
async (run) => {
|
|
101
|
-
await using
|
|
101
|
+
await using disposer = new AsyncDisposableStack();
|
|
102
102
|
const console = run.deps.console.child("relay");
|
|
103
103
|
|
|
104
|
-
|
|
104
|
+
disposer.defer(() => {
|
|
105
105
|
console.info("Shutdown complete");
|
|
106
106
|
});
|
|
107
107
|
|
|
108
108
|
const dbFileExists = existsSync(`${name}.db`);
|
|
109
|
-
const sqlite =
|
|
109
|
+
const sqlite = disposer.use(await run.orThrow(createSqlite(name)));
|
|
110
110
|
const deps = { ...run.deps, sqlite };
|
|
111
111
|
|
|
112
112
|
if (!dbFileExists) {
|
|
@@ -114,12 +114,12 @@ export const startRelay =
|
|
|
114
114
|
createRelayStorageTables(deps);
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
const server =
|
|
117
|
+
const server = disposer.use(createServer());
|
|
118
118
|
server.once("close", () => {
|
|
119
119
|
console.info("HTTP server closed");
|
|
120
120
|
});
|
|
121
121
|
|
|
122
|
-
const wss =
|
|
122
|
+
const wss = disposer.adopt(
|
|
123
123
|
new WebSocketServer({
|
|
124
124
|
maxPayload: defaultProtocolMessageMaxSize,
|
|
125
125
|
noServer: true,
|
|
@@ -135,8 +135,9 @@ export const startRelay =
|
|
|
135
135
|
|
|
136
136
|
const ownerSocketRelation = createRelation<OwnerId, WebSocket>();
|
|
137
137
|
|
|
138
|
-
const relayRun =
|
|
139
|
-
run.create(
|
|
138
|
+
const relayRun = disposer.use(
|
|
139
|
+
run.create({
|
|
140
|
+
...run.deps,
|
|
140
141
|
storage: createRelaySqliteStorage(deps)({
|
|
141
142
|
isOwnerWithinQuota,
|
|
142
143
|
}),
|
|
@@ -278,7 +279,7 @@ export const startRelay =
|
|
|
278
279
|
});
|
|
279
280
|
});
|
|
280
281
|
|
|
281
|
-
|
|
282
|
+
disposer.defer(() => {
|
|
282
283
|
console.info("Shutting down...");
|
|
283
284
|
for (const client of wss.clients) {
|
|
284
285
|
if (client.readyState === WebSocket.OPEN) {
|
|
@@ -293,13 +294,13 @@ export const startRelay =
|
|
|
293
294
|
const address = server.address();
|
|
294
295
|
assert(address && typeof address !== "string", "Expected TCP address");
|
|
295
296
|
|
|
296
|
-
const
|
|
297
|
+
const disposables = disposer.move();
|
|
297
298
|
|
|
298
299
|
console.info(`Started on port ${address.port}`);
|
|
299
300
|
|
|
300
301
|
return ok({
|
|
301
302
|
port: address.port,
|
|
302
|
-
[Symbol.asyncDispose]: () =>
|
|
303
|
+
[Symbol.asyncDispose]: () => disposables.disposeAsync(),
|
|
303
304
|
});
|
|
304
305
|
};
|
|
305
306
|
|