tileblaster 0.4.9 → 1.0.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/tileblaster.js ADDED
@@ -0,0 +1,445 @@
1
+ const http = require("node:http");
2
+ const path = require("node:path");
3
+ const fs = require("node:fs");
4
+ const os = require("node:os");
5
+
6
+ const decompress = require("./lib/decompress");
7
+ const retrieve = require("./lib/retrieve");
8
+ const router = require("./lib/router");
9
+ const strtpl = require("./lib/strtpl");
10
+ const debug = require("./lib/debug");
11
+ const store = require("./lib/store");
12
+ const tasks = require("./lib/tasks");
13
+ const load = require("./lib/load");
14
+ const mime = require("./lib/mime");
15
+
16
+ const quu = require("quu");
17
+ const sharp = load("sharp"); // optional dep
18
+
19
+ const tileblaster = module.exports = function tileblaster(config){
20
+ if (!(this instanceof tileblaster)) return new tileblaster(...arguments);
21
+ const self = this;
22
+
23
+ // configure
24
+ self.config = {};
25
+ self.configure(config);
26
+
27
+ // libraries (expose to plugins)
28
+ self.lib = { retrieve, debug, load, tasks, mime, store, strtpl, decompress, sharp };
29
+
30
+ // plugins
31
+ self.plugins = {};
32
+ self.loadPlugins(self.config.plugins);
33
+
34
+ // load builtins
35
+ self.builtins = {};
36
+ self.loadBuiltins([ "cors", "parse", "check", "noop", "tileserver", "versatiles", "pmtiles", "mbtiles", "edit", "compress", "cache", "deliver", "dump", "modernize", "optimize", "sharp" ]);
37
+
38
+ // assemble task lists for maps
39
+ self.maps = {};
40
+ self.prepareMaps();
41
+
42
+ self.queue = quu(self.config.queue);
43
+
44
+ // router
45
+ self.router = router({ mountpoint: self.config.server.mount });
46
+
47
+ // default route
48
+ self.router.default(function(req, res){
49
+ res.statusCode = 404;
50
+ res.setHeader("content-type", "text/plain");
51
+ res.end("not found.");
52
+ });
53
+
54
+ // index route
55
+ self.router.route("/", function(req, res){
56
+ res.statusCode = 200;
57
+ /*
58
+ res.setHeader("content-type", "text/plain");
59
+ res.end("tileblaster ready.");
60
+ */
61
+
62
+ // fancy index page
63
+ res.setHeader("content-type", "text/html");
64
+ res.end('<!doctype html><html lang="en"><head><meta charset="utf-8"><title>tileblaster</title><style>body{display: flex;height: 100vh;width: 100vw;margin: 0;padding: 0;justify-content: center;align-items: center;background: rgb(122,32,168);background: radial-gradient(circle, rgba(122,32,168,1) 8%, rgba(252,70,167,1) 100%);}svg{max-width: 90vw;max-height: 90vh;}</style></head><body><svg width="300" height="300" version="1.1" viewBox="0 50 150 150" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><linearGradient id="a" x1="400" x2="325" y1="468.69" y2="425.39" gradientTransform="matrix(3 2.359e-8 -2.359e-8 3 -800 -894.08)" gradientUnits="userSpaceOnUse"><stop stop-color="#8332ff" offset="0"/><stop stop-color="#e816ff" stop-opacity=".99492" offset="1"/></linearGradient><filter id="c" x="-.090127" y="-.084742" width="1.1803" height="1.1695" color-interpolation-filters="sRGB"><feGaussianBlur stdDeviation="3.6361132"/></filter><filter id="b" x="-.060783" y="-.070186" width="1.1216" height="1.1404" color-interpolation-filters="sRGB"><feGaussianBlur stdDeviation="11.396758"/></filter></defs><g transform="translate(179.54 1.2884)"><g transform="matrix(1.1362 0 0 1.1362 14.238 -16.848)"><path d="m-160.68 91.293-1e-5 64.834 56.148 32.417 56.148-32.417 1e-5 -64.834-56.148-32.417z" fill="#191919" stroke-linecap="round" stroke-width="1.1762"/><g transform="matrix(.22459 -.12967 .12967 .22459 -210.23 70.313)" fill="#31e2ff" filter="url(#b)" opacity=".12376" style="mix-blend-mode:normal"><path d="m137.5 187.23-112.5 194.85 112.5 194.86 150-4e-5 -37.5-64.952-75 5e-5 -75-129.9 37.5-64.951h75l-37.5 64.951 75 129.9 150 1e-5 75-129.9-75-129.9h-75l37.5-64.951-75-1e-5 -37.5 64.951-75 1e-5 37.5-64.951zm150 129.9 75-1e-5 37.5 64.951-37.5 64.951-75-2e-5 -37.5-64.951z" stroke-linecap="round" stroke-width="13.606"/><path d="m137.5 187.23-112.5 194.86 125-173.21 62.5-21.65zm150 0-37.5 64.951 50-43.301 62.5-21.648z" opacity=".18033"/><path d="m225 295.48-87.5 21.65h75zm250 86.604-87.5 108.25-137.5 21.65h150zm-200 173.21-137.5 21.65h150z" opacity=".18033"/></g><path d="m-154.03 95.135-2.6e-4 58.351 50.533 29.176 33.689-19.45-16.844-9.7252-16.844 9.7252-33.689-19.45 1e-4 -19.45 16.844-9.7252-1.1e-4 19.45 33.689 19.45 33.689-19.45 1.86e-4 -38.9-33.689-19.45-16.844 9.7252 9e-5 -19.45-16.844 9.7252-8e-5 19.45-16.845 9.7252 1.1e-4 -19.45zm50.533 9.7248 16.844-9.7252 16.844 9.725-8.8e-5 19.45-16.844 9.7252-16.844-9.725z" fill="#840577" fill-opacity=".39927" stroke-linecap="round" stroke-width="3.5286"/><g transform="matrix(.22459 -.12967 .12967 .22459 -210.23 70.313)"><path d="m137.5 187.23-112.5 194.85 112.5 194.86 150-4e-5 -37.5-64.952-75 5e-5 -75-129.9 37.5-64.951h75l-37.5 64.951 75 129.9 150 1e-5 75-129.9-75-129.9h-75l37.5-64.951-75-1e-5 -37.5 64.951-75 1e-5 37.5-64.951zm150 129.9 75-1e-5 37.5 64.951-37.5 64.951-75-2e-5 -37.5-64.951z" fill="url(#a)" stroke-linecap="round" stroke-width="13.606"/><path d="m137.5 187.23-112.5 194.86 125-173.21 62.5-21.65zm150 0-37.5 64.951 50-43.301 62.5-21.648z" fill="#fff" opacity=".18033"/><path d="m225 295.48-87.5 21.65h75zm250 86.604-87.5 108.25-137.5 21.65h150zm-200 173.21-137.5 21.65h150z" opacity=".18033"/></g><path transform="matrix(.98017 0 0 .98017 -2.0724 2.4526)" d="m-71.412 72.221-5.0958 2.9419v5.8849l-15.288 8.8263h-10.192l-10.193 5.8849-13.386 7.7282-1.2015 0.6935-0.70125 0.40462-25.481 14.711v17.654l5.0963 2.9425 10.192-5.8844 5.0963 2.9419v35.307l5.0958 2.9425 20.385-11.769-5.0963-2.9425v-29.422l10.193-5.8844v-5.8849l5.0958-2.9419v-5.8844l5.0963-2.9424v-5.8844l35.673-20.596-5.0963-2.9425v-5.8844l-5.0958 2.9419z" fill="#010101" filter="url(#c)" opacity=".57673" stroke-width=".23538" style="mix-blend-mode:normal"/><g transform="matrix(.1998 -.11536 .11536 .1998 -191.07 74.765)"><path d="m75 338.79 12.5 21.651-75 129.9 12.5 21.651h100l-12.5-21.651 87.5-151.55z" fill="#949fab"/><path d="m12.5 317.14 37.5-64.952h250l37.5 21.651-37.5 64.952h-275z" fill="#e6e9ed" stroke-linecap="round" stroke-width="4.5354"/><path d="m187.5 360.44h50l-12.5 21.651h-50z" fill="#ed5564" stroke-linecap="round" stroke-width="4.5354"/><path d="m300 252.18-25 43.301-12.5-21.651 12.5-21.651h-25l-25 43.301-11.862-20.546 11.862-22.756h-25l-25 43.301-12.351-21.393 12.352-21.908h100z" fill="#8994a2" stroke-linecap="round" stroke-width="4.5354"/><path d="m337.5 273.83-25 43.301 175-1e-5 -12.5-21.651 12.5-21.651z" fill="#949fab" stroke-linecap="round" stroke-width="4.5354"/><path d="m412.5 273.83 12.5-21.651h25l12.5 21.651h-50" fill="#8994a2" stroke-linecap="round" stroke-width="4.5354"/><path d="m200 338.79 75 1e-5 -12.5 21.651h-75z" fill="#8994a2" stroke-linecap="round" stroke-width="4.5354"/><path d="m12.5 317.14 37.5-64.952h250l-237.5 21.651z" fill="#fff" fill-opacity=".096774" stroke-linecap="round" stroke-width="4.5354"/><path d="m112.5 490.34-12.5-21.651 75-129.9h100l-87.5 21.651z" fill-opacity=".096774" stroke-linecap="round" stroke-width="4.5354"/><path d="m325 295.48 12.5-21.651h150z" fill="#fff" fill-opacity=".096774" stroke-linecap="round" stroke-width="4.5354"/><path d="m312.5 317.14 162.5-21.651 12.5 21.651z" fill-opacity=".096774" stroke-linecap="round" stroke-width="4.5354"/><path d="m337.5 273.83-62.5 64.952h25z" fill-opacity=".096774" stroke-linecap="round" stroke-width="4.5354"/><path d="m87.5 360.44-62.5 151.55-12.5-21.651z" fill="#fff" fill-opacity=".096774" stroke-linecap="round" stroke-width="4.5354"/><path d="m175 382.09h-25l12.5 21.651h-25l12.5 21.651h-25l12.5 21.651h-25l12.5 21.651z" fill-opacity=".13109" stroke-linecap="round" stroke-width="4.5354"/><path d="m175 382.09 12.5-21.651h50z" fill-opacity=".13109" stroke-linecap="round" stroke-width="4.5354"/><path d="m412.5 273.83 12.5-21.651h25z" fill="#fff" fill-opacity=".13109" stroke-linecap="round" stroke-width="4.5354"/></g></g></g></svg></body></html>');
65
+
66
+ });
67
+
68
+ // map use
69
+ self.router.use(function(req, res, next){
70
+ req.steps = (req.path) ? req.path.split("/").slice(1) : [];
71
+ if (self.config.maps.hasOwnProperty(req.steps[0])) {
72
+ req.route = "@map";
73
+ req.map = req.steps[0];
74
+ req.tilepath = "/"+req.steps.slice(1).join("/");
75
+ };
76
+ next();
77
+ });
78
+
79
+ // map route
80
+ self.router.route("@map", function(req, res){
81
+
82
+ // mark response stream if something pipes into it
83
+ // to avoid sending to response stream twice
84
+ res.once("pipe", function(){
85
+ res.piped = true;
86
+ });
87
+
88
+ // assemble args and initial data
89
+ const args = {
90
+ req, res,
91
+ data: {
92
+ map: req.map,
93
+ req: {},
94
+ tile: {
95
+ default: true,
96
+ // default tile
97
+ dest: req.path, // destination file - modification use extensions?
98
+ buffer: Buffer.alloc(0), // empty buffer
99
+ status: 204,
100
+ headers: {
101
+ "expires": new Date(Date.now()).toUTCString(),
102
+ }, // response header specific to tile
103
+ // explicit mimetye
104
+ mimetype: "application/octet-stream", // mimetype
105
+ type: "bin", // type
106
+ compression: false,
107
+ language: null,
108
+ expires: true, // instant expires
109
+ },
110
+ tiles: [], // modified versions
111
+ }
112
+ };
113
+
114
+ self.queue.push(function(next){ // queue tasks
115
+
116
+ // create tasks from map
117
+ tasks(self.maps[req.map]).run(args, function(err, { res }){
118
+
119
+ next(); // free queue
120
+
121
+ // FIXME provide more context
122
+ if (err) debug.error(err);
123
+
124
+ // end if response is already sent
125
+ if (!res.writable || res.destroyed || res.finished || res.closed || res.piped) return; // FIXME handle errors anyway
126
+
127
+ // send default error FIXME configure verbose errors
128
+ if (err) {
129
+ res.statusCode = 500;
130
+ res.setHeader("content-type", "text/plain");
131
+ res.end("Error.");
132
+ return;
133
+ };
134
+
135
+ // default response: no content
136
+ res.statusCode = 200; // FIXME 204
137
+ res.setHeader("content-length", "0");
138
+ res.end("");
139
+ return;
140
+
141
+ });
142
+
143
+ });
144
+
145
+ });
146
+
147
+ // listen
148
+ self.servers = [];
149
+ self.listen(self.router.serve);
150
+
151
+ // handle messages from main thred
152
+ process.on("message", function(message){
153
+ if (message === "shutdown") self.shutdown();
154
+ // FIXME: reload config without shutdown?
155
+ });
156
+
157
+ // shutdown on SIGINT
158
+ process.on("SIGINT", function(){
159
+ debug.info("SIGINT received");
160
+ self.shutdown();
161
+ });
162
+
163
+ // shutdown on SIGTERM
164
+ process.on("SIGTERM", function(){
165
+ debug.info("SIGTERM received");
166
+ self.shutdown();
167
+ });
168
+
169
+ return this;
170
+ };
171
+
172
+ // graceful shutdown
173
+ tileblaster.prototype.shutdown = function(){
174
+ const self = this;
175
+ self.shutdown = function(){}; // ensure shutdown only runs once
176
+ debug.info("shutting down");
177
+ let closed = 0;
178
+ self.servers.forEach(function(server){
179
+ server.close(function(){
180
+ if (++closed === self.servers.length) {
181
+ debug.info("All Servers Closed");
182
+ process.exit(0);
183
+ }
184
+ });
185
+ });
186
+ // watchdog
187
+ setTimeout(function(){
188
+ debug.warn("Closing servers timed out");
189
+ process.exit(1);
190
+ },3000);
191
+ };
192
+
193
+ // prepare jobs for maps
194
+ tileblaster.prototype.prepareMaps = function(){
195
+ const self = this;
196
+ self.maps = Object.entries(self.config.maps).reduce(function(maps, [ mapid, map ]){
197
+ maps[mapid] = self.prepareJobs(mapid, map);
198
+ return maps;
199
+ },{});
200
+
201
+ return this;
202
+ };
203
+
204
+ // prepare jobs FIXME merge with prepareMaps?
205
+ tileblaster.prototype.prepareJobs = function(mapid, map){
206
+ const self = this;
207
+ return map.map(function(job){
208
+
209
+ // job is a straight function
210
+ if (typeof job === "function") return job;
211
+
212
+ // if not an object, passthrough
213
+ if (typeof job !== "object") {
214
+ debug.warn("Invalid Task in map '%s'", mapid);
215
+ return function({}, next){ next(); };
216
+ };
217
+
218
+ // job is builtin
219
+ if (job.hasOwnProperty("builtin")) {
220
+ if (self.builtins.hasOwnProperty(job.builtin)) {
221
+ const fn = function(args, next, skip){
222
+ return self.builtins[job.builtin].call(self, { ...args, opts: job }, next, skip);
223
+ };
224
+ fn.label = job.builtin;
225
+ return fn;
226
+ } else {
227
+ // unknown plugin, pass through
228
+ debug.warn("Unknown builtin '%s' in map '%s'", job.builtin, mapid);
229
+ return function({}, next){ next(); };
230
+ };
231
+ };
232
+
233
+ // job is plugin
234
+ if (job.hasOwnProperty("plugin")) {
235
+ if (self.plugins.hasOwnProperty(job.plugin)) {
236
+ const fn = function(args, next, skip){
237
+ return self.plugins[job.plugin].call(self, { ...args, opts: job }, next, skip);
238
+ };
239
+ fn.label = "plugin:"+job.plugin;
240
+ return fn;
241
+ } else {
242
+ // unknown plugin, pass through
243
+ debug.warn("Unknown plugin '%s' in map '%s'", job.plugin, mapid);
244
+ return function({}, next){ next(); };
245
+ };
246
+ };
247
+
248
+ // unknown job type, passthrough with warning
249
+ debug.warn("Invalid Task type in map '%s'", reqmapid);
250
+ return function({}, next){ next(); };
251
+
252
+ });
253
+ };
254
+
255
+ // load builtins
256
+ tileblaster.prototype.loadBuiltins = function(builtins){
257
+ const self = this;
258
+ self.builtins = builtins.reduce(function(builtins, builtin){
259
+ let builtinPath = path.resolve(__dirname,"builtins",builtin);
260
+ if (load.exists(builtinPath)) {
261
+ debug.info("Loaded builtin '%s'", builtin);
262
+ builtins[builtin] = require(builtinPath);
263
+ } else {
264
+ debug.warn("Missing builtin: '%s'", builtin);
265
+ };
266
+ return builtins;
267
+ },{});
268
+ return this;
269
+ };
270
+
271
+ // load plugins
272
+ tileblaster.prototype.loadPlugins = function(){
273
+ const self = this;
274
+
275
+ self.plugins = Object.entries(self.config.plugins).reduce(function(plugins, [ name, plugin ]){
276
+
277
+ let pluginname = name.trim().toLowerCase().replace(/[^a-z0-9\-\_\.]+/g,'');
278
+ if (pluginname !== name) debug.warn("Warning: Plugin name has been sanitized: '%s' → '%s'", name, pluginname);
279
+
280
+ try {
281
+ if (load.exists(plugin)) {
282
+ plugins[pluginname] = require(plugin);
283
+ debug.info("Loaded Plugin '%s'", pluginname);
284
+ } else {
285
+ let localPlugin = path.resolve(self.config.paths.plugins, plugin);
286
+ if (load.exists(localPlugin)) {
287
+ plugins[pluginname] = require(localPlugin);
288
+ debug.info("Loaded Plugin '%s'", pluginname);
289
+ } else {
290
+ let packagePlugin = path.resolve(__dirname, "plugins", plugin);
291
+ if (load.exists(packagePlugin)) {
292
+ plugins[pluginname] = require(packagePlugin);
293
+ debug.info("Loaded Plugin '%s'", pluginname);
294
+ } else {
295
+ debug.warn("Error loading plugin '%s': Not found", pluginname);
296
+ }
297
+ }
298
+ }
299
+ } catch(err) {
300
+ debug.warn("Error loading plugin '%s':", pluginname, err);
301
+ }
302
+
303
+ return plugins;
304
+
305
+ },{});
306
+
307
+ return this;
308
+ };
309
+
310
+ // read config and fill in the blanks
311
+ tileblaster.prototype.configure = function(config){
312
+ const self = this;
313
+
314
+ // check config file version
315
+ if (!config.hasOwnProperty("version") || config.version !== 1) {
316
+ debug.error("Config has no version property. Possibly an old config file? Exiting.")
317
+ process.exit(1);
318
+ };
319
+
320
+ // check if any maps are configured
321
+ if (!config.hasOwnProperty("maps") || Object.keys(config.maps) === 0) {
322
+ debug.error("Config has no maps configured. Exiting.")
323
+ process.exit(1);
324
+ };
325
+
326
+ // check if any listen instructions are given
327
+ if (!config.hasOwnProperty("listen") || config.listen.length === 0) {
328
+ debug.error("Config has no listen instructions. Exiting.")
329
+ process.exit(1);
330
+ };
331
+
332
+ // set config
333
+ self.config = {
334
+ id: "tileblaster",
335
+ threads: 1,
336
+ queue: 12,
337
+ url: null,
338
+ mount: null,
339
+ paths: {},
340
+ plugins: {},
341
+ listen: [],
342
+ maps: {},
343
+ ...config,
344
+ };
345
+
346
+ // clamp number of threads to number of cores and queue size
347
+ self.config.threads = Math.max(1, Math.min(self.config.threads, os.cpus().length));
348
+ self.config.queue = Math.max(1, Math.min(self.config.queue, 100));
349
+
350
+ // warn user if queue size is less than 12 over all threads
351
+ if ((self.config.queue * self.config.threads) < 12) debug.warn("Warning: Queue size of %d is pretty small.", self.config.queue);
352
+
353
+ // url and mount
354
+ if (!self.config.server.url) self.config.server.url = "/";
355
+ if (!self.config.server.mount) self.config.server.mount = (self.config.server.url === "/") ? "/" : self.config.server.url.replace(/^https?:\/\/.*?\//,"/");
356
+
357
+ // remove trailing slashes from url and mount
358
+ while (self.config.server.url.length > 1 && self.config.server.url.charCodeAt(self.config.server.url.length-1) === 47) self.config.server.url = self.config.server.url.slice(0, -1);
359
+ while (self.config.server.mount.length > 1 && self.config.server.mount.charCodeAt(self.config.server.mount.length-1) === 47) self.config.server.mount = self.config.server.mount.slice(0, -1);
360
+
361
+ // paths
362
+ if (!self.config.paths.work) self.config.paths.work = path.resolve(os.homedir(), "tileblaster");
363
+ ["data","logs","plugins","sockets"].forEach(function(p){
364
+ if (!self.config.paths[p]) self.config.paths[p] = path.resolve(self.config.paths.work, p);
365
+ });
366
+
367
+ // default host to localhost if port is set
368
+ if (self.config.port && !self.config.host) self.config.host = "localhost";
369
+
370
+ // listen
371
+ self.config.listen = self.config.listen.filter(function(listen){
372
+ return listen.hasOwnProperty("port") || listen.hasOwnProperty("socket");
373
+ }).map(function(listen){
374
+ // ensure hostname is set
375
+ if (listen.hasOwnProperty("port")) {
376
+ if (typeof listen.port !== "number") listen.port = parseInt(listen.port,10);
377
+ if (!listen.host) listen.host = "localhost";
378
+ }
379
+ else if (listen.hasOwnProperty("socket")) {
380
+ listen.socket = path.resolve(self.config.paths.sockets, listen.socket);
381
+ };
382
+ return listen;
383
+ }).filter(function(listen){
384
+ // check for port and host types and NaN
385
+ return listen.socket || (!isNaN(listen.port) && typeof listen.host === "string");
386
+ });
387
+
388
+ // check again if any listen instructions are given
389
+ if (self.config.listen.length === 0) {
390
+ debug.error("Config has no valid listen instructions. Exiting.")
391
+ process.exit(1);
392
+ };
393
+
394
+ // maps
395
+ self.config.maps = Object.entries(config.maps).reduce(function(maps, [ id, map ]){
396
+ let mapid = id.trim().toLowerCase().replace(/[^a-z0-9\-\_\.]+/g,'');
397
+ if (mapid !== id) debug.warn("Warning: Map id has been sanitized: '%s' → '%s'", id, mapid);
398
+ maps[mapid] = map;
399
+ return maps;
400
+ },{});
401
+
402
+ return this;
403
+ };
404
+
405
+ // listen handler
406
+ tileblaster.prototype.listen = function(router){
407
+ const self = this;
408
+
409
+ self.config.listen.forEach(function(listen){
410
+
411
+ const server = http.createServer({ keepAlive: true }, function(){
412
+ router.call(self.router, ...arguments);
413
+ });
414
+
415
+ if (listen.port) {
416
+
417
+ server.listen(listen.port, listen.port, function(err){
418
+ if (err) return debug.error("listen: ERROR binding port '%s:%d':", listen.host, listen.port, err);
419
+ debug.info("Listening on '%s:%d'", listen.host, listen.port);
420
+ self.servers.push(server);
421
+ });
422
+
423
+ } else if (listen.socket) {
424
+
425
+ fs.unlink(listen.socket, function(err) { // try unlink leftover socket
426
+ if (err && err.code !== "ENOENT") return debug.error("Deleting socket '%s':", listen.socket, err);
427
+ server.listen(listen.socket, function(err) {
428
+ if (err) return debug.error("Binding to socket '%s':", listen.socket, err);
429
+ debug.info("Listening on socket '%s'", listen.socket);
430
+ self.servers.push(server);
431
+ if (listen.mode) fs.chmod(listen.socket, listen.mode, function(err){
432
+ if (err) return debug.error("Changing permissions of socket '%s' to '%s':", listen.socket, listen.perms.toString(8), err);
433
+ });
434
+ if (listen.group) fs.chown(listen.socket, os.userInfo().uid, listen.group, function(err){
435
+ if (err) return debug.error("Changing gid of socket '%s' to '%s':", listen.socket, listen.gid, err);
436
+ });
437
+ });
438
+ });
439
+
440
+ }
441
+
442
+ });
443
+
444
+ return self;
445
+ };
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- This is free and unencumbered software released into the public domain.
2
-
3
- Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
4
- software, either in source code form or as a compiled binary, for any purpose,
5
- commercial or non-commercial, and by any means.
6
-
7
- In jurisdictions that recognize copyright laws, the author or authors of this
8
- software dedicate any and all copyright interest in the software to the public
9
- domain. We make this dedication for the benefit of the public at large and to
10
- the detriment of our heirs and successors. We intend this dedication to be an
11
- overt act of relinquishment in perpetuity of all present and future rights to
12
- this software under copyright law.
13
-
14
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
- AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
18
- ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
19
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
-
21
- For more information, please refer to <https://unlicense.org/>
package/config.js.dist DELETED
@@ -1,81 +0,0 @@
1
- module.exports = {
2
-
3
- // path to socket file
4
- "socket": "/path/to/tileblaster.sock",
5
-
6
- // path to tile directory
7
- "tiles": "/path/to/tiles",
8
-
9
- // parallel request queue size
10
- "queue": 100,
11
-
12
- // parallel request queue size
13
- "id": "tileblaster",
14
-
15
- // provide errors as response headers
16
- "hints": false,
17
-
18
- // nsa heartbeat server, if so desired
19
- "heartbeat": "udp4://nsa.example.com:30826",
20
-
21
- // clean up every so often
22
- "cleanup": "15m",
23
-
24
- // maps
25
- "maps": {
26
-
27
- // map id, accessible via //server/mapid/z/x/y.ext
28
- "example": {
29
-
30
- // backend url:
31
- // * {s} subdomains specified in sub
32
- // * {z}, {x}, {y} tile coordinates
33
- // * {e} extension
34
- // * {r} resolution marker
35
- "url": "https://{s}.tiles.example.com/tiles/{z}/{x}/{y}{r}.{e}",
36
-
37
- // url points to versatiles container (→ https://github.com/versatiles-org/versatiles-spec)
38
- "versatiles": false,
39
-
40
- // backend uses tms instead of zxy
41
- "tms": false,
42
-
43
- // possible extensions
44
- "ext": ["mvt","json","topojson","png","jpg"],
45
-
46
- // valid zoom levels
47
- "zoom": [0,16],
48
-
49
- // valid subdomains
50
- "sub": ["a","b","c","d"],
51
-
52
- // bounding box, west, south, east, north
53
- "bbox": [-180,-90,180,90],
54
-
55
- // valid mimetypes from backend server
56
- "mime": ["application/vnd.mapbox-vector-tile","application/x-protobuf","application/json"],
57
-
58
- // valid resolution markers
59
- "res": ["@2x","@4x"],
60
-
61
- // optimize tiles
62
- // * .png with `optipng`
63
- // * .jpg with `mozjpeg`
64
- "optimize": true,
65
-
66
- // compress tiles (only makes sense for non-rastered tiles like json, pbf, mvt)
67
- "compress": [ "gz", "br" ],
68
-
69
- // write tiles to disk
70
- "cache": true,
71
-
72
- // minimum time tiles are kept
73
- "expires": "1d",
74
-
75
- },
76
-
77
- // more backends here
78
-
79
- },
80
-
81
- };