@naanlang/naan 1.0.16 → 1.2.0

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/LICENSE.md +2 -2
  2. package/README.md +13 -11
  3. package/bin/index.js +4 -4
  4. package/dist/naan.min.js +14 -14
  5. package/frameworks/browser/https_request.nlg +26 -10
  6. package/frameworks/browser/sworker.js +26 -25
  7. package/frameworks/browser/terminals.nlg +118 -55
  8. package/frameworks/browser/ws_client.nlg +124 -0
  9. package/frameworks/client/apiclient.nlg +82 -116
  10. package/frameworks/client/psm_client.nlg +37 -16
  11. package/frameworks/client/relaycon.nlg +308 -0
  12. package/frameworks/common/common.nlg +1 -1
  13. package/frameworks/common/utils.nlg +40 -2
  14. package/frameworks/node/apiserver.nlg +342 -103
  15. package/frameworks/node/https_request.nlg +32 -6
  16. package/frameworks/node/node.nlg +60 -2
  17. package/frameworks/node/psm_server.nlg +11 -7
  18. package/frameworks/node/worker.nlg +22 -9
  19. package/frameworks/node/ws_client.nlg +127 -0
  20. package/frameworks/project/build.nlg +3 -2
  21. package/frameworks/project/projects.nlg +8 -2
  22. package/frameworks/running/debugnub.nlg +2 -2
  23. package/frameworks/running/debugutil.nlg +1 -1
  24. package/frameworks/running/executors.nlg +69 -17
  25. package/frameworks/running/sourcecode.nlg +2 -2
  26. package/frameworks/running/taskexec.nlg +25 -25
  27. package/frameworks/storage/dbt_pouch.nlg +8 -6
  28. package/frameworks/storage/file_manager.nlg +6 -3
  29. package/frameworks/storage/psm.nlg +5 -3
  30. package/frameworks/storage/psm_dbtables.nlg +75 -53
  31. package/frameworks/storage/resources.nlg +4 -2
  32. package/lib/browser/env_web.js +6 -6
  33. package/lib/browser/env_webworker.js +1 -1
  34. package/lib/core/naanlib.js +14 -14
  35. package/lib/env_node.js +97 -8
  36. package/lib/env_nodeworker.js +22 -4
  37. package/package.json +1 -1
  38. package/plugins/serviceAws/aws/lambda_rest_index.js +5 -1
  39. package/plugins/serviceAws/aws_dynamo.nlg +91 -32
  40. package/plugins/serviceAws/dbt_aws.nlg +5 -5
  41. package/plugins/serviceAws/psm_aws.nlg +2 -2
  42. package/test/harness.nlg +1 -1
  43. package/test/test_01_core.nlg +30 -4
  44. package/test/test_02_context.nlg +2 -2
  45. package/test/test_07_lingo.nlg +3 -0
@@ -6,11 +6,66 @@
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2020-2021 by Richard C. Zulch
9
+ * Copyright (c) 2020-2024 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
13
13
 
14
+ /*
15
+ * NaanlangDir
16
+ *
17
+ * Ensure the naanlang directory exists, returning a result tuple.
18
+ *
19
+ */
20
+
21
+ closure NaanlangDir(local path, errmkdir, errstat, stat) {
22
+ global(JSpath, os, App, fs)
23
+ path = JSpath.join(os.homedir(), ".naanlang/")
24
+ `(errmkdir) = await(fs.promises.mkdir(path))
25
+ if errmkdir.code == "EEXIST" {
26
+ `(errstat, stat) = await(fs.promises.stat(path))
27
+ if errstat || !stat.isDirectory()
28
+ return (list(Error("mkdir conflict:", errmkdir))) // file in the way
29
+ }
30
+ else if errmkdir
31
+ return (list(Error("mkdir failed:", errmkdir)))
32
+ await(fs.promises.mkdir(JSpath.join(path, "nidecom"))) // ignore errors here
33
+ App.config = merge({
34
+ prefsdir: path
35
+ })
36
+ list(false, path)
37
+ };
38
+
39
+
40
+ /*
41
+ * NaanlangRC
42
+ *
43
+ * Return the naanlang RC file if found, or an empty dictionary otherwise.
44
+ *
45
+ */
46
+
47
+ closure NaanlangRC(local path, error, config) {
48
+ global(JSpath, fs, App)
49
+ `(error, path) = NaanlangDir()
50
+ if path {
51
+ path = JSpath.join(path, "naanlangrc")
52
+ `(error, config) = await(fs.promises.readFile(path, {
53
+ encoding: "utf8"
54
+ }))
55
+ if !error
56
+ `(error, config) = JsonParse(config)
57
+ }
58
+ if config
59
+ App.config = merge({
60
+ rcpath: path
61
+ config: config
62
+ })
63
+ else
64
+ config = { }
65
+ config
66
+ };
67
+
68
+
14
69
  /*
15
70
  * NodeStreamLiner
16
71
  *
@@ -63,7 +118,7 @@ closure NodeStreamLiner(endings, local liner) {
63
118
  */
64
119
 
65
120
  function nodrInit(local manifest) {
66
- manifest = `(NodeStreamLiner, nodrInit)
121
+ manifest = `(NaanlangDir, NaanlangRC, NodeStreamLiner, nodrInit)
67
122
 
68
123
  if !js.g
69
124
  throw("module frameworks/node cannot be used from a browser")
@@ -77,7 +132,10 @@ function nodrInit(local manifest) {
77
132
  stream = js.r("stream")
78
133
  nodeHttps = js.r("https")
79
134
  nodeUrl= js.r("url")
135
+ threads = js.r("worker_threads")
80
136
  }()
81
137
  module.reload = nodrReload
138
+ module.exports.NaanlangDir = NaanlangDir
139
+ module.exports.NaanlangRC = NaanlangRC
82
140
  })
83
141
  } ();
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2019-2022 by Richard C. Zulch
9
+ * Copyright (c) 2019-2023 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -450,8 +450,8 @@ closure psmsFsConnector(psm, api, local connClassID, connector, watch) {
450
450
  connector.info = function info(resID, local info, error, creds) {
451
451
  info = {
452
452
  classID: connClassID
453
- type: "remote filesystem"
454
- services: ["NideFS", "NideHostAPI"]
453
+ type: "remote server"
454
+ services: ["NideFS", "NideHostAPI", "NideTerminal"]
455
455
  }
456
456
  if resID == "Host" {
457
457
  info.name = "Host"
@@ -517,13 +517,17 @@ closure psmsFsConnector(psm, api, local connClassID, connector, watch) {
517
517
  *
518
518
  */
519
519
 
520
- function MakeServerPSM(api, local importPSM) {
520
+ function MakeServerPSM(api, name, local importPSM, error, path, psm) {
521
521
  importPSM = require("../storage/psm.nlg")
522
- App.psm = importPSM.MakePSM(JSpath.resolve(js.d, "NideDB")).1
523
- if App.psm {
522
+ `(error, path) = NaanlangDir() // put database in ~/.naanlang/
523
+ if !error
524
+ `(error, psm) = importPSM.MakePSM(JSpath.join(path, "${name}DB_${api.port.tostring}"))
525
+ if !error {
526
+ App.psm = psm
524
527
  psmsFsConnector(App.psm, api)
525
528
  require("frameworks/storage/dbt_pouch.nlg").DbConnector(App.psm) // Pouch databases as filesystems
526
- }
529
+ } else
530
+ ErrorDebuglog("Cannot create PSM", error)
527
531
  App.psm
528
532
  };
529
533
 
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2019-2021 by Richard C. Zulch
9
+ * Copyright (c) 2019-2024 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -18,9 +18,11 @@
18
18
  *
19
19
  */
20
20
 
21
- closure workCurrent(workerID, local current) {
21
+ closure workCurrent(workerID, startup, local current) {
22
+ global()
22
23
  current = new(object, this)
23
24
  current.workerID = workerID
25
+ current.name = startup.name
24
26
  current.links = []
25
27
  current.termif = xnew({
26
28
  textout: ONtextOut.proc
@@ -177,14 +179,17 @@ closure workCurrent(workerID, local current) {
177
179
  *
178
180
  */
179
181
 
180
- closure workThread(workerID, startup, local worker, subChannel, deathWatcher) {
182
+ closure workThread(workerID, startup, local worker, options, subChannel, deathWatcher) {
183
+ global(threads, JSpath)
181
184
  worker = new(object, this)
185
+ worker.name = startup.name
182
186
  worker.workerID = workerID
183
187
  worker.links = []
184
188
 
185
189
  // create our worker thread
186
190
 
187
- worker.thread = xnew(threads.Worker, JSpath.resolve(js.d, "node_worker.js"))
191
+ // options = { execArgv: ["--inspect-brk"] } // uncomment for NodeJS debugging
192
+ worker.thread = xnew(threads.Worker, JSpath.resolve(js.d, "node_worker.js"), options)
188
193
  subChannel = xnew(threads.MessageChannel)
189
194
  worker.thread.postMessage({ hereIsYourPort: subChannel.port1 }, [subChannel.port1])
190
195
  worker.msgport = subChannel.port2
@@ -224,7 +229,7 @@ closure workThread(workerID, startup, local worker, subChannel, deathWatcher) {
224
229
 
225
230
  worker.thread.on("error", function onerror(error) {
226
231
  debuglog("worker terminated with error", workerID, error)
227
- exitNotify(exitCode)
232
+ exitNotify(-1)
228
233
  })
229
234
 
230
235
  //
@@ -341,6 +346,7 @@ closure workThread(workerID, startup, local worker, subChannel, deathWatcher) {
341
346
  */
342
347
 
343
348
  closure workLink(workco, conn, worker, sender, local link) {
349
+ global()
344
350
  link = new(object, this)
345
351
 
346
352
  //
@@ -393,6 +399,7 @@ closure workLink(workco, conn, worker, sender, local link) {
393
399
  */
394
400
 
395
401
  closure workController(api, local workco) {
402
+ global()
396
403
  workco = new(object, this)
397
404
  workco.serverID = "Workers" // our serverID, used for communications
398
405
  workco.workers = { } // dictionary of workers by workerID
@@ -480,7 +487,7 @@ closure workController(api, local workco) {
480
487
  else if message.workerOp == "spawn" {
481
488
  if !worker { // create worker if needed
482
489
  if message.workerID == "NideServer"
483
- worker = workco.current = workCurrent(message.workerID)
490
+ worker = workco.current = workCurrent(message.workerID, message.payload)
484
491
  else
485
492
  worker = workThread(message.workerID, message.payload)
486
493
  workco.workers[message.workerID] = worker }
@@ -489,7 +496,14 @@ closure workController(api, local workco) {
489
496
  if message.workerID == "NideServer"
490
497
  workco.current.vitalUpdate({
491
498
  id: "workerlist", // send the initial list of workers
492
- workerIDs: workco.workers.*
499
+ workers: let(workers, workerID, worker) {
500
+ workers = { }
501
+ for `(workerID, worker) in workco.workers
502
+ workers[workerID] = {
503
+ name: worker.name
504
+ }
505
+ workers
506
+ }()
493
507
  })
494
508
  else {
495
509
  worker.vitalWatch(termination)
@@ -516,7 +530,7 @@ closure workController(api, local workco) {
516
530
  // Note that the connection with the client was closed.
517
531
  //
518
532
 
519
- workco.wsClose = function wsClose(conn, local workerID) {
533
+ workco.wsClose = function wsClose(conn, local workerID, link) {
520
534
  for workerID in workco.workers {
521
535
  link = workco.links[conn][workerID]
522
536
  if link
@@ -553,7 +567,6 @@ function workInit(local manifest) {
553
567
 
554
568
  Naan.module.build(module.id, "worker", function(modobj, compobj) {
555
569
  require("./node.nlg")
556
- threads = js.r("worker_threads") // ### should be on reload
557
570
  compobj.manifest = manifest
558
571
  modobj.exports.WorkerController = workController
559
572
  })
@@ -0,0 +1,127 @@
1
+ /*
2
+ * ws_client.nlg
3
+ *
4
+ * WebSocket client operations for NodeJS.
5
+ *
6
+ * column positioning: // // !
7
+ *
8
+ * Copyright (c) 2023 by Richard C. Zulch
9
+ *
10
+ */
11
+
12
+
13
+ /*
14
+ * WebSocket
15
+ *
16
+ * Create a WebSocket client connection object using the existing HTTPS connection.
17
+ *
18
+ * Required:
19
+ * host: <string> // host:port
20
+ *
21
+ * Options:
22
+ * {
23
+ * onopen: <proc>(e) // connection has opened
24
+ * onmessage: <proc>(e, message) // message received
25
+ * onerror: <proc>(e) // error occurred
26
+ * onclose: <proc>(e) // connection has closed
27
+ * }
28
+ *
29
+ */
30
+
31
+ closure WebSocket(host, options, local wsock) {
32
+ global(js)
33
+ wsock = new(object, this)
34
+ options = new(options) || { }
35
+
36
+ // connect
37
+ //
38
+ // Connect with the host if not already connected, returning a result tuple.
39
+ //
40
+ wsock.connect = closure connect(local pending, error, ws) {
41
+ if wsock.ws
42
+ return (list(false, { open: true }))
43
+ `(error, ws) = await(js.i("ws"))
44
+ if error
45
+ return (list(error))
46
+ wsock.ws = xnew(ws.default, "ws://${host}")
47
+ pending = new(nonce)
48
+
49
+ // onopen
50
+ // The WebSocket connection is now open.
51
+ wsock.ws.onopen = closure onopen(e) {
52
+ pending.signal(list(false, { ok: true }))
53
+ options.onopen(e, e.target, e.type)
54
+ }
55
+
56
+ // onmessage
57
+ // A message was received from the worker via WebSockets.
58
+ wsock.ws.onmessage = closure onmessage(e, local message) {
59
+ try {
60
+ message = new(JSONparse(e.data))
61
+ } catch {
62
+ debuglog("can't decode incoming ws message:", exception)
63
+ options.onerror(Error("can't decode incoming message:", exception))
64
+ return
65
+ }
66
+ options.onmessage(e, message)
67
+ }
68
+
69
+ // onerror
70
+ // An error occurred on the WebSocket connection.
71
+ wsock.ws.onerror = closure onerror(e, local error) {
72
+ error = Error("websocket failed", e)
73
+ options.onerror(e, error)
74
+ pending.signal(list(error)) // ensure connect completes
75
+ }
76
+
77
+ // onclose
78
+ // The WebSocket connection was closed.
79
+ wsock.ws.onclose = closure onclose(e) {
80
+ options.onclose(e, e.code, e.reason)
81
+ pending.signal(list(Error("connection closed"))) // ensure connect completes
82
+ wsock.ws = false
83
+ }
84
+
85
+ pending.wait()
86
+ }
87
+
88
+ // send
89
+ //
90
+ // Send a message dictionary.
91
+ //
92
+ wsock.send = function send(message) {
93
+ wsock.ws.send(JSONstringify(message))
94
+ }
95
+
96
+ // close
97
+ //
98
+ // Close the connection.
99
+ //
100
+ wsock.close = function close(reason) {
101
+ if !reason
102
+ reason = 1000 // normal close
103
+ wsock.ws.close(reason)
104
+ }
105
+
106
+ // finis
107
+
108
+ wsock
109
+ };
110
+
111
+
112
+ /*
113
+ * ws_clientInit
114
+ *
115
+ * Initialize WebSocket client operations for browsers.
116
+ *
117
+ */
118
+
119
+ function ws_clientInit(local manifest) {
120
+ manifest = `(WebSocket, ws_clientInit)
121
+
122
+ Naan.module.build(module.id, "ws_client", function(modobj, compobj) {
123
+ require("node.nlg")
124
+ compobj.manifest = manifest
125
+ modobj.exports.WebSocket = WebSocket
126
+ })
127
+ }();
@@ -933,7 +933,7 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
933
933
  xvrFileDefs = { }
934
934
  xvrFileDefs.BuildNumber = buildno
935
935
  `(error, verFileDefs) = readSubstitutionFile(build.verfilepath, xvrFileDefs)
936
- build.version = verFileDefs["Version"]
936
+ build.version = verFileDefs["Version"].trim()
937
937
  verFileDefs["CacheBuster"] = HashMD5(build.version.concat(Math.random()))
938
938
  }
939
939
  if build.tasks.catenate > 0 || build.tasks.zip > 0 {
@@ -1195,7 +1195,8 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
1195
1195
  if error
1196
1196
  error = Error("Builder: cannot read main file", error)
1197
1197
  else {
1198
- `(error, executor) = track.spawn(build.runway.spawn.0, build.runway.spawn.1, "Build-".concat(build.buildnum), {
1198
+ `(error, executor) = track.spawn(build.runway.spawn.0, "${rules.name} ${build.version}", {
1199
+ workerID: "Run-${build.buildnum}"
1199
1200
  startup: {
1200
1201
  initcmds: maintext
1201
1202
  dirpath: fs.path.resolve(fs.path.sep, fs.rootpath, build.destpath)
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2016-2022 by Richard C. Zulch
9
+ * Copyright (c) 2016-2024 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -296,7 +296,7 @@ closure projConnector(projman, projtype, local connector, watch) {
296
296
  *
297
297
  * Nide.proj/
298
298
  * nide_cliser.cfg - JSON definition of the project (see below)
299
- * NaanIDE_version.txt - [default] substitution file defining 1.0.16+1 etc.
299
+ * NaanIDE_version.txt - [default] substitution file defining 1.2.0+1 etc.
300
300
  * NaanIDE_version_builds.txt - [default] contains current build number as decimal string
301
301
  * build/ - [optional] default build output location
302
302
  * NaanIDE.lic - [optional] defines Zulch Laboratories, Inc. and other license info
@@ -602,6 +602,8 @@ closure projConfig(projman, where, projID, local procon, fs, configpath) {
602
602
  //
603
603
  procon.run = closure run(stage, buildonly,
604
604
  local error, rules, builder, buildpath, buildnum) {
605
+ if procon.builder
606
+ return (list(Error("build in progress")))
605
607
  `(error, rules) = buildrules(stage)
606
608
  if error
607
609
  return (list(error))
@@ -615,6 +617,7 @@ closure projConfig(projman, where, projID, local procon, fs, configpath) {
615
617
  `(error, builder) = Builder(rules, rules.operation, fs, "", buildpath)
616
618
  if error
617
619
  return (list(error))
620
+ procon.builder = builder
618
621
  `(error, buildnum) = builder.increment()
619
622
  if error {
620
623
  ErrorDebuglog("build number increment failed", error)
@@ -631,6 +634,7 @@ closure projConfig(projman, where, projID, local procon, fs, configpath) {
631
634
  `(error) = builder.run(projman.track)
632
635
  }
633
636
  builder.destroy()
637
+ procon.builder = false
634
638
  if error
635
639
  list(error)
636
640
  else
@@ -650,6 +654,8 @@ closure projConfig(projman, where, projID, local procon, fs, configpath) {
650
654
  //
651
655
  procon.publish = closure publish(stage,
652
656
  local error, rules, where, publish, outOptions, outfs, inpath) {
657
+ if procon.builder
658
+ return (list(Error("cannot publish while building")))
653
659
  `(error, rules) = buildrules(stage)
654
660
  if error
655
661
  return (list(error))
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * column positioning: // // !
7
7
  *
8
- * Copyright (c) 2021 by Richard C. Zulch
8
+ * Copyright (c) 2021-2024 by Richard C. Zulch
9
9
  *
10
10
  */
11
11
 
@@ -480,7 +480,7 @@ closure DebugNub(local nub, debug, paused) {
480
480
  locals = localist(frame)
481
481
  ns = frame.3.1.namespace
482
482
  if ns !== nsactive().0 {
483
- mod = module.owner.findModule(ns)
483
+ mod = module.owner.find(function(mod){ mod.namespace === ns })
484
484
  if mod {
485
485
  gohome = makeActivator(nsactive())
486
486
  chns(mod.id)
@@ -458,7 +458,7 @@ closure DebugRemote(commander, local remoter, here, there, library) {
458
458
  // .undefined
459
459
  // Execute methods remotely, returning a standard `(error, data) tuple.
460
460
 
461
- remoter[".undefined"] = closure remoter args {
461
+ remoter@[".undefined"] = closure remoter args {
462
462
  closure (selector, arglist, local error, context, result) {
463
463
  if !there[selector] { // not over there yet
464
464
  proc = here[selector]
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * column positioning: // // !
7
7
  *
8
- * Copyright (c) 2020-2021 by Richard C. Zulch
8
+ * Copyright (c) 2020-2024 by Richard C. Zulch
9
9
  *
10
10
  */
11
11
 
@@ -18,6 +18,7 @@
18
18
  */
19
19
 
20
20
  closure ExecutorBase(track, type, name, local exec) {
21
+ global()
21
22
  exec = new(object, this)
22
23
  exec.type = type
23
24
  exec.name = name
@@ -36,7 +37,7 @@ closure ExecutorBase(track, type, name, local exec) {
36
37
  // Send a message to the running instance and return a nonce to wait on. If data is not a
37
38
  // dictionary then it is ignored.
38
39
 
39
- exec.execSend = closure execSend(msg, data, local pending) {
40
+ exec.execSend = closure execSend(msg, data, local pending, error) {
40
41
  if !dictionary(data)
41
42
  data = { }
42
43
  data.xmsg = msg
@@ -44,7 +45,9 @@ closure ExecutorBase(track, type, name, local exec) {
44
45
  pending = new(nonce)
45
46
  pending.msgout = data
46
47
  exec.pending[data.xid] = pending
47
- exec.PostMessage(data)
48
+ `(error) = exec.PostMessage(data)
49
+ if error
50
+ pending.signal(Error("execSend failed", error))
48
51
  pending
49
52
  }
50
53
 
@@ -62,10 +65,19 @@ closure ExecutorBase(track, type, name, local exec) {
62
65
  pending.signal(list(false, data))
63
66
  }
64
67
 
68
+ // execFailed
69
+ // Communication has failed.
70
+
71
+ exec.execFailed = closure execFailed(error, local xid, pending) {
72
+ exec.pending.error = error
73
+ for `(xid, pending) in exec.pending
74
+ pending.signal(list(error))
75
+ }
76
+
65
77
  // execClosed
66
78
  // Note that the connection has closed, so signal all the waiters.
67
79
 
68
- exec.execClosed = closure execClosed(error, local xid) {
80
+ exec.execClosed = closure execClosed(error, local xid, pending) {
69
81
  for `(xid, pending) in exec.pending {
70
82
  pending.signal(list(error))
71
83
  pending[xid] = undefined
@@ -76,18 +88,25 @@ closure ExecutorBase(track, type, name, local exec) {
76
88
  // Return a context object for the instance, or an error, in a result tuple. Currently this does
77
89
  // not ever reclaim old contexts on the instance end of things, but it should. ###
78
90
 
79
- exec.context = closure context(local context, retries, ms) {
91
+ exec.context = closure context(local error, retries, ms, delta, excon) {
80
92
  retries = 0
81
93
  ms = milliseconds()
94
+ delta = 1
82
95
  while !exec.pending.ready {
83
- exec.PostMessage({
96
+ `(error) = exec.PostMessage({
84
97
  xmsg: "xstart"
85
98
  })
99
+ if error
100
+ return (Error("exec.context failed", error))
86
101
  if ++retries > 100
87
- return (list(Error("timeout creating remote worker context", type, name)))
88
- sleep(100) }
89
- context = ExecutorContext(exec, exec.nextContextID++)
90
- context.register()
102
+ return (list(Error("timeout creating remote context", type, name)))
103
+ if exec.pending.error
104
+ return (list(Error("cannot create remote context", exec.pending.error)))
105
+ sleep(delta)
106
+ if delta < 100
107
+ delta *= 2 }
108
+ excon = ExecutorContext(exec, exec.nextContextID++)
109
+ excon.register()
91
110
  }
92
111
 
93
112
  // attention
@@ -117,6 +136,7 @@ closure ExecutorBase(track, type, name, local exec) {
117
136
  */
118
137
 
119
138
  closure ExecutorContext(exec, contextID, local context, util) {
139
+ global(apply)
120
140
  context = new(object, this)
121
141
  util = modlist().Util.exports
122
142
 
@@ -172,13 +192,35 @@ closure ExecutorContext(exec, contextID, local context, util) {
172
192
  context.evalq = macro evalq(expr, put, get) {
173
193
  evalf(expr, eval(put), eval(get))
174
194
  }
195
+
196
+ // apply
197
+ // Apply list of arguments to the specified procure evaluated remotely. If the procedure is a
198
+ // not a tuple then this uses the tuple definition if there is one.
199
+
200
+ context.apply = closure applyf(proc, args, put, get) {
201
+ if !tuple(proc) && tuple(proc.proc)
202
+ proc = proc.proc
203
+ evalf(list(apply, proc, args), eval(put), eval(get))
204
+ }
205
+
206
+ // call
207
+ // Call the specified function remotely using local arguments:
208
+ // context.call(proc, args...)
175
209
 
210
+ context.call = macro callf args {
211
+ closure (proc, arglist, local eargs) {
212
+ while arglist
213
+ push(eval(pop(arglist)), eargs)
214
+ applyf(eval(proc), eargs)
215
+ } (pop(args), reverse(args))
216
+ }
217
+
176
218
  // rpc
177
219
  // Execute a remote procedure call without package translation, to reduce overhead. This will not
178
220
  // properly transfer Naan-specific types like symbols, and the results will be native JavaScript.
179
221
  // The procedure must be specified as a symbol or string.
180
222
 
181
- context.rpc = closure rpc(proc, args, local data, result) {
223
+ context.rpc = closure rpc(proc, args, local data, pending, result) {
182
224
  data = {
183
225
  contextID: contextID
184
226
  proc: proc
@@ -187,8 +229,12 @@ closure ExecutorContext(exec, contextID, local context, util) {
187
229
  result = pending.wait()
188
230
  if result.1.error
189
231
  list(Error(result.1.error))
190
- else
191
- list(false, totuple(result.1.result))
232
+ else {
233
+ result = result.1.result
234
+ if Array.isArray(result)
235
+ result = totuple(result)
236
+ list(false, result)
237
+ }
192
238
  }
193
239
 
194
240
  // finis
@@ -225,6 +271,7 @@ closure ExecutorContext(exec, contextID, local context, util) {
225
271
  */
226
272
 
227
273
  closure ExecutorParallel(options, local maxcount, basename, parallel, exqueue, crValue) {
274
+ global()
228
275
  maxcount = options.maxcount
229
276
  if !maxcount
230
277
  maxcount = 4 // default is 4 workers
@@ -251,7 +298,7 @@ closure ExecutorParallel(options, local maxcount, basename, parallel, exqueue, c
251
298
  return (false)
252
299
  parallel.workers[workdex] = false // reserve our spot
253
300
  name = basename.concat("-", workdex+1)
254
- worker = App.nide.track.spawn("Local", name, "ExecutorParallel")
301
+ worker = App.nide.track.spawn("Local", name, "ExecutorParallel") // ### App.nide.track.spawn may not exist
255
302
  if worker.0 { // ### wasting space on workers list
256
303
  debuglog("execParallel: cannot spawn worker:", ErrorString(worker.0))
257
304
  return (false)
@@ -401,6 +448,7 @@ closure ExecutorParallel(options, local maxcount, basename, parallel, exqueue, c
401
448
  */
402
449
 
403
450
  closure ExecutorTracker(local xtrak) {
451
+ global()
404
452
  xtrak = new(object, this)
405
453
  xtrak.instances = []
406
454
  xtrak.watchers = []
@@ -451,7 +499,7 @@ closure ExecutorTracker(local xtrak) {
451
499
  // enumlist
452
500
  // Enumerate the list of executors.
453
501
 
454
- xtrak.enumlist = function enumlist(local results) {
502
+ xtrak.enumlist = function enumlist(local results, instance) {
455
503
  results = []
456
504
  for instance in xtrak.instances
457
505
  results.push(instance.executor)
@@ -509,6 +557,7 @@ closure ExecutorTracker(local xtrak) {
509
557
  */
510
558
 
511
559
  closure execLambda(track, url, name, workerID, local target, nlregex, clientID, serverID) {
560
+ global()
512
561
  target = ExecutorBase(track, "Lambda", name)
513
562
  clientID = "DebugWorkers"
514
563
  serverID = "Workers"
@@ -670,11 +719,15 @@ closure execLambda(track, url, name, workerID, local target, nlregex, clientID,
670
719
  // Post a message to the target.
671
720
  //
672
721
 
673
- target.PostMessage = function PostMessage(data) {
722
+ target.PostMessage = function PostMessage(data, local result) {
723
+ result = connect()
724
+ if result.0
725
+ return (result)
674
726
  target.ws.send({ // send user-entered text to worker
675
727
  id: "msgin",
676
728
  data: data
677
729
  })
730
+ list(false, { posted: true })
678
731
  }
679
732
 
680
733
  //
@@ -753,7 +806,6 @@ closure execLambda(track, url, name, workerID, local target, nlregex, clientID,
753
806
  */
754
807
 
755
808
  function execInit(local manifest) {
756
-
757
809
  manifest = `(ExecutorBase, ExecutorContext, ExecutorParallel, ExecutorTracker, execLambda, execInit)
758
810
 
759
811
  Naan.module.build(module.id, "executors", function(modobj, compobj) {