@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
package/lib/env_node.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* column positioning: // // !
|
|
8
8
|
*
|
|
9
|
-
* Copyright (c) 2017-
|
|
9
|
+
* Copyright (c) 2017-2024 by Richard C. Zulch
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
@@ -20,6 +20,10 @@
|
|
|
20
20
|
* Currently defined options are:
|
|
21
21
|
* {
|
|
22
22
|
* replDisable: <boolean> // disable REPL and just respond to textCommand in API
|
|
23
|
+
* noWelcome: <boolean> // suppress the welcome banner/messages
|
|
24
|
+
* state: <object> // saved state to load
|
|
25
|
+
* require: <function> // use the specified require function
|
|
26
|
+
* import: <function> // use the specified import function
|
|
23
27
|
* }
|
|
24
28
|
*
|
|
25
29
|
*/
|
|
@@ -29,6 +33,14 @@ exports.NaanNodeREPL = function NaanNodeREPL(options) {
|
|
|
29
33
|
"use strict";
|
|
30
34
|
/*jshint -W024 */
|
|
31
35
|
var undefined;
|
|
36
|
+
|
|
37
|
+
//
|
|
38
|
+
// Persistent
|
|
39
|
+
//
|
|
40
|
+
|
|
41
|
+
var
|
|
42
|
+
kStateFirstVersion = 200, // increment when losing backwards compatbility
|
|
43
|
+
kStateCurrentVersion = 200; // increment when adding features
|
|
32
44
|
|
|
33
45
|
//
|
|
34
46
|
// Initialization
|
|
@@ -48,7 +60,8 @@ exports.NaanNodeREPL = function NaanNodeREPL(options) {
|
|
|
48
60
|
var useConsoleOut = false;
|
|
49
61
|
if (outEnable && process.env["RUNKIT_ENDPOINT_URL"])
|
|
50
62
|
useConsoleOut = true; // use console.log for output in RunKit
|
|
51
|
-
|
|
63
|
+
var dontSaveUntilWorking; // true when state loaded, must be reset to save state again
|
|
64
|
+
var prefs = { };
|
|
52
65
|
|
|
53
66
|
//==========================================================================
|
|
54
67
|
// API
|
|
@@ -58,6 +71,10 @@ exports.NaanNodeREPL = function NaanNodeREPL(options) {
|
|
|
58
71
|
naanlib.js.r = req;
|
|
59
72
|
};
|
|
60
73
|
|
|
74
|
+
this.setImport = function setImport(imp) { // override import function
|
|
75
|
+
naanlib.js.i = imp;
|
|
76
|
+
};
|
|
77
|
+
|
|
61
78
|
this.setDirectory = function setDirectory(path) { // override base directory
|
|
62
79
|
naanlib.js.d = path;
|
|
63
80
|
};
|
|
@@ -94,11 +111,11 @@ exports.NaanNodeREPL = function NaanNodeREPL(options) {
|
|
|
94
111
|
|
|
95
112
|
if (!options.replDisable)
|
|
96
113
|
{
|
|
97
|
-
replServer = repl.start({ prompt: '
|
|
114
|
+
replServer = repl.start({ prompt: '', eval: myEval});
|
|
98
115
|
|
|
99
|
-
replServer.on('line', function(cmd) {
|
|
116
|
+
replServer.on('line', function(cmd) { // process raw command line
|
|
100
117
|
if (cmd.charAt(0) != ".") {
|
|
101
|
-
textToRemoteTerm(cmd);
|
|
118
|
+
textToRemoteTerm("\x1b[90m\x1b[3m".concat(cmd, "\x1b[0m\n"));
|
|
102
119
|
naanlib.textLine(cmd + "\n");
|
|
103
120
|
}
|
|
104
121
|
});
|
|
@@ -139,7 +156,8 @@ exports.NaanNodeREPL = function NaanNodeREPL(options) {
|
|
|
139
156
|
if (termops)
|
|
140
157
|
{
|
|
141
158
|
setTimeout(function () {
|
|
142
|
-
termops
|
|
159
|
+
if (termops) // avoid crashing if it went away during delay
|
|
160
|
+
termops.debugtext(text, level);
|
|
143
161
|
}, 1);
|
|
144
162
|
}
|
|
145
163
|
} else if (outEnable) {
|
|
@@ -237,7 +255,7 @@ exports.NaanNodeREPL = function NaanNodeREPL(options) {
|
|
|
237
255
|
else if (msg.id == "keyline")
|
|
238
256
|
{ // typing from remote terminal
|
|
239
257
|
setTimeout(function () {
|
|
240
|
-
process.stdout.write(msg.text
|
|
258
|
+
process.stdout.write("\x1b[90m\x1b[3m".concat(msg.text.trim(), "\x1b[0m\r\n"));
|
|
241
259
|
outEnable = true;
|
|
242
260
|
naanlib.textLine(msg.text); // keyboard text from terminal
|
|
243
261
|
}, 1);
|
|
@@ -288,14 +306,85 @@ exports.NaanNodeREPL = function NaanNodeREPL(options) {
|
|
|
288
306
|
}
|
|
289
307
|
}, 1000);
|
|
290
308
|
|
|
309
|
+
|
|
310
|
+
//==========================================================================
|
|
311
|
+
// State load/save
|
|
312
|
+
//--------------------------------------------------------------------------
|
|
313
|
+
|
|
314
|
+
// SavePref
|
|
315
|
+
//
|
|
316
|
+
// Save a persistent preference object that can be retrieved in the future.
|
|
317
|
+
//
|
|
318
|
+
this.SavePref = function SavePref(key, value) {
|
|
319
|
+
return (prefs[key] = value);
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
// LoadPref
|
|
323
|
+
//
|
|
324
|
+
// Load a previously-saved preference object for future retrieval.
|
|
325
|
+
//
|
|
326
|
+
this.LoadPref = function LoadPref(key) {
|
|
327
|
+
return (prefs[key]);
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
// Working
|
|
331
|
+
//
|
|
332
|
+
// Note that the application is working, so it is safe to save state.
|
|
333
|
+
//
|
|
334
|
+
this.Working = function Working() {
|
|
335
|
+
dontSaveUntilWorking = false;
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
// MakeState
|
|
339
|
+
//
|
|
340
|
+
// Save our state into a new object.
|
|
341
|
+
//
|
|
342
|
+
this.MakeState = function MakeState() {
|
|
343
|
+
if (dontSaveUntilWorking)
|
|
344
|
+
return (false); // didn't get far enough to call Working()
|
|
345
|
+
var statedoc = {};
|
|
346
|
+
statedoc.curversion = kStateCurrentVersion;
|
|
347
|
+
statedoc.firstver = kStateFirstVersion;
|
|
348
|
+
statedoc.date = new Date().toISOString();
|
|
349
|
+
statedoc.prefs = prefs;
|
|
350
|
+
statedoc.naan = naanlib.saveState(true); // true to optimize, which is a bit slower
|
|
351
|
+
return (statedoc);
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
// loadState
|
|
355
|
+
//
|
|
356
|
+
// Load our state from an object.
|
|
357
|
+
//
|
|
358
|
+
function loadState(statedoc) {
|
|
359
|
+
dontSaveUntilWorking = true; // make sure we don't store bad state
|
|
360
|
+
if (typeof(statedoc) != "object" || typeof(statedoc.naan) != "string"
|
|
361
|
+
|| statedoc.firstver > kStateCurrentVersion
|
|
362
|
+
|| statedoc.curversion < kStateFirstVersion)
|
|
363
|
+
{
|
|
364
|
+
return (false);
|
|
365
|
+
}
|
|
366
|
+
if (typeof(statedoc.prefs) == "object")
|
|
367
|
+
prefs = statedoc.prefs;
|
|
368
|
+
return (statedoc.naan);
|
|
369
|
+
}
|
|
370
|
+
|
|
291
371
|
|
|
292
372
|
//==========================================================================
|
|
293
373
|
// Start the Naan Interpreter
|
|
294
374
|
//--------------------------------------------------------------------------
|
|
375
|
+
if (options.require)
|
|
376
|
+
naanlib.js.r = options.require;
|
|
377
|
+
if (options.import)
|
|
378
|
+
naanlib.js.i = options.import;
|
|
295
379
|
var startOptions = {};
|
|
296
380
|
if (options.noWelcome)
|
|
297
381
|
startOptions.noWelcome = true;
|
|
298
|
-
|
|
382
|
+
else
|
|
383
|
+
naanlib.banner();
|
|
384
|
+
if (options.state)
|
|
385
|
+
startOptions.state = loadState(options.state);
|
|
386
|
+
if (options.cmd)
|
|
387
|
+
startOptions.cmd = options.cmd;
|
|
299
388
|
naanlib.start(startOptions);
|
|
300
389
|
};
|
|
301
390
|
|
package/lib/env_nodeworker.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* column positioning: // // !
|
|
8
8
|
*
|
|
9
|
-
* Copyright (c) 2019-
|
|
9
|
+
* Copyright (c) 2019-2024 by Richard C. Zulch
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
@@ -28,6 +28,18 @@ exports.NaanWorkerActivate = function NaanWorkerActivate(cbReady) {
|
|
|
28
28
|
cbReady(naancon);
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
+
if (process.execArgv.includes("--inspect-brk"))
|
|
32
|
+
{
|
|
33
|
+
import("node:inspector").then(
|
|
34
|
+
function (inspector) {
|
|
35
|
+
inspector.open();
|
|
36
|
+
inspector.waitForDebugger();
|
|
37
|
+
/*jshint debug:true */
|
|
38
|
+
debugger;
|
|
39
|
+
}
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
31
43
|
/*
|
|
32
44
|
* NaanControllerNodeWorker
|
|
33
45
|
*
|
|
@@ -59,7 +71,11 @@ exports.NaanWorkerActivate = function NaanWorkerActivate(cbReady) {
|
|
|
59
71
|
this.setRequire = function setRequire(req) { // override require function
|
|
60
72
|
naanlib.js.r = req;
|
|
61
73
|
};
|
|
62
|
-
|
|
74
|
+
|
|
75
|
+
this.setImport = function setImport(imp) { // override import function
|
|
76
|
+
naanlib.js.i = imp;
|
|
77
|
+
};
|
|
78
|
+
|
|
63
79
|
this.setDirectory = function setDirectory(path) { // override base directory
|
|
64
80
|
naanlib.js.d = path;
|
|
65
81
|
};
|
|
@@ -140,7 +156,7 @@ exports.NaanWorkerActivate = function NaanWorkerActivate(cbReady) {
|
|
|
140
156
|
if (msg.altcmd)
|
|
141
157
|
naanlib.textLine(msg.altcmd);
|
|
142
158
|
/*
|
|
143
|
-
### already started ###
|
|
159
|
+
### already started by textLine() if not before ###
|
|
144
160
|
naanlib.start({
|
|
145
161
|
state: msg.state,
|
|
146
162
|
cmd: msg.altcmd
|
|
@@ -161,10 +177,12 @@ exports.NaanWorkerActivate = function NaanWorkerActivate(cbReady) {
|
|
|
161
177
|
}
|
|
162
178
|
});
|
|
163
179
|
|
|
180
|
+
naanlib.banner();
|
|
181
|
+
naanlib.start(false); // start explicitly
|
|
182
|
+
|
|
164
183
|
msgPort.postMessage({
|
|
165
184
|
id: "loaded" // initialization complete
|
|
166
185
|
});
|
|
167
|
-
naanlib.banner();
|
|
168
186
|
}
|
|
169
187
|
|
|
170
188
|
};
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* column positioning: // // !
|
|
8
8
|
*
|
|
9
|
-
* Copyright (c) 2019-
|
|
9
|
+
* Copyright (c) 2019-2023 by Richard C. Zulch
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
@@ -71,6 +71,10 @@ exports.handler = function awsHandler(event, context, callback) {
|
|
|
71
71
|
naanlib.js.r = req;
|
|
72
72
|
};
|
|
73
73
|
|
|
74
|
+
this.setImport = function setImport(imp) { // override import function
|
|
75
|
+
naanlib.js.i = imp;
|
|
76
|
+
};
|
|
77
|
+
|
|
74
78
|
this.setDirectory = function setDirectory(path) { // override base directory
|
|
75
79
|
naanlib.js.d = path;
|
|
76
80
|
};
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* column positioning: // // !
|
|
8
8
|
*
|
|
9
|
-
* Copyright (c) 2021-
|
|
9
|
+
* Copyright (c) 2021-2024 by Richard C. Zulch
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
*/
|
|
37
37
|
|
|
38
38
|
closure TimeKeys(prefix, baseMS, local timekeys, prelen) {
|
|
39
|
+
global(UUID)
|
|
39
40
|
if !UUID.proc
|
|
40
41
|
throw("TimeKeys requires UUID()")
|
|
41
42
|
timekeys = new(object, this)
|
|
@@ -158,6 +159,7 @@ closure TimeKeys(prefix, baseMS, local timekeys, prelen) {
|
|
|
158
159
|
*/
|
|
159
160
|
|
|
160
161
|
closure DynaConverter(local conv) {
|
|
162
|
+
global()
|
|
161
163
|
conv = new(object, this)
|
|
162
164
|
|
|
163
165
|
// fieldTypeDynToNaan
|
|
@@ -250,7 +252,7 @@ closure DynaConverter(local conv) {
|
|
|
250
252
|
if dtype == "BS" || dtype == "SS"
|
|
251
253
|
dvalue
|
|
252
254
|
else if dtype == "NS"
|
|
253
|
-
new(dvalue).map(Number.parseFloat(item))
|
|
255
|
+
new(dvalue).map(function(item) { Number.parseFloat(item) })
|
|
254
256
|
else if dtype == "M"
|
|
255
257
|
recordDynToNaan(dvalue)
|
|
256
258
|
else if dtype == "L"
|
|
@@ -294,12 +296,24 @@ closure DynaConverter(local conv) {
|
|
|
294
296
|
// genAttributeNameAdder
|
|
295
297
|
//
|
|
296
298
|
// Return a function that, when called repeatedly, adds attribute name definitions to the
|
|
297
|
-
// specified parameter block.
|
|
299
|
+
// specified parameter block. You can specify an attribute name that is a nested key expression
|
|
300
|
+
// resulting from makePath() below. This adds the individual components to the
|
|
301
|
+
// ExpressionAttributeNames list, but generates a return string that references nested elements.
|
|
298
302
|
//
|
|
299
303
|
closure genAttributeNameAdder(params, local adex) {
|
|
300
304
|
function attrib(name, local key, data) {
|
|
301
|
-
if
|
|
302
|
-
name = tostring(name)
|
|
305
|
+
if numeric(name) || name && symbol(name)
|
|
306
|
+
name = tostring(name)
|
|
307
|
+
else if !string(name) || string.trim().length == 0 { // compound or invalid
|
|
308
|
+
if tuple(name) && name.length == 3 {
|
|
309
|
+
if name.0 == `. // quote(a.b) => (`. a b)
|
|
310
|
+
return (strcat(attrib(name.1), ".", attrib(name.2)))
|
|
311
|
+
else if name.0 == `deref && integer(name.2) // quote(a[99]) => (`deref a 99)
|
|
312
|
+
return (strcat(attrib(name.1), "[", attrib(name.2), "]"))
|
|
313
|
+
}
|
|
314
|
+
debuglog("DynaConverter.genAttributeNameAdder: can't make expression from", typeof(name), name)
|
|
315
|
+
name = tostring(name)
|
|
316
|
+
}
|
|
303
317
|
if !params.ExpressionAttributeNames
|
|
304
318
|
params.ExpressionAttributeNames = { }
|
|
305
319
|
if !adex
|
|
@@ -313,6 +327,33 @@ closure DynaConverter(local conv) {
|
|
|
313
327
|
}
|
|
314
328
|
}
|
|
315
329
|
|
|
330
|
+
|
|
331
|
+
// makePath
|
|
332
|
+
//
|
|
333
|
+
// Make an attribute path expression that genAttributeNameAdder can use to make an evaluated
|
|
334
|
+
// attribute path. Specify a path expression and then list the symbols that should be evaluated,
|
|
335
|
+
// if any. For example:
|
|
336
|
+
// > b=4
|
|
337
|
+
// > makePath(a.b.c, b)
|
|
338
|
+
// $: a.4.c
|
|
339
|
+
//
|
|
340
|
+
conv.makePath = macro makePath args {
|
|
341
|
+
function mkatpa(path, local exp) {
|
|
342
|
+
exp = path.map(function(el) {
|
|
343
|
+
if tuple(el)
|
|
344
|
+
exp = mkatpa(el)
|
|
345
|
+
else if member(el, args)
|
|
346
|
+
car(el)
|
|
347
|
+
else
|
|
348
|
+
el
|
|
349
|
+
})
|
|
350
|
+
if !tuple(exp) || exp.0 != `deref || integer(exp.2)
|
|
351
|
+
exp
|
|
352
|
+
else
|
|
353
|
+
cons(`., cdr(exp)) // use `. for dictionary lookup
|
|
354
|
+
} (pop(args))
|
|
355
|
+
}
|
|
356
|
+
|
|
316
357
|
// genAttributeValueAdder
|
|
317
358
|
//
|
|
318
359
|
// Return a function that, when called repeatedly, adds attribute value definitions to the
|
|
@@ -357,7 +398,7 @@ closure DynaConverter(local conv) {
|
|
|
357
398
|
//
|
|
358
399
|
// condition dictionary:
|
|
359
400
|
// {
|
|
360
|
-
// key: <keyname, e.g. partition key name or path
|
|
401
|
+
// key: <keyname, e.g. partition key name or path> // required
|
|
361
402
|
// "=": <expression> // exclusive of other comparisons
|
|
362
403
|
// ">": <expression> // exclusive of other comparisons
|
|
363
404
|
// "<": <expression> // exclusive of other comparisons
|
|
@@ -368,9 +409,12 @@ closure DynaConverter(local conv) {
|
|
|
368
409
|
// exists: <boolean> // keyname existence matches boolean
|
|
369
410
|
// }
|
|
370
411
|
//
|
|
412
|
+
// The key entry above can be a keyname or a path expression resulting from a call to makePath().
|
|
413
|
+
// Please note that array subscripts, which index into lists, must be integers.
|
|
414
|
+
//
|
|
371
415
|
|
|
372
416
|
conv.createKeyExpression = function createKeyExpression(params, conds
|
|
373
|
-
local attval, attname, rangename, range, keyname, op, expr, rangex, arg1, arg2) {
|
|
417
|
+
local attval, attname, rangename, ranges, range, keyname, op, expr, rangex, arg1, arg2) {
|
|
374
418
|
|
|
375
419
|
// dupeop
|
|
376
420
|
//
|
|
@@ -389,18 +433,9 @@ closure DynaConverter(local conv) {
|
|
|
389
433
|
ranges = [ranges]
|
|
390
434
|
for range in ranges {
|
|
391
435
|
rangex = false
|
|
392
|
-
if
|
|
393
|
-
keyname = []
|
|
394
|
-
expr = range.key
|
|
395
|
-
while expr
|
|
396
|
-
keyname.push(attname(pop(expr)))
|
|
397
|
-
keyname = keyname.join(".")
|
|
398
|
-
}
|
|
399
|
-
else
|
|
400
|
-
keyname = tostring(range.key)
|
|
401
|
-
if !keyname
|
|
436
|
+
if !range.key
|
|
402
437
|
return (list(Error("key expression requires key name")))
|
|
403
|
-
keyname = attname(
|
|
438
|
+
keyname = attname(range.key)
|
|
404
439
|
for `(op, expr) in range {
|
|
405
440
|
if op == "key"
|
|
406
441
|
continue
|
|
@@ -465,7 +500,7 @@ closure DynaConverter(local conv) {
|
|
|
465
500
|
// value is a standard (error, data) tuple where the data is just { ok: true } if no error.
|
|
466
501
|
//
|
|
467
502
|
conv.createUpdateExpression = function createUpdateExpression(params, updates,
|
|
468
|
-
local attval, attname, output, item) {
|
|
503
|
+
local attval, attname, output, item, key, value) {
|
|
469
504
|
|
|
470
505
|
if params.UpdateExpression
|
|
471
506
|
return (list(Error("conv.createUpdateExpression: UpdateExpression already exists")))
|
|
@@ -519,13 +554,28 @@ closure DynaConverter(local conv) {
|
|
|
519
554
|
*
|
|
520
555
|
* DynamoDB table.
|
|
521
556
|
*
|
|
557
|
+
* options:
|
|
558
|
+
* {
|
|
559
|
+
* hashKey: <symbol> // hash key symbol
|
|
560
|
+
* rangeKey: <symbol> // range key symbol (requres hash key)
|
|
561
|
+
* }
|
|
562
|
+
*
|
|
522
563
|
*/
|
|
523
564
|
|
|
524
|
-
closure DynaTable(dyna, tablename, local table) {
|
|
565
|
+
closure DynaTable(dyna, tablename, options, local table) {
|
|
566
|
+
global()
|
|
525
567
|
table = new(object, this)
|
|
526
568
|
table.name = tablename
|
|
527
569
|
if dyna.ycdb
|
|
528
570
|
table.ycdb = true // Yandex Cloud version of DynamoDB
|
|
571
|
+
if options.hashKey || options.rangeKey {
|
|
572
|
+
if options.hashKey && symbol(options.hashKey) && symbol(options.rangeKey) { // NB: false is a symbol
|
|
573
|
+
table.hashKey = options.hashKey
|
|
574
|
+
if options.rangeKey
|
|
575
|
+
table.rangeKey = options.rangeKey
|
|
576
|
+
} else
|
|
577
|
+
debuglog("DynaTable: invalid (hashKey/rangeKey) option:", tablename, list(options.hashKey, options.rangeKey))
|
|
578
|
+
}
|
|
529
579
|
|
|
530
580
|
// info
|
|
531
581
|
//
|
|
@@ -544,13 +594,25 @@ closure DynaTable(dyna, tablename, local table) {
|
|
|
544
594
|
// rangeType: <name> // string | numeric (if exists)
|
|
545
595
|
// }
|
|
546
596
|
//
|
|
547
|
-
|
|
597
|
+
// 20240313: Note that the YCDB / AWS DynamoDB SDK combination has been found to occasionally
|
|
598
|
+
// fail to execute the callback within 30 seconds with multiple overlapping info() calls. In the
|
|
599
|
+
// current example three calls to a single table are made from inside asyncArray(), and all of
|
|
600
|
+
// them get hung up here lacking a callback. Unclear whether the cause is the SDK, the lack of
|
|
601
|
+
// using the newer promises API, or the underlying YDB service. The main reason this is called
|
|
602
|
+
// is to discover the hash and range keys, which can be statically specified. The pragmatic
|
|
603
|
+
// resolution is to allow the caller to specify the keys, which is faster and should avoid this
|
|
604
|
+
// particular challenge. But also info() now combines overlapping calls.
|
|
605
|
+
//
|
|
606
|
+
table.info = closure info(local params, pending, result) {
|
|
548
607
|
if !dyna.aws
|
|
549
608
|
return (list(Error("DynaTable.info: not logged into AWS")))
|
|
550
609
|
params = {
|
|
551
610
|
TableName: tablename
|
|
552
611
|
}
|
|
612
|
+
if (pending = table.info_pending)
|
|
613
|
+
return (pending.wait()) // use the first call's results
|
|
553
614
|
pending = new(nonce)
|
|
615
|
+
table.info_pending = pending // combine overlapping info() calls
|
|
554
616
|
dyna.aws.describeTable(params, function(error, data, local output, key) {
|
|
555
617
|
if error {
|
|
556
618
|
dynaTableError = error
|
|
@@ -581,7 +643,9 @@ closure DynaTable(dyna, tablename, local table) {
|
|
|
581
643
|
}
|
|
582
644
|
pending.signal(list(error, output))
|
|
583
645
|
})
|
|
584
|
-
pending.wait()
|
|
646
|
+
result = pending.wait()
|
|
647
|
+
table.info_pending = false
|
|
648
|
+
result
|
|
585
649
|
}
|
|
586
650
|
|
|
587
651
|
// create
|
|
@@ -817,7 +881,7 @@ closure DynaTable(dyna, tablename, local table) {
|
|
|
817
881
|
|
|
818
882
|
|
|
819
883
|
table.deleteRecord = closure deleteRecord(hashValue, rangeValue, options,
|
|
820
|
-
local params, pending) {
|
|
884
|
+
local params, error, pending) {
|
|
821
885
|
if preflightError(this)
|
|
822
886
|
return
|
|
823
887
|
params = {
|
|
@@ -881,7 +945,6 @@ closure DynaTable(dyna, tablename, local table) {
|
|
|
881
945
|
// indexHashKey: <string> // name of index hashKey
|
|
882
946
|
// range: <key-expression> // range of keys to retrieve
|
|
883
947
|
// filter: <key-expression> // conditions for items to return
|
|
884
|
-
// attmap: <dictionary> // map attribute names to #shortcuts
|
|
885
948
|
// reverse: <boolean> // reverse order
|
|
886
949
|
// project: <array> // project only specified attributes
|
|
887
950
|
// paging: <exclusive start key> // for paging; updated each call
|
|
@@ -924,8 +987,6 @@ closure DynaTable(dyna, tablename, local table) {
|
|
|
924
987
|
if error
|
|
925
988
|
return (list(Error("table.queryRecords filter:", error)))
|
|
926
989
|
}
|
|
927
|
-
if options.attmap
|
|
928
|
-
params.ExpressionAttributeNames = options.attmap
|
|
929
990
|
if options.reverse
|
|
930
991
|
params.ScanIndexForward = false
|
|
931
992
|
if options.project
|
|
@@ -955,7 +1016,6 @@ closure DynaTable(dyna, tablename, local table) {
|
|
|
955
1016
|
// {
|
|
956
1017
|
// index: <string> // index name in table
|
|
957
1018
|
// filter: <key-expression> // conditions for items to return
|
|
958
|
-
// attmap: <dictionary> // map attribute names to #shortcuts
|
|
959
1019
|
// project: <array> // project only specified attributes
|
|
960
1020
|
// paging: <exclusive start key> // for paging; updated each call
|
|
961
1021
|
// consistentRead: <boolean> // true to use consistent read
|
|
@@ -980,8 +1040,6 @@ closure DynaTable(dyna, tablename, local table) {
|
|
|
980
1040
|
if error
|
|
981
1041
|
return (list(Error("table.scanRecords filter:", error)))
|
|
982
1042
|
}
|
|
983
|
-
if options.attmap
|
|
984
|
-
params.ExpressionAttributeNames = attmap
|
|
985
1043
|
if options.project
|
|
986
1044
|
params.ProjectionExpression = options.project.join(", ")
|
|
987
1045
|
if options.paging
|
|
@@ -1016,6 +1074,7 @@ closure DynaTable(dyna, tablename, local table) {
|
|
|
1016
1074
|
*/
|
|
1017
1075
|
|
|
1018
1076
|
closure DynamoDB(local dyna) {
|
|
1077
|
+
global(awsSDK)
|
|
1019
1078
|
dyna = new(object, this)
|
|
1020
1079
|
dyna.timeout = 20000 // default 20-second timeout
|
|
1021
1080
|
dyna.conv = DynaConverter()
|
|
@@ -1045,12 +1104,12 @@ closure DynamoDB(local dyna) {
|
|
|
1045
1104
|
// table
|
|
1046
1105
|
//
|
|
1047
1106
|
// Return a new table access object for the table of the specified name. The table may not
|
|
1048
|
-
// yet exist; see DynaTable for methods.
|
|
1107
|
+
// yet exist; see DynaTable for options and methods.
|
|
1049
1108
|
//
|
|
1050
|
-
dyna.table = function table(name) {
|
|
1109
|
+
dyna.table = function table(name, options) {
|
|
1051
1110
|
if !dyna.aws
|
|
1052
1111
|
debuglog("DynamoDB.table: database not open:", name)
|
|
1053
|
-
DynaTable(dyna, name)
|
|
1112
|
+
DynaTable(dyna, name, options)
|
|
1054
1113
|
}
|
|
1055
1114
|
|
|
1056
1115
|
// timekeys
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* column positioning: // // !
|
|
8
8
|
*
|
|
9
|
-
* Copyright (c) 2020-
|
|
9
|
+
* Copyright (c) 2020-2024 by Richard C. Zulch
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
@@ -318,7 +318,7 @@ closure dawsTable(database, path, tableOptions, local table) {
|
|
|
318
318
|
//
|
|
319
319
|
// For browsers, convert a ReadableStream to an array buffer.
|
|
320
320
|
//
|
|
321
|
-
|
|
321
|
+
closure readableStreamToArrayBuffer(stream, local result, reader, error, data, chunk) {
|
|
322
322
|
result = xnew(js.w.Uint8Array, 0)
|
|
323
323
|
reader = stream.getReader()
|
|
324
324
|
loop {
|
|
@@ -480,7 +480,7 @@ closure dawsTable(database, path, tableOptions, local table) {
|
|
|
480
480
|
table.delete = closure delete(doc, callback) {
|
|
481
481
|
if !callback
|
|
482
482
|
return (syncAdapter(delete, doc))
|
|
483
|
-
if !doc._id || !doc._id.startsWith(table.s3prefix) || !(doc._md5 || doc._id.slice(-1) == "/")
|
|
483
|
+
if !doc._id || !doc._id.startsWith(table.s3prefix) || !(doc.md5 || doc._md5 || doc._id.slice(-1) == "/")
|
|
484
484
|
return (asyncResult(callback, Error("invalid argument")))
|
|
485
485
|
database.bucket.delete(doc._id, function(error, resp) {
|
|
486
486
|
if error
|
|
@@ -537,8 +537,8 @@ closure dawsTable(database, path, tableOptions, local table) {
|
|
|
537
537
|
database.bucket.get(id, true, function(error, resp) {
|
|
538
538
|
if error
|
|
539
539
|
error = Error("md5 failed", error, id, { status: error.status })
|
|
540
|
-
else if resp.
|
|
541
|
-
resp = resp.
|
|
540
|
+
else if resp.md5
|
|
541
|
+
resp = resp.md5
|
|
542
542
|
else
|
|
543
543
|
resp = ""
|
|
544
544
|
callback(error, resp)
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* column positioning: // // !
|
|
8
8
|
*
|
|
9
|
-
* Copyright (c) 2020-
|
|
9
|
+
* Copyright (c) 2020-2024 by Richard C. Zulch
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
@@ -202,7 +202,7 @@ closure psmaConnector(psm, local connClassID, connector, watch) {
|
|
|
202
202
|
info = {
|
|
203
203
|
classID: "AWS S3"
|
|
204
204
|
type: "AWS S3 bucket"
|
|
205
|
-
services: ["
|
|
205
|
+
services: ["NideDB", "NideFS"]
|
|
206
206
|
}
|
|
207
207
|
`(error, creds) = connector.vault.accessResource(resID)
|
|
208
208
|
if creds {
|
package/test/harness.nlg
CHANGED
package/test/test_01_core.nlg
CHANGED
|
@@ -616,12 +616,12 @@ RegisterTestCategory("core", [
|
|
|
616
616
|
["oo;", "{ true: a, 0.3333333333333333: b }"],
|
|
617
617
|
|
|
618
618
|
//
|
|
619
|
-
// object getters
|
|
619
|
+
// object getters/setters
|
|
620
620
|
//
|
|
621
621
|
|
|
622
622
|
[ "oo=new(object);", "Object{0}" ],
|
|
623
|
-
[ "oo['..prop'] = function(
|
|
624
|
-
[ "oo['.=prop'] = function(value) { oo.value = value };>", "(function lambda (value) (Lib::set\\. oo (`` value) value))" ],
|
|
623
|
+
[ "oo@['..prop'] = function() { oo.value };>", "(function lambda false (`. oo value))" ],
|
|
624
|
+
[ "oo@['.=prop'] = function(value) { oo.value = value };>", "(function lambda (value) (Lib::set\\. oo (`` value) value))" ],
|
|
625
625
|
[ "oo.prop = 8;", "8" ],
|
|
626
626
|
[ "oo.prop;", "8" ],
|
|
627
627
|
[ "oo['prop'] = 9;", "9" ],
|
|
@@ -633,8 +633,34 @@ RegisterTestCategory("core", [
|
|
|
633
633
|
[ "xset(oo, 'prop', 11);", "11" ],
|
|
634
634
|
[ "oo.prop;", "11" ],
|
|
635
635
|
[ "oo.value;", "10" ],
|
|
636
|
-
[ "oo.*;", '("
|
|
636
|
+
[ "oo.*;", '("value", "prop")' ],
|
|
637
637
|
|
|
638
|
+
//
|
|
639
|
+
// object hierarchy
|
|
640
|
+
//
|
|
641
|
+
|
|
642
|
+
[ "oo=new(object);", "Object{0}" ],
|
|
643
|
+
[ "op=new(object);", "Object{0}" ],
|
|
644
|
+
[ "oc=new(object);", "Object{0}" ],
|
|
645
|
+
[ "oo@['.class'] = oc;", "Object{0}" ],
|
|
646
|
+
[ "oc@['.parent'] = op;", "Object{0}" ],
|
|
647
|
+
[ "op.print = function (x) { print(self.id) };>", "(function lambda (x) (print (`. self id)))" ],
|
|
648
|
+
[ "oo.id = `oo;", "oo" ],
|
|
649
|
+
[ "oc.id = `oc;", "oc" ],
|
|
650
|
+
[ "op.id = `op;", "op" ],
|
|
651
|
+
[ "oo.*", '("id")' ],
|
|
652
|
+
[ "oc.*", '("id")' ],
|
|
653
|
+
[ "op.*", '("print", "id")' ],
|
|
654
|
+
[ "oo.print()", "oo|oo" ],
|
|
655
|
+
|
|
656
|
+
[ "op@['..prop'] = function() { self.value };>", "(function lambda false (`. self value))" ],
|
|
657
|
+
[ "oc@['.=prop'] = function(value) { self.value = value };>", "(function lambda (value) (Lib::set\\. self (`` value) value))" ],
|
|
658
|
+
[ "op.prop = op55;", "op55" ],
|
|
659
|
+
[ "oo.prop;", "false" ],
|
|
660
|
+
[ "op.prop;", "op55" ],
|
|
661
|
+
[ "oo.prop = oo11;", "oo11" ],
|
|
662
|
+
[ "oo.prop;", "oo11" ],
|
|
663
|
+
[ "oc.prop;", "false" ],
|
|
638
664
|
|
|
639
665
|
//
|
|
640
666
|
// object hooks
|