@naanlang/naan 1.0.4 → 1.0.5

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 (39) hide show
  1. package/LICENSE.md +1 -1
  2. package/README.md +8 -6
  3. package/bin/index.js +2 -2
  4. package/dist/env_web.js +25 -5
  5. package/dist/naan.min.js +4 -4
  6. package/frameworks/browser/https_request.nlg +120 -0
  7. package/frameworks/browser/sworker.js +19 -19
  8. package/frameworks/browser/terminals.nlg +5 -3
  9. package/frameworks/browser/workers.nlg +1 -0
  10. package/frameworks/client/psm_client.nlg +7 -1
  11. package/frameworks/common/common.nlg +95 -25
  12. package/frameworks/common/preferences.nlg +1 -0
  13. package/frameworks/common/watching.nlg +1 -0
  14. package/frameworks/node/https_request.nlg +119 -0
  15. package/frameworks/node/node.nlg +1 -0
  16. package/frameworks/project/proj_cliser.nlg +11 -3
  17. package/frameworks/project/proj_console.nlg +13 -4
  18. package/frameworks/project/proj_folder.nlg +10 -1
  19. package/frameworks/project/proj_lambda.nlg +13 -4
  20. package/frameworks/project/proj_static.nlg +11 -3
  21. package/frameworks/project/projects.nlg +80 -15
  22. package/frameworks/running/executors.nlg +1 -0
  23. package/lib/browser/env_web.js +27 -7
  24. package/lib/core/naanlib.js +4 -4
  25. package/package.json +1 -1
  26. package/plugins/serviceAws/aws/aws-sdk-node.min.js +2 -0
  27. package/plugins/serviceAws/aws/aws-sdk.min.js +2 -88
  28. package/plugins/serviceAws/aws_cloudwatchlogs.nlg +7 -6
  29. package/plugins/serviceAws/aws_dynamo.nlg +127 -44
  30. package/plugins/serviceAws/aws_lambda.nlg +11 -4
  31. package/plugins/serviceAws/aws_s3.nlg +44 -14
  32. package/plugins/serviceAws/dbt_aws.nlg +29 -36
  33. package/plugins/serviceAws/psm_aws.nlg +21 -16
  34. package/plugins/serviceAws/serviceAws.nlg +6 -4
  35. package/test/harness.nlg +3 -1
  36. package/test/test_01_core.nlg +5 -5
  37. package/test/test_02_context.nlg +4 -4
  38. package/test/test_03_jsinterop.nlg +9 -3
  39. package/plugins/serviceAws/aws_cognito.nlg +0 -76
@@ -26,10 +26,13 @@ closure CloudWatchLogs(local cwlogs) {
26
26
  //
27
27
  cwlogs.login = closure login(creds) {
28
28
  cwlogs.aws = xnew(awsSDK.CloudWatchLogs, {
29
- apiVersion: "2014-03-28",
30
- accessKeyId: creds.keyID,
31
- secretAccessKey: creds.keySecret,
32
- region: creds.region })
29
+ region: creds.region
30
+ credentials: {
31
+ accessKeyId: creds.keyID
32
+ secretAccessKey: creds.keySecret
33
+ }
34
+ })
35
+ list(false, { ok: true })
33
36
  }
34
37
 
35
38
  //
@@ -67,7 +70,6 @@ closure CloudWatchLogs(local cwlogs) {
67
70
  }
68
71
  output = []
69
72
  `(error) = cwlQueryPager(`describeLogStreams, params, options.limit, function(data) {
70
- debuglog("describeLogStreams:", params.limit, data.logStreams.length, output.length)
71
73
  if data.logStreams.length > 0
72
74
  output = output.concat(new(data.logStreams))
73
75
  data.logStreams.length
@@ -89,7 +91,6 @@ closure CloudWatchLogs(local cwlogs) {
89
91
  }
90
92
  output = []
91
93
  `(error) = cwlQueryPager(`getLogEvents, params, options.limit, function(data) {
92
- debuglog("getLogEvents:", params.limit, data.events.length, output.length)
93
94
  if data.events.length > 0
94
95
  output = output.concat(new(data.events))
95
96
  data.events.length
@@ -191,7 +191,7 @@ closure DynaConverter(local conv) {
191
191
  // datumNaanToDyn
192
192
  // Convert a Naan datum to DynamoDB format.
193
193
  //
194
- conv.datumNaanToDyn = function datumNaanToDyn(datum, local ntype, dtype) {
194
+ conv.datumNaanToDyn = function datumNaanToDyn(datum, local output, ntype, dtype) {
195
195
  output = { }
196
196
  if tuple(datum)
197
197
  datum = datum.toarray
@@ -299,13 +299,35 @@ closure DynaConverter(local conv) {
299
299
  output
300
300
  }
301
301
 
302
- // genAttributeAdder
302
+ // genAttributeNameAdder
303
303
  //
304
- // Return a function that, when called repeatedly, will add attribute definitions to the
304
+ // Return a function that, when called repeatedly, adds attribute name definitions to the
305
+ // specified parameter block.
306
+ //
307
+ closure genAttributeNameAdder(params, local adex) {
308
+ function attrib(name, local adef, atype, key, data) {
309
+ if !string(name)
310
+ name = tostring(name) // try to make it a string
311
+ if !params.ExpressionAttributeNames
312
+ params.ExpressionAttributeNames = { }
313
+ if !adex
314
+ adex = (keys(params.ExpressionAttributeNames).length || 0) + 1
315
+ for `(key, data) in params.ExpressionAttributeNames
316
+ if data == name
317
+ return (key) // found matching name
318
+ key = "#n".concat(adex++)
319
+ params.ExpressionAttributeNames[key] = name
320
+ key
321
+ }
322
+ }
323
+
324
+ // genAttributeValueAdder
325
+ //
326
+ // Return a function that, when called repeatedly, adds attribute value definitions to the
305
327
  // specified parameter block. Symbols are interpreted as literal names, e.g. attribute names, and
306
328
  // are not defined as values here.
307
329
  //
308
- closure genAttributeAdder(params, local adex) {
330
+ closure genAttributeValueAdder(params, local adex) {
309
331
  function attrib(expr, local adef, atype, key, data) {
310
332
  if symbol(expr) && !member(expr, `(true, false, null))
311
333
  return (expr.tostring) // literal attribute name
@@ -355,7 +377,7 @@ closure DynaConverter(local conv) {
355
377
  //
356
378
 
357
379
  conv.createKeyExpression = function createKeyExpression(params, conds
358
- local error, attrib, rangename, range, keyname, op, expr, rangex, arg1, arg2) {
380
+ local error, attval, rangename, range, keyname, op, expr, rangex, arg1, arg2) {
359
381
 
360
382
  // dupeop
361
383
  //
@@ -367,7 +389,7 @@ closure DynaConverter(local conv) {
367
389
  //
368
390
  // build the expression
369
391
  //
370
- attrib = genAttributeAdder(params)
392
+ attval = genAttributeValueAdder(params)
371
393
  for `(rangename, ranges) in conds {
372
394
  if !array(ranges)
373
395
  ranges = [ranges]
@@ -379,7 +401,7 @@ closure DynaConverter(local conv) {
379
401
  for `(op, expr) in range {
380
402
  if op == "key"
381
403
  continue
382
- expr = attrib(expr)
404
+ expr = attval(expr)
383
405
  if op == ">=" {
384
406
  if rangex.0 == "<="
385
407
  rangex = list("between", expr, rangex.1)
@@ -427,32 +449,38 @@ closure DynaConverter(local conv) {
427
449
  // set: [`(attribute, value)] // set attributes to values
428
450
  // sum: [`(attribute, value)] // attributes += values
429
451
  // diff: [`(attribute, value)] // attributes -= values
452
+ // add: [`(attribute, value)] // ADD(attributes, values) -- attrib need not exist
430
453
  // remove: [attribute] // remove attributes from the record
431
454
  // }
432
455
  // If successful the parameter block is updated, otherwise it may be partly updated. The return
433
456
  // value is a standard (error, data) tuple where the data is just { ok: true } if no error.
434
457
  //
435
458
  conv.createUpdateExpression = function createUpdateExpression(params, updates,
436
- local attrib, output, item) {
459
+ local attval, attname, output, item) {
437
460
 
438
461
  if params.UpdateExpression
439
462
  return (list(Error("conv.createUpdateExpression: UpdateExpression already exists")))
440
- attrib = genAttributeAdder(params)
463
+ attval = genAttributeValueAdder(params)
464
+ attname = genAttributeNameAdder(params)
441
465
  output = {
442
466
  SET: []
467
+ ADD: []
443
468
  REMOVE: []
444
469
  }
445
470
  for item in updates.set
446
- output.SET.push(strcat(item.0, "=", attrib(item.1)))
471
+ output.SET.push(strcat(attname(item.0), "=", attval(item.1)))
447
472
  for item in updates.sum
448
- output.SET.push(strcat(item.0, "=", item.0, "+", attrib(item.1)))
473
+ output.SET.push(strcat(attname(item.0), "=", attname(item.0), "+", attval(item.1)))
449
474
  for item in updates.diff
450
- output.SET.push(strcat(item.0, "=", item.0, "-", attrib(item.1)))
475
+ output.SET.push(strcat(attname(item.0), "=", attname(item.0), "-", attval(item.1)))
476
+ for item in updates.add
477
+ output.ADD.push(strcat(attname(item.0), space, attval(item.1)))
451
478
  for item in updates.remove
452
- output.REMOVE.push(tostring(item))
479
+ output.REMOVE.push(attname(item))
453
480
  params.UpdateExpression = ""
454
481
  for `(key, value) in output
455
- params.UpdateExpression = params.UpdateExpression.concat(key, space, value.join(", "))
482
+ if value.length > 0
483
+ params.UpdateExpression = params.UpdateExpression.concat(key, space, value.join(", "), space)
456
484
  list(false, { ok: true })
457
485
  }
458
486
 
@@ -515,7 +543,7 @@ closure DynaTable(dyna, tablename, local table) {
515
543
  dyna.aws.describeTable(params, function(error, data, local output, key) {
516
544
  if error {
517
545
  dynaTableError = error
518
- debuglog("aws.describeTable failed", tablename, error)
546
+ debuglog("aws.describeTable failed", tablename, ErrorString(error))
519
547
  error = Error("table.info(".concat(tablename, ") failed"), error)
520
548
  } else {
521
549
  data = data.Table
@@ -594,7 +622,7 @@ closure DynaTable(dyna, tablename, local table) {
594
622
  dyna.aws.createTable(params, function(error, data) {
595
623
  if error {
596
624
  dynaTableError = error
597
- debuglog("aws.createTable failed", tablename, error)
625
+ debuglog("aws.createTable failed", tablename, ErrorString(error))
598
626
  error = Error("table.create(".concat(tablename, ") failed"), error)
599
627
  }
600
628
  pending.signal(list(error, data))
@@ -664,7 +692,7 @@ closure DynaTable(dyna, tablename, local table) {
664
692
  dyna.aws.getItem(params, function(error, data, local output) {
665
693
  if error {
666
694
  dynaTableError = error
667
- debuglog("aws.getItem failed", tablename, error)
695
+ debuglog("aws.getItem failed", tablename, ErrorString(error))
668
696
  error = Error("table.getRecord(".concat(tablename, ") failed"), error)
669
697
  } else
670
698
  data = dyna.conv.recordDynToNaan(data.Item)
@@ -676,7 +704,7 @@ closure DynaTable(dyna, tablename, local table) {
676
704
  //
677
705
  // putRecord
678
706
  //
679
- // Put an record in the table, replacing any existing record with the same key.
707
+ // Put a record in the table, replacing any existing record with the same key.
680
708
 
681
709
  table.putRecord = closure putRecord(record, local result, params, pending) {
682
710
  if preflightError(this)
@@ -689,7 +717,7 @@ closure DynaTable(dyna, tablename, local table) {
689
717
  dyna.aws.putItem(params, function(error, data, local output) {
690
718
  if error {
691
719
  dynaTableError = error
692
- debuglog("aws.putItem failed", tablename, error)
720
+ debuglog("aws.putItem failed", tablename, ErrorString(error))
693
721
  error = Error("table.putRecord(".concat(tablename, ") failed"), error)
694
722
  }
695
723
  pending.signal(list(error, data))
@@ -718,7 +746,6 @@ closure DynaTable(dyna, tablename, local table) {
718
746
  return
719
747
  params = {
720
748
  TableName: tablename
721
- Item: dyna.conv.recordNaanToDyn(record)
722
749
  }
723
750
  params.Key = makeDynKey(list(hashValue, rangeValue))
724
751
  if options.condition {
@@ -731,22 +758,22 @@ closure DynaTable(dyna, tablename, local table) {
731
758
  if error
732
759
  return (list(Error("table.updateRecord:", error)))
733
760
  }
734
- dyna.conv.createUpdateExpression(params, `SET, options)
761
+ dyna.conv.createUpdateExpression(params, options)
735
762
  if options.return {
736
763
  params.ReturnValues = {
737
764
  all_old: "ALL_OLD"
738
765
  all_new: "ALL_NEW"
739
766
  updated_old: "UPDATED_OLD"
740
767
  updated_new: "UPDATED_NEW"
741
- }
768
+ }[options.return]
742
769
  if !params.ReturnValues
743
770
  params.ReturnValues = "NONE"
744
771
  }
745
772
  pending = new(nonce)
746
- dyna.aws.putItem(params, function(error, data) {
773
+ dyna.aws.updateItem(params, function(error, data) {
747
774
  if error {
748
775
  dynaTableError = error
749
- debuglog("aws.putItem failed", tablename, error)
776
+ debuglog("aws.updateItem failed", tablename, ErrorString(error), Dialect.print(params))
750
777
  error = Error("table.updateRecord(".concat(tablename, ") failed"), error)
751
778
  }
752
779
  else
@@ -755,6 +782,52 @@ closure DynaTable(dyna, tablename, local table) {
755
782
  })
756
783
  pending.wait()
757
784
  }
785
+
786
+ //
787
+ // deleteRecord
788
+ //
789
+ // Delete a record in the table. Options include conditional updates as follows:
790
+ // {
791
+ // condition: <range-expression> // conditional expression as in createKeyExpression()
792
+ // return: "all_old" // return previous attribute values, otherwise { }
793
+ // }
794
+
795
+
796
+ table.deleteRecord = closure deleteRecord(hashValue, rangeValue, options, local result, params, pending) {
797
+ if preflightError(this)
798
+ return
799
+ params = {
800
+ TableName: tablename
801
+ }
802
+ params.Key = makeDynKey(list(hashValue, rangeValue))
803
+ if options.condition {
804
+ `(error) = dyna.conv.createKeyExpression(params, {
805
+ KeyConditionExpression: {
806
+ key: table.hashKey
807
+ "=": hashValue
808
+ }
809
+ })
810
+ if error
811
+ return (list(Error("table.deleteRecord:", error)))
812
+ }
813
+ if options.return {
814
+ params.ReturnValues = {
815
+ all_old: "ALL_OLD"
816
+ }[options.return]
817
+ if !params.ReturnValues
818
+ params.ReturnValues = "NONE"
819
+ }
820
+ pending = new(nonce)
821
+ dyna.aws.deleteItem(params, function(error, data, local output) {
822
+ if error {
823
+ dynaTableError = error
824
+ debuglog("aws.deleteItem failed", tablename, ErrorString(error))
825
+ error = Error("table.deleteRecord(".concat(tablename, ") failed"), error)
826
+ }
827
+ pending.signal(list(error, data))
828
+ })
829
+ pending.wait()
830
+ }
758
831
 
759
832
  //
760
833
  // dynQueryPager
@@ -783,33 +856,38 @@ closure DynaTable(dyna, tablename, local table) {
783
856
  //
784
857
  // Options:
785
858
  // {
786
- // index: <string> // index name in table
787
- // range: <key-expression> // range of keys to retrieve
788
- // filter: <key-expression> // conditions for items to return
789
- // attmap: <dictionary> // map attribute names to #shortcuts
790
- // reverse: <boolean> // reverse order
791
- // project: <array> // project only specified attributes
792
- // exstart: <exclusive start key> // for paging
793
- // limit: <number> // maximum count of results
859
+ // indexName: <string> // index name in table
860
+ // indexHashKey: <string> // name of index hashKey
861
+ // range: <key-expression> // range of keys to retrieve
862
+ // filter: <key-expression> // conditions for items to return
863
+ // attmap: <dictionary> // map attribute names to #shortcuts
864
+ // reverse: <boolean> // reverse order
865
+ // project: <array> // project only specified attributes
866
+ // exstart: <exclusive start key> // for paging
867
+ // limit: <number> // maximum count of results
794
868
  // }
795
869
  //
796
870
 
797
871
  table.queryRecords = closure queryRecords(hashValue, options
798
- local error, result, params, op, expr, output, error)
872
+ local error, result, params, hashKey, op, expr, output, error)
799
873
  {
800
874
  if preflightError(this)
801
875
  return
802
876
  params = {
803
877
  TableName: tablename
804
878
  }
879
+ hashKey = table.hashKey
880
+ if options.indexName {
881
+ params.IndexName = options.indexName
882
+ if options.indexHashKey
883
+ hashKey = options.indexHashKey
884
+ }
805
885
  dyna.conv.createKeyExpression(params, {
806
886
  KeyConditionExpression: {
807
- key: table.hashKey
887
+ key: hashKey
808
888
  "=": hashValue
809
889
  }
810
890
  })
811
- if options.index
812
- params.IndexName = options.index
813
891
  if options.range {
814
892
  `(error) = dyna.conv.createKeyExpression(params, {
815
893
  KeyConditionExpression: options.range
@@ -825,7 +903,7 @@ closure DynaTable(dyna, tablename, local table) {
825
903
  return (list(Error("table.queryRecords filter:", error)))
826
904
  }
827
905
  if options.attmap
828
- params.ExpressionAttributeNames = attmap
906
+ params.ExpressionAttributeNames = options.attmap
829
907
  if options.reverse
830
908
  params.ScanIndexForward = false
831
909
  if options.project
@@ -869,10 +947,13 @@ closure DynaTable(dyna, tablename, local table) {
869
947
  }
870
948
  if options.index
871
949
  params.IndexName = options.index
872
- if options.filter
873
- dyna.conv.createKeyExpression(params, {
950
+ if options.filter {
951
+ `(error) = dyna.conv.createKeyExpression(params, {
874
952
  FilterExpression: options.filter
875
953
  })
954
+ if error
955
+ return (list(Error("table.scanRecords filter:", error)))
956
+ }
876
957
  if options.attmap
877
958
  params.ExpressionAttributeNames = attmap
878
959
  if options.project
@@ -915,10 +996,12 @@ closure DynamoDB(local dyna) {
915
996
  //
916
997
  dyna.login = function login(creds) {
917
998
  dyna.aws = xnew(awsSDK.DynamoDB, {
918
- apiVersion: "2012-08-10",
919
- accessKeyId: creds.keyID,
920
- secretAccessKey: creds.keySecret,
921
- region: creds.region })
999
+ region: creds.region
1000
+ credentials: {
1001
+ accessKeyId: creds.keyID
1002
+ secretAccessKey: creds.keySecret
1003
+ }
1004
+ })
922
1005
  list(false, { ok: true })
923
1006
  }
924
1007
 
@@ -985,9 +1068,9 @@ function dynaInit(local manifest) {
985
1068
  manifest = `(TimeKeys, DynaConverter, DynaTable, DynamoDB, dynaInit)
986
1069
 
987
1070
  Naan.module.build(module.id, "aws_dynamo", function(modobj, compobj) {
1071
+ require("./serviceAws.nlg")
988
1072
  compobj.manifest = manifest
989
1073
  modobj.exports.TimeKeys = TimeKeys
990
1074
  modobj.exports.DynamoDB = DynamoDB
991
- require("./serviceAws.nlg")
992
1075
  })
993
1076
  } ();
@@ -26,10 +26,13 @@ closure Lambda(local lamb) {
26
26
  //
27
27
  lamb.login = closure login(creds) {
28
28
  lamb.aws = xnew(awsSDK.Lambda, {
29
- apiVersion: "2015-03-31",
30
- accessKeyId: creds.keyID,
31
- secretAccessKey: creds.keySecret,
32
- region: creds.region })
29
+ region: creds.region
30
+ credentials: {
31
+ accessKeyId: creds.keyID
32
+ secretAccessKey: creds.keySecret
33
+ }
34
+ })
35
+ list(false, { ok: true })
33
36
  }
34
37
 
35
38
  //
@@ -55,8 +58,12 @@ closure Lambda(local lamb) {
55
58
  //
56
59
  // updateFunctionCode
57
60
  //
61
+ // Note that as of 2022-05-17 AWS SDK v3 requires that the zipfile be a Uint8Array, not an
62
+ // ArrayBuffer or anything simple like that. What a #$*#(! waste of time.
63
+ //
58
64
  lamb.updateFunctionCode = closure updateFunctionCode(name, zipfile, local params, pending) {
59
65
  params = {
66
+ Architectures: [ "x86_64" ]
60
67
  FunctionName: name
61
68
  ZipFile: zipfile
62
69
  }
@@ -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-2022 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -81,15 +81,17 @@ closure S3(local s3) {
81
81
  s3.login = closure login(creds, local params) {
82
82
  if !awsSDK
83
83
  return (list(Error("S3.login: no AWS SDK available")))
84
- params = {
85
- apiVersion: "2012-08-10"
86
- }
84
+ params = { }
87
85
  if creds {
88
- params.accessKeyId = creds.keyID,
89
- params.secretAccessKey = creds.keySecret,
90
86
  params.region = creds.region
87
+ params.credentials = {
88
+ accessKeyId: creds.keyID
89
+ secretAccessKey: creds.keySecret
90
+ }
91
91
  }
92
92
  s3.aws = xnew(awsSDK.S3, params)
93
+ if awsSDK.S3Client
94
+ s3.client = xnew(awsSDK.S3Client, params)
93
95
  `(false, { ok: true })
94
96
  }
95
97
 
@@ -99,16 +101,44 @@ closure S3(local s3) {
99
101
  // Return a new bucket access object for the table of the specified name. The bucket may not
100
102
  // yet exist; see S3bucket for methods.
101
103
  //
102
- // parameters:
104
+ s3.bucket = closure bucket(name) {
105
+ S3bucket(s3, name)
106
+ }
107
+
108
+ //
109
+ // getSignedURL
110
+ //
111
+ // This obtains a pre-signed URL for the specified command and its parameters. Options control
112
+ // how the URL is generated.
113
+ //
114
+ // Options:
103
115
  // {
104
- // hash: <name> // primary hash key attribute name (required)
105
- // hashType: <type> // string | numeric (required)
106
- // range: <name> // primary range key attribute name (optional)
107
- // rangeType: <name> // string | numeric (required if range specified)
116
+ // expiresIn: <seconds> // default expiration is 900 (15 minutes)
108
117
  // }
109
118
  //
110
- s3.bucket = closure bucket(name) {
111
- S3bucket(s3, name)
119
+ s3.getSignedURL = closure getSignedURL(cmdname, cmdparams, local command, options, pending) {
120
+ if s3.client {
121
+ if cmdname == "putObject"
122
+ cmdname = "PutObjectCommand"
123
+ else if cmdname == "getObject"
124
+ cmdname = "GetObjectCommand"
125
+ else
126
+ return (list(Error("S3bucket.getSignedURL: command not present:", cmdname)))
127
+ if cmdparams.Expires {
128
+ cmdparams = new(cmdparams)
129
+ options.expiresIn = cmdparams.Expires
130
+ cmdparams.Expires = undefined
131
+ } else
132
+ options = { }
133
+ command = xnew(awsSDK.s3Commands[cmdname], cmdparams)
134
+ await(awsSDK.getSignedUrl(s3.client, command, options))
135
+ } else {
136
+ pending = new(nonce)
137
+ s3.aws.getSignedUrl(cmdname, cmdparams, function(error, data) {
138
+ pending.signal(list(error, data))
139
+ })
140
+ pending.wait()
141
+ }
112
142
  }
113
143
 
114
144
  // finis
@@ -126,7 +156,7 @@ closure S3(local s3) {
126
156
  function s3Init(local manifest) {
127
157
  manifest = `(S3bucket, S3, s3Init)
128
158
 
129
- Naan.module.build(module.id, "S3", function(modobj, compobj) {
159
+ Naan.module.build(module.id, "aws_s3", function(modobj, compobj) {
130
160
  require("./serviceAws.nlg")
131
161
  compobj.manifest = manifest
132
162
  modobj.exports.S3 = S3