@naanlang/naan 1.0.16 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +2 -2
- package/README.md +13 -11
- package/bin/index.js +4 -4
- package/dist/naan.min.js +14 -14
- package/frameworks/browser/https_request.nlg +26 -10
- package/frameworks/browser/sworker.js +26 -25
- package/frameworks/browser/terminals.nlg +118 -55
- package/frameworks/browser/ws_client.nlg +124 -0
- package/frameworks/client/apiclient.nlg +82 -116
- package/frameworks/client/psm_client.nlg +37 -16
- package/frameworks/client/relaycon.nlg +308 -0
- package/frameworks/common/common.nlg +1 -1
- package/frameworks/common/utils.nlg +40 -2
- package/frameworks/node/apiserver.nlg +342 -103
- package/frameworks/node/https_request.nlg +32 -6
- package/frameworks/node/node.nlg +60 -2
- package/frameworks/node/psm_server.nlg +11 -7
- package/frameworks/node/worker.nlg +22 -9
- package/frameworks/node/ws_client.nlg +127 -0
- package/frameworks/project/build.nlg +3 -2
- package/frameworks/project/projects.nlg +8 -2
- package/frameworks/running/debugnub.nlg +2 -2
- package/frameworks/running/debugutil.nlg +1 -1
- package/frameworks/running/executors.nlg +69 -17
- package/frameworks/running/sourcecode.nlg +2 -2
- package/frameworks/running/taskexec.nlg +25 -25
- package/frameworks/storage/dbt_pouch.nlg +8 -6
- package/frameworks/storage/file_manager.nlg +6 -3
- package/frameworks/storage/psm.nlg +5 -3
- package/frameworks/storage/psm_dbtables.nlg +75 -53
- package/frameworks/storage/resources.nlg +4 -2
- package/lib/browser/env_web.js +6 -6
- package/lib/browser/env_webworker.js +1 -1
- package/lib/core/naanlib.js +14 -14
- package/lib/env_node.js +97 -8
- package/lib/env_nodeworker.js +22 -4
- package/package.json +1 -1
- package/plugins/serviceAws/aws/lambda_rest_index.js +5 -1
- package/plugins/serviceAws/aws_dynamo.nlg +91 -32
- package/plugins/serviceAws/dbt_aws.nlg +5 -5
- package/plugins/serviceAws/psm_aws.nlg +2 -2
- package/test/harness.nlg +1 -1
- package/test/test_01_core.nlg +30 -4
- package/test/test_02_context.nlg +2 -2
- package/test/test_07_lingo.nlg +3 -0
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* column positioning: // // !
|
|
7
7
|
*
|
|
8
|
-
* Copyright (c) 2021 by Richard C. Zulch
|
|
8
|
+
* Copyright (c) 2021-2024 by Richard C. Zulch
|
|
9
9
|
*
|
|
10
10
|
*/
|
|
11
11
|
|
|
@@ -59,7 +59,7 @@ closure CodeManager(options, local coman, cache)
|
|
|
59
59
|
coman.procComponent = function procComponent(procdef, local sym, mod, modname, compname, component) {
|
|
60
60
|
procdef = baseproc(rootproc(procdef)) // only work with the bases, not instances
|
|
61
61
|
sym = procdef.1
|
|
62
|
-
mod = module.owner.
|
|
62
|
+
mod = module.owner.find(function(mod){ mod.namespace === sym.namespace })
|
|
63
63
|
for `(compname, component) in mod.components
|
|
64
64
|
if member(sym, component.manifest)
|
|
65
65
|
return (CodeComposite(coman, component.manifest, mod.id, compname))
|
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
*
|
|
6
6
|
* column positioning: // // !
|
|
7
7
|
*
|
|
8
|
-
* Copyright (c) 2021 by Richard C. Zulch
|
|
8
|
+
* Copyright (c) 2021-2024 by Richard C. Zulch
|
|
9
9
|
*
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
/*
|
|
14
|
-
*
|
|
14
|
+
* TaskDispatcher
|
|
15
15
|
*
|
|
16
16
|
* Create a dispatcher for remote evaluation. The replier argument is a function that accepts
|
|
17
17
|
* outgoing messages. The return value is a closure that processes incomming messages. This code is
|
|
@@ -19,11 +19,12 @@
|
|
|
19
19
|
*
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
closure
|
|
22
|
+
closure TaskDispatcher(replier, local contexts, gohome, util) {
|
|
23
|
+
global()
|
|
23
24
|
contexts = { }
|
|
24
25
|
gohome = makeActivator()
|
|
25
26
|
util = modlist().Util.exports
|
|
26
|
-
closure(data, local reply, restorens, expr, roots, proc, args, result) {
|
|
27
|
+
closure(data, local reply, restorens, context, expr, roots, proc, args, result) {
|
|
27
28
|
reply = {
|
|
28
29
|
xmsg: "error"
|
|
29
30
|
xid: data.xid
|
|
@@ -68,24 +69,22 @@ closure taskDispatcher(replier, local contexts, gohome, util) {
|
|
|
68
69
|
})
|
|
69
70
|
expr = caar(expr)
|
|
70
71
|
reply.error = undefined
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
context.activator = makeActivator()
|
|
88
|
-
}
|
|
72
|
+
expr = evalactive(expr)
|
|
73
|
+
if data.get
|
|
74
|
+
roots = new(data.get).map(function(sym){ // new() to get symbol from compress
|
|
75
|
+
compress(sym)
|
|
76
|
+
})
|
|
77
|
+
if roots
|
|
78
|
+
roots = cons(`expr, totuple(roots))
|
|
79
|
+
else
|
|
80
|
+
roots = `(expr)
|
|
81
|
+
reply.result = util.pkgSave(`expr, roots, {
|
|
82
|
+
nsignore: true // don't report namespace errors
|
|
83
|
+
comments: true }) // include comments
|
|
84
|
+
if context.namespace !== nsactive().0
|
|
85
|
+
{ // evaluation changed namespace
|
|
86
|
+
context.namespace = nsactive().0
|
|
87
|
+
context.activator = makeActivator()
|
|
89
88
|
}
|
|
90
89
|
}
|
|
91
90
|
}
|
|
@@ -148,12 +147,12 @@ closure taskDispatcher(replier, local contexts, gohome, util) {
|
|
|
148
147
|
closure TaskExecutor() {
|
|
149
148
|
|
|
150
149
|
// taskInstall
|
|
151
|
-
// Install the
|
|
150
|
+
// Install the TaskDispatcher, and reload on state load.
|
|
152
151
|
//
|
|
153
152
|
function taskInstall() {
|
|
154
153
|
if !js.t.OnMessage
|
|
155
154
|
return (false) // false if not a worker
|
|
156
|
-
js.t.OnMessage(
|
|
155
|
+
js.t.OnMessage(TaskDispatcher(js.t.ReplyMessage))
|
|
157
156
|
}()
|
|
158
157
|
LoadNotify(taskInstall)
|
|
159
158
|
|
|
@@ -185,11 +184,12 @@ function TaskOnMessageHook(hook) {
|
|
|
185
184
|
|
|
186
185
|
function taxInit(local manifest) {
|
|
187
186
|
|
|
188
|
-
manifest = `(
|
|
187
|
+
manifest = `(TaskDispatcher, TaskExecutor, TaskOnMessageHook, taxInit)
|
|
189
188
|
|
|
190
189
|
Naan.module.build(module.id, "taskexec", function(modobj, compobj) {
|
|
191
190
|
require("./running.nlg").LiveImport()
|
|
192
191
|
compobj.manifest = manifest
|
|
192
|
+
modobj.exports.TaskDispatcher = TaskDispatcher
|
|
193
193
|
modobj.exports.TaskExecutor = TaskExecutor
|
|
194
194
|
modobj.exports.TaskOnMessageHook = TaskOnMessageHook
|
|
195
195
|
})
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* column positioning: // // !
|
|
9
9
|
*
|
|
10
|
-
* Copyright (c) 2017-
|
|
10
|
+
* Copyright (c) 2017-2024 by Richard C. Zulch
|
|
11
11
|
*
|
|
12
12
|
*/
|
|
13
13
|
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
*/
|
|
36
36
|
|
|
37
37
|
closure dapoTable(database, path, local table, prefix) {
|
|
38
|
+
global()
|
|
38
39
|
table = new(object, this)
|
|
39
40
|
table.type = database.type
|
|
40
41
|
table.db = database
|
|
@@ -277,13 +278,11 @@ closure dapoTable(database, path, local table, prefix) {
|
|
|
277
278
|
// rows : [
|
|
278
279
|
// {
|
|
279
280
|
// key : "john",
|
|
280
|
-
// _modified : [Date Sat Apr 04 2020 17:05:38 GMT-0700 (Pacific Daylight Time)]
|
|
281
281
|
// _md5 : "55ff07fb005494c4bd42322d4845e858"
|
|
282
282
|
// _length : 156043
|
|
283
283
|
// [ doc | error: <document from "get" or "head"> | <error from get|head> ] },
|
|
284
284
|
// {
|
|
285
285
|
// key : "jill",
|
|
286
|
-
// _modified : [Date Sat Apr 22 2020 17:05:38 GMT-0700 (Pacific Daylight Time)]
|
|
287
286
|
// _md5 : "f53f07fb00d42322dc4bd42322d4845e"
|
|
288
287
|
// _length : 155
|
|
289
288
|
// [ doc : <document from "get" or "head"> ] } ] }
|
|
@@ -335,7 +334,7 @@ closure dapoTable(database, path, local table, prefix) {
|
|
|
335
334
|
_id: obj.id
|
|
336
335
|
_rev: obj.value.rev
|
|
337
336
|
}
|
|
338
|
-
if obj.doc {
|
|
337
|
+
if obj.doc { // docs were requested
|
|
339
338
|
if obj.doc.content {
|
|
340
339
|
item._md5 = HashMD5(obj.doc.content)
|
|
341
340
|
item._length = length(obj.doc.content) }
|
|
@@ -373,6 +372,7 @@ closure dapoTable(database, path, local table, prefix) {
|
|
|
373
372
|
*/
|
|
374
373
|
|
|
375
374
|
closure PouchDB(urlname, local database, options) {
|
|
375
|
+
global(jspath, pouchdb)
|
|
376
376
|
options = {
|
|
377
377
|
revs_limit: 1 // number of revs to maintain for replication
|
|
378
378
|
auto_compaction: false } // don't compcat on every change
|
|
@@ -480,7 +480,8 @@ closure PouchDB(urlname, local database, options) {
|
|
|
480
480
|
*
|
|
481
481
|
*/
|
|
482
482
|
|
|
483
|
-
closure ListBrowserDBs(local pending, output) {
|
|
483
|
+
closure ListBrowserDBs(local pending, output, item) {
|
|
484
|
+
global(indexedDB)
|
|
484
485
|
if !indexedDB
|
|
485
486
|
return (list(Error("browser database unavailable")))
|
|
486
487
|
if !indexedDB.databases
|
|
@@ -507,6 +508,7 @@ closure ListBrowserDBs(local pending, output) {
|
|
|
507
508
|
*/
|
|
508
509
|
|
|
509
510
|
closure DbConnector(psm, local connClassID, connector, watch) {
|
|
511
|
+
global()
|
|
510
512
|
connClassID = "PouchDB"
|
|
511
513
|
connector = new(object, this)
|
|
512
514
|
connector.psm = psm
|
|
@@ -572,7 +574,7 @@ closure DbConnector(psm, local connClassID, connector, watch) {
|
|
|
572
574
|
// The return value is an (error, resID) tuple. Error, if non-false, is a dictionary of field
|
|
573
575
|
// keys and error diagnostic strings.
|
|
574
576
|
connector.writeResource = function writeResource(resource, resID,
|
|
575
|
-
local errors, creds, original, error, data) {
|
|
577
|
+
local errors, creds, original, error, data, change) {
|
|
576
578
|
// badField - test for a bad string
|
|
577
579
|
function badField(str) { !string(str) || str.trim().length == 0 }
|
|
578
580
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* column positioning: // // !
|
|
8
8
|
*
|
|
9
|
-
* Copyright (c) 2020-
|
|
9
|
+
* Copyright (c) 2020-2024 by Richard C. Zulch
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
24
|
closure FileManager(fs, options, local files, watch, opqueue, willgit, hadgit) {
|
|
25
|
+
global(commonWatching)
|
|
25
26
|
files = new(object, this)
|
|
26
27
|
files.fs = fs
|
|
27
28
|
files.nodes = [] // array of nodes, one per file or folder
|
|
@@ -159,7 +160,7 @@ closure FileManager(fs, options, local files, watch, opqueue, willgit, hadgit) {
|
|
|
159
160
|
//
|
|
160
161
|
// Return the parent path of the specified node, or "" for the root.
|
|
161
162
|
//
|
|
162
|
-
function parentPath(node) {
|
|
163
|
+
function parentPath(node, local path) {
|
|
163
164
|
path = node.path
|
|
164
165
|
if path.slice(-1) == files.pathsep
|
|
165
166
|
path = path.slice(0,-1)
|
|
@@ -415,7 +416,8 @@ closure FileManager(fs, options, local files, watch, opqueue, willgit, hadgit) {
|
|
|
415
416
|
return // no git checking available
|
|
416
417
|
opBeginOrReschedule(closure(callback) {
|
|
417
418
|
willgit = true
|
|
418
|
-
files.fs.gitStatus(false, function (error, statlist,
|
|
419
|
+
files.fs.gitStatus(false, function (error, statlist,
|
|
420
|
+
local changed, delgit, unknownFiles, unknownFileStatus, changes, classID, item) {
|
|
419
421
|
if error {
|
|
420
422
|
files.gitvalid = false
|
|
421
423
|
return (callback(error))
|
|
@@ -649,6 +651,7 @@ closure FileManager(fs, options, local files, watch, opqueue, willgit, hadgit) {
|
|
|
649
651
|
*/
|
|
650
652
|
|
|
651
653
|
closure fimaStore(fm, node, local store) {
|
|
654
|
+
global()
|
|
652
655
|
store = new(object, this)
|
|
653
656
|
store.fm = fm
|
|
654
657
|
store.fs = fm.fs
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* column positioning: // // !
|
|
8
8
|
*
|
|
9
|
-
* Copyright (c) 2019-
|
|
9
|
+
* Copyright (c) 2019-2024 by Richard C. Zulch
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
closure MakePSMutil(fs, local util) {
|
|
23
|
+
global()
|
|
23
24
|
util = new(object, this)
|
|
24
25
|
util.fs = fs
|
|
25
26
|
|
|
@@ -56,7 +57,8 @@ closure MakePSMutil(fs, local util) {
|
|
|
56
57
|
//
|
|
57
58
|
// Update a directory within an existing tree data structure. The result is a standard tuple with
|
|
58
59
|
// (error, changes) where the changes are added/deleted/changed. This assumes that children changed
|
|
59
|
-
// if their info is different
|
|
60
|
+
// if their info is different. If a child directory is found that does not yet have children, then
|
|
61
|
+
// the
|
|
60
62
|
|
|
61
63
|
util.treeDirUpdate = closure treeDirUpdate(treepath, dirpath, tree, callback, local error, inpath, treedir, files) {
|
|
62
64
|
if !callback
|
|
@@ -309,7 +311,7 @@ closure MakePSMutil(fs, local util) {
|
|
|
309
311
|
//
|
|
310
312
|
if options.erase {
|
|
311
313
|
asyncArray(keys(folders).toarray, 10, closure eraser(destpath
|
|
312
|
-
local error, outfolder, folder, namesin, removes, delopt) {
|
|
314
|
+
local error, outfolder, folder, namesin, node, removes, delopt) {
|
|
313
315
|
`(error, outfolder) = outfs.dirList(destpath) // existing files in our destination
|
|
314
316
|
if error
|
|
315
317
|
return
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* column positioning: // // !
|
|
8
8
|
*
|
|
9
|
-
* Copyright (c) 2020-
|
|
9
|
+
* Copyright (c) 2020-2024 by Richard C. Zulch
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
* - directory names are non-empty strings with the sole "/" at the end
|
|
24
24
|
* - the names ".", "..", and anything containing ":" are forbidden
|
|
25
25
|
* - every directory has (or should have) an associated database record for itself
|
|
26
|
-
* -
|
|
26
|
+
* - when these records are missing, trivial parents are created during tree enumeration
|
|
27
27
|
* - pathnames are relative to the root of the view an d consist of a string with:
|
|
28
28
|
* - zero or more directory names joined by "/", then
|
|
29
29
|
* - either "", "/" for a directory, or "/" with a filename
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
*/
|
|
38
38
|
|
|
39
39
|
closure FSview(table, rootpath, local view) {
|
|
40
|
+
global(JSpath)
|
|
40
41
|
if !table
|
|
41
42
|
return (list(Error("invalid arguments")))
|
|
42
43
|
view = new(object, this)
|
|
@@ -44,6 +45,14 @@ closure FSview(table, rootpath, local view) {
|
|
|
44
45
|
view.rootpath = rootpath
|
|
45
46
|
view.path = JSpath.posix // for view.path.join(), etc.
|
|
46
47
|
|
|
48
|
+
// dirPath
|
|
49
|
+
//
|
|
50
|
+
// True iff the path is a dirpath. If the path is empty this checks the rootpath.
|
|
51
|
+
|
|
52
|
+
function dirPath(path) {
|
|
53
|
+
path.slice(-1) == "/" || path == "" && rootpath.slice(-1) == "/"
|
|
54
|
+
}
|
|
55
|
+
|
|
47
56
|
// dirName
|
|
48
57
|
//
|
|
49
58
|
// Return the directory portion of the specified path.
|
|
@@ -83,7 +92,7 @@ closure FSview(table, rootpath, local view) {
|
|
|
83
92
|
if isPath {
|
|
84
93
|
if name == ""
|
|
85
94
|
return (true)
|
|
86
|
-
else if name.substring(0, 1) == "/" ||
|
|
95
|
+
else if name.substring(0, 1) == "/" || name.indexOf("//") >= 0
|
|
87
96
|
return (false)
|
|
88
97
|
else
|
|
89
98
|
name = baseName(name) }
|
|
@@ -98,9 +107,7 @@ closure FSview(table, rootpath, local view) {
|
|
|
98
107
|
if !string(path) {
|
|
99
108
|
if !msg
|
|
100
109
|
msg = "invalid path"
|
|
101
|
-
|
|
102
|
-
path = "<".concat(typeof(path), ">") // e.g. "<xobject>"
|
|
103
|
-
asyncResult(callback, Error(msg, typeof(content), {
|
|
110
|
+
asyncResult(callback, Error(msg, "invalid path ${typeof(path)}", {
|
|
104
111
|
status: 400
|
|
105
112
|
name: "invalid_arguments"
|
|
106
113
|
path: path
|
|
@@ -110,8 +117,10 @@ closure FSview(table, rootpath, local view) {
|
|
|
110
117
|
|
|
111
118
|
// checkPathName
|
|
112
119
|
//
|
|
113
|
-
// Return a `(path, name) tuple for a directory, or false if callback executed due to error.
|
|
114
|
-
|
|
120
|
+
// 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
|
+
//
|
|
115
124
|
function checkPathName(path, callback, local name, slashdex) {
|
|
116
125
|
if path == ""
|
|
117
126
|
name = ""
|
|
@@ -162,7 +171,7 @@ closure FSview(table, rootpath, local view) {
|
|
|
162
171
|
else
|
|
163
172
|
proc = view.table.get
|
|
164
173
|
`(error, data) = proc(path)
|
|
165
|
-
if error && error.status == 404 && path
|
|
174
|
+
if error && error.status == 404 && !dirPath(path)
|
|
166
175
|
`(error, data) = proc(path.concat("/"))
|
|
167
176
|
callback(error, data)
|
|
168
177
|
}
|
|
@@ -199,7 +208,7 @@ closure FSview(table, rootpath, local view) {
|
|
|
199
208
|
birthtime: nowstr
|
|
200
209
|
}
|
|
201
210
|
}
|
|
202
|
-
if path
|
|
211
|
+
if dirPath(path)
|
|
203
212
|
metadata.finfo.type = "directory"
|
|
204
213
|
else
|
|
205
214
|
metadata.finfo.type = "file"
|
|
@@ -232,7 +241,7 @@ closure FSview(table, rootpath, local view) {
|
|
|
232
241
|
else
|
|
233
242
|
metadata.finfo.encoding = "utf8" } // default string encoding
|
|
234
243
|
else if content && !xobject(content)
|
|
235
|
-
return (callback(Error("unsupported content type
|
|
244
|
+
return (callback(Error("unsupported content type ${typeof(content)}", {
|
|
236
245
|
status: 400
|
|
237
246
|
name: "invalid_arguments"
|
|
238
247
|
path: path
|
|
@@ -267,7 +276,7 @@ closure FSview(table, rootpath, local view) {
|
|
|
267
276
|
return (asyncResult(callback, false, { created: false })) // root always already exists
|
|
268
277
|
if checkPath(path, callback)
|
|
269
278
|
return
|
|
270
|
-
if path
|
|
279
|
+
if !dirPath(path)
|
|
271
280
|
path = path.concat("/")
|
|
272
281
|
view.table.get(path, function(error, data, local pathdirs, buildpath, item) {
|
|
273
282
|
if !error
|
|
@@ -285,7 +294,7 @@ closure FSview(table, rootpath, local view) {
|
|
|
285
294
|
name: "conflict"
|
|
286
295
|
path: buildpath
|
|
287
296
|
})))
|
|
288
|
-
if buildpath
|
|
297
|
+
if !dirPath(buildpath)
|
|
289
298
|
buildpath = buildpath.concat("/")
|
|
290
299
|
`(error, data) = view.table.head(buildpath) // get desired directory
|
|
291
300
|
if error.status == 404
|
|
@@ -310,7 +319,7 @@ closure FSview(table, rootpath, local view) {
|
|
|
310
319
|
if dest.indexOf("/") < 0
|
|
311
320
|
newpath = dirName(srcpath).concat(dest) // renaming within same directory
|
|
312
321
|
else {
|
|
313
|
-
if dest
|
|
322
|
+
if dirPath(dest) // moving to new directory location:
|
|
314
323
|
newpath = dest.concat(baseName(srcpath)) // new path is new pathname
|
|
315
324
|
else { // moving and renaming:
|
|
316
325
|
newpath = dest // new path is new pathname
|
|
@@ -352,10 +361,17 @@ closure FSview(table, rootpath, local view) {
|
|
|
352
361
|
|
|
353
362
|
// copyDirectory
|
|
354
363
|
//
|
|
355
|
-
// Copy a directory by its source path, with optional move.
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
364
|
+
// Copy a directory by its source path, with optional move. The
|
|
365
|
+
// destination is:
|
|
366
|
+
// - a new name in same parent if it has no embedded slashes, or
|
|
367
|
+
// - the same name in a new location if it ends in a slash, otherwise
|
|
368
|
+
// - a new pathname for the source.
|
|
369
|
+
//
|
|
370
|
+
closure copyDirectory(srcpath, dest, options,
|
|
371
|
+
local error, nodes, data, destpath, errors, docpath, newpath, srcroot, item) {
|
|
372
|
+
if !dirPath(srcpath)
|
|
373
|
+
srcpath = srcpath.concat("/") // normalize source as directory
|
|
374
|
+
`(error, nodes) = view.table.records({ prefix: srcpath }) // each item has key/_id/_rev, not doc
|
|
359
375
|
if error
|
|
360
376
|
return (list(Error("copyDirectory: can't enumerate source directory", {
|
|
361
377
|
source: srcpath
|
|
@@ -372,13 +388,13 @@ closure FSview(table, rootpath, local view) {
|
|
|
372
388
|
destpath = dirName(srcpath).concat(dest) // new directory name in same parent directory
|
|
373
389
|
dest = dirName(srcpath) } // dest is new parent pathname
|
|
374
390
|
else {
|
|
375
|
-
if dest
|
|
391
|
+
if dirPath(dest)
|
|
376
392
|
destpath = dest.concat(baseName(srcpath)) // same name; new location
|
|
377
393
|
else {
|
|
378
394
|
destpath = dest // new full pathname
|
|
379
395
|
dest = dirName(dest) } // dest is new parent pathname
|
|
380
396
|
}
|
|
381
|
-
if destpath != "" && destpath
|
|
397
|
+
if destpath != "" && !dirPath(destpath)
|
|
382
398
|
destpath = destpath.concat("/") // get actual path of new directory
|
|
383
399
|
//
|
|
384
400
|
// Change srcpath to destpath for all records. Note that dest is the name of our new parent
|
|
@@ -434,7 +450,7 @@ closure FSview(table, rootpath, local view) {
|
|
|
434
450
|
if !srcroot
|
|
435
451
|
srcroot = item // delete source root later
|
|
436
452
|
else {
|
|
437
|
-
`(error, data) = view.table.delete(item
|
|
453
|
+
`(error, data) = view.table.delete(item) // delete original
|
|
438
454
|
if error
|
|
439
455
|
errors.push(Error("copyDirectory: can't remove original", {
|
|
440
456
|
source: docpath
|
|
@@ -448,7 +464,7 @@ closure FSview(table, rootpath, local view) {
|
|
|
448
464
|
}, errors))
|
|
449
465
|
else {
|
|
450
466
|
if srcroot {
|
|
451
|
-
`(error, data) = view.table.delete(srcroot
|
|
467
|
+
`(error, data) = view.table.delete(srcroot) // delete original root
|
|
452
468
|
if error
|
|
453
469
|
return (list(Error("copyDirectory: can't remove original root", {
|
|
454
470
|
source: srcpath
|
|
@@ -481,13 +497,13 @@ closure FSview(table, rootpath, local view) {
|
|
|
481
497
|
finfo.length = data.length
|
|
482
498
|
else if data._length
|
|
483
499
|
finfo.length = data._length
|
|
484
|
-
if
|
|
500
|
+
if dirPath(path)
|
|
501
|
+
finfo.type = "directory"
|
|
502
|
+
else if finfo.length
|
|
485
503
|
finfo.type = "file" }
|
|
486
504
|
else if path == "" {
|
|
487
505
|
error = false // we were asked for info on superoot
|
|
488
506
|
finfo = {
|
|
489
|
-
md5: HashMD5("")
|
|
490
|
-
length: 0
|
|
491
507
|
type: "directory"
|
|
492
508
|
} }
|
|
493
509
|
callback(error, finfo)
|
|
@@ -545,7 +561,7 @@ closure FSview(table, rootpath, local view) {
|
|
|
545
561
|
return
|
|
546
562
|
if !content
|
|
547
563
|
return (asyncResult(callback, Error("content required")))
|
|
548
|
-
if path
|
|
564
|
+
if dirPath(path)
|
|
549
565
|
return (asyncResult(callback, Error("cannot write to a directory", { path: path })))
|
|
550
566
|
if !validName(path, true)
|
|
551
567
|
return (asyncResult(callback, Error("invalid filename", { path: path })))
|
|
@@ -573,12 +589,11 @@ closure FSview(table, rootpath, local view) {
|
|
|
573
589
|
// Copy / move / rename a file or directory. The source is the full path of the item. The
|
|
574
590
|
// destination is:
|
|
575
591
|
// - a new name if it has no embedded slashes, or
|
|
576
|
-
// - a new location if it ends in a slash, otherwise
|
|
577
|
-
// - a new pathname for the source
|
|
592
|
+
// - the same name in a new location if it ends in a slash, otherwise
|
|
593
|
+
// - a new pathname for the source.
|
|
578
594
|
// This works by creating a new file or directory of files in the new location and then
|
|
579
|
-
//
|
|
580
|
-
//
|
|
581
|
-
|
|
595
|
+
// deleting the files in the existing location after the copy if options.move is true.
|
|
596
|
+
//
|
|
582
597
|
view.copy = closure copy(srcpath, dest, options, callback) {
|
|
583
598
|
if !callback
|
|
584
599
|
return (syncAdapter(copy, srcpath, dest, options))
|
|
@@ -594,7 +609,7 @@ closure FSview(table, rootpath, local view) {
|
|
|
594
609
|
source: srcpath
|
|
595
610
|
destination: dest
|
|
596
611
|
}, error)))
|
|
597
|
-
else if
|
|
612
|
+
else if data.key.slice(-1) == "/"
|
|
598
613
|
result = copyDirectory(srcpath, dest, options) // hard case: directory with possibly many children
|
|
599
614
|
else
|
|
600
615
|
result = copyFile(data, dest, options) // easy case: single file
|
|
@@ -624,7 +639,7 @@ closure FSview(table, rootpath, local view) {
|
|
|
624
639
|
name: "forbidden"
|
|
625
640
|
path: path
|
|
626
641
|
})))
|
|
627
|
-
if path
|
|
642
|
+
if !dirPath(path)
|
|
628
643
|
path = path.concat("/") // avoid deleting longer filenames with same parent and prefix
|
|
629
644
|
`(error, data) = dbDirRecords(path, true)
|
|
630
645
|
if error
|
|
@@ -637,13 +652,13 @@ closure FSview(table, rootpath, local view) {
|
|
|
637
652
|
basedir = item // delete base directory at end
|
|
638
653
|
continue
|
|
639
654
|
}
|
|
640
|
-
`(error, data) = view.table.delete(item
|
|
655
|
+
`(error, data) = view.table.delete(item)
|
|
641
656
|
if error
|
|
642
657
|
errors.push(error) }
|
|
643
658
|
if errors.length != 0
|
|
644
659
|
error = Error("some directory children not deleted", errors)
|
|
645
660
|
else {
|
|
646
|
-
`(error, data) = view.table.delete(basedir
|
|
661
|
+
`(error, data) = view.table.delete(basedir)
|
|
647
662
|
if error
|
|
648
663
|
error = Error("delete directory failed", {
|
|
649
664
|
path: path
|
|
@@ -659,7 +674,7 @@ closure FSview(table, rootpath, local view) {
|
|
|
659
674
|
//
|
|
660
675
|
// Read a directory at the specified path
|
|
661
676
|
// ### options currently ignored, for better or woise
|
|
662
|
-
|
|
677
|
+
//
|
|
663
678
|
view.dirList = closure dirList(path, options, callback, local name, dirdict) {
|
|
664
679
|
if !callback
|
|
665
680
|
return (syncAdapter(readir, path))
|
|
@@ -667,7 +682,7 @@ closure FSview(table, rootpath, local view) {
|
|
|
667
682
|
if !path
|
|
668
683
|
return // already executed callback with error
|
|
669
684
|
dirdict = {
|
|
670
|
-
name: name
|
|
685
|
+
name: name
|
|
671
686
|
path: path
|
|
672
687
|
data: {
|
|
673
688
|
platform: table.type
|
|
@@ -700,10 +715,8 @@ closure FSview(table, rootpath, local view) {
|
|
|
700
715
|
finfo.type = "directory"
|
|
701
716
|
} else if !finfo.type
|
|
702
717
|
finfo.type = "file" // everything is a file in UNIX
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
if item.doc._length
|
|
706
|
-
finfo.length = item.doc._length
|
|
718
|
+
finfo.md5 = item.doc._md5 || undefined
|
|
719
|
+
finfo.length = item.doc._length || item.doc.length || undefined
|
|
707
720
|
if item.key.length > offset // not the directory itself
|
|
708
721
|
dirdict.children.push({
|
|
709
722
|
name: name
|
|
@@ -723,7 +736,7 @@ closure FSview(table, rootpath, local view) {
|
|
|
723
736
|
// tree
|
|
724
737
|
//
|
|
725
738
|
// Return a tree for the specified path. The path is relative to the root of the view.
|
|
726
|
-
|
|
739
|
+
//
|
|
727
740
|
view.tree = closure tree(path, options, callback, local name, treedict, item, offset) {
|
|
728
741
|
if !callback
|
|
729
742
|
return (syncAdapter(tree, path, options))
|
|
@@ -734,8 +747,10 @@ closure FSview(table, rootpath, local view) {
|
|
|
734
747
|
prefix: path
|
|
735
748
|
deep: true
|
|
736
749
|
}
|
|
750
|
+
if name == "" && rootpath != ""
|
|
751
|
+
name = rootpath.split("/").slice(0,-1).pop() || ""
|
|
737
752
|
treedict = {
|
|
738
|
-
name: name
|
|
753
|
+
name: name
|
|
739
754
|
data: {
|
|
740
755
|
platform: table.type
|
|
741
756
|
semantics: "dbfs"
|
|
@@ -748,7 +763,8 @@ closure FSview(table, rootpath, local view) {
|
|
|
748
763
|
for item in path.split("/").slice(0,-1)
|
|
749
764
|
treedict.parents.push(item)
|
|
750
765
|
offset = path.length // offset to path's child names (after any slash)
|
|
751
|
-
view.table.records(options, function(error, data,
|
|
766
|
+
view.table.records(options, function(error, data,
|
|
767
|
+
local index, leaf, parent, child, slashdex, parents) {
|
|
752
768
|
if error
|
|
753
769
|
return (callback(error))
|
|
754
770
|
if data.rows.length == 0 && path != ""
|
|
@@ -775,22 +791,28 @@ closure FSview(table, rootpath, local view) {
|
|
|
775
791
|
else { // named or non-root directory
|
|
776
792
|
slashdex = name.lastIndexOf("/", name.length-2) // penultimate slash
|
|
777
793
|
parent = name.substring(0, slashdex+1) // includes parent's ending slash
|
|
778
|
-
|
|
779
|
-
|
|
794
|
+
leaf = name.slice(slashdex+1, -1) } // omits child's ending slash
|
|
795
|
+
child = index[name] = {
|
|
780
796
|
name: leaf
|
|
781
797
|
children: [] } }
|
|
782
798
|
else { // file child not in root
|
|
783
799
|
parent = name.substring(0, slashdex+1)
|
|
784
800
|
leaf = name.slice(slashdex+1)
|
|
785
801
|
child = { name: leaf } }
|
|
786
|
-
if index[parent]
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
802
|
+
if !index[parent] { // doesn't have immediate <parent>/ record
|
|
803
|
+
parents = parent.split("/").slice(0,-1)
|
|
804
|
+
parent = item = ""
|
|
805
|
+
for leaf in parents {
|
|
806
|
+
item = item.concat(leaf, "/")
|
|
807
|
+
if !index[item]
|
|
808
|
+
index[parent].children.push(
|
|
809
|
+
index[item] = {
|
|
810
|
+
name: leaf
|
|
811
|
+
children: [] })
|
|
812
|
+
parent = item
|
|
813
|
+
}
|
|
793
814
|
}
|
|
815
|
+
index[parent].children.push(child)
|
|
794
816
|
}
|
|
795
817
|
callback(false, treedict)
|
|
796
818
|
})
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*
|
|
12
12
|
* column positioning: // // !
|
|
13
13
|
*
|
|
14
|
-
* Copyright (c) 2019-
|
|
14
|
+
* Copyright (c) 2019-2024 by Richard C. Zulch
|
|
15
15
|
*
|
|
16
16
|
*/
|
|
17
17
|
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
30
|
closure rsrcVault(db, tablename, local vault) {
|
|
31
|
+
global()
|
|
31
32
|
vault = new(object, this)
|
|
32
33
|
|
|
33
34
|
// addResource
|
|
@@ -72,7 +73,7 @@ closure rsrcVault(db, tablename, local vault) {
|
|
|
72
73
|
//
|
|
73
74
|
// Return credentials for access to the resource.
|
|
74
75
|
|
|
75
|
-
vault.accessResource = function accessResource(resID) {
|
|
76
|
+
vault.accessResource = function accessResource(resID, local error, data) {
|
|
76
77
|
`(error, data) = vault.table.get(resID)
|
|
77
78
|
if error
|
|
78
79
|
list(error)
|
|
@@ -134,6 +135,7 @@ closure rsrcVault(db, tablename, local vault) {
|
|
|
134
135
|
*/
|
|
135
136
|
|
|
136
137
|
closure ResourceManager(dbname, prefix, local reman, watch, result) {
|
|
138
|
+
global(commonWatching)
|
|
137
139
|
reman = new(object, this)
|
|
138
140
|
watch = commonWatching.Watchable()
|
|
139
141
|
reman.watch = watch.watch
|