@naanlang/naan 1.0.3 → 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 +1 -1
- package/bin/index.js +2 -2
- package/dist/env_web.js +1 -0
- package/dist/naan.min.js +3 -3
- package/frameworks/browser/sworker.js +26 -36
- package/frameworks/browser/terminals.nlg +1 -1
- package/frameworks/browser/workers.nlg +12 -6
- package/frameworks/client/psm_client.nlg +62 -28
- package/frameworks/common/common.nlg +2 -2
- package/frameworks/node/apiserver.nlg +7 -3
- package/frameworks/node/filesystem.nlg +57 -16
- package/frameworks/node/gitter.nlg +27 -6
- package/frameworks/node/psm_server.nlg +18 -5
- package/frameworks/project/projects.nlg +4 -2
- package/lib/browser/env_web.js +3 -2
- package/lib/core/naanlib.js +3 -3
- package/package.json +2 -1
|
@@ -27,10 +27,8 @@
|
|
|
27
27
|
* knowledge of which ones are responding. If a request takes longer than a second to respond then
|
|
28
28
|
* this executes a validate operation, which eliminates any clients that have gone away and rejects
|
|
29
29
|
* their pending operations.
|
|
30
|
-
* One potential problem is stale caches
|
|
31
|
-
*
|
|
32
|
-
* its ID and version along with the mesage port. If that is different from the previous version then
|
|
33
|
-
* the old caches are cleared and data re-fetched.
|
|
30
|
+
* One potential problem is stale caches. When an older version is discovered then we notify the
|
|
31
|
+
* client and invalidate the old cache.
|
|
34
32
|
*
|
|
35
33
|
* column positioning: // // !
|
|
36
34
|
*
|
|
@@ -38,7 +36,7 @@
|
|
|
38
36
|
*
|
|
39
37
|
*/
|
|
40
38
|
|
|
41
|
-
var CurrentCacheName = "Naanlang
|
|
39
|
+
var CurrentCacheName = "Naanlang-1.0.4-2";
|
|
42
40
|
|
|
43
41
|
|
|
44
42
|
//
|
|
@@ -91,18 +89,13 @@ var waitingForInit = []; // initialization functions, or
|
|
|
91
89
|
var pubVersion = event.data.hereIsMyVersion;
|
|
92
90
|
var sourceId = event.source.id; // client id of sender of message
|
|
93
91
|
msgPorts[sourceId] = msgport;
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
version: pubVersion
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
ClearCaches(newCacheName)
|
|
105
|
-
.then(Reaper); // clean up obsolete info
|
|
92
|
+
console.log("[1.0.4-2] received new msgport for", sourceId, pubID, "-", pubVersion);
|
|
93
|
+
if (pubVersion != "1.0.4-2")
|
|
94
|
+
msgport.postMessage({ // notify new version available
|
|
95
|
+
id: "upgrade",
|
|
96
|
+
version: "1.0.4-2"
|
|
97
|
+
});
|
|
98
|
+
Reaper(); // clean up obsolete info
|
|
106
99
|
|
|
107
100
|
// msgport.onmessage events
|
|
108
101
|
//
|
|
@@ -114,7 +107,7 @@ var waitingForInit = []; // initialization functions, or
|
|
|
114
107
|
if (msg.id == "response")
|
|
115
108
|
processResponse(msg);
|
|
116
109
|
else if (msg.id == "text")
|
|
117
|
-
console.log("[1.0.
|
|
110
|
+
console.log("[1.0.4-2] msg received:", msg.text);
|
|
118
111
|
};
|
|
119
112
|
|
|
120
113
|
// send text to IDE log
|
|
@@ -125,7 +118,7 @@ var waitingForInit = []; // initialization functions, or
|
|
|
125
118
|
// don't clutter the log
|
|
126
119
|
msgport.postMessage({
|
|
127
120
|
id: "text",
|
|
128
|
-
text: "port received by 1.0.
|
|
121
|
+
text: "port received by 1.0.4-2",
|
|
129
122
|
});
|
|
130
123
|
*/
|
|
131
124
|
if (--pending === 0)
|
|
@@ -142,7 +135,7 @@ var waitingForInit = []; // initialization functions, or
|
|
|
142
135
|
includeUncontrolled: true
|
|
143
136
|
}).then(function(clientList) {
|
|
144
137
|
clientList.every(function(client) {
|
|
145
|
-
console.log("[1.0.
|
|
138
|
+
console.log("[1.0.4-2] requesting new msgport for", client.id);
|
|
146
139
|
++pending;
|
|
147
140
|
client.postMessage({ // tell client(s) we need this fetch source
|
|
148
141
|
msg: "Naan_need_fetch_port",
|
|
@@ -188,12 +181,12 @@ function Reaper() {
|
|
|
188
181
|
clients[clientList[clidex].id] = clientList[clidex];
|
|
189
182
|
for (var sourceId in msgPorts)
|
|
190
183
|
if (!clients[sourceId]) {
|
|
191
|
-
console.log("[1.0.
|
|
184
|
+
console.log("[1.0.4-2] source gone:", sourceId);
|
|
192
185
|
delete msgPorts[sourceId]; // no longer a source
|
|
193
186
|
}
|
|
194
187
|
for (var clientId in fetchPorts)
|
|
195
188
|
if (!clients[clientId]) {
|
|
196
|
-
console.log("[1.0.
|
|
189
|
+
console.log("[1.0.4-2] client gone:", clientId);
|
|
197
190
|
delete fetchPorts[clientId]; // no longer a client
|
|
198
191
|
}
|
|
199
192
|
for (var fqdex = 0; fqdex < fetchQueue.length; ++fqdex) {
|
|
@@ -218,25 +211,22 @@ function Reaper() {
|
|
|
218
211
|
/*
|
|
219
212
|
* ClearCaches
|
|
220
213
|
*
|
|
221
|
-
* Clear obsolete caches
|
|
214
|
+
* Clear obsolete caches, returning a promise.
|
|
222
215
|
*
|
|
223
216
|
*/
|
|
224
217
|
|
|
225
|
-
function ClearCaches(
|
|
226
|
-
if (CurrentCacheName == newCacheName)
|
|
227
|
-
return (Promise.resolve()); // no change
|
|
228
|
-
CurrentCacheName = newCacheName;
|
|
218
|
+
function ClearCaches() {
|
|
229
219
|
var promise = caches.keys().then(function(cacheNames) { // delete old cache entries
|
|
230
220
|
return (Promise.all(
|
|
231
221
|
cacheNames.map(function(cacheName) {
|
|
232
|
-
if (cacheName
|
|
233
|
-
console.log('[1.0.
|
|
222
|
+
if (cacheName != CurrentCacheName) {
|
|
223
|
+
console.log('[1.0.4-2] deleting old cache:', cacheName);
|
|
234
224
|
return (caches.delete(cacheName));
|
|
235
225
|
}
|
|
236
226
|
})
|
|
237
227
|
));
|
|
238
228
|
}).then(function() { // claim all clients
|
|
239
|
-
console.log('[1.0.
|
|
229
|
+
console.log('[1.0.4-2] claiming clients');
|
|
240
230
|
return (self.clients.claim());
|
|
241
231
|
});
|
|
242
232
|
return (promise);
|
|
@@ -279,7 +269,7 @@ function GetClientResponse(event, urlpath) {
|
|
|
279
269
|
msgport.postMessage({
|
|
280
270
|
id: "fetch",
|
|
281
271
|
seq: seqno,
|
|
282
|
-
version: "1.0.
|
|
272
|
+
version: "1.0.4-2",
|
|
283
273
|
request: {
|
|
284
274
|
method: event.request.method,
|
|
285
275
|
url: event.request.url
|
|
@@ -327,7 +317,7 @@ function GetClientResponse(event, urlpath) {
|
|
|
327
317
|
*/
|
|
328
318
|
|
|
329
319
|
self.addEventListener('install', function(event) {
|
|
330
|
-
console.log("[1.0.
|
|
320
|
+
console.log("[1.0.4-2] install");
|
|
331
321
|
self.skipWaiting();
|
|
332
322
|
});
|
|
333
323
|
|
|
@@ -366,7 +356,7 @@ self.addEventListener('fetch', function(event) {
|
|
|
366
356
|
}
|
|
367
357
|
else
|
|
368
358
|
promise = fetch(event.request).catch(function (e) {
|
|
369
|
-
console.log("[1.0.
|
|
359
|
+
console.log("[1.0.4-2] fetch failed", e);
|
|
370
360
|
return (new Response(undefined, {
|
|
371
361
|
status: 404,
|
|
372
362
|
statusText: "Fetch Failed"
|
|
@@ -395,16 +385,16 @@ self.addEventListener('fetch', function(event) {
|
|
|
395
385
|
*/
|
|
396
386
|
|
|
397
387
|
self.addEventListener('activate', function(event) {
|
|
398
|
-
console.log("[1.0.
|
|
388
|
+
console.log("[1.0.4-2] activate");
|
|
399
389
|
self.clients.matchAll({ // for debugging, list controlled clients
|
|
400
390
|
includeUncontrolled: true
|
|
401
391
|
}).then(function(clientList) {
|
|
402
392
|
var urls = clientList.map(function(client) {
|
|
403
393
|
return (client.url);
|
|
404
394
|
});
|
|
405
|
-
console.log('[1.0.
|
|
395
|
+
console.log('[1.0.4-2] matching clients:', urls.join(', '));
|
|
406
396
|
});
|
|
407
|
-
var promise = ClearCaches(
|
|
397
|
+
var promise = ClearCaches();
|
|
408
398
|
if (event.waitUntil)
|
|
409
399
|
event.waitUntil(promise);
|
|
410
400
|
});
|
|
@@ -55,8 +55,13 @@ closure ServiceWorker(pubID, pubVersion, local serv, callback) {
|
|
|
55
55
|
version: msg.version
|
|
56
56
|
})
|
|
57
57
|
} else if msg.id == "upgrade" {
|
|
58
|
-
if
|
|
59
|
-
|
|
58
|
+
if serv.upgrade != msg.version {
|
|
59
|
+
serv.upgrade = msg.version // only report once
|
|
60
|
+
callback(false, {
|
|
61
|
+
message: "upgrade"
|
|
62
|
+
version: msg.version
|
|
63
|
+
})
|
|
64
|
+
}
|
|
60
65
|
}
|
|
61
66
|
else if msg.id == "text"
|
|
62
67
|
debuglog("sw[".concat(serv.scope, "]: "), msg.text) // handy remote debug capability
|
|
@@ -182,7 +187,7 @@ closure ServiceWorker(pubID, pubVersion, local serv, callback) {
|
|
|
182
187
|
*
|
|
183
188
|
*/
|
|
184
189
|
|
|
185
|
-
closure ScopedServiceWorker(pubID, pubVersion, local scoper, result) {
|
|
190
|
+
closure ScopedServiceWorker(pubID, pubVersion, callback, local scoper, result) {
|
|
186
191
|
scoper = new(object, ScopedServiceWorker)
|
|
187
192
|
scoper.scopes = {}
|
|
188
193
|
scoper.serv = ServiceWorker(pubID, pubVersion)
|
|
@@ -232,7 +237,8 @@ closure ScopedServiceWorker(pubID, pubVersion, local scoper, result) {
|
|
|
232
237
|
response.body = data
|
|
233
238
|
scoper.serv.msgport.postMessage(response)
|
|
234
239
|
return
|
|
235
|
-
}
|
|
240
|
+
} else if data.message == "upgrade"
|
|
241
|
+
callback(data.version)
|
|
236
242
|
})
|
|
237
243
|
if result.0 {
|
|
238
244
|
ErrorDebuglog("ScopedServiceWorker failed", result)
|
|
@@ -365,8 +371,8 @@ closure TrackedScope(track, scope, fs, rootpath, local trope, result) {
|
|
|
365
371
|
*
|
|
366
372
|
*/
|
|
367
373
|
|
|
368
|
-
closure TrackServiceWorkers(track, pubID, pubVersion, local error) {
|
|
369
|
-
`(error, serviceWorker) = ScopedServiceWorker(pubID, pubVersion)
|
|
374
|
+
closure TrackServiceWorkers(track, pubID, pubVersion, callback, local error) {
|
|
375
|
+
`(error, serviceWorker) = ScopedServiceWorker(pubID, pubVersion, callback)
|
|
370
376
|
if error
|
|
371
377
|
ErrorDebugLog("TrackServiceWorkers: cannot load service worker", error)
|
|
372
378
|
track.register(TrackedScope, "V-Site")
|
|
@@ -22,6 +22,44 @@ closure psmcFsView(api, rootpath, local view, pathmod) {
|
|
|
22
22
|
view = new(object, this)
|
|
23
23
|
view.api = api
|
|
24
24
|
view.rootpath = rootpath
|
|
25
|
+
|
|
26
|
+
// data
|
|
27
|
+
//
|
|
28
|
+
// Return the filesystem data, i.e. the tree data field. The minimum information is:
|
|
29
|
+
// platform -- e.g. "darwin" or "win32"
|
|
30
|
+
// semantics -- e.g. "win32" or "posix"
|
|
31
|
+
// pathsep -- e.g. / or \
|
|
32
|
+
|
|
33
|
+
view.data = closure data(callback) {
|
|
34
|
+
if !callback
|
|
35
|
+
return (syncAdapter(data))
|
|
36
|
+
params = {
|
|
37
|
+
path: ""
|
|
38
|
+
op: "tree"
|
|
39
|
+
depthlimit: 0
|
|
40
|
+
}
|
|
41
|
+
api.psmRemote(params, false, function(error, tree) {
|
|
42
|
+
view.fsdata = tree.data
|
|
43
|
+
callback(error, tree.data)
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// pathInit
|
|
48
|
+
//
|
|
49
|
+
// Initialize our path module for the applicable path type.
|
|
50
|
+
|
|
51
|
+
closure pathInit(local result) {
|
|
52
|
+
result = view.data()
|
|
53
|
+
if result.0
|
|
54
|
+
pathmod = false
|
|
55
|
+
else {
|
|
56
|
+
if view.fsdata.pathsep == "\\"
|
|
57
|
+
pathmod = JSpath.win32
|
|
58
|
+
else
|
|
59
|
+
pathmod = JSpath.posix
|
|
60
|
+
false
|
|
61
|
+
}
|
|
62
|
+
}
|
|
25
63
|
|
|
26
64
|
// psmRemote
|
|
27
65
|
//
|
|
@@ -30,10 +68,14 @@ closure psmcFsView(api, rootpath, local view, pathmod) {
|
|
|
30
68
|
closure psmRemote(path, op, options, putdata, callback, local params, item) {
|
|
31
69
|
if !callback
|
|
32
70
|
return (syncAdapter(psmRemote, path, op, options, putdata))
|
|
71
|
+
if !pathmod && pathInit()
|
|
72
|
+
return // failed to initialize path module
|
|
33
73
|
if !string(path)
|
|
34
74
|
return (asyncResult(callback, Error("invalid path:", typeof(path))))
|
|
75
|
+
if rootpath != ""
|
|
76
|
+
path = pathmod.resolve(rootpath, path)
|
|
35
77
|
params = {
|
|
36
|
-
path:
|
|
78
|
+
path: path
|
|
37
79
|
op: op
|
|
38
80
|
}
|
|
39
81
|
for item in options
|
|
@@ -59,38 +101,30 @@ closure psmcFsView(api, rootpath, local view, pathmod) {
|
|
|
59
101
|
|
|
60
102
|
// path
|
|
61
103
|
//
|
|
62
|
-
//
|
|
104
|
+
// Redirect path calls to module chosen for filesystem semantics. It is possible for this to fail
|
|
105
|
+
// on first connection to the filesystem, in which case we return false for the path result.
|
|
106
|
+
// Throwing an error would complicate our callers and testing. This way there's a small chance
|
|
107
|
+
// that a transient error will give a bad path, but that will be detected downstream.
|
|
63
108
|
|
|
64
109
|
view.path = new(object, this)
|
|
65
|
-
view.path[".undefined"] =
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if error
|
|
70
|
-
return (list(error))
|
|
71
|
-
if info.pathsep == "\\"
|
|
72
|
-
pathmod = JSpath.win32
|
|
73
|
-
else
|
|
74
|
-
pathmod = JSpath.posix
|
|
75
|
-
}
|
|
76
|
-
xapply(pathmod, selector, arglist)
|
|
77
|
-
} (args.0, args.-1)
|
|
110
|
+
view.path[".undefined"] = function path args {
|
|
111
|
+
if !pathmod
|
|
112
|
+
pathInit()
|
|
113
|
+
xapply(pathmod, args.0, args.-1)
|
|
78
114
|
}
|
|
79
115
|
|
|
80
|
-
//
|
|
116
|
+
// folderPath
|
|
81
117
|
//
|
|
82
|
-
// Return the
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
//
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
callback(error, tree.data)
|
|
93
|
-
})
|
|
118
|
+
// Return the path of the specified special folder:
|
|
119
|
+
// "HomeDir"
|
|
120
|
+
// "TempDir"
|
|
121
|
+
// "PackageDir"
|
|
122
|
+
// The returned path is relative to our root if within it, or absolute otherwise.
|
|
123
|
+
|
|
124
|
+
view.folderPath = function folderPath(folderID, callback) {
|
|
125
|
+
psmRemote("", "folderPath", {
|
|
126
|
+
folderid: folderID
|
|
127
|
+
}, false, callback)
|
|
94
128
|
}
|
|
95
129
|
|
|
96
130
|
// readLines
|
|
@@ -66,7 +66,7 @@ function JsonParse(data) {
|
|
|
66
66
|
list(false, data)
|
|
67
67
|
} catch {
|
|
68
68
|
if true
|
|
69
|
-
|
|
69
|
+
list(exception)
|
|
70
70
|
}
|
|
71
71
|
};
|
|
72
72
|
|
|
@@ -84,7 +84,7 @@ function JsonStringify(data) {
|
|
|
84
84
|
list(false, data)
|
|
85
85
|
} catch {
|
|
86
86
|
if true
|
|
87
|
-
|
|
87
|
+
list(exception)
|
|
88
88
|
}
|
|
89
89
|
};
|
|
90
90
|
|
|
@@ -110,8 +110,12 @@ closure MakeAPI(port, options, callback, local chroot, server) {
|
|
|
110
110
|
|
|
111
111
|
server.app.use(function(req,res,next) {
|
|
112
112
|
res.header("Access-Control-Allow-Origin", "*")
|
|
113
|
-
res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE")
|
|
114
|
-
|
|
113
|
+
res.header("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS")
|
|
114
|
+
res.header("Access-Control-Allow-Headers", "x-naanlang-api-guid")
|
|
115
|
+
if req.method == "OPTIONS"
|
|
116
|
+
res.sendStatus(200)
|
|
117
|
+
else
|
|
118
|
+
next()
|
|
115
119
|
})
|
|
116
120
|
|
|
117
121
|
server.app.use("", express.static(chroot))
|
|
@@ -197,7 +201,7 @@ closure MakeAPI(port, options, callback, local chroot, server) {
|
|
|
197
201
|
|
|
198
202
|
server.app.all("/psm", closure(req, res, local guid) {
|
|
199
203
|
guid = req.get("x-naanlang-api-guid")
|
|
200
|
-
if server.guid && guid != server.guid
|
|
204
|
+
if server.guid && guid != server.guid && req.method !== "OPTIONS"
|
|
201
205
|
res.status(403).send("unauthorized")
|
|
202
206
|
else if !server.hostfs
|
|
203
207
|
res.status(503).send("host filesystem not available")
|
|
@@ -23,7 +23,7 @@ closure Filesystem(rootpath, local files, badCreatePathRegEx) {
|
|
|
23
23
|
if !rootpath
|
|
24
24
|
return (list(Error("mandatory rootpath missing")))
|
|
25
25
|
else if rootpath == ""
|
|
26
|
-
rootpath =
|
|
26
|
+
rootpath = JSpath.sep
|
|
27
27
|
files = new(object, this)
|
|
28
28
|
files.rootpath = rootpath
|
|
29
29
|
files.path = JSpath
|
|
@@ -54,24 +54,31 @@ closure Filesystem(rootpath, local files, badCreatePathRegEx) {
|
|
|
54
54
|
// Convert a path argument to a local path, returning a standard result tuple. If the path is
|
|
55
55
|
// invalid this will report why. Otherwise this returns the correct localpath, which is the path
|
|
56
56
|
// we use with the underlying API.
|
|
57
|
+
// Win32 driver letters are annoying here. If the rootpath specifies a drive letter then the
|
|
58
|
+
// paths must match it. If the rootpath doesn't specify a drive letter then path can have any
|
|
59
|
+
// drive letter, or none.
|
|
57
60
|
|
|
58
61
|
badCreatePathRegEx = RegExp("[:]|[/\\\\][/\\\\]|[/\\\\]$|(^|[/\\\\])[.][.]?$")
|
|
59
62
|
|
|
60
|
-
closure localPath(path, create, local pattern, testpath) {
|
|
63
|
+
files.localPath = closure localPath(path, create, local pattern, testpath, locroot) {
|
|
61
64
|
if !path
|
|
62
65
|
return (list(Error("filesystem path false")))
|
|
63
66
|
else if !string(path)
|
|
64
67
|
return (list(Error("filesystem path invalid:", typeof(path))))
|
|
68
|
+
if JSpath === JSpath.win32 {
|
|
69
|
+
locroot = JSpath.resolve(path, rootpath) // use path's drive letter if we don't have one
|
|
70
|
+
pattern = "\\:\\" // unchanged and unique on win32
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
locroot = rootpath
|
|
74
|
+
pattern = "/:/" // unchanged and unique on posix
|
|
75
|
+
}
|
|
65
76
|
if JSpath.isAbsolute(path)
|
|
66
|
-
path = JSpath.resolve(path)
|
|
77
|
+
path = JSpath.resolve(locroot, path) // add drive letter to absolute path
|
|
67
78
|
else
|
|
68
79
|
path = JSpath.normalize(path) // normalize for current platform
|
|
69
|
-
if
|
|
70
|
-
|
|
71
|
-
else
|
|
72
|
-
pattern = "/:/" // unchanged and unique on posix
|
|
73
|
-
if ((testpath = commonParents(path, rootpath)) != "")
|
|
74
|
-
list(false, JSpath.resolve(rootpath, path)) // ### shouldn't be so lax on this test
|
|
80
|
+
if (commonParents(path, locroot) != "")
|
|
81
|
+
list(false, JSpath.resolve(locroot, path))
|
|
75
82
|
else if ((testpath = JSpath.resolve(pattern, path)).indexOf(pattern.slice(0,-1)) < 0)
|
|
76
83
|
list(Error("filesystem path reaches above root:", path))
|
|
77
84
|
else if create && testpath.indexOf(pattern) < 0
|
|
@@ -79,7 +86,31 @@ closure Filesystem(rootpath, local files, badCreatePathRegEx) {
|
|
|
79
86
|
else if create && badCreatePathRegEx.test(path)
|
|
80
87
|
list(Error("filesystem create path invalid", path))
|
|
81
88
|
else
|
|
82
|
-
list(false, JSpath.resolve(
|
|
89
|
+
list(false, JSpath.resolve(locroot, path))
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// folderPath
|
|
93
|
+
//
|
|
94
|
+
// Return a folder path for the specified folderID, relative to our root. If the folder path is
|
|
95
|
+
// not within our root then it is returned as an absolute path.
|
|
96
|
+
|
|
97
|
+
files.folderPath = function folderPath(folderID, callback, local envID, path) {
|
|
98
|
+
if !callback
|
|
99
|
+
return (syncAdapter(folderPath, folderID))
|
|
100
|
+
envID = {
|
|
101
|
+
"HomeDir": "HOME",
|
|
102
|
+
"TempDir": "TMPDIR",
|
|
103
|
+
"PackageDir": "npm_config_prefix"
|
|
104
|
+
}[folderID]
|
|
105
|
+
if envID {
|
|
106
|
+
path = js.g.process.env[envID]
|
|
107
|
+
if path.startsWith(rootpath)
|
|
108
|
+
path = path.slice(rootpath.length) // remove rootpath from beginning
|
|
109
|
+
}
|
|
110
|
+
if path
|
|
111
|
+
callback(false, path)
|
|
112
|
+
else
|
|
113
|
+
callback(Error("unknown folderID:", folderID))
|
|
83
114
|
}
|
|
84
115
|
|
|
85
116
|
// winDrives
|
|
@@ -639,8 +670,10 @@ closure Filesystem(rootpath, local files, badCreatePathRegEx) {
|
|
|
639
670
|
// Recursively build the tree
|
|
640
671
|
|
|
641
672
|
closure recurTree(localpath, depth, parent) {
|
|
642
|
-
if depthlimit && depth >= depthlimit
|
|
673
|
+
if depthlimit && depth >= depthlimit {
|
|
674
|
+
recurNext()
|
|
643
675
|
return
|
|
676
|
+
}
|
|
644
677
|
++pending.active
|
|
645
678
|
fs.readdir(localpath, { }, function(err, nodes, local child, node, stat) {
|
|
646
679
|
if err.code == "ENOTDIR"
|
|
@@ -670,13 +703,21 @@ closure Filesystem(rootpath, local files, badCreatePathRegEx) {
|
|
|
670
703
|
parent: child
|
|
671
704
|
}) } }
|
|
672
705
|
--pending.active
|
|
673
|
-
|
|
674
|
-
node = queue.pop()
|
|
675
|
-
recurTree(node.path, node.depth, node.parent) }
|
|
676
|
-
if pending.active == 0 && queue.length == 0
|
|
677
|
-
pending.signal(list(false, root))
|
|
706
|
+
recurNext()
|
|
678
707
|
})
|
|
679
708
|
}
|
|
709
|
+
|
|
710
|
+
// recurNext
|
|
711
|
+
//
|
|
712
|
+
// Pull more queued items and signal when done.
|
|
713
|
+
|
|
714
|
+
function recurNext(local node) {
|
|
715
|
+
while queue.length > 0 && pending.active < 20 {
|
|
716
|
+
node = queue.pop()
|
|
717
|
+
recurTree(node.path, node.depth, node.parent) }
|
|
718
|
+
if pending.active == 0 && queue.length == 0
|
|
719
|
+
pending.signal(list(false, root))
|
|
720
|
+
}
|
|
680
721
|
|
|
681
722
|
`(error, localpath) = localPath(path)
|
|
682
723
|
if error
|
|
@@ -23,6 +23,28 @@ closure Gitter(fs, local gitt) {
|
|
|
23
23
|
gitt = new(object, this)
|
|
24
24
|
gitt.roots = {}
|
|
25
25
|
|
|
26
|
+
// gitPath
|
|
27
|
+
//
|
|
28
|
+
// Convert a Windows path to a posix (git) path if needed.
|
|
29
|
+
//
|
|
30
|
+
function gitPath(fspath) {
|
|
31
|
+
if fs.path.sep == "/"
|
|
32
|
+
fspath
|
|
33
|
+
else
|
|
34
|
+
fspath.split(fs.path.sep).join("/")
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// fsPath
|
|
38
|
+
//
|
|
39
|
+
// Convert a posix (git) path to a filesystem path if needed.
|
|
40
|
+
//
|
|
41
|
+
function fsPath(gitpath) {
|
|
42
|
+
if fs.path.sep == "/"
|
|
43
|
+
gitpath
|
|
44
|
+
else
|
|
45
|
+
gitpath.split("/").join(fs.path.sep)
|
|
46
|
+
}
|
|
47
|
+
|
|
26
48
|
// repoRoot
|
|
27
49
|
//
|
|
28
50
|
// Compute the path from the fs root to the repo root.
|
|
@@ -54,11 +76,10 @@ closure Gitter(fs, local gitt) {
|
|
|
54
76
|
// Compute the bridge path to an absolute repopath (which can point inside a repo) from the
|
|
55
77
|
// phyiscal repo root. If these are the same, or an error occurs, then the result is "".
|
|
56
78
|
//
|
|
57
|
-
|
|
58
79
|
function bridgePath(repopath, local top) {
|
|
59
80
|
top = repoRoot(repopath).1
|
|
60
81
|
if top && repopath.startsWith(top) && repopath.length > top.length
|
|
61
|
-
top = repopath.slice(top.length+1).concat(
|
|
82
|
+
top = repopath.slice(top.length+1).concat(fs.path.sep) // move leading "/" of bridge path to trailing
|
|
62
83
|
else
|
|
63
84
|
"" // we are looking at the root, or an error
|
|
64
85
|
}
|
|
@@ -151,12 +172,12 @@ closure Gitter(fs, local gitt) {
|
|
|
151
172
|
outLE: "\x00" // lines delimited by nulls
|
|
152
173
|
}
|
|
153
174
|
if options.pathspec
|
|
154
|
-
execOptions.cmdargs = execOptions.cmdargs.concat("--", options.pathspec)
|
|
175
|
+
execOptions.cmdargs = execOptions.cmdargs.concat("--", gitPath(options.pathspec))
|
|
155
176
|
result = []
|
|
156
177
|
execOptions.outproc = function(output, liner, kill, local line, path) {
|
|
157
178
|
while (line = liner.read())
|
|
158
179
|
if line != "" {
|
|
159
|
-
path = line.substring(3)
|
|
180
|
+
path = fsPath(line.substring(3))
|
|
160
181
|
if path.startsWith(bridge) { // must start with bridge to be within repopath
|
|
161
182
|
path = path.slice(bridge.length)
|
|
162
183
|
result.push({
|
|
@@ -183,7 +204,7 @@ closure Gitter(fs, local gitt) {
|
|
|
183
204
|
// Retrieve the content of a specific file and revision. The file pathspec must be the path of
|
|
184
205
|
// the file within the repo.
|
|
185
206
|
|
|
186
|
-
gitt.showfile = closure showfile(repopath, options, callback, local execOptions
|
|
207
|
+
gitt.showfile = closure showfile(repopath, options, callback, local execOptions) {
|
|
187
208
|
if !callback
|
|
188
209
|
return (syncAdapter(showfile, repopath, options))
|
|
189
210
|
if !string(repopath)
|
|
@@ -192,7 +213,7 @@ closure Gitter(fs, local gitt) {
|
|
|
192
213
|
return (asyncResult(callback, Error("both pathspec and hash required:", options.pathspec, options.hash)))
|
|
193
214
|
repopath = fs.path.resolve(fs.rootpath, repopath)
|
|
194
215
|
execOptions = {
|
|
195
|
-
cmdargs: ["show", options.hash.concat(":", bridgePath(repopath), options.pathspec)]
|
|
216
|
+
cmdargs: ["show", options.hash.concat(":", bridgePath(repopath), gitPath(options.pathspec))]
|
|
196
217
|
execops: {
|
|
197
218
|
cwd: repopath
|
|
198
219
|
}
|
|
@@ -27,6 +27,7 @@ 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)
|
|
@@ -86,16 +87,27 @@ closure psmsFsView(rootpath, local view, gitter) {
|
|
|
86
87
|
}
|
|
87
88
|
res.send(new(array, result))
|
|
88
89
|
}
|
|
89
|
-
|
|
90
|
+
|
|
91
|
+
// op: folderPath
|
|
92
|
+
function folderPathApi() {
|
|
93
|
+
res.send(new(array, view.folderPath(req.query.folderid)))
|
|
94
|
+
}
|
|
95
|
+
|
|
90
96
|
// op: readLines
|
|
91
97
|
function readLinesApi() {
|
|
92
98
|
res.send(new(array, view.readLines(path, req.query)))
|
|
93
99
|
}
|
|
94
100
|
|
|
95
101
|
// op: readFile
|
|
96
|
-
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
|
+
}
|
|
97
108
|
options = {
|
|
98
|
-
dotfiles: "allow"
|
|
109
|
+
dotfiles: "allow"
|
|
110
|
+
}
|
|
99
111
|
if req.query.encoding == "binary"
|
|
100
112
|
options.headers = {
|
|
101
113
|
"Content-Type": "application/octet-stream"
|
|
@@ -104,7 +116,7 @@ closure psmsFsView(rootpath, local view, gitter) {
|
|
|
104
116
|
options.headers = { // ### is this always the right default?
|
|
105
117
|
"Content-Type": "text/html; charset=UTF-8"
|
|
106
118
|
}
|
|
107
|
-
res.sendFile(
|
|
119
|
+
res.sendFile(localpath, options, function (err, local status) {
|
|
108
120
|
if err {
|
|
109
121
|
if err.code == "ENOENT"
|
|
110
122
|
status = 404
|
|
@@ -187,7 +199,7 @@ closure psmsFsView(rootpath, local view, gitter) {
|
|
|
187
199
|
|
|
188
200
|
// op: tree
|
|
189
201
|
function treeApi() {
|
|
190
|
-
res.send(new(array, view.tree(path, req.query.depthlimit)))
|
|
202
|
+
res.send(new(array, view.tree(path, toint(req.query.depthlimit))))
|
|
191
203
|
}
|
|
192
204
|
|
|
193
205
|
// op: dirSearch
|
|
@@ -250,6 +262,7 @@ closure psmsFsView(rootpath, local view, gitter) {
|
|
|
250
262
|
|
|
251
263
|
proc = {
|
|
252
264
|
pathOp: pathOpApi
|
|
265
|
+
folderPath: folderPathApi
|
|
253
266
|
readFile: readFileApi
|
|
254
267
|
readLines: readLinesApi
|
|
255
268
|
writeFile: writeFileApi
|
|
@@ -293,7 +293,7 @@ closure projConnector(projman, projtype, local connector, watch) {
|
|
|
293
293
|
*
|
|
294
294
|
* Nide.proj/
|
|
295
295
|
* nide_cliser.cfg - JSON definition of the project (see below)
|
|
296
|
-
* NaanIDE_version.txt - [default] substitution file defining 1.0.
|
|
296
|
+
* NaanIDE_version.txt - [default] substitution file defining 1.0.4-1 etc.
|
|
297
297
|
* NaanIDE_version_builds.txt - [default] contains current build number as decimal string
|
|
298
298
|
* build/ - [optional] default build output location
|
|
299
299
|
* NaanIDE.lic - [optional] defines Zulch Laboratories, Inc. and other license info
|
|
@@ -457,7 +457,9 @@ closure projConfig(projman, where, local procon, fs, configpath) {
|
|
|
457
457
|
error = Error("project configuration not a directory", where.path)
|
|
458
458
|
else
|
|
459
459
|
`(error, data) = readConfig() }
|
|
460
|
-
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
|
|
461
463
|
return (list(Error("Project.load failed", error)))
|
|
462
464
|
procon.loaded = true
|
|
463
465
|
list(false, procon.configDict)
|