scoundrel-remote-eval 1.0.9 → 1.0.10
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/build/client/connections/web-socket/index.d.ts +2 -2
- package/build/client/connections/web-socket/index.d.ts.map +1 -1
- package/build/client/connections/web-socket/index.js +3 -3
- package/build/client/index.d.ts +7 -1
- package/build/client/index.d.ts.map +1 -1
- package/build/client/index.js +60 -21
- package/build/client/reference-proxy.d.ts.map +1 -1
- package/build/client/reference-proxy.js +3 -7
- package/build/server/index.d.ts +8 -1
- package/build/server/index.d.ts.map +1 -1
- package/build/server/index.js +11 -3
- package/build/utils/single-event-emitter.d.ts +46 -0
- package/build/utils/single-event-emitter.d.ts.map +1 -0
- package/build/utils/single-event-emitter.js +86 -0
- package/package.json +2 -1
|
@@ -22,9 +22,9 @@ export default class WebSocket {
|
|
|
22
22
|
*/
|
|
23
23
|
onSocketMessage: (event: MessageEvent) => void;
|
|
24
24
|
/**
|
|
25
|
-
* @param {Event}
|
|
25
|
+
* @param {Event} event
|
|
26
26
|
*/
|
|
27
|
-
onSocketOpen: (
|
|
27
|
+
onSocketOpen: (event: Event) => void;
|
|
28
28
|
/**
|
|
29
29
|
* @param {Record<string, any>} data
|
|
30
30
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/client/connections/web-socket/index.js"],"names":[],"mappings":"AAQA;IACE;;;OAGG;IACH,gBAFW,SAAS,EAgBnB;IAbC,cAAY;IAWZ,aAAkB;IAClB,sBAAsB;IAGxB,uBAEC;IAED;;OAEG;IACH,oBAFW,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,QAI7B;IADC,0BAHgB,GAAG,KAAK,IAAI,CAGK;IAGnC;;OAEG;IACH,gBAAiB,OAFN,KAEW,UAErB;IAED;;OAEG;IACH,kBAAmB,OAFR,YAEa,UAUvB;IAED;;OAEG;IACH,eAAgB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/client/connections/web-socket/index.js"],"names":[],"mappings":"AAQA;IACE;;;OAGG;IACH,gBAFW,SAAS,EAgBnB;IAbC,cAAY;IAWZ,aAAkB;IAClB,sBAAsB;IAGxB,uBAEC;IAED;;OAEG;IACH,oBAFW,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,QAI7B;IADC,0BAHgB,GAAG,KAAK,IAAI,CAGK;IAGnC;;OAEG;IACH,gBAAiB,OAFN,KAEW,UAErB;IAED;;OAEG;IACH,kBAAmB,OAFR,YAEa,UAUvB;IAED;;OAEG;IACH,eAAgB,OAFL,KAEU,UAEpB;IAED;;OAEG;IACH,WAFW,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,QAQ7B;IAED,kCAME;CACH"}
|
|
@@ -26,10 +26,10 @@ export default class WebSocket {
|
|
|
26
26
|
this.onCommandCallback(data);
|
|
27
27
|
};
|
|
28
28
|
/**
|
|
29
|
-
* @param {Event}
|
|
29
|
+
* @param {Event} event
|
|
30
30
|
*/
|
|
31
|
-
this.onSocketOpen = (
|
|
32
|
-
logger.log("onSocketOpen");
|
|
31
|
+
this.onSocketOpen = (event) => {
|
|
32
|
+
logger.log(() => ["onSocketOpen", event]);
|
|
33
33
|
};
|
|
34
34
|
this.waitForOpened = () => new Promise((resolve, reject) => {
|
|
35
35
|
// @ts-ignore
|
package/build/client/index.d.ts
CHANGED
|
@@ -3,8 +3,11 @@ export default class Client {
|
|
|
3
3
|
* Creates a new Scoundrel Client
|
|
4
4
|
*
|
|
5
5
|
* @param {any} backend The backend connection (e.g., WebSocket)
|
|
6
|
+
* @param {{enableServerControl?: boolean}} [options]
|
|
6
7
|
*/
|
|
7
|
-
constructor(backend: any
|
|
8
|
+
constructor(backend: any, options?: {
|
|
9
|
+
enableServerControl?: boolean;
|
|
10
|
+
});
|
|
8
11
|
backend: any;
|
|
9
12
|
/** @type {Record<number, any>} */
|
|
10
13
|
outgoingCommands: Record<number, any>;
|
|
@@ -19,6 +22,8 @@ export default class Client {
|
|
|
19
22
|
/** @type {Record<number, any>} */
|
|
20
23
|
objects: Record<number, any>;
|
|
21
24
|
objectsCount: number;
|
|
25
|
+
/** @type {boolean} */
|
|
26
|
+
serverControlEnabled: boolean;
|
|
22
27
|
/**
|
|
23
28
|
* Closes the client connection
|
|
24
29
|
*/
|
|
@@ -175,6 +180,7 @@ export default class Client {
|
|
|
175
180
|
* @returns {Reference}
|
|
176
181
|
*/
|
|
177
182
|
spawnReference(id: string): Reference;
|
|
183
|
+
enableServerControl(): void;
|
|
178
184
|
}
|
|
179
185
|
import Reference from "./reference.js";
|
|
180
186
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.js"],"names":[],"mappings":"AASA;IACE
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.js"],"names":[],"mappings":"AASA;IACE;;;;;OAKG;IACH,qBAHW,GAAG,YACH;QAAC,mBAAmB,CAAC,EAAE,OAAO,CAAA;KAAC,EA2BzC;IAxBC,aAAsB;IAGtB,kCAAkC;IAClC,kBADW,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CACJ;IAC1B,qBAA0B;IAC1B,8BAA8B;IAE9B,kCAAkC;IAClC,UADW,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CACZ;IAElB,kCAAkC;IAClC,UADW,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CACZ;IAElB,wCAAwC;IACxC,YADW,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAChB;IAEpB,kCAAkC;IAClC,SADW,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CACb;IAEjB,qBAAqB;IAErB,sBAAsB;IACtB,sBADW,OAAO,CAC8C;IAGlE;;OAEG;IACH,uBAEC;IAED;;;;;;;OAOG;IACH,mCALW,MAAM,cACN,MAAM,WACF,GAAG,EAAA,GACL,OAAO,CAAC,GAAG,CAAC,CAWxB;IAED;;;;;;;OAOG;IACH,gDALW,MAAM,cACN,MAAM,WACF,GAAG,EAAA,GACL,OAAO,CAAC,SAAS,CAAC,CAY9B;IAED;;;;;OAKG;IACH,8BAHW,MAAM,GACJ,OAAO,CAAC,SAAS,CAAC,CAM9B;IAED;;;;;OAKG;IACH,mBAHW,MAAM,GACJ,OAAO,CAAC,SAAS,CAAC,CAe9B;IAED;;;;;OAKG;IACH,sBAHW,MAAM,GACJ,OAAO,CAAC,SAAS,CAAC,CAY9B;IAED;;;;;;OAMG;IACH,kCAJW,MAAM,WACF,GAAG,EAAA,GACL,OAAO,CAAC,SAAS,CAAC,CAe9B;IAED;;;;OAIG;IACH,qBAHW,GAAG,GACD,OAAO,CAQnB;IAED;;;;;;;;OAQG;IACH,YAAa,0EANV;QAAqB,OAAO,EAApB,MAAM;QACO,UAAU,EAAvB,MAAM;QACI,IAAI,EAAd,GAAG;QACW,KAAK,GAAnB,MAAM;QACQ,UAAU,GAAxB,MAAM;KAEkE,UAkJlF;IAED;;;;;OAKG;IACH,cAHW,GAAG,GACD,GAAG,CAwBf;IAED;;;;;;OAMG;IACH,mDAJW,MAAM,iBACN,MAAM,GACJ,OAAO,CAAC,SAAS,CAAC,CAW9B;IAED;;;;;;OAMG;IACH,sCAJW,MAAM,iBACN,MAAM,GACJ,OAAO,CAAC,GAAG,CAAC,CASxB;IAED;;;;;OAKG;IACH,yBAHW,MAAM,iBACN,GAAG,QAMb;IAED;;;;;OAKG;IACH,oBAHW,MAAM,GACJ,GAAG,CAIf;IAED;;;;;OAKG;IACH,2BAHW,MAAM,kBACN,GAAG,QAMb;IAED;;;;;OAKG;IACH,iCAHW,MAAM,GACJ,GAAG,CAIf;IAED;;;;OAIG;IACH,4BAHW,MAAM,QACN,GAAG,QAIb;IAED;;;;;OAKG;IACH,qBAJW,MAAM,QACN,GAAG,GACD,OAAO,CAAC,GAAG,CAAC,CAexB;IAED;;;OAGG;IACH,WAFW,GAAG,QAIb;IAED;;;;;OAKG;IACH,gCAHW,MAAM,GACJ,OAAO,CAAC,GAAG,CAAC,CAMxB;IAED;;;;;OAKG;IACH,mBAHW,MAAM,GACJ,SAAS,CAQrB;IAED,4BAEC;CACF;sBAxfqB,gBAAgB"}
|
package/build/client/index.js
CHANGED
|
@@ -8,8 +8,9 @@ export default class Client {
|
|
|
8
8
|
* Creates a new Scoundrel Client
|
|
9
9
|
*
|
|
10
10
|
* @param {any} backend The backend connection (e.g., WebSocket)
|
|
11
|
+
* @param {{enableServerControl?: boolean}} [options]
|
|
11
12
|
*/
|
|
12
|
-
constructor(backend) {
|
|
13
|
+
constructor(backend, options = {}) {
|
|
13
14
|
/**
|
|
14
15
|
* Handles an incoming command from the backend
|
|
15
16
|
* @param {object} args
|
|
@@ -25,6 +26,28 @@ export default class Client {
|
|
|
25
26
|
if (!command) {
|
|
26
27
|
throw new Error(`No command key given in data: ${Object.keys(restArgs).join(", ")}`);
|
|
27
28
|
}
|
|
29
|
+
else if (command == "command_response") {
|
|
30
|
+
if (!(commandID in this.outgoingCommands)) {
|
|
31
|
+
throw new Error(`Outgoing command ${commandID} not found: ${Object.keys(this.outgoingCommands).join(", ")}`);
|
|
32
|
+
}
|
|
33
|
+
const savedCommand = this.outgoingCommands[commandID];
|
|
34
|
+
delete this.outgoingCommands[commandID];
|
|
35
|
+
if (error) {
|
|
36
|
+
const errorToThrow = new Error(error);
|
|
37
|
+
if (errorStack) {
|
|
38
|
+
errorToThrow.stack = `${errorStack}\n\n${errorToThrow.stack}`;
|
|
39
|
+
}
|
|
40
|
+
savedCommand.reject(errorToThrow);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
logger.log(() => [`Resolving command ${commandID} with data`, data]);
|
|
44
|
+
savedCommand.resolve(data.data);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
else if (!this.serverControlEnabled) {
|
|
48
|
+
this.send({ command: "command_response", command_id: commandID, error: "Server control is disabled" });
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
28
51
|
else if (command == "get_object") {
|
|
29
52
|
const serverObject = this._getRegisteredObject(data.object_name);
|
|
30
53
|
let object;
|
|
@@ -65,7 +88,14 @@ export default class Client {
|
|
|
65
88
|
if (!method)
|
|
66
89
|
throw new Error(`No method called '${data.method_name}' on a '${object.constructor.name}'`);
|
|
67
90
|
const response = method.call(object, ...data.args);
|
|
68
|
-
|
|
91
|
+
if (data.with == "reference") {
|
|
92
|
+
const objectId = ++this.objectsCount;
|
|
93
|
+
this.objects[objectId] = response;
|
|
94
|
+
this.respondToCommand(commandID, { response: objectId });
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
this.respondToCommand(commandID, { response });
|
|
98
|
+
}
|
|
69
99
|
}
|
|
70
100
|
else if (command == "serialize_reference") {
|
|
71
101
|
const referenceId = data.reference_id;
|
|
@@ -91,22 +121,30 @@ export default class Client {
|
|
|
91
121
|
this.respondToCommand(commandID, { response: attribute });
|
|
92
122
|
}
|
|
93
123
|
}
|
|
94
|
-
else if (command == "
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (error) {
|
|
101
|
-
const errorToThrow = new Error(error);
|
|
102
|
-
if (errorStack) {
|
|
103
|
-
errorToThrow.stack = `${errorStack}\n\n${errorToThrow.stack}`;
|
|
124
|
+
else if (command == "eval") {
|
|
125
|
+
const respondWithResult = (evalResult) => {
|
|
126
|
+
if (data.with_reference) {
|
|
127
|
+
const objectId = ++this.objectsCount;
|
|
128
|
+
this.objects[objectId] = evalResult;
|
|
129
|
+
this.respondToCommand(commandID, { object_id: objectId });
|
|
104
130
|
}
|
|
105
|
-
|
|
131
|
+
else {
|
|
132
|
+
this.respondToCommand(commandID, { response: evalResult });
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
const evalResult = eval(data.eval_string);
|
|
136
|
+
if (evalResult && typeof evalResult.then == "function") {
|
|
137
|
+
evalResult.then(respondWithResult).catch((promiseError) => {
|
|
138
|
+
if (promiseError instanceof Error) {
|
|
139
|
+
this.send({ command: "command_response", command_id: commandID, error: promiseError.message, errorStack: promiseError.stack });
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
this.send({ command: "command_response", command_id: commandID, error: String(promiseError) });
|
|
143
|
+
}
|
|
144
|
+
});
|
|
106
145
|
}
|
|
107
146
|
else {
|
|
108
|
-
|
|
109
|
-
savedCommand.resolve(data.data);
|
|
147
|
+
respondWithResult(evalResult);
|
|
110
148
|
}
|
|
111
149
|
}
|
|
112
150
|
else {
|
|
@@ -138,6 +176,8 @@ export default class Client {
|
|
|
138
176
|
/** @type {Record<number, any>} */
|
|
139
177
|
this.objects = {};
|
|
140
178
|
this.objectsCount = 0;
|
|
179
|
+
/** @type {boolean} */
|
|
180
|
+
this.serverControlEnabled = Boolean(options.enableServerControl);
|
|
141
181
|
}
|
|
142
182
|
/**
|
|
143
183
|
* Closes the client connection
|
|
@@ -187,12 +227,8 @@ export default class Client {
|
|
|
187
227
|
* @returns {Promise<Reference>}
|
|
188
228
|
*/
|
|
189
229
|
async evalWithReference(evalString) {
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
with_reference: true
|
|
193
|
-
});
|
|
194
|
-
const id = result.object_id;
|
|
195
|
-
return this.spawnReference(id);
|
|
230
|
+
const evalReference = await this.getObject("eval");
|
|
231
|
+
return await evalReference.callMethodWithReference("call", null, evalString);
|
|
196
232
|
}
|
|
197
233
|
/**
|
|
198
234
|
* Imports a module and returns a reference to it
|
|
@@ -410,4 +446,7 @@ export default class Client {
|
|
|
410
446
|
this.references[id] = reference;
|
|
411
447
|
return reference;
|
|
412
448
|
}
|
|
449
|
+
enableServerControl() {
|
|
450
|
+
this.serverControlEnabled = true;
|
|
451
|
+
}
|
|
413
452
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reference-proxy.d.ts","sourceRoot":"","sources":["../../src/client/reference-proxy.js"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"reference-proxy.d.ts","sourceRoot":"","sources":["../../src/client/reference-proxy.js"],"names":[],"mappings":";AAyCA;;;GAGG;AACH,+CAHW,GAAG,oBAGwE"}
|
|
@@ -27,14 +27,10 @@ const proxyObjectHandler = {
|
|
|
27
27
|
* @param {any} newValue
|
|
28
28
|
*/
|
|
29
29
|
set(receiver, prop, newValue) {
|
|
30
|
+
void receiver;
|
|
31
|
+
void prop;
|
|
32
|
+
void newValue;
|
|
30
33
|
throw new Error("set property isn't supported yet");
|
|
31
|
-
// @ts-expect-error
|
|
32
|
-
if (typeof receiver == "function")
|
|
33
|
-
receiver = receiver(); // eslint-disable-line no-unreachable
|
|
34
|
-
// @ts-expect-error
|
|
35
|
-
if (!(prop in receiver))
|
|
36
|
-
throw new PropertyNotFoundError(`Property not found: ${prop}`); // eslint-disable-line no-undef
|
|
37
|
-
return Reflect.set(receiver, prop, newValue);
|
|
38
34
|
}
|
|
39
35
|
};
|
|
40
36
|
/**
|
package/build/server/index.d.ts
CHANGED
|
@@ -8,13 +8,20 @@ export default class ScoundrelServer {
|
|
|
8
8
|
/** @type {Client[]} */
|
|
9
9
|
clients: Client[];
|
|
10
10
|
events: EventEmitter<any>;
|
|
11
|
+
/** @type {SingleEventEmitter<(client: Client) => void>} */
|
|
12
|
+
onNewClientEventEmitter: SingleEventEmitter<(client: Client) => void>;
|
|
13
|
+
onNewClient: (listener: (client: Client) => void) => void;
|
|
14
|
+
/** @returns {void} */
|
|
11
15
|
close(): void;
|
|
16
|
+
/** @returns {Client[]} */
|
|
12
17
|
getClients(): Client[];
|
|
13
18
|
/**
|
|
14
19
|
* @param {import("./connections/web-socket/client.js").default} clientBackend
|
|
20
|
+
* @returns {void}
|
|
15
21
|
*/
|
|
16
|
-
|
|
22
|
+
onNewClientFromBackend: (clientBackend: import("./connections/web-socket/client.js").default) => void;
|
|
17
23
|
}
|
|
18
24
|
import Client from "../client/index.js";
|
|
19
25
|
import EventEmitter from "events";
|
|
26
|
+
import SingleEventEmitter from "../utils/single-event-emitter.js";
|
|
20
27
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.js"],"names":[],"mappings":"AAMA;IACE;;;OAGG;IACH,qBAFW,OAAO,mCAAmC,EAAE,OAAO,EAc7D;IAXC,6DAAsB;IAGtB,uBAAuB;IACvB,SADW,MAAM,EAAE,CACF;IAEjB,0BAAgC;IAEhC,2DAA2D;IAC3D,yBADW,kBAAkB,CAAC,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC,CACA;IACvD,iCAFuC,MAAM,KAAK,IAAI,UAEK;IAG7D,sBAAsB;IACtB,SADc,IAAI,CACc;IAEhC,0BAA0B;IAC1B,cADc,MAAM,EAAE,CACc;IAEpC;;;OAGG;IACH,yBAA0B,eAHf,OAAO,oCAAoC,EAAE,OAGjB,KAF1B,IAAI,CAShB;CACF;mBAzCkB,oBAAoB;yBACd,QAAQ;+BACF,kCAAkC"}
|
package/build/server/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
import Client from "../client/index.js";
|
|
3
3
|
import EventEmitter from "events";
|
|
4
|
+
import SingleEventEmitter from "../utils/single-event-emitter.js";
|
|
4
5
|
export default class ScoundrelServer {
|
|
5
6
|
/**
|
|
6
7
|
* Creates a new Scoundrel server
|
|
@@ -9,18 +10,25 @@ export default class ScoundrelServer {
|
|
|
9
10
|
constructor(backend) {
|
|
10
11
|
/**
|
|
11
12
|
* @param {import("./connections/web-socket/client.js").default} clientBackend
|
|
13
|
+
* @returns {void}
|
|
12
14
|
*/
|
|
13
|
-
this.
|
|
14
|
-
const client = new Client(clientBackend);
|
|
15
|
+
this.onNewClientFromBackend = (clientBackend) => {
|
|
16
|
+
const client = new Client(clientBackend, { enableServerControl: true });
|
|
15
17
|
this.clients.push(client);
|
|
16
18
|
this.events.emit("newClient", client);
|
|
19
|
+
this.onNewClientEventEmitter.emit([client]);
|
|
17
20
|
};
|
|
18
21
|
this.backend = backend;
|
|
19
|
-
this.backend.onNewClient(this.
|
|
22
|
+
this.backend.onNewClient(this.onNewClientFromBackend);
|
|
20
23
|
/** @type {Client[]} */
|
|
21
24
|
this.clients = [];
|
|
22
25
|
this.events = new EventEmitter();
|
|
26
|
+
/** @type {SingleEventEmitter<(client: Client) => void>} */
|
|
27
|
+
this.onNewClientEventEmitter = new SingleEventEmitter();
|
|
28
|
+
this.onNewClient = this.onNewClientEventEmitter.connector();
|
|
23
29
|
}
|
|
30
|
+
/** @returns {void} */
|
|
24
31
|
close() { this.backend.close(); }
|
|
32
|
+
/** @returns {Client[]} */
|
|
25
33
|
getClients() { return this.clients; }
|
|
26
34
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SingleEventEmitter: one listener at a time, strongly typed via JSDoc.
|
|
3
|
+
*
|
|
4
|
+
* @template {(...args: any[]) => any} Listener
|
|
5
|
+
*/
|
|
6
|
+
export default class SingleEventEmitter<Listener extends (...args: any[]) => any> {
|
|
7
|
+
/**
|
|
8
|
+
* @param {{ strict?: boolean }=} options
|
|
9
|
+
* - strict (default true): throw if a listener is already connected
|
|
10
|
+
*/
|
|
11
|
+
constructor(options?: {
|
|
12
|
+
strict?: boolean;
|
|
13
|
+
} | undefined);
|
|
14
|
+
/**
|
|
15
|
+
* Connect a listener (type-checked).
|
|
16
|
+
* @param {Listener} listener
|
|
17
|
+
* @returns {void}
|
|
18
|
+
*/
|
|
19
|
+
connect(listener: Listener): void;
|
|
20
|
+
/**
|
|
21
|
+
* Disconnect only if the same listener is currently connected.
|
|
22
|
+
* @param {Listener} listener
|
|
23
|
+
* @returns {void}
|
|
24
|
+
*/
|
|
25
|
+
disconnect(listener: Listener): void;
|
|
26
|
+
/** @returns {void} */
|
|
27
|
+
clear(): void;
|
|
28
|
+
/** @returns {boolean} */
|
|
29
|
+
hasListener(): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Emit the event to the connected listener (if any).
|
|
32
|
+
* Arguments are type-checked against Listener parameters.
|
|
33
|
+
* @param {...Parameters<Listener>} args
|
|
34
|
+
* @returns {boolean} true if a listener was called
|
|
35
|
+
*/
|
|
36
|
+
emit(...args: Parameters<Listener>[]): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Create a "connect method" you can expose as `onNewClient` etc.
|
|
39
|
+
* The returned function is type-checked as (listener: Listener) => void.
|
|
40
|
+
*
|
|
41
|
+
* @returns {(listener: Listener) => void}
|
|
42
|
+
*/
|
|
43
|
+
connector(): (listener: Listener) => void;
|
|
44
|
+
#private;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=single-event-emitter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"single-event-emitter.d.ts","sourceRoot":"","sources":["../../src/utils/single-event-emitter.js"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,wCAFuC,QAAQ,SAAlC,CAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAI;IASpC;;;OAGG;IACH,sBAHW;QAAE,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,YAAC,EAK/B;IAED;;;;OAIG;IACH,kBAHW,QAAQ,GACN,IAAI,CAOhB;IAED;;;;OAIG;IACH,qBAHW,QAAQ,GACN,IAAI,CAIhB;IAED,sBAAsB;IACtB,SADc,IAAI,CAGjB;IAED,yBAAyB;IACzB,eADc,OAAO,CAGpB;IAED;;;;;OAKG;IACH,cAHc,UAAU,CAAC,QAAQ,CAAC,EAAA,GACrB,OAAO,CAOnB;IAED;;;;;OAKG;IACH,aAFa,CAAC,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAKxC;;CACF"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
3
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
4
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
5
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
6
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
7
|
+
};
|
|
8
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
9
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
10
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
11
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
12
|
+
};
|
|
13
|
+
var _SingleEventEmitter_listener, _SingleEventEmitter_strict;
|
|
14
|
+
/**
|
|
15
|
+
* SingleEventEmitter: one listener at a time, strongly typed via JSDoc.
|
|
16
|
+
*
|
|
17
|
+
* @template {(...args: any[]) => any} Listener
|
|
18
|
+
*/
|
|
19
|
+
class SingleEventEmitter {
|
|
20
|
+
/**
|
|
21
|
+
* @param {{ strict?: boolean }=} options
|
|
22
|
+
* - strict (default true): throw if a listener is already connected
|
|
23
|
+
*/
|
|
24
|
+
constructor(options) {
|
|
25
|
+
/** @type {Listener | null} */
|
|
26
|
+
_SingleEventEmitter_listener.set(this, null
|
|
27
|
+
/** @type {boolean} */
|
|
28
|
+
);
|
|
29
|
+
/** @type {boolean} */
|
|
30
|
+
_SingleEventEmitter_strict.set(this, void 0);
|
|
31
|
+
__classPrivateFieldSet(this, _SingleEventEmitter_strict, options?.strict ?? true, "f");
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Connect a listener (type-checked).
|
|
35
|
+
* @param {Listener} listener
|
|
36
|
+
* @returns {void}
|
|
37
|
+
*/
|
|
38
|
+
connect(listener) {
|
|
39
|
+
if (__classPrivateFieldGet(this, _SingleEventEmitter_listener, "f") && __classPrivateFieldGet(this, _SingleEventEmitter_strict, "f")) {
|
|
40
|
+
throw new Error("SingleEventEmitter already has a listener connected");
|
|
41
|
+
}
|
|
42
|
+
__classPrivateFieldSet(this, _SingleEventEmitter_listener, listener, "f");
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Disconnect only if the same listener is currently connected.
|
|
46
|
+
* @param {Listener} listener
|
|
47
|
+
* @returns {void}
|
|
48
|
+
*/
|
|
49
|
+
disconnect(listener) {
|
|
50
|
+
if (__classPrivateFieldGet(this, _SingleEventEmitter_listener, "f") === listener)
|
|
51
|
+
__classPrivateFieldSet(this, _SingleEventEmitter_listener, null, "f");
|
|
52
|
+
}
|
|
53
|
+
/** @returns {void} */
|
|
54
|
+
clear() {
|
|
55
|
+
__classPrivateFieldSet(this, _SingleEventEmitter_listener, null, "f");
|
|
56
|
+
}
|
|
57
|
+
/** @returns {boolean} */
|
|
58
|
+
hasListener() {
|
|
59
|
+
return __classPrivateFieldGet(this, _SingleEventEmitter_listener, "f") !== null;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Emit the event to the connected listener (if any).
|
|
63
|
+
* Arguments are type-checked against Listener parameters.
|
|
64
|
+
* @param {...Parameters<Listener>} args
|
|
65
|
+
* @returns {boolean} true if a listener was called
|
|
66
|
+
*/
|
|
67
|
+
emit(...args) {
|
|
68
|
+
const fn = __classPrivateFieldGet(this, _SingleEventEmitter_listener, "f");
|
|
69
|
+
if (!fn)
|
|
70
|
+
return false;
|
|
71
|
+
fn(...args);
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Create a "connect method" you can expose as `onNewClient` etc.
|
|
76
|
+
* The returned function is type-checked as (listener: Listener) => void.
|
|
77
|
+
*
|
|
78
|
+
* @returns {(listener: Listener) => void}
|
|
79
|
+
*/
|
|
80
|
+
connector() {
|
|
81
|
+
// bind() keeps `this`, and the return type is enforced by the JSDoc above
|
|
82
|
+
return this.connect.bind(this);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
_SingleEventEmitter_listener = new WeakMap(), _SingleEventEmitter_strict = new WeakMap();
|
|
86
|
+
export default SingleEventEmitter;
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "scoundrel-remote-eval",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.10",
|
|
5
5
|
"description": "",
|
|
6
6
|
"main": "build/index.js",
|
|
7
7
|
"types": "build/index.d.ts",
|
|
8
8
|
"files": ["build/**"],
|
|
9
9
|
"scripts": {
|
|
10
|
+
"all-checks": "npm run typecheck && npm run lint && npm run test",
|
|
10
11
|
"build": "rm -rf build && tsc",
|
|
11
12
|
"lint": "eslint",
|
|
12
13
|
"prepublishOnly": "npm run build",
|