@naanlang/naan 1.5.0 → 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 +2 -2
- package/bin/index.js +7 -3
- package/dist/env_web.js +162 -20
- package/dist/naan.min.js +5 -5
- package/frameworks/browser/https_request.nlg +2 -2
- package/frameworks/browser/sworker.js +28 -26
- package/frameworks/browser/terminals.nlg +329 -693
- 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 +181 -9
- 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 +31 -6
- 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/worker.nlg +409 -539
- package/frameworks/node/ws_client.nlg +2 -2
- package/frameworks/project/build.nlg +1 -1
- package/frameworks/project/projects.nlg +1 -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/lib/browser/env_web.js +169 -27
- package/lib/browser/env_webworker.js +91 -4
- 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/serviceNexara/speechrec.nlg +10 -6
- package/test/test_02_context.nlg +24 -1
|
@@ -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,43 +2,63 @@
|
|
|
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
|
|
16
21
|
*
|
|
17
|
-
*
|
|
22
|
+
* Make an API client object.
|
|
18
23
|
*
|
|
19
24
|
* Options:
|
|
20
25
|
* {
|
|
21
26
|
* url: <string> // hostname or URL of the remote server
|
|
22
27
|
* guid: <string> // shared secret for access to remote server
|
|
23
|
-
* instanceID: <string> // optional UUID for our client; can be persisted by caller
|
|
24
28
|
* }
|
|
25
29
|
*
|
|
26
30
|
*/
|
|
27
31
|
|
|
28
|
-
closure APIClient(options, local client, url, guid) {
|
|
29
|
-
global(App)
|
|
32
|
+
closure APIClient(imp, options, local client, url, guid) {
|
|
33
|
+
global(App, serviceMod)
|
|
30
34
|
client = new(object, this)
|
|
35
|
+
options = merge({
|
|
36
|
+
}, options)
|
|
31
37
|
url = options.url
|
|
32
38
|
if !url.match(RegExp("^https?:\/\/", "i"))
|
|
33
39
|
url = "http://${url}" // a protocol is required
|
|
34
40
|
if url.slice(-1) != "/"
|
|
35
41
|
url = url.concat("/") // must end in "/"
|
|
36
42
|
client.url = url
|
|
43
|
+
options.url = url
|
|
37
44
|
client.guid = options.guid
|
|
38
|
-
client.instanceID = options.instanceID || UUID() // our client's unique ID
|
|
39
45
|
client.notifyAfter = 0
|
|
40
46
|
client.requester = clirRequester()
|
|
41
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()
|
|
61
|
+
|
|
42
62
|
// clientRequest() - append our guid to the headers
|
|
43
63
|
|
|
44
64
|
closure clientRequest(url, reqops) {
|
|
@@ -152,24 +172,176 @@ closure APIClient(options, local client, url, guid) {
|
|
|
152
172
|
else
|
|
153
173
|
callback(error, content)
|
|
154
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
|
+
}
|
|
155
185
|
|
|
156
186
|
client
|
|
157
187
|
};
|
|
158
188
|
|
|
159
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
|
+
|
|
160
331
|
/*
|
|
161
332
|
* apicInit
|
|
162
333
|
*
|
|
163
|
-
*
|
|
334
|
+
* Initialize the apiclient component.
|
|
164
335
|
*
|
|
165
336
|
*/
|
|
166
337
|
|
|
167
338
|
function apicInit(local manifest) {
|
|
168
339
|
|
|
169
|
-
manifest = `(APIClient, apicInit)
|
|
340
|
+
manifest = `(APIClient, WebSocketClient, apicInit)
|
|
170
341
|
|
|
171
342
|
Naan.module.build(module.id, "apiclient", function(modobj, compobj) {
|
|
172
343
|
Naan.module.require("./client.nlg")
|
|
344
|
+
serviceMod = Naan.module.require("naanlib:frameworks/service/imp.nlg")
|
|
173
345
|
compobj.manifest = manifest
|
|
174
346
|
modobj.exports.APIClient = APIClient
|
|
175
347
|
})
|