@naanlang/naan 1.4.3 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/LICENSE.md +2 -2
  2. package/README.md +4 -4
  3. package/bin/index.js +22 -3
  4. package/dist/env_web.js +2 -2
  5. package/dist/naan.min.js +6 -6
  6. package/frameworks/browser/browser.nlg +2 -2
  7. package/frameworks/browser/https_request.nlg +39 -10
  8. package/frameworks/browser/sworker.js +23 -23
  9. package/frameworks/browser/terminals.nlg +33 -24
  10. package/frameworks/client/apiclient.nlg +31 -18
  11. package/frameworks/client/client.nlg +22 -2
  12. package/frameworks/client/psm_client.nlg +12 -3
  13. package/frameworks/node/filesystem.nlg +9 -7
  14. package/frameworks/node/gitter.nlg +9 -7
  15. package/frameworks/node/http_request.nlg +103 -0
  16. package/frameworks/node/https_request.nlg +42 -9
  17. package/frameworks/node/node.nlg +1 -0
  18. package/frameworks/node/shell.nlg +4 -4
  19. package/frameworks/node/worker.nlg +1 -0
  20. package/frameworks/project/projects.nlg +2 -1
  21. package/frameworks/running/executors.nlg +26 -7
  22. package/frameworks/running/taskexec.nlg +48 -14
  23. package/frameworks/storage/dbt_pouch.nlg +26 -13
  24. package/frameworks/storage/psm.nlg +39 -5
  25. package/frameworks/storage/psm_dbtables.nlg +10 -2
  26. package/lib/browser/env_web.js +9 -9
  27. package/lib/browser/env_webworker.js +1 -2
  28. package/lib/core/naanlib.js +6 -6
  29. package/lib/env_node.js +9 -3
  30. package/lib/node_repl_init.nlg +2 -2
  31. package/package.json +1 -1
  32. package/plugins/nodeLib/node_util.nlg +14 -7
  33. package/plugins/openSearch/os_dynamo.nlg +5 -5
  34. package/plugins/serviceAws/aws_cloudwatchlogs.nlg +2 -1
  35. package/plugins/serviceAws/aws_dynamo.nlg +26 -8
  36. package/plugins/serviceAws/aws_s3.nlg +3 -3
  37. package/plugins/serviceAws/aws_sqs.nlg +3 -1
  38. package/plugins/serviceAws/aws_utils.nlg +3 -1
  39. package/plugins/serviceAws/aws_vm.nlg +198 -0
  40. package/plugins/serviceAws/dbt_aws.nlg +22 -20
  41. package/plugins/serviceAws/psm_aws.nlg +2 -1
  42. package/plugins/serviceGC/gc_api.nlg +1 -0
  43. package/plugins/serviceNexara/serviceNexara.nlg +32 -0
  44. package/plugins/serviceNexara/speechrec.nlg +160 -0
  45. package/plugins/serviceYC/serviceYC.nlg +2 -3
  46. package/plugins/serviceYC/yc_speechrec.nlg +4 -3
  47. package/plugins/serviceYC/yc_vm.nlg +44 -54
  48. package/test/test_01_core.nlg +42 -33
  49. package/test/test_03_jsinterop.nlg +35 -6
  50. package/plugins/serviceGC/russian_trusted_root_ca_pem.crt +0 -33
package/lib/env_node.js CHANGED
@@ -110,11 +110,13 @@ exports.NaanNodeREPL = function NaanNodeREPL(options) {
110
110
  { // Node REPL does too much crap
111
111
  }
112
112
 
113
- if (process.stdin.isTTY && !options.replDisable)
113
+ if (process.stdin.isTTY)
114
114
  { // must have TTY for local REPL
115
115
  replServer = repl.start({ prompt: '', eval: myEval});
116
116
 
117
117
  replServer.on('line', function(cmd) { // process raw command line
118
+ if (options.replDisable)
119
+ return;
118
120
  if (cmd.charAt(0) != ".") {
119
121
  textToRemoteTerm("\x1b[90m\x1b[3m".concat(cmd, "\x1b[0m\n"));
120
122
  naanlib.textLine(cmd + "\n");
@@ -126,9 +128,13 @@ exports.NaanNodeREPL = function NaanNodeREPL(options) {
126
128
  //
127
129
 
128
130
  replServer._events.SIGINT = function () {
129
- naanlib.escape();
131
+ if (options.replDisable) {
132
+ console.log("^C");
133
+ process.exit();
134
+ } else
135
+ naanlib.escape();
130
136
  };
131
-
137
+
132
138
  //
133
139
  // Hook an exit command
134
140
  //
@@ -6,11 +6,11 @@
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2018-2021 by Richard C. Zulch
9
+ * Copyright (c) 2018-2025 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
13
- loglevel(2);
13
+ loglevel(3);
14
14
 
15
15
 
16
16
  /*
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naanlang/naan",
3
- "version": "1.4.3",
3
+ "version": "1.5.0",
4
4
  "author": "Richard C. Zulch",
5
5
  "description": "Naan™ software platform",
6
6
  "main": "./lib/core/naanlib.js",
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * column positioning: // // !
7
7
  *
8
- * Copyright (c) 2021-2023 by Richard C. Zulch
8
+ * Copyright (c) 2021-2025 by Richard C. Zulch
9
9
  *
10
10
  */
11
11
 
@@ -13,12 +13,11 @@
13
13
  /*
14
14
  * MultipartBody
15
15
  *
16
- * column positioning: // // !
17
16
  * Create a multipart Form body from the specified dictionary, returning a buffer. Any xobject in
18
17
  * the dictionary is coded as an octet. If you want a different filename, include an options object
19
18
  * that defines that filename.
20
19
  *
21
- *The result is a dictionary as follows:
20
+ *The return value is a dictionary as follows:
22
21
  * {
23
22
  * body: <Buffer>
24
23
  * header: {
@@ -29,11 +28,17 @@
29
28
  *
30
29
  */
31
30
 
32
- function MultipartBody(dict, options, local boundary, parts, key, data, filename) {
31
+ function MultipartBody(dict, options,
32
+ local boundary, parts, key, data, item, filename) {
33
33
  boundary = "xx-xx-xx-xx-xx"
34
34
  parts = []
35
35
  for `(key, data) in dict {
36
- if xobject(data) {
36
+ if Array.isArray(data) { // PHP-style arrays
37
+ for item in data
38
+ parts.push(xnew(js.g.Buffer.from, "--".concat(boundary, "\r\n",
39
+ "Content-Disposition: form-data; name=\"", key, "[]",
40
+ "\"; \r\n\r\n", item, "\r\n"), "utf8"))
41
+ } else if xobject(data) { // binary file data
37
42
  if options[key]
38
43
  filename = options[key]
39
44
  else
@@ -43,8 +48,7 @@ function MultipartBody(dict, options, local boundary, parts, key, data, filename
43
48
  "Content-Type:application/octet-stream\r\n\r\n"), "utf8"))
44
49
  parts.push(xnew(js.g.Buffer.from, data, "binary"))
45
50
  parts.push(xnew(js.g.Buffer.from, "\r\n", "utf8"))
46
- }
47
- else {
51
+ } else { // ordinary data
48
52
  parts.push(xnew(js.g.Buffer.from, "--".concat(boundary, "\r\n",
49
53
  "Content-Disposition: form-data; name=\"", key,
50
54
  "\"; \r\n\r\n", data, "\r\n"), "utf8"))
@@ -165,8 +169,11 @@ function nodeUtilInit(local manifest) {
165
169
  Naan.module.build(module.id, "node_util", function(modobj, compobj) {
166
170
  require("nodeLib.nlg")
167
171
  compobj.manifest = manifest
172
+ modobj.exports.MultipartBody = MultipartBody
173
+ modobj.exports.EncodeQuery = EncodeQuery
168
174
  modobj.exports.JsonParse = JsonParse
169
175
  modobj.exports.JsonStringify = JsonStringify
176
+ modobj.exports.RandomBytes = RandomBytes
170
177
  modobj.exports.UUID = UUID
171
178
  function postload() {
172
179
  nodeFs = js.r("fs")
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2023 by Richard C. Zulch
9
+ * Copyright (c) 2023-2025 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -65,7 +65,7 @@ closure OSDynIndexer(index, table, options,
65
65
  // Update ids into the database, returning a nonce signaled with error array.
66
66
  //
67
67
  closure updateRecordIDs(docs, ids, writeID) {
68
- asyncArray(docs, 10, function(entry, index, cancel, local error) {
68
+ asyncArray(docs, 10, closure(entry, index, cancel, local error) {
69
69
  ignore(error)
70
70
  `(error) = table.updateRecord(
71
71
  entry[table.hashKey],
@@ -78,11 +78,11 @@ closure OSDynIndexer(index, table, options,
78
78
  //
79
79
  // Batch index documents into OpenSearch, returning a nonce signaled with ids array.
80
80
  //
81
- closure batchIndex(records, local pending, docs) {
81
+ closure batchIndex(records, local pending) {
82
82
  pending = new(nonce)
83
- docs = records.map(function(record) {
83
+ docs = asyncArray(records, 10, closure(record) {
84
84
  options.converter(record)
85
- })
85
+ }).wait()
86
86
  future(function(local error, ids) {
87
87
  `(error, ids) = index.write(docs)
88
88
  pending.signal(list(error, records, ids))
@@ -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-2025 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -19,6 +19,7 @@
19
19
  */
20
20
 
21
21
  closure CloudWatchLogs(local cwlogs) {
22
+ global(awsSDK)
22
23
  cwlogs = new(object, this)
23
24
 
24
25
  //
@@ -946,7 +946,7 @@ closure DynaTable(dyna, tablename, options, local table) {
946
946
  // range: <key-expression> // range of keys to retrieve
947
947
  // filter: <key-expression> // conditions for items to return
948
948
  // reverse: <boolean> // reverse order
949
- // project: <array> // project only specified attributes
949
+ // project: <array> | `count // project only specified attributes
950
950
  // paging: <exclusive start key> // for paging; updated each call
951
951
  // consistentRead: <boolean> // true to use consistent read
952
952
  // limit: <number> // maximum count of results
@@ -962,6 +962,7 @@ closure DynaTable(dyna, tablename, options, local table) {
962
962
  TableName: tablename
963
963
  }
964
964
  hashKey = table.hashKey
965
+ output = []
965
966
  if options.indexName {
966
967
  params.IndexName = options.indexName
967
968
  if options.indexHashKey
@@ -989,15 +990,23 @@ closure DynaTable(dyna, tablename, options, local table) {
989
990
  }
990
991
  if options.reverse
991
992
  params.ScanIndexForward = false
992
- if options.project
993
+ if options.project == `count {
994
+ params.Select = "COUNT"
995
+ output = {
996
+ Count: 0
997
+ ScannedCount: 0
998
+ }
999
+ } else if options.project
993
1000
  params.ProjectionExpression = options.project.join(", ")
994
1001
  if options.paging
995
1002
  params.ExclusiveStartKey = options.paging
996
1003
  if options.consistentRead
997
1004
  params.ConsistentRead = options.consistentRead
998
- output = []
999
1005
  `(error) = dynQueryPager(`query, params, options.limit, function(data) {
1000
- if data.Count > 0
1006
+ if dictionary(output) {
1007
+ output.Count += data.Count
1008
+ output.ScannedCount += data.ScannedCount
1009
+ } else if data.Count > 0
1001
1010
  output = output.concat(dyna.conv.recordDynToNaan(data.Items))
1002
1011
  data.Count
1003
1012
  })
@@ -1016,7 +1025,7 @@ closure DynaTable(dyna, tablename, options, local table) {
1016
1025
  // {
1017
1026
  // index: <string> // index name in table
1018
1027
  // filter: <key-expression> // conditions for items to return
1019
- // project: <array> // project only specified attributes
1028
+ // project: <array> | `count // project only specified attributes
1020
1029
  // paging: <exclusive start key> // for paging; updated each call
1021
1030
  // consistentRead: <boolean> // true to use consistent read
1022
1031
  // limit: <number> // maximum count of results
@@ -1031,6 +1040,7 @@ closure DynaTable(dyna, tablename, options, local table) {
1031
1040
  params = {
1032
1041
  TableName: tablename
1033
1042
  }
1043
+ output = []
1034
1044
  if options.index
1035
1045
  params.IndexName = options.index
1036
1046
  if options.filter {
@@ -1040,15 +1050,23 @@ closure DynaTable(dyna, tablename, options, local table) {
1040
1050
  if error
1041
1051
  return (list(Error("table.scanRecords filter:", error)))
1042
1052
  }
1043
- if options.project
1053
+ if options.project == `count {
1054
+ params.Select = "COUNT"
1055
+ output = {
1056
+ Count: 0
1057
+ ScannedCount: 0
1058
+ }
1059
+ } else if options.project
1044
1060
  params.ProjectionExpression = options.project.join(", ")
1045
1061
  if options.paging
1046
1062
  params.ExclusiveStartKey = options.paging
1047
1063
  if options.consistentRead
1048
1064
  params.ConsistentRead = options.consistentRead
1049
- output = []
1050
1065
  `(error) = dynQueryPager(`scan, params, options.limit, function(data) {
1051
- if data.Count >= 0
1066
+ if dictionary(output) {
1067
+ output.Count += data.Count
1068
+ output.ScannedCount += data.ScannedCount
1069
+ } else if data.Count >= 0
1052
1070
  output = output.concat(dyna.conv.recordDynToNaan(data.Items))
1053
1071
  data.Count
1054
1072
  })
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2020-2024 by Richard C. Zulch
9
+ * Copyright (c) 2020-2025 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -22,6 +22,7 @@
22
22
  */
23
23
 
24
24
  closure S3bucket(s3, name, bucketOptions, local bucket) {
25
+ global()
25
26
  bucket = new(object, this)
26
27
  bucket.name = name
27
28
 
@@ -83,8 +84,6 @@ closure S3bucket(s3, name, bucketOptions, local bucket) {
83
84
  // }
84
85
  //
85
86
  bucket.put = closure put(key, data, options, local params, pending) {
86
- if !contentType
87
- contentType = "application/octet-stream"
88
87
  params = {
89
88
  Bucket: bucket.name
90
89
  Key: key
@@ -246,6 +245,7 @@ closure S3bucket(s3, name, bucketOptions, local bucket) {
246
245
  */
247
246
 
248
247
  closure S3(local s3) {
248
+ global(awsSDK)
249
249
  s3 = new(object, this)
250
250
 
251
251
  //
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2023 by Richard C. Zulch
9
+ * Copyright (c) 2023-2025 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -19,6 +19,7 @@
19
19
  */
20
20
 
21
21
  closure SqsMQ(sqs, queueUrl, local sqsq) {
22
+ global()
22
23
  sqsq = new(object, this)
23
24
 
24
25
  //
@@ -55,6 +56,7 @@ closure SqsMQ(sqs, queueUrl, local sqsq) {
55
56
  */
56
57
 
57
58
  closure SQS(local sqs) {
59
+ global(awsSDK)
58
60
  sqs = new(object, this)
59
61
 
60
62
  //
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2021 by Richard C. Zulch
9
+ * Copyright (c) 2021-2025 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -22,6 +22,7 @@
22
22
  */
23
23
 
24
24
  closure queryPager(apiobject, procname, params, xargs, doneCB, local pending, looping, result) {
25
+ global()
25
26
  if !apiobject[procname]
26
27
  return (list(Error("queryPager method undefined:", procname)))
27
28
  pending = new(nonce)
@@ -55,6 +56,7 @@ closure queryPager(apiobject, procname, params, xargs, doneCB, local pending, lo
55
56
  */
56
57
 
57
58
  function QueryPager args {
59
+ global()
58
60
  closure(local apiobject, procname, params, xargs, doneCB) {
59
61
  apiobject = pop(args)
60
62
  procname = pop(args)
@@ -0,0 +1,198 @@
1
+ /*
2
+ * aws_vm.nlg
3
+ * serviceAws
4
+ *
5
+ * VM controller under AWS.
6
+ *
7
+ * column positioning: // // !
8
+ *
9
+ * Copyright (c) 2024-2025 by Richard C. Zulch
10
+ *
11
+ */
12
+
13
+ vmState = false;
14
+
15
+ /*
16
+ * VmControl
17
+ *
18
+ * VM control object for AWS.
19
+ *
20
+ */
21
+
22
+ closure VmControl(creds, local dokon) {
23
+ global(js, http, vmState)
24
+ if vmState.dokon
25
+ return (vmState.dokon) // singleton
26
+ dokon = new(object, this)
27
+ vmState = new(nonce) // don't save with state
28
+ vmState.dokon = dokon
29
+
30
+ // detachVM
31
+ //
32
+ // Tell AWS to detach a VM from its scalability group. Returns a tuple with:
33
+ // {
34
+ // Activities: [
35
+ // {
36
+ // ActivityId: "STRING_VALUE",
37
+ // AutoScalingGroupName: "STRING_VALUE",
38
+ // Description: "STRING_VALUE",
39
+ // Cause: "STRING_VALUE",
40
+ // StartTime: new Date("TIMESTAMP"),
41
+ // EndTime: new Date("TIMESTAMP"),
42
+ // StatusCode: "PendingSpotBidPlacement" || "WaitingForSpotInstanceRequestId"
43
+ // || "WaitingForSpotInstanceId" || "WaitingForInstanceId" || "PreInService"
44
+ // || "InProgress" || "WaitingForELBConnectionDraining" || "MidLifecycleAction"
45
+ // || "WaitingForInstanceWarmup" || "Successful" || "Failed" || "Cancelled"
46
+ // || "WaitingForConnectionDraining",
47
+ // StatusMessage: "STRING_VALUE",
48
+ // Progress: Number("int"),
49
+ // Details: "STRING_VALUE",
50
+ // AutoScalingGroupState: "STRING_VALUE",
51
+ // AutoScalingGroupARN: "STRING_VALUE",
52
+ // }
53
+ // ]
54
+ // }
55
+ closure detachVM(instanceID, local error, ascSdk, params, asclient, response) {
56
+ `(error, ascSdk) = await(js.i("@aws-sdk/client-auto-scaling"))
57
+ if error
58
+ return (list(error))
59
+ params = { }
60
+ params.region = creds.region
61
+ params.credentials = {
62
+ accessKeyId: creds.keyID
63
+ secretAccessKey: creds.keySecret
64
+ }
65
+ if creds.endpoint
66
+ params.endpoint = creds.endpoint
67
+ asclient = xnew(ascSdk.AutoScalingClient, params)
68
+ `(error, response) = await(asclient.send(xnew(ascSdk.DetachInstancesCommand, {
69
+ AutoScalingGroupName: creds.ec2AsgName
70
+ InstanceIds: [ instanceID ]
71
+ ShouldDecrementDesiredCapacity: false
72
+ })))
73
+ list(error, response)
74
+ }
75
+
76
+ // terminateVM
77
+ //
78
+ // Tell AWS to terminate a VM, ours in point of fact. Returns a tuple with:
79
+ // {
80
+ // TerminatingInstances: [
81
+ // {
82
+ // InstanceId: "STRING_VALUE"
83
+ // CurrentState: { // InstanceState
84
+ // Code: Number("int"),
85
+ // Name: "pending" || "running" || "shutting-down" || "terminated"
86
+ // || "stopping" || "stopped",
87
+ // }
88
+ // PreviousState: {
89
+ // Code: Number("int"),
90
+ // Name: "pending" || "running" || "shutting-down" || "terminated"
91
+ // || "stopping" || "stopped",
92
+ // }
93
+ // }
94
+ // ]
95
+ // }
96
+ //
97
+ closure terminateVM(instanceID, local error, ec2sdk, params, ec2client, response) {
98
+ `(error, ec2sdk) = await(js.i("@aws-sdk/client-ec2"))
99
+ if error
100
+ return (list(error))
101
+ params = { }
102
+ params.region = creds.region
103
+ params.credentials = {
104
+ accessKeyId: creds.keyID
105
+ secretAccessKey: creds.keySecret
106
+ }
107
+ if creds.endpoint
108
+ params.endpoint = creds.endpoint
109
+ ec2client = xnew(ec2sdk.EC2Client, params)
110
+ `(error, response) = await(ec2client.send(xnew(ec2sdk.TerminateInstancesCommand,
111
+ { InstanceIds: [ instanceID ] })))
112
+ list(error, response)
113
+ }
114
+
115
+ // getHostname
116
+ //
117
+ // Ask the VM for its FQDN.
118
+ //
119
+ // TOKEN="`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"`"
120
+ // curl -H "X-aws-ec2-metadata-token: $TOKEN" 'http://169.254.169.254/latest/meta-data/hostname'
121
+ // => ip-172-31-80-96.ec2.internal
122
+ //
123
+ dokon.getHostname = closure getHostname(local error, token, hostname) {
124
+ `(error, token) = http.HttpRequest("http://169.254.169.254/latest/api/token", {
125
+ headers: {
126
+ "X-aws-ec2-metadata-token-ttl-seconds": "2160"
127
+ }
128
+ method: "PUT"
129
+ })
130
+ if !error
131
+ `(error, hostname) = http.HttpRequest("http://169.254.169.254/latest/meta-data/hostname", {
132
+ headers: {
133
+ "X-aws-ec2-metadata-token": token
134
+ }
135
+ })
136
+ if error
137
+ list(error)
138
+ else
139
+ list(false, hostname)
140
+ }
141
+
142
+ // vmShutdown
143
+ //
144
+ // Get our AWS EC2 VM instance ID and then request that it be shut down.
145
+ //
146
+ // How to get auth token:
147
+ // curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"
148
+ // => AQAEAJHVrb_zRdju_A3tYBJLtKHsjej5GFVsDu1VuBCErP8MNVOr0Q==
149
+ //
150
+ // How to get instance-id:
151
+ // curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/instance-id
152
+ // => i-008dc3846da316a93
153
+ //
154
+
155
+ dokon.shutdown = closure shutdown(local error, token, instanceID, response) {
156
+ `(error, token) = http.HttpRequest("http://169.254.169.254/latest/api/token", {
157
+ headers: {
158
+ "X-aws-ec2-metadata-token-ttl-seconds": "2160"
159
+ }
160
+ method: "PUT"
161
+ })
162
+ if !error
163
+ `(error, instanceID) = http.HttpRequest("http://169.254.169.254/latest/meta-data/instance-id", {
164
+ headers: {
165
+ "X-aws-ec2-metadata-token": token
166
+ }
167
+ })
168
+ if !error
169
+ `(error, response) = detachVM(instanceID)
170
+ if !error
171
+ `(error, response) = terminateVM(instanceID)
172
+ if error
173
+ list(error)
174
+ else
175
+ list(false, response)
176
+ }
177
+
178
+ // finis
179
+ dokon
180
+ };
181
+
182
+
183
+ /*
184
+ * vmConInit
185
+ *
186
+ * Initialize the component.
187
+ *
188
+ */
189
+
190
+ function vmConInit(local manifest) {
191
+ manifest = `(VmControl, vmConInit)
192
+ Naan.module.build(module.id, "aws_vm", function(modobj, compobj) {
193
+ require("./serviceAws.nlg")
194
+ compobj.manifest = manifest
195
+ http = require("naanlib:frameworks/node/http_request.nlg")
196
+ module.exports.VmControl = VmControl
197
+ })
198
+ } ();
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2020-2024 by Richard C. Zulch
9
+ * Copyright (c) 2020-2025 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -22,6 +22,7 @@
22
22
  */
23
23
 
24
24
  closure dawsBucket(s3b, local bucket, s3) {
25
+ global(JSpath)
25
26
  bucket = new(object, this)
26
27
  s3 = s3b.s3
27
28
  bucket.name = s3b.bucketName
@@ -311,6 +312,7 @@ closure dawsBucket(s3b, local bucket, s3) {
311
312
  */
312
313
 
313
314
  closure dawsTable(database, path, tableOptions, local table) {
315
+ global(js, TextDecoder)
314
316
  table = new(object, this)
315
317
  table.type = database.type
316
318
  table.db = database
@@ -411,9 +413,10 @@ closure dawsTable(database, path, tableOptions, local table) {
411
413
 
412
414
  // add
413
415
  //
414
- // Add a new record to the table with optional metadata. This will fail if the key already
415
- // exists. The metadata is stored with the record and cannot be changed without calling update.
416
- // Metadata keys cannot begin with underscore ("_").
416
+ // Add a new record to the table with options. This will fail if the key already exists. Optional
417
+ // metadata is stored with the record and cannot be changed without calling update. Metadata keys
418
+ // cannot begin with underscore ("_"). If optional cacheControl is not specified then the default
419
+ // tableOptions.cacheControl is used.
417
420
  //
418
421
  // The returned document, which can be used with update, has the following keys:
419
422
  // key - the key name of the record
@@ -421,26 +424,25 @@ closure dawsTable(database, path, tableOptions, local table) {
421
424
  // md5 - the MD5 hash of the content
422
425
  // <...> - the various metadata keys
423
426
 
424
- table.add = closure add(key, content, metadata, callback, local id, doc) {
427
+ table.add = closure add(key, content, options, callback, local id, doc) {
425
428
  if !callback
426
- return (syncAdapter(add, key, content, metadata))
429
+ return (syncAdapter(add, key, content, options))
427
430
  id = makeID(key)
428
431
  if !string(id)
429
- return (asyncResult(callback, id)) // error from makeID
430
- if metadata && !dictionary(metadata)
432
+ return (asyncResult(callback, id)) // error from makeID
433
+ if options.metadata && !dictionary(options.metadata)
431
434
  return (asyncResult(callback, Error("invalid arguments")))
432
- if metadata
433
- doc = new(metadata) // start with metadata
435
+ if options.metadata
436
+ doc = new(options.metadata) // start with metadata
434
437
  else
435
438
  doc = { }
436
439
  doc.key = key
437
440
  doc.id = id
438
441
  if content
439
442
  doc.content = content
440
- metadata = metadata2s3(metadata) // filter out any invalid metadata
441
443
  database.bucket.put(id, content, {
442
- metadata: metadata
443
- cacheControl: tableOptions.cacheControl
444
+ metadata: metadata2s3(options.metadata) // filter metadata for s3
445
+ cacheControl: options.cacheControl || tableOptions.cacheControl
444
446
  }, function(error, resp) {
445
447
  if error
446
448
  error = Error("add failed", error, id, { status: error.status })
@@ -453,17 +455,17 @@ closure dawsTable(database, path, tableOptions, local table) {
453
455
 
454
456
  // update
455
457
  //
456
- // Update a record in the table using the document previously returned by add or get.
458
+ // Update a record in the table using the document previously returned by add or get. Specify
459
+ // options to update those values, otherwise existing/default values are used.
457
460
 
458
- table.update = closure update(doc, callback, local metadata) {
461
+ table.update = closure update(doc, options, callback) {
459
462
  if !callback
460
- return (syncAdapter(update, doc))
463
+ return (syncAdapter(update, doc, options))
461
464
  if !doc._id || !doc._id.startsWith(table.s3prefix) || !doc._md5
462
465
  return (asyncResult(callback, Error("invalid argument")))
463
- metadata = metadata2s3(doc)
464
466
  database.bucket.put(doc._id, doc.content, {
465
- metadata: metadata
466
- cacheControl: tableOptions.cacheControl
467
+ metadata: metadata2s3(options.metadata || doc)
468
+ cacheControl: options.cacheControl || tableOptions.cacheControl
467
469
  }, function(error, resp) {
468
470
  if error
469
471
  error = Error("update failed", error, doc._id, { status: error.status })
@@ -519,7 +521,7 @@ closure dawsTable(database, path, tableOptions, local table) {
519
521
  resp.content = readableStreamToArrayBuffer(resp.content)
520
522
  else if jsTypedArray(resp.content)
521
523
  resp.content = xnew(TextDecoder, "utf-8").decode(resp.content)
522
- else if jsInstanceOf(resp.content, js.w.ReadableStream)
524
+ else if jsInstanceOf(resp.content, js.w.ReadableStream) // for browsers only
523
525
  resp.content = xnew(TextDecoder, "utf-8").decode(readableStreamToArrayBuffer(resp.content))
524
526
  resp._id = id }
525
527
  callback(error, resp)
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2020-2024 by Richard C. Zulch
9
+ * Copyright (c) 2020-2025 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -19,6 +19,7 @@
19
19
  */
20
20
 
21
21
  closure psmaConnector(psm, local connClassID, connector, watch) {
22
+ global(awsSDK)
22
23
  connClassID = "S3DB"
23
24
  require("./dbt_aws.nlg")
24
25
  connector = new(object, this)
@@ -83,6 +83,7 @@ closure GcApi(options, local gcapi, cacert, renew, access_token, expires_at) {
83
83
  `(error, data, status) = https.HttpsApiRequest(url, {
84
84
  putdata: put || undefined
85
85
  cacert: cacert || undefined
86
+ debug: options.debug || undefined
86
87
  headers: [
87
88
  `(Accept, "application/json"),
88
89
  list(`Authorization, "Bearer ".concat(access_token))