tileblaster 1.0.5 → 1.0.7
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/builtins/check.js +22 -8
- package/builtins/compress.js +4 -4
- package/builtins/cors.js +4 -1
- package/builtins/deliver.js +1 -0
- package/builtins/dump.js +0 -1
- package/builtins/edit.js +1 -1
- package/builtins/versatiles.js +14 -3
- package/config.dist.js +3 -1
- package/docs/config.md +3 -1
- package/docs/todo.md +3 -3
- package/lib/purge.js +1 -1
- package/lib/router.js +1 -1
- package/lib/store.js +1 -1
- package/package.json +1 -1
package/builtins/check.js
CHANGED
|
@@ -2,13 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
const cache = {};
|
|
4
4
|
|
|
5
|
-
module.exports = function({ req, res, opts, data }, next){
|
|
5
|
+
module.exports = function({ req, res, opts, data }, next, skip){
|
|
6
6
|
|
|
7
7
|
// fill cache for map
|
|
8
8
|
if (!cache.hasOwnProperty(data.map)) {
|
|
9
9
|
|
|
10
10
|
cache[data.map] = {};
|
|
11
11
|
|
|
12
|
+
// skip function
|
|
13
|
+
cache[data.map].abort = function(err){
|
|
14
|
+
if (res.used) return;
|
|
15
|
+
res.statusCode = cache[data.map].status;
|
|
16
|
+
if (cache[data.map].hints && err) res.setHeader("x-tileblaster-hint", err.message || err.toString());
|
|
17
|
+
res.end();
|
|
18
|
+
res.used = true; // mark connection as used
|
|
19
|
+
return skip(); // skip rest of jobs
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
cache[data.map].status = (opts.status && Number.isInteger(opts.status)) ? opts.status : 204;
|
|
23
|
+
|
|
24
|
+
cache[data.map].hints = !!opts.hints;
|
|
25
|
+
|
|
12
26
|
// assume reasonable default if no zoom level was specified
|
|
13
27
|
cache[data.map].zoom = (!opts.zoom || opts.zoom.length === 0) ? [ 0, 24 ] : opts.zoom;
|
|
14
28
|
|
|
@@ -86,28 +100,28 @@ module.exports = function({ req, res, opts, data }, next){
|
|
|
86
100
|
opts = cache[data.map];
|
|
87
101
|
|
|
88
102
|
// check for NaNs
|
|
89
|
-
if (isNaN(data.req.params.z) || isNaN(data.req.params.x) || isNaN(data.req.params.y)) return
|
|
103
|
+
if (isNaN(data.req.params.z) || isNaN(data.req.params.x) || isNaN(data.req.params.y)) return cache[data.map].abort(new Error("illegal zxy."));
|
|
90
104
|
|
|
91
105
|
// check zoom
|
|
92
|
-
if (data.req.params.z < opts.minZoom || data.req.params.z > opts.maxZoom) return
|
|
106
|
+
if (data.req.params.z < opts.minZoom || data.req.params.z > opts.maxZoom) return cache[data.map].abort(new Error("illegal zoom."));
|
|
93
107
|
|
|
94
108
|
// check bounds
|
|
95
109
|
if (opts.bounds) {
|
|
96
110
|
if (opts.bounds[data.req.params.z][0] < opts.bounds[data.req.params.z][2]) { // check for bounds spanning antimeridian
|
|
97
111
|
// bounds don't span antimeridian
|
|
98
|
-
if (data.req.params.x < opts.bounds[data.req.params.z][0] || data.req.params.x > opts.bounds[data.req.params.z][2]) return
|
|
112
|
+
if (data.req.params.x < opts.bounds[data.req.params.z][0] || data.req.params.x > opts.bounds[data.req.params.z][2]) return cache[data.map].abort(new Error("x is out of bounds."));
|
|
99
113
|
} else {
|
|
100
114
|
// bounds span antimeridian
|
|
101
|
-
if (data.req.params.x > opts.bounds[data.req.params.z][0] && data.req.params.x < opts.bounds[data.req.params.z][2]) return
|
|
115
|
+
if (data.req.params.x > opts.bounds[data.req.params.z][0] && data.req.params.x < opts.bounds[data.req.params.z][2]) return cache[data.map].abort(new Error("x is out of bounds, bounds span antimeridian"));
|
|
102
116
|
}
|
|
103
|
-
if (data.req.params.y < opts.bounds[data.req.params.z][1] || data.req.params.y > opts.bounds[data.req.params.z][3]) return
|
|
117
|
+
if (data.req.params.y < opts.bounds[data.req.params.z][1] || data.req.params.y > opts.bounds[data.req.params.z][3]) return cache[data.map].abort(new Error("y is out of bounds."));
|
|
104
118
|
}
|
|
105
119
|
|
|
106
120
|
// check extension
|
|
107
|
-
if (opts.extensions.length > 0 && !opts.extensions.includes(data.req.params.e) && !opts.extensions.includes(data.req.params.f)) return
|
|
121
|
+
if (opts.extensions.length > 0 && !opts.extensions.includes(data.req.params.e) && !opts.extensions.includes(data.req.params.f)) return cache[data.map].abort(new Error("illegal extension."));
|
|
108
122
|
|
|
109
123
|
// check density
|
|
110
|
-
if (opts.density && !opts.density.includes(data.req.params.d) && !opts.density.includes(data.req.params.f)) return
|
|
124
|
+
if (opts.density && !opts.density.includes(data.req.params.d) && !opts.density.includes(data.req.params.f)) return cache[data.map].abort(new Error("illegal density marker."));
|
|
111
125
|
|
|
112
126
|
// all passed
|
|
113
127
|
next();
|
package/builtins/compress.js
CHANGED
|
@@ -39,7 +39,7 @@ module.exports = function({ req, res, opts, data }, next){
|
|
|
39
39
|
if (opts.brotli) promises.push(new Promise(function(resolve, reject) {
|
|
40
40
|
brotli(tile.buffer, opts.brotli).then(function(compressed){
|
|
41
41
|
if (compressed.length > tile.buffer.length) {
|
|
42
|
-
debug.warn("Discarding useless Brotli compression for %s: +%db",
|
|
42
|
+
debug.warn("Discarding useless Brotli compression for %s: +%db", tile.path.magenta, compressed.length-tile.buffer.length);
|
|
43
43
|
return resolve();
|
|
44
44
|
};
|
|
45
45
|
data.tiles.push({
|
|
@@ -52,7 +52,7 @@ module.exports = function({ req, res, opts, data }, next){
|
|
|
52
52
|
});
|
|
53
53
|
resolve();
|
|
54
54
|
}).catch(function(err){
|
|
55
|
-
debug.error("Brotli failed for %s: %s",
|
|
55
|
+
debug.error("Brotli failed for %s: %s", tile.path.magenta, err);
|
|
56
56
|
reject(err);
|
|
57
57
|
});
|
|
58
58
|
}));
|
|
@@ -60,7 +60,7 @@ module.exports = function({ req, res, opts, data }, next){
|
|
|
60
60
|
if (opts.gzip) promises.push(new Promise(function(resolve, reject) {
|
|
61
61
|
gzip(tile.buffer, opts.gzip).then(function(compressed){
|
|
62
62
|
if (compressed.length > tile.buffer.length) {
|
|
63
|
-
debug.warn("Discarding useless Gzip compression for %s: +%db",
|
|
63
|
+
debug.warn("Discarding useless Gzip compression for %s: +%db", tile.path.magenta, compressed.length-tile.buffer.length);
|
|
64
64
|
return resolve();
|
|
65
65
|
}
|
|
66
66
|
data.tiles.push({
|
|
@@ -73,7 +73,7 @@ module.exports = function({ req, res, opts, data }, next){
|
|
|
73
73
|
});
|
|
74
74
|
resolve();
|
|
75
75
|
}).catch(function(err){
|
|
76
|
-
debug.error("Gzip failed for %s: %s",
|
|
76
|
+
debug.error("Gzip failed for %s: %s", tile.path.magenta, err);
|
|
77
77
|
reject(err);
|
|
78
78
|
});
|
|
79
79
|
}));
|
package/builtins/cors.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// send cors headers.
|
|
2
2
|
|
|
3
|
-
module.exports = function({ req, res, opts, data }, next){
|
|
3
|
+
module.exports = function({ req, res, opts, data }, next, skip){
|
|
4
4
|
|
|
5
5
|
// only if origin header is set
|
|
6
6
|
if (!req.headers.hasOwnProperty("origin") || !req.headers.origin) return next();
|
|
@@ -15,6 +15,7 @@ module.exports = function({ req, res, opts, data }, next){
|
|
|
15
15
|
if (!opts.origins.includes("*") && opts.origins.includes(req.headers.origin)) return next();
|
|
16
16
|
|
|
17
17
|
// send common headers
|
|
18
|
+
res.setHeader("Vary", "Origin"); // important for caching
|
|
18
19
|
res.setHeader("Access-Control-Allow-Origin", req.headers.origin||"*");
|
|
19
20
|
res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
|
|
20
21
|
res.setHeader("Access-Control-Allow-Headers", "DNT,If-None-Match,If-Modified-Since,Cache-Control,Content-Type,Range,Accept-Encoding");
|
|
@@ -27,6 +28,8 @@ module.exports = function({ req, res, opts, data }, next){
|
|
|
27
28
|
res.setHeader("Content-Length", 0);
|
|
28
29
|
res.statusCode = 204;
|
|
29
30
|
res.end();
|
|
31
|
+
res.used = true;
|
|
32
|
+
return skip();
|
|
30
33
|
};
|
|
31
34
|
|
|
32
35
|
next();
|
package/builtins/deliver.js
CHANGED
package/builtins/dump.js
CHANGED
package/builtins/edit.js
CHANGED
|
@@ -19,7 +19,7 @@ module.exports = function({ req, res, opts, data }, next){
|
|
|
19
19
|
return tile.type === "pbf";
|
|
20
20
|
}).forEach(function(tile){
|
|
21
21
|
try {
|
|
22
|
-
tile.buffer = vtt.pack(opts.edit(vtt.unpack(tile.buffer)));
|
|
22
|
+
tile.buffer = Buffer.from(vtt.pack(opts.edit(vtt.unpack(tile.buffer))));
|
|
23
23
|
} catch (err) {
|
|
24
24
|
debug.error("Editing vector tile failed:", err);
|
|
25
25
|
}
|
package/builtins/versatiles.js
CHANGED
|
@@ -4,7 +4,7 @@ const versatiles = load("versatiles");
|
|
|
4
4
|
const cache = {};
|
|
5
5
|
|
|
6
6
|
// versatiles backend
|
|
7
|
-
module.exports = function({ req, res, opts, data }, next){
|
|
7
|
+
module.exports = function({ req, res, opts, data }, next, skip){
|
|
8
8
|
const mime = this.lib.mime;
|
|
9
9
|
const debug = this.lib.debug;
|
|
10
10
|
|
|
@@ -16,12 +16,23 @@ module.exports = function({ req, res, opts, data }, next){
|
|
|
16
16
|
tms: (!!opts.tms),
|
|
17
17
|
headers: ((opts.hasOwnProperty("headers")) ? opts.headers : {}),
|
|
18
18
|
});
|
|
19
|
+
|
|
20
|
+
// skip function
|
|
21
|
+
cache[data.map].abort = function(err){
|
|
22
|
+
if (res.used) return;
|
|
23
|
+
res.statusCode = 204; // no content
|
|
24
|
+
res.setHeader("x-tileblaster-hint", err.message || err.toString());
|
|
25
|
+
res.end();
|
|
26
|
+
res.used = true; // mark connection as used
|
|
27
|
+
return skip(); // skip rest of jobs
|
|
28
|
+
};
|
|
29
|
+
|
|
19
30
|
};
|
|
20
31
|
const vt = cache[data.map];
|
|
21
32
|
|
|
22
33
|
debug.info("Fetching %s/%s/%s", data.req.params.z, data.req.params.x, data.req.params.y);
|
|
23
34
|
vt.getTile(data.req.params.z, data.req.params.x, data.req.params.y, function(err, buf){
|
|
24
|
-
if (err) return
|
|
35
|
+
if (err) return cache[data.map].abort(err); // fail gracefully
|
|
25
36
|
|
|
26
37
|
// if precompressed, keep in tile stack
|
|
27
38
|
/* TODO evaluate side effects
|
|
@@ -40,7 +51,7 @@ module.exports = function({ req, res, opts, data }, next){
|
|
|
40
51
|
|
|
41
52
|
// decompress tile
|
|
42
53
|
vt.decompress(vt.header.tile_precompression, buf, function(err, buf){
|
|
43
|
-
if (err) return
|
|
54
|
+
if (err) return cache[data.map].abort(err); // fail gracefully
|
|
44
55
|
|
|
45
56
|
const tile = {
|
|
46
57
|
buffer: buf,
|
package/config.dist.js
CHANGED
|
@@ -68,7 +68,9 @@ const config = module.exports = {
|
|
|
68
68
|
density: [ "", "@2x", "@3x" ], // allowed density markeers
|
|
69
69
|
check: function(params, fn) { // override check function, params from parse
|
|
70
70
|
fn(new Error("Check failed")); // deliver error if check failed
|
|
71
|
-
}
|
|
71
|
+
},
|
|
72
|
+
status: 204,
|
|
73
|
+
hints: true,
|
|
72
74
|
},{ // get from cache, skip to `skipto` if successful
|
|
73
75
|
builtin: "cache",
|
|
74
76
|
skipto: "deliver",
|
package/docs/config.md
CHANGED
|
@@ -151,7 +151,9 @@ within a bounding box, to specific extensions and densities, or use your own che
|
|
|
151
151
|
density: [ "", "@2x", "@3x" ], // allowed density markeers
|
|
152
152
|
check: function(params, fn) { // override check function, params from parse
|
|
153
153
|
fn(new Error("Check failed")); // deliver error if check failed
|
|
154
|
-
}
|
|
154
|
+
},
|
|
155
|
+
status: 204, // http status code delivered on fail; default: 204
|
|
156
|
+
hints: false, // send `x-tileblaster-hint` header with error message
|
|
155
157
|
}
|
|
156
158
|
```
|
|
157
159
|
|
package/docs/todo.md
CHANGED
|
@@ -70,9 +70,9 @@
|
|
|
70
70
|
* [ ] Map Web Interface, Configurable
|
|
71
71
|
* [ ] Configurable Index Page
|
|
72
72
|
* [ ] Support for Glyphs and Styles, tile.json proxy
|
|
73
|
-
* [
|
|
74
|
-
* [
|
|
75
|
-
* [
|
|
73
|
+
* [x] Better Error Handling
|
|
74
|
+
* [x] Error wrapper (to pass along http status etc)?
|
|
75
|
+
* [x] Skip for nonexistant tiles
|
|
76
76
|
* [ ] Render Vector Tiles with MapLibreGL-Native
|
|
77
77
|
* [ ] Improve Debug Consistency
|
|
78
78
|
* [ ] Documentation: How to write a plugin
|
package/lib/purge.js
CHANGED
|
@@ -87,7 +87,7 @@ if (worker.isMainThread) {
|
|
|
87
87
|
let deletable = [];
|
|
88
88
|
|
|
89
89
|
klaw(cache.dir).on("data", function(file){
|
|
90
|
-
if (file.stats.mtimeMs
|
|
90
|
+
if (file.stats.mtimeMs > cache.expires) deletable.push(file.path);
|
|
91
91
|
}).on("end", function(){
|
|
92
92
|
Promise.allSettled(deletable.map(function(file){
|
|
93
93
|
return new Promise(function(resolve,reject){
|
package/lib/router.js
CHANGED
|
@@ -14,7 +14,7 @@ const router = module.exports = function router({ mountpoint }) {
|
|
|
14
14
|
|
|
15
15
|
// default default route, very bare bones
|
|
16
16
|
self.routes[""] = function(req, res){
|
|
17
|
-
res.statusCode = 404, res.end();
|
|
17
|
+
res.statusCode = 404, res.end(), res.used = true;
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
return self;
|
package/lib/store.js
CHANGED
|
@@ -80,7 +80,7 @@ store.prototype.put = function(tile, fn){
|
|
|
80
80
|
const self = this;
|
|
81
81
|
|
|
82
82
|
const destfile = path.join(self.root, tile.path);
|
|
83
|
-
const tmpfile = destfile+self.ext;
|
|
83
|
+
const tmpfile = destfile+"."+Date.now()+self.ext;
|
|
84
84
|
|
|
85
85
|
// check if exists and still valid?
|
|
86
86
|
self.check(destfile, function(err, isValid){
|