scoundrel-remote-eval 1.0.7 → 1.0.9

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.
Files changed (45) hide show
  1. package/build/client/connections/web-socket/index.d.ts +34 -0
  2. package/build/client/connections/web-socket/index.d.ts.map +1 -0
  3. package/build/client/connections/web-socket/index.js +68 -0
  4. package/build/client/index.d.ts +180 -0
  5. package/build/client/index.d.ts.map +1 -0
  6. package/build/client/index.js +413 -0
  7. package/build/client/reference-proxy.d.ts +7 -0
  8. package/build/client/reference-proxy.d.ts.map +1 -0
  9. package/build/client/reference-proxy.js +45 -0
  10. package/build/client/reference.d.ts +50 -0
  11. package/build/client/reference.d.ts.map +1 -0
  12. package/build/client/reference.js +63 -0
  13. package/build/index.d.ts +2 -0
  14. package/build/index.d.ts.map +1 -0
  15. package/build/index.js +1 -0
  16. package/build/logger.d.ts +39 -0
  17. package/build/logger.d.ts.map +1 -0
  18. package/build/logger.js +64 -0
  19. package/build/python-web-socket-runner.d.ts +27 -0
  20. package/build/python-web-socket-runner.d.ts.map +1 -0
  21. package/build/python-web-socket-runner.js +94 -0
  22. package/build/server/connections/web-socket/client.d.ts +26 -0
  23. package/build/server/connections/web-socket/client.d.ts.map +1 -0
  24. package/build/server/connections/web-socket/client.js +39 -0
  25. package/build/server/connections/web-socket/index.d.ts +19 -0
  26. package/build/server/connections/web-socket/index.d.ts.map +1 -0
  27. package/build/server/connections/web-socket/index.js +29 -0
  28. package/build/server/index.d.ts +20 -0
  29. package/build/server/index.d.ts.map +1 -0
  30. package/build/server/index.js +26 -0
  31. package/package.json +17 -4
  32. package/spec/reference-with-proxy-spec.js +0 -87
  33. package/spec/support/jasmine.json +0 -13
  34. package/spec/web-socket-javascript-spec.js +0 -64
  35. package/spec/web-socket-python-spec.js +0 -55
  36. package/src/client/connections/web-socket/index.js +0 -51
  37. package/src/client/index.js +0 -423
  38. package/src/client/reference-proxy.js +0 -29
  39. package/src/client/reference.js +0 -67
  40. package/src/index.js +0 -0
  41. package/src/logger.js +0 -56
  42. package/src/python-web-socket-runner.js +0 -87
  43. package/src/server/connections/web-socket/client.js +0 -28
  44. package/src/server/connections/web-socket/index.js +0 -22
  45. package/src/server/index.js +0 -21
@@ -1,87 +0,0 @@
1
- import Client from "../src/client/index.js"
2
- import ClientWebSocket from "../src/client/connections/web-socket/index.js"
3
- import referenceWithProxy from "../src/client/reference-proxy.js"
4
- import Server from "../src/server/index.js"
5
- import ServerWebSocket from "../src/server/connections/web-socket/index.js"
6
- import {WebSocket, WebSocketServer} from "ws"
7
-
8
- const shared = {}
9
-
10
- describe("referenceWithProxy", () => {
11
- beforeEach(async () => {
12
- shared.wss = new WebSocketServer({port: 8080})
13
- shared.serverWebSocket = new ServerWebSocket(shared.wss)
14
- shared.server = new Server(shared.serverWebSocket)
15
-
16
- shared.ws = new WebSocket("http://localhost:8080")
17
- shared.clientWebSocket = new ClientWebSocket(shared.ws)
18
-
19
- await shared.clientWebSocket.waitForOpened()
20
-
21
- shared.client = new Client(shared.clientWebSocket)
22
-
23
- shared.serverClient = shared.server.getClients()[0]
24
-
25
- if (!shared.serverClient) {
26
- throw new Error("No client connected to server")
27
- }
28
- })
29
-
30
- afterEach(async () => {
31
- await shared.client.close()
32
- await shared.server.close()
33
- })
34
-
35
- it("creates a reference with a proxy", async () => {
36
- const stringObjectReference = await shared.client.newObjectWithReference("Array")
37
- const stringObject = referenceWithProxy(stringObjectReference)
38
-
39
- await stringObject.push("test1")
40
- await stringObject.push("test2")
41
-
42
- const result = await stringObject.__serialize()
43
-
44
- expect(result).toEqual(["test1", "test2"])
45
- })
46
-
47
- it("reads attributes from a reference", async () => {
48
- const testArray = await shared.client.newObjectWithReference("Array")
49
-
50
- await testArray.callMethod("push", "test1")
51
- await testArray.callMethod("push", "test2")
52
-
53
- const result = await testArray.serialize()
54
-
55
- expect(result).toEqual(["test1", "test2"])
56
-
57
- const firstValue = await testArray.readAttribute(0)
58
- const secondValue = await testArray.readAttribute(1)
59
-
60
- expect(firstValue).toEqual("test1")
61
- expect(secondValue).toEqual("test2")
62
- })
63
-
64
- it("calls methods", async () => {
65
- const stringObjectReference = await shared.client.newObjectWithReference("Array")
66
- const stringObject = referenceWithProxy(stringObjectReference)
67
-
68
- await stringObject.push("test1")
69
- await stringObject.push("test2")
70
-
71
- const result = await stringObject.__serialize()
72
-
73
- expect(result).toEqual(["test1", "test2"])
74
- })
75
-
76
- it("calls methods on the client from the server", async () => {
77
- const stringObjectReference = await shared.serverClient.newObjectWithReference("Array")
78
- const stringObject = referenceWithProxy(stringObjectReference)
79
-
80
- await stringObject.push("test1")
81
- await stringObject.push("test2")
82
-
83
- const result = await stringObject.__serialize()
84
-
85
- expect(result).toEqual(["test1", "test2"])
86
- })
87
- })
@@ -1,13 +0,0 @@
1
- {
2
- "spec_dir": "spec",
3
- "spec_files": [
4
- "**/*[sS]pec.?(m)js"
5
- ],
6
- "helpers": [
7
- "helpers/**/*.?(m)js"
8
- ],
9
- "env": {
10
- "stopSpecOnExpectationFailure": false,
11
- "random": true
12
- }
13
- }
@@ -1,64 +0,0 @@
1
- import Client from "../src/client/index.js"
2
- import ClientWebSocket from "../src/client/connections/web-socket/index.js"
3
- import Server from "../src/server/index.js"
4
- import ServerWebSocket from "../src/server/connections/web-socket/index.js"
5
- import {WebSocket, WebSocketServer} from "ws"
6
-
7
- const shared = {}
8
-
9
- describe("scoundrel - web-socket - javascript", () => {
10
- beforeEach(async () => {
11
- shared.wss = new WebSocketServer({port: 8080})
12
- shared.serverWebSocket = new ServerWebSocket(shared.wss)
13
- shared.server = new Server(shared.serverWebSocket)
14
-
15
- shared.ws = new WebSocket("http://localhost:8080")
16
- shared.clientWebSocket = new ClientWebSocket(shared.ws)
17
-
18
- await shared.clientWebSocket.waitForOpened()
19
-
20
- shared.client = new Client(shared.clientWebSocket)
21
- })
22
-
23
- afterEach(async () => {
24
- await shared.client.close()
25
- await shared.server.close()
26
- })
27
-
28
- it("creates a server and connects to it with the client", async () => {
29
- const stringObject = await shared.client.newObjectWithReference("Array")
30
-
31
- await stringObject.callMethod("push", "test1")
32
- await stringObject.callMethod("push", "test2")
33
-
34
- const result = await stringObject.serialize()
35
-
36
- expect(result).toEqual(["test1", "test2"])
37
- })
38
-
39
- it("returns results from method calls", async () => {
40
- const stringObject = await shared.client.newObjectWithReference("Array")
41
-
42
- await stringObject.callMethod("push", "test1")
43
- await stringObject.callMethod("push", "test2")
44
-
45
- const result = await stringObject.callMethod("join", ", ")
46
-
47
- expect(result).toEqual("test1, test2")
48
- })
49
-
50
- it("handles errors from method calls", async () => {
51
- const stringObject = await shared.client.newObjectWithReference("Array")
52
-
53
- let caughtError = null
54
-
55
- try {
56
- await stringObject.callMethod("nonExistentMethod")
57
- } catch (error) {
58
- caughtError = error
59
- }
60
-
61
- expect(caughtError).toBeInstanceOf(Error)
62
- expect(caughtError.message).toEqual("No method called 'nonExistentMethod' on a 'Array'")
63
- })
64
- })
@@ -1,55 +0,0 @@
1
- import Client from "../src/client/index.js"
2
- import ClientWebSocket from "../src/client/connections/web-socket/index.js"
3
- import Logger from "../src/logger.js"
4
- import PythonWebSocketRunner from "../src/python-web-socket-runner.js"
5
- import {WebSocket} from "ws"
6
-
7
- const shared = {}
8
- const logger = new Logger("Scoundrel WebSocket Python Spec")
9
-
10
- describe("scoundrel - web-socket - python", () => {
11
- beforeEach(async () => {
12
- logger.log("Starting Python with client")
13
- shared.pythonWebSocketRunner = new PythonWebSocketRunner()
14
-
15
- logger.log("Running Python WebSocket runner and waiting for PID")
16
- await shared.pythonWebSocketRunner.runAndWaitForPid()
17
-
18
- logger.log("Starting WebSocket client connection")
19
- const ws = new WebSocket("ws://localhost:53874")
20
-
21
- logger.log("Creating ClientWebSocket")
22
- const clientWebSocket = new ClientWebSocket(ws)
23
-
24
- logger.log("Waiting for WebSocket to open")
25
- await clientWebSocket.waitForOpened()
26
-
27
- logger.log("Creating Scoundrel Client")
28
- shared.client = new Client(clientWebSocket)
29
- })
30
-
31
- afterEach(async () => {
32
- shared.client?.close()
33
- shared.pythonWebSocketRunner?.close()
34
- })
35
-
36
- it("creates a server and connects to it with the client", async () => {
37
- const stringObject = await shared.client.newObjectWithReference("[]")
38
-
39
- await stringObject.callMethod("append", "test1")
40
- await stringObject.callMethod("append", "test2")
41
-
42
- const result = await stringObject.serialize()
43
-
44
- expect(result).toEqual(["test1", "test2"])
45
- })
46
-
47
- it("imports classes and uses them", async () => {
48
- const math = await shared.client.import("math")
49
- const pi = await math.readAttributeWithReference("pi")
50
- const cosOfPi = await math.callMethodWithReference("cos", pi)
51
- const result = await cosOfPi.serialize()
52
-
53
- expect(result).toEqual(-1)
54
- })
55
- })
@@ -1,51 +0,0 @@
1
- import Logger from "../../../logger.js"
2
-
3
- const logger = new Logger("Scoundrel WebSocket")
4
-
5
- // logger.setDebug(true)
6
-
7
- export default class WebSocket {
8
- constructor(ws) {
9
- this.ws = ws
10
- this.ws.addEventListener("error", this.onSocketError)
11
- this.ws.addEventListener("open", this.onSocketOpen)
12
- this.ws.addEventListener("message", this.onSocketMessage)
13
-
14
- this.commands = {}
15
- this.commandsCount = 0
16
- }
17
-
18
- async close() {
19
- await this.ws.close()
20
- }
21
-
22
- onCommand(callback) {
23
- this.onCommandCallback = callback
24
- }
25
-
26
- onSocketError = (event) => {
27
- logger.error(() => ["onSocketError", event])
28
- }
29
-
30
- onSocketMessage = (event) => {
31
- const data = JSON.parse(event.data)
32
-
33
- logger.log(() => ["Client::Connections::WebSocket onSocketMessage", data])
34
- this.onCommandCallback(data)
35
- }
36
-
37
- onSocketOpen = (_event) => {
38
- logger.log("onSocketOpen")
39
- }
40
-
41
- send(data) {
42
- const sendData = JSON.stringify(data)
43
- logger.log(() => ["Sending", sendData])
44
- this.ws.send(sendData)
45
- }
46
-
47
- waitForOpened = () => new Promise((resolve, reject) => {
48
- this.ws.addEventListener("open", resolve)
49
- this.ws.addEventListener("error", reject)
50
- })
51
- }
@@ -1,423 +0,0 @@
1
- import Logger from "../logger.js"
2
- import Reference from "./reference.js"
3
-
4
- const logger = new Logger("Scoundrel Client")
5
-
6
- // logger.setDebug(true)
7
-
8
- export default class Client {
9
- /**
10
- * Creates a new Scoundrel Client
11
- *
12
- * @param {any} backend The backend connection (e.g., WebSocket)
13
- */
14
- constructor(backend) {
15
- this.backend = backend
16
- this.backend.onCommand(this.onCommand)
17
-
18
- this.outgoingCommands = {}
19
- this.incomingCommands = {}
20
- this.outgoingCommandsCount = 0
21
-
22
- this._classes = {}
23
- this._objects = {}
24
- this.references = {}
25
- this.objects = {}
26
- this.objectsCount = 0
27
- }
28
-
29
- /**
30
- * Closes the client connection
31
- */
32
- async close() {
33
- this.backend.close()
34
- }
35
-
36
- /**
37
- * Calls a method on a reference and returns the result directly
38
- *
39
- * @param {number} referenceId
40
- * @param {string} methodName
41
- * @param {...any} args
42
- * @returns {Promise<any>}
43
- */
44
- async callMethodOnReference(referenceId, methodName, ...args) {
45
- const result = await this.sendCommand("call_method_on_reference", {
46
- args: this.parseArg(args),
47
- method_name: methodName,
48
- reference_id: referenceId,
49
- with: "result"
50
- })
51
-
52
- return result.response
53
- }
54
-
55
- /**
56
- * Calls a method on a reference and returns a new reference
57
- *
58
- * @param {number} referenceId
59
- * @param {string} methodName
60
- * @param {...any} args
61
- * @returns {Promise<Reference>}
62
- */
63
- async callMethodOnReferenceWithReference(referenceId, methodName, ...args) {
64
- const result = await this.sendCommand("call_method_on_reference", {
65
- args: this.parseArg(args),
66
- method_name: methodName,
67
- reference_id: referenceId,
68
- with: "reference"
69
- })
70
- const id = result.response
71
-
72
- return this.spawnReference(id)
73
- }
74
-
75
- /**
76
- * Evaluates a string and returns a new reference
77
- *
78
- * @param {string} evalString
79
- * @returns {Promise<Reference>}
80
- */
81
- async evalWithReference(evalString) {
82
- const result = await this.sendCommand("eval", {
83
- eval_string: evalString,
84
- with_reference: true
85
- })
86
- const id = result.object_id
87
-
88
- return this.spawnReference(id)
89
- }
90
-
91
- /**
92
- * Imports a module and returns a reference to it
93
- *
94
- * @param {string} importName
95
- * @returns {Promise<Reference>}
96
- */
97
- async import(importName) {
98
- const result = await this.sendCommand("import", {
99
- import_name: importName
100
- })
101
-
102
- logger.log(() => ["import", {result}])
103
-
104
- if (!result) throw new Error("No result given")
105
- if (!result.object_id) throw new Error(`No object ID given in result: ${JSON.stringify(result)}`)
106
-
107
- const id = result.object_id
108
-
109
- return this.spawnReference(id)
110
- }
111
-
112
- /**
113
- * Gets a registered object by name
114
- *
115
- * @param {string} objectName
116
- * @returns {Promise<Reference>}
117
- */
118
- async getObject(objectName) {
119
- const result = await this.sendCommand("get_object", {
120
- object_name: objectName
121
- })
122
-
123
- if (!result) throw new Error("Blank result given")
124
-
125
- const id = result.object_id
126
-
127
- return this.spawnReference(id)
128
- }
129
-
130
- /**
131
- * Spawns a new reference to an object
132
- *
133
- * @param {string} id
134
- * @returns {Promise<Reference>}
135
- */
136
- async newObjectWithReference(className, ...args) {
137
- const result = await this.sendCommand("new_object_with_reference", {
138
- args: this.parseArg(args),
139
- class_name: className
140
- })
141
-
142
- if (!result) throw new Error("Blank result given")
143
-
144
- const id = result.object_id
145
-
146
- if (!id) throw new Error(`No object ID given in result: ${JSON.stringify(result)}`)
147
-
148
- return this.spawnReference(id)
149
- }
150
-
151
- isPlainObject(input) {
152
- if (input && typeof input === "object" && !Array.isArray(input)) {
153
- return true
154
- }
155
-
156
- return false
157
- }
158
-
159
- onCommand = ({command, command_id: commandID, data, error, errorStack, ...restArgs}) => {
160
- logger.log(() => ["onCommand", {command, commandID, data, error, errorStack, restArgs}])
161
-
162
- try {
163
- if (!command) {
164
- throw new Error(`No command key given in data: ${Object.keys(restArgs).join(", ")}`)
165
- } else if (command == "get_object") {
166
- const serverObject = this._getRegisteredObject(data.object_name)
167
- let object
168
-
169
- if (serverObject) {
170
- object = serverObject
171
- } else {
172
- object = global[data.object_name]
173
-
174
- if (!object) throw new Error(`No such object: ${data.object_name}`)
175
- }
176
-
177
- const objectId = ++this.objectsCount
178
-
179
- this.objects[objectId] = object
180
- this.respondToCommand(commandID, {object_id: objectId})
181
- } else if (command == "new_object_with_reference") {
182
- const className = data.class_name
183
- let object
184
-
185
- if (typeof className == "string") {
186
- const ClassInstance = this.getClass(className) || global[className]
187
-
188
- if (!ClassInstance) throw new Error(`No such class: ${className}`)
189
-
190
- object = new ClassInstance(...data.args)
191
- } else {
192
- throw new Error(`Don't know how to handle class name: ${typeof className}`)
193
- }
194
-
195
- const objectId = ++this.objectsCount
196
-
197
- this.objects[objectId] = object
198
- this.respondToCommand(commandID, {object_id: objectId})
199
- } else if (command == "call_method_on_reference") {
200
- const referenceId = data.reference_id
201
- const object = this.objects[referenceId]
202
-
203
- if (!object) throw new Error(`No object by that ID: ${referenceId}`)
204
-
205
- const method = object[data.method_name]
206
-
207
- if (!method) throw new Error(`No method called '${data.method_name}' on a '${object.constructor.name}'`)
208
-
209
- const response = method.call(object, ...data.args)
210
-
211
- this.respondToCommand(commandID, {response})
212
- } else if (command == "serialize_reference") {
213
- const referenceId = data.reference_id
214
- const object = this.objects[referenceId]
215
-
216
- if (!object) throw new Error(`No object by that ID: ${referenceId}`)
217
-
218
- this.respondToCommand(commandID, JSON.stringify(object))
219
- } else if (command == "read_attribute") {
220
- const attributeName = data.attribute_name
221
- const referenceId = data.reference_id
222
- const returnWith = data.with
223
- const object = this.objects[referenceId]
224
-
225
- if (!object) throw new Error(`No object by that ID: ${referenceId}`)
226
-
227
- const attribute = object[attributeName]
228
-
229
- if (returnWith == "reference") {
230
- const objectId = ++this.objectsCount
231
-
232
- this.objects[objectId] = attribute
233
- this.respondToCommand(commandID, {response: objectId})
234
- } else {
235
- this.respondToCommand(commandID, {response: attribute})
236
- }
237
- } else if (command == "command_response") {
238
- if (!(commandID in this.outgoingCommands)) {
239
- throw new Error(`Outgoing command ${commandID} not found: ${Object.keys(this.outgoingCommands).join(", ")}`)
240
- }
241
-
242
- const savedCommand = this.outgoingCommands[commandID]
243
-
244
- delete this.outgoingCommands[commandID]
245
-
246
- if (error) {
247
- const errorToThrow = new Error(error)
248
-
249
- if (errorStack) {
250
- errorToThrow.stack = `${errorStack}\n\n${errorToThrow.stack}`
251
- }
252
-
253
- savedCommand.reject(errorToThrow)
254
- } else {
255
- logger.log(() => [`Resolving command ${commandID} with data`, data])
256
- savedCommand.resolve(data.data)
257
- }
258
- } else {
259
- throw new Error(`Unknown command: ${command}`)
260
- }
261
- } catch (error) {
262
- this.send({command: "command_response", command_id: commandID, error: error.message, errorStack: error.stack})
263
-
264
- logger.error(error)
265
- }
266
- }
267
-
268
- /**
269
- * Parases an argument for sending to the server
270
- *
271
- * @param {any} arg
272
- * @returns {any}
273
- */
274
- parseArg(arg) {
275
- if (Array.isArray(arg)) {
276
- return arg.map((argInArray) => this.parseArg(argInArray))
277
- } else if (arg instanceof Reference) {
278
- return {
279
- __scoundrel_object_id: arg.id,
280
- __scoundrel_type: "reference"
281
- }
282
- } else if (this.isPlainObject(arg)) {
283
- const newObject = {}
284
-
285
- for (const key in arg) {
286
- const value = arg[key]
287
-
288
- newObject[key] = this.parseArg(value)
289
- }
290
-
291
- return newObject
292
- }
293
-
294
- return arg
295
- }
296
-
297
- /**
298
- * Reads an attribute on a reference and returns a new reference
299
- *
300
- * @param {number} referenceId
301
- * @param {string} attributeName
302
- * @returns {Promise<Reference>}
303
- */
304
- async readAttributeOnReferenceWithReference(referenceId, attributeName) {
305
- const result = await this.sendCommand("read_attribute", {
306
- attribute_name: attributeName,
307
- reference_id: referenceId,
308
- with: "reference"
309
- })
310
- const id = result.response
311
-
312
- return this.spawnReference(id)
313
- }
314
-
315
- /**
316
- * Reads an attribute on a reference and returns the result directly
317
- *
318
- * @param {number} referenceId
319
- * @param {string} attributeName
320
- * @returns {Promise<any>}
321
- */
322
- async readAttributeOnReference(referenceId, attributeName) {
323
- const result = await this.sendCommand("read_attribute", {
324
- attribute_name: attributeName,
325
- reference_id: referenceId,
326
- with: "result"
327
- })
328
- return result.response
329
- }
330
-
331
- /**
332
- * Registers a class by name
333
- *
334
- * @param {string} className
335
- * @param {any} classInstance
336
- */
337
- registerClass(className, classInstance) {
338
- if (className in this._classes) throw new Error(`Class already exists: ${className}`)
339
-
340
- this._classes[className] = classInstance
341
- }
342
-
343
- /**
344
- * Gets a registered class by name
345
- *
346
- * @param {string} className
347
- * @returns {any}
348
- */
349
- getClass(className) {
350
- return this._classes[className]
351
- }
352
-
353
- /**
354
- * Registers an object by name
355
- *
356
- * @param {string} objectName
357
- * @param {any} objectInstance
358
- */
359
- registerObject(objectName, objectInstance) {
360
- if (objectName in this._objects) throw new Error(`Object already exists: ${objectName}`)
361
-
362
- this._objects[objectName] = objectInstance
363
- }
364
-
365
- /**
366
- * Gets a registered object by name
367
- *
368
- * @param {string} objectName
369
- * @returns {any}
370
- */
371
- _getRegisteredObject(objectName) {
372
- return this._objects[objectName]
373
- }
374
-
375
- respondToCommand(commandId, data) {
376
- this.sendCommand("command_response", {command_id: commandId, data})
377
- }
378
-
379
- sendCommand(command, data) {
380
- return new Promise((resolve, reject) => {
381
- const outgoingCommandCount = ++this.outgoingCommandsCount
382
- const commandData = {
383
- command,
384
- command_id: outgoingCommandCount,
385
- data
386
- }
387
-
388
- this.outgoingCommands[outgoingCommandCount] = {resolve, reject}
389
- logger.log(() => ["Sending", commandData])
390
- this.send(commandData)
391
- })
392
- }
393
-
394
- send(data) {
395
- this.backend.send(data)
396
- }
397
-
398
- /**
399
- * Serializes a reference and returns the result directly
400
- *
401
- * @param {number} referenceId
402
- * @returns {Promise<any>}
403
- */
404
- async serializeReference(referenceId) {
405
- const json = await this.sendCommand("serialize_reference", {reference_id: referenceId})
406
-
407
- return JSON.parse(json)
408
- }
409
-
410
- /**
411
- * Spawns a new reference to an object
412
- *
413
- * @param {string} id
414
- * @returns {Promise<Reference>}
415
- */
416
- spawnReference(id) {
417
- const reference = new Reference(this, id)
418
-
419
- this.references[id] = reference
420
-
421
- return reference
422
- }
423
- }