@naanlang/naan 1.4.0 → 1.4.2

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 (42) hide show
  1. package/LICENSE.md +1 -1
  2. package/README.md +10 -10
  3. package/bin/index.js +25 -15
  4. package/dist/env_web.js +68 -73
  5. package/dist/naan.min.js +5 -5
  6. package/frameworks/browser/sworker.js +23 -23
  7. package/frameworks/browser/terminals.nlg +52 -3
  8. package/frameworks/client/psm_client.nlg +2 -0
  9. package/frameworks/client/relaycon.nlg +1 -1
  10. package/frameworks/common/common.nlg +10 -54
  11. package/frameworks/node/filesystem.nlg +5 -4
  12. package/frameworks/node/psm_server.nlg +1 -1
  13. package/frameworks/node/shell.nlg +134 -0
  14. package/frameworks/node/worker.nlg +1 -1
  15. package/frameworks/project/build.nlg +19 -5
  16. package/frameworks/project/projects.nlg +12 -1
  17. package/frameworks/running/debugnub.nlg +102 -12
  18. package/frameworks/running/debugutil.nlg +30 -13
  19. package/frameworks/running/executors.nlg +7 -3
  20. package/frameworks/running/running.nlg +26 -6
  21. package/frameworks/running/sourcecode.nlg +3 -1
  22. package/frameworks/running/taskexec.nlg +1 -1
  23. package/frameworks/storage/dbt_pouch.nlg +37 -19
  24. package/frameworks/storage/file_manager.nlg +1 -1
  25. package/frameworks/storage/psm_dbtables.nlg +23 -24
  26. package/frameworks/storage/resources.nlg +12 -4
  27. package/frameworks/storage/storage.nlg +2 -2
  28. package/lib/browser/env_web.js +73 -78
  29. package/lib/core/naanlib.js +5 -5
  30. package/package.json +1 -1
  31. package/plugins/gitClient/gitClient.nlg +3 -3
  32. package/plugins/openSearch/openSearch.nlg +2 -2
  33. package/plugins/serviceAws/aws_s3.nlg +17 -1
  34. package/plugins/serviceAws/psm_aws.nlg +4 -2
  35. package/plugins/serviceAws/serviceAws.nlg +1 -1
  36. package/plugins/serviceGC/gc_api.nlg +4 -4
  37. package/plugins/serviceGitHub/serviceGitHub.nlg +1 -1
  38. package/plugins/serviceGitLab/psm_gitlab.nlg +1 -1
  39. package/plugins/serviceGitLab/serviceGitLab.nlg +1 -1
  40. package/test/harness.nlg +9 -9
  41. package/test/test_01_core.nlg +24 -2
  42. package/test/test_02_context.nlg +1 -1
@@ -33,6 +33,9 @@ closure DebugNub(local nub, debug, paused) {
33
33
  nub.codemgr = CodeManager()
34
34
  nub.exenable = true
35
35
  nub.watches = []
36
+ nub.contIDs = new(weakmap)
37
+ nub.stacks = { }
38
+ nub.usage = { }
36
39
 
37
40
  // debugDepth
38
41
  // Return the recursive debugging depth as an integer.
@@ -80,7 +83,7 @@ closure DebugNub(local nub, debug, paused) {
80
83
  for frame in nub.frames
81
84
  if procedure(frame)
82
85
  break
83
- `(procdef, task) = nub.procTask(frame)
86
+ `(procdef, task) = debug.frameProcTask(frame)
84
87
  }
85
88
  if !procdef
86
89
  return (false)
@@ -396,14 +399,75 @@ closure DebugNub(local nub, debug, paused) {
396
399
  output.push(breakInfo(bpid))
397
400
  output
398
401
  }
402
+
403
+ // procLoc
404
+ //
405
+ // Get the location of a procdef.
406
+
407
+ nub.procLoc = function procLoc(procdef, task, local comp, position) {
408
+ if !task
409
+ task = rrrest(procdef)
410
+ comp = nub.codemgr.procComponent(procdef)
411
+ position = comp.textpos(procdef, task)
412
+ {
413
+ modname: comp.modname
414
+ compname: comp.compname
415
+ first: position.first
416
+ last: position.last
417
+ line: position.line
418
+ }
419
+ }
420
+
421
+ // getContID
422
+ //
423
+ // Get the contID for a continuation.
424
+
425
+ nub.getContID = function getContID(cont, local contID, frames) {
426
+ contID = nub.contIDs[cont]
427
+ if !contID
428
+ nub.contIDs[cont] = contID = UUID()
429
+ frames = cont.stacktrace().toarray
430
+ if frames.slice(-1).0 == `interrupt
431
+ frames.pop()
432
+ nub.stacks[contID] = frames
433
+ contID
434
+ }
435
+
436
+ // contGC
437
+ //
438
+ // Remove any continuations that are not in the specified array.
439
+
440
+ nub.contGC = function contGC(contIDs, local contID) {
441
+ contIDs = contIDs.concat(nub.usage.*.toarray) // keep preserved ids
442
+ for contID in nub.stacks
443
+ if contIDs.indexOf(contID) < 0
444
+ nub.stacks[contID] = undefined
445
+ }
446
+
447
+ // usingContID
448
+ //
449
+ // Mark that the contID is being used.
450
+ //
451
+ nub.usingContID = function usingContID(contID, usage) {
452
+ if nub.usage[contID]
453
+ nub.usage[contID] += usage
454
+ else if usage > 0
455
+ nub.usage[contID] = usage
456
+ if nub.usage[contID] == 0
457
+ nub.usage[contID] = undefined
458
+ }
399
459
 
400
460
  // callStack
401
461
  //
402
- // List our callers as of the last entry.
462
+ // List our callers as of the last entry in the optional frames or current debugger stack.
403
463
 
404
- nub.callStack = function callStack(local output, frame, type, record) {
464
+ nub.callStack = function callStack(contID, local frames, output, frame, type, record) {
405
465
  output = []
406
- for frame in nub.frames {
466
+ if contID
467
+ frames = nub.stacks[contID]
468
+ else
469
+ frames = nub.frames
470
+ for frame in frames {
407
471
  record = {
408
472
  type: frame.0.tostring // e.g. "function"
409
473
  }
@@ -425,8 +489,12 @@ closure DebugNub(local nub, debug, paused) {
425
489
  //
426
490
  // Return the nth stack frame in the list returned by callStack above.
427
491
 
428
- nub.nthFrame = function nthFrame(nth, local frame) {
429
- for frame in nub.frames
492
+ nub.nthFrame = function nthFrame(nth, contID, local frames, frame) {
493
+ if contID
494
+ frames = nub.stacks[contID]
495
+ else
496
+ frames = nub.frames
497
+ for frame in frames
430
498
  if procedure(frame) || frame.0.tostring == "interrupt" {
431
499
  if nth-- == 0
432
500
  return (frame)
@@ -469,14 +537,15 @@ closure DebugNub(local nub, debug, paused) {
469
537
 
470
538
  // evalWatchesInFrame
471
539
  //
472
- // Parse and evaluate our watchpoints, optionally in the context of the nth frame. Each entry
473
- // in the returned array is for a watchpoint, comprising a tuple: `(error, expression, evaluated)
540
+ // Parse and evaluate our watchpoints, optionally in the context of the nth frame of the
541
+ // specified continuation or the current debugger stack. Each entry in the returned array is for
542
+ // a watchpoint, comprising a tuple: `(error, expression, evaluated).
474
543
 
475
- nub.evalWatchesInFrame = function evalWatchesInFrame(nth,
544
+ nub.evalWatchesInFrame = function evalWatchesInFrame(nth, contID,
476
545
  local frame, locals, ns, mod, gohome, texts, watch, exprs, error, expr)
477
546
  {
478
547
  if integer(nth) {
479
- frame = nthFrame(nth)
548
+ frame = nthFrame(nth, contID)
480
549
  locals = localist(frame)
481
550
  ns = frame.3.1.namespace
482
551
  if ns !== nsactive().0 {
@@ -506,8 +575,29 @@ closure DebugNub(local nub, debug, paused) {
506
575
  //
507
576
  // Return procedure and task from a frame, as a `(procdef, task) tuple.
508
577
 
509
- nub.procTask = function procTask(frame) {
510
- debug.frameProcTask(frame)
578
+ nub.procTask = function procTask(frame, contID, local frames, ftrav, procdef, bodydef) {
579
+ if contID
580
+ frames = new(nub.stacks[contID])
581
+ else
582
+ frames = new(debug.frames)
583
+ loop {
584
+ if frames.length == 0
585
+ break
586
+ ftrav = frames.shift()
587
+ if ftrav eq frame
588
+ frame = false // can now stop on our desired procedure
589
+ if procedure(ftrav) {
590
+ if !frame {
591
+ procdef = ftrav.3 // first procedure definition starting with desired frame
592
+ if !bodydef
593
+ bodydef = procdef // use procdef as body if no body frame found
594
+ break }
595
+ bodydef = false } // any previously-found body applies to wrong procedure
596
+ else if ftrav.0 == `body || ftrav.0 == `loop {
597
+ if !bodydef
598
+ bodydef = ftrav.2 } } // use first body found before desired procedure
599
+ if procdef
600
+ list(procdef, bodydef) // found valid location
511
601
  }
512
602
 
513
603
  // lastStepValue
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2021 by Richard C. Zulch
9
+ * Copyright (c) 2021-2024 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -260,24 +260,33 @@ closure DebugRemote(commander, local remoter, here, there, library) {
260
260
  here.breakInfo = function breakInfo(bpid) {
261
261
  libBreakInfo(bpid)
262
262
  }
263
+
264
+ // usingContID
265
+ //
266
+ // Mark that the contID is being used.
267
+ //
268
+ here.usingContID = function usingContID(contID, usage) {
269
+ debugNub = car(compress("debugNub"))
270
+ debugNub.usingContID(contID, usage)
271
+ }
263
272
 
264
273
  // callstack
265
274
  //
266
- // Report current callstack.
275
+ // Report the specified fiber callstack or current debugger stack.
267
276
  //
268
- here.callStack = function callStack(local debugNub) {
277
+ here.callStack = function callStack(contID, local debugNub) {
269
278
  debugNub = car(compress("debugNub"))
270
- debugNub.callStack()
279
+ debugNub.callStack(contID)
271
280
  }
272
281
 
273
282
  // frameinfo
274
283
  //
275
- // Report information about the nth frame in the callStack.
284
+ // Report information about the nth frame in a fiber callstack or the current debugger stack.
276
285
  //
277
- here.frameinfo = function frameinfo(nth, local debugNub, frame, proctask, comp, position) {
286
+ here.frameinfo = function frameinfo(nth, contID, local debugNub, frame, proctask, comp, position) {
278
287
  debugNub = car(compress("debugNub"))
279
- frame = debugNub.nthFrame(nth)
280
- proctask = debugNub.procTask(frame)
288
+ frame = debugNub.nthFrame(nth, contID)
289
+ proctask = debugNub.procTask(frame, contID)
281
290
  comp = debugNub.codemgr.procComponent(proctask.0)
282
291
  position = comp.textpos(proctask.0, proctask.1)
283
292
  {
@@ -302,11 +311,11 @@ closure DebugRemote(commander, local remoter, here, there, library) {
302
311
  // The optional visible array is a preceding result of this function, where "visible=true"
303
312
  // can be applied to nested values to add recursive depth.
304
313
  //
305
- here.scopeinfo = function scopeinfo(nth, visible,
314
+ here.scopeinfo = function scopeinfo(nth, visible, contID
306
315
  local debugNub, frame, procsym, parent, proc, lex1, ex1, params, content, scope, level, level1)
307
316
  {
308
317
  debugNub = car(compress("debugNub"))
309
- frame = debugNub.nthFrame(nth)
318
+ frame = debugNub.nthFrame(nth, contID)
310
319
  procsym = frame.3.1
311
320
  parent = procsym@\.parent
312
321
  while parent {
@@ -370,11 +379,11 @@ closure DebugRemote(commander, local remoter, here, there, library) {
370
379
  // can be applied to nested values to add recursive depth. A stack frame index can be specified
371
380
  // to evaluate an expression in that context.
372
381
 
373
- here.watchInfo = function watchInfo(visible, nth,
382
+ here.watchInfo = function watchInfo(visible, nth, contID,
374
383
  local debugNub, values, content, watch, child1, error, value, level, expr)
375
384
  {
376
385
  debugNub = car(compress("debugNub"))
377
- values = debugNub.evalWatchesInFrame(nth)
386
+ values = debugNub.evalWatchesInFrame(nth, contID)
378
387
  visible = new(visible) // don't modify original
379
388
  content = []
380
389
  for watch in debugNub.watches {
@@ -417,8 +426,9 @@ closure DebugRemote(commander, local remoter, here, there, library) {
417
426
  //
418
427
  // List the current futures.
419
428
 
420
- here.futureslist = function futureslist(local debugNub, output, future, record, procsym) {
429
+ here.futureslist = closure futureslist(local debugNub, contIDs, output, future, record, procsym) {
421
430
  debugNub = car(compress("debugNub"))
431
+ contIDs = []
422
432
  output = []
423
433
  for future in futures() {
424
434
  if member(debugNub.pauser, future.waiting)
@@ -431,6 +441,7 @@ closure DebugRemote(commander, local remoter, here, there, library) {
431
441
  if procedure(future.execute) {
432
442
  procsym = future.execute.1
433
443
  record.execute = strcat(future.execute.0, space, procsym.namespace.tostring, "::", debugNub.procname(procsym))
444
+ record.execloc = debugNub.procLoc(future.execute)
434
445
  }
435
446
  else if future.execute
436
447
  record.execute = tostring(future.execute)
@@ -439,9 +450,15 @@ closure DebugRemote(commander, local remoter, here, there, library) {
439
450
  procsym = item.1
440
451
  strcat(item.0, space, procsym.namespace.tostring, "::", debugNub.procname(procsym))
441
452
  })
453
+ record.continuations = future.continuations.toarray.map(function(cont, local contID) {
454
+ contID = debugNub.getContID(cont)
455
+ contIDs.push(contID)
456
+ contID
457
+ })
442
458
  }
443
459
  output.push(record)
444
460
  }
461
+ debugNub.contGC(contIDs)
445
462
  output
446
463
  }
447
464
 
@@ -71,10 +71,14 @@ closure ExecutorBase(track, type, name, local exec) {
71
71
 
72
72
  // execSetStatus
73
73
  //
74
- // Received a status update from the instance.
74
+ // Received a status update from the instance. This makes a copy of the specified dictionary for
75
+ // garbage collection, because if we store the status from a separate vsite window then it can
76
+ // cause the main window to retain a vsite window after it's closed. The last status update sets
77
+ // an object in the context of the detached window, and then it never gets updated until the
78
+ // executor goes away. That doesn't always happen as soon as you think.
75
79
  //
76
80
  exec.execSetStatus = function execSetStatus(status) {
77
- exec.xstatus = status
81
+ exec.xstatus = new(status) // for GC (!)
78
82
  }
79
83
 
80
84
  // execFailed
@@ -218,7 +222,7 @@ closure ExecutorContext(exec, state, local context, coder, util) {
218
222
  else if result.1.result
219
223
  list(false, caar(util.pkgLoad(result.1.result, {
220
224
  localroots: false
221
- intern: false
225
+ intern: "symbols"
222
226
  execute: false
223
227
  objectDecoder: coder.decodeObject
224
228
  })))
@@ -207,12 +207,26 @@ closure packageCoder(evalf, local coder) {
207
207
  quote(x@\.tostring = function() { "{proxy ${nn}}" }),
208
208
  quote(x@\.undefined = closure undef args {
209
209
  let (result) {
210
- result = evalf(list(`\.execute\-method, objectID, args))
210
+ result = evalf(list(`\.invoke\-method, objectID, args))
211
211
  if result.0
212
212
  throw("remoting failure: ${ErrorString(result.0)}")
213
213
  else
214
214
  result.1
215
215
  } ()
216
+ }),
217
+ quote(x@\.\.undefined = function (key, local result) {
218
+ result = evalf(list(`\.invoke\-method, objectID, list(`\.\.undefined, key)))
219
+ if result.0
220
+ throw("remoting failure: ${ErrorString(result.0)}")
221
+ else
222
+ result.1
223
+ }),
224
+ quote(x@\.\=undefined = function (key, value, local result) {
225
+ result = evalf(list(`\.invoke\-method, objectID, list(`\.\=undefined, key, value)))
226
+ if result.0
227
+ throw("remoting failure: ${ErrorString(result.0)}")
228
+ else
229
+ result.1
216
230
  }), `x)))
217
231
  } (ident.name, ident.objectID)
218
232
  coder.wmapEncoding[obj] = ident
@@ -225,12 +239,18 @@ closure packageCoder(evalf, local coder) {
225
239
  //
226
240
  // Invoke a local object method from the remote proxy, returning a result tuple.
227
241
  //
228
- coder.invokeMethod = closure invokeMethod(objectID, args, local obj) {
242
+ coder.invokeMethod = closure invokeMethod(objectID, args, local obj, method) {
229
243
  obj = coder.mapDecoding[objectID]
230
- if obj
231
- list(false, eval(list(deref, obj, pop(args), args)))
232
- else
233
- list(Error("object ${objectID} not found"))
244
+ method = pop(args)
245
+ if obj {
246
+ if method == `\.\.undefined
247
+ obj[args.0]
248
+ else if method == `\.\=undefined
249
+ obj[args.0] = args.1
250
+ else
251
+ eval(list(deref, obj, method, args))
252
+ } else
253
+ throw("remoting error: object ${objectID} not found")
234
254
  }
235
255
 
236
256
  // finis
@@ -87,12 +87,14 @@ closure CodeManager(options, local coman, cache)
87
87
  output.push(false)
88
88
  continue
89
89
  }
90
+ if text.startsWith("!")
91
+ text = "(${text})" // no shell escapes here
90
92
  try {
91
93
  source = new(textstream, text.concat(" ;")) // avoid EOF
92
94
  if locals
93
95
  source.setsymbols(locals)
94
96
  registry = modlist().Lib.exports.Registry // dialect registry
95
- dialect = registry.findName(registry.dialects().0) // ### default dialecct
97
+ dialect = registry.findName(registry.dialects().0) // ### default dialect
96
98
  output.push(dialect.parse(source))
97
99
  } catch {
98
100
  if true {
@@ -88,7 +88,7 @@ closure TaskDispatcher(replier, local contexts, gohome, coder, util) {
88
88
  objectDecoder: coder.decodeObject
89
89
  })
90
90
  expr = caar(expr)
91
- if expr.0 == `\.execute\-method
91
+ if expr.0 == `\.invoke\-method
92
92
  expr = coder.invokeMethod(expr.1, expr.2) // object method, args
93
93
  else
94
94
  expr = evalactive(expr) // evaluate expression
@@ -34,13 +34,17 @@
34
34
  *
35
35
  */
36
36
 
37
- closure dapoTable(database, path, local table, prefix) {
37
+ closure dapoTable(database, rootpath, local table, prefix) {
38
38
  global()
39
+ if !database || !string(rootpath)
40
+ return (list(Error("invalid arguments")))
39
41
  table = new(object, this)
40
42
  table.type = database.type
41
43
  table.db = database
42
- table.path = path
43
- table.prefix = makePrefix(path)
44
+ if rootpath != "" && rootpath.slice(-1) != "/"
45
+ rootpath = rootpath.concat("/") // "" or "<path>/"
46
+ table.rootpath = rootpath
47
+ table.prefix = makePrefix(rootpath)
44
48
 
45
49
  // makePrefix
46
50
  //
@@ -80,7 +84,7 @@ closure dapoTable(database, path, local table, prefix) {
80
84
  return (syncAdapter(add, key, content, metadata))
81
85
  id = makeID(key)
82
86
  if !string(id)
83
- return (asyncResult(callback, id)) // error from makeID
87
+ return (asyncResult(callback, id)) // error from makeID
84
88
  if metadata && !dictionary(metadata)
85
89
  return (asyncResult(callback, Error("invalid arguments")))
86
90
  if !database.pouch
@@ -291,7 +295,7 @@ closure dapoTable(database, path, local table, prefix) {
291
295
  // only immediate children are listed, with directories at the end--and no metadata for them.
292
296
  //
293
297
 
294
- table.records = closure records(options, callback, local prefix, pouchOpts, output) {
298
+ table.records = closure records(options, callback, local prefix, prelen, pouchOpts, output) {
295
299
  if !callback
296
300
  return (syncAdapter(records, options))
297
301
  output = {
@@ -300,9 +304,11 @@ closure dapoTable(database, path, local table, prefix) {
300
304
  if !string(options.prefix)
301
305
  return (asyncResult(callback, Error("prefix option must be string")))
302
306
  output.prefix = options.prefix
303
- prefix = table.prefix.concat(makePrefix(options.prefix)) }
304
- else
307
+ prefix = table.prefix.concat(makePrefix(options.prefix))
308
+ prelen = options.prefix.length }
309
+ else {
305
310
  prefix = table.prefix
311
+ prelen = 0 }
306
312
  if !database.pouch
307
313
  return (asyncResult(callback, Error("database closed")))
308
314
  pouchOpts = {
@@ -321,19 +327,24 @@ closure dapoTable(database, path, local table, prefix) {
321
327
  key = obj.id.substring(table.prefix.length)
322
328
  else
323
329
  key = "*key error*" // should not happen
324
- if !options.deep {
325
- offset = key.indexOf("/", prefix.length)
326
- if offset >= 0 {
327
- key = key.substring(0, offset+1) // child directory name
328
- if !firstkids[key]
329
- firstkids[key] = key // first time we've seen this child directory
330
- else
331
- continue } } // ignore deeper child
332
330
  item = {
333
331
  key: key
334
332
  _id: obj.id
335
333
  _rev: obj.value.rev
336
334
  }
335
+ if !options.deep {
336
+ offset = key.indexOf("/", prelen) // start looking past options.prefix
337
+ if offset >= 0 { // directory child
338
+ if length(key) > offset+1 { // transitive child (not directory itself)
339
+ obj.doc = undefined // just record directory name
340
+ item._id = undefined
341
+ item._rev = undefined }
342
+ key = key.substring(0, offset+1) // child directory name
343
+ if !firstkids[key]
344
+ firstkids[key] = key // first time we've seen this child directory
345
+ else
346
+ continue // ignore deeper child
347
+ item.key = key } }
337
348
  if obj.doc { // docs were requested
338
349
  if obj.doc.content {
339
350
  item._md5 = HashMD5(obj.doc.content)
@@ -518,7 +529,7 @@ closure DbConnector(psm, local connClassID, connector, watch) {
518
529
 
519
530
  // localDBcred
520
531
  //
521
- // True iff the specified credential dictionary is for a local browser DB.
532
+ // True iff the specified credential dictionary is for a local DB.
522
533
  function localDBcred(creds) {
523
534
  !creds.authName && creds.urlName.indexOf("://") < 0
524
535
  }
@@ -711,8 +722,13 @@ closure DbConnector(psm, local connClassID, connector, watch) {
711
722
  if creds {
712
723
  if creds.label
713
724
  info.name = creds.label
714
- if localDBcred(creds)
715
- info.type = "browser storage"
725
+ if localDBcred(creds) {
726
+ if js.w
727
+ info.type = "PouchDB-browser"
728
+ else
729
+ info.type = "PouchDB-filesystem"
730
+ } else
731
+ info.type = "PouchDB-network"
716
732
  if creds.locked
717
733
  info.locked = true
718
734
  if creds.hidden
@@ -741,6 +757,8 @@ closure DbConnector(psm, local connClassID, connector, watch) {
741
757
  //
742
758
  // Connect to a database, returning a PSM view.
743
759
  connector.connect = function connect(resID, service, args, local error, creds, db, rootpath, table) {
760
+ if !resID || args && !tuple(args)
761
+ return (list(Error("invalid arguments")))
744
762
  `(error, creds) = connector.access(resID)
745
763
  if !error
746
764
  `(error, db) = PouchDB(creds)
@@ -751,7 +769,7 @@ closure DbConnector(psm, local connClassID, connector, watch) {
751
769
  else if service == "NideFS" {
752
770
  rootpath = args.0 // connect to a NideFS filesystem
753
771
  if !rootpath
754
- return (Error("mandatory rootpath missing"))
772
+ return (list(Error("mandatory rootpath missing")))
755
773
  `(error, table) = db.table(rootpath)
756
774
  if error
757
775
  list(error)
@@ -766,7 +766,7 @@ function fimaInit(local manifest) {
766
766
 
767
767
  manifest = `(FileManager, fimaStore, fimaInit)
768
768
 
769
- Naan.module.build(module.id, "fileManager", function(modobj, compobj) {
769
+ Naan.module.build(module.id, "file_manager", function(modobj, compobj) {
770
770
  require("./storage.nlg")
771
771
  commonWatching = require("../common/watching.nlg")
772
772
  compobj.manifest = manifest
@@ -38,10 +38,12 @@
38
38
 
39
39
  closure FSview(table, rootpath, local view) {
40
40
  global(JSpath)
41
- if !table
41
+ if !table || !string(rootpath)
42
42
  return (list(Error("invalid arguments")))
43
43
  view = new(object, this)
44
44
  view.table = table
45
+ if rootpath != "" && rootpath.slice(-1) != "/"
46
+ rootpath = rootpath.concat("/") // "" or "<path>/"
45
47
  view.rootpath = rootpath
46
48
  view.path = JSpath.posix // for view.path.join(), etc.
47
49
 
@@ -118,23 +120,21 @@ closure FSview(table, rootpath, local view) {
118
120
  // checkPathName
119
121
  //
120
122
  // Return a `(path, name) tuple for a directory, or false if callback executed due to error. The
121
- // name is the empty string if the path is empty. Path "/" has name "/". Otherwise the name is
122
- // the last segment of the path, omitting the trailing slash.
123
+ // name is the empty string if the path is empty. Path "/" has name "". Otherwise the name is the
124
+ // last segment of the path, omitting any trailing slash. A key invariant is that the information
125
+ // should be the same whether the pathname was part of the rootpath or specified in the source
126
+ // path to an operation. That is why this "steals" the rootpath name if needed.
123
127
  //
124
- function checkPathName(path, callback, local name, slashdex) {
125
- if path == ""
126
- name = ""
127
- else {
128
- if checkPath(path, callback)
129
- return (false) // non-string
130
- if path.slice(-1) != "/"
131
- path = path.concat("/") // force to be directory
132
- slashdex = path.lastIndexOf("/", path.length-2)
133
- if path == "/" || slashdex < 0
134
- name = path
135
- else
136
- name = path.slice(slashdex+1, -1) // omit trailing "/"
137
- }
128
+ function checkPathName(path, callback, local name) {
129
+ if checkPath(path, callback)
130
+ return (false) // non-string
131
+ if path != "" && path.slice(-1) != "/"
132
+ path = path.concat("/") // force to be directory
133
+ if path == "/" && rootpath != ""
134
+ name = rootpath // avoid making "//"
135
+ else
136
+ name = rootpath.concat(path)
137
+ name = name.split("/").slice(0,-1).pop() || "" // <|| ""> not needed
138
138
  list(path, name)
139
139
  }
140
140
 
@@ -683,7 +683,7 @@ closure FSview(table, rootpath, local view) {
683
683
  return // already executed callback with error
684
684
  dirdict = {
685
685
  name: name
686
- path: path
686
+ path: rootpath.concat(path)
687
687
  data: {
688
688
  platform: table.type
689
689
  semantics: "dbfs"
@@ -715,8 +715,8 @@ closure FSview(table, rootpath, local view) {
715
715
  finfo.type = "directory"
716
716
  } else if !finfo.type
717
717
  finfo.type = "file" // everything is a file in UNIX
718
- finfo.md5 = item.doc._md5 || undefined
719
- finfo.length = item.doc._length || item.doc.length || undefined
718
+ finfo.md5 = item._md5 || item.doc._md5 || undefined
719
+ finfo.length = item._length || item.doc._length || item.doc.length || undefined
720
720
  if item.key.length > offset // not the directory itself
721
721
  dirdict.children.push({
722
722
  name: name
@@ -743,14 +743,13 @@ closure FSview(table, rootpath, local view) {
743
743
  `(path, name) = checkPathName(path, callback)
744
744
  if !path
745
745
  return // already executed callback with error
746
- options = { // ### might want to allow some options
746
+ options = merge({
747
747
  prefix: path
748
748
  deep: true
749
- }
750
- if name == "" && rootpath != ""
751
- name = rootpath.split("/").slice(0,-1).pop() || ""
749
+ }, options)
752
750
  treedict = {
753
751
  name: name
752
+ path: rootpath.concat(path)
754
753
  data: {
755
754
  platform: table.type
756
755
  semantics: "dbfs"
@@ -142,9 +142,14 @@ closure ResourceManager(dbname, prefix, local reman, watch, result) {
142
142
  reman.unWatch = watch.unWatch
143
143
  reman.dbname = dbname
144
144
  reman.conns = {}
145
- result = PouchDB(dbname)
145
+ try {
146
+ require("./dbt_pouch.nlg")
147
+ result = PouchDB(dbname)
148
+ } catch {
149
+ result = list(Error("PouchDB not available", exception))
150
+ }
146
151
  if result.0
147
- return (result) // can't access our vault database
152
+ return (list(result)) // can't access our vault database
148
153
  reman.db = result.1
149
154
 
150
155
  // vault
@@ -423,19 +428,22 @@ closure ResourceTracker(reman, local retor, watch) {
423
428
  // Start tracking resources.
424
429
  retor.open = function open() {
425
430
  if retor.classIdList
426
- return // already open
431
+ return (list(false, { open: true })) // already open
427
432
  watch.reset()
428
433
  retor.resids = { } // start empty
429
434
  retor.classIdList = []
430
435
  for classID in reman.connectors()
431
436
  addConnResources(classID)
432
437
  reman.watch(remanUpdateOnChange)
438
+ list(false, { ok: true })
433
439
  }
434
440
 
435
441
  // close
436
442
  //
437
443
  // stop tracking resources.
438
444
  retor.close = function close(local classID, connector) {
445
+ if !retor.classIdList
446
+ return (list(false, { open: false }))
439
447
  watch.reset()
440
448
  if retor.classIdList {
441
449
  for classID in retor.classIdList {
@@ -446,6 +454,7 @@ closure ResourceTracker(reman, local retor, watch) {
446
454
  }
447
455
  reman.unWatch(remanUpdateOnChange)
448
456
  retor.resids = false
457
+ list(false, { ok: true })
449
458
  }
450
459
 
451
460
  // finis
@@ -466,7 +475,6 @@ function rsrcInit(local manifest) {
466
475
 
467
476
  Naan.module.build(module.id, "resources", function(modobj, compobj) {
468
477
  require("./storage.nlg")
469
- require("./dbt_pouch.nlg")
470
478
  commonWatching = require("../common/watching.nlg")
471
479
  compobj.manifest = manifest
472
480
  module.exports.ResourceManager = ResourceManager