@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.
- package/LICENSE.md +2 -2
- package/README.md +13 -11
- package/bin/index.js +4 -4
- package/dist/naan.min.js +14 -14
- package/frameworks/browser/https_request.nlg +26 -10
- package/frameworks/browser/sworker.js +26 -25
- package/frameworks/browser/terminals.nlg +118 -55
- package/frameworks/browser/ws_client.nlg +124 -0
- package/frameworks/client/apiclient.nlg +82 -116
- package/frameworks/client/psm_client.nlg +37 -16
- package/frameworks/client/relaycon.nlg +308 -0
- package/frameworks/common/common.nlg +1 -1
- package/frameworks/common/utils.nlg +40 -2
- package/frameworks/node/apiserver.nlg +342 -103
- package/frameworks/node/https_request.nlg +32 -6
- package/frameworks/node/node.nlg +60 -2
- package/frameworks/node/psm_server.nlg +11 -7
- package/frameworks/node/worker.nlg +22 -9
- package/frameworks/node/ws_client.nlg +127 -0
- package/frameworks/project/build.nlg +3 -2
- package/frameworks/project/projects.nlg +8 -2
- package/frameworks/running/debugnub.nlg +2 -2
- package/frameworks/running/debugutil.nlg +1 -1
- package/frameworks/running/executors.nlg +69 -17
- package/frameworks/running/sourcecode.nlg +2 -2
- package/frameworks/running/taskexec.nlg +25 -25
- package/frameworks/storage/dbt_pouch.nlg +8 -6
- package/frameworks/storage/file_manager.nlg +6 -3
- package/frameworks/storage/psm.nlg +5 -3
- package/frameworks/storage/psm_dbtables.nlg +75 -53
- package/frameworks/storage/resources.nlg +4 -2
- package/lib/browser/env_web.js +6 -6
- package/lib/browser/env_webworker.js +1 -1
- package/lib/core/naanlib.js +14 -14
- package/lib/env_node.js +97 -8
- package/lib/env_nodeworker.js +22 -4
- package/package.json +1 -1
- package/plugins/serviceAws/aws/lambda_rest_index.js +5 -1
- package/plugins/serviceAws/aws_dynamo.nlg +91 -32
- package/plugins/serviceAws/dbt_aws.nlg +5 -5
- package/plugins/serviceAws/psm_aws.nlg +2 -2
- package/test/harness.nlg +1 -1
- package/test/test_01_core.nlg +30 -4
- package/test/test_02_context.nlg +2 -2
- 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
|
-
|
|
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
|
} ();
|