styled-map-package 3.0.0 → 4.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/bin/smp-view.js CHANGED
@@ -1,11 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import fastifyStatic from '@fastify/static'
3
+ import { createServerAdapter } from '@whatwg-node/server'
3
4
  import { Command } from 'commander'
4
5
  import fastify from 'fastify'
5
6
  import openApp from 'open'
6
7
 
7
8
  import path from 'node:path'
8
9
 
10
+ import { Reader } from '../dist/reader.js'
9
11
  import { createServer } from '../dist/server.js'
10
12
 
11
13
  const program = new Command()
@@ -35,6 +37,12 @@ program.parseAsync(process.argv)
35
37
  * @returns
36
38
  */
37
39
  function serve({ port = 3000, filepath }) {
40
+ const reader = new Reader(path.relative(process.cwd(), filepath))
41
+ const smpServer = createServer({ base: '/map' })
42
+ const serverAdapter = createServerAdapter((request) => {
43
+ return smpServer.fetch(request, reader)
44
+ })
45
+
38
46
  const server = fastify()
39
47
 
40
48
  server.register(fastifyStatic, {
@@ -45,9 +53,12 @@ function serve({ port = 3000, filepath }) {
45
53
  return reply.sendFile('index.html')
46
54
  })
47
55
 
48
- server.register(createServer, {
49
- filepath: path.relative(process.cwd(), filepath),
50
- prefix: '/map',
56
+ server.route({
57
+ url: '/map/*',
58
+ method: ['GET', 'HEAD', 'OPTIONS'],
59
+ handler: (req, reply) => {
60
+ return serverAdapter.handleNodeRequestAndResponse(req, reply)
61
+ },
51
62
  })
52
63
  return server.listen({ port })
53
64
  }
package/dist/index.d.cts CHANGED
@@ -14,8 +14,8 @@ import 'type-fest';
14
14
  import 'readable-stream';
15
15
  import 'events';
16
16
  import 'yauzl-promise';
17
- import 'http';
18
- import 'fastify';
17
+ import 'itty-router';
18
+ import 'itty-router/IttyRouter';
19
19
  import 'ky';
20
20
  import './utils/geo.cjs';
21
21
  import './utils/fetch.cjs';
package/dist/index.d.ts CHANGED
@@ -14,8 +14,8 @@ import 'type-fest';
14
14
  import 'readable-stream';
15
15
  import 'events';
16
16
  import 'yauzl-promise';
17
- import 'http';
18
- import 'fastify';
17
+ import 'itty-router';
18
+ import 'itty-router/IttyRouter';
19
19
  import 'ky';
20
20
  import './utils/geo.js';
21
21
  import './utils/fetch.js';
package/dist/server.cjs CHANGED
@@ -1,9 +1,7 @@
1
1
  "use strict";
2
- var __create = Object.create;
3
2
  var __defProp = Object.defineProperty;
4
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
6
  var __export = (target, all) => {
9
7
  for (var name in all)
@@ -17,61 +15,63 @@ var __copyProps = (to, from, except, desc) => {
17
15
  }
18
16
  return to;
19
17
  };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
19
  var server_exports = {};
30
20
  __export(server_exports, {
31
21
  createServer: () => createServer
32
22
  });
33
23
  module.exports = __toCommonJS(server_exports);
34
- var import_http_errors = __toESM(require("http-errors"), 1);
35
- var import_reader = require('./reader.cjs');
24
+ var import_IttyRouter = require("itty-router/IttyRouter");
25
+ var import_StatusError = require("itty-router/StatusError");
26
+ var import_createResponse = require("itty-router/createResponse");
27
+ var import_node_stream = require("node:stream");
36
28
  var import_errors = require('./utils/errors.cjs');
37
- var import_misc = require('./utils/misc.cjs');
38
- function sendResource(reply, resource) {
39
- reply.type(resource.contentType).header("content-length", resource.contentLength);
29
+ function resourceResponse(resource, options = {}) {
30
+ const response = new Response(
31
+ // @ts-expect-error Some discrepancy between Typescript lib dom typings and @types/node typings
32
+ import_node_stream.Readable.toWeb(resource.stream),
33
+ options
34
+ );
35
+ response.headers.set("Content-Type", resource.contentType);
36
+ response.headers.set("Content-Length", resource.contentLength.toString());
40
37
  if (resource.contentEncoding) {
41
- reply.header("content-encoding", resource.contentEncoding);
38
+ response.headers.set("Content-Encoding", resource.contentEncoding);
42
39
  }
43
- return reply.send(resource.stream);
40
+ return response;
44
41
  }
45
- function createServer(fastify, opts, done) {
46
- const reader = "reader" in opts ? opts.reader : new import_reader.Reader(opts.filepath);
47
- if (!("reader" in opts)) {
48
- fastify.addHook("onClose", () => reader.close().catch(import_misc.noop));
49
- }
50
- fastify.get("/style.json", async () => {
51
- try {
52
- const baseUrl = new URL(fastify.prefix, fastify.listeningOrigin);
53
- const style = await reader.getStyle(baseUrl.href);
54
- return style;
55
- } catch (error) {
56
- if ((0, import_errors.isFileNotThereError)(error)) {
57
- throw (0, import_http_errors.default)(404, error.message);
58
- }
59
- throw error;
60
- }
42
+ const jsonRaw = (0, import_createResponse.createResponse)("application/json; charset=utf-8");
43
+ const encoder = new TextEncoder();
44
+ function json(obj) {
45
+ const data = encoder.encode(JSON.stringify(obj));
46
+ return jsonRaw(data, {
47
+ headers: { "Content-Length": data.length.toString() }
61
48
  });
62
- fastify.get("*", async (request, reply) => {
63
- const path = request.params["*"];
64
- try {
65
- const resource = await reader.getResource(path);
66
- return sendResource(reply, resource);
67
- } catch (error) {
68
- if ((0, import_errors.isFileNotThereError)(error)) {
69
- throw (0, import_http_errors.default)(404, error.message);
70
- }
71
- throw error;
72
- }
49
+ }
50
+ function createServer({ base = "/" } = {}) {
51
+ base = base.endsWith("/") ? base : base + "/";
52
+ const router = (0, import_IttyRouter.IttyRouter)({
53
+ base
54
+ }).get("/style.json", async (request, reader) => {
55
+ const baseUrl = new URL(base, request.url);
56
+ const style = await reader.getStyle(baseUrl.href);
57
+ return json(style);
58
+ }).get("*", async (request, reader) => {
59
+ const url = new URL(request.url);
60
+ const path = decodeURIComponent(url.pathname.slice(base.length - 1));
61
+ const resource = await reader.getResource(path);
62
+ return resourceResponse(resource);
73
63
  });
74
- done();
64
+ return {
65
+ fetch: (request, reader) => {
66
+ return router.fetch(request, reader).catch((err) => {
67
+ if ((0, import_errors.isFileNotThereError)(err)) {
68
+ throw new import_StatusError.StatusError(404, "Not Found");
69
+ } else {
70
+ throw err;
71
+ }
72
+ });
73
+ }
74
+ };
75
75
  }
76
76
  // Annotate the CommonJS export names for ESM import in node:
77
77
  0 && (module.exports = {
package/dist/server.d.cts CHANGED
@@ -1,6 +1,6 @@
1
- import * as http from 'http';
2
- import * as fastify from 'fastify';
1
+ import { RequestLike } from 'itty-router';
3
2
  import { Reader } from './reader.cjs';
3
+ import { IttyRouter } from 'itty-router/IttyRouter';
4
4
  import 'stream';
5
5
  import './types-B4Xn1F9K.cjs';
6
6
  import '@maplibre/maplibre-gl-style-spec';
@@ -10,28 +10,39 @@ import 'readable-stream';
10
10
  import 'events';
11
11
  import 'yauzl-promise';
12
12
 
13
- declare function createServer(instance: fastify.FastifyInstance<fastify.RawServerDefault, http.IncomingMessage, http.ServerResponse<http.IncomingMessage>, fastify.FastifyBaseLogger, fastify.FastifyTypeProviderDefault>, opts: PluginOptions, done: (err
14
13
  /**
15
- * Fastify plugin for serving a styled map package.
14
+ * Create a server for serving styled map packages (SMP) over http. The server
15
+ * is a `fetch` handler that must be provided a WHATWG `Request` and a SMP
16
+ * `Reader` instance. Use `@whatwg-node/server` to use with Node.js HTTP server.
16
17
  *
17
- * If you provide a `Reader` (or `ReaderWatch`) instance via the `reader` opt,
18
- * you must manually close the instance yourself.
18
+ * To handle errors, catch errors from `fetch` and return appropriate HTTP responses.
19
+ * You can use `itty-router/error` for this.
19
20
  *
20
- * @type {FastifyPluginCallback<PluginOptions>}
21
+ * @example
22
+ * ```js
23
+ * import { createServer } from 'node:http'
24
+ * import { error } from 'itty-router/error'
25
+ * import { createServerAdapter } from '@whatwg-node/server'
26
+ * import { createServer as createSMPServer } from 'styled-map-package/server'
27
+ * import { Reader } from 'styled-map-package/reader'
28
+ *
29
+ * const reader = new Reader('path/to/your-style.smp')
30
+ * const smpServer = createSMPServer()
31
+ * const httpServer = createServer(createServerAdapter((request) => {
32
+ * return smpServer.fetch(request, reader).catch(error)
33
+ * }))
34
+ * ```
35
+ *
36
+ * @param {object} [options]
37
+ * @param {string} [options.base='/'] Base path for the server routes
38
+ * @returns {{ fetch: (request: RequestLike, reader: ReaderLike) => Promise<Response> }} server instance
21
39
  */
22
- ?: Error) => void): void;
23
- type PluginOptionsFilepath = {
24
- /**
25
- * Path to styled map package (`.smp`) file
26
- */
27
- filepath: string;
28
- };
29
- type PluginOptionsReader = {
30
- /**
31
- * SMP Reader interface (also supports ReaderWatch)
32
- */
33
- reader: Pick<Reader, keyof Reader>;
40
+ declare function createServer({ base }?: {
41
+ base?: string | undefined;
42
+ }): {
43
+ fetch: (request: RequestLike, reader: ReaderLike) => Promise<Response>;
34
44
  };
35
- type PluginOptions = PluginOptionsFilepath | PluginOptionsReader;
45
+ type ReaderLike = Pick<Reader, keyof Reader>;
46
+ type RouterType = typeof IttyRouter<IRequestStrict, [ReaderLike], Response>;
36
47
 
37
- export { type PluginOptions, type PluginOptionsFilepath, type PluginOptionsReader, createServer };
48
+ export { type ReaderLike, type RouterType, createServer };
package/dist/server.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import * as http from 'http';
2
- import * as fastify from 'fastify';
1
+ import { RequestLike } from 'itty-router';
3
2
  import { Reader } from './reader.js';
3
+ import { IttyRouter } from 'itty-router/IttyRouter';
4
4
  import 'stream';
5
5
  import './types-B4Xn1F9K.js';
6
6
  import '@maplibre/maplibre-gl-style-spec';
@@ -10,28 +10,39 @@ import 'readable-stream';
10
10
  import 'events';
11
11
  import 'yauzl-promise';
12
12
 
13
- declare function createServer(instance: fastify.FastifyInstance<fastify.RawServerDefault, http.IncomingMessage, http.ServerResponse<http.IncomingMessage>, fastify.FastifyBaseLogger, fastify.FastifyTypeProviderDefault>, opts: PluginOptions, done: (err
14
13
  /**
15
- * Fastify plugin for serving a styled map package.
14
+ * Create a server for serving styled map packages (SMP) over http. The server
15
+ * is a `fetch` handler that must be provided a WHATWG `Request` and a SMP
16
+ * `Reader` instance. Use `@whatwg-node/server` to use with Node.js HTTP server.
16
17
  *
17
- * If you provide a `Reader` (or `ReaderWatch`) instance via the `reader` opt,
18
- * you must manually close the instance yourself.
18
+ * To handle errors, catch errors from `fetch` and return appropriate HTTP responses.
19
+ * You can use `itty-router/error` for this.
19
20
  *
20
- * @type {FastifyPluginCallback<PluginOptions>}
21
+ * @example
22
+ * ```js
23
+ * import { createServer } from 'node:http'
24
+ * import { error } from 'itty-router/error'
25
+ * import { createServerAdapter } from '@whatwg-node/server'
26
+ * import { createServer as createSMPServer } from 'styled-map-package/server'
27
+ * import { Reader } from 'styled-map-package/reader'
28
+ *
29
+ * const reader = new Reader('path/to/your-style.smp')
30
+ * const smpServer = createSMPServer()
31
+ * const httpServer = createServer(createServerAdapter((request) => {
32
+ * return smpServer.fetch(request, reader).catch(error)
33
+ * }))
34
+ * ```
35
+ *
36
+ * @param {object} [options]
37
+ * @param {string} [options.base='/'] Base path for the server routes
38
+ * @returns {{ fetch: (request: RequestLike, reader: ReaderLike) => Promise<Response> }} server instance
21
39
  */
22
- ?: Error) => void): void;
23
- type PluginOptionsFilepath = {
24
- /**
25
- * Path to styled map package (`.smp`) file
26
- */
27
- filepath: string;
28
- };
29
- type PluginOptionsReader = {
30
- /**
31
- * SMP Reader interface (also supports ReaderWatch)
32
- */
33
- reader: Pick<Reader, keyof Reader>;
40
+ declare function createServer({ base }?: {
41
+ base?: string | undefined;
42
+ }): {
43
+ fetch: (request: RequestLike, reader: ReaderLike) => Promise<Response>;
34
44
  };
35
- type PluginOptions = PluginOptionsFilepath | PluginOptionsReader;
45
+ type ReaderLike = Pick<Reader, keyof Reader>;
46
+ type RouterType = typeof IttyRouter<IRequestStrict, [ReaderLike], Response>;
36
47
 
37
- export { type PluginOptions, type PluginOptionsFilepath, type PluginOptionsReader, createServer };
48
+ export { type ReaderLike, type RouterType, createServer };
package/dist/server.js CHANGED
@@ -1,44 +1,54 @@
1
- import createError from "http-errors";
2
- import { Reader } from "./reader.js";
1
+ import { IttyRouter } from "itty-router/IttyRouter";
2
+ import { StatusError } from "itty-router/StatusError";
3
+ import { createResponse } from "itty-router/createResponse";
4
+ import { Readable } from "node:stream";
3
5
  import { isFileNotThereError } from "./utils/errors.js";
4
- import { noop } from "./utils/misc.js";
5
- function sendResource(reply, resource) {
6
- reply.type(resource.contentType).header("content-length", resource.contentLength);
6
+ function resourceResponse(resource, options = {}) {
7
+ const response = new Response(
8
+ // @ts-expect-error Some discrepancy between Typescript lib dom typings and @types/node typings
9
+ Readable.toWeb(resource.stream),
10
+ options
11
+ );
12
+ response.headers.set("Content-Type", resource.contentType);
13
+ response.headers.set("Content-Length", resource.contentLength.toString());
7
14
  if (resource.contentEncoding) {
8
- reply.header("content-encoding", resource.contentEncoding);
15
+ response.headers.set("Content-Encoding", resource.contentEncoding);
9
16
  }
10
- return reply.send(resource.stream);
17
+ return response;
11
18
  }
12
- function createServer(fastify, opts, done) {
13
- const reader = "reader" in opts ? opts.reader : new Reader(opts.filepath);
14
- if (!("reader" in opts)) {
15
- fastify.addHook("onClose", () => reader.close().catch(noop));
16
- }
17
- fastify.get("/style.json", async () => {
18
- try {
19
- const baseUrl = new URL(fastify.prefix, fastify.listeningOrigin);
20
- const style = await reader.getStyle(baseUrl.href);
21
- return style;
22
- } catch (error) {
23
- if (isFileNotThereError(error)) {
24
- throw createError(404, error.message);
25
- }
26
- throw error;
27
- }
19
+ const jsonRaw = createResponse("application/json; charset=utf-8");
20
+ const encoder = new TextEncoder();
21
+ function json(obj) {
22
+ const data = encoder.encode(JSON.stringify(obj));
23
+ return jsonRaw(data, {
24
+ headers: { "Content-Length": data.length.toString() }
28
25
  });
29
- fastify.get("*", async (request, reply) => {
30
- const path = request.params["*"];
31
- try {
32
- const resource = await reader.getResource(path);
33
- return sendResource(reply, resource);
34
- } catch (error) {
35
- if (isFileNotThereError(error)) {
36
- throw createError(404, error.message);
37
- }
38
- throw error;
39
- }
26
+ }
27
+ function createServer({ base = "/" } = {}) {
28
+ base = base.endsWith("/") ? base : base + "/";
29
+ const router = IttyRouter({
30
+ base
31
+ }).get("/style.json", async (request, reader) => {
32
+ const baseUrl = new URL(base, request.url);
33
+ const style = await reader.getStyle(baseUrl.href);
34
+ return json(style);
35
+ }).get("*", async (request, reader) => {
36
+ const url = new URL(request.url);
37
+ const path = decodeURIComponent(url.pathname.slice(base.length - 1));
38
+ const resource = await reader.getResource(path);
39
+ return resourceResponse(resource);
40
40
  });
41
- done();
41
+ return {
42
+ fetch: (request, reader) => {
43
+ return router.fetch(request, reader).catch((err) => {
44
+ if (isFileNotThereError(err)) {
45
+ throw new StatusError(404, "Not Found");
46
+ } else {
47
+ throw err;
48
+ }
49
+ });
50
+ }
51
+ };
42
52
  }
43
53
  export {
44
54
  createServer
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "styled-map-package",
3
- "version": "3.0.0",
4
- "description": "",
3
+ "version": "4.0.0",
4
+ "description": "A Styled Map Package (`.smp`) file is a Zip archive containing all the resources needed to serve a Maplibre vector styled map offline. This includes the style JSON, vector and raster tiles, glyphs (fonts), the sprite image, and the sprite metadata.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
7
7
  "module": "./dist/index.js",
@@ -100,8 +100,8 @@
100
100
  "./package.json": "./package.json"
101
101
  },
102
102
  "bin": {
103
- "styled-map-package": "./bin/smp.js",
104
- "smp": "./bin/smp.js"
103
+ "styled-map-package": "bin/smp.js",
104
+ "smp": "bin/smp.js"
105
105
  },
106
106
  "files": [
107
107
  "bin",
@@ -121,23 +121,24 @@
121
121
  "license": "MIT",
122
122
  "dependencies": {
123
123
  "@commander-js/extra-typings": "^12.1.0",
124
- "@fastify/static": "^7.0.4",
124
+ "@fastify/static": "^8.3.0",
125
125
  "@inquirer/prompts": "^6.0.1",
126
126
  "@mapbox/sphericalmercator": "^1.2.0",
127
127
  "@maplibre/maplibre-gl-style-spec": "^20.3.1",
128
128
  "@placemarkio/check-geojson": "^0.1.12",
129
129
  "@turf/bbox": "^7.2.0",
130
130
  "@turf/helpers": "^7.2.0",
131
+ "@whatwg-node/server": "^0.10.17",
131
132
  "ansi-diff": "^1.2.0",
132
133
  "archiver": "^7.0.1",
133
134
  "buffer-peek-stream": "^1.1.0",
134
135
  "chalk": "^5.4.1",
135
136
  "commander": "^12.1.0",
136
- "fastify": "^4.28.1",
137
+ "fastify": "^5.6.2",
137
138
  "filter-obj": "^6.1.0",
138
- "http-errors": "^2.0.0",
139
139
  "into-stream": "^8.0.1",
140
140
  "is-stream": "^4.0.1",
141
+ "itty-router": "^5.0.22",
141
142
  "ky": "^1.7.5",
142
143
  "log-symbols": "^7.0.0",
143
144
  "map-obj": "^5.0.2",
@@ -163,7 +164,7 @@
163
164
  "@types/geojson": "^7946.0.16",
164
165
  "@types/http-errors": "^2.0.4",
165
166
  "@types/mapbox__sphericalmercator": "^1.2.3",
166
- "@types/node": "^20.16.3",
167
+ "@types/node": "^18.19.130",
167
168
  "@types/readable-stream": "^4.0.18",
168
169
  "@types/yauzl-promise": "^4.0.1",
169
170
  "ava": "^6.2.0",
@@ -183,7 +184,7 @@
183
184
  "tempy": "^3.1.0",
184
185
  "tsup": "^8.4.0",
185
186
  "type-fest": "^4.35.0",
186
- "typescript": "^5.7.3"
187
+ "typescript": "^5.9.3"
187
188
  },
188
189
  "prettier": {
189
190
  "semi": false,
@@ -202,5 +203,16 @@
202
203
  "eslint --cache --fix"
203
204
  ],
204
205
  "*.{js,css,md}": "prettier --write"
206
+ },
207
+ "directories": {
208
+ "lib": "lib",
209
+ "test": "test"
210
+ },
211
+ "repository": {
212
+ "type": "git",
213
+ "url": "git+https://github.com/digidem/styled-map-package.git"
214
+ },
215
+ "bugs": {
216
+ "url": "https://github.com/digidem/styled-map-package/issues"
205
217
  }
206
218
  }