@naanlang/naan 1.6.0 → 1.7.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 (50) hide show
  1. package/LICENSE.md +1 -1
  2. package/README.md +1 -1
  3. package/bin/index.js +2 -2
  4. package/dist/env_web.js +3 -31
  5. package/dist/naan.min.js +10 -10
  6. package/frameworks/browser/sworker.js +23 -23
  7. package/frameworks/browser/terminals.nlg +44 -122
  8. package/frameworks/browser/worker.nlg +64 -24
  9. package/frameworks/browser/workers.nlg +1 -1
  10. package/frameworks/client/apiclient.nlg +73 -43
  11. package/frameworks/client/psm_client.nlg +10 -14
  12. package/frameworks/common/common.nlg +2 -0
  13. package/frameworks/node/apiserver.nlg +0 -1
  14. package/frameworks/node/node.nlg +1 -0
  15. package/frameworks/node/pty.nlg +21 -21
  16. package/frameworks/node/shell.nlg +26 -3
  17. package/frameworks/node/worker.nlg +153 -79
  18. package/frameworks/project/build.nlg +55 -13
  19. package/frameworks/project/projects.nlg +1 -1
  20. package/frameworks/running/debugcom.nlg +20 -3
  21. package/frameworks/running/debugnub.nlg +16 -7
  22. package/frameworks/running/executors.nlg +14 -12
  23. package/frameworks/running/instances.nlg +1 -0
  24. package/frameworks/running/remote_req.nlg +4 -8
  25. package/frameworks/running/remote_res.nlg +1 -1
  26. package/frameworks/running/sourcecode.nlg +3 -2
  27. package/frameworks/running/taskexec.nlg +14 -57
  28. package/frameworks/service/imp.nlg +15 -0
  29. package/frameworks/service/nubnode.nlg +35 -4
  30. package/frameworks/service/nubweb.nlg +22 -4
  31. package/frameworks/storage/file_manager.nlg +3 -2
  32. package/lib/browser/env_web.js +10 -38
  33. package/lib/browser/env_webworker.js +1 -30
  34. package/lib/core/naanlib.js +10 -10
  35. package/lib/env_node.js +5 -22
  36. package/lib/env_nodeworker.js +8 -43
  37. package/lib/node_server_init.nlg +149 -0
  38. package/lib/node_worker.js +36 -0
  39. package/lib/node_worker_init.nlg +43 -0
  40. package/package.json +1 -1
  41. package/plugins/openSearch/openSearch.nlg +3 -2
  42. package/plugins/openSearch/os_dynamo.nlg +3 -2
  43. package/plugins/serviceAws/aws_cloudwatchlogs.nlg +44 -2
  44. package/plugins/serviceAws/aws_dynamo.nlg +144 -63
  45. package/plugins/serviceAws/aws_utils.nlg +6 -11
  46. package/plugins/serviceAws/serviceAws.nlg +3 -2
  47. package/test/test_01_core.nlg +2 -2
  48. package/test/test_02_context.nlg +8 -8
  49. package/test/test_03_jsinterop.nlg +3 -3
  50. package/test/test_07_lingo.nlg +1 -1
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2021-2024 by Richard C. Zulch
9
+ * Copyright (c) 2021-2026 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -562,7 +562,7 @@ closure DynaConverter(local conv) {
562
562
  *
563
563
  */
564
564
 
565
- closure DynaTable(dyna, tablename, options, local table) {
565
+ closure DynaTable(dyna, tablename, options, local table, delayf, backoffMS) {
566
566
  global()
567
567
  table = new(object, this)
568
568
  table.name = tablename
@@ -577,6 +577,38 @@ closure DynaTable(dyna, tablename, options, local table) {
577
577
  debuglog("DynaTable: invalid (hashKey/rangeKey) option:", tablename, list(options.hashKey, options.rangeKey))
578
578
  }
579
579
 
580
+ // retrier
581
+ //
582
+ // Perform exponential backoff when throttled, returning true for retry or false otherwise.
583
+ //
584
+ function retrier(error, local waitms, newbo) {
585
+ if !error
586
+ delayf = false
587
+ else if error.name == "ThrottlingException" { // retriable errors
588
+ if backoffMS > milliseconds()
589
+ return (true) // use existing backoff
590
+ if !delayf
591
+ delayf = ExbackFactory(6,60) // 6s max backoff, 60s deadline
592
+ waitms = delayf()
593
+ if waitms { // returns false for no retry
594
+ newbo = milliseconds() + waitms // tick when proceeding is allowed
595
+ if !(backoffMS > newbo)
596
+ backoffMS = newbo
597
+ true
598
+ }
599
+ }
600
+ }
601
+
602
+ // delayer
603
+ //
604
+ // Delay until the current backoff is complete.
605
+ //
606
+ function delayer(local waitms) {
607
+ waitms = backoffMS - milliseconds()
608
+ if waitms > 0
609
+ sleep(waitms + waitms * Math.random()/2)
610
+ }
611
+
580
612
  // info
581
613
  //
582
614
  // Return info about the table, or an error, in standard (error, dictionary) tuple format.
@@ -694,16 +726,24 @@ closure DynaTable(dyna, tablename, options, local table) {
694
726
  KeyType: "RANGE"
695
727
  })
696
728
  }
697
- pending = new(nonce)
698
- dyna.aws.createTable(params, function(error, data) {
699
- if error {
700
- dynaTableError = error
701
- debuglog("aws.createTable failed", tablename, ErrorString(error))
702
- error = Error("table.create(".concat(tablename, ") failed"), error)
703
- }
704
- pending.signal(list(error, data))
705
- })
706
- `(error, data) = pending.wait()
729
+
730
+ loop {
731
+ pending = new(nonce)
732
+ delayer()
733
+ dyna.aws.createTable(params, function(error, data) {
734
+ if error {
735
+ dynaTableError = error
736
+ if error.name != "ThrottlingException"
737
+ debuglog("aws.createTable failed", tablename, ErrorString(error))
738
+ error = Error("table.create(".concat(tablename, ") failed"), error)
739
+ }
740
+ pending.signal(list(error, data))
741
+ })
742
+ `(error, data) = pending.wait()
743
+ if !retrier(error)
744
+ break
745
+ }
746
+
707
747
  if error
708
748
  return(list(Error("aws.createTable failed", error)))
709
749
  start = milliseconds()
@@ -764,7 +804,8 @@ closure DynaTable(dyna, tablename, options, local table) {
764
804
  // The result is a standard result tuple `(error, record).
765
805
 
766
806
  //
767
- table.getRecord = closure getRecord(hashValue, options, local params, rangeValue, pending) {
807
+ table.getRecord = closure getRecord(hashValue, options,
808
+ local params, rangeValue, pending, error, record) {
768
809
  if preflightError(this)
769
810
  return
770
811
  params = {
@@ -776,17 +817,25 @@ closure DynaTable(dyna, tablename, options, local table) {
776
817
  } else
777
818
  rangeValue = options
778
819
  params.Key = makeDynKey(list(hashValue, rangeValue))
779
- pending = new(nonce)
780
- dyna.aws.getItem(params, function(error, data) {
781
- if error {
782
- dynaTableError = error
783
- debuglog("aws.getItem failed", tablename, ErrorString(error))
784
- error = Error("table.getRecord(".concat(tablename, ") failed"), error)
785
- } else
786
- data = dyna.conv.recordDynToNaan(data.Item)
787
- pending.signal(list(error, data))
788
- })
789
- pending.wait()
820
+
821
+ loop {
822
+ pending = new(nonce)
823
+ delayer()
824
+ dyna.aws.getItem(params, function(error, data) {
825
+ if error {
826
+ dynaTableError = error
827
+ if error.name != "ThrottlingException"
828
+ debuglog("aws.getItem failed", tablename, ErrorString(error))
829
+ error = Error("table.getRecord(".concat(tablename, ") failed"), error)
830
+ } else
831
+ data = dyna.conv.recordDynToNaan(data.Item)
832
+ pending.signal(list(error, data))
833
+ })
834
+ `(error, record) = pending.wait()
835
+ if !retrier(error)
836
+ break
837
+ }
838
+ list(error, record)
790
839
  }
791
840
 
792
841
  //
@@ -794,23 +843,32 @@ closure DynaTable(dyna, tablename, options, local table) {
794
843
  //
795
844
  // Put a record in the table, replacing any existing record with the same key.
796
845
 
797
- table.putRecord = closure putRecord(record, local params, pending) {
846
+ table.putRecord = closure putRecord(record,
847
+ local params, pending, error) {
798
848
  if preflightError(this)
799
849
  return
800
850
  params = {
801
851
  TableName: tablename
802
852
  Item: dyna.conv.recordNaanToDyn(record)
803
853
  }
804
- pending = new(nonce)
805
- dyna.aws.putItem(params, function(error, data) {
806
- if error {
807
- dynaTableError = error
808
- debuglog("aws.putItem failed", tablename, ErrorString(error))
809
- error = Error("table.putRecord(".concat(tablename, ") failed"), error)
810
- }
811
- pending.signal(list(error, data))
812
- })
813
- pending.wait()
854
+
855
+ loop {
856
+ pending = new(nonce)
857
+ delayer()
858
+ dyna.aws.putItem(params, function(error, data) {
859
+ if error {
860
+ dynaTableError = error
861
+ if error.name != "ThrottlingException"
862
+ debuglog("aws.putItem failed", tablename, ErrorString(error))
863
+ error = Error("table.putRecord(".concat(tablename, ") failed"), error)
864
+ }
865
+ pending.signal(list(error, data))
866
+ })
867
+ `(error, record) = pending.wait()
868
+ if !retrier(error)
869
+ break
870
+ }
871
+ list(error, record)
814
872
  }
815
873
 
816
874
  //
@@ -831,7 +889,7 @@ closure DynaTable(dyna, tablename, options, local table) {
831
889
  // The result is a standard result tuple `(error, record).
832
890
 
833
891
  table.updateRecord = closure updateRecord(hashValue, rangeValue, options,
834
- local error, params, pending) {
892
+ local error, params, pending, record) {
835
893
  if preflightError(this)
836
894
  return
837
895
  params = {
@@ -856,18 +914,25 @@ closure DynaTable(dyna, tablename, options, local table) {
856
914
  if !params.ReturnValues
857
915
  params.ReturnValues = "NONE"
858
916
  }
859
- pending = new(nonce)
860
- dyna.aws.updateItem(params, function(error, data) {
861
- if error {
862
- dynaTableError = error
863
- debuglog("aws.updateItem failed", tablename, ErrorString(error), Dialect.print(params))
864
- error = Error("table.updateRecord(".concat(tablename, ") failed"), error)
865
- }
866
- else
867
- data = dyna.conv.recordDynToNaan(data.Attributes)
868
- pending.signal(list(error, data))
869
- })
870
- pending.wait()
917
+ loop {
918
+ pending = new(nonce)
919
+ delayer()
920
+ dyna.aws.updateItem(params, function(error, data) {
921
+ if error {
922
+ dynaTableError = error
923
+ if error.name != "ThrottlingException"
924
+ debuglog("aws.updateItem failed", tablename, ErrorString(error), Dialect.print(params))
925
+ error = Error("table.updateRecord(".concat(tablename, ") failed"), error)
926
+ }
927
+ else
928
+ data = dyna.conv.recordDynToNaan(data.Attributes)
929
+ pending.signal(list(error, data))
930
+ })
931
+ `(error, record) = pending.wait()
932
+ if !retrier(error)
933
+ break
934
+ }
935
+ list(error, record)
871
936
  }
872
937
 
873
938
  //
@@ -881,7 +946,7 @@ closure DynaTable(dyna, tablename, options, local table) {
881
946
 
882
947
 
883
948
  table.deleteRecord = closure deleteRecord(hashValue, rangeValue, options,
884
- local params, error, pending) {
949
+ local params, error, pending, record) {
885
950
  if preflightError(this)
886
951
  return
887
952
  params = {
@@ -902,16 +967,25 @@ closure DynaTable(dyna, tablename, options, local table) {
902
967
  if !params.ReturnValues
903
968
  params.ReturnValues = "NONE"
904
969
  }
905
- pending = new(nonce)
906
- dyna.aws.deleteItem(params, function(error, data) {
907
- if error {
908
- dynaTableError = error
909
- debuglog("aws.deleteItem failed", tablename, ErrorString(error))
910
- error = Error("table.deleteRecord(".concat(tablename, ") failed"), error)
911
- }
912
- pending.signal(list(error, data))
913
- })
914
- pending.wait()
970
+
971
+ loop {
972
+ pending = new(nonce)
973
+ delayer()
974
+ dyna.aws.deleteItem(params, function(error, data) {
975
+ if error {
976
+ dynaTableError = error
977
+ if error.name != "ThrottlingException"
978
+ debuglog("aws.deleteItem failed", tablename, ErrorString(error))
979
+ error = Error("table.deleteRecord(".concat(tablename, ") failed"), error)
980
+ }
981
+ pending.signal(list(error, data))
982
+ })
983
+ pending.wait()
984
+ `(error, record) = pending.wait()
985
+ if !retrier(error)
986
+ break
987
+ }
988
+ list(error, record)
915
989
  }
916
990
 
917
991
  //
@@ -922,16 +996,23 @@ closure DynaTable(dyna, tablename, options, local table) {
922
996
  //
923
997
  closure dynQueryPager(procname, params, remaining, doneCB) {
924
998
  if !remaining
925
- remaining = 10 // default retrieve limit
999
+ remaining = 10 // default retrieve limit
926
1000
  params.Limit = remaining
927
- QueryPager(dyna.aws, procname, params, function(data, local processed) {
1001
+ delayer()
1002
+ QueryPager(dyna.aws, procname, params, function(error, data, local processed) {
1003
+ if error {
1004
+ if !retrier(error)
1005
+ return (false)
1006
+ delayer()
1007
+ return (true)
1008
+ }
928
1009
  processed = doneCB(data)
929
1010
  if processed
930
1011
  remaining -= processed
931
1012
  params.ExclusiveStartKey = data.LastEvaluatedKey
932
1013
  if processed && remaining > 0 && data.LastEvaluatedKey {
933
- params.Limit = remaining // set up for next iteration
934
- true // continue looping
1014
+ params.Limit = remaining // set up for next iteration
1015
+ true // continue looping
935
1016
  }
936
1017
  })
937
1018
  }
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2021-2025 by Richard C. Zulch
9
+ * Copyright (c) 2021-2026 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -27,19 +27,14 @@ closure queryPager(apiobject, procname, params, xargs, doneCB, local pending, lo
27
27
  return (list(Error("queryPager method undefined:", procname)))
28
28
  pending = new(nonce)
29
29
  looping = true
30
- while looping { // loop for multiple pages
30
+ while looping { // loop for multiple pages or retry
31
31
  pending.reset()
32
32
  xapply(apiobject, procname, append(cons(params, xargs), cons(function (error, data) {
33
- if error {
34
- qpPagerError = error
35
- debuglog(procname, "failed", error)
36
- looping = false
37
- pending.signal(list(error))
38
- }
39
- else {
40
- looping = doneCB(data)
33
+ looping = doneCB(error, data)
34
+ if looping || !error
41
35
  pending.signal(list(false, {ok: true}))
42
- }
36
+ else
37
+ pending.signal(list(error))
43
38
  })))
44
39
  result = pending.wait()
45
40
  }
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2020-2022 by Richard C. Zulch
9
+ * Copyright (c) 2020-2026 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -23,7 +23,8 @@ function sawsInit(local manifest) {
23
23
 
24
24
  Naan.module.build(module.id, "serviceAws", function(modobj, compobj) {
25
25
  compobj.manifest = manifest
26
- require("naanlib:frameworks/common").LiveImport()
26
+ require("naanlib:frameworks/common/utils.nlg") // get ExbackFactory
27
+ require("naanlib:frameworks/common").LiveImport() // applies to all exports
27
28
  require("./aws_utils.nlg")
28
29
  function sawsPostload() {
29
30
  if js.g
@@ -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-2026 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -881,7 +881,7 @@ RegisterTestCategory("core", [
881
881
 
882
882
  [ "unintern(true) === true", "false"],
883
883
 
884
- [ "length(stacktrace());", "55" ],
884
+ [ "length(stacktrace());", "57" ],
885
885
 
886
886
  //
887
887
  // relate builtin
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2017-2024 by Richard C. Zulch
9
+ * Copyright (c) 2017-2026 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -38,7 +38,7 @@ RegisterTestCategory("context", [
38
38
  [ "function setNS(ns) { ns=reverse(ns),sudo(nsactive(reverse(ns, defNS))) };", "setNS" ],
39
39
  [ "x;", "x" ],
40
40
  [ "y;", "y" ],
41
- [ "length(symlist(nsactive(true).0));", "173"],
41
+ [ "length(symlist(nsactive(true).0));", "175"],
42
42
  [ "testvar;", "testvar" ],
43
43
  [ "function setTestvar(v) { testvar=v };", "setTestvar" ],
44
44
  [ "naanvar;", "naanvar" ],
@@ -200,20 +200,20 @@ RegisterTestCategory("context", [
200
200
  [ "test2(4);", "10001|10001" ],
201
201
 
202
202
  [ "function test1(x) { if x > 10000 printline(x) else test2(x+1), undefined() };", "==> redefined: test1|test1" ],
203
- [ "test2(5);", { errors: "exception: recursed 101" } ],
203
+ [ "test2(5);", { errors: "exception: recursed 99" } ],
204
204
 
205
205
  [ "function test1(x) { if x > 10000 printline(x) else loop test2(x+1) };", "==> redefined: test1|test1" ],
206
- [ "test2(6);", { errors: "exception: recursed 102" } ],
206
+ [ "test2(6);", { errors: "exception: recursed 100" } ],
207
207
 
208
208
  [ "function test1(x) { if x > 10000 printline(x) else 0+test2(x+1) };", "==> redefined: test1|test1" ],
209
- [ "test2(7);", { errors: "exception: recursed 149" } ],
209
+ [ "test2(7);", { errors: "exception: recursed 147" } ],
210
210
 
211
211
  [ "function test2(x) { testRecurCheck(x), if true test1(x+1) else print(x) };", "==> redefined: test2|test2" ],
212
- [ "test2(8);", { errors: "exception: recursed 150" } ],
212
+ [ "test2(8);", { errors: "exception: recursed 148" } ],
213
213
 
214
214
  [ "function test1(x) { if x > 10000 printline(x) else loop test2(x+1, 4, 5) };", "==> redefined: test1|test1" ],
215
215
  [ "function test2(x,y,z) { testRecurCheck(x), loop if true { test1(x+1),break } else print(x) };", "==> redefined: test2|test2" ],
216
- [ "test2(9);", { errors: "exception: recursed 51" } ],
216
+ [ "test2(9);", { errors: "exception: recursed 49" } ],
217
217
 
218
218
  [ "function test1 xt { if car(xt) > 10000 printline(xt) else test2(car(xt)+1) };", "==> redefined: test1|test1" ],
219
219
  [ "function test2 xt { testRecurCheck(car(xt)), test1(car(xt)+1) };", "==> redefined: test2|test2" ],
@@ -222,7 +222,7 @@ RegisterTestCategory("context", [
222
222
  [ "oo=new(object);", "Object{0}"],
223
223
  [ "oo.test1 = function test1(x) { if x > 10000 printline(x) else test2(x+1) };", "==> redefined: test1|test1"],
224
224
  [ "oo.test2 = function test2 xt { testRecurCheck(car(xt)), oo.test1(car(xt)+1) };", "==> redefined: test2|test2"],
225
- [ "oo.test1(11);", { errors: "exception: recursed 154" } ],
225
+ [ "oo.test1(11);", { errors: "exception: recursed 152" } ],
226
226
 
227
227
  [ "function test1 xt { testRecurCheck(car(xt)), if car(xt) > 10000 printline(xt) else test1(car(xt)+1) };",
228
228
  "==> redefined: test1|test1" ],
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2020-2025 by Richard C. Zulch
9
+ * Copyright (c) 2020-2026 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -35,11 +35,11 @@ RegisterTestCategory("JSinterop", [
35
35
 
36
36
  [ "o(3);", {
37
37
  "dev": { errors: "xcall \"[object Object]\": TypeError: object.apply is not a function" }
38
- "prod": { errors: "xcall \"[object Object]\": TypeError: e[3].apply is not a function" }
38
+ "prod": { errors: "xcall \"[object Object]\": TypeError: r[3].apply is not a function" }
39
39
  }[ "prod"]],
40
40
  [ "o(Number);", {
41
41
  "dev": { errors: "xcall \"[object Object]\": TypeError: object.apply is not a function" }
42
- "prod": { errors: "xcall \"[object Object]\": TypeError: e[3].apply is not a function" }
42
+ "prod": { errors: "xcall \"[object Object]\": TypeError: r[3].apply is not a function" }
43
43
  }[ "prod"]],
44
44
  [ "Number(3);", "[Number 3]" ],
45
45
  [ "Number(3).toString();", "\"3\"" ],
@@ -474,7 +474,7 @@ RegisterTestCategory("Lingo", [
474
474
  { errors: "invalid iteration variable at (1, 5) found 3" } ],
475
475
  [ "for x in xnew() print(x);>", "Dictionary{6}"],
476
476
  [ "for x in Array(e, f, g) print(x);>", "efg|Dictionary{7}"],
477
- [ "for x in false print(x);>", "Dictionary{5}"],
477
+ [ "for x in false print(x);>", "Dictionary{6}"],
478
478
  [ "for x in { } print(x);>", "Dictionary{7}"],
479
479
 
480
480
  //