@naanlang/naan 1.5.0 → 1.7.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 (62) hide show
  1. package/LICENSE.md +2 -2
  2. package/README.md +2 -2
  3. package/bin/index.js +7 -3
  4. package/dist/env_web.js +162 -48
  5. package/dist/naan.min.js +11 -11
  6. package/frameworks/browser/https_request.nlg +2 -2
  7. package/frameworks/browser/sworker.js +28 -26
  8. package/frameworks/browser/terminals.nlg +295 -737
  9. package/frameworks/browser/worker.nlg +295 -0
  10. package/frameworks/browser/workers.nlg +6 -6
  11. package/frameworks/browser/ws_client.nlg +15 -10
  12. package/frameworks/client/apiclient.nlg +211 -9
  13. package/frameworks/client/psm_client.nlg +10 -14
  14. package/frameworks/client/relaycon.nlg +87 -161
  15. package/frameworks/common/common.nlg +2 -0
  16. package/frameworks/common/events.nlg +101 -0
  17. package/frameworks/common/repltools.nlg +80 -1
  18. package/frameworks/node/apiserver.nlg +38 -246
  19. package/frameworks/node/filesystem.nlg +31 -6
  20. package/frameworks/node/http_request.nlg +34 -13
  21. package/frameworks/node/https_request.nlg +8 -3
  22. package/frameworks/node/node.nlg +3 -0
  23. package/frameworks/node/pty.nlg +346 -0
  24. package/frameworks/node/shell.nlg +26 -3
  25. package/frameworks/node/worker.nlg +504 -560
  26. package/frameworks/node/ws_client.nlg +2 -2
  27. package/frameworks/project/build.nlg +56 -14
  28. package/frameworks/project/projects.nlg +1 -1
  29. package/frameworks/running/debugcom.nlg +20 -3
  30. package/frameworks/running/debugnub.nlg +33 -11
  31. package/frameworks/running/executors.nlg +40 -285
  32. package/frameworks/running/instances.nlg +394 -0
  33. package/frameworks/running/remote_req.nlg +139 -0
  34. package/frameworks/running/remote_res.nlg +89 -0
  35. package/frameworks/running/sourcecode.nlg +3 -2
  36. package/frameworks/running/taskexec.nlg +16 -59
  37. package/frameworks/service/imp.nlg +751 -0
  38. package/frameworks/service/model.nlg +71 -0
  39. package/frameworks/service/nubnode.nlg +136 -0
  40. package/frameworks/service/nubweb.nlg +124 -0
  41. package/frameworks/service/service.nlg +28 -0
  42. package/frameworks/storage/file_manager.nlg +3 -2
  43. package/lib/browser/env_web.js +169 -55
  44. package/lib/browser/env_webworker.js +89 -31
  45. package/lib/core/naanlib.js +11 -11
  46. package/lib/env_node.js +186 -27
  47. package/lib/env_nodeworker.js +82 -44
  48. package/lib/node_server_init.nlg +149 -0
  49. package/lib/node_worker.js +36 -0
  50. package/lib/node_worker_init.nlg +43 -0
  51. package/package.json +1 -1
  52. package/plugins/openSearch/openSearch.nlg +3 -2
  53. package/plugins/openSearch/os_dynamo.nlg +3 -2
  54. package/plugins/serviceAws/aws_cloudwatchlogs.nlg +44 -2
  55. package/plugins/serviceAws/aws_dynamo.nlg +144 -63
  56. package/plugins/serviceAws/aws_utils.nlg +6 -11
  57. package/plugins/serviceAws/serviceAws.nlg +3 -2
  58. package/plugins/serviceNexara/speechrec.nlg +10 -6
  59. package/test/test_01_core.nlg +2 -2
  60. package/test/test_02_context.nlg +32 -9
  61. package/test/test_03_jsinterop.nlg +3 -3
  62. package/test/test_07_lingo.nlg +1 -1
@@ -6,408 +6,118 @@
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2019-2025 by Richard C. Zulch
9
+ * Copyright (c) 2019-2026 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
13
- /*
14
- * globals
15
- *
16
- */
17
-
18
- gWorkers;
19
-
20
13
 
21
14
  /*
22
15
  * TermHost
23
16
  *
24
- * Make an Nide API Server debugger target.
17
+ * Make a debugger target on our Host NaanIDE Server or a remote one.
25
18
  *
26
19
  * Options:
27
20
  * {
28
- * type: <string> // target category, e.g. "Host", "api.yyy.com"
29
- * workerID: <string> // kind of worker in server subsystem
30
- * dispatcher: <tuple> // dispatcher function for incoming messages
31
- * startup: <dictionary> // startup options
32
- * clientID: <string> // (rare) our identification on client
33
- * serverID: <string> // (rare) server subsystem for us
21
+ * serverID: <string> // target server if not our Host
22
+ * type: <string> // target category, e.g. "Remote", "api.yyy.com"
23
+ * workerID: <string> // kind of worker in server subsystem
24
+ * startup: <dictionary> // startup options
34
25
  * }
35
26
  *
36
27
  */
37
28
 
38
- closure TermHost(track, api, name, options, local target, nlregex, xtarg)
29
+ closure TermHost(track, name, options,
30
+ local relay, error, serverID, targetID, target, xc)
39
31
  {
40
- global(window)
41
- options = merge({
42
- type: "Host"
43
- workerID: "myDebugWorker"
44
- clientID: "DebugWorkers"
45
- serverID: "Workers"
46
- }, options)
47
- target = require("../running/executors.nlg").ExecutorBase(track, options.type, name)
48
- nlregex = RegExp("\\n", "g")
49
- target.workerID = options.workerID
50
- target.msgQueue = []
51
- target.dispatcher = call(options.dispatcher)
52
-
53
- // findWorker
54
- //
55
- function findWorker(testID, local instance) {
56
- for instance in track.instances {
57
- if instance.executor.hostWorkerMatch(api, testID)
58
- return (instance.executor)
59
- }
60
- false
61
- }
62
-
63
- // connect
64
- // Connect with the host if not already connected.
65
- closure connect(local url, pending) {
66
- if target.ws
67
- return(list(false, { connected: true }))
68
- try {
69
- url = xnew(window.URL, api.url)
70
- } catch {
71
- return(list(Error("invalid URL ${api.url}", exception)))
72
- }
73
- if url.protocol == "https:"
74
- url.protocol = "wss:"
75
- else
76
- url.protocol = "ws:"
77
- target.ws = xnew(window.WebSocket, url.href)
78
- pending = new(nonce)
79
-
80
- // onopen
81
- // The WebSocket connection is now open.
82
- target.ws.onopen = function onopen(e, local info, spawner) {
83
- info = {
84
- name: name
85
- workerID: target.workerID
32
+ global(App)
33
+
34
+ // findHostInstance
35
+ //
36
+ // Find the host instance name/type, or create new remote worker. The default for no name/type is
37
+ // the server itself.
38
+ //
39
+ function findHostInstance(relay, name, type,
40
+ local error, found, dest, worker) {
41
+ if !serverID {
42
+ `(error, found) = relay.clients()
43
+ if !error {
44
+ dest = found.find(function(item) { item.name == "NaanIDE Server" && item.type == "Server IMP" })
45
+ if dest
46
+ serverID = dest.destID
47
+ else
48
+ error = Error("cannot locate host")
86
49
  }
87
- if options.dispatcher
88
- info.destID = api.instanceID // valid destination for relay
89
- target.ws.send(window.JSON.stringify({
90
- guid: api.guid
91
- init: info
92
- }))
93
- spawner = new(options.startup) || { }
94
- spawner.name = name
95
- sendControl("spawn", spawner)
96
- pending.signal(list(false, { ok: true }))
97
50
  }
98
-
99
- // onmessage
100
- // A message was received from the worker via WebSockets.
101
- target.ws.onmessage = function onmessage(e, message, local msg) {
102
- try {
103
- message = new(window.JSON.parse(e.data))
104
- } catch {
105
- if true {
106
- debuglog("can't decode incoming ws message:", exception)
107
- return }
51
+ if !error
52
+ `(error, found) = relay.clients(serverID)
53
+ if !error {
54
+ if name && type {
55
+ dest = found.find(function(item) { item.name == name && item.type == type })
56
+ if dest
57
+ return (list(false, dest.destID)) // found worker
108
58
  }
109
- if message.respOp == "msgout" {
110
- msg = message.payload
111
- if msg.id == "spawned" || msg.id == "workerlist" || msg.id == "termination"
112
- return (hostWorkerUpdate(msg))
113
- if msg.id != "targetout" {
114
- if msg.id == "status" {
115
- target.lastStatus = milliseconds()
116
- target.execSetStatus(msg.status)
117
- }
118
- if target.msgQueue {
119
- if msg.id != "status"
120
- target.msgQueue.push(msg)
121
- return } }
122
- target.doMessage(msg) }
123
- else if message.respOp == "error"
124
- debuglog("worker controller error:", ErrorString(message.payload[0]))
125
- else if message.respOp == "linked"
126
- future(function termLinked(){
127
- if !target.written
128
- target.term.WriteLn("(reconnected)")
129
- }, 1000).run()
130
- else if message.workerOp == "msgin" && message.payload.id == "targetin"
131
- dispatcher(message) // request from Host
132
- else if message.respOp || message.id
133
- debuglog("unknown host response message:", message.respOp || message.id)
134
- }
135
-
136
- // dispatcher
137
- // Respond to a host request while preserving context to permit reply routing
138
- closure dispatcher(message) {
139
- target.dispatcher(message.payload.data, function(reply) {
140
- target.ws.send(window.JSON.stringify({
141
- guid: api.guid
142
- clientID: message.clientID
143
- serverID: message.serverID
144
- workerID: message.workerID
145
- respOp: "msgout"
146
- payload: {
147
- id: "targetout",
148
- data: reply
149
- }
150
- }))
151
- })
152
- }
153
-
154
- // onerror
155
- // An error occurred on the WebSocket connection.
156
- // This can happen in normal operation when the connection times out.
157
- target.ws.onerror = function onerror(e) {
158
- pending.signal(list(Error("websocket failed", e)))
159
- }
160
-
161
- // onclose
162
- // The WebSocket connection was closed.
163
- target.ws.onclose = function onclose(e) {
164
- if e.code != 1000 && e.code != 1001 && e.code != 1005 && e.code != 1006
165
- debuglog("ws close:", e.code, e.reason)
166
- target.execClosed(Error("socket closed", e.code, e.reason))
167
- target.ws = false
168
- }
169
-
170
- pending.wait()
171
- }
172
-
173
- //
174
- // hostWorkerUpdate
175
- //
176
- // New workers created/discovered on this host.
177
- //
178
- function hostWorkerUpdate(msg, local localID, worker) {
179
- if msg.id == "spawned" {
180
- if !findWorker(msg.workerID)
181
- TermHost(track, api, msg.name, { type: options.type, workerID: msg.workerID })
182
- }
183
- else if msg.id == "termination"
184
- track.delete(findWorker(msg.workerID))
185
- else if msg.id == "workerlist" {
186
- for `(localID, worker) in msg.workers
187
- if !findWorker(localID)
188
- TermHost(track, api, worker.name, { type: options.type, workerID: localID })
189
- }
190
- }
191
-
192
- //
193
- // hostWorkerMatch
194
- //
195
- // True iff the specified information matches us.
196
- //
197
- target.hostWorkerMatch = function hostWorkerMatch(testApi, testID) {
198
- testApi === api && testID == target.workerID
199
- }
200
-
201
- //
202
- // doMessage
203
- //
204
- // Process the specified undecoded message.
205
- target.doMessage = function doMessage(msg) {
206
- if msg.id == "textout" {
207
- target.written = true
208
- target.term.Write(msg.text.replace(nlregex, "\r\n")) }
209
- else if msg.id == "debugtext" {
210
- target.written = true
211
- target.debugWrite(msg.text, msg.level) }
212
- else if msg.id == "targetout"
213
- target.execReceived(msg.data)
214
- else if msg.id == "started"
215
- { }
216
- else if msg.id == "typed"
217
- target.term.Write(msg.text.replace(nlregex, "\r\n"))
218
- else if msg.id == "status"
219
- target.statusCB(target, msg.status)
220
- else if msg.id == "debugout"
221
- target.debugger.debugData(target, msg.data)
222
- else
223
- debuglog("unknown host worker message:", msg.id)
224
- }
225
-
226
- //
227
- // sendControl
228
- //
229
- // Send a message to the worker controller.
230
-
231
- target.sendControl = function sendControl(workerOp, payload) {
232
- target.ws.send(window.JSON.stringify({
233
- guid: api.guid
234
- clientID: options.clientID
235
- serverID: options.serverID
236
- workerID: target.workerID
237
- workerOp: workerOp
238
- payload: payload
239
- }))
240
- }
241
-
242
- //
243
- // send
244
- //
245
- // Send a message to the worker.
246
-
247
- target.send = function send(msg) {
248
- sendControl("msgin", msg)
249
- }
250
-
251
- //
252
- // oninterrupt
253
- //
254
-
255
- function oninterrupt() {
256
- send({ // send interrupt to worker
257
- id: "interrupt"
258
- })
259
- }
260
-
261
- //
262
- // onreturn
263
- //
264
-
265
- function onreturn(local text) {
266
- text = target.term.TakeText()
267
- echotext(text)
268
- send({ // send user-entered text to worker
269
- id: "keyline",
270
- text: text
271
- })
272
- }
273
-
274
- // echotext
275
- //
276
- // Echo text to other terminals.
277
-
278
- function echotext(text) {
279
- sendControl("echo", { // send user-entered text to worker
280
- id: "typed",
281
- text: "\x1b[90m\x1b[3m".concat(text, "\x1b[0m\n") // dark-grey, italic
282
- })
283
- }
284
-
285
- // target.KeyText
286
- //
287
- // Send text to the worker, with optional echo to terminal.
288
-
289
- target.KeyText = function KeyText(text, echo) {
290
- if echo {
291
- target.term.WriteLn(text.trim())
292
- echotext(text.trim())
59
+ if !name || !type { // find main instance
60
+ dest = found.find(function(item) { item.name == "NaanIDE Server" && item.type == "NaaN Server" })
61
+ if !dest
62
+ return (list(Error("cannot locate NaanIDE Server")))
63
+ else
64
+ return (list(false, dest.destID))
65
+ }
66
+ dest = found.find(function(item) { item.name == "NaanIDE Server" && item.type == "Node Remote" })
67
+ if !dest
68
+ return (list(Error("cannot locate NaanIDE Server Remote")))
69
+ `(error, xc) = relay.context(dest.destID)
70
+ if !error {
71
+ `(error) = xc.evalq(chns("node"))
72
+ if error
73
+ return (list(Error("cannot switch namespace", error)))
74
+ }
75
+ if !error
76
+ closure (options) {
77
+ `(error, worker) = xc.evalq(NobBot(options), `(options))
78
+ }({
79
+ name: name
80
+ type: options.type
81
+ startup: options.startup
82
+ rootPath: options.rootPath || undefined
83
+ topOriginID: App.imp.us.instanceID
84
+ })
85
+ if error
86
+ error = Error("cannot spawn remote instance", error)
87
+ else
88
+ return (list(false, worker.instanceID))
293
89
  }
294
- send({ // send user-entered text to worker
295
- id: "keyline",
296
- text: text
297
- })
90
+ list(error)
298
91
  }
299
92
 
300
- //
301
- // target.PostMessage
302
- //
303
- // Post a message to the target.
304
- //
305
-
306
- target.PostMessage = function PostMessage(data, local result) {
307
- result = connect()
308
- if result.0
309
- return (result)
310
- send({ // send data to worker
311
- id: "targetin",
312
- data: data
313
- })
314
- list(false, { posted: true })
315
- }
316
-
317
- //
318
- // target.DebugCmd
319
- //
320
- // Post a debug command to the target.
321
- //
322
-
323
- target.DebugCmd = function DebugCmd(data) {
324
- connect()
325
- send({ // send debug command to worker
326
- id: "debugin",
327
- data: data
328
- })
329
- }
93
+ options = merge({
94
+ type: "Host"
95
+ }, options)
330
96
 
331
- //
332
- // target.debugWrite
333
- //
334
- // Write a debug message to the repl.
335
- //
336
-
337
- target.debugWrite = function(text, level) {
338
- text = text.replaceAll("\n", "\r\n")
339
- if (level >= 5)
340
- target.term.WriteLn(text, target.term.Cyan) // cyan for builtin logging
341
- else if (level >= 4)
342
- target.term.WriteLn(text, target.term.Blue) // blue for Naan function logging
343
- else if (level >= 3)
344
- target.term.WriteLn(text, target.term.Red) // light red for warnings
345
- else if (level >= 2)
346
- target.term.WriteLn(text, target.term.Green) // green for Naan.debug.debuglog logging
347
- else
348
- target.term.WriteLn(text, target.term.Red+target.term.Bold) // heavy red for errors
349
- }
97
+ target = findIDEtarget(track, name, options.workerID)
98
+ if target
99
+ return (list(false, target)) // already exists
100
+
101
+ relay = require("naanlib:frameworks/client/relaycon.nlg").RelayCon(App.imp)
102
+ serverID = options.serverID
103
+ if options.workerID == "NideServer" // server main thread
104
+ `(error, targetID) = findHostInstance(relay)
105
+ else
106
+ `(error, targetID) = findHostInstance(relay, name, options.type)
107
+ relay.close()
108
+ if error
109
+ return (list(error))
350
110
 
351
- //
352
- // termAttach
353
- //
354
- // A terminal and/or debugger was attached to our instance.
355
-
356
- target.termAttach = function termAttach(repl, db, statusCB, local msg) {
357
- target.term = repl
358
- target.debugger = db
359
- target.statusCB = statusCB
360
- if target.termSaved {
361
- target.term.StateLoad(target.termSaved)
362
- target.termSaved = false }
363
- else
364
- target.term.Reset()
365
- target.debugger.attach(target)
366
- while target.msgQueue.length > 0 {
367
- for msg in target.msgQueue.splice(0) // all current messages
368
- target.doMessage(msg)
369
- }
370
- target.msgQueue = false
371
- target.term.On("interrupt", oninterrupt.proc)
372
- target.term.On("return", onreturn.proc)
373
- target.term.Focus()
374
- connect() // ensure we are connected
375
- target
376
- }
377
-
378
- //
379
- // termDetach
380
- //
381
- // The terminal is being deattached from our instance.
382
-
383
- target.termDetach = function termDetach(repl, db) {
384
- target.term.On("interrupt")
385
- target.term.On("return")
386
- target.msgQueue = []
387
- target.debugger.detach(target)
388
- target.termSaved = target.term.StateSave()
389
- target.term.Reset()
390
- target.statusCB = false
391
- target.debugger = false
392
- target.term = false
393
- }
394
-
395
- // destroy
396
- //
397
- // Destroy the execution instance.
398
-
399
- target.destroy = function destroy() {
400
- track.delete(target)
401
- termDetach()
402
- sendControl("terminate", { })
403
- target.ws.close(1000) // 1000: normal close
404
- }
111
+ target = require("../running/instances.nlg").InTop(track, {
112
+ name: name
113
+ type: options.type
114
+ })
115
+ target.serverID = serverID // server instanceID
116
+ target.workerID = options.workerID // worker on server instanceID
405
117
 
406
- xtarg = findWorker(target.workerID)
407
- if xtarg
408
- return (list(false, xtarg)) // already exists
409
- track.add(target)
410
- connect() // connect right away to start receiving messages
118
+ `(error) = target.open(targetID)
119
+ if error
120
+ return (list(Error("cannot create InTop", error)))
411
121
 
412
122
  // finis
413
123
 
@@ -424,311 +134,43 @@ closure TermHost(track, api, name, options, local target, nlregex, xtarg)
424
134
  * {
425
135
  * workerID: <string> // kind of worker in server subsystem
426
136
  * // "NaanIDE" for local IDE
427
- * // "NaanIDE-Debug" for browser debug target
428
137
  * startup: <dictionary> // startup options
429
138
  * type: <string> // (rare) target category, e.g. "Local"
430
139
  * }
431
140
  *
432
141
  */
433
142
 
434
- closure TermLocal(track, name, options, local target, nlregex, listener)
143
+ closure TermLocal(track, name, options, local target, worker, wowbot, error)
435
144
  {
436
- global(js, window, gWorkers)
437
- if !dictionary(gWorkers)
438
- gWorkers = {}
145
+ global(js, window)
439
146
  options = merge({
440
147
  type: "Local"
441
148
  }, options)
442
149
  if options.workerID == "NaanIDE" {
443
150
  if !js.t
444
151
  return (list(Error("Cannot access Local IDE executor")))
445
- termInstallVirtualWatcher(track, js.t)
152
+ termInstallVirtualWatcher(track, window)
446
153
  return (termIDE(track, name, options.workerID, js.t)) // connect to IDE execution instance itself
447
154
  }
448
- target = require("../running/executors.nlg").ExecutorBase(track, options.type, name)
449
- nlregex = RegExp("\\n", "g")
450
- target.workerID = options.workerID
451
- target.msgQueue = []
452
- if options.workerID == "NaanIDE-Debug" {
453
- target.worker = window.open(window.location.href, "_blank")
454
- target.worker.addEventListener("beforeunload", function(e) { // our target is closing
455
- destroy()
456
- })
457
- window.addEventListener("beforeunload", function(e) { // we are closing
458
- // ### tell target we are closing (if needed)
459
- })
460
- listener = window
461
- target.guid = 1
462
- } else {
463
- if not window.Worker
464
- return (list(Error("Local environment does not support workers")))
465
- target.worker = xnew(window.Worker, "env_webworker.js")
466
- listener = target.worker
467
- target.guid = UUID()
468
- }
469
- gWorkers[target.guid] = target
470
155
 
471
- // onmessage
472
- // A message was received from the worker.
473
-
474
- listener.addEventListener("message", function(e, msg) {
475
- msg = new(e.data)
476
- if msg.id == "termination" {
477
- destroy()
478
- return }
479
- else if msg.id != "targetout" && msg.id != "loaded" {
480
- if msg.id == "status" {
481
- target.lastStatus = milliseconds()
482
- target.execSetStatus(msg.status)
483
- }
484
- if target.msgQueue {
485
- if msg.id != "status"
486
- target.msgQueue.push(msg)
487
- return } }
488
- target.doMessage(msg)
489
- })
156
+ target = findIDEtarget(track, name, options.workerID)
157
+ if target
158
+ return (list(false, target)) // already exists
490
159
 
491
- // onerror
492
- // The worker had an error.
493
-
494
- target.worker.addEventListener("error", function(e) {
495
- debuglog("TermLocal worker error:", e.message)
496
- target.execClosed(Error("instance error", e.message))
160
+ worker = xnew(window.Worker, "env_webworker.js")
161
+ wowbot = WowBot(worker, {
162
+ name: name
163
+ type: options.workerID
164
+ startup: options.startup
497
165
  })
498
-
499
- //
500
- // oninterrupt
501
- //
502
-
503
- function oninterrupt() {
504
- target.worker.postMessage({ // send interrupt to worker
505
- id: "interrupt"
506
- })
507
- }
508
-
509
- //
510
- // onreturn
511
- //
512
-
513
- function onreturn() {
514
- target.worker.postMessage({ // send user-entered text to worker
515
- id: "keyline",
516
- text: target.term.TakeText()
517
- })
518
- }
519
-
520
- // target.KeyText
521
- //
522
- // Send text to the worker, with optional echo to terminal.
523
-
524
- target.KeyText = function KeyText(text, echo) {
525
- if echo
526
- target.term.WriteLn(text.trim())
527
- target.worker.postMessage({ // send text to worker
528
- id: "keyline",
529
- text: text
530
- })
531
- }
532
-
533
- //
534
- // doMessage
535
- //
536
- // Process the specified undecoded message.
537
-
538
- target.doMessage = function doMessage(msg) {
539
- if msg.id == "textout"
540
- target.term.Write(msg.text.replace(nlregex, "\r\n"))
541
- else if msg.id == "debugtext"
542
- target.debugWrite(msg.text, msg.level)
543
- else if msg.id == "targetout"
544
- target.execReceived(msg.data)
545
- else if msg.id == "status"
546
- target.statusCB(target, msg.status)
547
- else if msg.id == "debugout"
548
- target.debugger.debugData(target, msg.data)
549
- else if msg.id == "download"
550
- FileDownload(xnew(window.File, msg.bits, msg.name, msg.options))
551
- else if msg.id == "loaded" {
552
- if !target.started
553
- target.worker.postMessage({
554
- id: "start",
555
- state: false // resume state, if we have any
556
- altcmd: options.startup.initcmds
557
- workerID: target.workerID
558
- workerGUID: target.guid
559
- })
560
- target.started = true
561
- } else if msg.id == "targetsend" {
562
- msg.data._guid = target.guid
563
- js.t.DispatchMessage(msg.data)
564
- }
565
- }
566
-
567
- //
568
- // target.DebugCmd
569
- //
570
- // Post a debug command to the target.
571
- //
572
-
573
- target.DebugCmd = function DebugCmd(data) {
574
- target.worker.postMessage({ // send data to worker
575
- id: "debugin"
576
- data: data
577
- })
578
- }
579
-
580
- //
581
- // target.PostMessage
582
- //
583
- // Post a message to the target, if it has registered a listener.
584
- //
585
-
586
- target.PostMessage = function PostMessage(data) {
587
- target.worker.postMessage({ // send data to worker
588
- id: "targetin",
589
- data: data
590
- })
591
- list(false, { posted: true })
592
- }
593
-
594
- //
595
- // target.debugWrite
596
- //
597
- // Write a debug message to the repl.
598
- //
599
-
600
- target.debugWrite = function(text, level) {
601
- text = text.replaceAll("\n", "\r\n")
602
- if (level >= 5)
603
- target.term.WriteLn(text, target.term.Cyan) // cyan for builtin logging
604
- else if (level >= 4)
605
- target.term.WriteLn(text, target.term.Blue) // blue for Naan function logging
606
- else if (level >= 3)
607
- target.term.WriteLn(text, target.term.Red) // light red for warnings
608
- else if (level >= 2)
609
- target.term.WriteLn(text, target.term.Green) // green for Naan.debug.debuglog logging
610
- else
611
- target.term.WriteLn(text, target.term.Red+target.term.Bold) // heavy red for errors
612
- }
613
-
614
- //
615
- // termAttach
616
- //
617
- // A terminal was attached to our instance.
618
-
619
- target.termAttach = function termAttach(repl, db, statusCB, local msg) {
620
- target.term = repl
621
- target.debugger = db
622
- target.statusCB = statusCB
623
- if target.termSaved {
624
- target.term.StateLoad(target.termSaved)
625
- target.termSaved = false }
626
- else
627
- target.term.Reset()
628
- target.debugger.attach(target)
629
- while target.msgQueue.length > 0 {
630
- for msg in target.msgQueue.splice(0) // all current messages
631
- target.doMessage(msg)
632
- }
633
- target.msgQueue = false
634
- target.term.On("interrupt", oninterrupt.proc)
635
- target.term.On("return", onreturn.proc)
636
- target.term.Focus()
637
- target
638
- }
639
-
640
- //
641
- // termDetach
642
- //
643
- // The terminal is being deattached from our instance.
644
-
645
- target.termDetach = function termDetach() {
646
- target.term.On("interrupt")
647
- target.term.On("return")
648
- target.msgQueue = []
649
- target.debugger.detach(target)
650
- target.termSaved = target.term.StateSave()
651
- target.statusCB = false
652
- target.debugger = false
653
- target.term = false
654
- }
655
-
656
- // destroy
657
- //
658
- // Destroy the execution instance.
659
-
660
- target.destroy = function destroy() {
661
- gWorkers[target.guid] = undefined
662
- target.execClosed(Error("instance destroyed"))
663
- track.delete(target)
664
- termDetach()
665
- target.worker.terminate() // kill without saving
666
- target.worker = false
667
- }
668
-
669
- track.add(target)
670
-
671
- // finis
672
-
673
- list(false, target)
674
- };
675
-
676
-
677
- /*
678
- * WorkerConnect
679
- *
680
- * Return the existing web worker for a workerGUID, or false if it does not exist.
681
- *
682
- */
683
-
684
- function WorkerConnect(workerGUID) {
685
- gWorkers[workerGUID]
686
- };
687
-
688
-
689
- /*
690
- * termMainProxy
691
- *
692
- * Make a browser proxy representing the main thread. This runs inside the worker and allows it
693
- * to do remote execution with Main.
694
- *
695
- */
696
-
697
- closure termMainProxy(track, name, options, local target) {
698
- global(js)
699
- target = require("../running/executors.nlg").ExecutorBase(track, "mainProxy", name)
700
- target.name = name
701
-
702
- //
703
- // target.PostMessage
704
- //
705
- // Post a message to the main thread.
706
- //
707
- target.PostMessage = function PostMessage(data) {
708
- js.t.DispatchMessage(data)
709
- list(false, { posted: true })
710
- }
711
-
712
- //
713
- // onreceive
714
- //
715
- // Process a received message from the main thread, typically in response.
716
- //
717
- function onreceive(data) {
718
- target.execReceived(data)
719
- }
720
-
721
- // destroy
722
- //
723
- // Destroy the execution instance.
724
- //
725
- target.destroy = function destroy() {
726
- target.execClosed(Error("instance destroyed"))
727
- track.delete(target)
728
- }
729
-
730
- js.t.OnReceive(onreceive.proc)
731
- track.add(target)
166
+ target = require("../running/instances.nlg").InTop(track, {
167
+ name: name
168
+ type: options.type
169
+ workerID: options.workerID
170
+ })
171
+ `(error) = target.open(wowbot.instanceID)
172
+ if error
173
+ return (list(Error("cannot create InTop", error)))
732
174
 
733
175
  // finis
734
176
 
@@ -736,31 +178,6 @@ closure termMainProxy(track, name, options, local target) {
736
178
  };
737
179
 
738
180
 
739
- /*
740
- * WorkerToMain
741
- *
742
- * Connect our worker to the main thread, returning a result tuple with an executor representing
743
- * Main. Use this to create a context object for Main, allowing remote evaluation.
744
- *
745
- */
746
-
747
- closure WorkerToMain(local nideRunning, track) {
748
- // mainProxy -- link tracker to main thread proxy
749
- function mainProxy(track, name, options) {
750
- termMainProxy(track, name, options)
751
- }
752
-
753
- if !js.s {
754
- error = Error("WorkerToMain only available in browser workers")
755
- return (list(error))
756
- }
757
- nideRunning = require("naanlib:frameworks/running/executors.nlg")
758
- track = nideRunning.ExecutorTracker()
759
- track.register(mainProxy, "proxy")
760
- track.spawn("proxy") // returns result tuple
761
- };
762
-
763
-
764
181
  /*
765
182
  * findIDEtarget
766
183
  *
@@ -791,7 +208,7 @@ closure findIDEtarget(track, name, workerID, title, naancont, local target) {
791
208
  */
792
209
 
793
210
  closure termIDE(track, name, workerID, naancont, title, local target, connected) {
794
- global(js, gWorkers)
211
+ global(js)
795
212
  target = findIDEtarget(track, name, workerID, title, naancont)
796
213
  if target { // new naancont on existing target
797
214
  if target.naancont && naancont !== target.naancont
@@ -802,63 +219,66 @@ closure termIDE(track, name, workerID, naancont, title, local target, connected)
802
219
  return (target)
803
220
  }
804
221
  target = require("../running/executors.nlg").ExecutorBase(track, "Local", name)
222
+ target.dispatcher = require("../running/taskexec.nlg").TaskDispatcher(false, false, "termIDE")
805
223
  target.name = name
806
224
  target.title = title
807
225
  target.workerID = workerID
808
226
  target.naancont = naancont
227
+ naancont.ioctl_open(ioctl_callback.proc)
809
228
  connected = []
810
-
811
- // replyHook
229
+
230
+ // PostMessage
812
231
  //
813
- // Intercept the ReplyMessage function to see if we should handle it.
232
+ // Process a message through the dispatcher.
814
233
  //
815
- function replyHook(data) {
816
- if data._guid
817
- gWorkers[data._guid].worker.postMessage({
818
- id: "targetreceive"
819
- data: data
820
- })
234
+ target.PostMessage = function PostMessage(data) {
235
+ target.dispatcher(data, function(reply) {
236
+ target.execReceived(reply)
237
+ })
238
+ list(false, { posted: true })
821
239
  }
822
240
 
823
- // target.DebugCmd
241
+ // ioctl_callback
824
242
  //
825
- // Post a debug command to the target, ourselves.
243
+ // Ioctl from our instance.
826
244
  //
827
- target.DebugCmd = function DebugCmd(data) {
828
- naancont.DispatchDebugger(data)
245
+ function ioctl_callback(op, arg) { // where ioctls go when they have nowhere to go
246
+ debuglog("no-terminal ioctl:", op, arg)
247
+ }
248
+
249
+ // ioctl_event
250
+ //
251
+ target.ioctl_event = function ioctl_event(msg, arg) {
252
+ naancont.ioctl_event(msg, arg)
829
253
  }
830
254
 
831
- // target.PostMessage
255
+ // target.DebugCmd
832
256
  //
833
- // Post a message to the target, ourselves.
257
+ // Post a debug command to the target, ourselves.
834
258
  //
835
- target.PostMessage = function PostMessage(data) {
836
- naancont.DispatchMessage(data)
837
- list(false, { posted: true })
259
+ target.DebugCmd = function DebugCmd(data) {
260
+ naancont.DispatchDebugger(data)
838
261
  }
839
262
 
840
263
  // termAttach
841
264
  //
842
265
  // Create a closure for an attaching terminal.
843
266
  //
844
- target.termAttach = closure termAttach(term, db, statusCB, local connection, inbound) {
267
+ target.termAttach = closure termAttach(term, db, notify, local connection, jsterm, inbound) {
845
268
  connection = new(object, this)
269
+ jsterm = term.jsAcquire() // termIDE must be direct
846
270
 
847
271
  function oninterrupt() {
848
272
  naancont.Interrupt(inbound)
849
273
  }
850
274
 
851
275
  function onreturn() {
852
- naancont.Return(term.TakeText(), inbound)
276
+ naancont.Return(jsterm.TakeText(), inbound)
853
277
  }
854
278
 
855
279
  function onstatus(status) {
856
280
  target.execSetStatus(status)
857
- statusCB(target, status)
858
- }
859
-
860
- function onmessageout(data) {
861
- target.execReceived(data)
281
+ notify.execStatus(target, status)
862
282
  }
863
283
 
864
284
  function ondebugout(data) {
@@ -867,7 +287,7 @@ closure termIDE(track, name, workerID, naancont, title, local target, connected)
867
287
 
868
288
  connection.KeyText = function KeyText(text, echo) { // send text to instance
869
289
  if echo
870
- term.WriteLn(text.trim()) // echo to terminal
290
+ jsterm.WriteLn(text.trim()) // echo to terminal
871
291
  naancont.Return(text, inbound)
872
292
  }
873
293
 
@@ -877,16 +297,15 @@ closure termIDE(track, name, workerID, naancont, title, local target, connected)
877
297
  }
878
298
 
879
299
  connection.WriteLn = function WriteLn(text) {
880
- term.WriteLn(text)
300
+ jsterm.WriteLn(text)
881
301
  }
882
302
 
883
303
  connection.termDetach = function termDetach() { // disconnect the terminal
884
304
  db.detach(target)
885
- term.On("interrupt")
886
- term.On("return")
887
305
  if inbound
888
306
  naancont.Detach(inbound)
889
307
  connected = connected.filter(function(conn) { conn !== connection })
308
+ term.jsRelease()
890
309
  }
891
310
 
892
311
  connection.popup = true // we can handle multiple terminals
@@ -894,17 +313,16 @@ closure termIDE(track, name, workerID, naancont, title, local target, connected)
894
313
  let (replstate) {
895
314
  replstate = naancont.StateGet()
896
315
  if (replstate)
897
- term.StateLoad(replstate)
316
+ jsterm.StateLoad(replstate)
898
317
  else
899
- term.Reset()
900
- term.On("interrupt", oninterrupt.proc)
901
- term.On("return", onreturn.proc)
902
- term.Focus()
318
+ jsterm.Reset()
319
+ jsterm.On("interrupt", oninterrupt.proc)
320
+ jsterm.On("return", onreturn.proc)
321
+ jsterm.Focus()
903
322
  inbound = xnew({
904
- OnMessage: term.OnMessage
905
- StateSave: term.StateSave
323
+ OnMessage: jsterm.OnMessage
324
+ StateSave: jsterm.StateSave
906
325
  OnStatus: onstatus.proc
907
- OnMessageOut: onmessageout.proc
908
326
  OnDebugOut: ondebugout.proc
909
327
  })
910
328
  naancont.Attach(inbound)
@@ -913,7 +331,33 @@ closure termIDE(track, name, workerID, naancont, title, local target, connected)
913
331
  connected.push(connection)
914
332
  db.attach(target)
915
333
  } ()
916
-
334
+
335
+ naancont.ioctl_open(function (op, arg) {
336
+ if op == "sane"
337
+ jsterm.Sane()
338
+ else if op == "raw" {
339
+ if arg // enable raw mode
340
+ jsterm.OnRaw(function(str, origin) {
341
+ ioctl_event("raw-key", {
342
+ key: str
343
+ origin: origin
344
+ })
345
+ })
346
+ else // else disable raw
347
+ jsterm.OnRaw()
348
+ } else if op == "keystroke"
349
+ jsterm.PutKey(arg) // simulate a keystroke
350
+ else if op == "echo"
351
+ jsterm.EchoSet(arg) // echo: 0, 1, 2
352
+ })
353
+
354
+ jsterm.OnResize(function(columns, rows) {
355
+ ioctl_event("term-resize", {
356
+ cols: columns
357
+ rows: rows
358
+ })
359
+ })
360
+
917
361
  // finis
918
362
  connection
919
363
  }
@@ -923,7 +367,9 @@ closure termIDE(track, name, workerID, naancont, title, local target, connected)
923
367
  // Update the naan controller on this target, which means updating for all the terminals.
924
368
  //
925
369
  target.updateController = function updateController(newnc, local conn) {
370
+ naancont.ioctl_close()
926
371
  naancont = newnc
372
+ naancont.ioctl_open(ioctl_callback.proc)
927
373
  target.naancont = newnc
928
374
  for conn in connected {
929
375
  if naancont {
@@ -946,6 +392,7 @@ closure termIDE(track, name, workerID, naancont, title, local target, connected)
946
392
  conn.WriteLn("\x1b[90m\x1b[3m".concat("\ntarget closed", "\x1b[0m"))
947
393
  }
948
394
  target.execReset()
395
+ naancont.ioctl_close()
949
396
  target.naancont = naancont = false
950
397
  }
951
398
 
@@ -992,7 +439,6 @@ closure termIDE(track, name, workerID, naancont, title, local target, connected)
992
439
  track.delete(target)
993
440
  }
994
441
 
995
- js.t.OnReplyHook(replyHook.proc)
996
442
  track.add(target)
997
443
 
998
444
  // finis
@@ -1010,7 +456,7 @@ closure termIDE(track, name, workerID, naancont, title, local target, connected)
1010
456
  */
1011
457
 
1012
458
  closure termInstallVirtualWatcher(track, naancont) {
1013
- global()
459
+ global(js)
1014
460
 
1015
461
  function watchVsites(msg, local error, target) {
1016
462
  if msg.op == "VsiteOpen" {
@@ -1029,7 +475,121 @@ closure termInstallVirtualWatcher(track, naancont) {
1029
475
  }
1030
476
  }
1031
477
 
1032
- require("../running/executors.nlg").TaskOnMessageHook(watchVsites)
478
+ js.t.OnMessage(watchVsites.proc)
479
+ };
480
+
481
+
482
+ /*
483
+ * testTab
484
+ *
485
+ * Quick test of the InTop/InBot connection.
486
+ *
487
+ */
488
+
489
+ closure testTab(destID,
490
+ local comMod, relay, error, found, dest, xc, inbotDestID, intop) {
491
+
492
+ // findBottomID
493
+ //
494
+ // Find the inbot destID that goes with our desired workerID.
495
+ //
496
+ function findBottomID(found, workerID, local item) {
497
+ for item in found
498
+ if item.name == "Play" && item.type == workerID
499
+ return (item.destID)
500
+ false
501
+ }
502
+
503
+ comMod = require("naanlib:frameworks/client/relaycon.nlg")
504
+ relay = comMod.RelayCon(App.imp)
505
+ `(error, found) = relay.clients(destID)
506
+ dest = found.find(function(item) { item.name == "NaanIDE" && item.services.indexOf("naan-remote-responder") >= 0 })
507
+ if !dest
508
+ return (list(Error("cannot locate remoting")))
509
+ `(error, xc)= relay.context(dest.destID)
510
+ if error
511
+ return (list(Error("cannot access remoting", error)))
512
+ inbotDestID = findBottomID(found, "Webworker-Play")
513
+ if !inbotDestID
514
+ return (list(Error("cannot find web worker")))
515
+ intop = require("../running/instances.nlg").InTop(App.nide.track, {
516
+ name: "NaanIDE-Play"
517
+ type: "Relay"
518
+ })
519
+ `(error) = intop.open(inbotDestID)
520
+ if error
521
+ return (list(Error("cannot create InTop", error)))
522
+ list(false, xc, intop)
523
+ };
524
+
525
+
526
+ /*
527
+ * relayTab
528
+ *
529
+ * Open a relay tab for the first peer NaanIDE found on the same server.
530
+ *
531
+ */
532
+
533
+ closure relayTab(destID, local comMod, relay, error, found, dest, xc, reterm, originID, inbot) {
534
+
535
+ // remoteFindTerm
536
+ //
537
+ // Executes on remote to give us a (proxy) terminal.
538
+ //
539
+ function remoteFindTerm(name, workerID, local target) {
540
+ for target in App.nide.track.enumlist()
541
+ if target.name == name && target.workerID == workerID
542
+ return (target)
543
+ false
544
+ }
545
+
546
+ comMod = require("naanlib:frameworks/client/relaycon.nlg")
547
+ relay = comMod.RelayCon(App.imp)
548
+ if !destID {
549
+ `(error, found) = relay.clients()
550
+ if !error {
551
+ dest = found.find(function(item) { item.name == "NaanIDE Server" && item.type == "Server IMP" })
552
+ if !dest
553
+ error = Error("cannot locate server")
554
+ }
555
+ if !error
556
+ `(error, found) = relay.clients(dest.destID)
557
+ if !error {
558
+ dest = found.find(function(item) { item.name == "NaanIDE"
559
+ && item.type == "Client IMP" && item.destID != App.imp.us.instanceID })
560
+ if !dest
561
+ error = Error("cannot locate peer tab")
562
+ else
563
+ destID = dest.destID
564
+ }
565
+ }
566
+ if error
567
+ return (list(Error("cannot locate target", error)))
568
+ `(error, xc) = relay.context(destID)
569
+ if error
570
+ return (list(Error("cannot access remoting", error)))
571
+ `(error) = xc.evalq(chns("browser"))
572
+ if error
573
+ return (list(Error("cannot switch namespace", error)))
574
+ `(error, reterm) = xc.evalq(remoteFindTerm("NaanIDE", "NaanIDE"), `(remoteFindTerm))
575
+ if error
576
+ return (list(Error("cannot get remote terminal", error)))
577
+ originID = App.imp.us.instanceID
578
+ `(error, inbot) = xc.evalq(MewBot(window, {
579
+ name: "mewbot"
580
+ type: reterm.workerID
581
+ topOriginID: originID
582
+ }), `(reterm, originID))
583
+ if error
584
+ return (list(Error("cannot create inBot", error)))
585
+ intop = require("../running/instances.nlg").InTop(App.nide.track, {
586
+ name: "NaanIDE Relay"
587
+ type: "Relay"
588
+ })
589
+ `(error) = intop.open(inbot.instanceID)
590
+ if error
591
+ return (list(Error("cannot create InTop", error)))
592
+ list(false, xc, intop)
1033
593
  };
1034
594
 
1035
595
 
@@ -1041,15 +601,13 @@ closure termInstallVirtualWatcher(track, naancont) {
1041
601
  */
1042
602
 
1043
603
  function termInit(local manifest) {
1044
- manifest = `(TermHost, TermLocal, WorkerConnect, termMainProxy, WorkerToMain, findIDEtarget,
1045
- termIDE, termInstallVirtualWatcher, termInit)
604
+ manifest = `(TermHost, TermLocal,
605
+ findIDEtarget, termIDE, termInstallVirtualWatcher, testTab, relayTab, termInit)
1046
606
 
1047
607
  Naan.module.build(module.id, "terminals", function(modobj, compobj) {
1048
- require("./browser.nlg")
608
+ require("./worker.nlg")
1049
609
  compobj.manifest = manifest
1050
610
  modobj.exports.TermLocal = TermLocal
1051
611
  modobj.exports.TermHost = TermHost
1052
- modobj.exports.WorkerConnect = WorkerConnect
1053
- modobj.exports.WorkerToMain = WorkerToMain
1054
612
  })
1055
613
  } ();