@naanlang/naan 1.2.1 → 1.3.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 (35) hide show
  1. package/LICENSE.md +1 -1
  2. package/README.md +1 -1
  3. package/bin/index.js +5 -2
  4. package/dist/env_web.js +11 -1
  5. package/dist/naan.min.js +4 -4
  6. package/frameworks/browser/https_request.nlg +2 -0
  7. package/frameworks/browser/sworker.js +23 -23
  8. package/frameworks/browser/terminals.nlg +123 -21
  9. package/frameworks/browser/workers.nlg +4 -1
  10. package/frameworks/browser/ws_client.nlg +5 -3
  11. package/frameworks/client/apiclient.nlg +8 -6
  12. package/frameworks/client/relaycon.nlg +3 -1
  13. package/frameworks/node/apiserver.nlg +190 -4
  14. package/frameworks/node/worker.nlg +112 -10
  15. package/frameworks/node/ws_client.nlg +5 -3
  16. package/frameworks/project/build.nlg +3 -1
  17. package/frameworks/project/projects.nlg +1 -1
  18. package/frameworks/running/executors.nlg +3 -1
  19. package/frameworks/running/taskexec.nlg +5 -1
  20. package/lib/browser/env_web.js +17 -7
  21. package/lib/browser/env_webworker.js +22 -3
  22. package/lib/core/naanlib.js +4 -4
  23. package/lib/env_node.js +15 -4
  24. package/lib/env_nodeworker.js +21 -1
  25. package/package.json +8 -5
  26. package/plugins/serviceAws/aws/aws-sdk-node.min.js +2 -2
  27. package/plugins/serviceAws/aws/aws-sdk.min.js +3 -2
  28. package/plugins/serviceAws/aws_dynamo.nlg +19 -19
  29. package/plugins/serviceYC/generic_api.yaml +36 -0
  30. package/plugins/serviceYC/naanide-relay-index.js +148 -0
  31. package/plugins/serviceYC/serviceYC.nlg +60 -0
  32. package/plugins/serviceYC/yc_apigateway.nlg +205 -0
  33. package/plugins/serviceYC/yc_function.nlg +163 -0
  34. package/plugins/serviceYC/yc_vm.nlg +207 -0
  35. package/test/test_07_lingo.nlg +1 -1
@@ -83,7 +83,16 @@ closure APIServer(options, local server, chroot) {
83
83
  if server.guid
84
84
  server.oururl = server.oururl.concat("?guid=", server.guid)
85
85
  App.api = server // used remotely by client/relaycon.nlg
86
- server.ws = WebSocketServer(server)
86
+ if options.gateway { // processes at /wsgw endpoint
87
+ server.ws = WebSocketGatewayServer()
88
+ `(error) = server.ws.open(server, options.gateway)
89
+ if error {
90
+ error = Error("APIServer: cannot establish websocket gateway", options.gateway, error)
91
+ ErrorDebuglog(error)
92
+ return (list(error))
93
+ }
94
+ } else // default is native websocket protocols
95
+ server.ws = WebSocketServer(server)
87
96
  WesRelay(server) // registers as subsystem
88
97
  server.psm = psmServerMod.MakeServerPSM(server, options.name)
89
98
  `(error, hostfs) = server.psm.conns.NodeFS.connect("Host", "NideFS", list(""))
@@ -267,7 +276,7 @@ closure APIServer(options, local server, chroot) {
267
276
 
268
277
  server.app.all("/psm", closure(req, res, local guid) {
269
278
  guid = req.get("x-naanlang-api-guid")
270
- if server.guid && guid != server.guid && req.method !== "OPTIONS"
279
+ if server.guid && guid != server.guid
271
280
  res.status(403).send("unauthorized")
272
281
  else if !server.hostfs
273
282
  res.status(503).send("host filesystem not available")
@@ -275,12 +284,187 @@ closure APIServer(options, local server, chroot) {
275
284
  server.hostfs.api(req, res)
276
285
  })
277
286
 
287
+ // "/wsgw"
288
+
289
+ server.app.all("/wsgw", closure(req, res) {
290
+ if !server.ws.requested
291
+ res.status(503).send("websocket gateway not available")
292
+ else
293
+ server.ws.requested(req, res)
294
+ res.set('Content-Type', 'application/json')
295
+ res.status(200).send("")
296
+ })
297
+
278
298
  // finis
279
299
 
280
300
  server
281
301
  };
282
302
 
283
303
 
304
+ /*
305
+ * WebSocketGatewayServer
306
+ *
307
+ * Process websocket operations forwarded by an API Gateway. This connects to the specified gateway
308
+ * component to process the details.
309
+ *
310
+ * Gateway handler methods:
311
+ * handler = Gateway(server) -- constructor, returns gateway object
312
+ * `(error) = open(wsgate) -- initialize for any API Gateway
313
+ * `(error, data) = requested(req, res) -- process incoming requests
314
+ * `(error) = send(connID, message) -- send a message
315
+ * close() -- close the gateway for all connections
316
+ *
317
+ */
318
+
319
+ closure WebSocketGatewayServer(local wsgate) {
320
+ wsgate = new(object, this)
321
+ wsgate.conns = {} // our connections indexed by connID
322
+ wsgate.subsystems = { } // registered subsystems
323
+
324
+ // open
325
+ //
326
+ // Open the server with the specified gateway.
327
+ //
328
+ wsgate.open = closure open(server, gatewayPath) {
329
+ try {
330
+ wsgate.handler = require(gatewayPath).Gateway(server)
331
+ result = wsgate.handler.open()
332
+ if result.0
333
+ return (list(result.0))
334
+ } catch {
335
+ return (list(exception))
336
+ }
337
+ wsgate.guid = server.guid
338
+ list(false, { ok: true })
339
+ }
340
+
341
+ // requested
342
+ //
343
+ // We have an incoming request translated from WS by the API Gateway. This finds out from our
344
+ // gateway handler the client this pertains to and connects/disconnects/messages as appropriate.
345
+ //
346
+ wsgate.requested = closure requested(req, res, local error, data, conn, message) {
347
+ // reqOpen
348
+ // Get our connection or make one.
349
+ function reqOpen(connID) {
350
+ conn = wsgate.conns[connID]
351
+ if !conn {
352
+ conn = WebSocketConn(wsgate, connID)
353
+ wsgate.conns[connID] = conn
354
+ for serverID in wsgate.subsystems
355
+ wsgate.subsystems[serverID].wsOpen(conn)
356
+ }
357
+ conn
358
+ }
359
+
360
+ `(error, data) = wsgate.handler.requested(req, res)
361
+ if data.op == "connect"
362
+ conn = reqOpen(data.connID)
363
+ else if data.op == "message"
364
+ reqOpen(data.connID).msgin(data.message)
365
+ else if data.op == "disconnect" {
366
+ conn = wsgate.conns[data.connID]
367
+ wsgate.conns[data.connID] = undefined
368
+ if conn {
369
+ conn.close(data.code, data.reason)
370
+ for serverID in wsgate.subsystems
371
+ wsgate.subsystems[serverID].wsClose(conn)
372
+ }
373
+ }
374
+ }
375
+
376
+ // close
377
+ //
378
+ // Respond to server closing.
379
+ //
380
+ wsgate.close = function close(conn, local serverID) {
381
+ wsgate.handler.close()
382
+ wsgate.handler = false
383
+ for serverID in wsgate.subsystems
384
+ wsgate.subsystems[serverID].wsClose()
385
+ }
386
+
387
+ // finis
388
+ wsgate
389
+ };
390
+
391
+
392
+ /*
393
+ * WebSocketConn
394
+ *
395
+ * WebSocketConn objects represent a connection with a particular client. These are created when
396
+ * a new client connects. The protocol is JSON with the top level being a dictionary having the
397
+ * following static keys specified by the client:
398
+ * serverID: defined by the server, this string defines where requests should be routed.
399
+ * clientID: defined by the client, this string defines where replies should be sent.
400
+ * These are class (code) designators. Further keys can be defined to identify a specific instance on
401
+ * the client or server.
402
+ *
403
+ */
404
+
405
+ closure WebSocketConn(wsgate, connID, local wsconn) {
406
+ global()
407
+ wsconn = new(object, this)
408
+ wsconn.info = {
409
+ destID: UUID()
410
+ }
411
+
412
+ // msgin
413
+ //
414
+ // This finds the appropriate target for an incoming message, which can be a relay message that
415
+ // we send on to the next node on the network.
416
+ //
417
+ wsconn.msgin = closure msgin(message, local error) {
418
+ if wsgate.guid && message.guid != wsgate.guid
419
+ error = Error("unauthorized")
420
+ else if message.init {
421
+ if message.init.destID {
422
+ wsconn.relay = true
423
+ wsgate.subsystems.Relay.wsChangeDestID(conn, wsconn.info.destID, message.init.destID)
424
+ }
425
+ wsconn.info = merge(wsconn.info, message.init)
426
+ }
427
+ else if message.respOp && wsgate.subsystems.Relay
428
+ wsgate.subsystems.Relay.wsReceive(message, wsconn) // with respOp, must be a relay
429
+ else if message.clientID && wsgate.subsystems[message.serverID]
430
+ wsgate.subsystems[message.serverID].wsReceive(message, wsconn)
431
+ else
432
+ error = Error("can't route message")
433
+ if error
434
+ ErrorDebuglog("WebSocketConn.msgin error:", error)
435
+ }
436
+
437
+ // on close
438
+ //
439
+ // Connection has closed.
440
+ //
441
+ wsconn.close = closure close(code, reason) {
442
+ if code != 1000 && code != 1001 && code != 1005 && code != 1006
443
+ debuglog("WebSocketConn: connection has closed:", code, reason, JSONstringify(wsconn.info))
444
+ wsconn.closed = {
445
+ code: code
446
+ reason: reason
447
+ }
448
+ }
449
+
450
+ // send
451
+ //
452
+ // Send a dictionary, which must have a clientID defined.
453
+ //
454
+ wsconn.send = function send(message) {
455
+ if !message.clientID || !message.serverID
456
+ throw("WebSocketConn.send: client and server IDs required")
457
+ if wsgate.closed
458
+ list(Error("cannot send - websocket closed"))
459
+ wsgate.handler.send(connID, JSONstringify(message))
460
+ list(false, { ok: true })
461
+ }
462
+
463
+ // finis
464
+ wsconn
465
+ };
466
+
467
+
284
468
  /*
285
469
  * WebSocketServer
286
470
  *
@@ -322,8 +506,9 @@ closure WebSocketServer(server, local websocks) {
322
506
  })
323
507
 
324
508
  // closeConn
509
+ //
325
510
  // respond to connection closing
326
-
511
+ //
327
512
  websocks.closeConn = function closeConn(conn, local serverID) {
328
513
  websocks.conns = websocks.conns.filter(function(item) {
329
514
  item !== conn
@@ -641,7 +826,8 @@ function apisLoadLibs() {
641
826
  };
642
827
 
643
828
  function ApisInit(local manifest) {
644
- manifest = `(APIServer, WebSocketServer, WesCon, WesRelay, apisLoadLibs, ApisInit)
829
+ manifest = `(APIServer, WebSocketGatewayServer, WebSocketConn, WebSocketServer, WesCon, WesRelay,
830
+ apisLoadLibs, ApisInit)
645
831
 
646
832
  Naan.module.build(module.id, "apiserver", function(modobj, compobj) {
647
833
  require("./node.nlg")
@@ -18,7 +18,7 @@
18
18
  *
19
19
  */
20
20
 
21
- closure workCurrent(workerID, startup, local current) {
21
+ closure workCurrent(workco, workerID, startup, local current) {
22
22
  global()
23
23
  current = new(object, this)
24
24
  current.workerID = workerID
@@ -31,9 +31,22 @@ closure workCurrent(workerID, startup, local current) {
31
31
  debugout: ONdebugOut.proc
32
32
  messageout: ONmessageOut.proc
33
33
  })
34
+ current.guid = 1
35
+ current.host = js.t // our magic Naan controller reference
34
36
 
35
- current.host = js.t // our magic Naan controller reference
36
-
37
+ // replyHook
38
+ //
39
+ // Intercept the ReplyMessage function to see if we should handle it.
40
+ //
41
+ function replyHook(data) {
42
+ if data._guid
43
+ workco.uworkers[data._guid].msgport.postMessage({
44
+ id: "targetreceive"
45
+ data: data
46
+ })
47
+ }
48
+ current.host.OnReplyHook(replyHook.proc)
49
+
37
50
  // ONtextOut
38
51
  // Process console text output by Naan.
39
52
 
@@ -79,6 +92,8 @@ closure workCurrent(workerID, startup, local current) {
79
92
  // Process message output.
80
93
 
81
94
  function ONmessageOut(data) {
95
+ if data._guid
96
+ return // already replied
82
97
  sendAll({
83
98
  id: "targetout",
84
99
  data: data
@@ -98,6 +113,7 @@ closure workCurrent(workerID, startup, local current) {
98
113
  // Notify all links that we have terminated.
99
114
 
100
115
  function exitNotify(exitCode, local link) {
116
+ workco.uworkers[current.guid] = undefined
101
117
  for link in current.links
102
118
  link.exitWorker(exitCode)
103
119
  }
@@ -157,7 +173,7 @@ closure workCurrent(workerID, startup, local current) {
157
173
  if !(linkref.link eq thislink)
158
174
  linkref.responder(msg)
159
175
  }
160
-
176
+
161
177
  //
162
178
  // vitalUpdate
163
179
  //
@@ -172,6 +188,81 @@ closure workCurrent(workerID, startup, local current) {
172
188
  };
173
189
 
174
190
 
191
+ /*
192
+ * workMainProxy
193
+ *
194
+ * Make a nodejs proxy representing the main thread. This runs inside the worker and allows it
195
+ * to do remote execution with Main.
196
+ *
197
+ */
198
+
199
+ closure workMainProxy(track, name, options, local target) {
200
+ global(js)
201
+ target = require("../running/executors.nlg").ExecutorBase(track, "mainProxy", name)
202
+ target.name = name
203
+
204
+ //
205
+ // target.PostMessage
206
+ //
207
+ // Post a message to the main thread.
208
+ //
209
+ target.PostMessage = function PostMessage(data) {
210
+ js.t.DispatchMessage(data)
211
+ list(false, { posted: true })
212
+ }
213
+
214
+ //
215
+ // onreceive
216
+ //
217
+ // Process a received message from the main thread, typically in response.
218
+ //
219
+ function onreceive(data) {
220
+ target.execReceived(data)
221
+ }
222
+
223
+ // destroy
224
+ //
225
+ // Destroy the execution instance.
226
+ //
227
+ target.destroy = function destroy() {
228
+ target.execClosed(Error("instance destroyed"))
229
+ track.delete(target)
230
+ }
231
+
232
+ js.t.OnReceive(onreceive.proc)
233
+ track.add(target)
234
+
235
+ // finis
236
+
237
+ list(false, target)
238
+ };
239
+
240
+
241
+ /*
242
+ * WorkerToMain
243
+ *
244
+ * Connect our worker to the main thread, returning a result tuple with an executor representing
245
+ * Main. Use this to create a context object for Main, allowing remote evaluation.
246
+ *
247
+ */
248
+
249
+ closure WorkerToMain(local nideRunning, track) {
250
+ // mainProxy -- link tracker to main thread proxy
251
+ function mainProxy(track, name, options) {
252
+ workMainProxy(track, name, options)
253
+ }
254
+
255
+ if !js.g {
256
+ error = Error("WorkerToMain only available in node workers")
257
+ return (list(error))
258
+ }
259
+ nideRunning = require("naanlib:frameworks/running/executors.nlg")
260
+ track = nideRunning.ExecutorTracker()
261
+ track.register(mainProxy, "proxy")
262
+ track.spawn("proxy") // returns result tuple
263
+ };
264
+
265
+
175
266
  /*
176
267
  * workThread
177
268
  *
@@ -179,12 +270,13 @@ closure workCurrent(workerID, startup, local current) {
179
270
  *
180
271
  */
181
272
 
182
- closure workThread(workerID, startup, local worker, options, subChannel, deathWatcher) {
273
+ closure workThread(workco, workerID, startup, local worker, options, subChannel, deathWatcher) {
183
274
  global(threads, JSpath)
184
275
  worker = new(object, this)
185
276
  worker.name = startup.name
186
277
  worker.workerID = workerID
187
278
  worker.links = []
279
+ worker.guid = UUID()
188
280
 
189
281
  // create our worker thread
190
282
 
@@ -207,6 +299,7 @@ closure workThread(workerID, startup, local worker, options, subChannel, deathWa
207
299
  // Notify all links that we have terminated.
208
300
 
209
301
  function exitNotify(exitCode, local link) {
302
+ workco.uworkers[worker.guid] = undefined
210
303
  for link in worker.links
211
304
  link.exitWorker(exitCode)
212
305
  worker.links = []
@@ -262,6 +355,12 @@ closure workThread(workerID, startup, local worker, options, subChannel, deathWa
262
355
  worker.links[0].responder({ // only send this to first link
263
356
  id: "started" // don't want multiple initializers
264
357
  }) }
358
+ else if msg.id == "targetsend" {
359
+ msg.data._guid = worker.guid
360
+ js.t.DispatchMessage({
361
+ id: "targetin"
362
+ data: msg.data
363
+ }) }
265
364
  else {
266
365
  if msg.id == "status"
267
366
  worker.lastStatus = milliseconds()
@@ -403,6 +502,7 @@ closure workController(api, local workco) {
403
502
  workco = new(object, this)
404
503
  workco.serverID = "Workers" // our serverID, used for communications
405
504
  workco.workers = { } // dictionary of workers by workerID
505
+ workco.uworkers = { } // dictionary of workers by GUID
406
506
  workco.links = new(weakmap)
407
507
  api.Register(workco.serverID, workco)
408
508
 
@@ -487,9 +587,10 @@ closure workController(api, local workco) {
487
587
  else if message.workerOp == "spawn" {
488
588
  if !worker { // create worker if needed
489
589
  if message.workerID == "NideServer"
490
- worker = workco.current = workCurrent(message.workerID, message.payload)
590
+ worker = workco.current = workCurrent(workco, message.workerID, message.payload)
491
591
  else
492
- worker = workThread(message.workerID, message.payload)
592
+ worker = workThread(workco, message.workerID, message.payload)
593
+ workco.uworkers[worker.guid] = worker
493
594
  workco.workers[message.workerID] = worker }
494
595
  if !link
495
596
  makeLink(message, conn) // make sure we have a link for responses
@@ -521,7 +622,7 @@ closure workController(api, local workco) {
521
622
  return } }
522
623
  error = Error("invalid parameters", message.workerOp, message.workerID)
523
624
  }
524
- call(makeSender(conn, message), "error", [error])
625
+ ErrorDebuglog("workController.wsReceive error:", error)
525
626
  }
526
627
 
527
628
  //
@@ -562,12 +663,13 @@ closure workController(api, local workco) {
562
663
  */
563
664
 
564
665
  function workInit(local manifest) {
565
-
566
- manifest = `(workCurrent, workThread, workLink, workController, workInit)
666
+ manifest = `(workCurrent, workMainProxy, WorkerToMain, workThread, workLink, workController,
667
+ workInit)
567
668
 
568
669
  Naan.module.build(module.id, "worker", function(modobj, compobj) {
569
670
  require("./node.nlg")
570
671
  compobj.manifest = manifest
571
672
  modobj.exports.WorkerController = workController
673
+ modobj.exports.WorkerToMain = WorkerToMain
572
674
  })
573
675
  }();
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * column positioning: // // !
7
7
  *
8
- * Copyright (c) 2023 by Richard C. Zulch
8
+ * Copyright (c) 2023-2024 by Richard C. Zulch
9
9
  *
10
10
  */
11
11
 
@@ -25,6 +25,8 @@
25
25
  * onmessage: <proc>(e, message) // message received
26
26
  * onerror: <proc>(e) // error occurred
27
27
  * 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
28
30
  * }
29
31
  *
30
32
  */
@@ -45,9 +47,9 @@ closure WebSocket(host, options, local wsock) {
45
47
  if error
46
48
  return (list(error))
47
49
  if options.wss
48
- url = "wss://${host}"
50
+ url = "wss://${host}${options.querys||''}"
49
51
  else
50
- url = "ws://${host}"
52
+ url = "ws://${host}${options.query||''}"
51
53
  wsock.ws = xnew(ws.default, url)
52
54
  pending = new(nonce)
53
55
 
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2019-2023 by Richard C. Zulch
9
+ * Copyright (c) 2019-2024 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -728,6 +728,7 @@ closure Builder(rules, operation, fs, srcpath, destpath, track, local build, gro
728
728
  tasks: group.tasks
729
729
  readpath: infile,
730
730
  writepath: outfile
731
+ follow: group.follow || undefined
731
732
  }
732
733
  if group.input.startsWith("@")
733
734
  entry.insource = JSpath.join(group.input.substring(1), group.sources[sdex])
@@ -982,6 +983,7 @@ closure Builder(rules, operation, fs, srcpath, destpath, track, local build, gro
982
983
  overwrite: true // overwrite files
983
984
  force: true // ignore modify date
984
985
  idmode: true // preserve ids and mode
986
+ follow: entry.follow // optional follow links
985
987
  })
986
988
  if error // deepcopy returns a list of errors
987
989
  error = error.0 // but we're only doing one item
@@ -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.2.1+1 etc.
299
+ * NaanIDE_version.txt - [default] substitution file defining 1.3.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
@@ -56,9 +56,11 @@ closure ExecutorBase(track, type, name, local exec) {
56
56
 
57
57
  exec.execReceived = closure execReceived(data, local pending) {
58
58
  pending = exec.pending[data.xid]
59
- if !pending {
59
+ if !pending { // not waiting on message
60
60
  if data.xmsg == "xready"
61
61
  exec.pending.ready = true
62
+ if data.xmsg == "xattention"
63
+ { }
62
64
  return
63
65
  }
64
66
  exec.pending[data.xid] = undefined
@@ -28,12 +28,16 @@ closure TaskDispatcher(replier, local contexts, gohome, util) {
28
28
  reply = {
29
29
  xmsg: "error"
30
30
  xid: data.xid
31
+ _guid: data._guid || undefined
31
32
  error: "unknown operation"
32
33
  }
33
34
  try {
34
35
  restorens = makeActivator()
35
36
  if data.xmsg == "xstart"
36
- reply = { xmsg: "xready" } // ready to accept messages
37
+ reply = { // ready to accept messages
38
+ xmsg: "xready"
39
+ _guid: data._guid || undefined
40
+ }
37
41
  else if data.xmsg == "register" {
38
42
  //
39
43
  // register a new context to default namespace
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2017-2022 by Richard C. Zulch
9
+ * Copyright (c) 2017-2024 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -223,7 +223,17 @@ exports.NaanControllerWeb = function() {
223
223
  }, 1);
224
224
  };
225
225
 
226
+ var replyHook;
227
+ this.OnReplyHook = function OnReplyHook(proc) {
228
+ replyHook = proc;
229
+ return (proc);
230
+ };
231
+
226
232
  this.ReplyMessage = function ReplyMessage(data) { // "remote" -> "local"
233
+ if (replyHook)
234
+ setTimeout(function() {
235
+ replyHook(data);
236
+ }, 1);
227
237
  termlist.forEach(function(term) {
228
238
  if (term.OnMessageOut)
229
239
  term.OnMessageOut(data);
@@ -526,7 +536,7 @@ exports.NaanControllerWeb = function() {
526
536
  statedoc.curversion = kStateCurrentVersion;
527
537
  statedoc.firstver = kStateFirstVersion;
528
538
  statedoc.licensee = "MIT-License";
529
- statedoc.verstring = "1.2.1+1";
539
+ statedoc.verstring = "1.3.0+1";
530
540
  statedoc.date = new Date().toISOString();
531
541
  statedoc.prefs = prefs;
532
542
  statedoc.naan = naanlib.saveState(false); // true to optimize, which is a bit slower
@@ -544,7 +554,7 @@ exports.NaanControllerWeb = function() {
544
554
  || statedoc.firstver > kStateCurrentVersion
545
555
  || statedoc.curversion < kStateFirstVersion
546
556
  || statedoc.licensee != "MIT-License"
547
- || statedoc.verstring != "1.2.1+1")
557
+ || statedoc.verstring != "1.3.0+1")
548
558
  {
549
559
  localStorage.removeItem("NaanState_Hosted");
550
560
  return (false);
@@ -603,7 +613,7 @@ exports.NaanControllerWeb = function() {
603
613
  op: "VsiteOpen",
604
614
  name: vsiteName,
605
615
  naancont: contSelf,
606
- title: vsiteName + " 1.2.1+1"
616
+ title: vsiteName + " 1.3.0+1"
607
617
  });
608
618
  }
609
619
  } catch (e) {
@@ -617,7 +627,7 @@ exports.NaanControllerWeb = function() {
617
627
  op: "VsiteClose",
618
628
  name: vsiteName,
619
629
  naancont: contSelf,
620
- title: vsiteName + " 1.2.1+1"
630
+ title: vsiteName + " 1.3.0+1"
621
631
  });
622
632
  termTextOut("\x1b[90m\x1b[3m".concat("\nwindow closed", "\x1b[0m\n"));
623
633
  }
@@ -632,8 +642,8 @@ exports.NaanControllerWeb = function() {
632
642
  naanlib.banner();
633
643
  var hostpath = naanlib.js.r("path").dirname(window.location.href);
634
644
  naanlib.start({
635
- cmd: 'App.version = "1.2.1+1";;\r\n'
636
- + 'App.cache = "f22e60a842b7a65ac309da378d469eed";;\r\n'
645
+ cmd: 'App.version = "1.3.0+1";;\r\n'
646
+ + 'App.cache = "49a9b89fb804111a36026df163e15009";;\r\n'
637
647
  + 'Naan.module.requireQuery({ naanver: App.cache });;\r\n'
638
648
  + 'Naan.module.webparse("naan_init.nlg", "' + hostpath + '", { naanver: App.cache });;\r\n'
639
649
  });
@@ -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-2024 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -83,13 +83,27 @@ exports.NaanControllerWebWorker = function NaanControllerWebWorker() {
83
83
  });
84
84
  return (data);
85
85
  };
86
-
86
+
87
87
  var targetListener;
88
88
  targSelf.OnMessage = function OnMessage(proc) {
89
89
  targetListener = proc;
90
90
  return (proc);
91
91
  };
92
-
92
+
93
+ this.DispatchMessage = function DispatchMessage(data) {
94
+ postMessage({ // target is sending a message
95
+ id: "targetsend",
96
+ data: data
97
+ });
98
+ return (data);
99
+ };
100
+
101
+ var targetReceiver;
102
+ targSelf.OnReceive = function OnReceive(proc) {
103
+ targetReceiver = proc;
104
+ return (proc);
105
+ };
106
+
93
107
  targSelf.ReplyDebugger = function ReplyDebugger(data) {
94
108
  postMessage({ // target is sending debug data
95
109
  id: "debugout",
@@ -149,6 +163,11 @@ exports.NaanControllerWebWorker = function NaanControllerWebWorker() {
149
163
  if (debugListener)
150
164
  debugListener(msg.data); // target received a message
151
165
  }
166
+ else if (msg.id == "targetreceive")
167
+ {
168
+ if (targetReceiver)
169
+ targetReceiver(msg.data);
170
+ }
152
171
  };
153
172
 
154
173
  //