@naanlang/naan 1.4.3 → 1.5.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 +22 -3
- package/dist/env_web.js +2 -2
- package/dist/naan.min.js +6 -6
- package/frameworks/browser/browser.nlg +2 -2
- package/frameworks/browser/https_request.nlg +39 -10
- package/frameworks/browser/sworker.js +23 -23
- package/frameworks/browser/terminals.nlg +33 -24
- package/frameworks/client/apiclient.nlg +31 -18
- package/frameworks/client/client.nlg +22 -2
- package/frameworks/client/psm_client.nlg +12 -3
- package/frameworks/node/filesystem.nlg +9 -7
- package/frameworks/node/gitter.nlg +9 -7
- package/frameworks/node/http_request.nlg +103 -0
- package/frameworks/node/https_request.nlg +42 -9
- package/frameworks/node/node.nlg +1 -0
- package/frameworks/node/shell.nlg +4 -4
- package/frameworks/node/worker.nlg +1 -0
- package/frameworks/project/projects.nlg +2 -1
- package/frameworks/running/executors.nlg +26 -7
- package/frameworks/running/taskexec.nlg +48 -14
- 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 +9 -9
- package/lib/browser/env_webworker.js +1 -2
- package/lib/core/naanlib.js +6 -6
- package/lib/env_node.js +9 -3
- package/lib/node_repl_init.nlg +2 -2
- package/package.json +1 -1
- package/plugins/nodeLib/node_util.nlg +14 -7
- package/plugins/openSearch/os_dynamo.nlg +5 -5
- package/plugins/serviceAws/aws_cloudwatchlogs.nlg +2 -1
- package/plugins/serviceAws/aws_dynamo.nlg +26 -8
- 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/aws_vm.nlg +198 -0
- package/plugins/serviceAws/dbt_aws.nlg +22 -20
- package/plugins/serviceAws/psm_aws.nlg +2 -1
- package/plugins/serviceGC/gc_api.nlg +1 -0
- package/plugins/serviceNexara/serviceNexara.nlg +32 -0
- package/plugins/serviceNexara/speechrec.nlg +160 -0
- package/plugins/serviceYC/serviceYC.nlg +2 -3
- package/plugins/serviceYC/yc_speechrec.nlg +4 -3
- package/plugins/serviceYC/yc_vm.nlg +44 -54
- package/test/test_01_core.nlg +42 -33
- package/test/test_03_jsinterop.nlg +35 -6
- 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) 2020-
|
|
9
|
+
* Copyright (c) 2020-2025 by Richard C. Zulch
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
@@ -56,7 +56,7 @@ closure Gitter(fs, local gitt) {
|
|
|
56
56
|
asyncResult(callback, false, gitt.roots[repopath])
|
|
57
57
|
execOptions = {
|
|
58
58
|
cmdargs: ["rev-parse", "--show-toplevel"]
|
|
59
|
-
|
|
59
|
+
spawnops: {
|
|
60
60
|
cwd: repopath
|
|
61
61
|
}
|
|
62
62
|
outLE: "\x00" // lines delimited by nulls
|
|
@@ -117,7 +117,7 @@ closure Gitter(fs, local gitt) {
|
|
|
117
117
|
repopath = fs.path.resolve(fs.rootpath, repopath)
|
|
118
118
|
execOptions = {
|
|
119
119
|
cmdargs: ["log", "-z", "--pretty=format:%H|%an|%ae|%aI|%B"]
|
|
120
|
-
|
|
120
|
+
spawnops: {
|
|
121
121
|
cwd: repopath
|
|
122
122
|
}
|
|
123
123
|
outLE: "\x00" // lines delimited by nulls
|
|
@@ -168,10 +168,12 @@ closure Gitter(fs, local gitt) {
|
|
|
168
168
|
if !string(repopath)
|
|
169
169
|
return (asyncResult(callback, Error("invalid path:", repopath)))
|
|
170
170
|
repopath = fs.path.resolve(fs.rootpath, repopath)
|
|
171
|
-
|
|
171
|
+
bridge = fs.path.normalize(bridgePath(repopath)) // ensure it works for our FS
|
|
172
|
+
if bridge == "."
|
|
173
|
+
bridge = "" // no bridge in this case
|
|
172
174
|
execOptions = {
|
|
173
175
|
cmdargs: ["status", "-z", "--porcelain"]
|
|
174
|
-
|
|
176
|
+
spawnops: {
|
|
175
177
|
cwd: repopath
|
|
176
178
|
}
|
|
177
179
|
outLE: "\x00" // lines delimited by nulls
|
|
@@ -219,13 +221,13 @@ closure Gitter(fs, local gitt) {
|
|
|
219
221
|
repopath = fs.path.resolve(fs.rootpath, repopath)
|
|
220
222
|
execOptions = {
|
|
221
223
|
cmdargs: ["show", options.hash.concat(":", bridgePath(repopath), gitPath(options.pathspec))]
|
|
222
|
-
|
|
224
|
+
spawnops: {
|
|
223
225
|
cwd: repopath
|
|
224
226
|
}
|
|
225
227
|
}
|
|
226
228
|
nodecp.execFile("git",
|
|
227
229
|
execOptions.cmdargs,
|
|
228
|
-
execOptions.
|
|
230
|
+
execOptions.spawnops,
|
|
229
231
|
function(err, stdout, stderr) {
|
|
230
232
|
if err
|
|
231
233
|
callback(Error("git failed", err, stderr))
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* http_request.nlg
|
|
3
|
+
*
|
|
4
|
+
* Http operations for NodeJS.
|
|
5
|
+
*
|
|
6
|
+
* column positioning: // // !
|
|
7
|
+
*
|
|
8
|
+
* Copyright (c) 2024-2025 by Richard C. Zulch
|
|
9
|
+
*
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
/* HttpRequest
|
|
14
|
+
*
|
|
15
|
+
* Make a simple http request. This is not supported by the standard NaaN framework HttpsApiRequest
|
|
16
|
+
* because it is not SSL.
|
|
17
|
+
*
|
|
18
|
+
* Options:
|
|
19
|
+
* {
|
|
20
|
+
* method: <string> // HTTP method (defaults to GET)
|
|
21
|
+
* headers: <dictionary> // key/data pairs
|
|
22
|
+
* putdata: <data> // data to put
|
|
23
|
+
* json: <boolean> // ignore content-type of return and decode JSON
|
|
24
|
+
* }
|
|
25
|
+
*
|
|
26
|
+
* The return is a result tuple comprising:
|
|
27
|
+
* (<error>, -- false on success
|
|
28
|
+
* <content>, -- returned data
|
|
29
|
+
* {
|
|
30
|
+
* status: <integer> -- http response status
|
|
31
|
+
* headers: <array> -- headers if available
|
|
32
|
+
* })
|
|
33
|
+
*
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
closure HttpRequest(url, options,
|
|
37
|
+
local pending, chunks, params, request) {
|
|
38
|
+
global(js, nodeHttp)
|
|
39
|
+
pending = new(nonce)
|
|
40
|
+
chunks = []
|
|
41
|
+
params = {
|
|
42
|
+
method: options.method || undefined
|
|
43
|
+
headers: options.headers || undefined
|
|
44
|
+
}
|
|
45
|
+
request = nodeHttp.request(url, params, closure (response) {
|
|
46
|
+
response.on("data", function (chunk) {
|
|
47
|
+
chunks.push(chunk)
|
|
48
|
+
})
|
|
49
|
+
response.on("end", function (local content, error) {
|
|
50
|
+
content = js.g.Buffer.concat(chunks)
|
|
51
|
+
if response.statusCode < 200 || response.statusCode >= 299 { // 299 is "cancelled"
|
|
52
|
+
debuglog("httpRequest status:", url, response.statusCode, "message:", content)
|
|
53
|
+
pending.signal(list(Error("httpRequest statusCode:", url, response.statusCode, {
|
|
54
|
+
status: response.statusCode
|
|
55
|
+
}), false, { status: response.statusCode }))
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
if response.complete {
|
|
59
|
+
content = content.toString()
|
|
60
|
+
if options.json
|
|
61
|
+
`(error, content) = JsonParse(content)
|
|
62
|
+
}
|
|
63
|
+
else
|
|
64
|
+
error = Error("httpRequest terminated prematurely:", url, response.statusCode)
|
|
65
|
+
pending.signal(list(error, content, {
|
|
66
|
+
headers: response.headers
|
|
67
|
+
status: response.statusCode
|
|
68
|
+
contentType: response.headers["content-type"]
|
|
69
|
+
}))
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
request.on("error", function hre(error) {
|
|
73
|
+
debuglog("httpRequest error:", url, "error:", error)
|
|
74
|
+
error = Error("httpRequest failed:", url, error)
|
|
75
|
+
pending.signal(list(error))
|
|
76
|
+
})
|
|
77
|
+
if options.putdata {
|
|
78
|
+
if string(options.putdata)
|
|
79
|
+
request.write(options.putdata)
|
|
80
|
+
else
|
|
81
|
+
request.write(JSONstringify(options.putdata))
|
|
82
|
+
}
|
|
83
|
+
request.end()
|
|
84
|
+
pending.wait()
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
/*
|
|
89
|
+
* http_nodeInit
|
|
90
|
+
*
|
|
91
|
+
* Initialize HTTP request operations for NodeJS.
|
|
92
|
+
*
|
|
93
|
+
*/
|
|
94
|
+
|
|
95
|
+
function http_nodeInit(local manifest) {
|
|
96
|
+
manifest = `(HttpRequest, http_nodeInit)
|
|
97
|
+
|
|
98
|
+
Naan.module.build(module.id, "http_request", function(modobj, compobj) {
|
|
99
|
+
require("./node.nlg")
|
|
100
|
+
compobj.manifest = manifest
|
|
101
|
+
modobj.exports.HttpRequest = HttpRequest
|
|
102
|
+
})
|
|
103
|
+
}();
|
|
@@ -47,7 +47,8 @@
|
|
|
47
47
|
*/
|
|
48
48
|
|
|
49
49
|
closure httpsApiReqOnce(url, options,
|
|
50
|
-
local urlq, reqOptions, putdata, item, xurl, pending, chunks, request) {
|
|
50
|
+
local urlq, reqOptions, putdata, error, item, xurl, pending, chunks, request) {
|
|
51
|
+
global(js, nodeHttps, nodeUrl)
|
|
51
52
|
if options.query
|
|
52
53
|
urlq = url.concat(EncodeQuery("?", options.query))
|
|
53
54
|
else
|
|
@@ -126,7 +127,10 @@ closure httpsApiReqOnce(url, options,
|
|
|
126
127
|
debuglog("HttpsApiRequest status:", url, response.statusCode, "message:", content)
|
|
127
128
|
pending.signal(list(Error("HttpsApiRequest statusCode:", url, response.statusCode, {
|
|
128
129
|
status: response.statusCode
|
|
129
|
-
}), false, {
|
|
130
|
+
}), false, {
|
|
131
|
+
headers: response.headers
|
|
132
|
+
status: response.statusCode
|
|
133
|
+
}))
|
|
130
134
|
return
|
|
131
135
|
}
|
|
132
136
|
if response.complete {
|
|
@@ -234,28 +238,56 @@ closure sseParserFactory(message, local parser) {
|
|
|
234
238
|
*
|
|
235
239
|
*/
|
|
236
240
|
|
|
237
|
-
closure HttpsApiRequest(url, options,
|
|
241
|
+
closure HttpsApiRequest(url, options,
|
|
242
|
+
local delayf, parser, decoder, error, content, status) {
|
|
243
|
+
global(stringDecoder, common)
|
|
244
|
+
//
|
|
245
|
+
// defRetrier -- backoff and retry selected errors
|
|
246
|
+
//
|
|
247
|
+
function defRetrier(error, headers, local retryAfter, retryms, waitms) {
|
|
248
|
+
if member(error.status, `(408, 500, 502, 503, 504)) { // retriable errors
|
|
249
|
+
retryAfter = headers["retry-after"]
|
|
250
|
+
if retryAfter {
|
|
251
|
+
retryms = toint(retryAfter) * 1000
|
|
252
|
+
if !retryms
|
|
253
|
+
retryms = Date(retryAfter).getTime() - Date.now()
|
|
254
|
+
if retryms < 0
|
|
255
|
+
retryms = false
|
|
256
|
+
}
|
|
257
|
+
if !delayf
|
|
258
|
+
delayf = common.ExbackFactory(12,30) // 12s max backoff, 30s deadline
|
|
259
|
+
waitms = delayf()
|
|
260
|
+
if !waitms || waitms < retryms
|
|
261
|
+
waitms = retryms
|
|
262
|
+
if options.debug
|
|
263
|
+
debuglog("HttpsApiRequest.defRetrier: wait ${waitms} msec using retry-after ${retryAfter}")
|
|
264
|
+
if waitms // return false to give up
|
|
265
|
+
sleep(waitms) // return true from sleep to retry
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
options = merge({ retrier: defRetrier }, options)
|
|
238
270
|
if options.sseHook {
|
|
239
271
|
parser = sseParserFactory(options.sseHook)
|
|
240
272
|
decoder = xnew(stringDecoder.StringDecoder, "utf8")
|
|
241
|
-
options = merge(
|
|
273
|
+
options = merge({
|
|
242
274
|
streamHook: function(chunk) {
|
|
243
275
|
if chunk
|
|
244
276
|
parser.write(decoder.write(chunk)) // data arrived
|
|
245
277
|
else
|
|
246
278
|
parser.write(decoder.end()) // end of data
|
|
247
279
|
}
|
|
248
|
-
})
|
|
280
|
+
}, options)
|
|
249
281
|
}
|
|
250
282
|
loop {
|
|
251
|
-
|
|
252
|
-
if
|
|
253
|
-
if !options.retrier(
|
|
283
|
+
`(error, content, status) = httpsApiReqOnce(url, options)
|
|
284
|
+
if error && error.status { // check for retries
|
|
285
|
+
if !options.retrier(error, status.headers)
|
|
254
286
|
break
|
|
255
287
|
} else
|
|
256
288
|
break
|
|
257
289
|
}
|
|
258
|
-
|
|
290
|
+
list(error, content, status)
|
|
259
291
|
};
|
|
260
292
|
|
|
261
293
|
|
|
@@ -271,6 +303,7 @@ function https_nodeInit(local manifest) {
|
|
|
271
303
|
|
|
272
304
|
Naan.module.build(module.id, "https_request", function(modobj, compobj) {
|
|
273
305
|
require("./node.nlg")
|
|
306
|
+
common = require("../common/utils.nlg")
|
|
274
307
|
compobj.manifest = manifest
|
|
275
308
|
modobj.exports.HttpsApiRequest = HttpsApiRequest
|
|
276
309
|
})
|
package/frameworks/node/node.nlg
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* column positioning: // // !
|
|
8
8
|
*
|
|
9
|
-
* Copyright (c) 2024 by Richard C. Zulch
|
|
9
|
+
* Copyright (c) 2024-2025 by Richard C. Zulch
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
@@ -68,9 +68,9 @@ closure ShellConnect(options, local shob, error, fs, xparser, shelly) {
|
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
if cwd
|
|
71
|
-
xlparams.
|
|
71
|
+
xlparams.spawnops = { cwd: cwd }
|
|
72
72
|
else if options.cwd
|
|
73
|
-
xlparams.
|
|
73
|
+
xlparams.spawnops = { cwd: options.cwd }
|
|
74
74
|
fs.execLines(cmd, xlparams, function(error, result) {
|
|
75
75
|
evalactive(list(`assign, `(compress, "$$"), report)) // assign to $$ in active namespace
|
|
76
76
|
pending.signal(list(error, merge(result, report)))
|
|
@@ -80,7 +80,7 @@ closure ShellConnect(options, local shob, error, fs, xparser, shelly) {
|
|
|
80
80
|
|
|
81
81
|
// shellparser
|
|
82
82
|
// intercept command-line parsing and look for a !shell escape
|
|
83
|
-
function shellparser(source, grammar, operators, local token) {
|
|
83
|
+
function shellparser(source, grammar, operators, local token, text) {
|
|
84
84
|
token = source.tokenread()
|
|
85
85
|
if token.atom == `! {
|
|
86
86
|
source.tokenlast(true) // "consume" the ! token
|
|
@@ -296,7 +296,7 @@ closure projConnector(projman, projtype, local connector, watch) {
|
|
|
296
296
|
*
|
|
297
297
|
* Nide.proj/
|
|
298
298
|
* nide_cliser.cfg - JSON definition of the project (see below)
|
|
299
|
-
* NaanIDE_version.txt - [default] substitution file defining 1.
|
|
299
|
+
* NaanIDE_version.txt - [default] substitution file defining 1.5.0+1 etc.
|
|
300
300
|
* NaanIDE_version_builds.txt - [default] contains current build number as decimal string
|
|
301
301
|
* build/ - [optional] default build output location
|
|
302
302
|
* NaanIDE.lic - [optional] defines Zulch Laboratories, Inc. and other license info
|
|
@@ -692,6 +692,7 @@ closure projConfig(projman, where, projID, local procon, fs, configpath) {
|
|
|
692
692
|
erase: true
|
|
693
693
|
overwrite: true
|
|
694
694
|
follow: true
|
|
695
|
+
cacheControl: publish.cacheControlFiles
|
|
695
696
|
})
|
|
696
697
|
}
|
|
697
698
|
|
|
@@ -22,6 +22,7 @@ closure ExecutorBase(track, type, name, local exec) {
|
|
|
22
22
|
exec = new(object, this)
|
|
23
23
|
exec.type = type
|
|
24
24
|
exec.name = name
|
|
25
|
+
exec.contexts = { }
|
|
25
26
|
|
|
26
27
|
// execReset
|
|
27
28
|
//
|
|
@@ -57,6 +58,18 @@ closure ExecutorBase(track, type, name, local exec) {
|
|
|
57
58
|
// The specified message was received from the instance, so signal it.
|
|
58
59
|
//
|
|
59
60
|
exec.execReceived = closure execReceived(data, local pending) {
|
|
61
|
+
if data.reverse {
|
|
62
|
+
if !exec.contexts[data.contextID]
|
|
63
|
+
return // ignore unknown context
|
|
64
|
+
if !exec.reverseDispatch
|
|
65
|
+
exec.reverseDispatch = require("./taskexec.nlg").TaskDispatcher(function(data) {
|
|
66
|
+
data.reverse = true
|
|
67
|
+
exec.PostMessage(data)
|
|
68
|
+
}, exec.contexts[data.contextID].coder)
|
|
69
|
+
data.reverse = false
|
|
70
|
+
exec.reverseDispatch(data)
|
|
71
|
+
return
|
|
72
|
+
}
|
|
60
73
|
pending = exec.pending[data.xid]
|
|
61
74
|
if !pending { // not waiting on message
|
|
62
75
|
if data.xmsg == "xready"
|
|
@@ -110,7 +123,7 @@ closure ExecutorBase(track, type, name, local exec) {
|
|
|
110
123
|
// persistent reconnection. Currently this does not ever reclaim old contexts on the instance
|
|
111
124
|
// end of things, but it should. ###
|
|
112
125
|
//
|
|
113
|
-
exec.context = closure context(state, local error, retries, ms, delta, excon) {
|
|
126
|
+
exec.context = closure context(state, coder, local error, retries, ms, delta, excon) {
|
|
114
127
|
retries = 0
|
|
115
128
|
ms = milliseconds()
|
|
116
129
|
delta = 1
|
|
@@ -127,8 +140,14 @@ closure ExecutorBase(track, type, name, local exec) {
|
|
|
127
140
|
sleep(delta)
|
|
128
141
|
if delta < 100
|
|
129
142
|
delta *= 2 }
|
|
130
|
-
excon = ExecutorContext(exec, state)
|
|
131
|
-
excon.register()
|
|
143
|
+
excon = ExecutorContext(exec, state, coder)
|
|
144
|
+
`(error) = excon.register()
|
|
145
|
+
if error
|
|
146
|
+
list(error)
|
|
147
|
+
else {
|
|
148
|
+
exec.contexts[excon.state.contextID] = excon
|
|
149
|
+
list(false, excon)
|
|
150
|
+
}
|
|
132
151
|
}
|
|
133
152
|
|
|
134
153
|
// attention
|
|
@@ -160,7 +179,7 @@ closure ExecutorBase(track, type, name, local exec) {
|
|
|
160
179
|
*
|
|
161
180
|
*/
|
|
162
181
|
|
|
163
|
-
closure ExecutorContext(exec, state, local context,
|
|
182
|
+
closure ExecutorContext(exec, state, coder, local context, util) {
|
|
164
183
|
global(apply)
|
|
165
184
|
context = new(object, this)
|
|
166
185
|
if !state || !state.contextID
|
|
@@ -168,7 +187,7 @@ closure ExecutorContext(exec, state, local context, coder, util) {
|
|
|
168
187
|
contextID: UUID()
|
|
169
188
|
}
|
|
170
189
|
context.state = state
|
|
171
|
-
coder = packageCoder(evalf)
|
|
190
|
+
context.coder = coder || packageCoder(evalf)
|
|
172
191
|
util = modlist().Util.exports
|
|
173
192
|
|
|
174
193
|
// getState
|
|
@@ -208,7 +227,7 @@ closure ExecutorContext(exec, state, local context, coder, util) {
|
|
|
208
227
|
pkg = util.pkgSave(`expr, roots, {
|
|
209
228
|
nsignore: true
|
|
210
229
|
comments: true
|
|
211
|
-
objectEncoder: coder.encodeObject
|
|
230
|
+
objectEncoder: context.coder.encodeObject
|
|
212
231
|
})
|
|
213
232
|
data = {
|
|
214
233
|
contextID: state.contextID
|
|
@@ -224,7 +243,7 @@ closure ExecutorContext(exec, state, local context, coder, util) {
|
|
|
224
243
|
localroots: false
|
|
225
244
|
intern: "symbols"
|
|
226
245
|
execute: false
|
|
227
|
-
objectDecoder: coder.decodeObject
|
|
246
|
+
objectDecoder: context.coder.decodeObject
|
|
228
247
|
})))
|
|
229
248
|
else
|
|
230
249
|
result
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* column positioning: // // !
|
|
7
7
|
*
|
|
8
|
-
* Copyright (c) 2021-
|
|
8
|
+
* Copyright (c) 2021-2025 by Richard C. Zulch
|
|
9
9
|
*
|
|
10
10
|
*/
|
|
11
11
|
|
|
@@ -19,27 +19,57 @@
|
|
|
19
19
|
*
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
closure TaskDispatcher(replier, local contexts, gohome,
|
|
22
|
+
closure TaskDispatcher(replier, coder, local contexts, gohome, util) {
|
|
23
23
|
global()
|
|
24
24
|
contexts = { }
|
|
25
25
|
gohome = makeActivator()
|
|
26
|
-
coder = packageCoder(evalf)
|
|
27
26
|
util = modlist().Util.exports
|
|
28
|
-
|
|
29
|
-
//
|
|
27
|
+
|
|
28
|
+
// ContextCoder
|
|
30
29
|
//
|
|
31
|
-
//
|
|
30
|
+
// A context-specific coder allowing remote-initiated communication.
|
|
32
31
|
//
|
|
33
|
-
closure
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
closure ContextCoder(contextID, _guid, dynreply, local exec, error, context) {
|
|
33
|
+
contexts[contextID].coder = packageCoder(closure evalf(expr) {
|
|
34
|
+
if !exec {
|
|
35
|
+
exec = require("./executors.nlg").ExecutorBase(false, "reverser", "TD")
|
|
36
|
+
exec.PostMessage = closure(data) {
|
|
37
|
+
if data.xmsg == "xstart" { // report ready locally
|
|
38
|
+
data.xmsg = "xready"
|
|
39
|
+
exec.execReceived(data)
|
|
40
|
+
} else {
|
|
41
|
+
data.reverse = true
|
|
42
|
+
data._guid = _guid || undefined
|
|
43
|
+
dynreply(data)
|
|
44
|
+
}
|
|
45
|
+
list(false)
|
|
46
|
+
}
|
|
47
|
+
contexts[contextID].execReceived = exec.execReceived // needed for registered reply
|
|
48
|
+
`(error, context) = exec.context({
|
|
49
|
+
contextID: contextID
|
|
50
|
+
}, contexts[contextID].coder)
|
|
51
|
+
if error {
|
|
52
|
+
ErrorDebuglog("TaskDispatcher.ContextCoder failed:", error)
|
|
53
|
+
exec = false
|
|
54
|
+
return (list(error))
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
context.eval(expr)
|
|
58
|
+
})
|
|
36
59
|
}
|
|
37
60
|
|
|
38
61
|
// dispatcher
|
|
39
62
|
//
|
|
40
63
|
// Return a new task dispatcher.
|
|
41
64
|
//
|
|
42
|
-
closure dispatcher(data, local reply, restorens, context, expr, roots, proc, args, result) {
|
|
65
|
+
closure dispatcher(data, dynreply, local reply, restorens, context, expr, roots, proc, args, result) {
|
|
66
|
+
if data.reverse {
|
|
67
|
+
data.reverse = false
|
|
68
|
+
contexts[data.contextID].execReceived(data)
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
if !dynreply
|
|
72
|
+
dynreply = replier
|
|
43
73
|
reply = {
|
|
44
74
|
xmsg: "error"
|
|
45
75
|
xid: data.xid
|
|
@@ -62,6 +92,10 @@ closure TaskDispatcher(replier, local contexts, gohome, coder, util) {
|
|
|
62
92
|
namespace: nsactive()
|
|
63
93
|
activator: gohome
|
|
64
94
|
}
|
|
95
|
+
if coder
|
|
96
|
+
context.coder = coder
|
|
97
|
+
else
|
|
98
|
+
ContextCoder(data.contextID, data._guid, dynreply)
|
|
65
99
|
restorens()
|
|
66
100
|
reply.xmsg = "registered"
|
|
67
101
|
reply.contextID = data.contextID
|
|
@@ -85,11 +119,11 @@ closure TaskDispatcher(replier, local contexts, gohome, coder, util) {
|
|
|
85
119
|
intern: true // intern symbols
|
|
86
120
|
localroots: true // make roots local if needed to intern
|
|
87
121
|
execute: true // execute auto-running procedures
|
|
88
|
-
objectDecoder: coder.decodeObject
|
|
122
|
+
objectDecoder: context.coder.decodeObject
|
|
89
123
|
})
|
|
90
124
|
expr = caar(expr)
|
|
91
125
|
if expr.0 == `\.invoke\-method
|
|
92
|
-
expr = coder.invokeMethod(expr.1, expr.2)
|
|
126
|
+
expr = context.coder.invokeMethod(expr.1, expr.2) // object method, args
|
|
93
127
|
else
|
|
94
128
|
expr = evalactive(expr) // evaluate expression
|
|
95
129
|
reply.error = undefined
|
|
@@ -104,7 +138,7 @@ closure TaskDispatcher(replier, local contexts, gohome, coder, util) {
|
|
|
104
138
|
reply.result = util.pkgSave(`expr, roots, {
|
|
105
139
|
nsignore: true // don't report namespace errors
|
|
106
140
|
comments: true
|
|
107
|
-
objectEncoder: coder.encodeObject})
|
|
141
|
+
objectEncoder: context.coder.encodeObject}) // include comments
|
|
108
142
|
if context.namespace !== nsactive().0
|
|
109
143
|
{ // evaluation changed namespace
|
|
110
144
|
context.namespace = nsactive().0
|
|
@@ -154,7 +188,7 @@ closure TaskDispatcher(replier, local contexts, gohome, coder, util) {
|
|
|
154
188
|
} finally {
|
|
155
189
|
restorens()
|
|
156
190
|
}
|
|
157
|
-
|
|
191
|
+
dynreply(reply)
|
|
158
192
|
}
|
|
159
193
|
};
|
|
160
194
|
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* column positioning: // // !
|
|
9
9
|
*
|
|
10
|
-
* Copyright (c) 2017-
|
|
10
|
+
* Copyright (c) 2017-2025 by Richard C. Zulch
|
|
11
11
|
*
|
|
12
12
|
*/
|
|
13
13
|
|
|
@@ -76,21 +76,31 @@ closure dapoTable(database, rootpath, local table, prefix) {
|
|
|
76
76
|
|
|
77
77
|
// add
|
|
78
78
|
//
|
|
79
|
-
// Add a new record to the table with
|
|
80
|
-
//
|
|
81
|
-
|
|
82
|
-
|
|
79
|
+
// Add a new record to the table with options. This will fail if the key already exists. Optional
|
|
80
|
+
// metadata is stored with the record and cannot be changed without calling update. Metadata keys
|
|
81
|
+
// cannot begin with underscore ("_").
|
|
82
|
+
//
|
|
83
|
+
// The returned document, which can be used with update, has the following keys:
|
|
84
|
+
// key - the key name of the record
|
|
85
|
+
// content - the body content stored under in the record
|
|
86
|
+
// _md5 - the MD5 hash of the content
|
|
87
|
+
// _length - the length of the content
|
|
88
|
+
// _id - the PouchDB ID
|
|
89
|
+
// _rev - the PouchDB revision
|
|
90
|
+
// <...> - various metadata keys
|
|
91
|
+
|
|
92
|
+
table.add = closure add(key, content, options, callback, local id, doc) {
|
|
83
93
|
if !callback
|
|
84
|
-
return (syncAdapter(add, key, content,
|
|
94
|
+
return (syncAdapter(add, key, content, options))
|
|
85
95
|
id = makeID(key)
|
|
86
96
|
if !string(id)
|
|
87
97
|
return (asyncResult(callback, id)) // error from makeID
|
|
88
|
-
if
|
|
98
|
+
if options.metadata && !dictionary(options.metadata)
|
|
89
99
|
return (asyncResult(callback, Error("invalid arguments")))
|
|
90
100
|
if !database.pouch
|
|
91
101
|
return (asyncResult(callback, Error("database closed")))
|
|
92
|
-
if metadata {
|
|
93
|
-
doc = new(metadata)
|
|
102
|
+
if options.metadata {
|
|
103
|
+
doc = new(options.metadata) // start with metadata
|
|
94
104
|
doc._rev = undefined } // _rev not allowed in metadata
|
|
95
105
|
else
|
|
96
106
|
doc = { }
|
|
@@ -115,12 +125,13 @@ closure dapoTable(database, rootpath, local table, prefix) {
|
|
|
115
125
|
|
|
116
126
|
// update
|
|
117
127
|
//
|
|
118
|
-
// Update a record in the table using the document previously returned by add or get.
|
|
119
|
-
//
|
|
128
|
+
// Update a record in the table using the document previously returned by add or get. Specify
|
|
129
|
+
// options to update those values, otherwise existing/default values are used. This will fail
|
|
130
|
+
// if the record has been modified by someone else in the meantime.
|
|
120
131
|
|
|
121
|
-
table.update = closure update(doc, callback) {
|
|
132
|
+
table.update = closure update(doc, options, callback) {
|
|
122
133
|
if !callback
|
|
123
|
-
return (syncAdapter(update, doc))
|
|
134
|
+
return (syncAdapter(update, doc, options))
|
|
124
135
|
if !doc._id || !doc._id.startsWith(table.prefix) || !doc._rev
|
|
125
136
|
return (asyncResult(callback, Error("invalid argument")))
|
|
126
137
|
if makeID(doc.key) != doc._id
|
|
@@ -130,6 +141,8 @@ closure dapoTable(database, rootpath, local table, prefix) {
|
|
|
130
141
|
doc = new(doc)
|
|
131
142
|
doc._md5 = undefined
|
|
132
143
|
doc._length = undefined
|
|
144
|
+
if options.metadata
|
|
145
|
+
doc = merge(doc, options.metadata)
|
|
133
146
|
database.pouch.put(doc, function(error, resp) {
|
|
134
147
|
if error
|
|
135
148
|
error = Error("update failed", error)
|