@naanlang/naan 1.4.4 → 1.6.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 +4 -4
- package/bin/index.js +25 -3
- package/dist/env_web.js +164 -22
- package/dist/naan.min.js +5 -5
- package/frameworks/browser/browser.nlg +2 -2
- package/frameworks/browser/https_request.nlg +2 -2
- package/frameworks/browser/sworker.js +28 -26
- package/frameworks/browser/terminals.nlg +342 -700
- package/frameworks/browser/worker.nlg +255 -0
- package/frameworks/browser/workers.nlg +5 -5
- package/frameworks/browser/ws_client.nlg +15 -10
- package/frameworks/client/apiclient.nlg +204 -19
- package/frameworks/client/client.nlg +22 -2
- package/frameworks/client/psm_client.nlg +12 -3
- package/frameworks/client/relaycon.nlg +87 -161
- package/frameworks/common/events.nlg +101 -0
- package/frameworks/common/repltools.nlg +80 -1
- package/frameworks/node/apiserver.nlg +39 -246
- package/frameworks/node/filesystem.nlg +39 -12
- package/frameworks/node/gitter.nlg +9 -7
- package/frameworks/node/http_request.nlg +34 -13
- package/frameworks/node/https_request.nlg +8 -3
- package/frameworks/node/node.nlg +2 -0
- package/frameworks/node/pty.nlg +346 -0
- package/frameworks/node/shell.nlg +4 -4
- package/frameworks/node/worker.nlg +409 -539
- package/frameworks/node/ws_client.nlg +2 -2
- package/frameworks/project/build.nlg +1 -1
- package/frameworks/project/projects.nlg +2 -1
- package/frameworks/running/debugnub.nlg +17 -4
- package/frameworks/running/executors.nlg +27 -274
- package/frameworks/running/instances.nlg +393 -0
- package/frameworks/running/remote_req.nlg +143 -0
- package/frameworks/running/remote_res.nlg +89 -0
- package/frameworks/running/taskexec.nlg +2 -2
- package/frameworks/service/imp.nlg +736 -0
- package/frameworks/service/model.nlg +71 -0
- package/frameworks/service/nubnode.nlg +105 -0
- package/frameworks/service/nubweb.nlg +106 -0
- package/frameworks/service/service.nlg +28 -0
- package/frameworks/storage/dbt_pouch.nlg +26 -13
- package/frameworks/storage/psm.nlg +39 -5
- package/frameworks/storage/psm_dbtables.nlg +10 -2
- package/lib/browser/env_web.js +171 -29
- package/lib/browser/env_webworker.js +91 -5
- package/lib/core/naanlib.js +5 -5
- package/lib/env_node.js +184 -8
- package/lib/env_nodeworker.js +74 -1
- package/package.json +1 -1
- package/plugins/serviceAws/aws_cloudwatchlogs.nlg +2 -1
- package/plugins/serviceAws/aws_dynamo.nlg +1 -0
- package/plugins/serviceAws/aws_s3.nlg +3 -3
- package/plugins/serviceAws/aws_sqs.nlg +3 -1
- package/plugins/serviceAws/aws_utils.nlg +3 -1
- package/plugins/serviceAws/dbt_aws.nlg +22 -20
- package/plugins/serviceAws/psm_aws.nlg +2 -1
- package/plugins/serviceNexara/speechrec.nlg +10 -6
- package/test/test_02_context.nlg +24 -1
- package/test/test_03_jsinterop.nlg +32 -3
- package/plugins/serviceGC/russian_trusted_root_ca_pem.crt +0 -33
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* model.nlg
|
|
3
|
+
* Naanlib/frameworks/service
|
|
4
|
+
*
|
|
5
|
+
* Naan service model.
|
|
6
|
+
*
|
|
7
|
+
* column positioning: // // !
|
|
8
|
+
*
|
|
9
|
+
* Copyright (c) 2023-2026 by Richard C. Zulch
|
|
10
|
+
*
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
/*
|
|
15
|
+
* ServiceModel
|
|
16
|
+
*
|
|
17
|
+
* Model object for services, such as instance management. This is used to conceal architectural
|
|
18
|
+
* details from service users.
|
|
19
|
+
*
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
closure ServiceModel(local model) {
|
|
23
|
+
global()
|
|
24
|
+
model = new(object, this)
|
|
25
|
+
model@\.class = new(object)
|
|
26
|
+
|
|
27
|
+
// add
|
|
28
|
+
//
|
|
29
|
+
// Add a location to our model with the specified name.
|
|
30
|
+
//
|
|
31
|
+
model@\.class.add = function add(name, local node) {
|
|
32
|
+
if !string(name) || name.trim() == ""
|
|
33
|
+
throw("ServiceModel.add: invalid name")
|
|
34
|
+
node = new(object)
|
|
35
|
+
node.id = name
|
|
36
|
+
node.parent = self
|
|
37
|
+
node@\.parent = self
|
|
38
|
+
self[name] = node
|
|
39
|
+
node
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// children
|
|
43
|
+
//
|
|
44
|
+
// List the ids of the children of the specified location, which is false for the root.
|
|
45
|
+
//
|
|
46
|
+
model@\.class.children = function children(parent) {
|
|
47
|
+
self.*.toarray.filter(function(id) { !id.startsWith(".") })
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// finis
|
|
51
|
+
|
|
52
|
+
model
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
/*
|
|
57
|
+
* modelInit
|
|
58
|
+
*
|
|
59
|
+
* Initialize the component.
|
|
60
|
+
*
|
|
61
|
+
*/
|
|
62
|
+
|
|
63
|
+
function modelInit(local manifest) {
|
|
64
|
+
manifest = `(ServiceModel, modelInit)
|
|
65
|
+
|
|
66
|
+
Naan.module.build(module.id, "model", function(modobj, compobj) {
|
|
67
|
+
require("./service.nlg")
|
|
68
|
+
compobj.manifest = manifest
|
|
69
|
+
modobj.exports.ServiceModel = ServiceModel
|
|
70
|
+
})
|
|
71
|
+
} ();
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* nubnode.nlg
|
|
3
|
+
* Naanlib/frameworks/service
|
|
4
|
+
*
|
|
5
|
+
* Nub for node worker access to IMP.
|
|
6
|
+
*
|
|
7
|
+
* column positioning: // // !
|
|
8
|
+
*
|
|
9
|
+
* Copyright (c) 2026 by Richard C. Zulch
|
|
10
|
+
*
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
/*
|
|
15
|
+
* NodeWorkerNub
|
|
16
|
+
*
|
|
17
|
+
* NodeWorkerNub provides an IMP interface that runs on node workers. This allows the worker to
|
|
18
|
+
* communicate with the IMP network and the main thread. The worker IMP must be created before this.
|
|
19
|
+
*
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
closure NodeWorkerNub(imp, options, local nownub) {
|
|
23
|
+
global(App,js)
|
|
24
|
+
if App.workerNub
|
|
25
|
+
return (App.workerNub) // singleton
|
|
26
|
+
|
|
27
|
+
options = merge({
|
|
28
|
+
name: "NodeWorkerNub"
|
|
29
|
+
type: "node-worker-nub"
|
|
30
|
+
}, options)
|
|
31
|
+
nownub = new(object, this)
|
|
32
|
+
nownub.instanceID = imp.us.instanceID || UUID()
|
|
33
|
+
nownub.dispatcher = require("naanlib:frameworks/running/taskexec.nlg").TaskDispatcher()
|
|
34
|
+
|
|
35
|
+
// impReceive
|
|
36
|
+
//
|
|
37
|
+
// Receive a remote request and reply to it.
|
|
38
|
+
//
|
|
39
|
+
nownub.impReceive = function impReceive(message, sourceID) {
|
|
40
|
+
if message.id == "targetin"
|
|
41
|
+
nownub.dispatcher(message.data, function(reply) {
|
|
42
|
+
nownub.sender(sourceID, {
|
|
43
|
+
id: "targetout",
|
|
44
|
+
data: reply
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// impUnknown
|
|
50
|
+
//
|
|
51
|
+
// Received an unknown destination notice from the other side.
|
|
52
|
+
//
|
|
53
|
+
nownub.impUnknown = function impUnknown(unknownID, sourceID, gateway) {
|
|
54
|
+
if !gateway
|
|
55
|
+
debuglog("NodeWorkerNub.impUnknownID:", unknownID, "sourceID:", sourceID)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// message event
|
|
59
|
+
//
|
|
60
|
+
// Incoming data.
|
|
61
|
+
//
|
|
62
|
+
js.t.msgPort.on("message", function(msg) {
|
|
63
|
+
if msg.id == "naan-imp-node"
|
|
64
|
+
nownub.impconn.dispatch(new(msg.data))
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
// register our connects *after* the receivers are configured above
|
|
68
|
+
|
|
69
|
+
nownub.sender = imp.register(nownub, {
|
|
70
|
+
name: options.name
|
|
71
|
+
type: options.type
|
|
72
|
+
meta: options.meta || undefined
|
|
73
|
+
instanceID: nownub.instanceID
|
|
74
|
+
services: [ "naan-node-worker" ]
|
|
75
|
+
})
|
|
76
|
+
nownub.impconn = imp.requester(function(data) {
|
|
77
|
+
js.t.msgPort.postMessage({ // send to main
|
|
78
|
+
id: "naan-imp-node",
|
|
79
|
+
data: data
|
|
80
|
+
})
|
|
81
|
+
list(false, { sent: true })
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
// finis
|
|
85
|
+
|
|
86
|
+
App.workerNub = nownub
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
/*
|
|
91
|
+
* nubnoInit
|
|
92
|
+
*
|
|
93
|
+
* Initialize the component.
|
|
94
|
+
*
|
|
95
|
+
*/
|
|
96
|
+
|
|
97
|
+
function nubnoInit(local manifest) {
|
|
98
|
+
manifest = `(NodeWorkerNub, nubnoInit)
|
|
99
|
+
|
|
100
|
+
Naan.module.build(module.id, "nubnode", function(modobj, compobj) {
|
|
101
|
+
require("./service.nlg")
|
|
102
|
+
compobj.manifest = manifest
|
|
103
|
+
modobj.exports.NodeWorkerNub = NodeWorkerNub
|
|
104
|
+
})
|
|
105
|
+
} ();
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* nubweb.nlg
|
|
3
|
+
* Naanlib/frameworks/service
|
|
4
|
+
*
|
|
5
|
+
* Nub for web worker access to IMP.
|
|
6
|
+
*
|
|
7
|
+
* column positioning: // // !
|
|
8
|
+
*
|
|
9
|
+
* Copyright (c) 2026 by Richard C. Zulch
|
|
10
|
+
*
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
/*
|
|
15
|
+
* WebWorkerNub
|
|
16
|
+
*
|
|
17
|
+
* WebWorkerNub provides an IMP interface that runs on web workers. This allows the worker to
|
|
18
|
+
* communicate with the IMP network and the main thread. The worker IMP must be created before this.
|
|
19
|
+
*
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
closure WebWorkerNub(imp, options, local wonub) {
|
|
23
|
+
global(App,js)
|
|
24
|
+
if App.workerNub
|
|
25
|
+
return (App.workerNub) // singleton
|
|
26
|
+
|
|
27
|
+
options = merge({
|
|
28
|
+
name: "WebWorkerNub"
|
|
29
|
+
type: "web-worker-nub"
|
|
30
|
+
}, options)
|
|
31
|
+
wonub = new(object, this)
|
|
32
|
+
wonub.instanceID = imp.us.instanceID || UUID()
|
|
33
|
+
wonub.dispatcher = require("naanlib:frameworks/running/taskexec.nlg").TaskDispatcher()
|
|
34
|
+
|
|
35
|
+
// impReceive
|
|
36
|
+
//
|
|
37
|
+
// Receive a remote request and reply to it.
|
|
38
|
+
//
|
|
39
|
+
wonub.impReceive = function impReceive(message, sourceID) {
|
|
40
|
+
if message.id == "targetin"
|
|
41
|
+
wonub.dispatcher(message.data, function(reply) {
|
|
42
|
+
wonub.sender(sourceID, {
|
|
43
|
+
id: "targetout",
|
|
44
|
+
data: reply
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// impUnknown
|
|
50
|
+
//
|
|
51
|
+
// Received an unknown destination notice from the other side.
|
|
52
|
+
//
|
|
53
|
+
wonub.impUnknown = function impUnknown(unknownID, sourceID, gateway) {
|
|
54
|
+
if !gateway
|
|
55
|
+
debuglog("WebWorkerNub.impUnknownID:", unknownID, "sourceID:", sourceID)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// message event
|
|
59
|
+
//
|
|
60
|
+
// Incoming data.
|
|
61
|
+
//
|
|
62
|
+
js.w.addEventListener("message", function message(e, local msg) {
|
|
63
|
+
msg = new(e.data)
|
|
64
|
+
if msg.scope == "naan-imp-web"
|
|
65
|
+
wonub.impconn.dispatch(msg.data)
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
// register our connects *after* the receivers are configured above
|
|
69
|
+
|
|
70
|
+
wonub.sender = imp.register(wonub, {
|
|
71
|
+
name: options.name
|
|
72
|
+
type: options.type
|
|
73
|
+
meta: options.meta || undefined
|
|
74
|
+
instanceID: wonub.instanceID
|
|
75
|
+
services: [ "naan-web-worker" ]
|
|
76
|
+
})
|
|
77
|
+
wonub.impconn = imp.requester(function(data) {
|
|
78
|
+
js.w.postMessage({ // send to main
|
|
79
|
+
scope: "naan-imp-web",
|
|
80
|
+
data: data
|
|
81
|
+
})
|
|
82
|
+
list(false, { sent: true })
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
// finis
|
|
86
|
+
|
|
87
|
+
App.workerNub = wonub
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
/*
|
|
92
|
+
* nubweInit
|
|
93
|
+
*
|
|
94
|
+
* Initialize the component.
|
|
95
|
+
*
|
|
96
|
+
*/
|
|
97
|
+
|
|
98
|
+
function nubweInit(local manifest) {
|
|
99
|
+
manifest = `(WebWorkerNub, nubweInit)
|
|
100
|
+
|
|
101
|
+
Naan.module.build(module.id, "nubweb", function(modobj, compobj) {
|
|
102
|
+
require("./service.nlg")
|
|
103
|
+
compobj.manifest = manifest
|
|
104
|
+
modobj.exports.WebWorkerNub = WebWorkerNub
|
|
105
|
+
})
|
|
106
|
+
} ();
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* service.nlg
|
|
3
|
+
* Naanlib/frameworks/service
|
|
4
|
+
*
|
|
5
|
+
* Root component for services functionality.
|
|
6
|
+
*
|
|
7
|
+
* column positioning: // // !
|
|
8
|
+
*
|
|
9
|
+
* Copyright (c) 2026 by Richard C. Zulch
|
|
10
|
+
*
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
/*
|
|
15
|
+
* svcInit
|
|
16
|
+
*
|
|
17
|
+
* Initialize the service module.
|
|
18
|
+
*
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
function svcInit(local manifest) {
|
|
22
|
+
manifest = `(svcInit)
|
|
23
|
+
|
|
24
|
+
Naan.module.build(module.id, "service", function(modobj, compobj) {
|
|
25
|
+
require("../common/common.nlg").LiveImport()
|
|
26
|
+
compobj.manifest = manifest
|
|
27
|
+
})
|
|
28
|
+
} ();
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* column positioning: // // !
|
|
9
9
|
*
|
|
10
|
-
* Copyright (c) 2017-
|
|
10
|
+
* Copyright (c) 2017-2025 by Richard C. Zulch
|
|
11
11
|
*
|
|
12
12
|
*/
|
|
13
13
|
|
|
@@ -76,21 +76,31 @@ closure dapoTable(database, rootpath, local table, prefix) {
|
|
|
76
76
|
|
|
77
77
|
// add
|
|
78
78
|
//
|
|
79
|
-
// Add a new record to the table with
|
|
80
|
-
//
|
|
81
|
-
|
|
82
|
-
|
|
79
|
+
// Add a new record to the table with options. This will fail if the key already exists. Optional
|
|
80
|
+
// metadata is stored with the record and cannot be changed without calling update. Metadata keys
|
|
81
|
+
// cannot begin with underscore ("_").
|
|
82
|
+
//
|
|
83
|
+
// The returned document, which can be used with update, has the following keys:
|
|
84
|
+
// key - the key name of the record
|
|
85
|
+
// content - the body content stored under in the record
|
|
86
|
+
// _md5 - the MD5 hash of the content
|
|
87
|
+
// _length - the length of the content
|
|
88
|
+
// _id - the PouchDB ID
|
|
89
|
+
// _rev - the PouchDB revision
|
|
90
|
+
// <...> - various metadata keys
|
|
91
|
+
|
|
92
|
+
table.add = closure add(key, content, options, callback, local id, doc) {
|
|
83
93
|
if !callback
|
|
84
|
-
return (syncAdapter(add, key, content,
|
|
94
|
+
return (syncAdapter(add, key, content, options))
|
|
85
95
|
id = makeID(key)
|
|
86
96
|
if !string(id)
|
|
87
97
|
return (asyncResult(callback, id)) // error from makeID
|
|
88
|
-
if
|
|
98
|
+
if options.metadata && !dictionary(options.metadata)
|
|
89
99
|
return (asyncResult(callback, Error("invalid arguments")))
|
|
90
100
|
if !database.pouch
|
|
91
101
|
return (asyncResult(callback, Error("database closed")))
|
|
92
|
-
if metadata {
|
|
93
|
-
doc = new(metadata)
|
|
102
|
+
if options.metadata {
|
|
103
|
+
doc = new(options.metadata) // start with metadata
|
|
94
104
|
doc._rev = undefined } // _rev not allowed in metadata
|
|
95
105
|
else
|
|
96
106
|
doc = { }
|
|
@@ -115,12 +125,13 @@ closure dapoTable(database, rootpath, local table, prefix) {
|
|
|
115
125
|
|
|
116
126
|
// update
|
|
117
127
|
//
|
|
118
|
-
// Update a record in the table using the document previously returned by add or get.
|
|
119
|
-
//
|
|
128
|
+
// Update a record in the table using the document previously returned by add or get. Specify
|
|
129
|
+
// options to update those values, otherwise existing/default values are used. This will fail
|
|
130
|
+
// if the record has been modified by someone else in the meantime.
|
|
120
131
|
|
|
121
|
-
table.update = closure update(doc, callback) {
|
|
132
|
+
table.update = closure update(doc, options, callback) {
|
|
122
133
|
if !callback
|
|
123
|
-
return (syncAdapter(update, doc))
|
|
134
|
+
return (syncAdapter(update, doc, options))
|
|
124
135
|
if !doc._id || !doc._id.startsWith(table.prefix) || !doc._rev
|
|
125
136
|
return (asyncResult(callback, Error("invalid argument")))
|
|
126
137
|
if makeID(doc.key) != doc._id
|
|
@@ -130,6 +141,8 @@ closure dapoTable(database, rootpath, local table, prefix) {
|
|
|
130
141
|
doc = new(doc)
|
|
131
142
|
doc._md5 = undefined
|
|
132
143
|
doc._length = undefined
|
|
144
|
+
if options.metadata
|
|
145
|
+
doc = merge(doc, options.metadata)
|
|
133
146
|
database.pouch.put(doc, function(error, resp) {
|
|
134
147
|
if error
|
|
135
148
|
error = Error("update failed", error)
|
|
@@ -252,6 +252,14 @@ closure MakePSMutil(fs, local util) {
|
|
|
252
252
|
// follow: <boolean> -- follow links instead of treating them as files
|
|
253
253
|
// idmode: <boolean> -- preserve id/mode where possible
|
|
254
254
|
// times: <boolean> -- preserve time where possible
|
|
255
|
+
// cacheControl: [
|
|
256
|
+
// { -- apply cacheControl to files matching regex
|
|
257
|
+
// match: <regex-string>
|
|
258
|
+
// flags: <regex-flags> -- default "i" ignore case
|
|
259
|
+
// cacheControl: <string>
|
|
260
|
+
// }
|
|
261
|
+
// ...
|
|
262
|
+
// ]
|
|
255
263
|
// }
|
|
256
264
|
//
|
|
257
265
|
// Limitations:
|
|
@@ -295,10 +303,13 @@ closure MakePSMutil(fs, local util) {
|
|
|
295
303
|
if destpath == "."
|
|
296
304
|
destpath = "" // joined empty strings
|
|
297
305
|
`(error, stat) = fs.info(srcpath, fsopt)
|
|
298
|
-
if stat.type
|
|
306
|
+
if stat.type == "symlink" && options.follow && !fs.dirList(srcpath).0
|
|
307
|
+
stat.type = "directory" // for our purposes this is a directory
|
|
308
|
+
if stat.type != "directory" {
|
|
299
309
|
copies.push({ // copy file or symlink
|
|
300
310
|
source: srcpath
|
|
301
311
|
dest: destpath
|
|
312
|
+
entry: entry
|
|
302
313
|
})
|
|
303
314
|
return (list(false, { ok: true })) // generic success not used
|
|
304
315
|
}
|
|
@@ -346,10 +357,29 @@ closure MakePSMutil(fs, local util) {
|
|
|
346
357
|
}).wait()
|
|
347
358
|
}
|
|
348
359
|
//
|
|
360
|
+
// Pass 3 - determine cache control values
|
|
361
|
+
//
|
|
362
|
+
let (regexes, item, file) {
|
|
363
|
+
regexes = new(options.cacheControl)
|
|
364
|
+
try {
|
|
365
|
+
for item in regexes
|
|
366
|
+
item.regex = RegExp(item.match, item.flags || "i")
|
|
367
|
+
} catch {
|
|
368
|
+
ErrorDebuglog("PsmUtil.deepcopy: RegExp exception", exception)
|
|
369
|
+
regexes = []
|
|
370
|
+
}
|
|
371
|
+
for file in copies
|
|
372
|
+
for item in regexes
|
|
373
|
+
if item.regex.test(file.entry) {
|
|
374
|
+
file.cacheControl = item.cacheControl
|
|
375
|
+
break
|
|
376
|
+
}
|
|
377
|
+
} ()
|
|
378
|
+
//
|
|
349
379
|
// Pass 3 - copy files and links
|
|
350
380
|
//
|
|
351
381
|
asyncArray(copies, 10, closure cpfiles(entry,
|
|
352
|
-
local error, data, dest, instat, outstat) {
|
|
382
|
+
local error, data, dest, instat, outstat, foptions) {
|
|
353
383
|
`(error, instat) = fs.info(entry.source, fsopt)
|
|
354
384
|
if !error {
|
|
355
385
|
`(error, outstat) = outfs.info(entry.dest)
|
|
@@ -372,9 +402,13 @@ closure MakePSMutil(fs, local util) {
|
|
|
372
402
|
`(error, data) = outfs.symlink(data, entry.dest)
|
|
373
403
|
}
|
|
374
404
|
else {
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
405
|
+
foptions = { encoding: "binary"}
|
|
406
|
+
`(error, data) = fs.readFile(entry.source, foptions)
|
|
407
|
+
if !error {
|
|
408
|
+
if entry.cacheControl
|
|
409
|
+
foptions.cacheControl = entry.cacheControl
|
|
410
|
+
`(error, data) = outfs.writeFile(entry.dest, data, foptions)
|
|
411
|
+
}
|
|
378
412
|
if !error && (options.idmode || options.times) {
|
|
379
413
|
if !options.idmode
|
|
380
414
|
instat.mode = instat.uid = instat.gid = false
|
|
@@ -185,6 +185,7 @@ closure FSview(table, rootpath, local view) {
|
|
|
185
185
|
// emptyok: true -- allow empty directory names (leading "/" or embdedded "//")
|
|
186
186
|
// md5: <hash> -- existing file content must match hash
|
|
187
187
|
// writenew: true -- write as a new file, with updated create time
|
|
188
|
+
// cacheControl: <string> -- set the cacheControl header
|
|
188
189
|
|
|
189
190
|
closure dbWriteRecord(path, content, options, callback) {
|
|
190
191
|
if !callback
|
|
@@ -247,9 +248,15 @@ closure FSview(table, rootpath, local view) {
|
|
|
247
248
|
path: path
|
|
248
249
|
})))
|
|
249
250
|
if doc
|
|
250
|
-
`(error, data) = view.table.update(doc
|
|
251
|
+
`(error, data) = view.table.update(doc, { // update existing record
|
|
252
|
+
metadata: metadata
|
|
253
|
+
cacheControl: options.cacheControl
|
|
254
|
+
})
|
|
251
255
|
else
|
|
252
|
-
`(error, data) = view.table.add(path, content,
|
|
256
|
+
`(error, data) = view.table.add(path, content, { // create new record
|
|
257
|
+
metadata: metadata
|
|
258
|
+
cacheControl: options.cacheControl
|
|
259
|
+
})
|
|
253
260
|
if error
|
|
254
261
|
callback(error)
|
|
255
262
|
else
|
|
@@ -553,6 +560,7 @@ closure FSview(table, rootpath, local view) {
|
|
|
553
560
|
// available:
|
|
554
561
|
// exclusive: true -- file must not already exist
|
|
555
562
|
// md5: <hash> -- existing file content must match hash
|
|
563
|
+
// cacheControl: <string> -- set the cacheControl header (if supported by underlying storage)
|
|
556
564
|
|
|
557
565
|
view.write = closure write(path, content, options, callback) {
|
|
558
566
|
if !callback
|