@naanlang/naan 1.4.1 → 1.4.3

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.
@@ -36,7 +36,7 @@
36
36
  *
37
37
  */
38
38
 
39
- var CurrentCacheName = "Naanlang-1.4.1+1";
39
+ var CurrentCacheName = "Naanlang-1.4.3+1";
40
40
 
41
41
 
42
42
  //
@@ -92,18 +92,18 @@ var terminated; // flag that this service worker
92
92
  var pubVersion = event.data.hereIsMyVersion;
93
93
  var sourceId = event.source.id; // client id of sender of message
94
94
  msgPorts[sourceId] = msgport;
95
- console.log("[1.4.1+1] received new msgport for", sourceId, pubID, "-", pubVersion);
96
- if (pubVersion != "1.4.1+1") {
95
+ console.log("[1.4.3+1] received new msgport for", sourceId, pubID, "-", pubVersion);
96
+ if (pubVersion != "1.4.3+1") {
97
97
  if (upgradeMsgPorts) { // if not activated
98
- console.log("[1.4.1+1] update pending for", sourceId);
98
+ console.log("[1.4.3+1] update pending for", sourceId);
99
99
  upgradeMsgPorts[sourceId] = msgport;
100
100
  }
101
101
  else {
102
102
  msgport.postMessage({ // notify new version available
103
103
  id: "upgrade",
104
- version: "1.4.1+1"
104
+ version: "1.4.3+1"
105
105
  });
106
- console.log("[1.4.1+1] update sent for:", sourceId);
106
+ console.log("[1.4.3+1] update sent for:", sourceId);
107
107
  }
108
108
  }
109
109
  Reaper(); // clean up obsolete info
@@ -134,10 +134,10 @@ var terminated; // flag that this service worker
134
134
  msgport.onmessage = function(msg) {
135
135
  msg = msg.data;
136
136
  if (msg.id == "skipWaiting") { // try to activate this SW now
137
- console.log("[1.4.1+1] attempting skipWaiting");
137
+ console.log("[1.4.3+1] attempting skipWaiting");
138
138
  self.skipWaiting();
139
139
  } else if (msg.id == "terminate") { // termiante this SW now
140
- console.log("[1.4.1+1] terminated");
140
+ console.log("[1.4.3+1] terminated");
141
141
  terminate();
142
142
  } else if (msg.id == "abortURL") { // abort an I/O transaction
143
143
  var href = new URL(msg.url).href;
@@ -150,7 +150,7 @@ var terminated; // flag that this service worker
150
150
  } else if (msg.id == "response") // response from fetch request
151
151
  processResponse(msg);
152
152
  else if (msg.id == "text") // just log some text
153
- console.log("[1.4.1+1] msg received:", msg.text);
153
+ console.log("[1.4.3+1] msg received:", msg.text);
154
154
  };
155
155
 
156
156
  // send text to IDE log
@@ -161,7 +161,7 @@ var terminated; // flag that this service worker
161
161
  // don't clutter the log
162
162
  msgport.postMessage({
163
163
  id: "text",
164
- text: "port received by 1.4.1+1",
164
+ text: "port received by 1.4.3+1",
165
165
  });
166
166
  */
167
167
  if (--pending === 0)
@@ -178,7 +178,7 @@ var terminated; // flag that this service worker
178
178
  includeUncontrolled: true
179
179
  }).then(function(clientList) {
180
180
  clientList.every(function(client) {
181
- console.log("[1.4.1+1] requesting new msgport for", client.id);
181
+ console.log("[1.4.3+1] requesting new msgport for", client.id);
182
182
  ++pending;
183
183
  client.postMessage({ // tell client(s) we need this fetch source
184
184
  msg: "Naan_need_fetch_port",
@@ -224,12 +224,12 @@ function Reaper() {
224
224
  clients[clientList[clidex].id] = clientList[clidex];
225
225
  for (var sourceId in msgPorts)
226
226
  if (!clients[sourceId]) {
227
- console.log("[1.4.1+1] source gone:", sourceId);
227
+ console.log("[1.4.3+1] source gone:", sourceId);
228
228
  delete msgPorts[sourceId]; // no longer a source
229
229
  }
230
230
  for (var clientId in fetchPorts)
231
231
  if (!clients[clientId]) {
232
- console.log("[1.4.1+1] client gone:", clientId);
232
+ console.log("[1.4.3+1] client gone:", clientId);
233
233
  delete fetchPorts[clientId]; // no longer a client
234
234
  }
235
235
  for (var fqdex = 0; fqdex < fetchQueue.length; ++fqdex) {
@@ -263,16 +263,16 @@ function ClearCaches() {
263
263
  return (Promise.all(
264
264
  cacheNames.map(function(cacheName) {
265
265
  if (cacheName != CurrentCacheName) {
266
- console.log('[1.4.1+1] deleting old cache:', cacheName);
266
+ console.log('[1.4.3+1] deleting old cache:', cacheName);
267
267
  return (caches.delete(cacheName));
268
268
  }
269
269
  })
270
270
  ));
271
271
  }).then(function() { // claim all clients
272
- console.log('[1.4.1+1] claiming clients');
272
+ console.log('[1.4.3+1] claiming clients');
273
273
  return (self.clients.claim());
274
274
  }).then(function() {
275
- console.log('[1.4.1+1] clients claimed');
275
+ console.log('[1.4.3+1] clients claimed');
276
276
  return (Promise.resolve(true));
277
277
  });
278
278
  return (promise);
@@ -315,7 +315,7 @@ function GetClientResponse(event, urlpath) {
315
315
  msgport.postMessage({
316
316
  id: "fetch",
317
317
  seq: seqno,
318
- version: "1.4.1+1",
318
+ version: "1.4.3+1",
319
319
  request: {
320
320
  method: event.request.method,
321
321
  url: event.request.url
@@ -363,7 +363,7 @@ function GetClientResponse(event, urlpath) {
363
363
  */
364
364
 
365
365
  self.addEventListener('install', function(event) {
366
- console.log("[1.4.1+1] install");
366
+ console.log("[1.4.3+1] install");
367
367
  self.skipWaiting();
368
368
  });
369
369
 
@@ -376,20 +376,20 @@ self.addEventListener('install', function(event) {
376
376
  */
377
377
 
378
378
  self.addEventListener('activate', function(event) {
379
- console.log("[1.4.1+1] activate");
379
+ console.log("[1.4.3+1] activate");
380
380
  self.clients.matchAll({ // for debugging, list controlled clients
381
381
  includeUncontrolled: true
382
382
  }).then(function(clientList) {
383
383
  var urls = clientList.map(function(client) {
384
384
  return (client.url);
385
385
  });
386
- console.log('[1.4.1+1] matching clients:', urls.join(', '));
386
+ console.log('[1.4.3+1] matching clients:', urls.join(', '));
387
387
  });
388
388
  var promise = ClearCaches().then(function() {
389
389
  for (var sourceId in upgradeMsgPorts) {
390
390
  upgradeMsgPorts[sourceId].postMessage({ // notify new version available
391
391
  id: "upgrade",
392
- version: "1.4.1+1"
392
+ version: "1.4.3+1"
393
393
  });
394
394
  }
395
395
  upgradeMsgPorts = false;
@@ -421,7 +421,7 @@ self.addEventListener('fetch', function(event) {
421
421
  var promise;
422
422
  var url = new URL(request.url);
423
423
  var nocache = request.method != "GET"
424
- || url.search.length !== 0 && url.searchParams.get("naanver") !== "348dfbc247edf982d7b9f50b51d55de4"
424
+ || url.search.length !== 0 && url.searchParams.get("naanver") !== "3b6364f71856e32f6cb8edae0e67bbc2"
425
425
  || request.headers.get('range');
426
426
  if (url.pathname.startsWith("/run/")) {
427
427
  nocache = true;
@@ -455,7 +455,7 @@ self.addEventListener('fetch', function(event) {
455
455
  statusText: "Request Cancelled"
456
456
  }));
457
457
  }
458
- console.log("[1.4.1+1] fetch failed", request.url, e);
458
+ console.log("[1.4.3+1] fetch failed", request.url, e);
459
459
  return (new Response(undefined, {
460
460
  status: 404,
461
461
  statusText: "Fetch Failed"
@@ -152,9 +152,9 @@ closure TermHost(track, api, name, options, local target, nlregex, xtarg)
152
152
 
153
153
  // onerror
154
154
  // An error occurred on the WebSocket connection.
155
+ // This can happen in normal operation when the connection times out.
155
156
  target.ws.onerror = function onerror(e) {
156
157
  pending.signal(list(Error("websocket failed", e)))
157
- debuglog("ws error")
158
158
  }
159
159
 
160
160
  // onclose
@@ -655,6 +655,7 @@ closure TermLocal(track, name, options, local target, nlregex, listener)
655
655
  // Destroy the execution instance.
656
656
 
657
657
  target.destroy = function destroy() {
658
+ gWorkers[target.guid] = undefined
658
659
  target.execClosed(Error("instance destroyed"))
659
660
  track.delete(target)
660
661
  termDetach()
@@ -865,6 +866,10 @@ closure termIDE(track, name, workerID, naancont, title, local target, connected)
865
866
  if inbound
866
867
  inbound.active = active
867
868
  }
869
+
870
+ connection.WriteLn = function WriteLn(text) {
871
+ term.WriteLn(text)
872
+ }
868
873
 
869
874
  connection.termDetach = function termDetach() { // disconnect the terminal
870
875
  db.detach(target)
@@ -921,7 +926,46 @@ closure termIDE(track, name, workerID, naancont, title, local target, connected)
921
926
  }
922
927
  target.execReset() // reset our parent class
923
928
  }
924
-
929
+
930
+ // targetClose
931
+ //
932
+ // Mark the target window as closed.
933
+ //
934
+ function targetClose(local conn) {
935
+ for conn in connected {
936
+ conn.db.detach(target)
937
+ conn.WriteLn("\x1b[90m\x1b[3m".concat("\ntarget closed", "\x1b[0m"))
938
+ }
939
+ target.execReset()
940
+ target.naancont = naancont = false
941
+ }
942
+
943
+ // setCheckStatus
944
+ //
945
+ // Begin or end checking status on the worker to see if stopped responding. The challenge
946
+ // here is that we open vsite windows in the main thread, but there is no reliable event to learn
947
+ // when they are closed. The solution is to start checking if the window is closed after it gets
948
+ // a visibilityChange event to hidden. This terminates when the window gets marked closed or when
949
+ // it becomes visible again--which is quite unlikely unless someone uses the explicit hide/show
950
+ // window methods.
951
+ //
952
+ target.setCheckStatus = closure setCheckStatus(hidden, window) {
953
+ if hidden {
954
+ if !target.statusPoll
955
+ target.statusPoll = future(function() {
956
+ while target.statusPoll {
957
+ if window.closed {
958
+ targetClose()
959
+ target.statusPoll = false
960
+ break
961
+ } else
962
+ sleep(100) // check 10x/second
963
+ }
964
+ }, 10) // start after 10 msec
965
+ } else
966
+ target.statusPoll = false
967
+ }
968
+
925
969
  // vsiteRefresh
926
970
  //
927
971
  // The vsite upon which we are based has changed.
@@ -969,6 +1013,11 @@ closure termInstallVirtualWatcher(track, naancont) {
969
1013
  if target
970
1014
  target.updateController()
971
1015
  }
1016
+ else if msg.op == "VsiteVisibilityChange" {
1017
+ target = findIDEtarget(track, msg.name, "NaanVsite", msg.naancont)
1018
+ if target
1019
+ target.setCheckStatus(msg.hidden, msg.window)
1020
+ }
972
1021
  }
973
1022
 
974
1023
  require("../running/executors.nlg").TaskOnMessageHook(watchVsites)
@@ -182,53 +182,6 @@ function JsonStringify(data) {
182
182
  }
183
183
  };
184
184
 
185
-
186
- /*
187
- * JsLoadScript
188
- *
189
- * Load the specified script, or return its main entry point if already loaded. This blocks the
190
- * caller until the script load completes. The return value is false if an error occurred.
191
- *
192
- */
193
-
194
- jsScripts = { }; // script values become false on reload
195
-
196
- closure JsLoadScript(libpath, globalID, local savens, hostpath, pending, scriptTag) {
197
- global(jsScripts, JSpath, js)
198
- if jsScripts[libpath]
199
- return // already loaded
200
- savens = nsactive()
201
- hostpath = js.w.location.origin.concat(JSpath.join(JSpath.dirname(js.w.location.pathname), libpath))
202
- if js.s.importScripts && tostring(js.s.importScripts) == "[Function importScripts]" {
203
- try {
204
- js.s.importScripts(hostpath) // load in worker context
205
- jsScripts[libpath] = js.s[globalID]
206
- } catch {
207
- debuglog("JsLoadScript:", libpath, "failed", exception)
208
- js.s.console.log("JsLoadScript:", libpath, "failed", exception) // ensure visibility in JS console
209
- }
210
- } else if js.w { // load in browser context
211
- pending = new(nonce)
212
- scriptTag = js.w.document.createElement("script")
213
- scriptTag.src = hostpath.concat(requireQuery())
214
- scriptTag.onload = function (event) {
215
- jsScripts[libpath] = js.w[globalID]
216
- pending.signal(js.w[globalID])
217
- true
218
- }
219
- scriptTag.onerror = function (event) {
220
- debuglog("JsLoadScript:", libpath, "not found")
221
- pending.signal(false)
222
- true
223
- }
224
- js.w.document.body.appendChild(scriptTag)
225
- pending.wait()
226
- } else if js.g
227
- jsScripts[libpath] = js.r(libpath) // load in NodeJS context
228
- nsactive(savens)
229
- jsScripts[libpath]
230
- };
231
-
232
185
 
233
186
  /*
234
187
  * LoadComponentOnDemand
@@ -365,7 +318,7 @@ Uint8ArrayFromString = false;
365
318
 
366
319
  function comrInit(local manifest) {
367
320
  manifest = `(EncodeQuery, DecodeQuery, ContentTypeFromFileExt, UUID, VersionCheck, JsonParse,
368
- JsonStringify, JsLoadScript, LoadComponentOnDemand, LiveExport, comrInit)
321
+ JsonStringify, LoadComponentOnDemand, LiveExport, comrInit)
369
322
 
370
323
  Naan.module.build(module.id, "common", closure(modobj, compobj) {
371
324
  compobj.manifest = manifest
@@ -373,8 +326,9 @@ function comrInit(local manifest) {
373
326
  modobj.exports.DecodeQuery = DecodeQuery
374
327
  modobj.exports.ContentTypeFromFileExt = ContentTypeFromFileExt
375
328
  modobj.exports.UUID = UUID
376
- modobj.exports.JsLoadScript = JsLoadScript
377
329
  modobj.exports.LoadComponentOnDemand = LoadComponentOnDemand
330
+ letimport(module.owner.list.Util.exports.JsLoadScript) // use the Util version
331
+ modobj.exports.JsLoadScript = JsLoadScript
378
332
  modobj.exports.LiveExport = LiveExport // allow others to create export updaters
379
333
  updateExports = LiveExport() // export LiveImport() so we can update
380
334
 
@@ -481,13 +435,12 @@ function comrInit(local manifest) {
481
435
  }
482
436
  //
483
437
  // MD5 functions
484
- if symbol(NideBuild) { // execute if we are not building
485
- JsLoadScript("frameworks/browser/spark-md5/spark-md5.min.js", "SparkMD5")
486
- }
487
438
  //
488
439
  // hasMD5 in hex
489
440
  HashMD5 = function browserMD5(data, raw) {
490
441
  data = binaryFrom(data)
442
+ if !wsg.SparkMD5
443
+ JsLoadScript("frameworks/browser/spark-md5/spark-md5.min.js", "SparkMD5")
491
444
  if xobject(data)
492
445
  wsg.SparkMD5.ArrayBuffer.hash(data, raw)
493
446
  else
@@ -230,17 +230,26 @@ closure Builder(rules, operation, fs, srcpath, destpath, track, local build, gro
230
230
  // <substitute text, one or more lines>
231
231
  // [File EOF]
232
232
  //
233
+ // Special line starts:
234
+ // # comment
235
+ // \# line starts with #
236
+ // \\ line starts with \
237
+ //
233
238
  // Note that the newlines before and after the substitute text are not included. This returns a
234
239
  // a dictionary of variables and replacement text. The defs argument is optional, and defines a
235
240
  // substitution dictionary applied to the replacement text defined in this file.
236
241
  //
237
- function parseSubstitutionLines(lines, defs, local ignoregex, varegex, line, find, subs, curvar, sub) {
242
+ function parseSubstitutionLines(lines, defs,
243
+ local ignoregex, escapex, varegex, line, find, subs, curvar, sub) {
238
244
  subs = { }
239
245
  ignoregex = RegExp("^#")
246
+ escapex = RegExp("^\\\\(#|\\\\)")
240
247
  varegex = RegExp("^[$]([^$]+)[$]")
241
248
  for line in lines {
242
- if ignoregex.exec(line)
243
- continue
249
+ if ignoregex.test(line)
250
+ continue // comment
251
+ if escapex.test(line)
252
+ line = line.slice(1) // not-a-comment escape
244
253
  if find = line.match(varegex) {
245
254
  curvar = find[1]
246
255
  subs[curvar] = ""
@@ -271,6 +280,11 @@ closure Builder(rules, operation, fs, srcpath, destpath, track, local build, gro
271
280
  // <substitute text, one or more lines>
272
281
  // [File EOF]
273
282
  //
283
+ // Special line starts:
284
+ // # comment
285
+ // \# line starts with #
286
+ // \\ line starts with \
287
+ //
274
288
  // Note that the newlines before and after the substitute text are not included. This returns a
275
289
  // standard `(error, subs) result tuple where subs is a dictionary of variables and replacement
276
290
  // text. The defs argument is optional, and defines a substitution dictionary applied to the
@@ -293,7 +307,7 @@ closure Builder(rules, operation, fs, srcpath, destpath, track, local build, gro
293
307
  //
294
308
  function applySubstitutions(text, subs, local subbed) {
295
309
  subbed = { }
296
- list(text.replace(RegExp("[$][-A-Za-z0-9_./()\\\\]+[$]", "g"), function(match, local subst) {
310
+ list(text.replace(RegExp("[$][^$\\s]+[$]", "g"), function(match, local subst) {
297
311
  subst = subs[match.slice(1,-1)]
298
312
  if subst {
299
313
  subbed[match] = true
@@ -296,7 +296,7 @@ closure projConnector(projman, projtype, local connector, watch) {
296
296
  *
297
297
  * Nide.proj/
298
298
  * nide_cliser.cfg - JSON definition of the project (see below)
299
- * NaanIDE_version.txt - [default] substitution file defining 1.4.1+1 etc.
299
+ * NaanIDE_version.txt - [default] substitution file defining 1.4.3+1 etc.
300
300
  * NaanIDE_version_builds.txt - [default] contains current build number as decimal string
301
301
  * build/ - [optional] default build output location
302
302
  * NaanIDE.lic - [optional] defines Zulch Laboratories, Inc. and other license info
@@ -563,6 +563,17 @@ closure projConfig(projman, where, projID, local procon, fs, configpath) {
563
563
  }
564
564
  }
565
565
 
566
+ // persist
567
+ //
568
+ // Return a dictionary for persistent local project state, like view configuration.
569
+ //
570
+ procon.persist = function persist() {
571
+ if procon.localDict.persist {
572
+ }
573
+ else
574
+ procon.localDict.persist = {}
575
+ }
576
+
566
577
  // buildrules
567
578
  //
568
579
  // Get the build rules for the named stage, reloading the project configuration if changed.
@@ -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,11 +239,17 @@ 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
- eval(list(deref, obj, pop(args), args))
232
- else
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
233
253
  throw("remoting error: object ${objectID} not found")
234
254
  }
235
255
 
@@ -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
@@ -529,7 +529,7 @@ closure DbConnector(psm, local connClassID, connector, watch) {
529
529
 
530
530
  // localDBcred
531
531
  //
532
- // True iff the specified credential dictionary is for a local browser DB.
532
+ // True iff the specified credential dictionary is for a local DB.
533
533
  function localDBcred(creds) {
534
534
  !creds.authName && creds.urlName.indexOf("://") < 0
535
535
  }
@@ -722,8 +722,13 @@ closure DbConnector(psm, local connClassID, connector, watch) {
722
722
  if creds {
723
723
  if creds.label
724
724
  info.name = creds.label
725
- if localDBcred(creds)
726
- 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"
727
732
  if creds.locked
728
733
  info.locked = true
729
734
  if creds.hidden
@@ -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