@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.
Files changed (45) hide show
  1. package/LICENSE.md +2 -2
  2. package/README.md +13 -11
  3. package/bin/index.js +4 -4
  4. package/dist/naan.min.js +14 -14
  5. package/frameworks/browser/https_request.nlg +26 -10
  6. package/frameworks/browser/sworker.js +26 -25
  7. package/frameworks/browser/terminals.nlg +118 -55
  8. package/frameworks/browser/ws_client.nlg +124 -0
  9. package/frameworks/client/apiclient.nlg +82 -116
  10. package/frameworks/client/psm_client.nlg +37 -16
  11. package/frameworks/client/relaycon.nlg +308 -0
  12. package/frameworks/common/common.nlg +1 -1
  13. package/frameworks/common/utils.nlg +40 -2
  14. package/frameworks/node/apiserver.nlg +342 -103
  15. package/frameworks/node/https_request.nlg +32 -6
  16. package/frameworks/node/node.nlg +60 -2
  17. package/frameworks/node/psm_server.nlg +11 -7
  18. package/frameworks/node/worker.nlg +22 -9
  19. package/frameworks/node/ws_client.nlg +127 -0
  20. package/frameworks/project/build.nlg +3 -2
  21. package/frameworks/project/projects.nlg +8 -2
  22. package/frameworks/running/debugnub.nlg +2 -2
  23. package/frameworks/running/debugutil.nlg +1 -1
  24. package/frameworks/running/executors.nlg +69 -17
  25. package/frameworks/running/sourcecode.nlg +2 -2
  26. package/frameworks/running/taskexec.nlg +25 -25
  27. package/frameworks/storage/dbt_pouch.nlg +8 -6
  28. package/frameworks/storage/file_manager.nlg +6 -3
  29. package/frameworks/storage/psm.nlg +5 -3
  30. package/frameworks/storage/psm_dbtables.nlg +75 -53
  31. package/frameworks/storage/resources.nlg +4 -2
  32. package/lib/browser/env_web.js +6 -6
  33. package/lib/browser/env_webworker.js +1 -1
  34. package/lib/core/naanlib.js +14 -14
  35. package/lib/env_node.js +97 -8
  36. package/lib/env_nodeworker.js +22 -4
  37. package/package.json +1 -1
  38. package/plugins/serviceAws/aws/lambda_rest_index.js +5 -1
  39. package/plugins/serviceAws/aws_dynamo.nlg +91 -32
  40. package/plugins/serviceAws/dbt_aws.nlg +5 -5
  41. package/plugins/serviceAws/psm_aws.nlg +2 -2
  42. package/test/harness.nlg +1 -1
  43. package/test/test_01_core.nlg +30 -4
  44. package/test/test_02_context.nlg +2 -2
  45. 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-2023 by Richard C. Zulch
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: 'Naan> ', eval: myEval});
114
+ replServer = repl.start({ prompt: '', eval: myEval});
98
115
 
99
- replServer.on('line', function(cmd) { // process raw command line
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.debugtext(text, level);
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 + "\r\n");
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
- naanlib.banner();
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
 
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2019-2021 by Richard C. Zulch
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naanlang/naan",
3
- "version": "1.0.16",
3
+ "version": "1.2.0",
4
4
  "author": "Richard C. Zulch",
5
5
  "description": "Naan™ software platform",
6
6
  "main": "./lib/core/naanlib.js",
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2019-2021 by Richard C. Zulch
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-2023 by Richard C. Zulch
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 !string(name)
302
- name = tostring(name) // try to make it a string
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 tuple> // required
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 tuple(range.key) {
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(keyname)
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
- table.info = closure info(local params, pending) {
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-2023 by Richard C. Zulch
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
- function readableStreamToArrayBuffer(stream, local result, reader, error, data, chunk) {
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._md5
541
- resp = resp._md5
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-2021 by Richard C. Zulch
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: ["DBT", "FS"]
205
+ services: ["NideDB", "NideFS"]
206
206
  }
207
207
  `(error, creds) = connector.vault.accessResource(resID)
208
208
  if creds {
package/test/harness.nlg CHANGED
@@ -18,7 +18,7 @@
18
18
  */
19
19
 
20
20
  loglevel(2);;
21
- Naan.module.chns("Play");;
21
+ Naan.module.chns("Start");;
22
22
 
23
23
 
24
24
  /*
@@ -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(value) { oo.value };>", "(function lambda (value) (`. oo value))" ],
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.*;", '("..prop", ".=prop", "value", "prop")' ],
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