@naanlang/naan 1.0.2 → 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 (56) hide show
  1. package/LICENSE.md +1 -1
  2. package/README.md +10 -8
  3. package/bin/index.js +2 -2
  4. package/dist/env_web.js +172 -22
  5. package/dist/naan.min.js +6 -6
  6. package/frameworks/browser/https_request.nlg +120 -0
  7. package/frameworks/browser/sworker.js +299 -91
  8. package/frameworks/browser/terminals.nlg +28 -13
  9. package/frameworks/browser/workers.nlg +27 -12
  10. package/frameworks/client/apiclient.nlg +17 -4
  11. package/frameworks/client/psm_client.nlg +184 -81
  12. package/frameworks/common/common.nlg +105 -25
  13. package/frameworks/common/preferences.nlg +1 -0
  14. package/frameworks/common/watching.nlg +1 -0
  15. package/frameworks/node/apiserver.nlg +31 -15
  16. package/frameworks/node/filesystem.nlg +230 -46
  17. package/frameworks/node/gitter.nlg +27 -6
  18. package/frameworks/node/https_request.nlg +119 -0
  19. package/frameworks/node/node.nlg +1 -0
  20. package/frameworks/node/psm_server.nlg +113 -37
  21. package/frameworks/project/build.nlg +40 -23
  22. package/frameworks/project/proj_cliser.nlg +11 -3
  23. package/frameworks/project/proj_console.nlg +13 -4
  24. package/frameworks/project/proj_folder.nlg +10 -1
  25. package/frameworks/project/proj_lambda.nlg +13 -4
  26. package/frameworks/project/proj_static.nlg +11 -3
  27. package/frameworks/project/projects.nlg +128 -40
  28. package/frameworks/running/debugnub.nlg +2 -5
  29. package/frameworks/running/executors.nlg +1 -0
  30. package/frameworks/storage/csv.nlg +120 -0
  31. package/frameworks/storage/dbt_pouch.nlg +41 -25
  32. package/frameworks/storage/psm.nlg +108 -28
  33. package/frameworks/storage/psm_dbtables.nlg +7 -3
  34. package/frameworks/storage/resources.nlg +30 -2
  35. package/lib/browser/env_web.js +170 -20
  36. package/lib/browser/env_webworker.js +1 -1
  37. package/lib/browser/require.js +1 -1
  38. package/lib/core/naanlib.js +6 -6
  39. package/package.json +2 -1
  40. package/plugins/serviceAws/aws/aws-sdk-node.min.js +2 -0
  41. package/plugins/serviceAws/aws/aws-sdk.min.js +2 -88
  42. package/plugins/serviceAws/aws_cloudwatchlogs.nlg +8 -7
  43. package/plugins/serviceAws/aws_dynamo.nlg +715 -148
  44. package/plugins/serviceAws/aws_dynextra.nlg +294 -0
  45. package/plugins/serviceAws/aws_lambda.nlg +11 -4
  46. package/plugins/serviceAws/aws_s3.nlg +44 -14
  47. package/plugins/serviceAws/dbt_aws.nlg +58 -45
  48. package/plugins/serviceAws/psm_aws.nlg +61 -40
  49. package/plugins/serviceAws/serviceAws.nlg +7 -4
  50. package/plugins/serviceGitHub/psm_github.nlg +41 -25
  51. package/plugins/serviceGitLab/psm_gitlab.nlg +41 -25
  52. package/test/harness.nlg +3 -1
  53. package/test/test_01_core.nlg +5 -5
  54. package/test/test_02_context.nlg +4 -4
  55. package/test/test_03_jsinterop.nlg +9 -3
  56. package/plugins/serviceAws/aws_cognito.nlg +0 -76
@@ -22,6 +22,50 @@ closure psmcFsView(api, rootpath, local view, pathmod) {
22
22
  view = new(object, this)
23
23
  view.api = api
24
24
  view.rootpath = rootpath
25
+
26
+ // data
27
+ //
28
+ // Return the filesystem data, i.e. the tree data field. The minimum information is:
29
+ // platform -- e.g. "darwin" or "win32"
30
+ // semantics -- e.g. "win32" or "posix"
31
+ // pathsep -- e.g. / or \
32
+
33
+ view.data = closure data(callback) {
34
+ if !callback
35
+ return (syncAdapter(data))
36
+ params = {
37
+ path: ""
38
+ op: "tree"
39
+ depthlimit: 0
40
+ }
41
+ api.psmRemote(params, false, function(error, tree) {
42
+ if error
43
+ debuglog("psmcFsView.data: can't get initial filesystem data:", ErrorString(error))
44
+ else {
45
+ view.fsdata = tree.data
46
+ if rootpath == ""
47
+ rootpath = tree.data.pathsep
48
+ }
49
+ callback(error, tree.data)
50
+ })
51
+ }
52
+
53
+ // pathInit
54
+ //
55
+ // Initialize our path module for the applicable path type.
56
+
57
+ closure pathInit(local result) {
58
+ result = view.data()
59
+ if result.0
60
+ pathmod = false
61
+ else {
62
+ if view.fsdata.pathsep == "\\"
63
+ pathmod = JSpath.win32
64
+ else
65
+ pathmod = JSpath.posix
66
+ false
67
+ }
68
+ }
25
69
 
26
70
  // psmRemote
27
71
  //
@@ -30,10 +74,14 @@ closure psmcFsView(api, rootpath, local view, pathmod) {
30
74
  closure psmRemote(path, op, options, putdata, callback, local params, item) {
31
75
  if !callback
32
76
  return (syncAdapter(psmRemote, path, op, options, putdata))
77
+ if !pathmod && pathInit()
78
+ return // failed to initialize path module
33
79
  if !string(path)
34
80
  return (asyncResult(callback, Error("invalid path:", typeof(path))))
81
+ if rootpath != ""
82
+ path = pathmod.resolve(rootpath, path)
35
83
  params = {
36
- path: JSpath.resolve(rootpath, path)
84
+ path: path
37
85
  op: op
38
86
  }
39
87
  for item in options
@@ -59,38 +107,30 @@ closure psmcFsView(api, rootpath, local view, pathmod) {
59
107
 
60
108
  // path
61
109
  //
62
- // The path sub-object remotes calls to the NodeJS path module.
110
+ // Redirect path calls to module chosen for filesystem semantics. It is possible for this to fail
111
+ // on first connection to the filesystem, in which case we return false for the path result.
112
+ // Throwing an error would complicate our callers and testing. This way there's a small chance
113
+ // that a transient error will give a bad path, but that will be detected downstream.
63
114
 
64
115
  view.path = new(object, this)
65
- view.path[".undefined"] = closure path args {
66
- closure (selector, arglist, local error, info) {
67
- if !pathmod {
68
- `(error, data) = view.data()
69
- if error
70
- return (list(error))
71
- if info.pathsep == "\\"
72
- pathmod = JSpath.win32
73
- else
74
- pathmod = JSpath.posix
75
- }
76
- xapply(pathmod, selector, arglist)
77
- } (args.0, args.-1)
116
+ view.path[".undefined"] = function path args {
117
+ if !pathmod
118
+ pathInit()
119
+ xapply(pathmod, args.0, args.-1)
78
120
  }
79
121
 
80
- // data
122
+ // folderPath
81
123
  //
82
- // Return the filesystem data, i.e. the tree data field. The minimum information is:
83
- // platform -- e.g. "darwin" or "win32"
84
- // semantics -- e.g. "win32" or "posix"
85
- // pathsep -- e.g. / or \
86
-
87
- view.data = closure data(callback) {
88
- if !callback
89
- return (syncAdapter(data))
90
- psmRemote("", "tree", { depthlimit: 0 }, false, function(error, tree) {
91
- view.fsdata = tree.data
92
- callback(error, tree.data)
93
- })
124
+ // Return the path of the specified special folder:
125
+ // "HomeDir"
126
+ // "TempDir"
127
+ // "PackageDir"
128
+ // The returned path is relative to our root if within it, or absolute otherwise.
129
+
130
+ view.folderPath = function folderPath(folderID, callback) {
131
+ psmRemote("", "folderPath", {
132
+ folderid: folderID
133
+ }, false, callback)
94
134
  }
95
135
 
96
136
  // readLines
@@ -123,7 +163,28 @@ closure psmcFsView(api, rootpath, local view, pathmod) {
123
163
  view.writeFile = function writeFile(path, data, options, callback) {
124
164
  psmRemote(path, "writeFile", options, data, callback)
125
165
  }
126
-
166
+
167
+ // readlink
168
+ //
169
+ // Read the contents of a symbolic link at the specified path
170
+
171
+ view.readlink = function readlink(path, callback) {
172
+ psmRemote(path, "readlink", { }, false, callback)
173
+ }
174
+
175
+ // symlink
176
+ //
177
+ // Create a symbolic link path pointing to (containing the string) target
178
+
179
+ view.symlink = function symlink(target, path, options, callback) {
180
+ if options
181
+ options = new(options)
182
+ else
183
+ options = { }
184
+ options.target = target
185
+ psmRemote(path, "symlink", options, false, callback)
186
+ }
187
+
127
188
  // delete
128
189
  //
129
190
  // Delete a file or directory at the specified path
@@ -134,12 +195,45 @@ closure psmcFsView(api, rootpath, local view, pathmod) {
134
195
 
135
196
  // info
136
197
  //
137
- // Get file info at the specified path
198
+ // Get file or directory info at the specified path
199
+
200
+ view.info = function info(path, options, callback) {
201
+ if !options
202
+ options = { }
203
+ psmRemote(path, "info", options, false, callback)
204
+ }
205
+
206
+ // setInfo
207
+ //
208
+ // Set file or directory info obtained from info() call.
138
209
 
139
- view.info = function info(path, callback) {
140
- psmRemote(path, "info", { }, false, callback)
210
+ view.setInfo = function setInfo(path, info, callback) {
211
+ psmRemote(path, "setInfo", {
212
+ encoding: "json"
213
+ }, info, callback)
141
214
  }
142
215
 
216
+ // setOwnerGroup
217
+ //
218
+ // Set file or directory owner or group at the specified path
219
+
220
+ view.setOwnerGroup = function setOwnerGroup(path, uid, gid, callback) {
221
+ psmRemote(path, "setOwnerGroup", {
222
+ uid: uid
223
+ gid: gid
224
+ }, false, callback)
225
+ }
226
+
227
+ // setMode
228
+ //
229
+ // Set file or directory mode at the specified path
230
+
231
+ view.setMode = function setMode(path, mode, callback) {
232
+ psmRemote(path, "setMode", {
233
+ mode: mode
234
+ }, false, callback)
235
+ }
236
+
143
237
  // setTimes
144
238
  //
145
239
  // Set file modify and access times at the specified path
@@ -157,7 +251,7 @@ closure psmcFsView(api, rootpath, local view, pathmod) {
157
251
 
158
252
  view.dirList = function dirList(path, options, callback) {
159
253
  psmRemote(path, "dirList", {
160
- head: options.head
254
+ stat: options.stat
161
255
  }, false, callback)
162
256
  }
163
257
 
@@ -167,7 +261,7 @@ closure psmcFsView(api, rootpath, local view, pathmod) {
167
261
 
168
262
  view.readir = function readir(path, callback) {
169
263
  psmRemote(path, "dirList", {
170
- head: true
264
+ stat: true
171
265
  }, false, callback)
172
266
  }
173
267
 
@@ -209,7 +303,9 @@ closure psmcFsView(api, rootpath, local view, pathmod) {
209
303
  // delete the source after the copy.
210
304
 
211
305
  view.copy = closure copy(srcpath, dest, options, callback, local parameters) {
212
- if !options
306
+ if options
307
+ options = new(options)
308
+ else
213
309
  options = { }
214
310
  options.destination = JSpath.resolve(rootpath, dest)
215
311
  psmRemote(srcpath, "copy", options, false, callback)
@@ -325,7 +421,7 @@ closure psmcFsConnector(psm, nicc, local connClassID, connector, watch) {
325
421
  connector.psm = psm
326
422
  connector.api = nicc.api
327
423
  connector.vault = psm.vault(connClassID)
328
- connector.label = "Naan IDE Server"
424
+ connector.label = "Naan-Server"
329
425
  watch = psm.watchable(connector)
330
426
 
331
427
  // uifields
@@ -337,57 +433,44 @@ closure psmcFsConnector(psm, nicc, local connClassID, connector, watch) {
337
433
  placeholder: "http://host.com/endpoint"
338
434
  key: "urlName" },
339
435
  {
340
- label: "Auth Name"
341
- placeholder: "account name"
342
- key: "authName" },
343
- {
344
- label: "Auth Password"
345
- placeholder: "account password"
346
- key: "authPW"
347
- type: "password" },
436
+ label: "Auth Secret"
437
+ placeholder: "guid"
438
+ key: "authSecret" }
348
439
  ]
349
440
  }
350
-
351
- // hashResourceID
352
- //
353
- // Make a stable hash based on the resource credentials that we can persist or share between
354
- // different domains accessing the same resource.
355
- connector.hashResourceID = function hashResourceID(resource, local ident) {
356
- ident = resource.urlName
357
- if resource.label
358
- ident = ident.concat(resource.label)
359
- HashSHA256(ident)
360
- }
361
441
 
362
442
  // findResource
363
443
  //
364
- // Find a resource with the specified contents, returning the resID or false. The loose criteria
365
- // is: if added, would the specified contents be redundant to an existing resource?
444
+ // Find a resource with the specified contents, returning the resID or false.
366
445
  connector.findResource = function findResource(resource, local resID, creds) {
367
446
  for resID in listResources().1 {
368
447
  creds = access(resID).1
369
- if resource.urlName == creds.urlName
370
- && (!resource.label || resource.label == creds.label)
371
- && resource.authName == creds.authName
448
+ if ((!resource.urlName || resource.urlName == creds.urlName)
449
+ && (!resource.label || resource.label == creds.label))
372
450
  return (resID)
373
451
  }
374
452
  false
375
453
  }
376
454
 
377
- // addResource
455
+
456
+ // writeResource
378
457
  //
379
- // Add credentials for a named resource. The resource is a dictionary comprising these keys:
458
+ // Add or update credentials for a named resource. If no resID is specified this adds a new
459
+ // resource; otherwise it updates an existing one. The resource is a dictionary comprising these
460
+ // keys:
380
461
  // label - [optional] label we use for this resource
381
462
  // urlName - URL (e.g. "http://host.com/node/fsapi")
382
- // authName - [optional] the authentication name for a URL
383
- // authPW - [optional] the autentication password, required if authname is provided
463
+ // authSecret - access-control token for this URL
384
464
  // hidden - [optional] don't enumerate to user
385
465
  // locked - [optional] don't allow user delete
386
466
  //
387
- // The return value is (error, resID) tuple. Error, if non-false, is a dictionary of field
467
+ // The return value is an (error, resID) tuple. Error, if non-false, is a dictionary of field
388
468
  // keys and error diagnostic strings.
389
- connector.addResource = function addResource(resource, local creds, errors, error, resID, data) {
469
+ connector.writeResource = function writeResource(resource, resID,
470
+ local creds, errors, error, data, change) {
471
+ // badField - test for a bad string
390
472
  function badField(str) { !string(str) || str.trim().length == 0 }
473
+
391
474
  creds = { urlName: resource.urlName }
392
475
  errors = { }
393
476
  if resource.label
@@ -396,13 +479,13 @@ closure psmcFsConnector(psm, nicc, local connClassID, connector, watch) {
396
479
  creds.label = resource.urlName
397
480
  if badField(creds.label)
398
481
  errors.label = "invalid label"
482
+ else if !resID && findResource({ label: creds.label })
483
+ errors.label = "duplicate name"
399
484
  if badField(resource.urlName)
400
485
  errors.urlName = "required field"
401
- if !badField(resource.authName) {
402
- creds.authName = resource.authName
403
- creds.authPW = resource.authPW
404
- if badField(resource.authPW)
405
- errors.authPW = "required if Auth Name specified" }
486
+ if badField(resource.authSecret)
487
+ errors.authSecret = "required field"
488
+ creds.authSecret = resource.authSecret
406
489
  if length(errors) > 0
407
490
  error = errors
408
491
  else {
@@ -410,14 +493,36 @@ closure psmcFsConnector(psm, nicc, local connClassID, connector, watch) {
410
493
  creds.hidden = resource.hidden
411
494
  if resource.locked
412
495
  creds.locked = resource.locked
413
- resID = hashResourceID(resource)
414
- `(error, data) = connector.vault.addResource(resID, creds)
496
+ if resID {
497
+ `(error, data) = connector.vault.updateResource(resID, creds)
498
+ change = { changed: [resID] }
499
+ } else {
500
+ resID = UUID()
501
+ `(error, data) = connector.vault.addResource(resID, creds)
502
+ change = { added: [resID] }
503
+ }
415
504
  if data {
416
- watch.notify(connClassID, { added: [resID] })
417
- data = resID } }
505
+ watch.notify(connClassID, change)
506
+ data = resID
507
+ }
508
+ }
418
509
  list(error, data)
419
510
  }
420
-
511
+
512
+ // addResource
513
+ //
514
+ // Add a new resource, returning a standard error tuple per writeResource.
515
+ connector.addResource = function addResource(resource) {
516
+ writeResource(resource)
517
+ }
518
+
519
+ // updateResource
520
+ //
521
+ // Update an existing resource, returning a standard error tuple per writeResource.
522
+ connector.updateResource = function updateResource(resID, resource) {
523
+ writeResource(resource, resID)
524
+ }
525
+
421
526
  // getResource
422
527
  //
423
528
  // Get credentials for a named resource, matching the dictionary semantics of addResource. The
@@ -432,10 +537,8 @@ closure psmcFsConnector(psm, nicc, local connClassID, connector, watch) {
432
537
  }
433
538
  if string(creds.urlName)
434
539
  resource.urlName = creds.urlName
435
- if string(creds.authName)
436
- resource.authName = creds.authName
437
- if string(creds.authPW)
438
- resource.authPW = creds.authPW
540
+ if string(creds.authSecret)
541
+ resource.authSecret = creds.authSecret
439
542
  list(false, resource)
440
543
  }
441
544
 
@@ -510,7 +613,7 @@ closure psmcFsConnector(psm, nicc, local connClassID, connector, watch) {
510
613
  if creds == "HostFS"
511
614
  api = connector.api
512
615
  else
513
- api = MakeNideAPIclient(creds.urlName) // API to remote NodeJS filesystem
616
+ api = MakeNideAPIclient(creds.urlName, creds.authSecret) // API to remote NodeJS filesystem
514
617
  }
515
618
  if error
516
619
  list(error)
@@ -30,6 +30,49 @@ function EncodeQuery(prefix, items, local result, item) {
30
30
  };
31
31
 
32
32
 
33
+ /*
34
+ * ContentTypeFromFileExt
35
+ *
36
+ * Given a filename with extension, return a tuple of the recommended contentType and encoding
37
+ * for that type of data: `(contentType, encoding). Currently the encoding is false for text and
38
+ * "binary" for everything else.
39
+ *
40
+ */
41
+
42
+ function ContentTypeFromFileExt(filepath, local mimeType, encoding) {
43
+ mimeType = {
44
+ "css": "text/css",
45
+ "csv": "text/csv",
46
+ "gif": "image/gif",
47
+ "htm": "text/html",
48
+ "html": "text/html",
49
+ "ico": "image/x-icon",
50
+ "ics": "application/octet-stream",
51
+ "jpeg": "image/jpeg",
52
+ "jpg": "image/jpeg",
53
+ "js": "text/javascript",
54
+ "json": "application/json",
55
+ "mov": "video/mp4",
56
+ "mp3": "audio/mpeg",
57
+ "mp4": "video/mp4",
58
+ "nlg": "text/plain",
59
+ "npk": "application/json",
60
+ "ogg": "video/ogg",
61
+ "svg": "image/svg+xml",
62
+ "text": "text/plain",
63
+ "txt": "text/plain",
64
+ "zip": "application/octet-stream"
65
+ }[JSpath.extname(filepath).substring(1)]
66
+ if !mimeType
67
+ mimeType = "application/octet-stream"
68
+ if mimeType.startsWith("text/")
69
+ mimeType = mimeType.concat("; charset=UTF-8")
70
+ else
71
+ encoding = "binary"
72
+ list(mimeType, encoding)
73
+ };
74
+
75
+
33
76
  /*
34
77
  * UUID
35
78
  *
@@ -66,7 +109,7 @@ function JsonParse(data) {
66
109
  list(false, data)
67
110
  } catch {
68
111
  if true
69
- list(exception)
112
+ list(exception)
70
113
  }
71
114
  };
72
115
 
@@ -84,7 +127,7 @@ function JsonStringify(data) {
84
127
  list(false, data)
85
128
  } catch {
86
129
  if true
87
- list(exception)
130
+ list(exception)
88
131
  }
89
132
  };
90
133
 
@@ -125,11 +168,8 @@ function JsLoadScript(libpath, globalID, local scriptTag, fpath) {
125
168
  }
126
169
  js.w.document.body.appendChild(scriptTag)
127
170
  pending.wait()
128
- } else if js.g {
129
- if !libpath.startsWith("/")
130
- fpath = "./".concat(libpath)
131
- jsScripts[libpath] = js.r(fpath) // load in NodeJS context
132
- }
171
+ } else if js.g
172
+ jsScripts[libpath] = js.r(libpath) // load in NodeJS context
133
173
  jsScripts[libpath]
134
174
  };
135
175
 
@@ -256,15 +296,19 @@ TextEncoder = false;
256
296
  TextDecoder = false;
257
297
  EncodeURIComponent = false;
258
298
  DecodeURIComponent = false;
299
+ EncodeBase64 = false;
300
+ DecodeBase64 = false;
259
301
  JSONstringify = false;
260
302
  JSONparse = false;
303
+ Uint8ArrayFromString = false;
261
304
 
262
305
  function comrInit(local manifest) {
263
- manifest = `(EncodeQuery, UUID, JsLoadScript, LoadComponentOnDemand, LiveExport, comrInit)
306
+ manifest = `(EncodeQuery, ContentTypeFromFileExt, UUID, JsLoadScript, LoadComponentOnDemand, LiveExport, comrInit)
264
307
 
265
- Naan.module.build(module.id, "nideCommon", closure(modobj, compobj) {
308
+ Naan.module.build(module.id, "common", closure(modobj, compobj) {
266
309
  compobj.manifest = manifest
267
310
  modobj.exports.EncodeQuery = EncodeQuery
311
+ modobj.exports.ContentTypeFromFileExt = ContentTypeFromFileExt
268
312
  modobj.exports.UUID = UUID
269
313
  modobj.exports.JsLoadScript = JsLoadScript
270
314
  modobj.exports.LoadComponentOnDemand = LoadComponentOnDemand
@@ -293,6 +337,14 @@ function comrInit(local manifest) {
293
337
  JSONstringify = wsg.JSON.stringify
294
338
  JSONparse = wsg.JSON.parse
295
339
  //
340
+ // everywhere: Uint8ArrayFromString
341
+ //
342
+ Uint8ArrayFromString = function uint8ArrayFromString(data) {
343
+ wsg.Uint8Array.from(Array.from(data).map(function(letter) {
344
+ letter.charCodeAt(0)
345
+ }))
346
+ }
347
+ //
296
348
  // binaryFrom - convert arbitrary item to binary buffer or text.
297
349
  //
298
350
  function binaryFrom(data) {
@@ -321,29 +373,42 @@ function comrInit(local manifest) {
321
373
  result
322
374
  }
323
375
  //
324
- // base64Format - create base 64 string from binary array
325
- //
326
- function base64Format(data, local blob, reader, pending, result) {
327
- blob = xnew(wsg.Blob, [data])
328
- reader = xnew(wsg.FileReader)
329
- pending = new(nonce)
330
- reader.onload = function(event) { pending.signal(event.target.result) }
331
- reader.readAsDataURL(blob)
332
- result = pending.wait()
333
- result.slice(37)
334
- }
335
- //
336
376
  // DecodeBase64url - decode base64url to original string
337
377
  // See https://en.wikipedia.org/wiki/Base64 for details on variants
338
378
  //
339
379
  DecodeBase64url = function decodeBase64url(base64url, local padding) {
340
380
  base64url = base64url.replace(RegExp("-", "g"), '+').replace(RegExp("_", "g"), '/')
341
- wsg.atob(base64url)
381
+ DecodeBase64(base64url)
342
382
  }
343
383
  //
344
384
  // For browser windows and web workers
345
385
  //
346
386
  if js.w || js.s {
387
+ //
388
+ // base64Format - create base 64 string from binary array
389
+ //
390
+ function browserBase64Format(data, local blob, reader, pending, result) {
391
+ blob = xnew(wsg.Blob, [data])
392
+ reader = xnew(wsg.FileReader)
393
+ pending = new(nonce)
394
+ reader.onload = function(event) { pending.signal(event.target.result) }
395
+ reader.readAsDataURL(blob)
396
+ result = pending.wait()
397
+ result.slice(37)
398
+ }
399
+ //
400
+ // binary to Base64
401
+ EncodeBase64 = function browserEncodeBase64(data) {
402
+ if xobject(data)
403
+ browserBase64Format(data)
404
+ else
405
+ wsg.btoa(data)
406
+ }
407
+ //
408
+ // Base64 to binary
409
+ DecodeBase64 = function browserDecodeBase64(data) {
410
+ wsg.atob(data)
411
+ }
347
412
  //
348
413
  // MD5 functions
349
414
  JsLoadScript("frameworks/browser/spark-md5/spark-md5.min.js", "SparkMD5")
@@ -358,7 +423,7 @@ function comrInit(local manifest) {
358
423
  //
359
424
  // HashMD5 in base64
360
425
  HashMD5_base64 = function browserMD5_base64(data) {
361
- wsg.btoa(browserMD5(data, true)) }
426
+ EncodeBase64(browserMD5(data, true)) }
362
427
  //
363
428
  // browserDigest - only in secure contexts (localhost and HTTPS)
364
429
  //
@@ -367,7 +432,7 @@ function comrInit(local manifest) {
367
432
  throw(Error("crypto functions unavailable"))
368
433
  data = binaryFrom(data)
369
434
  if string(data)
370
- data = xnew(TextEncoder).encode(data)
435
+ data = Uint8ArrayFromString(data)
371
436
  `(error, result) = await(wsg.crypto.subtle.digest(algo, data))
372
437
  if error
373
438
  throw(Error("crypto digest failed", error))
@@ -386,7 +451,7 @@ function comrInit(local manifest) {
386
451
  //
387
452
  // HashSHA-256 in base64
388
453
  HashSHA256_base64 = function browserSHA256_base64(data) {
389
- base64Format(browserDigest(data, "SHA-256"))
454
+ browserBase64Format(browserDigest(data, "SHA-256"))
390
455
  }
391
456
  //
392
457
  // random bytes as UInt8Array - use toint() for integer
@@ -399,6 +464,16 @@ function comrInit(local manifest) {
399
464
  // For NodeJS
400
465
  //
401
466
  if js.g {
467
+ //
468
+ // binary to Base64
469
+ EncodeBase64 = function nodejsEncodeBase64(data) {
470
+ wsg.Buffer.from(data, "binary").toString("base64")
471
+ }
472
+ //
473
+ // Base64 to binary
474
+ DecodeBase64 = function nodejsDecodeBase64(data) {
475
+ wsg.Buffer.from(data, "base64").toString("binary")
476
+ }
402
477
  if !crypto
403
478
  crypto = js.r("crypto")
404
479
  if crypto {
@@ -431,6 +506,8 @@ function comrInit(local manifest) {
431
506
  HashMD5 =
432
507
  HashMD5_base64 =
433
508
  HashSHA1 =
509
+ HashSHA256 =
510
+ HashSHA256_base64 =
434
511
  RandomBytes = function() { throw(Error("crypto functions unavailable")) }
435
512
  }
436
513
  }
@@ -442,10 +519,13 @@ function comrInit(local manifest) {
442
519
  modobj.exports.TextDecoder = TextDecoder
443
520
  modobj.exports.EncodeURIComponent = EncodeURIComponent
444
521
  modobj.exports.DecodeURIComponent = DecodeURIComponent
522
+ modobj.exports.EncodeBase64 = EncodeBase64
523
+ modobj.exports.DecodeBase64 = DecodeBase64
445
524
  modobj.exports.JSONstringify = JSONstringify
446
525
  modobj.exports.JSONparse = JSONparse
447
526
  modobj.exports.JsonStringify = JsonStringify
448
527
  modobj.exports.JsonParse = JsonParse
528
+ modobj.exports.Uint8ArrayFromString = Uint8ArrayFromString
449
529
  modobj.exports.DecodeBase64url = DecodeBase64url
450
530
  modobj.exports.HashMD5 = HashMD5
451
531
  modobj.exports.HashMD5_base64 = HashMD5_base64
@@ -125,6 +125,7 @@ function prefsInit(local manifest) {
125
125
  Naan.module.build(module.id, "preferences", function(modobj, compobj) {
126
126
  require("../common/common.nlg")
127
127
  compobj.manifest = manifest
128
+ updateExports()
128
129
  })
129
130
 
130
131
  module.exports.Preferences = Preferences
@@ -93,5 +93,6 @@ function wachInit(local manifest) {
93
93
  require("./common.nlg")
94
94
  compobj.manifest = manifest
95
95
  module.exports.Watchable = Watchable
96
+ updateExports()
96
97
  })
97
98
  } ();