@naanlang/naan 1.4.4 → 1.6.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 (61) hide show
  1. package/LICENSE.md +2 -2
  2. package/README.md +4 -4
  3. package/bin/index.js +25 -3
  4. package/dist/env_web.js +164 -22
  5. package/dist/naan.min.js +5 -5
  6. package/frameworks/browser/browser.nlg +2 -2
  7. package/frameworks/browser/https_request.nlg +2 -2
  8. package/frameworks/browser/sworker.js +28 -26
  9. package/frameworks/browser/terminals.nlg +342 -700
  10. package/frameworks/browser/worker.nlg +255 -0
  11. package/frameworks/browser/workers.nlg +5 -5
  12. package/frameworks/browser/ws_client.nlg +15 -10
  13. package/frameworks/client/apiclient.nlg +204 -19
  14. package/frameworks/client/client.nlg +22 -2
  15. package/frameworks/client/psm_client.nlg +12 -3
  16. package/frameworks/client/relaycon.nlg +87 -161
  17. package/frameworks/common/events.nlg +101 -0
  18. package/frameworks/common/repltools.nlg +80 -1
  19. package/frameworks/node/apiserver.nlg +39 -246
  20. package/frameworks/node/filesystem.nlg +39 -12
  21. package/frameworks/node/gitter.nlg +9 -7
  22. package/frameworks/node/http_request.nlg +34 -13
  23. package/frameworks/node/https_request.nlg +8 -3
  24. package/frameworks/node/node.nlg +2 -0
  25. package/frameworks/node/pty.nlg +346 -0
  26. package/frameworks/node/shell.nlg +4 -4
  27. package/frameworks/node/worker.nlg +409 -539
  28. package/frameworks/node/ws_client.nlg +2 -2
  29. package/frameworks/project/build.nlg +1 -1
  30. package/frameworks/project/projects.nlg +2 -1
  31. package/frameworks/running/debugnub.nlg +17 -4
  32. package/frameworks/running/executors.nlg +27 -274
  33. package/frameworks/running/instances.nlg +393 -0
  34. package/frameworks/running/remote_req.nlg +143 -0
  35. package/frameworks/running/remote_res.nlg +89 -0
  36. package/frameworks/running/taskexec.nlg +2 -2
  37. package/frameworks/service/imp.nlg +736 -0
  38. package/frameworks/service/model.nlg +71 -0
  39. package/frameworks/service/nubnode.nlg +105 -0
  40. package/frameworks/service/nubweb.nlg +106 -0
  41. package/frameworks/service/service.nlg +28 -0
  42. package/frameworks/storage/dbt_pouch.nlg +26 -13
  43. package/frameworks/storage/psm.nlg +39 -5
  44. package/frameworks/storage/psm_dbtables.nlg +10 -2
  45. package/lib/browser/env_web.js +171 -29
  46. package/lib/browser/env_webworker.js +91 -5
  47. package/lib/core/naanlib.js +5 -5
  48. package/lib/env_node.js +184 -8
  49. package/lib/env_nodeworker.js +74 -1
  50. package/package.json +1 -1
  51. package/plugins/serviceAws/aws_cloudwatchlogs.nlg +2 -1
  52. package/plugins/serviceAws/aws_dynamo.nlg +1 -0
  53. package/plugins/serviceAws/aws_s3.nlg +3 -3
  54. package/plugins/serviceAws/aws_sqs.nlg +3 -1
  55. package/plugins/serviceAws/aws_utils.nlg +3 -1
  56. package/plugins/serviceAws/dbt_aws.nlg +22 -20
  57. package/plugins/serviceAws/psm_aws.nlg +2 -1
  58. package/plugins/serviceNexara/speechrec.nlg +10 -6
  59. package/test/test_02_context.nlg +24 -1
  60. package/test/test_03_jsinterop.nlg +32 -3
  61. package/plugins/serviceGC/russian_trusted_root_ca_pem.crt +0 -33
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2019-2024 by Richard C. Zulch
9
+ * Copyright (c) 2019-2025 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -633,8 +633,17 @@ closure psmcFsConnector(psm, api, local connClassID, connector, watch) {
633
633
  api = connector.apis[creds]
634
634
  else {
635
635
  api = connector.apis[creds.urlName]
636
- if !api // get API to remote server
637
- connector.apis[creds.urlName] = api = NideAPIclient(creds.urlName, creds.authSecret)
636
+ if !api { // create API to remote server
637
+ connector.apis[creds.urlName] = api = APIClient({
638
+ url: creds.urlName
639
+ guid: creds.authSecret
640
+ instanceID: creds.instanceID
641
+ })
642
+ if creds.instanceID != api.instanceID { // update our client instanceID
643
+ creds.instanceID = api.instanceID
644
+ `(error) = connector.vault.updateResource(resID, creds)
645
+ }
646
+ }
638
647
  }
639
648
  }
640
649
  if error
@@ -2,11 +2,11 @@
2
2
  * relaycon.nlg
3
3
  * Naanlib/frameworks/client
4
4
  *
5
- * Control NaanIDE Server and browser clients from the command line.
5
+ * Remote control of NaaN execution instances through the IMP network.
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2023-2024 by Richard C. Zulch
9
+ * Copyright (c) 2026 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -17,33 +17,32 @@
17
17
  */
18
18
 
19
19
  execsMod;; // running/executors module
20
- wsMod;; // websocket module
21
20
 
22
21
 
23
22
  /*
24
23
  * RelayCon
25
24
  *
26
- * A host controller for NaanIDE Server.
25
+ * Connect with an existing or new remote instance via IMP.
26
+ *
27
+ * Example usage:
28
+ *
29
+ comMod = require("naanlib:frameworks/client/relaycon.nlg")
30
+ relay = comMod.RelayCon(App.imp)
31
+ relay.clients()
32
+ // find desired instance
33
+ relay.context(<destID>)
27
34
  *
28
35
  * Options:
29
36
  * {
30
- * destID: <string> // client UUID or "server"
31
37
  * name: <string> // internal identification
32
- * config: {
33
- * clientID: <string> // distinguishes clients
34
- * serverID: <string> // subsystem on server (server destinations only)
35
- * workerID: <string> // worker on server (server destinations only)
36
- * }
37
- * startup: <dictionary> // worker spawn init (server destinations only)
38
- * wss: <boolean> // true to use secure connection
39
38
  * debug: <boolean> // debug logging
40
39
  * }
41
40
  *
42
41
  */
43
42
 
44
- closure RelayCon(host, secret, options, local relay, nlregex)
43
+ closure RelayCon(imp, options, local relay, nlregex)
45
44
  {
46
- global(App, execsMod, wsMod)
45
+ global(App, execsMod)
47
46
  relay = new(object, this)
48
47
  options = merge({
49
48
  name: "NaanIDE Server"
@@ -53,125 +52,71 @@ closure RelayCon(host, secret, options, local relay, nlregex)
53
52
  serverID: "Workers"
54
53
  workerID: "NideServer"
55
54
  }, options.config)
56
- if options.destID != "server" {
57
- relay.config.endServerID = relay.config.serverID
58
- relay.config.serverID = "Relay"
59
- relay.config.destID = options.destID
60
- relay.config.workerID = undefined
61
- }
55
+ relay.instanceID = UUID()
62
56
  relay.exec = execsMod.ExecutorBase(false, "Host", options.name)
63
57
  nlregex = RegExp("\\n", "g")
64
58
 
65
59
  // connect
66
60
  //
67
- // Connect with the host if not already connected, returning a result tuple. When this connects
68
- // it sends a "spawn" message, which establishes the connection with the server's "worker" which
69
- // in the case of NideServer is just the main NodeJS process itself. It could also spawn a new
70
- // worker thread, but we don't expose that functionality for the time being.
61
+ // Connect with the destination if not already connected.
71
62
  //
72
- closure connect(local result) {
73
- if relay.wscon
74
- return (list(false, { connected: true }))
75
- relay.guid = secret
76
- relay.host = host // e.g. "localhost:8009"
77
- relay.wscon = wsMod.WebSocket(host, {
78
- wss: options.wss || undefined
79
- onopen: onopen
80
- onmessage: onmessage
81
- onerror: onerror
82
- onclose: onclose
83
- query: undefined // don't put secret on unsecure connections
84
- querys: secret && EncodeQuery("?", { guid: secret })
63
+ relay.connect = closure connect(destID) {
64
+ if relay.curDestID
65
+ return (list(false, { connectedID: relay.curDestID }))
66
+ if !string(destID)
67
+ return (list(Error("invalid connect destination")))
68
+ relay.curDestID = destID
69
+
70
+ relay.sender = imp.register(relay, {
71
+ name: options.name
72
+ type: relay.config.workerID
73
+ instanceID: relay.instanceID
74
+ services: [ "term_client" ]
85
75
  })
86
- result = relay.wscon.connect()
87
- if !result.0
88
- sendControl("spawn", { name: options.name }) // ensure worker is connected
89
- else if options.debug
90
- ErrorDebuglog("WebSocket connect failed:", result.0)
91
- result
92
- }
93
76
 
94
- // onopen
95
- //
96
- // The WebSocket connection has opened.
97
- //
98
- closure onopen(e, target, type, local spawner) {
99
- if options.debug
100
- debuglog("WebSocket onopen", relay.host, target, type, e.*)
101
- spawner = new(options.startup)
102
- spawner.name = options.name
103
- if spawner
104
- sendControl("spawn", spawner)
105
- }
77
+ function initWorker(local spawner) {
78
+ return // ###
79
+ relay.sender(relay.curDestID, {
80
+ init: { // initialize our worker (or main)...
81
+ name: options.name
82
+ workerID: relay.config.workerID
83
+ }
84
+ })
85
+ spawner = new(options.startup) || { }
86
+ spawner.name = options.name
87
+ // sendControl("spawn", spawner)
88
+ }
106
89
 
107
- // onclose
108
- //
109
- // The WebSocket connection has closed.
110
- //
111
- closure onclose(e, code, reason) {
112
- if options.debug && code != 1000 && code != 1001 && code != 1005
113
- debuglog("WebSocket onclose", relay.host, code, reason, e.*)
114
- relay.wscon = false
115
- relay.exec.execClosed(Error("socket closed", e.code, e.reason))
90
+ initWorker()
91
+ list(false, { connectedID: relay.curDestID })
116
92
  }
117
93
 
118
- // onerror
94
+ // impUnknown
119
95
  //
120
- // The WebSocket connection has failed.
96
+ // Received an unknown destination notice from the other side.
121
97
  //
122
- closure onerror(e) {
123
- if options.debug
124
- debuglog("WebSocket onerror", relay.host, e.*)
125
- relay.exec.execFailed(Error("WebSocket onerror"))
98
+ relay.impUnknown = function impUnknown(unknownID, sourceID, gateway) {
99
+ if !gateway
100
+ debuglog("RelayCon.impUnknownID:", unknownID, "sourceID:", sourceID)
126
101
  }
127
102
 
128
- // onmessage
103
+ // impReceive
129
104
  //
130
- // A WebSocket message was received.
105
+ // Received a payload from the other side.
131
106
  //
132
- closure onmessage(e, message, local msg) {
133
- if options.debug && (message.relayOp || Array.isArray(message))
134
- printline("Relay response:", Dialect.print(message))
135
- if Array.isArray(message)
136
- relay.exec.execFailed(message.0) // must be an error
137
- else if message.respOp == "msgout" {
138
- msg = message.payload
139
- if msg.id == "spawned" || msg.id == "workerlist" || msg.id == "termination"
140
- return (hostWorkerUpdate(msg))
141
- if msg.id != "targetout" {
142
- if msg.id == "status"
143
- relay.lastStatus = milliseconds() }
144
- doMessage(msg) }
145
- else if options.debug {
146
- if message.respOp == "error"
147
- debuglog("worker controller error:", ErrorString(message.payload[0]))
148
- else if message.respOp == "linked"
149
- debuglog("terminal linked")
150
- else if message.respOp
151
- debuglog("unknown host response message:", message.respOp)
152
- }
153
- }
154
-
155
- // hostWorkerUpdate
156
- //
157
- // New workers created/discovered on this host.
158
- //
159
- closure hostWorkerUpdate(msg) {
160
- if options.debug {
161
- if msg.id == "spawned"
162
- debuglog("host worker spawned", msg.name, "(${msg.workerID})")
163
- else if msg.id == "termination"
164
- debuglog("host worker terminated", msg.workerID)
165
- else if msg.id == "workerlist"
166
- printline("host workers:", Dialect.print(msg.workers))
167
- }
107
+ relay.impReceive = function impReceive(message, sourceID) {
108
+ if options.debug
109
+ printline("Relay response:", Dialect.print(new(message)))
110
+ if message.id == "status"
111
+ relay.lastStatus = milliseconds()
112
+ doMessage(message)
168
113
  }
169
114
 
170
115
  // doMessage
171
116
  //
172
- // Process the specified undecoded message.
117
+ // Process the specified undecoded message.
173
118
  //
174
- closure doMessage(msg) {
119
+ relay.doMessage = function doMessage(msg) {
175
120
  if msg.id == "targetout"
176
121
  relay.exec.execReceived(msg.data)
177
122
  else if msg.id == "debugout"
@@ -191,34 +136,23 @@ closure RelayCon(host, secret, options, local relay, nlregex)
191
136
  debuglog("unknown host worker message:", msg.id)
192
137
  }
193
138
  }
194
-
195
- // sendControl
196
- //
197
- // Send a message to the worker controller.
198
- //
199
- relay.sendControl = function sendControl(workerOp, payload) {
200
- relay.wscon.send(merge({
201
- guid: relay.guid
202
- workerOp: workerOp
203
- payload: payload
204
- }, relay.config))
205
- }
206
-
139
+
207
140
  // send
208
141
  //
209
- // Send a message to the worker.
142
+ // Send a message to the worker.
210
143
  //
211
144
  relay.send = function send(msg) {
212
- sendControl("msgin", msg)
145
+ relay.sender(relay.curDestID, msg)
213
146
  }
214
147
 
215
148
  // exec.PostMessage
216
149
  //
217
- // Post a message to the target, returning a result tuple with an error if the connection
218
- // has closed.
150
+ // Post a message to the target, returning a result tuple with an error if the connection has
151
+ // closed.
219
152
  //
220
153
  relay.exec.PostMessage = function PostMessage(data, local result) {
221
- result = connect()
154
+ if !relay.curDestID
155
+ return (list(Error("connection not open")))
222
156
  if result.0
223
157
  return (result)
224
158
  send({ // send data to worker
@@ -230,27 +164,10 @@ closure RelayCon(host, secret, options, local relay, nlregex)
230
164
 
231
165
  // clients
232
166
  //
233
- // Return an array of available clients (destination browsers) with which we can communicate, as
234
- // a result tuple like the following array:
167
+ // Return an array of available clients (destination browsers) with which we can communicate.
235
168
  //
236
- // [
237
- // {
238
- // destID : "52733f52-869b-4758-b553-611df95eb5fe",
239
- // address : { address: "::1", family: "IPv6", port: 8009 },
240
- // name : "NaanIDE Server",
241
- // workerID: "NideServer" },
242
- // {
243
- // destID : "ba4b7e69-abe5-4d13-b091-7abe833fd029",
244
- // address : { address: "::1", family: "IPv6", port: 8009 },
245
- // name : "NaanIDE Server",
246
- // workerID: "NideServer" }
247
- // ]
248
- //
249
- relay.clients = closure clients(excon, local error) {
250
- if !excon
251
- `(error, excon) = context()
252
- if excon
253
- excon.evalq(App.api.Relays())
169
+ relay.clients = closure clients(destID) {
170
+ imp.discover(destID)
254
171
  }
255
172
 
256
173
  // context
@@ -268,10 +185,20 @@ closure RelayCon(host, secret, options, local relay, nlregex)
268
185
  // context.rpc(test3, `(45))
269
186
  // (rpc does JSON serialization)
270
187
  // 4. Call via RPC showing Date object conversion
271
- // context.rpc(test3)
188
+ // context.rpc(test3, list(Date()))
272
189
  // (JSON serializationn converts the date to an ISO string)
273
190
  //
274
- relay.context = closure context() {
191
+ relay.context = closure context(destID, local result) {
192
+ if destID != relay.curDestID {
193
+ if relay.curDestID
194
+ close()
195
+ result = connect(destID)
196
+ if result.0
197
+ return (result)
198
+ }
199
+ connect(destID)
200
+ if imp.us.instanceID != destID
201
+ imp.inform(imp.us.instanceID, destID) // ensure our IMP is reachable by them
275
202
  relay.exec.context()
276
203
  }
277
204
 
@@ -280,8 +207,11 @@ closure RelayCon(host, secret, options, local relay, nlregex)
280
207
  // Close the connection, though it can be reopened
281
208
  //
282
209
  relay.close = closure close() {
283
- relay.wscon.close()
284
- relay.wscon = false
210
+ if relay.curDestID {
211
+ imp.unregister(relay.instanceID)
212
+ relay.curDestID = false
213
+ }
214
+ list(false, { closed: true })
285
215
  }
286
216
 
287
217
  // finis
@@ -291,22 +221,18 @@ closure RelayCon(host, secret, options, local relay, nlregex)
291
221
 
292
222
 
293
223
  /*
294
- * hocoInit
224
+ * relayInit
295
225
  *
296
- * Orchestrate using CsvJson in the console.
226
+ * Initialize the component.
297
227
  *
298
228
  */
299
229
 
300
- function hocoInit(local manifest) {
301
- manifest = `(RelayCon, hocoInit)
230
+ function relayInit(local manifest) {
231
+ manifest = `(RelayCon, relayInit)
302
232
 
303
233
  Naan.module.build(module.id, "relaycon", function(modobj, compobj) {
304
234
  require("./client.nlg")
305
235
  execsMod = require("../running/executors.nlg")
306
- if js.w
307
- wsMod = require("../browser/ws_client.nlg")
308
- else
309
- wsMod = require("../node/ws_client.nlg")
310
236
  compobj.manifest = manifest
311
237
  module.exports.RelayCon = RelayCon
312
238
  })
@@ -0,0 +1,101 @@
1
+ /*
2
+ * events.nlg
3
+ * naanlib/frameworks/common/events
4
+ *
5
+ * A simple notification system replicating JS events.
6
+ *
7
+ * column positioning: // // !
8
+ *
9
+ * Copyright (c) 2026 by Richard C. Zulch
10
+ *
11
+ */
12
+
13
+
14
+ /*
15
+ * Events
16
+ *
17
+ * Events objects provide a common protocol for adding/removing event listeners and sending events
18
+ * to them. Events are dispatched via a future, so they are asynchronous to the caller but invoked
19
+ * serially. Initialize this on an object to define the methods directly on it.
20
+ *
21
+ */
22
+
23
+ closure Events(target, local events, eventsdb) {
24
+ global()
25
+ if target
26
+ events = target
27
+ else
28
+ events = new(object, this)
29
+
30
+ // dispatch
31
+ //
32
+ // Dispatch the specified event, which will be sent to all listeners.
33
+ //
34
+ events.dispatchEvent = function dispatch args {
35
+ closure Event(type, args, local event) {
36
+ event = new(object, this)
37
+ event.type = type
38
+ event.target = target || undefined
39
+ event.timeStamp = milliseconds()
40
+ future(function(local listener) {
41
+ for listener in eventsdb[type]
42
+ apply(listener.code, cons(event, args))
43
+ },0)
44
+ } (pop(args), args)
45
+ }
46
+
47
+ // addEventListener
48
+ //
49
+ // Add the specified function to the call list for events of the type.
50
+ //
51
+ events.addEventListener = function addEventListener(type, code, option) {
52
+ if !eventsdb[type]
53
+ eventsdb[type] = []
54
+ else if eventsdb[type].find(function(listener) { listener.code === code })
55
+ return // no duplicates
56
+ eventsdb[type].push({
57
+ code: code
58
+ option: option
59
+ })
60
+ }
61
+
62
+ // removeEventListener
63
+ //
64
+ // Remove the specified function from the call list for events of the type.
65
+ //
66
+ events.removeEventListener = function removeEventListener(type, code) {
67
+ eventsdb[type] = eventsdb[type].filter(function(listener) {
68
+ listener.code !== code
69
+ })
70
+ }
71
+
72
+ // reset
73
+ //
74
+ // Reset the events list, which is done at construction time.
75
+ //
76
+ events.reset = function reset() {
77
+ eventsdb = {}
78
+ } ()
79
+
80
+ // finis
81
+ events
82
+ };
83
+
84
+
85
+ /*
86
+ * eventsInit
87
+ *
88
+ * Initialize the component.
89
+ *
90
+ */
91
+
92
+ function eventsInit(local manifest) {
93
+ manifest = `(Events, eventsInit)
94
+
95
+ Naan.module.build(module.id, "events", function(modobj, compobj) {
96
+ require("./common.nlg")
97
+ compobj.manifest = manifest
98
+ module.exports.Events = Events
99
+ updateExports()
100
+ })
101
+ } ();
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * column positioning: // // !
7
7
  *
8
- * Copyright (c) 2017-2021 by Richard C. Zulch
8
+ * Copyright (c) 2017-2026 by Richard C. Zulch
9
9
  *
10
10
  */
11
11
 
@@ -70,6 +70,85 @@ function dcaller(proc, lambdaOK, local x, found) {
70
70
  };
71
71
 
72
72
 
73
+ /*
74
+ * dwrapper
75
+ *
76
+ * Wrap an object so that all calls in or out are logged.
77
+ *
78
+ */
79
+
80
+ closure dwrapper(obj, name, local wrap, indents) {
81
+ indents = dwrapper@indents
82
+ if !indents {
83
+ dwrapper@indents = indents = unintern(`indents)
84
+ assign(indents, 0)
85
+ }
86
+
87
+ wrap = new(object, dwrapper)
88
+
89
+ function indent(callin) {
90
+ debuglog("${spaces()}${callin} ==>")
91
+ assign(indents, indents.value+1)
92
+ }
93
+
94
+ function outdent(result) {
95
+ assign(indents, indents.value-1)
96
+ debuglog("${spaces()}<== ${result}", )
97
+ result
98
+ }
99
+
100
+ function spaces() {
101
+ "| ".repeat(indents.value)
102
+ }
103
+
104
+ function swap(o) {
105
+ if tuple(o)
106
+ o.map(function(item){ item === wrap && name || item })
107
+ else if o === wrap
108
+ name
109
+ else
110
+ o
111
+ }
112
+
113
+ wrap@[".undefined"] = function trampoline args {
114
+ function (selector, arglist, result) {
115
+ indent("${name}.${selector}${swap(arglist)||'()'}")
116
+ try {
117
+ result = eval(list(`deref, obj, selector, arglist))
118
+ outdent(result)
119
+ } catch {
120
+ outdent("${selector}() exception: ${exception}")
121
+ result = mu()
122
+ }
123
+ } (args.0, args.-1)
124
+ }
125
+
126
+ wrap@["..undefined"] = function getter(selector) {
127
+ indent("${name}[${selector}]")
128
+ try {
129
+ result = obj[selector]
130
+ outdent(result)
131
+ } catch {
132
+ outdent("${selector}[] exception: ${exception}")
133
+ result = mu()
134
+ }
135
+ }
136
+
137
+ wrap@[".=undefined"] = function setter(selector, value) {
138
+ indent("${name}[${selector}] = ${swap(value)}")
139
+ try {
140
+ result = obj[selector] = value
141
+ outdent(result)
142
+ } catch {
143
+ outdent("${selector}[]= exception: ${exception}")
144
+ result = mu()
145
+ }
146
+ }
147
+
148
+ wrap
149
+ };
150
+
151
+
73
152
  /*
74
153
  * ImportReplTools
75
154
  *