@naanlang/naan 1.5.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 (62) hide show
  1. package/LICENSE.md +2 -2
  2. package/README.md +2 -2
  3. package/bin/index.js +7 -3
  4. package/dist/env_web.js +162 -48
  5. package/dist/naan.min.js +11 -11
  6. package/frameworks/browser/https_request.nlg +2 -2
  7. package/frameworks/browser/sworker.js +28 -26
  8. package/frameworks/browser/terminals.nlg +295 -737
  9. package/frameworks/browser/worker.nlg +295 -0
  10. package/frameworks/browser/workers.nlg +6 -6
  11. package/frameworks/browser/ws_client.nlg +15 -10
  12. package/frameworks/client/apiclient.nlg +211 -9
  13. package/frameworks/client/psm_client.nlg +10 -14
  14. package/frameworks/client/relaycon.nlg +87 -161
  15. package/frameworks/common/common.nlg +2 -0
  16. package/frameworks/common/events.nlg +101 -0
  17. package/frameworks/common/repltools.nlg +80 -1
  18. package/frameworks/node/apiserver.nlg +38 -246
  19. package/frameworks/node/filesystem.nlg +31 -6
  20. package/frameworks/node/http_request.nlg +34 -13
  21. package/frameworks/node/https_request.nlg +8 -3
  22. package/frameworks/node/node.nlg +3 -0
  23. package/frameworks/node/pty.nlg +346 -0
  24. package/frameworks/node/shell.nlg +26 -3
  25. package/frameworks/node/worker.nlg +504 -560
  26. package/frameworks/node/ws_client.nlg +2 -2
  27. package/frameworks/project/build.nlg +56 -14
  28. package/frameworks/project/projects.nlg +1 -1
  29. package/frameworks/running/debugcom.nlg +20 -3
  30. package/frameworks/running/debugnub.nlg +33 -11
  31. package/frameworks/running/executors.nlg +40 -285
  32. package/frameworks/running/instances.nlg +394 -0
  33. package/frameworks/running/remote_req.nlg +139 -0
  34. package/frameworks/running/remote_res.nlg +89 -0
  35. package/frameworks/running/sourcecode.nlg +3 -2
  36. package/frameworks/running/taskexec.nlg +16 -59
  37. package/frameworks/service/imp.nlg +751 -0
  38. package/frameworks/service/model.nlg +71 -0
  39. package/frameworks/service/nubnode.nlg +136 -0
  40. package/frameworks/service/nubweb.nlg +124 -0
  41. package/frameworks/service/service.nlg +28 -0
  42. package/frameworks/storage/file_manager.nlg +3 -2
  43. package/lib/browser/env_web.js +169 -55
  44. package/lib/browser/env_webworker.js +89 -31
  45. package/lib/core/naanlib.js +11 -11
  46. package/lib/env_node.js +186 -27
  47. package/lib/env_nodeworker.js +82 -44
  48. package/lib/node_server_init.nlg +149 -0
  49. package/lib/node_worker.js +36 -0
  50. package/lib/node_worker_init.nlg +43 -0
  51. package/package.json +1 -1
  52. package/plugins/openSearch/openSearch.nlg +3 -2
  53. package/plugins/openSearch/os_dynamo.nlg +3 -2
  54. package/plugins/serviceAws/aws_cloudwatchlogs.nlg +44 -2
  55. package/plugins/serviceAws/aws_dynamo.nlg +144 -63
  56. package/plugins/serviceAws/aws_utils.nlg +6 -11
  57. package/plugins/serviceAws/serviceAws.nlg +3 -2
  58. package/plugins/serviceNexara/speechrec.nlg +10 -6
  59. package/test/test_01_core.nlg +2 -2
  60. package/test/test_02_context.nlg +32 -9
  61. package/test/test_03_jsinterop.nlg +3 -3
  62. package/test/test_07_lingo.nlg +1 -1
@@ -0,0 +1,36 @@
1
+ /*
2
+ * node_worker.js
3
+ * naanide
4
+ *
5
+ * Start a Naanide worker.
6
+ *
7
+ * column positioning: // // !
8
+ *
9
+ * Copyright (c) 2017-2026 by Richard C. Zulch
10
+ *
11
+ */
12
+
13
+ "use strict";
14
+
15
+
16
+ /*
17
+ * Begin
18
+ *
19
+ */
20
+
21
+ var workerData = require('node:worker_threads').workerData || { }; // passed from creator
22
+ var jspath = require("path");
23
+ var naanpath = jspath.resolve(require.resolve('@naanlang/naan'), '../../../');
24
+ var envpath = jspath.resolve(naanpath, 'lib/env_nodeworker.js');
25
+ var naanenv = require(envpath);
26
+
27
+ naanenv.NaanWorkerActivate(function(naanworker) {
28
+ naanworker.setRequire(require); // require relative to this file
29
+ naanworker.setImport(function(m) { return (import(m)); }); // import relative to this file
30
+
31
+ naanworker.textCommand(""
32
+ + `App.naanpath = '${ naanpath.replace(/\\/g, "\\\\") }';;` // path/to/our naan library
33
+ + "App.rootpath = js.d;;" // path/to/our package.json
34
+ + "Naan.module.defineLoc('naanlib', App.naanpath);;" // defines "naanlib:" prefix for require
35
+ + "Naan.module.nodeparse('lib/node_worker_init.nlg');;\n"); // Naan initialization
36
+ });
@@ -0,0 +1,43 @@
1
+ /*
2
+ * nide_worker_init.nlg
3
+ * NaanIDE
4
+ *
5
+ * Loaded automatically for Node.js before REPL turned over to the user.
6
+ *
7
+ * column positioning: // // !
8
+ *
9
+ * Copyright (c) 2020-2026 by Richard C. Zulch
10
+ *
11
+ */
12
+
13
+
14
+ loglevel(3);
15
+ chns("Start");
16
+
17
+
18
+ /*
19
+ * Debugging & Tools
20
+ *
21
+ */
22
+
23
+ function init(local metadata) {
24
+ sprint() // no interruptions
25
+ if !module.owner.list.running.components.debugnub {
26
+ require("naanlib:frameworks/common/repltools.nlg").ImportReplTools()
27
+ require("naanlib:frameworks/running/debugnub.nlg").DebugNub()
28
+ }
29
+ App.shell = require("naanlib:frameworks/node/shell.nlg").ShellConnect({
30
+ cwd: js.d
31
+ })
32
+ metadata = new(js.t.metadata())
33
+ App.mainInstanceID = metadata.mainInstanceID
34
+ App.imp = require("naanlib:frameworks/service/imp.nlg").Imp({
35
+ name: metadata.name
36
+ type: "node-worker"
37
+ })
38
+ require("naanlib:frameworks/service/nubnode.nlg").NodeWorkerNub(App.imp, {
39
+ name: App.imp.us.name
40
+ })
41
+ if metadata.maintext
42
+ textparse(metadata.maintext)
43
+ }();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naanlang/naan",
3
- "version": "1.5.0",
3
+ "version": "1.7.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) 2023 by Richard C. Zulch
9
+ * Copyright (c) 2023-2026 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -53,6 +53,7 @@ closure openSearchIndex(prosc, indexName, ioptions, local osin) {
53
53
  osin = new(object, this)
54
54
  if symbol(indexName)
55
55
  indexName = indexName.tostring()
56
+ osin.name = indexName
56
57
  osin.analyzers = { // map names, language to analyzer
57
58
  english: "english"
58
59
  hashtag: "hashtag"
@@ -335,7 +336,7 @@ closure openSearchIndex(prosc, indexName, ioptions, local osin) {
335
336
  `(error, data) = prosc.request(indexName.concat("/_bulk"), {
336
337
  method: "POST"
337
338
  putdata: docs
338
- contentType: "application/json"
339
+ contentType: "application/x-ndjson"
339
340
  })
340
341
  if data
341
342
  data = data.items.map(function(item) {
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2023-2025 by Richard C. Zulch
9
+ * Copyright (c) 2023-2026 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -41,6 +41,7 @@
41
41
 
42
42
  closure OSDynIndexer(index, table, options,
43
43
  local dbOptions, scanPending, indexPending, updatePending, output, error, records, data, ids) {
44
+ global()
44
45
  if !options.converter
45
46
  return (Error("OSDynIndexer: options.converter is required"))
46
47
  dbOptions = {
@@ -78,7 +79,7 @@ closure OSDynIndexer(index, table, options,
78
79
  //
79
80
  // Batch index documents into OpenSearch, returning a nonce signaled with ids array.
80
81
  //
81
- closure batchIndex(records, local pending) {
82
+ closure batchIndex(records, local pending, docs) {
82
83
  pending = new(nonce)
83
84
  docs = asyncArray(records, 10, closure(record) {
84
85
  options.converter(record)
@@ -18,7 +18,7 @@
18
18
  *
19
19
  */
20
20
 
21
- closure CloudWatchLogs(local cwlogs) {
21
+ closure CloudWatchLogs(local cwlogs, delayf, backoffMS) {
22
22
  global(awsSDK)
23
23
  cwlogs = new(object, this)
24
24
 
@@ -35,7 +35,42 @@ closure CloudWatchLogs(local cwlogs) {
35
35
  })
36
36
  list(false, { ok: true })
37
37
  }
38
+
39
+ // retrier
40
+ //
41
+ // Perform exponential backoff when throttled, returning true for retry or false otherwise.
42
+ //
43
+ function retrier(error, local waitms, newbo) {
44
+ if !error
45
+ delayf = false
46
+ else if error.name == "ThrottlingException" { // retriable errors
47
+ if backoffMS > milliseconds() {
48
+ debuglog("DynaTable.retrier: existing backoff", backoffMS - milliseconds(), "msec")
49
+ return (true) // use existing backoff
50
+ }
51
+ if !delayf
52
+ delayf = ExbackFactory(6,60) // 6s max backoff, 60s deadline
53
+ waitms = delayf()
54
+ debuglog("DynaTable.retrier: new backoff:", waitms, "msec")
55
+ if waitms { // returns false for no retry
56
+ newbo = milliseconds() + waitms // tick when proceeding is allowed
57
+ if !(backoffMS > newbo)
58
+ backoffMS = newbo
59
+ true
60
+ }
61
+ }
62
+ }
38
63
 
64
+ // delayer
65
+ //
66
+ // Delay until the current backoff is complete.
67
+ //
68
+ function delayer(local waitms) {
69
+ waitms = backoffMS - milliseconds()
70
+ if waitms > 0
71
+ sleep(waitms + waitms * Math.random()/2)
72
+ }
73
+
39
74
  //
40
75
  // cwlQueryPager
41
76
  //
@@ -46,7 +81,14 @@ closure CloudWatchLogs(local cwlogs) {
46
81
  if !remaining
47
82
  remaining = 10 // default retrieve limit
48
83
  params.limit = remaining
49
- QueryPager(cwlogs.aws, procname, params, function(data, local processed) {
84
+ delayer()
85
+ QueryPager(cwlogs.aws, procname, params, function(error, data, local processed) {
86
+ if error {
87
+ if !retrier(error)
88
+ return (false)
89
+ delayer()
90
+ return (true)
91
+ }
50
92
  processed = doneCB(data)
51
93
  if processed
52
94
  remaining -= processed
@@ -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
@@ -100,7 +100,7 @@ closure NexaraSpeechRec(local nxspr) {
100
100
  // Convert a Nexara response to a Yandex SpeechKit structure. Hey, it was first.
101
101
  //
102
102
  function nexaraToYCSK(resp,
103
- local output, wordx, segment, chunk, word, wordet) {
103
+ local output, wordx, segment, chunk, syncerror, word, wordet, wordtrim, tstart) {
104
104
  output = {
105
105
  \@type: "type.googleapis.com/yandex.cloud.ai.stt.v2.LongRunningRecognitionResponse"
106
106
  chunks: []
@@ -120,14 +120,18 @@ closure NexaraSpeechRec(local nxspr) {
120
120
  ]
121
121
  }
122
122
  for word in segment.text.split(" ") {
123
+ if word == ""
124
+ continue // ignore extra spaces
123
125
  wordet = resp.words[wordx++]
124
- if wordet.word != word {
125
- debuglog("nexaraToYCSK: expected ${word} but got ${wordet.word}")
126
- return (false)
126
+ wordtrim = wordet.word.trim() // detected word without spaces
127
+ tstart = wordet.start.toFixed(3).concat("s")
128
+ if !syncerror && wordtrim != word {
129
+ syncerror = true
130
+ debuglog("nexaraToYCSK: warning: at ${tstart} expected '${word}' but got '${wordtrim}'")
127
131
  }
128
132
  chunk.alternatives.0.words.push({
129
- word: wordet.word
130
- startTime: wordet.start.toFixed(3).concat("s")
133
+ word: wordtrim
134
+ startTime: tstart
131
135
  endTime: wordet.end.toFixed(3).concat("s")
132
136
  })
133
137
  }
@@ -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