styled-map-package-api 5.0.0-pre.0 → 5.0.0-pre.2

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/README.md ADDED
@@ -0,0 +1,97 @@
1
+ # styled-map-package-api
2
+
3
+ JavaScript API for reading, writing, and serving Styled Map Package (`.smp`) files. Works in both Node.js and browsers.
4
+
5
+ An `.smp` file is a ZIP archive containing all the resources needed to serve a MapLibre vector styled map offline: style JSON, vector and raster tiles, glyphs (fonts), sprites, and metadata.
6
+
7
+ ## Installation
8
+
9
+ ```sh
10
+ npm install styled-map-package-api
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ### Reading an SMP file
16
+
17
+ ```js
18
+ import { Reader } from 'styled-map-package-api/reader'
19
+
20
+ const reader = new Reader('path/to/map.smp')
21
+ const style = await reader.getStyle()
22
+ ```
23
+
24
+ ### Writing an SMP file
25
+
26
+ ```js
27
+ import { Writer } from 'styled-map-package-api/writer'
28
+
29
+ const writer = new Writer(style, sources)
30
+ const stream = writer.outputStream
31
+ // Pipe stream to a file or other writable destination
32
+ await writer.finish()
33
+ ```
34
+
35
+ ### Serving over HTTP
36
+
37
+ ```js
38
+ import { Reader } from 'styled-map-package-api/reader'
39
+ import { createServer } from 'styled-map-package-api/server'
40
+
41
+ const reader = new Reader('path/to/map.smp')
42
+ const server = createServer()
43
+ // server.fetch(request, reader) returns a WHATWG Response
44
+ ```
45
+
46
+ ### Downloading a map for offline use
47
+
48
+ ```js
49
+ import { download } from 'styled-map-package-api/download'
50
+
51
+ const stream = download({
52
+ styleUrl: 'https://demotiles.maplibre.org/style.json',
53
+ bbox: [-180, -80, 180, 80],
54
+ maxzoom: 5,
55
+ })
56
+ // Pipe the ReadableStream to a file
57
+ ```
58
+
59
+ ### Converting from MBTiles
60
+
61
+ > **Note:** MBTiles conversion requires Node >= 20 (uses `better-sqlite3` which dropped Node 18 support).
62
+
63
+ ```js
64
+ import { fromMBTiles } from 'styled-map-package-api/from-mbtiles'
65
+
66
+ // From a file path (Node.js)
67
+ const stream = fromMBTiles('path/to/tiles.mbtiles')
68
+
69
+ // From an ArrayBuffer or Uint8Array (Node.js and browsers)
70
+ const stream = fromMBTiles(buffer)
71
+
72
+ // Pipe the ReadableStream to an .smp file
73
+ ```
74
+
75
+ ## API
76
+
77
+ ### Exports
78
+
79
+ | Export path | Description |
80
+ | ----------------------------------------- | ----------------------------------------------------------------- |
81
+ | `styled-map-package-api` | Main entry — `Reader`, `Writer`, `createServer`, `download`, etc. |
82
+ | `styled-map-package-api/reader` | `Reader` class for reading `.smp` files |
83
+ | `styled-map-package-api/writer` | `Writer` class for creating `.smp` files |
84
+ | `styled-map-package-api/server` | `createServer()` — HTTP handler using WHATWG Request/Response |
85
+ | `styled-map-package-api/download` | `download()` — download an online map style for offline use |
86
+ | `styled-map-package-api/style-downloader` | `StyleDownloader` — downloads styles, sprites, and glyphs |
87
+ | `styled-map-package-api/tile-downloader` | `downloadTiles()` — downloads tile data |
88
+ | `styled-map-package-api/from-mbtiles` | `fromMBTiles()` — convert MBTiles to SMP stream |
89
+ | `styled-map-package-api/utils/mapbox` | Mapbox URL detection and API utilities |
90
+
91
+ ### Browser support
92
+
93
+ All stream APIs use WHATWG `ReadableStream`, making the library compatible with both Node.js and browser environments. The `Reader` class accepts either a file path (Node.js) or a `ZipReader` instance (browser).
94
+
95
+ ## License
96
+
97
+ MIT
@@ -1,6 +1,6 @@
1
1
  import { GlyphDownloadStats } from './style-downloader.cjs';
2
2
  import { TileDownloadStats } from './tile-downloader.cjs';
3
- import { D as DownloadStream } from './types-CJq90eOB.cjs';
3
+ import { D as DownloadStream } from './types-qfyJk4ot.cjs';
4
4
  import { BBox } from './utils/geo.cjs';
5
5
  import 'ky';
6
6
  import '@maplibre/maplibre-gl-style-spec';
@@ -9,7 +9,6 @@ import './utils/streams.cjs';
9
9
  import 'stream/web';
10
10
  import 'geojson';
11
11
  import 'type-fest';
12
- import 'events';
13
12
 
14
13
  /**
15
14
  * @typedef {object} DownloadProgress
@@ -1,6 +1,6 @@
1
1
  import { GlyphDownloadStats } from './style-downloader.js';
2
2
  import { TileDownloadStats } from './tile-downloader.js';
3
- import { D as DownloadStream } from './types-CJq90eOB.js';
3
+ import { D as DownloadStream } from './types-qfyJk4ot.js';
4
4
  import { BBox } from './utils/geo.js';
5
5
  import 'ky';
6
6
  import '@maplibre/maplibre-gl-style-spec';
@@ -9,7 +9,6 @@ import './utils/streams.js';
9
9
  import 'stream/web';
10
10
  import 'geojson';
11
11
  import 'type-fest';
12
- import 'events';
13
12
 
14
13
  /**
15
14
  * @typedef {object} DownloadProgress
@@ -25,50 +25,77 @@ var import_mbtiles_reader = require("mbtiles-reader");
25
25
  var import_streams = require('./utils/streams.cjs');
26
26
  var import_writer = require('./writer.cjs');
27
27
  const SOURCE_ID = "mbtiles-source";
28
- function fromMBTiles(mbtilesPath) {
29
- const reader = new import_mbtiles_reader.MBTiles(mbtilesPath);
30
- if (reader.metadata.format === "pbf") {
31
- throw new Error("Vector MBTiles are not yet supported");
32
- }
33
- const style = {
34
- version: 8,
35
- name: reader.metadata.name,
36
- sources: {
37
- [SOURCE_ID]: {
38
- ...reader.metadata,
39
- type: "raster"
28
+ function fromMBTiles(source) {
29
+ let outputReader;
30
+ let conversionDone;
31
+ const pipeAbort = new AbortController();
32
+ return new ReadableStream({
33
+ async start() {
34
+ const reader = await import_mbtiles_reader.MBTiles.open(source);
35
+ if (reader.metadata.format === "pbf") {
36
+ throw new Error("Vector MBTiles are not yet supported");
40
37
  }
41
- },
42
- layers: [
43
- {
44
- id: "background",
45
- type: "background",
46
- paint: {
47
- "background-color": "white"
48
- }
49
- },
50
- {
51
- id: "raster",
52
- type: "raster",
53
- source: SOURCE_ID,
54
- paint: {
55
- "raster-opacity": 1
38
+ const style = {
39
+ version: 8,
40
+ name: reader.metadata.name,
41
+ sources: {
42
+ [SOURCE_ID]: {
43
+ ...reader.metadata,
44
+ type: "raster"
45
+ }
46
+ },
47
+ layers: [
48
+ {
49
+ id: "background",
50
+ type: "background",
51
+ paint: {
52
+ "background-color": "white"
53
+ }
54
+ },
55
+ {
56
+ id: "raster",
57
+ type: "raster",
58
+ source: SOURCE_ID,
59
+ paint: {
60
+ "raster-opacity": 1
61
+ }
62
+ }
63
+ ]
64
+ };
65
+ const writer = new import_writer.Writer(style);
66
+ outputReader = writer.outputStream.getReader();
67
+ conversionDone = (async () => {
68
+ try {
69
+ await (0, import_streams.readableFromAsync)(mbtilesToTileArgs(reader)).pipeTo(
70
+ writer.createTileWriteStream(),
71
+ { signal: pipeAbort.signal }
72
+ );
73
+ writer.finish();
74
+ } catch (err) {
75
+ try {
76
+ writer.abort(err instanceof Error ? err : new Error(String(err)));
77
+ } catch {
78
+ }
56
79
  }
80
+ })();
81
+ },
82
+ async pull(controller) {
83
+ const { done, value } = await /** @type {ReadableStreamDefaultReader<Uint8Array>} */
84
+ outputReader.read();
85
+ if (done) {
86
+ controller.close();
87
+ } else {
88
+ controller.enqueue(value);
57
89
  }
58
- ]
59
- };
60
- const writer = new import_writer.Writer(style);
61
- (async () => {
62
- try {
63
- await (0, import_streams.readableFromAsync)(mbtilesToTileArgs(reader)).pipeTo(
64
- writer.createTileWriteStream()
65
- );
66
- writer.finish();
67
- } catch (err) {
68
- writer.abort(err instanceof Error ? err : new Error(String(err)));
90
+ },
91
+ async cancel(reason) {
92
+ pipeAbort.abort(reason);
93
+ await conversionDone;
94
+ await /** @type {ReadableStreamDefaultReader<Uint8Array>} */
95
+ outputReader.cancel(reason).catch(() => {
96
+ });
69
97
  }
70
- })();
71
- return writer.outputStream;
98
+ });
72
99
  }
73
100
  async function* mbtilesToTileArgs(mbtiles) {
74
101
  for (const { z, x, y, data, format } of mbtiles) {
@@ -1,10 +1,14 @@
1
1
  /**
2
2
  * Convert a MBTiles file to a styled map package, returned as a web
3
- * ReadableStream.
3
+ * ReadableStream. The async MBTiles.open() happens lazily inside the
4
+ * stream's start(), so this function is synchronous.
4
5
  *
5
- * @param {string} mbtilesPath
6
+ * Requires Node >= 20 (uses better-sqlite3 which dropped Node 18 support).
7
+ *
8
+ * @param {string | ArrayBuffer | Uint8Array} source MBTiles source — file path
9
+ * (Node), OPFS path (browser Worker), or in-memory buffer.
6
10
  * @returns {ReadableStream<Uint8Array>}
7
11
  */
8
- declare function fromMBTiles(mbtilesPath: string): ReadableStream<Uint8Array>;
12
+ declare function fromMBTiles(source: string | ArrayBuffer | Uint8Array): ReadableStream<Uint8Array>;
9
13
 
10
14
  export { fromMBTiles };
@@ -1,10 +1,14 @@
1
1
  /**
2
2
  * Convert a MBTiles file to a styled map package, returned as a web
3
- * ReadableStream.
3
+ * ReadableStream. The async MBTiles.open() happens lazily inside the
4
+ * stream's start(), so this function is synchronous.
4
5
  *
5
- * @param {string} mbtilesPath
6
+ * Requires Node >= 20 (uses better-sqlite3 which dropped Node 18 support).
7
+ *
8
+ * @param {string | ArrayBuffer | Uint8Array} source MBTiles source — file path
9
+ * (Node), OPFS path (browser Worker), or in-memory buffer.
6
10
  * @returns {ReadableStream<Uint8Array>}
7
11
  */
8
- declare function fromMBTiles(mbtilesPath: string): ReadableStream<Uint8Array>;
12
+ declare function fromMBTiles(source: string | ArrayBuffer | Uint8Array): ReadableStream<Uint8Array>;
9
13
 
10
14
  export { fromMBTiles };
@@ -2,50 +2,77 @@ import { MBTiles } from "mbtiles-reader";
2
2
  import { readableFromAsync } from "./utils/streams.js";
3
3
  import { Writer } from "./writer.js";
4
4
  const SOURCE_ID = "mbtiles-source";
5
- function fromMBTiles(mbtilesPath) {
6
- const reader = new MBTiles(mbtilesPath);
7
- if (reader.metadata.format === "pbf") {
8
- throw new Error("Vector MBTiles are not yet supported");
9
- }
10
- const style = {
11
- version: 8,
12
- name: reader.metadata.name,
13
- sources: {
14
- [SOURCE_ID]: {
15
- ...reader.metadata,
16
- type: "raster"
5
+ function fromMBTiles(source) {
6
+ let outputReader;
7
+ let conversionDone;
8
+ const pipeAbort = new AbortController();
9
+ return new ReadableStream({
10
+ async start() {
11
+ const reader = await MBTiles.open(source);
12
+ if (reader.metadata.format === "pbf") {
13
+ throw new Error("Vector MBTiles are not yet supported");
17
14
  }
18
- },
19
- layers: [
20
- {
21
- id: "background",
22
- type: "background",
23
- paint: {
24
- "background-color": "white"
25
- }
26
- },
27
- {
28
- id: "raster",
29
- type: "raster",
30
- source: SOURCE_ID,
31
- paint: {
32
- "raster-opacity": 1
15
+ const style = {
16
+ version: 8,
17
+ name: reader.metadata.name,
18
+ sources: {
19
+ [SOURCE_ID]: {
20
+ ...reader.metadata,
21
+ type: "raster"
22
+ }
23
+ },
24
+ layers: [
25
+ {
26
+ id: "background",
27
+ type: "background",
28
+ paint: {
29
+ "background-color": "white"
30
+ }
31
+ },
32
+ {
33
+ id: "raster",
34
+ type: "raster",
35
+ source: SOURCE_ID,
36
+ paint: {
37
+ "raster-opacity": 1
38
+ }
39
+ }
40
+ ]
41
+ };
42
+ const writer = new Writer(style);
43
+ outputReader = writer.outputStream.getReader();
44
+ conversionDone = (async () => {
45
+ try {
46
+ await readableFromAsync(mbtilesToTileArgs(reader)).pipeTo(
47
+ writer.createTileWriteStream(),
48
+ { signal: pipeAbort.signal }
49
+ );
50
+ writer.finish();
51
+ } catch (err) {
52
+ try {
53
+ writer.abort(err instanceof Error ? err : new Error(String(err)));
54
+ } catch {
55
+ }
33
56
  }
57
+ })();
58
+ },
59
+ async pull(controller) {
60
+ const { done, value } = await /** @type {ReadableStreamDefaultReader<Uint8Array>} */
61
+ outputReader.read();
62
+ if (done) {
63
+ controller.close();
64
+ } else {
65
+ controller.enqueue(value);
34
66
  }
35
- ]
36
- };
37
- const writer = new Writer(style);
38
- (async () => {
39
- try {
40
- await readableFromAsync(mbtilesToTileArgs(reader)).pipeTo(
41
- writer.createTileWriteStream()
42
- );
43
- writer.finish();
44
- } catch (err) {
45
- writer.abort(err instanceof Error ? err : new Error(String(err)));
67
+ },
68
+ async cancel(reason) {
69
+ pipeAbort.abort(reason);
70
+ await conversionDone;
71
+ await /** @type {ReadableStreamDefaultReader<Uint8Array>} */
72
+ outputReader.cancel(reason).catch(() => {
73
+ });
46
74
  }
47
- })();
48
- return writer.outputStream;
75
+ });
49
76
  }
50
77
  async function* mbtilesToTileArgs(mbtiles) {
51
78
  for (const { z, x, y, data, format } of mbtiles) {
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as SMPSource$1, a as SMPStyle$1 } from './types-CJq90eOB.cjs';
2
- export { W as Writer } from './types-CJq90eOB.cjs';
1
+ import { S as SMPSource$1, a as SMPStyle$1 } from './types-qfyJk4ot.cjs';
2
+ export { W as Writer } from './types-qfyJk4ot.cjs';
3
3
  export { Reader } from './reader.cjs';
4
4
  export { createServer } from './server.cjs';
5
5
  export { StyleDownloader } from './style-downloader.cjs';
@@ -9,7 +9,6 @@ export { fromMBTiles } from './from-mbtiles.cjs';
9
9
  import '@maplibre/maplibre-gl-style-spec';
10
10
  import 'geojson';
11
11
  import 'type-fest';
12
- import 'events';
13
12
  import '@gmaclennan/zip-reader';
14
13
  import 'itty-router';
15
14
  import 'itty-router/IttyRouter';
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as SMPSource$1, a as SMPStyle$1 } from './types-CJq90eOB.js';
2
- export { W as Writer } from './types-CJq90eOB.js';
1
+ import { S as SMPSource$1, a as SMPStyle$1 } from './types-qfyJk4ot.js';
2
+ export { W as Writer } from './types-qfyJk4ot.js';
3
3
  export { Reader } from './reader.js';
4
4
  export { createServer } from './server.js';
5
5
  export { StyleDownloader } from './style-downloader.js';
@@ -9,7 +9,6 @@ export { fromMBTiles } from './from-mbtiles.js';
9
9
  import '@maplibre/maplibre-gl-style-spec';
10
10
  import 'geojson';
11
11
  import 'type-fest';
12
- import 'events';
13
12
  import '@gmaclennan/zip-reader';
14
13
  import 'itty-router';
15
14
  import 'itty-router/IttyRouter';
package/dist/reader.cjs CHANGED
@@ -155,7 +155,7 @@ class Reader {
155
155
  sourcePromise.catch(import_misc.noop);
156
156
  zipPromise = sourcePromise.then((source) => {
157
157
  this.#fileSource = source;
158
- return import_zip_reader.ZipReader.from(source);
158
+ return import_zip_reader.ZipReader.from(source, { skipUniqueEntryCheck: true });
159
159
  });
160
160
  } else {
161
161
  zipPromise = Promise.resolve(filepathOrZip);
package/dist/reader.d.cts CHANGED
@@ -1,9 +1,8 @@
1
- import { a as SMPStyle } from './types-CJq90eOB.cjs';
1
+ import { a as SMPStyle } from './types-qfyJk4ot.cjs';
2
2
  import * as _gmaclennan_zip_reader from '@gmaclennan/zip-reader';
3
3
  import '@maplibre/maplibre-gl-style-spec';
4
4
  import 'geojson';
5
5
  import 'type-fest';
6
- import 'events';
7
6
 
8
7
  /**
9
8
  * @typedef {object} Resource
package/dist/reader.d.ts CHANGED
@@ -1,9 +1,8 @@
1
- import { a as SMPStyle } from './types-CJq90eOB.js';
1
+ import { a as SMPStyle } from './types-qfyJk4ot.js';
2
2
  import * as _gmaclennan_zip_reader from '@gmaclennan/zip-reader';
3
3
  import '@maplibre/maplibre-gl-style-spec';
4
4
  import 'geojson';
5
5
  import 'type-fest';
6
- import 'events';
7
6
 
8
7
  /**
9
8
  * @typedef {object} Resource
package/dist/reader.js CHANGED
@@ -128,7 +128,7 @@ class Reader {
128
128
  sourcePromise.catch(noop);
129
129
  zipPromise = sourcePromise.then((source) => {
130
130
  this.#fileSource = source;
131
- return ZipReader.from(source);
131
+ return ZipReader.from(source, { skipUniqueEntryCheck: true });
132
132
  });
133
133
  } else {
134
134
  zipPromise = Promise.resolve(filepathOrZip);
package/dist/server.d.cts CHANGED
@@ -1,11 +1,10 @@
1
1
  import { RequestLike } from 'itty-router';
2
2
  import { Reader } from './reader.cjs';
3
3
  import { IttyRouter } from 'itty-router/IttyRouter';
4
- import './types-CJq90eOB.cjs';
4
+ import './types-qfyJk4ot.cjs';
5
5
  import '@maplibre/maplibre-gl-style-spec';
6
6
  import 'geojson';
7
7
  import 'type-fest';
8
- import 'events';
9
8
  import '@gmaclennan/zip-reader';
10
9
 
11
10
  /**
package/dist/server.d.ts CHANGED
@@ -1,11 +1,10 @@
1
1
  import { RequestLike } from 'itty-router';
2
2
  import { Reader } from './reader.js';
3
3
  import { IttyRouter } from 'itty-router/IttyRouter';
4
- import './types-CJq90eOB.js';
4
+ import './types-qfyJk4ot.js';
5
5
  import '@maplibre/maplibre-gl-style-spec';
6
6
  import 'geojson';
7
7
  import 'type-fest';
8
- import 'events';
9
8
  import '@gmaclennan/zip-reader';
10
9
 
11
10
  /**
@@ -1,11 +1,10 @@
1
1
  import * as ky from 'ky';
2
2
  import { BBox } from './utils/geo.cjs';
3
- import { b as StyleInlinedSources, G as GlyphInfo, T as TileInfo } from './types-CJq90eOB.cjs';
3
+ import { b as StyleInlinedSources, G as GlyphInfo, T as TileInfo } from './types-qfyJk4ot.cjs';
4
4
  import { TileDownloadStats } from './tile-downloader.cjs';
5
5
  import { StyleSpecification } from '@maplibre/maplibre-gl-style-spec';
6
6
  import 'geojson';
7
7
  import 'type-fest';
8
- import 'events';
9
8
  import './utils/fetch.cjs';
10
9
  import './utils/streams.cjs';
11
10
  import 'stream/web';
@@ -1,11 +1,10 @@
1
1
  import * as ky from 'ky';
2
2
  import { BBox } from './utils/geo.js';
3
- import { b as StyleInlinedSources, G as GlyphInfo, T as TileInfo } from './types-CJq90eOB.js';
3
+ import { b as StyleInlinedSources, G as GlyphInfo, T as TileInfo } from './types-qfyJk4ot.js';
4
4
  import { TileDownloadStats } from './tile-downloader.js';
5
5
  import { StyleSpecification } from '@maplibre/maplibre-gl-style-spec';
6
6
  import 'geojson';
7
7
  import 'type-fest';
8
- import 'events';
9
8
  import './utils/fetch.js';
10
9
  import './utils/streams.js';
11
10
  import 'stream/web';
@@ -1,10 +1,9 @@
1
- import { T as TileInfo$1 } from './types-CJq90eOB.cjs';
1
+ import { T as TileInfo$1 } from './types-qfyJk4ot.cjs';
2
2
  import { BBox } from './utils/geo.cjs';
3
3
  import { FetchQueue } from './utils/fetch.cjs';
4
4
  import '@maplibre/maplibre-gl-style-spec';
5
5
  import 'geojson';
6
6
  import 'type-fest';
7
- import 'events';
8
7
  import './utils/streams.cjs';
9
8
  import 'stream/web';
10
9
 
@@ -1,10 +1,9 @@
1
- import { T as TileInfo$1 } from './types-CJq90eOB.js';
1
+ import { T as TileInfo$1 } from './types-qfyJk4ot.js';
2
2
  import { BBox } from './utils/geo.js';
3
3
  import { FetchQueue } from './utils/fetch.js';
4
4
  import '@maplibre/maplibre-gl-style-spec';
5
5
  import 'geojson';
6
6
  import 'type-fest';
7
- import 'events';
8
7
  import './utils/streams.js';
9
8
  import 'stream/web';
10
9
 
@@ -1,7 +1,6 @@
1
1
  import { StyleSpecification, SourceSpecification, GeoJSONSourceSpecification, VectorSourceSpecification, RasterSourceSpecification, RasterDEMSourceSpecification } from '@maplibre/maplibre-gl-style-spec';
2
2
  import { GeoJSON, BBox } from 'geojson';
3
3
  import { SetRequired } from 'type-fest';
4
- import { EventEmitter } from 'events';
5
4
 
6
5
  /** @typedef {string | Uint8Array | ReadableStream } Source */
7
6
  /** @typedef {`${number}-${number}`} GlyphRange */
@@ -28,20 +27,28 @@ import { EventEmitter } from 'events';
28
27
  /** @import { StyleSpecification } from '@maplibre/maplibre-gl-style-spec' */
29
28
  /** @import { InputSource, SMPSource } from './types.js' */
30
29
  declare const SUPPORTED_SOURCE_TYPES: readonly ["raster", "vector", "geojson"];
30
+ /**
31
+ * @typedef {object} WriterOptions
32
+ * @property {boolean} [dedupe] When true, duplicate tiles (with identical
33
+ * content) are stored only once in the archive. Additional entries in the
34
+ * central directory point to the same data. This reduces file size for
35
+ * tilesets with many repeated tiles (e.g. ocean tiles).
36
+ */
31
37
  /**
32
38
  * Write a styled map package to a stream. Stream `writer.outputStream` to a
33
39
  * destination, e.g. `fs.createWriteStream('my-map.styledmap')`. You must call
34
40
  * `witer.finish()` and then wait for your writable stream to `finish` before
35
41
  * using the output.
36
42
  */
37
- declare class Writer extends EventEmitter<[never]> {
43
+ declare class Writer {
38
44
  static SUPPORTED_SOURCE_TYPES: readonly ["raster", "vector", "geojson"];
39
45
  /**
40
46
  * @param {any} style A v7 or v8 MapLibre style. v7 styles will be migrated to
41
47
  * v8. (There are currently no typescript declarations for v7 styles, hence
42
48
  * this is typed as `any` and validated internally)
49
+ * @param {WriterOptions} [options]
43
50
  */
44
- constructor(style: any);
51
+ constructor(style: any, { dedupe }?: WriterOptions);
45
52
  /**
46
53
  * @returns {ReadableStream<Uint8Array>} Readable stream of the styled map package
47
54
  */
@@ -132,6 +139,15 @@ type GlyphInfo = {
132
139
  font: string;
133
140
  range: GlyphRange;
134
141
  };
142
+ type WriterOptions = {
143
+ /**
144
+ * When true, duplicate tiles (with identical
145
+ * content) are stored only once in the archive. Additional entries in the
146
+ * central directory point to the same data. This reduces file size for
147
+ * tilesets with many repeated tiles (e.g. ocean tiles).
148
+ */
149
+ dedupe?: boolean | undefined;
150
+ };
135
151
 
136
152
  type TransformInlinedSource<T extends SourceSpecification> = T extends GeoJSONSourceSpecification ? OmitUnion<T, 'data'> & {
137
153
  data: GeoJSON;
@@ -181,4 +197,4 @@ type TransformSMPStyle<T extends StyleSpecification> = Omit<T, 'sources'> & {
181
197
  type DownloadStream = ReadableStream<Uint8Array>;
182
198
  type OmitUnion<T, K extends keyof any> = T extends unknown ? Omit<T, K> : never;
183
199
 
184
- export { type DownloadStream as D, type GlyphInfo as G, type InlinedSource as I, type SMPSource as S, type TileInfo as T, Writer as W, type SMPStyle as a, type StyleInlinedSources as b, type TileFormat as c, type GlyphRange as d, SUPPORTED_SOURCE_TYPES as e, type Source as f, type SourceInfo as g };
200
+ export { type DownloadStream as D, type GlyphInfo as G, type InlinedSource as I, type SMPSource as S, type TileInfo as T, Writer as W, type SMPStyle as a, type StyleInlinedSources as b, type TileFormat as c, type GlyphRange as d, SUPPORTED_SOURCE_TYPES as e, type Source as f, type SourceInfo as g, type WriterOptions as h };
@@ -1,7 +1,6 @@
1
1
  import { StyleSpecification, SourceSpecification, GeoJSONSourceSpecification, VectorSourceSpecification, RasterSourceSpecification, RasterDEMSourceSpecification } from '@maplibre/maplibre-gl-style-spec';
2
2
  import { GeoJSON, BBox } from 'geojson';
3
3
  import { SetRequired } from 'type-fest';
4
- import { EventEmitter } from 'events';
5
4
 
6
5
  /** @typedef {string | Uint8Array | ReadableStream } Source */
7
6
  /** @typedef {`${number}-${number}`} GlyphRange */
@@ -28,20 +27,28 @@ import { EventEmitter } from 'events';
28
27
  /** @import { StyleSpecification } from '@maplibre/maplibre-gl-style-spec' */
29
28
  /** @import { InputSource, SMPSource } from './types.js' */
30
29
  declare const SUPPORTED_SOURCE_TYPES: readonly ["raster", "vector", "geojson"];
30
+ /**
31
+ * @typedef {object} WriterOptions
32
+ * @property {boolean} [dedupe] When true, duplicate tiles (with identical
33
+ * content) are stored only once in the archive. Additional entries in the
34
+ * central directory point to the same data. This reduces file size for
35
+ * tilesets with many repeated tiles (e.g. ocean tiles).
36
+ */
31
37
  /**
32
38
  * Write a styled map package to a stream. Stream `writer.outputStream` to a
33
39
  * destination, e.g. `fs.createWriteStream('my-map.styledmap')`. You must call
34
40
  * `witer.finish()` and then wait for your writable stream to `finish` before
35
41
  * using the output.
36
42
  */
37
- declare class Writer extends EventEmitter<[never]> {
43
+ declare class Writer {
38
44
  static SUPPORTED_SOURCE_TYPES: readonly ["raster", "vector", "geojson"];
39
45
  /**
40
46
  * @param {any} style A v7 or v8 MapLibre style. v7 styles will be migrated to
41
47
  * v8. (There are currently no typescript declarations for v7 styles, hence
42
48
  * this is typed as `any` and validated internally)
49
+ * @param {WriterOptions} [options]
43
50
  */
44
- constructor(style: any);
51
+ constructor(style: any, { dedupe }?: WriterOptions);
45
52
  /**
46
53
  * @returns {ReadableStream<Uint8Array>} Readable stream of the styled map package
47
54
  */
@@ -132,6 +139,15 @@ type GlyphInfo = {
132
139
  font: string;
133
140
  range: GlyphRange;
134
141
  };
142
+ type WriterOptions = {
143
+ /**
144
+ * When true, duplicate tiles (with identical
145
+ * content) are stored only once in the archive. Additional entries in the
146
+ * central directory point to the same data. This reduces file size for
147
+ * tilesets with many repeated tiles (e.g. ocean tiles).
148
+ */
149
+ dedupe?: boolean | undefined;
150
+ };
135
151
 
136
152
  type TransformInlinedSource<T extends SourceSpecification> = T extends GeoJSONSourceSpecification ? OmitUnion<T, 'data'> & {
137
153
  data: GeoJSON;
@@ -181,4 +197,4 @@ type TransformSMPStyle<T extends StyleSpecification> = Omit<T, 'sources'> & {
181
197
  type DownloadStream = ReadableStream<Uint8Array>;
182
198
  type OmitUnion<T, K extends keyof any> = T extends unknown ? Omit<T, K> : never;
183
199
 
184
- export { type DownloadStream as D, type GlyphInfo as G, type InlinedSource as I, type SMPSource as S, type TileInfo as T, Writer as W, type SMPStyle as a, type StyleInlinedSources as b, type TileFormat as c, type GlyphRange as d, SUPPORTED_SOURCE_TYPES as e, type Source as f, type SourceInfo as g };
200
+ export { type DownloadStream as D, type GlyphInfo as G, type InlinedSource as I, type SMPSource as S, type TileInfo as T, Writer as W, type SMPStyle as a, type StyleInlinedSources as b, type TileFormat as c, type GlyphRange as d, SUPPORTED_SOURCE_TYPES as e, type Source as f, type SourceInfo as g, type WriterOptions as h };
@@ -1,8 +1,7 @@
1
- import { c as TileFormat } from '../types-CJq90eOB.cjs';
1
+ import { c as TileFormat } from '../types-qfyJk4ot.cjs';
2
2
  import '@maplibre/maplibre-gl-style-spec';
3
3
  import 'geojson';
4
4
  import 'type-fest';
5
- import 'events';
6
5
 
7
6
  /**
8
7
  * For a given buffer, determine the tile format based on the magic bytes.
@@ -1,8 +1,7 @@
1
- import { c as TileFormat } from '../types-CJq90eOB.js';
1
+ import { c as TileFormat } from '../types-qfyJk4ot.js';
2
2
  import '@maplibre/maplibre-gl-style-spec';
3
3
  import 'geojson';
4
4
  import 'type-fest';
5
- import 'events';
6
5
 
7
6
  /**
8
7
  * For a given buffer, determine the tile format based on the magic bytes.
@@ -1,10 +1,9 @@
1
1
  import { BBox } from './geo.cjs';
2
- import { I as InlinedSource } from '../types-CJq90eOB.cjs';
2
+ import { I as InlinedSource } from '../types-qfyJk4ot.cjs';
3
3
  import * as _maplibre_maplibre_gl_style_spec from '@maplibre/maplibre-gl-style-spec';
4
4
  import { StyleSpecification, ValidationError } from '@maplibre/maplibre-gl-style-spec';
5
5
  import 'geojson';
6
6
  import 'type-fest';
7
- import 'events';
8
7
 
9
8
  /** @import {StyleSpecification, ExpressionSpecification, ValidationError} from '@maplibre/maplibre-gl-style-spec' */
10
9
  /**
@@ -1,10 +1,9 @@
1
1
  import { BBox } from './geo.js';
2
- import { I as InlinedSource } from '../types-CJq90eOB.js';
2
+ import { I as InlinedSource } from '../types-qfyJk4ot.js';
3
3
  import * as _maplibre_maplibre_gl_style_spec from '@maplibre/maplibre-gl-style-spec';
4
4
  import { StyleSpecification, ValidationError } from '@maplibre/maplibre-gl-style-spec';
5
5
  import 'geojson';
6
6
  import 'type-fest';
7
- import 'events';
8
7
 
9
8
  /** @import {StyleSpecification, ExpressionSpecification, ValidationError} from '@maplibre/maplibre-gl-style-spec' */
10
9
  /**
@@ -1,8 +1,7 @@
1
1
  import * as type_fest from 'type-fest';
2
- import { d as GlyphRange, T as TileInfo, c as TileFormat } from '../types-CJq90eOB.cjs';
2
+ import { d as GlyphRange, T as TileInfo, c as TileFormat } from '../types-qfyJk4ot.cjs';
3
3
  import '@maplibre/maplibre-gl-style-spec';
4
4
  import 'geojson';
5
- import 'events';
6
5
 
7
6
  /**
8
7
  * @param {string} path
@@ -1,8 +1,7 @@
1
1
  import * as type_fest from 'type-fest';
2
- import { d as GlyphRange, T as TileInfo, c as TileFormat } from '../types-CJq90eOB.js';
2
+ import { d as GlyphRange, T as TileInfo, c as TileFormat } from '../types-qfyJk4ot.js';
3
3
  import '@maplibre/maplibre-gl-style-spec';
4
4
  import 'geojson';
5
- import 'events';
6
5
 
7
6
  /**
8
7
  * @param {string} path
package/dist/writer.cjs CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
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
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
  var writer_exports = {};
20
30
  __export(writer_exports, {
@@ -24,7 +34,6 @@ __export(writer_exports, {
24
34
  module.exports = __toCommonJS(writer_exports);
25
35
  var import_maplibre_gl_style_spec = require("@maplibre/maplibre-gl-style-spec");
26
36
  var import_bbox = require("@turf/bbox");
27
- var import_events = require("events");
28
37
  var import_filter_obj = require("filter-obj");
29
38
  var import_zip_writer = require("zip-writer");
30
39
  var import_file_formats = require('./utils/file-formats.cjs');
@@ -41,7 +50,7 @@ const SUPPORTED_SOURCE_TYPES = (
41
50
  "geojson"
42
51
  ]
43
52
  );
44
- class Writer extends import_events.EventEmitter {
53
+ class Writer {
45
54
  #zipWriter = new import_zip_writer.ZipWriter();
46
55
  /** @type {Set<string>} */
47
56
  #addedFiles = /* @__PURE__ */ new Set();
@@ -57,14 +66,20 @@ class Writer extends import_events.EventEmitter {
57
66
  #outputStream;
58
67
  /** @type {ReadableStreamDefaultController<Uint8Array>} */
59
68
  #outputController;
69
+ /** @type {boolean} */
70
+ #dedupe;
71
+ /** @type {Map<string, string>} hash → first entry name */
72
+ #tileHashes = /* @__PURE__ */ new Map();
73
+ /** @type {Array<{ name: string, originalName: string }>} */
74
+ #duplicateEntries = [];
60
75
  static SUPPORTED_SOURCE_TYPES = SUPPORTED_SOURCE_TYPES;
61
76
  /**
62
77
  * @param {any} style A v7 or v8 MapLibre style. v7 styles will be migrated to
63
78
  * v8. (There are currently no typescript declarations for v7 styles, hence
64
79
  * this is typed as `any` and validated internally)
80
+ * @param {WriterOptions} [options]
65
81
  */
66
- constructor(style) {
67
- super();
82
+ constructor(style, { dedupe = false } = {}) {
68
83
  if (!style || !("version" in style)) {
69
84
  throw new Error("Invalid style");
70
85
  }
@@ -81,6 +96,7 @@ class Writer extends import_events.EventEmitter {
81
96
  throw new AggregateError(errors, "Invalid style");
82
97
  }
83
98
  this.#style = styleCopy;
99
+ this.#dedupe = dedupe;
84
100
  for (const [sourceId, source] of Object.entries(this.#style.sources)) {
85
101
  if (source.type !== "geojson") continue;
86
102
  this.#addSource(sourceId, source);
@@ -254,6 +270,23 @@ class Writer extends import_events.EventEmitter {
254
270
  source.bounds = (0, import_geo.unionBBox)([source.bounds, bbox2]);
255
271
  }
256
272
  const name = (0, import_templates.getTileFilename)({ sourceId: encodedSourceId, z, x, y, format });
273
+ if (this.#dedupe) {
274
+ const data = await toUint8Array(tileData);
275
+ const hash = await hashData(data);
276
+ if (this.#addedFiles.has(name)) {
277
+ throw new Error(`${name} already added`);
278
+ }
279
+ this.#addedFiles.add(name);
280
+ const existingName = this.#tileHashes.get(hash);
281
+ if (existingName) {
282
+ this.#duplicateEntries.push({ name, originalName: existingName });
283
+ return;
284
+ }
285
+ this.#tileHashes.set(hash, name);
286
+ const readable = toWebStream(data);
287
+ await this.#zipWriter.addEntry({ readable, name, store: true });
288
+ return;
289
+ }
257
290
  return this.#append(tileData, { name, store: true });
258
291
  }
259
292
  /**
@@ -318,7 +351,17 @@ class Writer extends import_events.EventEmitter {
318
351
  this.#prepareStyle();
319
352
  const style = JSON.stringify(this.#style);
320
353
  await this.#append(style, { name: import_templates.STYLE_FILE });
321
- const entries = await this.#zipWriter.entries();
354
+ let entries = await this.#zipWriter.entries();
355
+ if (this.#duplicateEntries.length > 0) {
356
+ const entriesByName = new Map(entries.map((e) => [e.name, e]));
357
+ for (const { name, originalName } of this.#duplicateEntries) {
358
+ const original = entriesByName.get(originalName);
359
+ if (!original) {
360
+ throw new Error(`Original entry ${originalName} not found`);
361
+ }
362
+ entries.push({ ...original, name });
363
+ }
364
+ }
322
365
  const sortedEntries = sortEntries(entries);
323
366
  await this.#zipWriter.finalize({ entries: sortedEntries });
324
367
  }
@@ -432,6 +475,37 @@ function get2DBBox(bbox2) {
432
475
  if (bbox2.length === 4) return bbox2;
433
476
  return [bbox2[0], bbox2[1], bbox2[3], bbox2[4]];
434
477
  }
478
+ async function toUint8Array(source) {
479
+ if (source instanceof Uint8Array) return source;
480
+ if (typeof source === "string") return new TextEncoder().encode(source);
481
+ const reader = (
482
+ /** @type {ReadableStream<Uint8Array>} */
483
+ source.getReader()
484
+ );
485
+ const chunks = [];
486
+ let totalLength = 0;
487
+ while (true) {
488
+ const { done, value } = await reader.read();
489
+ if (done) break;
490
+ chunks.push(value);
491
+ totalLength += value.byteLength;
492
+ }
493
+ const result = new Uint8Array(totalLength);
494
+ let offset = 0;
495
+ for (const chunk of chunks) {
496
+ result.set(chunk, offset);
497
+ offset += chunk.byteLength;
498
+ }
499
+ return result;
500
+ }
501
+ async function hashData(data) {
502
+ let c = globalThis.crypto;
503
+ if (!c) {
504
+ c = (await import("node:crypto")).webcrypto;
505
+ }
506
+ const buf = await c.subtle.digest("SHA-256", data);
507
+ return Array.from(new Uint8Array(buf)).map((b) => b.toString(16).padStart(2, "0")).join("");
508
+ }
435
509
  function sortEntries(entries) {
436
510
  return [...entries].sort((a, b) => {
437
511
  if (a.name === import_templates.VERSION_FILE) return -1;
package/dist/writer.d.cts CHANGED
@@ -1,5 +1,4 @@
1
- export { G as GlyphInfo, d as GlyphRange, e as SUPPORTED_SOURCE_TYPES, f as Source, g as SourceInfo, c as TileFormat, T as TileInfo, W as Writer } from './types-CJq90eOB.cjs';
2
- import 'events';
1
+ export { G as GlyphInfo, d as GlyphRange, e as SUPPORTED_SOURCE_TYPES, f as Source, g as SourceInfo, c as TileFormat, T as TileInfo, W as Writer, h as WriterOptions } from './types-qfyJk4ot.cjs';
3
2
  import '@maplibre/maplibre-gl-style-spec';
4
3
  import 'geojson';
5
4
  import 'type-fest';
package/dist/writer.d.ts CHANGED
@@ -1,5 +1,4 @@
1
- export { G as GlyphInfo, d as GlyphRange, e as SUPPORTED_SOURCE_TYPES, f as Source, g as SourceInfo, c as TileFormat, T as TileInfo, W as Writer } from './types-CJq90eOB.js';
2
- import 'events';
1
+ export { G as GlyphInfo, d as GlyphRange, e as SUPPORTED_SOURCE_TYPES, f as Source, g as SourceInfo, c as TileFormat, T as TileInfo, W as Writer, h as WriterOptions } from './types-qfyJk4ot.js';
3
2
  import '@maplibre/maplibre-gl-style-spec';
4
3
  import 'geojson';
5
4
  import 'type-fest';
package/dist/writer.js CHANGED
@@ -1,6 +1,5 @@
1
1
  import { validateStyleMin, migrate } from "@maplibre/maplibre-gl-style-spec";
2
2
  import { bbox } from "@turf/bbox";
3
- import { EventEmitter } from "events";
4
3
  import { excludeKeys } from "filter-obj";
5
4
  import { ZipWriter } from "zip-writer";
6
5
  import { getTileFormatFromStream } from "./utils/file-formats.js";
@@ -29,7 +28,7 @@ const SUPPORTED_SOURCE_TYPES = (
29
28
  "geojson"
30
29
  ]
31
30
  );
32
- class Writer extends EventEmitter {
31
+ class Writer {
33
32
  #zipWriter = new ZipWriter();
34
33
  /** @type {Set<string>} */
35
34
  #addedFiles = /* @__PURE__ */ new Set();
@@ -45,14 +44,20 @@ class Writer extends EventEmitter {
45
44
  #outputStream;
46
45
  /** @type {ReadableStreamDefaultController<Uint8Array>} */
47
46
  #outputController;
47
+ /** @type {boolean} */
48
+ #dedupe;
49
+ /** @type {Map<string, string>} hash → first entry name */
50
+ #tileHashes = /* @__PURE__ */ new Map();
51
+ /** @type {Array<{ name: string, originalName: string }>} */
52
+ #duplicateEntries = [];
48
53
  static SUPPORTED_SOURCE_TYPES = SUPPORTED_SOURCE_TYPES;
49
54
  /**
50
55
  * @param {any} style A v7 or v8 MapLibre style. v7 styles will be migrated to
51
56
  * v8. (There are currently no typescript declarations for v7 styles, hence
52
57
  * this is typed as `any` and validated internally)
58
+ * @param {WriterOptions} [options]
53
59
  */
54
- constructor(style) {
55
- super();
60
+ constructor(style, { dedupe = false } = {}) {
56
61
  if (!style || !("version" in style)) {
57
62
  throw new Error("Invalid style");
58
63
  }
@@ -69,6 +74,7 @@ class Writer extends EventEmitter {
69
74
  throw new AggregateError(errors, "Invalid style");
70
75
  }
71
76
  this.#style = styleCopy;
77
+ this.#dedupe = dedupe;
72
78
  for (const [sourceId, source] of Object.entries(this.#style.sources)) {
73
79
  if (source.type !== "geojson") continue;
74
80
  this.#addSource(sourceId, source);
@@ -242,6 +248,23 @@ class Writer extends EventEmitter {
242
248
  source.bounds = unionBBox([source.bounds, bbox2]);
243
249
  }
244
250
  const name = getTileFilename({ sourceId: encodedSourceId, z, x, y, format });
251
+ if (this.#dedupe) {
252
+ const data = await toUint8Array(tileData);
253
+ const hash = await hashData(data);
254
+ if (this.#addedFiles.has(name)) {
255
+ throw new Error(`${name} already added`);
256
+ }
257
+ this.#addedFiles.add(name);
258
+ const existingName = this.#tileHashes.get(hash);
259
+ if (existingName) {
260
+ this.#duplicateEntries.push({ name, originalName: existingName });
261
+ return;
262
+ }
263
+ this.#tileHashes.set(hash, name);
264
+ const readable = toWebStream(data);
265
+ await this.#zipWriter.addEntry({ readable, name, store: true });
266
+ return;
267
+ }
245
268
  return this.#append(tileData, { name, store: true });
246
269
  }
247
270
  /**
@@ -306,7 +329,17 @@ class Writer extends EventEmitter {
306
329
  this.#prepareStyle();
307
330
  const style = JSON.stringify(this.#style);
308
331
  await this.#append(style, { name: STYLE_FILE });
309
- const entries = await this.#zipWriter.entries();
332
+ let entries = await this.#zipWriter.entries();
333
+ if (this.#duplicateEntries.length > 0) {
334
+ const entriesByName = new Map(entries.map((e) => [e.name, e]));
335
+ for (const { name, originalName } of this.#duplicateEntries) {
336
+ const original = entriesByName.get(originalName);
337
+ if (!original) {
338
+ throw new Error(`Original entry ${originalName} not found`);
339
+ }
340
+ entries.push({ ...original, name });
341
+ }
342
+ }
310
343
  const sortedEntries = sortEntries(entries);
311
344
  await this.#zipWriter.finalize({ entries: sortedEntries });
312
345
  }
@@ -420,6 +453,37 @@ function get2DBBox(bbox2) {
420
453
  if (bbox2.length === 4) return bbox2;
421
454
  return [bbox2[0], bbox2[1], bbox2[3], bbox2[4]];
422
455
  }
456
+ async function toUint8Array(source) {
457
+ if (source instanceof Uint8Array) return source;
458
+ if (typeof source === "string") return new TextEncoder().encode(source);
459
+ const reader = (
460
+ /** @type {ReadableStream<Uint8Array>} */
461
+ source.getReader()
462
+ );
463
+ const chunks = [];
464
+ let totalLength = 0;
465
+ while (true) {
466
+ const { done, value } = await reader.read();
467
+ if (done) break;
468
+ chunks.push(value);
469
+ totalLength += value.byteLength;
470
+ }
471
+ const result = new Uint8Array(totalLength);
472
+ let offset = 0;
473
+ for (const chunk of chunks) {
474
+ result.set(chunk, offset);
475
+ offset += chunk.byteLength;
476
+ }
477
+ return result;
478
+ }
479
+ async function hashData(data) {
480
+ let c = globalThis.crypto;
481
+ if (!c) {
482
+ c = (await import("node:crypto")).webcrypto;
483
+ }
484
+ const buf = await c.subtle.digest("SHA-256", data);
485
+ return Array.from(new Uint8Array(buf)).map((b) => b.toString(16).padStart(2, "0")).join("");
486
+ }
423
487
  function sortEntries(entries) {
424
488
  return [...entries].sort((a, b) => {
425
489
  if (a.name === VERSION_FILE) return -1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "styled-map-package-api",
3
- "version": "5.0.0-pre.0",
3
+ "version": "5.0.0-pre.2",
4
4
  "description": "JavaScript API for reading, writing, and serving Styled Map Package (.smp) files",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -114,7 +114,7 @@
114
114
  "keywords": [],
115
115
  "license": "MIT",
116
116
  "dependencies": {
117
- "@gmaclennan/zip-reader": "^1.0.0-pre.2",
117
+ "@gmaclennan/zip-reader": "^1.0.0",
118
118
  "@mapbox/sphericalmercator": "^1.2.0",
119
119
  "@maplibre/maplibre-gl-style-spec": "^20.3.1",
120
120
  "@placemarkio/check-geojson": "^0.1.12",
@@ -124,12 +124,15 @@
124
124
  "itty-router": "^5.0.22",
125
125
  "ky": "^1.7.5",
126
126
  "map-obj": "^5.0.2",
127
- "mbtiles-reader": "^1.0.0",
127
+ "mbtiles-reader": "^2.0.1",
128
128
  "p-limit": "^6.2.0",
129
129
  "readable-stream": "^4.7.0",
130
130
  "yocto-queue": "^1.1.1",
131
131
  "zip-writer": "^2.2.0"
132
132
  },
133
+ "optionalDependencies": {
134
+ "better-sqlite3": "^12.8.0"
135
+ },
133
136
  "devDependencies": {
134
137
  "@commander-js/extra-typings": "^12.1.0",
135
138
  "@types/geojson": "^7946.0.16",