@naanlang/naan 1.6.0 → 1.7.1

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 (54) hide show
  1. package/LICENSE.md +1 -1
  2. package/README.md +1 -1
  3. package/bin/index.js +2 -2
  4. package/dist/env_web.js +3 -32
  5. package/dist/naan.min.js +10 -10
  6. package/frameworks/browser/sworker.js +23 -23
  7. package/frameworks/browser/terminals.nlg +161 -212
  8. package/frameworks/browser/worker.nlg +78 -30
  9. package/frameworks/browser/workers.nlg +1 -1
  10. package/frameworks/browser/ws_client.nlg +23 -87
  11. package/frameworks/client/apiclient.nlg +91 -58
  12. package/frameworks/client/client.nlg +22 -2
  13. package/frameworks/client/psm_client.nlg +10 -14
  14. package/frameworks/client/relaycon.nlg +1 -1
  15. package/frameworks/common/common.nlg +13 -9
  16. package/frameworks/node/apiserver.nlg +1 -2
  17. package/frameworks/node/node.nlg +1 -0
  18. package/frameworks/node/pty.nlg +21 -21
  19. package/frameworks/node/shell.nlg +26 -3
  20. package/frameworks/node/worker.nlg +166 -84
  21. package/frameworks/node/ws_client.nlg +25 -84
  22. package/frameworks/project/build.nlg +83 -32
  23. package/frameworks/project/projects.nlg +2 -2
  24. package/frameworks/running/debugcom.nlg +20 -3
  25. package/frameworks/running/debugnub.nlg +16 -7
  26. package/frameworks/running/executors.nlg +36 -26
  27. package/frameworks/running/instances.nlg +34 -22
  28. package/frameworks/running/remote_req.nlg +6 -9
  29. package/frameworks/running/remote_res.nlg +2 -2
  30. package/frameworks/running/sourcecode.nlg +3 -2
  31. package/frameworks/running/taskexec.nlg +14 -57
  32. package/frameworks/service/imp.nlg +37 -5
  33. package/frameworks/service/nubnode.nlg +39 -8
  34. package/frameworks/service/nubweb.nlg +28 -10
  35. package/frameworks/storage/file_manager.nlg +3 -2
  36. package/lib/browser/env_web.js +10 -39
  37. package/lib/browser/env_webworker.js +1 -30
  38. package/lib/core/naanlib.js +10 -10
  39. package/lib/env_node.js +5 -22
  40. package/lib/env_nodeworker.js +8 -43
  41. package/lib/node_server_init.nlg +149 -0
  42. package/lib/node_worker.js +36 -0
  43. package/lib/node_worker_init.nlg +43 -0
  44. package/package.json +1 -1
  45. package/plugins/openSearch/openSearch.nlg +3 -2
  46. package/plugins/openSearch/os_dynamo.nlg +3 -2
  47. package/plugins/serviceAws/aws_cloudwatchlogs.nlg +44 -2
  48. package/plugins/serviceAws/aws_dynamo.nlg +144 -63
  49. package/plugins/serviceAws/aws_utils.nlg +6 -11
  50. package/plugins/serviceAws/serviceAws.nlg +3 -2
  51. package/test/test_01_core.nlg +2 -2
  52. package/test/test_02_context.nlg +8 -8
  53. package/test/test_03_jsinterop.nlg +3 -3
  54. package/test/test_07_lingo.nlg +1 -1
@@ -10,13 +10,6 @@
10
10
  *
11
11
  */
12
12
 
13
- /*
14
- * globals
15
- *
16
- */
17
-
18
- gWorkerController = false;
19
-
20
13
 
21
14
  /*
22
15
  * CurBot
@@ -46,13 +39,12 @@ closure CurBot(options, local curbot) {
46
39
  debugtext: ONdebugText.proc
47
40
  status: ONstatus.proc
48
41
  debugout: ONdebugOut.proc
49
- messageout: ONmessageOut.proc
50
42
  ioctl: ONioctl.proc
51
43
  })
52
44
  curbot.guid = 1
53
45
  curbot.host = js.t // our magic Naan controller reference
54
46
 
55
- curbot.instanceID = UUID()
47
+ curbot.instanceID = App.imp.getUUID("CurBot(${options.name}) instanceID")
56
48
  curbot.sender = App.imp.register(curbot, {
57
49
  name: curbot.name
58
50
  type: curbot.type
@@ -86,12 +78,46 @@ closure CurBot(options, local curbot) {
86
78
  //
87
79
  // Send the message to all our subscribers.
88
80
  //
89
- function notifyAll(message, exceptID, local topDestID) {
81
+ curbot.notifyAll = function notifyAll(message, exceptID, local topDestID) {
90
82
  for topDestID in curbot.topsubs
91
83
  if topDestID != exceptID
92
84
  curbot.sender(topDestID, message)
93
85
  }
94
86
 
87
+ // textEntry
88
+ //
89
+ // Simulate text entry for subprocess support.
90
+ //
91
+ curbot.textEntry = function textEntry(text) {
92
+ curbot.host.DispatchMessage({
93
+ id: "keyline"
94
+ text: text
95
+ })
96
+ }
97
+
98
+ // rawStart
99
+ //
100
+ // Configure ioctls for raw terminal operation using the spawned PTY process.
101
+ //
102
+ curbot.rawStart = closure rawStart(ptySpawned, local dokey, doresize, doattach) {
103
+ dokey = xnew(function(msg, arg) { ptySpawned.write(arg.key) })
104
+ js.t.ioctl_add_listener("raw-key", dokey)
105
+ doresize = xnew(function(msg, dim) { ptySpawned.resize(dim.cols, dim.rows) })
106
+ js.t.ioctl_add_listener("term-resize", doresize)
107
+ js.t.ioctl("raw", true)
108
+ doattach = xnew(function(msg, arg) { js.t.ioctl("raw", true) }) // restore raw mode when we are attached
109
+ js.t.ioctl_add_listener("term-attach", doattach)
110
+
111
+ curbot.rawStop = function rawStop() {
112
+ js.t.ioctl("raw")
113
+ js.t.ioctl_remove_listener("raw-key", dokey)
114
+ js.t.ioctl_remove_listener("term-resize", doresize)
115
+ js.t.ioctl_remove_listener("term-attach", doattach)
116
+ dokey = doresize = doattach = false
117
+ curbot.rawStop = undefined
118
+ }
119
+ }
120
+
95
121
  // impReceive
96
122
  //
97
123
  // Received a payload.
@@ -104,11 +130,14 @@ closure CurBot(options, local curbot) {
104
130
  subscribe(sourceID)
105
131
  else if message.id == "unsubscribe"
106
132
  unsubscribe(sourceID)
107
- else if message.id == "typed"
108
- notifyAll(message, sourceID)
109
133
  else {
110
- message.data._guid = sourceID
111
- curbot.host.DispatchMessage(message)
134
+ subscribe(sourceID) // auto-subscribe
135
+ if message.id == "typed"
136
+ notifyAll(message, sourceID)
137
+ else if message.id == "keyline" && curbot.keyin
138
+ curbot.keyin(message.text) // terminal keyboard -> pty subprocess
139
+ else
140
+ curbot.host.DispatchMessage(message)
112
141
  }
113
142
  }
114
143
 
@@ -117,7 +146,7 @@ closure CurBot(options, local curbot) {
117
146
  // Received an unknown destination notice.
118
147
  //
119
148
  curbot.impUnknown = function impUnknown(unknownID, sourceID, gateway) {
120
- if curbot.topsubs[unknownID] { // did not have courtesy to unsubscribe!
149
+ if curbot.topsubs.indexOf(unknownID) >= 0 { // did not have courtesy to unsubscribe!
121
150
  unsubscribe(unknownID)
122
151
  return
123
152
  }
@@ -125,28 +154,15 @@ closure CurBot(options, local curbot) {
125
154
  debuglog("CurBot.impUnknownID:", unknownID, "sourceID:", sourceID)
126
155
  }
127
156
 
128
- // replyHook
129
- //
130
- // Intercept the ReplyMessage function to see if we should handle it.
131
- //
132
- function replyHook(data) {
133
- // js.g.console.log("CurBot.replyHook:\n", Dialect.print(new(data)))
134
- // debuglog("CurBot.replyHook:\n", Dialect.print(new(data)))
135
- /* ###
136
- if data._guid
137
- workco.uworkers[data._guid].msgport.postMessage({
138
- id: "targetreceive"
139
- data: data
140
- })
141
- */
142
- }
143
- curbot.host.OnReplyHook(replyHook.proc)
144
-
145
157
  // ONtextOut
146
158
  //
147
159
  // Process console text output by Naan.
148
160
  //
149
161
  function ONtextOut(text) {
162
+ if curbot.naanin {
163
+ curbot.naanin(text) // naan output -> pty subprocess
164
+ return
165
+ }
150
166
  notifyAll({
151
167
  id: "textout",
152
168
  text: text,
@@ -187,22 +203,6 @@ closure CurBot(options, local curbot) {
187
203
  })
188
204
  }
189
205
 
190
- // ONmessageOut
191
- //
192
- // Process message output.
193
- //
194
- function ONmessageOut(data, local _guid) {
195
- if data._guid {
196
- _guid = data._guid
197
- data._guid = undefined
198
- curbot.sender(_guid, {
199
- id: "targetout",
200
- data: data
201
- })
202
- return
203
- }
204
- }
205
-
206
206
  // ONioctl
207
207
  //
208
208
  // Process ioctl.
@@ -236,11 +236,17 @@ closure CurBot(options, local curbot) {
236
236
  * type: <string> // target category, e.g. "Local"
237
237
  * startup: <dictionary> // startup options
238
238
  * topOriginID: <string> // so top can route to us through IMP
239
+ * rootPath: <string> // root path of worker's folder, must have lib/node_worker_init.nlg
240
+ * node: {
241
+ * // see NodeJS docs
242
+ * workerData: { // passed to node_worker.js
243
+ * }
244
+ * }
239
245
  * }
240
246
  *
241
247
  */
242
248
 
243
- closure NowBot(options, local nowbot, subChannel) {
249
+ closure NowBot(options, local nowbot, nodeops, subChannel, ptySpawned) {
244
250
  global(App, js, threads, JSpath)
245
251
  nowbot = new(object, this)
246
252
  nowbot.topsubs = []
@@ -248,13 +254,19 @@ closure NowBot(options, local nowbot, subChannel) {
248
254
  type: "node-worker"
249
255
  }, options)
250
256
 
251
- // options = { execArgv: ["--inspect-brk"] } // uncomment for NodeJS debugging
252
- nowbot.thread = xnew(threads.Worker, JSpath.resolve(js.d, "lib/node_worker.js"), options)
257
+ nodeops = merge({
258
+ // execArgv: ["--inspect-brk"] // unfortunately doesn't work; use VS Code instead
259
+ }, options.node)
260
+ if options.rootPath
261
+ nodeops.workerData = merge({
262
+ rootPath: options.rootPath
263
+ }, nodeops.workerData)
264
+ nowbot.thread = xnew(threads.Worker, JSpath.resolve(js.d, "lib/node_worker.js"), nodeops)
253
265
  subChannel = xnew(threads.MessageChannel)
254
266
  nowbot.thread.postMessage({ hereIsYourPort: subChannel.port1 }, [subChannel.port1])
255
267
  nowbot.msgport = subChannel.port2
256
268
 
257
- nowbot.instanceID = UUID()
269
+ nowbot.instanceID = App.imp.getUUID("NowBot(${options.name}) instanceID")
258
270
  nowbot.sender = App.imp.register(nowbot, {
259
271
  name: options.name
260
272
  type: options.type
@@ -303,6 +315,43 @@ closure NowBot(options, local nowbot, subChannel) {
303
315
  nowbot.sender(topDestID, message)
304
316
  }
305
317
 
318
+ // textEntry
319
+ //
320
+ // Simulate text entry for subprocess support.
321
+ //
322
+ nowbot.textEntry = function textEntry(text) {
323
+ nowbot.msgport.postMessage({
324
+ id: "keyline"
325
+ text: text
326
+ })
327
+ }
328
+
329
+ // ioctl
330
+ //
331
+ // Send an ioctl to the terminal.
332
+ //
333
+ nowbot.ioctl = function ioctl(op, arg) {
334
+ notifyAll({
335
+ id: "ioctl"
336
+ op: op
337
+ arg: arg
338
+ })
339
+ }
340
+
341
+ // rawMode
342
+ //
343
+ // Configure ioctls for raw terminal operation using the spawned PTY process.
344
+ //
345
+ nowbot.rawStart = function rawStart(ptys) {
346
+ ptySpawned = ptys
347
+ ioctl("raw", true)
348
+ nowbot.rawStop = function rawStop() {
349
+ ioctl("raw", false)
350
+ nowbot.rawStop = undefined
351
+ ptySpawned = false
352
+ }
353
+ }
354
+
306
355
  // impReceive
307
356
  //
308
357
  // Received a payload.
@@ -314,22 +363,35 @@ closure NowBot(options, local nowbot, subChannel) {
314
363
  subscribe(sourceID)
315
364
  else if message.id == "unsubscribe"
316
365
  unsubscribe(sourceID)
317
- else if message.id == "typed"
318
- notifyAll(message, sourceID)
319
366
  else {
367
+ subscribe(sourceID) // auto-subscribe
368
+ if message.id == "typed" {
369
+ notifyAll(message, sourceID)
370
+ return
371
+ }
372
+ if ptySpawned { // raw mode: kludgey, but works
373
+ if message.id == "ioctl-event" {
374
+ if message.msg == "raw-key"
375
+ ptySpawned.write(message.arg.key)
376
+ else if message.msg == "term-resize"
377
+ ptySpawned.resize(message.arg.cols, message.arg.rows)
378
+ }
379
+ if (message.id == "attached")
380
+ ioctl("raw", true) // restore raw mode after terminal attach
381
+ }
320
382
  if message.id == "keyline" && nowbot.keyin
321
383
  nowbot.keyin(message.text) // terminal keyboard -> pty subprocess
322
384
  else
323
385
  nowbot.msgport.postMessage(message)
324
386
  }
325
387
  }
326
-
388
+
327
389
  // impUnknown
328
390
  //
329
391
  // Received an unknown destination notice.
330
392
  //
331
393
  nowbot.impUnknown = function impUnknown(unknownID, sourceID, gateway) {
332
- if nowbot.topsubs[unknownID] { // did not have courtesy to unsubscribe!
394
+ if nowbot.topsubs.indexOf(unknownID) >= 0 { // did not have courtesy to unsubscribe!
333
395
  unsubscribe(unknownID)
334
396
  return
335
397
  }
@@ -348,9 +410,11 @@ closure NowBot(options, local nowbot, subChannel) {
348
410
  id: "terminated"
349
411
  error: error
350
412
  })
413
+ nowbot.dispatchEvent("terminated", nowbot.instanceID, { error: error })
414
+ nowbot.thread = false
351
415
  destroy()
352
416
  })
353
-
417
+
354
418
  // onexit
355
419
  //
356
420
  // Note that the worker thread has terminated.
@@ -360,9 +424,21 @@ closure NowBot(options, local nowbot, subChannel) {
360
424
  id: "terminated"
361
425
  exitCode: exitCode
362
426
  })
427
+ nowbot.dispatchEvent("terminated", nowbot.instanceID, { exitCode: exitCode })
428
+ nowbot.thread = false
363
429
  destroy()
364
430
  })
365
431
 
432
+ // close
433
+ //
434
+ // Close and remove our IMP resources, but leave the worker alone.
435
+ //
436
+ nowbot.close = function close() {
437
+ nowbot.impconn.destroy()
438
+ nowbot.impconn = false
439
+ App.imp.unregister(nowbot.instanceID)
440
+ }
441
+
366
442
  // destroy
367
443
  //
368
444
  // Ensure the worker is destroyed.
@@ -370,9 +446,7 @@ closure NowBot(options, local nowbot, subChannel) {
370
446
  nowbot.destroy = function destroy() {
371
447
  terminate()
372
448
  nowbot.thread = false
373
- nowbot.impconn.close()
374
- nowbot.impconn = false
375
- App.imp.unregister(nowbot.instanceID)
449
+ close()
376
450
  }
377
451
 
378
452
  // NaaN message
@@ -380,42 +454,35 @@ closure NowBot(options, local nowbot, subChannel) {
380
454
  // A message has been received from NaaN running on the worker thread.
381
455
  //
382
456
  nowbot.msgport.on("message", function (msg, local startmsg) {
383
- if msg.id == "naan-imp-node" {
457
+ if msg.id == "naan-imp-out" {
384
458
  nowbot.impconn.dispatch(new(msg.data))
385
459
  return
386
460
  } else if msg.id == "loaded" {
387
461
  startmsg = {
388
462
  id: "start"
389
463
  state: false // resume state, if we have any
390
- altcmd: false
464
+ altcmd: options.startup.initcmds
391
465
  dirpath: js.d
392
466
  workerID: workerID
393
- workerGUID: worker.guid
394
467
  metadata: {
395
468
  mainInstanceID: App.imp.us.instanceID
396
469
  name: options.name
470
+ maintext: options.startup.maintext.concat("\n")
397
471
  }
398
472
  }
399
- if options.startup.initcmds
400
- startmsg.altcmd = options.startup.initcmds.concat("\n")
401
473
  if options.startup.dirpath
402
474
  startmsg.dirpath = options.startup.dirpath
403
475
  nowbot.msgport.postMessage(startmsg)
404
476
  notifyAll({
405
477
  id: "started"
406
478
  })
407
- } else if msg.id == "targetsend" {
408
- msg.data._guid = nowbot.guid
409
- js.t.DispatchMessage({
410
- id: "targetin"
411
- data: msg.data
412
- }) }
413
- else if msg.id == "textout" && nowbot.naanin
479
+ nowbot.dispatchEvent("started", nowbot.instanceID)
480
+ } else if msg.id == "textout" && nowbot.naanin
414
481
  nowbot.naanin(msg.text) // naan output -> pty subprocess
415
482
  else
416
483
  notifyAll(msg)
417
484
  })
418
-
485
+
419
486
  // terminate
420
487
  //
421
488
  // Terminate this worker asap.
@@ -440,6 +507,7 @@ closure NowBot(options, local nowbot, subChannel) {
440
507
  }
441
508
 
442
509
  nowbot.subprocess = MakeSubProcessor(nowbot) // Enable PTY subprocesses on our terminal
510
+ Events(nowbot)
443
511
 
444
512
  // finis
445
513
 
@@ -482,7 +550,7 @@ closure NobBot(options, local nobbot)
482
550
 
483
551
  nobbot.impconn = App.imp.responder(function(data) {
484
552
  nobbot.msgport.postMessage({ // send to worker
485
- id: "naan-imp-node" // scoped for NodeWorkerNub
553
+ id: "naan-imp-in" // scoped for NodeWorkerNub
486
554
  data: data
487
555
  })
488
556
  list(false, { sent: true })
@@ -518,7 +586,9 @@ closure NobBot(options, local nobbot)
518
586
  * return value is a result tuple with an object that can be used to kill or monitor the
519
587
  * subprocess.
520
588
  *
521
- * The subprocess can be connected in a variety of ways:
589
+ * exec.subprocess(executable, args, options)
590
+ *
591
+ * The subprocess can be connected to the terminal raw, or in a variety of ways:
522
592
  *
523
593
  * term-keyboard | term-display | worker-in | worker-out
524
594
  * -------------------------------------|---------------|------------|-----------
@@ -527,29 +597,28 @@ closure NobBot(options, local nobbot)
527
597
  *
528
598
  * Options:
529
599
  * {
530
- * input: false | "keyboard" | "pipe"
531
- * output: false | "display" | "pipe" | "tee"
532
- * spawn: <PtyManager.spawn options>
600
+ * input: false | "keyboard" | "pipe"
601
+ * output: false | "display" | "pipe" | "tee"
602
+ * raw: <boolean>
603
+ * spawn: <PtyManager.spawn options>
533
604
  * }
534
605
  *
535
606
  */
536
607
 
537
608
  closure MakeSubProcessor(execbot) {
609
+ global()
538
610
  closure subprocess(
539
611
  executable, args, options, local error, pty) {
540
612
 
541
613
  // ptyData - text has been returned from the subprocess
542
614
  function ptyData(text) {
543
615
  if options.output == "display" || options.output == "tee"
544
- notifyAll({ // send to display
616
+ execbot.notifyAll({ // send to display
545
617
  id: "textout",
546
618
  text: text
547
619
  })
548
620
  if options.output == "pipe" || options.output == "tee"
549
- msg({ // send to NaaN worker
550
- id: "keyline"
551
- text: text
552
- })
621
+ execbot.textEntry(text) // send to NaaN worker
553
622
  }
554
623
 
555
624
  // ptyKilled - the subprocess has terminated
@@ -558,6 +627,10 @@ closure MakeSubProcessor(execbot) {
558
627
  execbot.keyin = false
559
628
  if execbot.naanin === naanin
560
629
  execbot.naanin = false
630
+ if options.raw {
631
+ execbot.rawStop()
632
+ execbot.textEntry("\x15mu();;\n") // send to NaaN worker
633
+ }
561
634
  }
562
635
 
563
636
  // keyin - the terminal has generated text for the subprocess
@@ -570,6 +643,7 @@ closure MakeSubProcessor(execbot) {
570
643
  pty.write(text)
571
644
  }
572
645
 
646
+ require("./pty.nlg") // load on demand
573
647
  `(error, pty) = PtyManager().spawn(executable, args, {
574
648
  ptyData: ptyData
575
649
  ptyKilled: ptyKilled
@@ -577,9 +651,17 @@ closure MakeSubProcessor(execbot) {
577
651
  if error
578
652
  return (list(error))
579
653
  if options.input == "keyboard"
580
- nowbot.keyin = keyin // replace existing
654
+ execbot.keyin = keyin // replace existing
581
655
  else if options.input == "pipe"
582
656
  execbot.naanin = naanin // replace existing
657
+ if options.raw
658
+ execbot.rawStart(pty)
659
+
660
+ // our worker has terminated
661
+ execbot.addEventListener("terminated", function() {
662
+ pty.kill()
663
+ })
664
+
583
665
  list(false, pty)
584
666
  }
585
667
  };
@@ -13,106 +13,47 @@
13
13
  /*
14
14
  * WebSocket
15
15
  *
16
- * Create a WebSocket client connection object.
16
+ * Create a WebSocket client connection object, returning a tuple.
17
17
  *
18
18
  * Required:
19
- * host: <string> // host:port
19
+ * urlstr: <string>
20
20
  *
21
21
  * Options:
22
22
  * {
23
23
  * wss: <boolean> // true to use secure connection
24
+ * query: <string> // query string starting with ?
24
25
  * onopen: <proc>(e) // connection has opened
25
26
  * onmessage: <proc>(e, message) // message received
26
27
  * onerror: <proc>(e) // error occurred
27
28
  * onclose: <proc>(e) // connection has closed
28
- * query: <string> // query string starting with ? for insecure connections
29
- * querys: <string> // query string starting with ? for secure connections
30
29
  * }
31
30
  *
32
31
  */
33
32
 
34
- closure WebSocket(host, options, local wsock) {
35
- global(js)
36
- wsock = new(object, this)
37
- options = new(options) || { }
33
+ closure WebSocket(urlstr, options, local error, wslib, url, ws) {
34
+ `(error, wslib) = await(js.i("ws"))
35
+ if error
36
+ return (list(error))
38
37
 
39
- // connect
40
- //
41
- // Connect with the host if not already connected, returning a result tuple.
42
- //
43
- wsock.connect = closure connect(local error, ws, url, pending) {
44
- if wsock.ws
45
- return (list(false, { open: true }))
46
- `(error, ws) = await(js.i("ws"))
47
- if error
48
- return (list(error))
49
- if options.wss
50
- url = "wss://${host}${options.querys||''}"
51
- else
52
- url = "ws://${host}${options.query||''}"
53
- wsock.ws = xnew(ws.default, url)
54
- pending = new(nonce)
55
-
56
- // onopen
57
- // The WebSocket connection is now open.
58
- wsock.ws.onopen = closure onopen(e) {
59
- pending.signal(list(false, { ok: true }))
60
- options.onopen(e, e.target, e.type)
61
- }
62
-
63
- // onmessage
64
- // A message was received from the worker via WebSockets.
65
- wsock.ws.onmessage = closure onmessage(e, local message) {
66
- try {
67
- message = new(JSONparse(e.data))
68
- } catch {
69
- debuglog("can't decode incoming ws message:", exception)
70
- options.onerror(Error("can't decode incoming message:", exception))
71
- return
72
- }
73
- options.onmessage(e, message)
74
- }
75
-
76
- // onerror
77
- // An error occurred on the WebSocket connection.
78
- wsock.ws.onerror = closure onerror(e, local error) {
79
- error = Error("websocket failed", e)
80
- options.onerror(e, error)
81
- pending.signal(list(error)) // ensure connect completes
82
- }
83
-
84
- // onclose
85
- // The WebSocket connection was closed.
86
- wsock.ws.onclose = closure onclose(e) {
87
- options.onclose(e, e.code, e.reason)
88
- pending.signal(list(Error("connection closed"))) // ensure connect completes
89
- wsock.ws = false
90
- }
91
-
92
- pending.wait()
38
+ try {
39
+ url = xnew(js.g.URL, urlstr)
40
+ } catch {
41
+ return (list(Error("invalid URL ${urlstr}", exception)))
93
42
  }
94
-
95
- // send
96
- //
97
- // Send a message dictionary.
98
- //
99
- wsock.send = function send(message) {
100
- wsock.ws.send(JSONstringify(message))
101
- }
102
-
103
- // close
104
- //
105
- // Close the connection.
106
- //
107
- wsock.close = function close(reason) {
108
- if !reason
109
- reason = 1000 // normal close
110
- wsock.ws.close(reason)
111
- }
112
-
113
- // finis
114
-
115
- wsock
43
+ options = merge({
44
+ query: url.search || ''
45
+ wss: url.protocol == "https:"
46
+ }, options)
47
+ if options.wss
48
+ url = "wss://${url.host}${options.query}"
49
+ else
50
+ url = "ws://${url.host}${options.query}"
51
+ ws = xnew(wslib.default, url)
52
+ ws.onopen = options.onopen
53
+ ws.onmessage = options.onmessage
54
+ ws.onerror = options.onerror
55
+ ws.onclose = options.onclose
56
+ list(false, ws)
116
57
  };
117
58
 
118
59