@naanlang/naan 1.0.1 → 1.0.4
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 +1 -1
- package/README.md +5 -5
- package/bin/index.js +18 -15
- package/dist/env_web.js +146 -16
- package/dist/naan.min.js +5 -5
- package/frameworks/browser/sworker.js +299 -91
- package/frameworks/browser/terminals.nlg +23 -10
- package/frameworks/browser/workers.nlg +26 -12
- package/frameworks/client/apiclient.nlg +17 -4
- package/frameworks/client/psm_client.nlg +178 -81
- package/frameworks/common/common.nlg +14 -4
- package/frameworks/node/apiserver.nlg +31 -15
- package/frameworks/node/filesystem.nlg +230 -46
- package/frameworks/node/gitter.nlg +27 -6
- package/frameworks/node/psm_server.nlg +113 -37
- package/frameworks/project/build.nlg +40 -23
- package/frameworks/project/projects.nlg +50 -27
- package/frameworks/running/debugnub.nlg +2 -5
- package/frameworks/storage/csv.nlg +120 -0
- package/frameworks/storage/dbt_pouch.nlg +41 -25
- package/frameworks/storage/psm.nlg +108 -28
- package/frameworks/storage/psm_dbtables.nlg +7 -3
- package/frameworks/storage/resources.nlg +30 -2
- package/lib/browser/env_web.js +148 -18
- package/lib/browser/env_webworker.js +1 -1
- package/lib/browser/require.js +1 -1
- package/lib/core/naanlib.js +5 -5
- package/lib/env_node.js +9 -1
- package/package.json +2 -1
- package/plugins/serviceAws/aws_cloudwatchlogs.nlg +1 -1
- package/plugins/serviceAws/aws_dynamo.nlg +625 -141
- package/plugins/serviceAws/aws_dynextra.nlg +294 -0
- package/plugins/serviceAws/dbt_aws.nlg +31 -11
- package/plugins/serviceAws/psm_aws.nlg +42 -26
- package/plugins/serviceAws/serviceAws.nlg +1 -0
- package/plugins/serviceGitHub/psm_github.nlg +41 -25
- package/plugins/serviceGitLab/psm_gitlab.nlg +41 -25
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* column positioning: // // !
|
|
8
8
|
*
|
|
9
|
-
* Copyright (c) 2019-
|
|
9
|
+
* Copyright (c) 2019-2022 by Richard C. Zulch
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
@@ -27,12 +27,18 @@ closure psmsFsView(rootpath, local view, gitter) {
|
|
|
27
27
|
view = view.1
|
|
28
28
|
|
|
29
29
|
// inherited
|
|
30
|
+
// -- view.folderPath(folderid, callback)
|
|
30
31
|
// -- view.winDrives(callback)
|
|
31
32
|
// -- view.testHidden(path, callback)
|
|
32
33
|
// -- view.readLines(path, options, callback)
|
|
33
34
|
// -- view.readFile(path, options, callback)
|
|
34
35
|
// -- view.writeFile(path, data, options, callback)
|
|
35
|
-
// -- view.
|
|
36
|
+
// -- view.readlink(path, options, callback)
|
|
37
|
+
// -- view.symlink(target, path, options, callback)
|
|
38
|
+
// -- view.info(path, options, callback)
|
|
39
|
+
// -- view.setInfo(path, info, callback)
|
|
40
|
+
// -- view.setOwnerGroup(path, uid, gid, callback)
|
|
41
|
+
// -- view.setMode(path, mode, callback)
|
|
36
42
|
// -- view.setTimes(path, atime, mtime, callback)
|
|
37
43
|
// -- view.dirList(path, options, callback)
|
|
38
44
|
// -- view.mkdir(path, callback)
|
|
@@ -81,16 +87,27 @@ closure psmsFsView(rootpath, local view, gitter) {
|
|
|
81
87
|
}
|
|
82
88
|
res.send(new(array, result))
|
|
83
89
|
}
|
|
84
|
-
|
|
90
|
+
|
|
91
|
+
// op: folderPath
|
|
92
|
+
function folderPathApi() {
|
|
93
|
+
res.send(new(array, view.folderPath(req.query.folderid)))
|
|
94
|
+
}
|
|
95
|
+
|
|
85
96
|
// op: readLines
|
|
86
97
|
function readLinesApi() {
|
|
87
98
|
res.send(new(array, view.readLines(path, req.query)))
|
|
88
99
|
}
|
|
89
100
|
|
|
90
101
|
// op: readFile
|
|
91
|
-
function readFileApi(local options) {
|
|
102
|
+
function readFileApi(local error, localpath, options) {
|
|
103
|
+
`(error, localpath) = view.localPath(path)
|
|
104
|
+
if error {
|
|
105
|
+
res.send(new(array, list(error)))
|
|
106
|
+
return
|
|
107
|
+
}
|
|
92
108
|
options = {
|
|
93
|
-
dotfiles: "allow"
|
|
109
|
+
dotfiles: "allow"
|
|
110
|
+
}
|
|
94
111
|
if req.query.encoding == "binary"
|
|
95
112
|
options.headers = {
|
|
96
113
|
"Content-Type": "application/octet-stream"
|
|
@@ -99,7 +116,7 @@ closure psmsFsView(rootpath, local view, gitter) {
|
|
|
99
116
|
options.headers = { // ### is this always the right default?
|
|
100
117
|
"Content-Type": "text/html; charset=UTF-8"
|
|
101
118
|
}
|
|
102
|
-
res.sendFile(
|
|
119
|
+
res.sendFile(localpath, options, function (err, local status) {
|
|
103
120
|
if err {
|
|
104
121
|
if err.code == "ENOENT"
|
|
105
122
|
status = 404
|
|
@@ -118,7 +135,18 @@ closure psmsFsView(rootpath, local view, gitter) {
|
|
|
118
135
|
options.encoding = req.query.encoding
|
|
119
136
|
res.send(new(array, view.writeFile(path, req.body, options)))
|
|
120
137
|
}
|
|
121
|
-
|
|
138
|
+
|
|
139
|
+
// op: readlink
|
|
140
|
+
function readlinkApi() {
|
|
141
|
+
res.send(new(array, view.readlink(path)))
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// op: symlink
|
|
145
|
+
function symlinkApi(local options) {
|
|
146
|
+
options = { }
|
|
147
|
+
res.send(new(array, view.symlink(req.query.target, path, options)))
|
|
148
|
+
}
|
|
149
|
+
|
|
122
150
|
// op: delete
|
|
123
151
|
function deleteApi(local options) {
|
|
124
152
|
options = { }
|
|
@@ -130,8 +158,26 @@ closure psmsFsView(rootpath, local view, gitter) {
|
|
|
130
158
|
}
|
|
131
159
|
|
|
132
160
|
// op: info
|
|
133
|
-
function infoApi() {
|
|
134
|
-
|
|
161
|
+
function infoApi(local options) {
|
|
162
|
+
options - { }
|
|
163
|
+
if req.query.follow
|
|
164
|
+
options.follow = req.query.follow
|
|
165
|
+
res.send(new(array, view.info(path, options)))
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// op: setInfo
|
|
169
|
+
function setInfoApi() {
|
|
170
|
+
res.send(new(array, view.setInfo(path, req.body)))
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// op: setOwnerGroup
|
|
174
|
+
function setOwnerGroupApi() {
|
|
175
|
+
res.send(new(array, view.setOwnerGroup(path, req.query.uid, req.query.gid)))
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// op: setMode
|
|
179
|
+
function setModeApi() {
|
|
180
|
+
res.send(new(array, view.setMode(path, req.query.mode)))
|
|
135
181
|
}
|
|
136
182
|
|
|
137
183
|
// op: setTimes
|
|
@@ -141,8 +187,8 @@ closure psmsFsView(rootpath, local view, gitter) {
|
|
|
141
187
|
|
|
142
188
|
// op: dirList
|
|
143
189
|
function dirListApi(local options) {
|
|
144
|
-
if req.query.
|
|
145
|
-
options = {
|
|
190
|
+
if req.query.stat
|
|
191
|
+
options = { stat: true }
|
|
146
192
|
res.send(new(array, view.dirList(path, options)))
|
|
147
193
|
}
|
|
148
194
|
|
|
@@ -153,7 +199,7 @@ closure psmsFsView(rootpath, local view, gitter) {
|
|
|
153
199
|
|
|
154
200
|
// op: tree
|
|
155
201
|
function treeApi() {
|
|
156
|
-
res.send(new(array, view.tree(path, req.query.depthlimit)))
|
|
202
|
+
res.send(new(array, view.tree(path, toint(req.query.depthlimit))))
|
|
157
203
|
}
|
|
158
204
|
|
|
159
205
|
// op: dirSearch
|
|
@@ -216,11 +262,17 @@ closure psmsFsView(rootpath, local view, gitter) {
|
|
|
216
262
|
|
|
217
263
|
proc = {
|
|
218
264
|
pathOp: pathOpApi
|
|
265
|
+
folderPath: folderPathApi
|
|
219
266
|
readFile: readFileApi
|
|
220
267
|
readLines: readLinesApi
|
|
221
268
|
writeFile: writeFileApi
|
|
269
|
+
readlink: readlinkApi
|
|
270
|
+
symlink: symlinkApi
|
|
222
271
|
delete: deleteApi
|
|
223
272
|
info: infoApi
|
|
273
|
+
setInfo: setInfoApi
|
|
274
|
+
setOwnerGroup: setOwnerGroupApi
|
|
275
|
+
setMode: setModeApi
|
|
224
276
|
setTimes: setTimesApi
|
|
225
277
|
dirList: dirListApi
|
|
226
278
|
mkdir: mkdirApi
|
|
@@ -263,38 +315,38 @@ closure psmsFsConnector(psm, api, local connClassID, connector, watch) {
|
|
|
263
315
|
connector.vault = psm.vault(connClassID)
|
|
264
316
|
connector.label = "Naan IDE Server"
|
|
265
317
|
watch = psm.watchable(connector)
|
|
266
|
-
|
|
267
|
-
// hashResourceID
|
|
268
|
-
//
|
|
269
|
-
// Make a stable hash based on the resource credentials that we can persist or share between
|
|
270
|
-
// different domains accessing the same resource.
|
|
271
|
-
connector.hashResourceID = function hashResourceID(resource, local ident) {
|
|
272
|
-
ident = resource.urlName
|
|
273
|
-
if resource.label
|
|
274
|
-
ident = ident.concat(resource.label)
|
|
275
|
-
HashSHA256(ident)
|
|
276
|
-
}
|
|
277
|
-
|
|
318
|
+
|
|
278
319
|
// findResource
|
|
279
320
|
//
|
|
280
|
-
// Find a resource with the specified contents, returning the resID or false.
|
|
281
|
-
// is: if added, would the specified contents be redundant to an existing resource?
|
|
321
|
+
// Find a resource with the specified contents, returning the resID or false.
|
|
282
322
|
connector.findResource = function findResource(resource, local resID, creds) {
|
|
283
323
|
for resID in listResources().1 {
|
|
284
324
|
creds = access(resID).1
|
|
285
|
-
if resource.urlName == creds.urlName
|
|
286
|
-
&& (!resource.label || resource.label == creds.label)
|
|
325
|
+
if ((!resource.urlName || resource.urlName == creds.urlName)
|
|
326
|
+
&& (!resource.label || resource.label == creds.label))
|
|
287
327
|
return (resID)
|
|
288
328
|
}
|
|
289
329
|
false
|
|
290
330
|
}
|
|
291
331
|
|
|
292
|
-
//
|
|
332
|
+
// writeResource
|
|
293
333
|
//
|
|
294
|
-
// Add credentials for a named resource.
|
|
295
|
-
|
|
296
|
-
|
|
334
|
+
// Add or update credentials for a named resource. If no resID is specified this adds a new
|
|
335
|
+
// resource; otherwise it updates an existing one. The resource is a dictionary comprising these
|
|
336
|
+
// keys:
|
|
337
|
+
// label - [optional] label we use for this resource
|
|
338
|
+
// urlName - URL (e.g. "http://host.com/node/fsapi")
|
|
339
|
+
// authSecret - access-control token for this URL
|
|
340
|
+
// hidden - [optional] don't enumerate to user
|
|
341
|
+
// locked - [optional] don't allow user delete
|
|
342
|
+
//
|
|
343
|
+
// The return value is an (error, resID) tuple. Error, if non-false, is a dictionary of field
|
|
344
|
+
// keys and error diagnostic strings.
|
|
345
|
+
connector.writeResource = function writeResource(resource, resID,
|
|
346
|
+
local creds, errors, error, data, change) {
|
|
347
|
+
// badField - test for a bad string
|
|
297
348
|
function badField(str) { !string(str) || str.trim().length == 0 }
|
|
349
|
+
|
|
298
350
|
creds = { urlName: resource.urlName }
|
|
299
351
|
errors = { }
|
|
300
352
|
if resource.label
|
|
@@ -303,6 +355,8 @@ closure psmsFsConnector(psm, api, local connClassID, connector, watch) {
|
|
|
303
355
|
creds.label = resource.urlName
|
|
304
356
|
if badField(creds.label)
|
|
305
357
|
errors.label = "invalid label"
|
|
358
|
+
else if !resID && findResource({ label: creds.label })
|
|
359
|
+
errors.label = "duplicate name"
|
|
306
360
|
if badField(resource.urlName)
|
|
307
361
|
errors.urlName = "required field"
|
|
308
362
|
if length(errors) > 0
|
|
@@ -312,14 +366,36 @@ closure psmsFsConnector(psm, api, local connClassID, connector, watch) {
|
|
|
312
366
|
creds.hidden = resource.hidden
|
|
313
367
|
if resource.locked
|
|
314
368
|
creds.locked = resource.locked
|
|
315
|
-
resID
|
|
316
|
-
|
|
369
|
+
if resID {
|
|
370
|
+
`(error, data) = connector.vault.updateResource(resID, creds)
|
|
371
|
+
change = { changed: [resID] }
|
|
372
|
+
} else {
|
|
373
|
+
resID = UUID()
|
|
374
|
+
`(error, data) = connector.vault.addResource(resID, creds)
|
|
375
|
+
change = { added: [resID] }
|
|
376
|
+
}
|
|
317
377
|
if data {
|
|
318
|
-
watch.notify(connClassID,
|
|
319
|
-
data = resID
|
|
378
|
+
watch.notify(connClassID, change)
|
|
379
|
+
data = resID
|
|
380
|
+
}
|
|
381
|
+
}
|
|
320
382
|
list(error, data)
|
|
321
383
|
}
|
|
322
|
-
|
|
384
|
+
|
|
385
|
+
// addResource
|
|
386
|
+
//
|
|
387
|
+
// Add a new resource, returning a standard error tuple per writeResource.
|
|
388
|
+
connector.addResource = function addResource(resource) {
|
|
389
|
+
writeResource(resource)
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// updateResource
|
|
393
|
+
//
|
|
394
|
+
// Update an existing resource, returning a standard error tuple per writeResource.
|
|
395
|
+
connector.updateResource = function updateResource(resID, resource) {
|
|
396
|
+
writeResource(resource, resID)
|
|
397
|
+
}
|
|
398
|
+
|
|
323
399
|
// getResource
|
|
324
400
|
//
|
|
325
401
|
// Get credentials for a named resource, matching the dictionary semantics of addResource. The
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* column positioning: // // !
|
|
8
8
|
*
|
|
9
|
-
* Copyright (c) 2019-
|
|
9
|
+
* Copyright (c) 2019-2022 by Richard C. Zulch
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
@@ -267,15 +267,20 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
|
|
|
267
267
|
//
|
|
268
268
|
// applySubstitutions
|
|
269
269
|
//
|
|
270
|
-
// Apply substitutions from readSubstitutionFile to string, returning a
|
|
270
|
+
// Apply substitutions from readSubstitutionFile to string, returning a tuple of the resulting
|
|
271
|
+
// text and the number of substitution patterns actually used from the supplied dictionary.
|
|
271
272
|
//
|
|
272
|
-
function applySubstitutions(text, subs) {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
273
|
+
function applySubstitutions(text, subs, local subbed) {
|
|
274
|
+
subbed = { }
|
|
275
|
+
list(text.replace(RegExp("[$][-A-Za-z0-9_./()\\\\]+[$]", "g"), function(match, local subst) {
|
|
276
|
+
subst = subs[match.slice(1,-1)]
|
|
277
|
+
if subst {
|
|
278
|
+
subbed[match] = true
|
|
279
|
+
subst
|
|
280
|
+
}
|
|
276
281
|
else
|
|
277
282
|
match
|
|
278
|
-
})
|
|
283
|
+
}), length(subbed))
|
|
279
284
|
}
|
|
280
285
|
|
|
281
286
|
//
|
|
@@ -573,7 +578,7 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
|
|
|
573
578
|
//
|
|
574
579
|
// Handling the catenates is a bit tricky. One set of buildrules can have multiple catentates,
|
|
575
580
|
// distinguished by having a unique writepath derived from the destfile parameter. All groups
|
|
576
|
-
// having the same writepath contribute to a single catenate, with the sources
|
|
581
|
+
// having the same writepath contribute to a single catenate, with the sources ordered first by
|
|
577
582
|
// group number (low to high) and then order in the source list, first to last. If any component
|
|
578
583
|
// contributing to a catenate is out-of-date with respect to it then they are all built and
|
|
579
584
|
// combined into the single output file. This computes a set of ordering integers for the parts
|
|
@@ -649,13 +654,13 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
|
|
|
649
654
|
outfile = JSpath.join(outdirpath, group.destfile)
|
|
650
655
|
else
|
|
651
656
|
outfile = JSpath.join(outdirpath, source)
|
|
652
|
-
fs.info(infile, function(error, instat, local listing, addgroup, file) {
|
|
657
|
+
fs.info(infile, false, function(error, instat, local listing, addgroup, file) {
|
|
653
658
|
if error
|
|
654
659
|
build.errorlist.push(error)
|
|
655
660
|
else if instat.type != "directory"
|
|
656
661
|
addEntry(infile, outfile, instat, sdex) // add execution entry if needed
|
|
657
662
|
else { // add a new group for directory source
|
|
658
|
-
`(error, listing) = fs.dirList(infile, {
|
|
663
|
+
`(error, listing) = fs.dirList(infile, { stat: true })
|
|
659
664
|
if error
|
|
660
665
|
build.errorlist.push(error)
|
|
661
666
|
else {
|
|
@@ -754,19 +759,20 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
|
|
|
754
759
|
entry.zip = true
|
|
755
760
|
if !entry.writepath.toLowerCase().endsWith(".zip")
|
|
756
761
|
entry.writepath = entry.writepath.concat(".zip") // always ends with .zip
|
|
762
|
+
outfile = entry.writepath
|
|
757
763
|
}
|
|
758
764
|
if entry.catenate || entry.zip {
|
|
759
765
|
entry.catorder = group.groupno * sourcemax + sdex // order of groups then sources
|
|
760
766
|
if build.testats[outfile] && instat.mtimeMs < build.testats[outfile].mtimeMs
|
|
761
767
|
instat = build.testats[outfile] // make source at least as recent as template
|
|
762
|
-
} else
|
|
768
|
+
} else if !entry.unzip
|
|
763
769
|
entry.writepath = outfile
|
|
764
770
|
++pending.active
|
|
765
771
|
if destcheck { // destcheck is special target for determining if task needed
|
|
766
772
|
entry.destcheck = JSpath.join(outdirpath, destcheck.pop())
|
|
767
|
-
fs.info(entry.destcheck, addIfOutOfDate)
|
|
773
|
+
fs.info(entry.destcheck, false, addIfOutOfDate)
|
|
768
774
|
} else
|
|
769
|
-
fs.info(outfile, addIfOutOfDate)
|
|
775
|
+
fs.info(outfile, false, addIfOutOfDate)
|
|
770
776
|
|
|
771
777
|
function addIfOutOfDate(error, outstat, local entryop) {
|
|
772
778
|
if error || toint(instat.mtimeMs) > toint(outstat.mtimeMs) {
|
|
@@ -861,9 +867,8 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
|
|
|
861
867
|
if error
|
|
862
868
|
build.errorlist.push(error)
|
|
863
869
|
else if content.startsWith("$$") // prefix to cause version substitution
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
build.templates[writepath] = content
|
|
870
|
+
`(content) = applySubstitutions(content.slice(2), verFileDefs)
|
|
871
|
+
build.templates[writepath] = content
|
|
867
872
|
}
|
|
868
873
|
} ()
|
|
869
874
|
}
|
|
@@ -890,13 +895,21 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
|
|
|
890
895
|
`(error, result) = fs.unzip(entry.readpath, entry.writepath) // unzip source into destination
|
|
891
896
|
if !error && entry.destcheck
|
|
892
897
|
fs.touch(entry.destcheck) // show we completed
|
|
893
|
-
} else if entry.copy
|
|
894
|
-
`(error, result) = fs.
|
|
898
|
+
} else if entry.copy {
|
|
899
|
+
`(error, result) = fs.util.deepcopy(entry.readpath, false, fs, dirpath, {
|
|
900
|
+
erase: true // not really needed
|
|
901
|
+
overwrite: true // overwrite files
|
|
902
|
+
force: true // ignore modify date
|
|
903
|
+
preserve: true // preserve attributes
|
|
904
|
+
})
|
|
905
|
+
if error // deepcopy returns a list of errors
|
|
906
|
+
error = error.0 // but we're only doing one item
|
|
907
|
+
}
|
|
895
908
|
else if ((`(error, content) = fs.readFile(entry.readpath)) && !error) {
|
|
896
909
|
if entry.license
|
|
897
|
-
content = applySubstitutions(content, licFileDefs)
|
|
910
|
+
`(content) = applySubstitutions(content, licFileDefs)
|
|
898
911
|
if entry.version
|
|
899
|
-
content = applySubstitutions(content, verFileDefs)
|
|
912
|
+
`(content) = applySubstitutions(content, verFileDefs)
|
|
900
913
|
if entry.naanpack
|
|
901
914
|
`(error, content) = parseAndPack(content, entry.readpath, { naanpack: true })
|
|
902
915
|
else if entry.jspack
|
|
@@ -962,11 +975,15 @@ closure Builder(rules, operation, fs, srcpath, destpath, local build, group, new
|
|
|
962
975
|
docat = true
|
|
963
976
|
}
|
|
964
977
|
if docat { // catenate the files
|
|
965
|
-
if template
|
|
966
|
-
content = applySubstitutions(template, catsub)
|
|
978
|
+
if template {
|
|
979
|
+
`(content, result) = applySubstitutions(template, catsub)
|
|
980
|
+
if length(catsub) != result
|
|
981
|
+
build.errorlist.push(Error("Matched ", result, " of ", length(catsub),
|
|
982
|
+
"substitutions in template:", build.tepaths[writepath]))
|
|
983
|
+
}
|
|
967
984
|
else
|
|
968
985
|
content = catenates[writepath].map(function(entry){entry.content}).join("\n")
|
|
969
|
-
if
|
|
986
|
+
if dozip {
|
|
970
987
|
zipmap = { }
|
|
971
988
|
zipmap[JSpath.basename(writepath)] = content
|
|
972
989
|
`(error, content) = zip(zipmap) // compress the catenated file
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* column positioning: // // !
|
|
8
8
|
*
|
|
9
|
-
* Copyright (c) 2016-
|
|
9
|
+
* Copyright (c) 2016-2022 by Richard C. Zulch
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
@@ -150,40 +150,36 @@ closure projConnector(projman, projtype, local connector, watch) {
|
|
|
150
150
|
connector.label = projtype.classLabel
|
|
151
151
|
connector.vault = projman.vault(projtype.classID)
|
|
152
152
|
watch = projman.watchable(connector)
|
|
153
|
-
|
|
154
|
-
// hashResourceID
|
|
155
|
-
//
|
|
156
|
-
// Make a stable hash based on the resource credentials that we can persist or share between
|
|
157
|
-
// different domains accessing the same resource.
|
|
158
|
-
connector.hashResourceID = function hashResourceID(resource, local ident) {
|
|
159
|
-
ident = resource.name.concat(resource.locationID, resource.path)
|
|
160
|
-
HashSHA256(ident)
|
|
161
|
-
}
|
|
162
153
|
|
|
163
154
|
// findResource
|
|
164
155
|
//
|
|
165
|
-
// Find a resource with the specified contents, returning the resID or false.
|
|
166
|
-
// is: if added, would the specified contents be redundant to an existing resource?
|
|
156
|
+
// Find a resource with the specified contents, returning the resID or false.
|
|
167
157
|
connector.findResource = function findResource(resource, local resID, resInfo) {
|
|
168
158
|
for resID in listResources().1 {
|
|
169
159
|
resInfo = access(resID).1
|
|
170
|
-
if resource.name == resInfo.name
|
|
171
|
-
&& resource.locationID == resInfo.locationID
|
|
172
|
-
&& resource.path == resInfo.path
|
|
160
|
+
if ((!resource.name || resource.name == resInfo.name)
|
|
161
|
+
&& (!resource.locationID || resource.locationID == resInfo.locationID)
|
|
162
|
+
&& (!resource.path || resource.path == resInfo.path))
|
|
173
163
|
return (resID)
|
|
174
164
|
}
|
|
175
165
|
false
|
|
176
166
|
}
|
|
177
167
|
|
|
178
|
-
//
|
|
168
|
+
// writeResource
|
|
179
169
|
//
|
|
180
|
-
// Add credentials for a named resource.
|
|
170
|
+
// Add or update credentials for a named resource. If no resID is specified this adds a new
|
|
171
|
+
// resource; otherwise it updates an existing one. The resource is a dictionary comprising these
|
|
172
|
+
// keys:
|
|
181
173
|
// name: <string> - the user visible name of the project
|
|
182
174
|
// locationID: <string> - resID of location resource where this project resides
|
|
183
175
|
// path: <string> - path to project within location
|
|
184
|
-
// Error, if non-false, is a dictionary of field
|
|
185
|
-
|
|
176
|
+
// The return value is an (error, resID) tuple. Error, if non-false, is a dictionary of field
|
|
177
|
+
// keys and error diagnostic strings.
|
|
178
|
+
connector.writeResource = function writeResource(resource, resID,
|
|
179
|
+
local errors, error, data, change) {
|
|
180
|
+
// badField - test for a bad string
|
|
186
181
|
function badField(str) { !string(str) || str.trim().length == 0 }
|
|
182
|
+
|
|
187
183
|
errors = { }
|
|
188
184
|
if badField(resource.name)
|
|
189
185
|
errors.name = "invalid name"
|
|
@@ -197,19 +193,40 @@ closure projConnector(projman, projtype, local connector, watch) {
|
|
|
197
193
|
if length(errors) > 0
|
|
198
194
|
error = errors
|
|
199
195
|
else {
|
|
200
|
-
resID
|
|
201
|
-
|
|
196
|
+
if resID {
|
|
197
|
+
`(error, data) = connector.vault.updateResource(resID, resource)
|
|
198
|
+
change = { changed: [resID] }
|
|
199
|
+
} else {
|
|
200
|
+
resID = UUID()
|
|
201
|
+
`(error, data) = connector.vault.addResource(resID, resource)
|
|
202
|
+
change = { added: [resID] }
|
|
203
|
+
}
|
|
202
204
|
if data {
|
|
203
|
-
watch.notify(projtype.classID,
|
|
204
|
-
data = resID
|
|
205
|
+
watch.notify(projtype.classID, change)
|
|
206
|
+
data = resID
|
|
207
|
+
}
|
|
205
208
|
}
|
|
206
209
|
list(error, data)
|
|
207
210
|
}
|
|
208
211
|
|
|
212
|
+
// addResource
|
|
213
|
+
//
|
|
214
|
+
// Add a new resource, returning a standard error tuple per writeResource.
|
|
215
|
+
connector.addResource = function addResource(resource) {
|
|
216
|
+
writeResource(resource)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// updateResource
|
|
220
|
+
//
|
|
221
|
+
// Update an existing resource, returning a standard error tuple per writeResource.
|
|
222
|
+
connector.updateResource = function updateResource(resID, resource) {
|
|
223
|
+
writeResource(resource, resID)
|
|
224
|
+
}
|
|
225
|
+
|
|
209
226
|
// deleteResource
|
|
210
227
|
//
|
|
211
228
|
// Delete information for a project. The return value is (error, result) tuple.
|
|
212
|
-
connector.deleteResource = function deleteResource(resID
|
|
229
|
+
connector.deleteResource = function deleteResource(resID) {
|
|
213
230
|
connector.vault.deleteResource(resID)
|
|
214
231
|
watch.notify(projtype.classID, { deleted: [resID] })
|
|
215
232
|
list(false, { ok: true})
|
|
@@ -276,7 +293,7 @@ closure projConnector(projman, projtype, local connector, watch) {
|
|
|
276
293
|
*
|
|
277
294
|
* Nide.proj/
|
|
278
295
|
* nide_cliser.cfg - JSON definition of the project (see below)
|
|
279
|
-
* NaanIDE_version.txt - [default] substitution file defining 1.0.1
|
|
296
|
+
* NaanIDE_version.txt - [default] substitution file defining 1.0.4-1 etc.
|
|
280
297
|
* NaanIDE_version_builds.txt - [default] contains current build number as decimal string
|
|
281
298
|
* build/ - [optional] default build output location
|
|
282
299
|
* NaanIDE.lic - [optional] defines Zulch Laboratories, Inc. and other license info
|
|
@@ -440,7 +457,9 @@ closure projConfig(projman, where, local procon, fs, configpath) {
|
|
|
440
457
|
error = Error("project configuration not a directory", where.path)
|
|
441
458
|
else
|
|
442
459
|
`(error, data) = readConfig() }
|
|
443
|
-
if error
|
|
460
|
+
if error.code == "ENOENT"
|
|
461
|
+
return (list(Error("Project <b>".concat(where.name, "</b>:<br>not found at ", where.path))))
|
|
462
|
+
else if error
|
|
444
463
|
return (list(Error("Project.load failed", error)))
|
|
445
464
|
procon.loaded = true
|
|
446
465
|
list(false, procon.configDict)
|
|
@@ -539,7 +558,11 @@ closure projConfig(projman, where, local procon, fs, configpath) {
|
|
|
539
558
|
if error
|
|
540
559
|
return (list(Error("can't access publishing filesystem", error)))
|
|
541
560
|
inpath = fs.path.join(proj_configDataFolder, rules.products, publish.input)
|
|
542
|
-
fs.util.deepcopy(inpath, publish.sources, outfs, publish.output
|
|
561
|
+
fs.util.deepcopy(inpath, publish.sources, outfs, publish.output, {
|
|
562
|
+
erase: true
|
|
563
|
+
overwrite: true
|
|
564
|
+
follow: true
|
|
565
|
+
})
|
|
543
566
|
}
|
|
544
567
|
|
|
545
568
|
// close
|
|
@@ -217,7 +217,7 @@ closure DebugNub(local nub, debug, paused) {
|
|
|
217
217
|
// constant string, with corresponding arguments defined as follows:
|
|
218
218
|
//
|
|
219
219
|
// "startpause" - pause at start of evaluating an expression
|
|
220
|
-
// tuple -
|
|
220
|
+
// tuple - dictionary of current step info
|
|
221
221
|
// "breakpoint" - hit a breakpoint
|
|
222
222
|
// where - dictionary
|
|
223
223
|
// value - applied value
|
|
@@ -237,10 +237,7 @@ closure DebugNub(local nub, debug, paused) {
|
|
|
237
237
|
if entry != "steptrap"
|
|
238
238
|
lineStepClear()
|
|
239
239
|
if entry == "startpause" {
|
|
240
|
-
|
|
241
|
-
msg.error = Error("can't debug atom:", arg1)
|
|
242
|
-
else
|
|
243
|
-
msg.expr = arg1
|
|
240
|
+
msg.expr = arg1
|
|
244
241
|
} else if entry == "breakpoint" {
|
|
245
242
|
if breakpointHit(msg, arg1, arg2)
|
|
246
243
|
return
|