@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.
- package/LICENSE.md +2 -2
- package/README.md +4 -4
- package/bin/index.js +25 -3
- package/dist/env_web.js +164 -22
- package/dist/naan.min.js +5 -5
- package/frameworks/browser/browser.nlg +2 -2
- package/frameworks/browser/https_request.nlg +2 -2
- package/frameworks/browser/sworker.js +28 -26
- package/frameworks/browser/terminals.nlg +342 -700
- package/frameworks/browser/worker.nlg +255 -0
- package/frameworks/browser/workers.nlg +5 -5
- package/frameworks/browser/ws_client.nlg +15 -10
- package/frameworks/client/apiclient.nlg +204 -19
- package/frameworks/client/client.nlg +22 -2
- package/frameworks/client/psm_client.nlg +12 -3
- package/frameworks/client/relaycon.nlg +87 -161
- package/frameworks/common/events.nlg +101 -0
- package/frameworks/common/repltools.nlg +80 -1
- package/frameworks/node/apiserver.nlg +39 -246
- package/frameworks/node/filesystem.nlg +39 -12
- package/frameworks/node/gitter.nlg +9 -7
- package/frameworks/node/http_request.nlg +34 -13
- package/frameworks/node/https_request.nlg +8 -3
- package/frameworks/node/node.nlg +2 -0
- package/frameworks/node/pty.nlg +346 -0
- package/frameworks/node/shell.nlg +4 -4
- package/frameworks/node/worker.nlg +409 -539
- package/frameworks/node/ws_client.nlg +2 -2
- package/frameworks/project/build.nlg +1 -1
- package/frameworks/project/projects.nlg +2 -1
- package/frameworks/running/debugnub.nlg +17 -4
- package/frameworks/running/executors.nlg +27 -274
- package/frameworks/running/instances.nlg +393 -0
- package/frameworks/running/remote_req.nlg +143 -0
- package/frameworks/running/remote_res.nlg +89 -0
- package/frameworks/running/taskexec.nlg +2 -2
- package/frameworks/service/imp.nlg +736 -0
- package/frameworks/service/model.nlg +71 -0
- package/frameworks/service/nubnode.nlg +105 -0
- package/frameworks/service/nubweb.nlg +106 -0
- package/frameworks/service/service.nlg +28 -0
- package/frameworks/storage/dbt_pouch.nlg +26 -13
- package/frameworks/storage/psm.nlg +39 -5
- package/frameworks/storage/psm_dbtables.nlg +10 -2
- package/lib/browser/env_web.js +171 -29
- package/lib/browser/env_webworker.js +91 -5
- package/lib/core/naanlib.js +5 -5
- package/lib/env_node.js +184 -8
- package/lib/env_nodeworker.js +74 -1
- package/package.json +1 -1
- package/plugins/serviceAws/aws_cloudwatchlogs.nlg +2 -1
- package/plugins/serviceAws/aws_dynamo.nlg +1 -0
- package/plugins/serviceAws/aws_s3.nlg +3 -3
- package/plugins/serviceAws/aws_sqs.nlg +3 -1
- package/plugins/serviceAws/aws_utils.nlg +3 -1
- package/plugins/serviceAws/dbt_aws.nlg +22 -20
- package/plugins/serviceAws/psm_aws.nlg +2 -1
- package/plugins/serviceNexara/speechrec.nlg +10 -6
- package/test/test_02_context.nlg +24 -1
- package/test/test_03_jsinterop.nlg +32 -3
- package/plugins/serviceGC/russian_trusted_root_ca_pem.crt +0 -33
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* worker.nlg
|
|
3
|
+
* Naanlib/frameworks/browser
|
|
4
|
+
*
|
|
5
|
+
* Manage NaaN instances.
|
|
6
|
+
*
|
|
7
|
+
* NaaN instances are managed by a top/bottom pair of of objects. InBot (INstance BOTtom) objects
|
|
8
|
+
* reside in the same JavaScript environment (node/browser) as their worker and communicate with the
|
|
9
|
+
* appropriate env_xxx.js adapter for NaaN in that environment.
|
|
10
|
+
*
|
|
11
|
+
* Zero or more InTop objects connect to each InBot object via the IMP network, providing remote
|
|
12
|
+
* evaluation and terminal access services. Each InTop supports only one terminal simultaneously, but
|
|
13
|
+
* they can be attached and detached at any time.
|
|
14
|
+
*
|
|
15
|
+
* column positioning: // // !
|
|
16
|
+
*
|
|
17
|
+
* Copyright (c) 2026 by Richard C. Zulch
|
|
18
|
+
*
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
/*
|
|
23
|
+
* InBot
|
|
24
|
+
*
|
|
25
|
+
* Create a remote interface for a single execution instance, returning the object. This provides
|
|
26
|
+
* instance access to zero or more InTops via IMP connections. The tops subscribe to receive
|
|
27
|
+
* notifications/responses.
|
|
28
|
+
*
|
|
29
|
+
* Options:
|
|
30
|
+
* {
|
|
31
|
+
* name: <string> // target name, e.g. "Play"
|
|
32
|
+
* type: <string> // target category, e.g. "Local"
|
|
33
|
+
* startup: <dictionary> // startup options
|
|
34
|
+
* workerID: <string> // initialize worker with this
|
|
35
|
+
* workerGUID: <string> // initialize worker with this
|
|
36
|
+
* topOriginID: <string> // so top can route to us through IMP
|
|
37
|
+
* }
|
|
38
|
+
*
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
closure InBot(worker, options, local inbot)
|
|
42
|
+
{
|
|
43
|
+
global(App)
|
|
44
|
+
inbot = new(object, this)
|
|
45
|
+
inbot.topsubs = []
|
|
46
|
+
options = merge({
|
|
47
|
+
type: "Local"
|
|
48
|
+
}, options)
|
|
49
|
+
inbot.workerID = options.workerID
|
|
50
|
+
inbot.worker = worker
|
|
51
|
+
|
|
52
|
+
inbot.instanceID = UUID()
|
|
53
|
+
inbot.sender = App.imp.register(inbot, {
|
|
54
|
+
name: options.name
|
|
55
|
+
type: options.type
|
|
56
|
+
instanceID: inbot.instanceID
|
|
57
|
+
services: ["naan-worker"]
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
// If this InBot is created remotely then we need to send a message from our new instanceID
|
|
61
|
+
// to the InTop origin so that it can send messages to us. The intermediate gateways save the
|
|
62
|
+
// route to allow replies.
|
|
63
|
+
if options.topOriginID && options.topOriginID != App.imp.us.instanceID
|
|
64
|
+
inbot.sender(options.topOriginID) // create route top->bottom
|
|
65
|
+
|
|
66
|
+
// subscribe
|
|
67
|
+
//
|
|
68
|
+
// Add an InTop to our subscribers if not already known.
|
|
69
|
+
//
|
|
70
|
+
function subscribe(destID) {
|
|
71
|
+
if !inbot.topsubs.find(function(item) { item == destID }) {
|
|
72
|
+
inbot.topsubs.push(destID)
|
|
73
|
+
if inbot.workerDestID
|
|
74
|
+
inbot.sender(destID, {
|
|
75
|
+
id: "workerDestID"
|
|
76
|
+
workerDestID: inbot.workerDestID
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// unsubscribe
|
|
82
|
+
//
|
|
83
|
+
// Remove an InTop from our subscribers if present.
|
|
84
|
+
//
|
|
85
|
+
function unsubscribe(destID) {
|
|
86
|
+
inbot.topsubs = inbot.topsubs.filter(function(item) { item != destID })
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// notifyAll
|
|
90
|
+
//
|
|
91
|
+
// Send the message to all our subscribers.
|
|
92
|
+
//
|
|
93
|
+
inbot.notifyAll = function notifyAll(message, exceptID, local topDestID) {
|
|
94
|
+
for topDestID in inbot.topsubs
|
|
95
|
+
if topDestID != exceptID
|
|
96
|
+
inbot.sender(topDestID, message)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// impUnknown
|
|
100
|
+
//
|
|
101
|
+
// Received an unknown destination notice from the other side.
|
|
102
|
+
//
|
|
103
|
+
inbot.impUnknown = function impUnknown(unknownID, sourceID, gateway) {
|
|
104
|
+
if inbot.topsubs[unknownID] { // did not have courtesy to unsubscribe!
|
|
105
|
+
unsubscribe(unknownID)
|
|
106
|
+
return
|
|
107
|
+
}
|
|
108
|
+
if !gateway
|
|
109
|
+
debuglog("InBot.impUnknownID:", unknownID, "sourceID:", sourceID)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// impReceive
|
|
113
|
+
//
|
|
114
|
+
// Received a message from InTop.
|
|
115
|
+
//
|
|
116
|
+
inbot.impReceive = function impReceive(message, sourceID) {
|
|
117
|
+
if message.id == "destroy"
|
|
118
|
+
destroy() // told to destroy worker
|
|
119
|
+
else if message.id == "subscribe"
|
|
120
|
+
subscribe(sourceID)
|
|
121
|
+
else if message.id == "unsubscribe"
|
|
122
|
+
unsubscribe(sourceID)
|
|
123
|
+
else if message.id == "typed"
|
|
124
|
+
notifyAll(message, sourceID)
|
|
125
|
+
else { // relay to worker
|
|
126
|
+
message.scope = "naan-env-web"
|
|
127
|
+
inbot.worker.postMessage(message)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// onmessage
|
|
132
|
+
//
|
|
133
|
+
// A message was received from the worker.
|
|
134
|
+
//
|
|
135
|
+
inbot.worker.addEventListener("message", function(e, local msg) {
|
|
136
|
+
msg = new(e.data)
|
|
137
|
+
if msg.scope == "naan-imp-web" {
|
|
138
|
+
inbot.impconn.dispatch(msg.data)
|
|
139
|
+
return
|
|
140
|
+
}
|
|
141
|
+
if msg.scope != "naan-env-web"
|
|
142
|
+
return
|
|
143
|
+
if msg.id == "loaded" {
|
|
144
|
+
if !inbot.started
|
|
145
|
+
inbot.worker.postMessage({
|
|
146
|
+
scope: "naan-env-web"
|
|
147
|
+
id: "start",
|
|
148
|
+
state: false // resume state, if we have any
|
|
149
|
+
altcmd: options.startup.initcmds
|
|
150
|
+
workerID: options.workerID
|
|
151
|
+
workerGUID: options.workerGUID
|
|
152
|
+
metadata: {
|
|
153
|
+
mainInstanceID: App.imp.us.instanceID
|
|
154
|
+
name: options.name
|
|
155
|
+
}
|
|
156
|
+
})
|
|
157
|
+
inbot.started = true
|
|
158
|
+
}
|
|
159
|
+
notifyAll(msg)
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
// onerror
|
|
163
|
+
//
|
|
164
|
+
// The worker had an error.
|
|
165
|
+
//
|
|
166
|
+
inbot.worker.addEventListener("error", function(e) {
|
|
167
|
+
debuglog("worker error:", e.message)
|
|
168
|
+
notifyAll({
|
|
169
|
+
id: "error"
|
|
170
|
+
data: e.message
|
|
171
|
+
})
|
|
172
|
+
inbot.worker = false
|
|
173
|
+
destroy()
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
// destroy
|
|
177
|
+
//
|
|
178
|
+
// Ensure the worker is destroyed and notify everyone.
|
|
179
|
+
//
|
|
180
|
+
inbot.destroy = function destroy() {
|
|
181
|
+
if inbot.worker {
|
|
182
|
+
inbot.worker.terminate() // kill without saving
|
|
183
|
+
inbot.worker = false
|
|
184
|
+
}
|
|
185
|
+
notifyAll({
|
|
186
|
+
id: "terminated"
|
|
187
|
+
})
|
|
188
|
+
inbot.impconn.close()
|
|
189
|
+
inbot.impconn = false
|
|
190
|
+
App.imp.unregister(inbot.instanceID)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// register our connects *after* the receivers are configured above
|
|
194
|
+
|
|
195
|
+
// finis
|
|
196
|
+
|
|
197
|
+
inbot
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
/*
|
|
202
|
+
* NubBot
|
|
203
|
+
*
|
|
204
|
+
* Create a remote interface like InBot, but also provide messaging support for WebWorkerNub inside
|
|
205
|
+
* the worker.
|
|
206
|
+
*
|
|
207
|
+
*/
|
|
208
|
+
|
|
209
|
+
closure NubBot(worker, options, local nubbot)
|
|
210
|
+
{
|
|
211
|
+
global(App)
|
|
212
|
+
nubbot = InBot(worker, options)
|
|
213
|
+
|
|
214
|
+
App.imp.addEventListener("active", function(event, info) {
|
|
215
|
+
if info.ourID == nubbot.instanceID {
|
|
216
|
+
nubbot.workerDestID = info.destID // handshake completed
|
|
217
|
+
nubbot.notifyAll({ // send to tops
|
|
218
|
+
id: "workerDestID"
|
|
219
|
+
workerDestID: nubbot.workerDestID
|
|
220
|
+
})
|
|
221
|
+
}
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
nubbot.impconn = App.imp.responder(function(data) {
|
|
225
|
+
nubbot.worker.postMessage({ // send to worker
|
|
226
|
+
scope: "naan-imp-web" // scoped for WorkerNub
|
|
227
|
+
data: data
|
|
228
|
+
})
|
|
229
|
+
list(false, { sent: true })
|
|
230
|
+
}, {
|
|
231
|
+
ourID: nubbot.instanceID
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
// finis
|
|
235
|
+
|
|
236
|
+
nubbot
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
/*
|
|
241
|
+
* workInit
|
|
242
|
+
*
|
|
243
|
+
* Initialize the component.
|
|
244
|
+
*
|
|
245
|
+
*/
|
|
246
|
+
|
|
247
|
+
function workInit(local manifest) {
|
|
248
|
+
manifest = `(InBot, NubBot, workInit)
|
|
249
|
+
|
|
250
|
+
Naan.module.build(module.id, "worker", function(modobj, compobj) {
|
|
251
|
+
require("./browser.nlg")
|
|
252
|
+
compobj.manifest = manifest
|
|
253
|
+
modobj.exports.InBot = InBot
|
|
254
|
+
})
|
|
255
|
+
} ();
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* column positioning: // // !
|
|
8
8
|
*
|
|
9
|
-
* Copyright (c) 2020-
|
|
9
|
+
* Copyright (c) 2020-2026 by Richard C. Zulch
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
@@ -427,22 +427,22 @@ closure TrackServiceWorkers(track, pubID, pubVersion, callback, local error) {
|
|
|
427
427
|
`(error, serviceWorker) = ScopedServiceWorker(pubID, pubVersion, callback)
|
|
428
428
|
if error
|
|
429
429
|
ErrorDebugLog("TrackServiceWorkers: cannot load service worker", error)
|
|
430
|
-
track.register(
|
|
430
|
+
track.register("V-Site", TrackedScope)
|
|
431
431
|
serviceWorker
|
|
432
432
|
};
|
|
433
433
|
|
|
434
434
|
|
|
435
435
|
/*
|
|
436
|
-
*
|
|
436
|
+
* worksInit
|
|
437
437
|
*
|
|
438
438
|
* Initialize the debugger page.
|
|
439
439
|
*
|
|
440
440
|
*/
|
|
441
441
|
|
|
442
|
-
function
|
|
442
|
+
function worksInit(local manifest) {
|
|
443
443
|
|
|
444
444
|
manifest = `(ServiceWorker, ScopedServiceWorker, findTrackedScope, TrackedScope,
|
|
445
|
-
TrackServiceWorkers,
|
|
445
|
+
TrackServiceWorkers, worksInit)
|
|
446
446
|
|
|
447
447
|
Naan.module.build(module.id, "workers", function(modobj, compobj) {
|
|
448
448
|
require("./browser.nlg")
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* column positioning: // // !
|
|
7
7
|
*
|
|
8
|
-
* Copyright (c) 2023-
|
|
8
|
+
* Copyright (c) 2023-2026 by Richard C. Zulch
|
|
9
9
|
*
|
|
10
10
|
*/
|
|
11
11
|
|
|
@@ -13,11 +13,14 @@
|
|
|
13
13
|
/*
|
|
14
14
|
* WebSocket
|
|
15
15
|
*
|
|
16
|
-
* Create a WebSocket client connection object
|
|
16
|
+
* Create a WebSocket client connection object.
|
|
17
|
+
*
|
|
18
|
+
* Optional, default Host:
|
|
19
|
+
* host: <string> // host:port
|
|
17
20
|
*
|
|
18
21
|
* Options:
|
|
19
22
|
* {
|
|
20
|
-
*
|
|
23
|
+
* wss: <boolean> // true to use secure connection
|
|
21
24
|
* onopen: <proc>(e) // connection has opened
|
|
22
25
|
* onmessage: <proc>(e, message) // message received
|
|
23
26
|
* onerror: <proc>(e) // error occurred
|
|
@@ -28,12 +31,14 @@
|
|
|
28
31
|
*
|
|
29
32
|
*/
|
|
30
33
|
|
|
31
|
-
closure WebSocket(options, local wsock) {
|
|
34
|
+
closure WebSocket(host, options, local wsock) {
|
|
32
35
|
global(window)
|
|
33
36
|
wsock = new(object, this)
|
|
34
37
|
options = new(options) || { }
|
|
35
|
-
if !
|
|
36
|
-
|
|
38
|
+
if !host {
|
|
39
|
+
host = window.location.host
|
|
40
|
+
options.wss = window.location.protocol == "https:"
|
|
41
|
+
}
|
|
37
42
|
|
|
38
43
|
// connect
|
|
39
44
|
//
|
|
@@ -42,10 +47,10 @@ closure WebSocket(options, local wsock) {
|
|
|
42
47
|
wsock.connect = closure connect(local url, pending) {
|
|
43
48
|
if wsock.ws
|
|
44
49
|
return (list(false, { open: true }))
|
|
45
|
-
if
|
|
46
|
-
url = "wss://${
|
|
50
|
+
if options.wss
|
|
51
|
+
url = "wss://${host}${options.querys||''}"
|
|
47
52
|
else
|
|
48
|
-
url = "ws://${
|
|
53
|
+
url = "ws://${host}${options.query||''}"
|
|
49
54
|
wsock.ws = xnew(window.WebSocket, url)
|
|
50
55
|
pending = new(nonce)
|
|
51
56
|
|
|
@@ -58,7 +63,7 @@ closure WebSocket(options, local wsock) {
|
|
|
58
63
|
|
|
59
64
|
// onmessage
|
|
60
65
|
// A message was received from the worker via WebSockets.
|
|
61
|
-
wsock.ws.onmessage = closure onmessage(e, message) {
|
|
66
|
+
wsock.ws.onmessage = closure onmessage(e, local message) {
|
|
62
67
|
try {
|
|
63
68
|
message = new(JSONparse(e.data))
|
|
64
69
|
} catch {
|
|
@@ -2,41 +2,70 @@
|
|
|
2
2
|
* apiclient.nlg
|
|
3
3
|
* Naanlib/frameworks/client
|
|
4
4
|
*
|
|
5
|
-
*
|
|
5
|
+
* Access to Nide API with specified HTTP client.
|
|
6
6
|
*
|
|
7
7
|
* column positioning: // // !
|
|
8
8
|
*
|
|
9
|
-
* Copyright (c) 2017-
|
|
9
|
+
* Copyright (c) 2017-2026 by Richard C. Zulch
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
// Globals
|
|
15
|
+
|
|
16
|
+
serviceMod;;
|
|
17
|
+
|
|
18
|
+
|
|
14
19
|
/*
|
|
15
|
-
*
|
|
20
|
+
* APIClient
|
|
21
|
+
*
|
|
22
|
+
* Make an API client object.
|
|
16
23
|
*
|
|
17
|
-
*
|
|
24
|
+
* Options:
|
|
25
|
+
* {
|
|
26
|
+
* url: <string> // hostname or URL of the remote server
|
|
27
|
+
* guid: <string> // shared secret for access to remote server
|
|
28
|
+
* }
|
|
18
29
|
*
|
|
19
30
|
*/
|
|
20
31
|
|
|
21
|
-
closure
|
|
22
|
-
global()
|
|
32
|
+
closure APIClient(imp, options, local client, url, guid) {
|
|
33
|
+
global(App, serviceMod)
|
|
23
34
|
client = new(object, this)
|
|
35
|
+
options = merge({
|
|
36
|
+
}, options)
|
|
37
|
+
url = options.url
|
|
24
38
|
if !url.match(RegExp("^https?:\/\/", "i"))
|
|
25
39
|
url = "http://${url}" // a protocol is required
|
|
26
40
|
if url.slice(-1) != "/"
|
|
27
41
|
url = url.concat("/") // must end in "/"
|
|
28
42
|
client.url = url
|
|
29
|
-
|
|
43
|
+
options.url = url
|
|
44
|
+
client.guid = options.guid
|
|
30
45
|
client.notifyAfter = 0
|
|
31
|
-
client.
|
|
46
|
+
client.requester = clirRequester()
|
|
47
|
+
|
|
48
|
+
//
|
|
49
|
+
// wsopen
|
|
50
|
+
//
|
|
51
|
+
|
|
52
|
+
client.wsopen = function wsopen(error) {
|
|
53
|
+
if !client.wesc {
|
|
54
|
+
client.wesc = WebSocketClient(options, imp)
|
|
55
|
+
`(error) = client.wesc.connect()
|
|
56
|
+
if error
|
|
57
|
+
ErrorDebuglog("APIClient: cannot connect to", url)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
wsopen()
|
|
32
61
|
|
|
33
62
|
// clientRequest() - append our guid to the headers
|
|
34
63
|
|
|
35
|
-
closure clientRequest(url,
|
|
36
|
-
|
|
37
|
-
if guid
|
|
38
|
-
|
|
39
|
-
|
|
64
|
+
closure clientRequest(url, reqops) {
|
|
65
|
+
reqops = merge(reqops)
|
|
66
|
+
if options.guid
|
|
67
|
+
reqops.headers = [list("x-naanlang-api-guid", options.guid)]
|
|
68
|
+
client.requester(url, reqops)
|
|
40
69
|
}
|
|
41
70
|
|
|
42
71
|
//
|
|
@@ -57,7 +86,7 @@ closure NideAPIclient(url, guid, apiRequester, local client) {
|
|
|
57
86
|
timeout: timeoutms
|
|
58
87
|
after: client.notifyAfter
|
|
59
88
|
})
|
|
60
|
-
`(error, content, extra) = clientRequest(url.concat(query))
|
|
89
|
+
`(error, content, extra) = clientRequest(url.concat(query), { retrier: false }) // don't retry inside
|
|
61
90
|
if (error || extra.status != 200)
|
|
62
91
|
cbStatus()
|
|
63
92
|
else if (length(content) == 0)
|
|
@@ -76,8 +105,12 @@ closure NideAPIclient(url, guid, apiRequester, local client) {
|
|
|
76
105
|
// get
|
|
77
106
|
//
|
|
78
107
|
|
|
79
|
-
client.get = closure get(path, options, local error, content, extra) {
|
|
80
|
-
|
|
108
|
+
client.get = closure get(path, options, local requrl, error, content, extra) {
|
|
109
|
+
if App.cache
|
|
110
|
+
requrl = url.concat(EncodeQuery(path.concat("?"), { naanver: App.cache }))
|
|
111
|
+
else
|
|
112
|
+
requrl = url.concat(path)
|
|
113
|
+
`(error, content, extra) = clientRequest(requrl)
|
|
81
114
|
if !error && extra.status != 200
|
|
82
115
|
error = Error("readFile failed: ".concat(extra.status), {
|
|
83
116
|
status: extra.status
|
|
@@ -139,26 +172,178 @@ closure NideAPIclient(url, guid, apiRequester, local client) {
|
|
|
139
172
|
else
|
|
140
173
|
callback(error, content)
|
|
141
174
|
}
|
|
175
|
+
|
|
176
|
+
// destroy
|
|
177
|
+
//
|
|
178
|
+
// Close down the client and release all resources.
|
|
179
|
+
//
|
|
180
|
+
client.destroy = closure destroy() {
|
|
181
|
+
debuglog("client destroy websocket")
|
|
182
|
+
client.wesc.destroy()
|
|
183
|
+
client.wesc = false
|
|
184
|
+
}
|
|
142
185
|
|
|
143
186
|
client
|
|
144
187
|
};
|
|
145
188
|
|
|
146
189
|
|
|
190
|
+
/*
|
|
191
|
+
* WebSocketClient
|
|
192
|
+
*
|
|
193
|
+
* WebSocket client for websocket communications with a server. The Instance Messaage Processor (IMP)
|
|
194
|
+
* handles routing between instances using intermediaries.
|
|
195
|
+
*
|
|
196
|
+
* Options:
|
|
197
|
+
* {
|
|
198
|
+
* url: <string> // hostname or URL of the remote server
|
|
199
|
+
* guid: <string> // shared secret for access to remote server
|
|
200
|
+
* instanceID: <string> // UUID for our client; can be persisted by caller
|
|
201
|
+
* }
|
|
202
|
+
*
|
|
203
|
+
*/
|
|
204
|
+
|
|
205
|
+
closure WebSocketClient(options, imp, local wesc, ws) {
|
|
206
|
+
global(js)
|
|
207
|
+
wesc = new(object, this)
|
|
208
|
+
|
|
209
|
+
// connect
|
|
210
|
+
//
|
|
211
|
+
// Establish a websocket connection with the host.
|
|
212
|
+
//
|
|
213
|
+
wesc.connect = closure connect(local url, pending) {
|
|
214
|
+
if ws && !wesc.fatal
|
|
215
|
+
return(list(false, { connected: true }))
|
|
216
|
+
if wesc.fatal
|
|
217
|
+
debuglog("WebSocketClient reopen")
|
|
218
|
+
wesc.fatal = false
|
|
219
|
+
try {
|
|
220
|
+
url = xnew(js.w.URL, options.url)
|
|
221
|
+
} catch {
|
|
222
|
+
return(list(Error("invalid URL ${options.url}", exception)))
|
|
223
|
+
}
|
|
224
|
+
if url.protocol == "https:"
|
|
225
|
+
url.protocol = "wss:"
|
|
226
|
+
else
|
|
227
|
+
url.protocol = "ws:"
|
|
228
|
+
ws = xnew(js.w.WebSocket, url.href)
|
|
229
|
+
pending = new(nonce)
|
|
230
|
+
|
|
231
|
+
// onopen
|
|
232
|
+
// The WebSocket connection is now open.
|
|
233
|
+
ws.onopen = function onopen(e, local error) {
|
|
234
|
+
wesc.impconn = imp.requester(function(data) {
|
|
235
|
+
if wesc.fatal // raw send function
|
|
236
|
+
return (list(wesc.fatal))
|
|
237
|
+
ws.send(js.w.JSON.stringify(data))
|
|
238
|
+
list(false, { sent: true })
|
|
239
|
+
}, {
|
|
240
|
+
secret: options.guid
|
|
241
|
+
label: "api-server"
|
|
242
|
+
wesc: wesc
|
|
243
|
+
})
|
|
244
|
+
if error
|
|
245
|
+
pending.signal(list(error))
|
|
246
|
+
else
|
|
247
|
+
pending.signal(list(false, { ok: true }))
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// onmessage
|
|
251
|
+
// A message was received via WebSockets.
|
|
252
|
+
ws.onmessage = function onmessage(e, local error, message) {
|
|
253
|
+
`(error, message) = JsonParse(e.data)
|
|
254
|
+
if !error {
|
|
255
|
+
message = new(message)
|
|
256
|
+
if wesc.impconn.dispatch(message)
|
|
257
|
+
return
|
|
258
|
+
}
|
|
259
|
+
debuglog("socket message:", e.data)
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// onerror
|
|
263
|
+
// An error occurred on the WebSocket connection.
|
|
264
|
+
// This can happen in normal operation when the connection times out.
|
|
265
|
+
ws.onerror = function onerror(e) {
|
|
266
|
+
if !wesc.fatal
|
|
267
|
+
wesc.fatal = Error("websocket failed", e)
|
|
268
|
+
pending.signal(list(wesc.fatal))
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// onclose
|
|
272
|
+
// The WebSocket connection was closed.
|
|
273
|
+
ws.onclose = function onclose(e) {
|
|
274
|
+
// if e.code != 1000 && e.code != 1001 && e.code != 1005 && e.code != 1006
|
|
275
|
+
debuglog("WebSocketClient: closed:", e.code, e.reason)
|
|
276
|
+
ws = false
|
|
277
|
+
if !wesc.fatal
|
|
278
|
+
wesc.fatal = Error("websocket closed", e.code, e.reason)
|
|
279
|
+
pending.signal(list(wesc.fatal))
|
|
280
|
+
wesc.impconn.close({
|
|
281
|
+
code: e.code
|
|
282
|
+
reason: e.reason
|
|
283
|
+
})
|
|
284
|
+
wesc.impconn = false
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
pending.wait()
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// close
|
|
291
|
+
//
|
|
292
|
+
// Close the connection, but can be opened again with connect().
|
|
293
|
+
//
|
|
294
|
+
wesc.close = function close() {
|
|
295
|
+
if !ws
|
|
296
|
+
return
|
|
297
|
+
ws.close(1000) // 1000: normal close
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// destroy
|
|
301
|
+
//
|
|
302
|
+
// Destroy the connection permanently.
|
|
303
|
+
//
|
|
304
|
+
wesc.destroy = function destroy() {
|
|
305
|
+
close()
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// send
|
|
309
|
+
//
|
|
310
|
+
// Send a message.
|
|
311
|
+
//
|
|
312
|
+
wesc.send = closure send(data) {
|
|
313
|
+
if wesc.fatal
|
|
314
|
+
return (wesc.fatal)
|
|
315
|
+
wesc.impconn.send(data)
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// sendRaw
|
|
319
|
+
//
|
|
320
|
+
// Send a message with reconnect but no routing.
|
|
321
|
+
//
|
|
322
|
+
wesc.sendRaw = closure sendRaw(data) {
|
|
323
|
+
ws.send(data)
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// finis
|
|
327
|
+
wesc
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
|
|
147
331
|
/*
|
|
148
332
|
* apicInit
|
|
149
333
|
*
|
|
150
|
-
*
|
|
334
|
+
* Initialize the apiclient component.
|
|
151
335
|
*
|
|
152
336
|
*/
|
|
153
337
|
|
|
154
338
|
function apicInit(local manifest) {
|
|
155
339
|
|
|
156
|
-
manifest = `(
|
|
340
|
+
manifest = `(APIClient, WebSocketClient, apicInit)
|
|
157
341
|
|
|
158
342
|
Naan.module.build(module.id, "apiclient", function(modobj, compobj) {
|
|
159
343
|
Naan.module.require("./client.nlg")
|
|
344
|
+
serviceMod = Naan.module.require("naanlib:frameworks/service/imp.nlg")
|
|
160
345
|
compobj.manifest = manifest
|
|
161
|
-
modobj.exports.
|
|
346
|
+
modobj.exports.APIClient = APIClient
|
|
162
347
|
})
|
|
163
348
|
|
|
164
349
|
} ();
|
|
@@ -6,11 +6,31 @@
|
|
|
6
6
|
*
|
|
7
7
|
* column positioning: // // !
|
|
8
8
|
*
|
|
9
|
-
* Copyright (c) 2020-
|
|
9
|
+
* Copyright (c) 2020-2025 by Richard C. Zulch
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
/*
|
|
15
|
+
* clirRequester
|
|
16
|
+
*
|
|
17
|
+
* Return an appropriate HttpsRequestApi client based on our environment.
|
|
18
|
+
*
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
clirRequestApi = false;
|
|
22
|
+
|
|
23
|
+
function clirRequester() {
|
|
24
|
+
if !clirRequestApi {
|
|
25
|
+
if js.w
|
|
26
|
+
clirRequestApi = require("naanlib:frameworks/browser/https_request.nlg").HttpsApiRequest
|
|
27
|
+
else
|
|
28
|
+
clirRequestApi = require("naanlib:frameworks/node/https_request.nlg").HttpsApiRequest
|
|
29
|
+
}
|
|
30
|
+
clirRequestApi
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
|
|
14
34
|
/*
|
|
15
35
|
* clirInit
|
|
16
36
|
*
|
|
@@ -20,7 +40,7 @@
|
|
|
20
40
|
*/
|
|
21
41
|
|
|
22
42
|
function clirInit(local manifest) {
|
|
23
|
-
manifest = `(clirInit)
|
|
43
|
+
manifest = `(clirRequester, clirInit)
|
|
24
44
|
|
|
25
45
|
Naan.module.build(module.id, "client", function(modobj, compobj) {
|
|
26
46
|
require("../common/").LiveImport()
|