@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
@@ -0,0 +1,308 @@
1
+ /*
2
+ * relaycon.nlg
3
+ * Naanlib/frameworks/client
4
+ *
5
+ * Control NaanIDE Server and browser clients from the command line.
6
+ *
7
+ * column positioning: // // !
8
+ *
9
+ * Copyright (c) 2023 by Richard C. Zulch
10
+ *
11
+ */
12
+
13
+
14
+ /*
15
+ * globals
16
+ *
17
+ */
18
+
19
+ execsMod;; // running/executors module
20
+ wsMod;; // websocket module
21
+
22
+
23
+ /*
24
+ * RelayCon
25
+ *
26
+ * A host controller for NaanIDE Server.
27
+ *
28
+ * Options:
29
+ * {
30
+ * destID: <string> // client UUID or "server"
31
+ * 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
+ * debug: <boolean> // debug logging
39
+ * }
40
+ *
41
+ */
42
+
43
+ closure RelayCon(host, secret, options, local relay, nlregex)
44
+ {
45
+ global(execsMod, wsMod)
46
+ relay = new(object, this)
47
+ options = merge({
48
+ name: "NaanIDE Server"
49
+ }, options)
50
+ relay.config = merge({
51
+ clientID: "relaycon"
52
+ serverID: "Workers"
53
+ workerID: "NideServer"
54
+ }, options.config)
55
+ if options.destID != "server" {
56
+ relay.config.endServerID = relay.config.serverID
57
+ relay.config.serverID = "Relay"
58
+ relay.config.destID = options.destID
59
+ relay.config.workerID = undefined
60
+ }
61
+ relay.exec = execsMod.ExecutorBase(false, "Host", options.name)
62
+ nlregex = RegExp("\\n", "g")
63
+
64
+ // connect
65
+ //
66
+ // Connect with the host if not already connected, returning a result tuple. When this connects
67
+ // it sends a "spawn" message, which establishes the connection with the server's "worker" which
68
+ // in the case of NideServer is just the main NodeJS process itself. It could also spawn a new
69
+ // worker thread, but we don't expose that functionality for the time being.
70
+ //
71
+ closure connect(local result) {
72
+ if relay.wscon
73
+ return (list(false, { connected: true }))
74
+ relay.guid = secret
75
+ relay.host = host // e.g. "localhost:8009"
76
+ relay.wscon = wsMod.WebSocket(host, {
77
+ onopen: onopen
78
+ onmessage: onmessage
79
+ onerror: onerror
80
+ onclose: onclose
81
+ })
82
+ result = relay.wscon.connect()
83
+ if !result.0
84
+ sendControl("spawn", { name: "NaanIDE Server" }) // ensure worker is connected
85
+ else if options.debug
86
+ ErrorDebuglog("WebSocket connect failed:", result.0)
87
+ result
88
+ }
89
+
90
+ // onopen
91
+ //
92
+ // The WebSocket connection has opened.
93
+ //
94
+ closure onopen(e, target, type, local spawner) {
95
+ if options.debug
96
+ debuglog("WebSocket onopen", relay.host, target, type, e.*)
97
+ spawner = new(options.startup)
98
+ spawner.name = options.name
99
+ if spawner
100
+ sendControl("spawn", spawner)
101
+ }
102
+
103
+ // onclose
104
+ //
105
+ // The WebSocket connection has closed.
106
+ //
107
+ closure onclose(e, code, reason) {
108
+ if options.debug && code != 1000 && code != 1001 && code != 1005
109
+ debuglog("WebSocket onclose", relay.host, code, reason, e.*)
110
+ relay.wscon = false
111
+ relay.exec.execClosed(Error("socket closed", e.code, e.reason))
112
+ }
113
+
114
+ // onerror
115
+ //
116
+ // The WebSocket connection has failed.
117
+ //
118
+ closure onerror(e) {
119
+ if options.debug
120
+ debuglog("WebSocket onerror", relay.host, e.*)
121
+ relay.exec.execFailed(Error("WebSocket onerror"))
122
+ }
123
+
124
+ // onmessage
125
+ //
126
+ // A WebSocket message was received.
127
+ //
128
+ closure onmessage(e, message, local msg) {
129
+ if options.debug && (message.relayOp || Array.isArray(message))
130
+ printline("Relay response:", Dialect.print(message))
131
+ if Array.isArray(message)
132
+ relay.exec.execFailed(message.0) // must be an error
133
+ else if message.respOp == "msgout" {
134
+ msg = message.payload
135
+ if msg.id == "spawned" || msg.id == "workerlist" || msg.id == "termination"
136
+ return (hostWorkerUpdate(msg))
137
+ if msg.id != "targetout" {
138
+ if msg.id == "status"
139
+ relay.lastStatus = milliseconds() }
140
+ doMessage(msg) }
141
+ else if options.debug {
142
+ if message.respOp == "error"
143
+ debuglog("worker controller error:", ErrorString(message.payload[0]))
144
+ else if message.respOp == "linked"
145
+ debuglog("terminal linked")
146
+ else if message.respOp
147
+ debuglog("unknown host response message:", message.respOp)
148
+ }
149
+ }
150
+
151
+ // hostWorkerUpdate
152
+ //
153
+ // New workers created/discovered on this host.
154
+ //
155
+ closure hostWorkerUpdate(msg) {
156
+ if options.debug {
157
+ if msg.id == "spawned"
158
+ debuglog("host worker spawned", msg.name, "(${msg.workerID})")
159
+ else if msg.id == "termination"
160
+ debuglog("host worker terminated", msg.workerID)
161
+ else if msg.id == "workerlist"
162
+ printline("host workers:", Dialect.print(msg.workers))
163
+ }
164
+ }
165
+
166
+ // doMessage
167
+ //
168
+ // Process the specified undecoded message.
169
+ //
170
+ closure doMessage(msg) {
171
+ if msg.id == "targetout"
172
+ relay.exec.execReceived(msg.data)
173
+ else if msg.id == "debugout"
174
+ relay.exec.debugger.debugData(relay, msg.data)
175
+ else if false { // below cause feedback loops
176
+ if msg.id == "textout"
177
+ printline(msg.text.replace(nlregex, "\r\n"))
178
+ else if msg.id == "debugtext"
179
+ debuglog(msg.text)
180
+ else if msg.id == "started"
181
+ { debuglog("doMessage: started") }
182
+ else if options.debug && msg.id == "typed"
183
+ printline(msg.text.replace(nlregex, "\r\n"))
184
+ else if msg.id == "status"
185
+ { }
186
+ else
187
+ debuglog("unknown host worker message:", msg.id)
188
+ }
189
+ }
190
+
191
+ // sendControl
192
+ //
193
+ // Send a message to the worker controller.
194
+ //
195
+ relay.sendControl = function sendControl(workerOp, payload) {
196
+ relay.wscon.send(merge({
197
+ guid: relay.guid
198
+ workerOp: workerOp
199
+ payload: payload
200
+ }, relay.config))
201
+ }
202
+
203
+ // send
204
+ //
205
+ // Send a message to the worker.
206
+ //
207
+ relay.send = function send(msg) {
208
+ sendControl("msgin", msg)
209
+ }
210
+
211
+ // exec.PostMessage
212
+ //
213
+ // Post a message to the target, returning a result tuple with an error if the connection
214
+ // has closed.
215
+ //
216
+ relay.exec.PostMessage = function PostMessage(data, local result) {
217
+ result = connect()
218
+ if result.0
219
+ return (result)
220
+ send({ // send data to worker
221
+ id: "targetin",
222
+ data: data
223
+ })
224
+ list(false, { posted: true })
225
+ }
226
+
227
+ // clients
228
+ //
229
+ // Return an array of available clients (destination browsers) with which we can communicate, as
230
+ // a result tuple like the following array:
231
+ //
232
+ // [
233
+ // {
234
+ // destID : "52733f52-869b-4758-b553-611df95eb5fe",
235
+ // address : { address: "::1", family: "IPv6", port: 8009 },
236
+ // name : "NaanIDE Server",
237
+ // workerID: "NideServer" },
238
+ // {
239
+ // destID : "ba4b7e69-abe5-4d13-b091-7abe833fd029",
240
+ // address : { address: "::1", family: "IPv6", port: 8009 },
241
+ // name : "NaanIDE Server",
242
+ // workerID: "NideServer" }
243
+ // ]
244
+ //
245
+ relay.clients = closure clients(excon, local error) {
246
+ if !excon
247
+ `(error, excon) = context()
248
+ if excon
249
+ excon.evalq(App.api.Relays())
250
+ }
251
+
252
+ // context
253
+ //
254
+ // Return a remote evaluation context.
255
+ //
256
+ // Examples:
257
+ // 1. Define a remote function
258
+ // context.evalq(function test3(x) { debuglog("test3", x), x}, `(test3))
259
+ // (the test3 definition is uninterned without the put symbol argument)
260
+ // 2. Call the remote function
261
+ // context.evalq(test3(Date().toString()))
262
+ // (evalq cannot send xobjects, objects, or namespaces across the link)
263
+ // 3. Call via RPC
264
+ // context.rpc(test3, `(45))
265
+ // (rpc does JSON serialization)
266
+ // 4. Call via RPC showing Date object conversion
267
+ // context.rpc(test3)
268
+ // (JSON serializationn converts the date to an ISO string)
269
+ //
270
+ relay.context = closure context() {
271
+ relay.exec.context()
272
+ }
273
+
274
+ // close
275
+ //
276
+ // Close the connection, though it can be reopened
277
+ //
278
+ relay.close = closure close() {
279
+ relay.wscon.close()
280
+ relay.wscon = false
281
+ }
282
+
283
+ // finis
284
+
285
+ relay
286
+ };
287
+
288
+
289
+ /*
290
+ * hocoInit
291
+ *
292
+ * Orchestrate using CsvJson in the console.
293
+ *
294
+ */
295
+
296
+ function hocoInit(local manifest) {
297
+ manifest = `(RelayCon, hocoInit)
298
+
299
+ Naan.module.build(module.id, "relaycon", function(modobj, compobj) {
300
+ execsMod = require("../running/executors.nlg")
301
+ if js.w
302
+ wsMod = require("../browser/ws_client.nlg")
303
+ else
304
+ wsMod = require("../node/ws_client.nlg")
305
+ compobj.manifest = manifest
306
+ module.exports.RelayCon = RelayCon
307
+ })
308
+ } ();
@@ -185,7 +185,7 @@ function JsonStringify(data) {
185
185
 
186
186
  jsScripts = { }; // script values become false on reload
187
187
 
188
- function JsLoadScript(libpath, globalID, local scriptTag, fpath) {
188
+ closure JsLoadScript(libpath, globalID, local scriptTag, fpath) {
189
189
  if jsScripts[libpath]
190
190
  return // already loaded
191
191
  if libpath.startsWith("/")
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2023 by Richard C. Zulch
9
+ * Copyright (c) 2023-2024 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -58,6 +58,43 @@ function DictCompare(aa, bb, local output, keysa, keysb, ka, kb, comp) {
58
58
  };
59
59
 
60
60
 
61
+ /*
62
+ * ExbackFactory
63
+ *
64
+ * A factory returning an exponential backoff function based on the specified backoff and deadline
65
+ * values. Each time you call this it returns a new delay value until the deadline is reached, when
66
+ * it returns false. Backoff is the "maximum" backoff interval in seconds. Deadline is the maximum
67
+ * total time in seconds. The return value is integer milliseconds.
68
+ *
69
+ * For example, with a backoff of 12 seconds and a deadline of 60 seconds, the return delays for
70
+ * multiple iterations are:
71
+ * 1571
72
+ * 2660
73
+ * 4883
74
+ * 8579
75
+ * 12139
76
+ * 12650
77
+ * 12434
78
+ *
79
+ * https://en.wikipedia.org/wiki/Exponential_backoff
80
+ *
81
+ */
82
+
83
+ closure ExbackFactory(backoff, deadline, local expon, total)
84
+ {
85
+ expon = 0
86
+ total = 0
87
+ function delayms(local wait) {
88
+ wait = Math.min(Math.pow(2, expon++), backoff)
89
+ total += wait
90
+ if total > deadline
91
+ return (false)
92
+ wait += Math.random()
93
+ Math.ceil(wait * 1000)
94
+ }
95
+ };
96
+
97
+
61
98
  /*
62
99
  * utilsInit
63
100
  *
@@ -66,12 +103,13 @@ function DictCompare(aa, bb, local output, keysa, keysb, ka, kb, comp) {
66
103
  */
67
104
 
68
105
  function utilsInit(local manifest) {
69
- manifest = `(DictCompare, utilsInit)
106
+ manifest = `(DictCompare, ExbackFactory, utilsInit)
70
107
 
71
108
  Naan.module.build(module.id, "utils", function(modobj, compobj) {
72
109
  require("./common.nlg")
73
110
  compobj.manifest = manifest
74
111
  module.exports.DictCompare = DictCompare
112
+ module.exports.ExbackFactory = ExbackFactory
75
113
  updateExports()
76
114
  })
77
115
  } ();