@scrypted/server 0.1.7 → 0.1.8
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.
Potentially problematic release.
This version of @scrypted/server might be problematic. Click here for more details.
- package/dist/listen-zero.js +38 -0
- package/dist/listen-zero.js.map +1 -0
- package/dist/plugin/media.js +1 -1
- package/dist/plugin/media.js.map +1 -1
- package/dist/plugin/plugin-console.js +1 -1
- package/dist/plugin/plugin-console.js.map +1 -1
- package/dist/plugin/plugin-host.js +33 -45
- package/dist/plugin/plugin-host.js.map +1 -1
- package/dist/plugin/plugin-http.js +1 -1
- package/dist/plugin/plugin-http.js.map +1 -1
- package/dist/plugin/plugin-npm-dependencies.js +3 -1
- package/dist/plugin/plugin-npm-dependencies.js.map +1 -1
- package/dist/plugin/plugin-repl.js +1 -1
- package/dist/plugin/plugin-repl.js.map +1 -1
- package/dist/plugin/runtime/node-fork-worker.js +8 -1
- package/dist/plugin/runtime/node-fork-worker.js.map +1 -1
- package/dist/rpc-serializer.js +121 -0
- package/dist/rpc-serializer.js.map +1 -0
- package/dist/runtime.js +0 -36
- package/dist/runtime.js.map +1 -1
- package/dist/scrypted-server-main.js +0 -2
- package/dist/scrypted-server-main.js.map +1 -1
- package/dist/services/plugin.js +4 -10
- package/dist/services/plugin.js.map +1 -1
- package/dist/threading.js +6 -28
- package/dist/threading.js.map +1 -1
- package/package.json +1 -1
- package/src/listen-zero.ts +34 -0
- package/src/plugin/media.ts +1 -1
- package/src/plugin/plugin-console.ts +1 -1
- package/src/plugin/plugin-host.ts +42 -43
- package/src/plugin/plugin-http.ts +2 -2
- package/src/plugin/plugin-npm-dependencies.ts +3 -1
- package/src/plugin/plugin-repl.ts +1 -1
- package/src/plugin/runtime/node-fork-worker.ts +8 -1
- package/src/plugin/runtime/runtime-worker.ts +2 -1
- package/src/rpc-serializer.ts +141 -0
- package/src/runtime.ts +0 -40
- package/src/scrypted-server-main.ts +0 -2
- package/src/services/plugin.ts +4 -10
- package/src/threading.ts +8 -35
- package/test/rpc-duplex-test.ts +30 -0
- package/test/threading-test.ts +29 -0
- package/dist/plugin/listen-zero.js +0 -23
- package/dist/plugin/listen-zero.js.map +0 -1
- package/src/plugin/listen-zero.ts +0 -21
@@ -5,10 +5,12 @@ import path from 'path';
|
|
5
5
|
import { once } from 'events';
|
6
6
|
import process from 'process';
|
7
7
|
import mkdirp from "mkdirp";
|
8
|
+
import semver from 'semver';
|
8
9
|
|
9
10
|
export function getPluginNodePath(name: string) {
|
10
11
|
const pluginVolume = ensurePluginVolume(name);
|
11
|
-
const
|
12
|
+
const nodeMajorVersion = semver.parse(process.version).major;
|
13
|
+
const nodePrefix = path.join(pluginVolume, `${process.platform}-${process.arch}-${nodeMajorVersion}`);
|
12
14
|
return nodePrefix;
|
13
15
|
}
|
14
16
|
|
@@ -30,7 +30,14 @@ export class NodeForkWorker extends ChildProcessWorker {
|
|
30
30
|
}
|
31
31
|
|
32
32
|
setupRpcPeer(peer: RpcPeer): void {
|
33
|
-
this.worker.on('message', message =>
|
33
|
+
this.worker.on('message', (message, sendHandle) => {
|
34
|
+
if (sendHandle) {
|
35
|
+
this.emit('rpc', message, sendHandle);
|
36
|
+
}
|
37
|
+
else {
|
38
|
+
peer.handleMessage(message as any);
|
39
|
+
}
|
40
|
+
});
|
34
41
|
peer.transportSafeArgumentTypes.add(Buffer.name);
|
35
42
|
}
|
36
43
|
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import { RpcMessage, RpcPeer } from "../../rpc";
|
2
2
|
import { PluginDebug } from "../plugin-debug";
|
3
3
|
import {Readable} from "stream";
|
4
|
+
import net from "net";
|
4
5
|
|
5
6
|
export interface RuntimeWorkerOptions {
|
6
7
|
pluginDebug: PluginDebug;
|
@@ -15,6 +16,7 @@ export interface RuntimeWorker {
|
|
15
16
|
|
16
17
|
kill(): void;
|
17
18
|
|
19
|
+
on(event: 'rpc', listener: (message: any, sendHandle: net.Socket) => void): this;
|
18
20
|
on(event: 'error', listener: (err: Error) => void): this;
|
19
21
|
on(event: 'close', listener: (code: number | null, signal: NodeJS.Signals | null) => void): this;
|
20
22
|
on(event: 'disconnect', listener: () => void): this;
|
@@ -25,4 +27,3 @@ export interface RuntimeWorker {
|
|
25
27
|
|
26
28
|
setupRpcPeer(peer: RpcPeer): void;
|
27
29
|
}
|
28
|
-
|
@@ -0,0 +1,141 @@
|
|
1
|
+
import type { Readable, Writable } from "stream";
|
2
|
+
import { SidebandBufferSerializer } from "./plugin/buffer-serializer";
|
3
|
+
import { RpcPeer } from "./rpc";
|
4
|
+
|
5
|
+
export function createDuplexRpcPeer(selfName: string, peerName: string, readable: Readable, writable: Writable) {
|
6
|
+
const serializer = createRpcDuplexSerializer(readable, writable);
|
7
|
+
|
8
|
+
const rpcPeer = new RpcPeer(selfName, peerName, (message, reject, serializationContext) => {
|
9
|
+
try {
|
10
|
+
serializer.sendMessage(message, reject, serializationContext);
|
11
|
+
}
|
12
|
+
catch (e) {
|
13
|
+
reject?.(e);
|
14
|
+
readable.destroy();
|
15
|
+
}
|
16
|
+
});
|
17
|
+
|
18
|
+
serializer.setupRpcPeer(rpcPeer);
|
19
|
+
readable.on('close', serializer.onDisconnected);
|
20
|
+
readable.on('error', serializer.onDisconnected);
|
21
|
+
return rpcPeer;
|
22
|
+
}
|
23
|
+
|
24
|
+
export function createRpcSerializer(options: {
|
25
|
+
sendMessageBuffer: (buffer: Buffer) => void,
|
26
|
+
sendMessageFinish: (message: any) => void,
|
27
|
+
}) {
|
28
|
+
let rpcPeer: RpcPeer;
|
29
|
+
|
30
|
+
const { sendMessageBuffer, sendMessageFinish } = options;
|
31
|
+
let connected = true;
|
32
|
+
const onDisconnected = () => {
|
33
|
+
connected = false;
|
34
|
+
rpcPeer.kill('connection closed.');
|
35
|
+
}
|
36
|
+
|
37
|
+
const sendMessage = (message: any, reject: (e: Error) => void, serializationContext: any, ) => {
|
38
|
+
if (!connected) {
|
39
|
+
reject?.(new Error('peer disconnected'));
|
40
|
+
return;
|
41
|
+
}
|
42
|
+
|
43
|
+
const buffers = serializationContext?.buffers;
|
44
|
+
if (buffers) {
|
45
|
+
for (const buffer of buffers) {
|
46
|
+
sendMessageBuffer(buffer);
|
47
|
+
}
|
48
|
+
}
|
49
|
+
sendMessageFinish(message);
|
50
|
+
}
|
51
|
+
|
52
|
+
let pendingSerializationContext: any = {};
|
53
|
+
const setupRpcPeer = (peer: RpcPeer) => {
|
54
|
+
rpcPeer = peer;
|
55
|
+
rpcPeer.addSerializer(Buffer, 'Buffer', new SidebandBufferSerializer());
|
56
|
+
}
|
57
|
+
|
58
|
+
const onMessageBuffer = (buffer: Buffer) => {
|
59
|
+
pendingSerializationContext = pendingSerializationContext || {
|
60
|
+
buffers: [],
|
61
|
+
};
|
62
|
+
const buffers: Buffer[] = pendingSerializationContext.buffers;
|
63
|
+
buffers.push(buffer);
|
64
|
+
};
|
65
|
+
|
66
|
+
const onMessageFinish = (message: any) => {
|
67
|
+
const messageSerializationContext = pendingSerializationContext;
|
68
|
+
pendingSerializationContext = undefined;
|
69
|
+
rpcPeer.handleMessage(message, messageSerializationContext);
|
70
|
+
}
|
71
|
+
|
72
|
+
return {
|
73
|
+
sendMessage,
|
74
|
+
setupRpcPeer,
|
75
|
+
onMessageBuffer,
|
76
|
+
onMessageFinish,
|
77
|
+
onDisconnected,
|
78
|
+
};
|
79
|
+
}
|
80
|
+
|
81
|
+
export function createRpcDuplexSerializer(readable: Readable, writable: Writable) {
|
82
|
+
const socketSend = (type: number, data: Buffer) => {
|
83
|
+
const header = Buffer.alloc(5);
|
84
|
+
header.writeUInt32BE(data.length + 1, 0);
|
85
|
+
header.writeUInt8(type, 4);
|
86
|
+
|
87
|
+
writable.write(Buffer.concat([header, data]));
|
88
|
+
}
|
89
|
+
|
90
|
+
const createSocketSend = (type: number) => {
|
91
|
+
return (data: Buffer) => {
|
92
|
+
return socketSend(type, data);
|
93
|
+
}
|
94
|
+
}
|
95
|
+
|
96
|
+
const sendMessageBuffer = createSocketSend(1);
|
97
|
+
const sendMessageFinish = createSocketSend(0);
|
98
|
+
|
99
|
+
const serializer = createRpcSerializer({
|
100
|
+
sendMessageBuffer,
|
101
|
+
sendMessageFinish: (message) => sendMessageFinish(Buffer.from(JSON.stringify(message))),
|
102
|
+
});
|
103
|
+
|
104
|
+
let header: Buffer;
|
105
|
+
const readMessages = () => {
|
106
|
+
while (true) {
|
107
|
+
if (!header) {
|
108
|
+
header = readable.read(5);
|
109
|
+
if (!header)
|
110
|
+
return;
|
111
|
+
}
|
112
|
+
|
113
|
+
const length = header.readUInt32BE(0);
|
114
|
+
const type = header.readUInt8(4);
|
115
|
+
const payload: Buffer = readable.read(length - 1);
|
116
|
+
if (!payload)
|
117
|
+
return;
|
118
|
+
|
119
|
+
header = undefined;
|
120
|
+
|
121
|
+
const data = payload;
|
122
|
+
|
123
|
+
if (type === 0) {
|
124
|
+
const message = JSON.parse(data.toString());
|
125
|
+
serializer.onMessageFinish(message);
|
126
|
+
}
|
127
|
+
else {
|
128
|
+
serializer.onMessageBuffer(data);
|
129
|
+
}
|
130
|
+
}
|
131
|
+
}
|
132
|
+
|
133
|
+
readable.on('readable', readMessages);
|
134
|
+
readMessages();
|
135
|
+
|
136
|
+
return {
|
137
|
+
setupRpcPeer: serializer.setupRpcPeer,
|
138
|
+
sendMessage: serializer.sendMessage,
|
139
|
+
onDisconnected: serializer.onDisconnected,
|
140
|
+
};
|
141
|
+
}
|
package/src/runtime.ts
CHANGED
@@ -78,46 +78,6 @@ export class ScryptedRuntime extends PluginHttp<HttpPluginData> {
|
|
78
78
|
|
79
79
|
app.disable('x-powered-by');
|
80
80
|
|
81
|
-
this.app.options(['/endpoint/@:owner/:pkg/engine.io/api/activate', '/endpoint/@:owner/:pkg/engine.io/api/activate'], (req, res) => {
|
82
|
-
this.addAccessControlHeaders(req, res);
|
83
|
-
res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
|
84
|
-
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With');
|
85
|
-
res.send(200);
|
86
|
-
});
|
87
|
-
|
88
|
-
this.app.post(['/endpoint/@:owner/:pkg/engine.io/api/activate', '/endpoint/@:owner/:pkg/engine.io/api/activate'], (req, res) => {
|
89
|
-
const { username } = (req as any);
|
90
|
-
if (!username) {
|
91
|
-
res.status(401);
|
92
|
-
res.send('Not Authorized');
|
93
|
-
return;
|
94
|
-
}
|
95
|
-
|
96
|
-
const { owner, pkg } = req.params;
|
97
|
-
let endpoint = pkg;
|
98
|
-
if (owner)
|
99
|
-
endpoint = `@${owner}/${endpoint}`;
|
100
|
-
|
101
|
-
const { id } = req.body;
|
102
|
-
try {
|
103
|
-
const host = this.plugins?.[endpoint];
|
104
|
-
if (!host)
|
105
|
-
throw new Error('invalid plugin');
|
106
|
-
// @ts-expect-error
|
107
|
-
const socket: IOServerSocket = host.io.clients[id];
|
108
|
-
if (!socket)
|
109
|
-
throw new Error('invalid socket');
|
110
|
-
socket.emit('/api/activate');
|
111
|
-
res.send({
|
112
|
-
id,
|
113
|
-
})
|
114
|
-
}
|
115
|
-
catch (e) {
|
116
|
-
res.status(500);
|
117
|
-
res.end();
|
118
|
-
}
|
119
|
-
});
|
120
|
-
|
121
81
|
this.addMiddleware();
|
122
82
|
|
123
83
|
app.get('/web/oauth/callback', (req, res) => {
|
@@ -411,7 +411,6 @@ async function start() {
|
|
411
411
|
secure: req.secure,
|
412
412
|
signed: true,
|
413
413
|
httpOnly: true,
|
414
|
-
sameSite: !req.secure ? true : 'none',
|
415
414
|
});
|
416
415
|
|
417
416
|
if (change_password) {
|
@@ -453,7 +452,6 @@ async function start() {
|
|
453
452
|
secure: req.secure,
|
454
453
|
signed: true,
|
455
454
|
httpOnly: true,
|
456
|
-
sameSite: !req.secure ? true : 'none',
|
457
455
|
});
|
458
456
|
|
459
457
|
res.send({
|
package/src/services/plugin.ts
CHANGED
@@ -51,26 +51,20 @@ export class PluginComponent {
|
|
51
51
|
this.scrypted.getDevice(id);
|
52
52
|
await this.scrypted.devices[id]?.handler?.ensureProxy();
|
53
53
|
}
|
54
|
-
async getMixins(id: string) {
|
55
|
-
console.warn('legacy use of getMixins, use the mixins property');
|
56
|
-
const pluginDevice = this.scrypted.findPluginDeviceById(id);
|
57
|
-
return getState(pluginDevice, ScryptedInterfaceProperty.mixins) || [];
|
58
|
-
}
|
59
54
|
async getIdForPluginId(pluginId: string) {
|
60
55
|
return this.scrypted.findPluginDevice(pluginId)?._id;
|
61
56
|
}
|
62
57
|
async getIdForNativeId(pluginId: string, nativeId: ScryptedNativeId) {
|
63
58
|
return this.scrypted.findPluginDevice(pluginId, nativeId)?._id;
|
64
59
|
}
|
60
|
+
/**
|
61
|
+
* @deprecated available as device.pluginId now.
|
62
|
+
* Remove at some point after core/ui rolls out 6/20/2022.
|
63
|
+
*/
|
65
64
|
async getPluginId(id: string) {
|
66
65
|
const pluginDevice = this.scrypted.findPluginDeviceById(id);
|
67
66
|
return pluginDevice.pluginId;
|
68
67
|
}
|
69
|
-
async getPluginProcessId(pluginId: string) {
|
70
|
-
if (this.scrypted.plugins[pluginId]?.worker?.killed)
|
71
|
-
return 'killed';
|
72
|
-
return this.scrypted.plugins[pluginId]?.worker?.pid;
|
73
|
-
}
|
74
68
|
async reload(pluginId: string) {
|
75
69
|
const plugin = await this.scrypted.datastore.tryGet(Plugin, pluginId);
|
76
70
|
await this.scrypted.runPlugin(plugin);
|
package/src/threading.ts
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
import worker_threads from 'worker_threads';
|
2
2
|
import { getEvalSource, RpcPeer } from './rpc';
|
3
3
|
import v8 from 'v8';
|
4
|
+
import vm from 'vm';
|
4
5
|
|
5
6
|
export async function newThread<T>(thread: () => Promise<T>): Promise<T>;
|
6
7
|
export async function newThread<V, T>(params: V, thread: (params: V) => Promise<T>): Promise<T>;
|
@@ -28,22 +29,22 @@ export async function newThread<T>(...args: any[]): Promise<T> {
|
|
28
29
|
const g = global as any;
|
29
30
|
g[customRequire] = g.require;
|
30
31
|
}
|
31
|
-
const v8 = global.require('v8');
|
32
|
-
const worker_threads = global.require('worker_threads');
|
33
|
-
const vm = global.require('vm');
|
34
|
-
const mainPeer = new RpcPeer('thread', 'main', (message: any, reject: any) => {
|
32
|
+
const thread_v8: typeof v8 = global.require('v8');
|
33
|
+
const thread_worker_threads: typeof worker_threads = global.require('worker_threads');
|
34
|
+
const thread_vm: typeof vm = global.require('vm');
|
35
|
+
const mainPeer: RpcPeer = new RpcPeer('thread', 'main', (message: any, reject: any) => {
|
35
36
|
try {
|
36
|
-
|
37
|
+
thread_worker_threads.parentPort.postMessage(thread_v8.serialize(message));
|
37
38
|
}
|
38
39
|
catch (e) {
|
39
40
|
reject?.(e);
|
40
41
|
}
|
41
42
|
});
|
42
43
|
mainPeer.transportSafeArgumentTypes.add(Buffer.name);
|
43
|
-
|
44
|
+
thread_worker_threads.parentPort.on('message', (message: any) => mainPeer.handleMessage(thread_v8.deserialize(message)));
|
44
45
|
|
45
46
|
mainPeer.params.eval = async (script: string, moduleNames: string[], paramNames: string[], ...paramValues: any[]) => {
|
46
|
-
const f =
|
47
|
+
const f = thread_vm.compileFunction(`return (${script})`, paramNames, {
|
47
48
|
filename: 'script.js',
|
48
49
|
});
|
49
50
|
const params: any = {};
|
@@ -92,31 +93,3 @@ export async function newThread<T>(...args: any[]): Promise<T> {
|
|
92
93
|
worker.terminate();
|
93
94
|
}
|
94
95
|
}
|
95
|
-
|
96
|
-
async function test() {
|
97
|
-
const foo = 5;
|
98
|
-
const bar = 6;
|
99
|
-
|
100
|
-
console.log(await newThread({
|
101
|
-
foo, bar,
|
102
|
-
}, async () => {
|
103
|
-
return foo + bar;
|
104
|
-
}));
|
105
|
-
|
106
|
-
|
107
|
-
console.log(await newThread({
|
108
|
-
foo, bar,
|
109
|
-
}, async ({foo,bar}) => {
|
110
|
-
return foo + bar;
|
111
|
-
}));
|
112
|
-
|
113
|
-
const sayHelloInMainThread = () => console.log('hello! main thread:', worker_threads.isMainThread);
|
114
|
-
await newThread({
|
115
|
-
sayHelloInMainThread,
|
116
|
-
}, async () => {
|
117
|
-
sayHelloInMainThread();
|
118
|
-
})
|
119
|
-
}
|
120
|
-
|
121
|
-
// if (true)
|
122
|
-
// test();
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import net from 'net';
|
2
|
+
import { listenZeroSingleClient } from "../src/listen-zero";
|
3
|
+
import { createDuplexRpcPeer } from "../src/rpc-serializer";
|
4
|
+
|
5
|
+
async function test() {
|
6
|
+
const { port, clientPromise } = await listenZeroSingleClient();
|
7
|
+
|
8
|
+
|
9
|
+
const n1 = net.connect({
|
10
|
+
port,
|
11
|
+
host: '127.0.0.1',
|
12
|
+
});
|
13
|
+
|
14
|
+
const n2 = await clientPromise;
|
15
|
+
console.log('connected');
|
16
|
+
|
17
|
+
const p1 = createDuplexRpcPeer('p1', 'p2', n1, n1);
|
18
|
+
const p2 = createDuplexRpcPeer('p2', 'p1', n2, n2);
|
19
|
+
|
20
|
+
p1.params.test = () => console.log('p1 test');
|
21
|
+
p2.params.test = () => console.log('p2 test');
|
22
|
+
|
23
|
+
await (await p1.getParam('test'))();
|
24
|
+
await (await p2.getParam('test'))();
|
25
|
+
|
26
|
+
n1.destroy();
|
27
|
+
n2.destroy();
|
28
|
+
}
|
29
|
+
|
30
|
+
test();
|
@@ -0,0 +1,29 @@
|
|
1
|
+
import worker_threads from 'worker_threads';
|
2
|
+
import { newThread } from "../src/threading";
|
3
|
+
|
4
|
+
async function test() {
|
5
|
+
const foo = 5;
|
6
|
+
const bar = 6;
|
7
|
+
|
8
|
+
console.log(await newThread({
|
9
|
+
foo, bar,
|
10
|
+
}, async () => {
|
11
|
+
return foo + bar;
|
12
|
+
}));
|
13
|
+
|
14
|
+
|
15
|
+
console.log(await newThread({
|
16
|
+
foo, bar,
|
17
|
+
}, async ({ foo, bar }) => {
|
18
|
+
return foo + bar;
|
19
|
+
}));
|
20
|
+
|
21
|
+
const sayHelloInMainThread = () => console.log('hello! main thread:', worker_threads.isMainThread);
|
22
|
+
await newThread({
|
23
|
+
sayHelloInMainThread,
|
24
|
+
}, async () => {
|
25
|
+
sayHelloInMainThread();
|
26
|
+
})
|
27
|
+
}
|
28
|
+
|
29
|
+
test();
|
@@ -1,23 +0,0 @@
|
|
1
|
-
"use strict";
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
-
exports.listenZeroExpress = exports.listenZero = void 0;
|
4
|
-
const events_1 = require("events");
|
5
|
-
async function listenZero(server) {
|
6
|
-
server.listen(0);
|
7
|
-
await (0, events_1.once)(server, 'listening');
|
8
|
-
return server.address().port;
|
9
|
-
}
|
10
|
-
exports.listenZero = listenZero;
|
11
|
-
function listenZeroExpress(app) {
|
12
|
-
const server = app.listen(0);
|
13
|
-
return {
|
14
|
-
server,
|
15
|
-
port: (async () => {
|
16
|
-
await (0, events_1.once)(server, 'listening');
|
17
|
-
const { port } = server.address();
|
18
|
-
return port;
|
19
|
-
})()
|
20
|
-
};
|
21
|
-
}
|
22
|
-
exports.listenZeroExpress = listenZeroExpress;
|
23
|
-
//# sourceMappingURL=listen-zero.js.map
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"file":"listen-zero.js","sourceRoot":"","sources":["../../src/plugin/listen-zero.ts"],"names":[],"mappings":";;;AACA,mCAA8B;AAGvB,KAAK,UAAU,UAAU,CAAC,MAAkB;IAC/C,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACjB,MAAM,IAAA,aAAI,EAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAChC,OAAQ,MAAM,CAAC,OAAO,EAAsB,CAAC,IAAI,CAAC;AACtD,CAAC;AAJD,gCAIC;AAED,SAAgB,iBAAiB,CAAC,GAAoB;IAClD,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAC7B,OAAO;QACH,MAAM;QACN,IAAI,EAAE,CAAC,KAAK,IAAI,EAAE;YACd,MAAM,IAAA,aAAI,EAAC,MAAM,EAAE,WAAW,CAAC,CAAC;YAChC,MAAM,EAAE,IAAI,EAAE,GAAI,MAAM,CAAC,OAAO,EAAsB,CAAC;YACvD,OAAO,IAAI,CAAC;QAChB,CAAC,CAAC,EAAE;KACP,CAAA;AACL,CAAC;AAVD,8CAUC"}
|
@@ -1,21 +0,0 @@
|
|
1
|
-
import net from 'net';
|
2
|
-
import { once } from 'events';
|
3
|
-
import express from 'express';
|
4
|
-
|
5
|
-
export async function listenZero(server: net.Server) {
|
6
|
-
server.listen(0);
|
7
|
-
await once(server, 'listening');
|
8
|
-
return (server.address() as net.AddressInfo).port;
|
9
|
-
}
|
10
|
-
|
11
|
-
export function listenZeroExpress(app: express.Express) {
|
12
|
-
const server = app.listen(0);
|
13
|
-
return {
|
14
|
-
server,
|
15
|
-
port: (async () => {
|
16
|
-
await once(server, 'listening');
|
17
|
-
const { port } = (server.address() as net.AddressInfo);
|
18
|
-
return port;
|
19
|
-
})()
|
20
|
-
}
|
21
|
-
}
|