styled-map-package 2.2.1 → 3.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.
Files changed (107) hide show
  1. package/bin/smp-download.js +3 -3
  2. package/bin/smp-mbtiles.js +1 -1
  3. package/bin/smp-view.js +2 -2
  4. package/dist/download.cjs +101 -0
  5. package/dist/download.d.cts +65 -0
  6. package/dist/{lib/download.d.ts → download.d.ts} +22 -6
  7. package/dist/download.js +77 -0
  8. package/dist/from-mbtiles.cjs +91 -0
  9. package/dist/from-mbtiles.d.cts +17 -0
  10. package/dist/from-mbtiles.d.ts +17 -0
  11. package/dist/from-mbtiles.js +57 -0
  12. package/dist/index.cjs +49 -0
  13. package/dist/index.d.cts +27 -0
  14. package/dist/index.d.ts +27 -0
  15. package/dist/index.js +18 -0
  16. package/dist/reader-watch.cjs +135 -0
  17. package/dist/reader-watch.d.cts +24 -0
  18. package/dist/reader-watch.d.ts +24 -0
  19. package/dist/reader-watch.js +101 -0
  20. package/dist/reader.cjs +167 -0
  21. package/dist/reader.d.cts +62 -0
  22. package/dist/{lib/reader.d.ts → reader.d.ts} +16 -5
  23. package/dist/reader.js +138 -0
  24. package/dist/reporters.cjs +122 -0
  25. package/dist/reporters.d.cts +10 -0
  26. package/dist/{lib/reporters.d.ts → reporters.d.ts} +5 -2
  27. package/dist/reporters.js +88 -0
  28. package/dist/server.cjs +79 -0
  29. package/dist/server.d.cts +37 -0
  30. package/dist/server.d.ts +37 -0
  31. package/dist/server.js +45 -0
  32. package/dist/style-downloader.cjs +312 -0
  33. package/dist/style-downloader.d.cts +120 -0
  34. package/dist/{lib/style-downloader.d.ts → style-downloader.d.ts} +23 -13
  35. package/dist/style-downloader.js +288 -0
  36. package/dist/tile-downloader.cjs +158 -0
  37. package/dist/tile-downloader.d.cts +84 -0
  38. package/dist/{lib/tile-downloader.d.ts → tile-downloader.d.ts} +22 -10
  39. package/dist/tile-downloader.js +126 -0
  40. package/dist/{lib/writer.d.ts → types-B4Xn1F9K.d.cts} +74 -14
  41. package/dist/types-B4Xn1F9K.d.ts +189 -0
  42. package/dist/utils/errors.cjs +41 -0
  43. package/dist/utils/errors.d.cts +18 -0
  44. package/dist/{lib/utils → utils}/errors.d.ts +4 -2
  45. package/dist/utils/errors.js +16 -0
  46. package/dist/utils/fetch.cjs +96 -0
  47. package/dist/utils/fetch.d.cts +51 -0
  48. package/dist/{lib/utils → utils}/fetch.d.ts +10 -4
  49. package/dist/utils/fetch.js +62 -0
  50. package/dist/utils/file-formats.cjs +98 -0
  51. package/dist/utils/file-formats.d.cts +35 -0
  52. package/dist/{lib/utils → utils}/file-formats.d.ts +13 -3
  53. package/dist/utils/file-formats.js +62 -0
  54. package/dist/utils/geo.cjs +84 -0
  55. package/dist/utils/geo.d.cts +46 -0
  56. package/dist/{lib/utils → utils}/geo.d.ts +8 -6
  57. package/dist/utils/geo.js +56 -0
  58. package/dist/utils/mapbox.cjs +121 -0
  59. package/dist/utils/mapbox.d.cts +43 -0
  60. package/dist/utils/mapbox.d.ts +43 -0
  61. package/dist/utils/mapbox.js +91 -0
  62. package/dist/utils/misc.cjs +39 -0
  63. package/{lib/utils/misc.js → dist/utils/misc.d.cts} +5 -9
  64. package/dist/{lib/utils → utils}/misc.d.ts +5 -3
  65. package/dist/utils/misc.js +13 -0
  66. package/dist/utils/streams.cjs +130 -0
  67. package/dist/utils/streams.d.cts +73 -0
  68. package/dist/{lib/utils → utils}/streams.d.ts +14 -10
  69. package/dist/utils/streams.js +103 -0
  70. package/dist/utils/style.cjs +126 -0
  71. package/dist/utils/style.d.cts +69 -0
  72. package/dist/{lib/utils → utils}/style.d.ts +19 -9
  73. package/dist/utils/style.js +98 -0
  74. package/dist/utils/templates.cjs +114 -0
  75. package/dist/utils/templates.d.cts +78 -0
  76. package/dist/{lib/utils → utils}/templates.d.ts +24 -14
  77. package/dist/utils/templates.js +79 -0
  78. package/dist/writer.cjs +401 -0
  79. package/dist/writer.d.cts +7 -0
  80. package/dist/writer.d.ts +7 -0
  81. package/dist/writer.js +374 -0
  82. package/package.json +87 -33
  83. package/dist/lib/from-mbtiles.d.ts +0 -13
  84. package/dist/lib/index.d.ts +0 -10
  85. package/dist/lib/reader-watch.d.ts +0 -13
  86. package/dist/lib/server.d.ts +0 -15
  87. package/dist/lib/types.d.ts +0 -64
  88. package/dist/lib/utils/mapbox.d.ts +0 -41
  89. package/lib/download.js +0 -114
  90. package/lib/from-mbtiles.js +0 -83
  91. package/lib/index.js +0 -11
  92. package/lib/reader-watch.js +0 -133
  93. package/lib/reader.js +0 -165
  94. package/lib/reporters.js +0 -92
  95. package/lib/server.js +0 -81
  96. package/lib/style-downloader.js +0 -363
  97. package/lib/tile-downloader.js +0 -188
  98. package/lib/types.ts +0 -104
  99. package/lib/utils/errors.js +0 -24
  100. package/lib/utils/fetch.js +0 -100
  101. package/lib/utils/file-formats.js +0 -85
  102. package/lib/utils/geo.js +0 -87
  103. package/lib/utils/mapbox.js +0 -155
  104. package/lib/utils/streams.js +0 -165
  105. package/lib/utils/style.js +0 -174
  106. package/lib/utils/templates.js +0 -136
  107. package/lib/writer.js +0 -478
@@ -1,41 +0,0 @@
1
- /** @param {string} url */
2
- export function isMapboxURL(url: string): boolean;
3
- /**
4
- * @param {string} url
5
- * @param {string} [accessToken]
6
- */
7
- export function normalizeStyleURL(url: string, accessToken?: string): string;
8
- /**
9
- * @param {string} url
10
- * @param {string} [accessToken]
11
- */
12
- export function normalizeGlyphsURL(url: string, accessToken?: string): string;
13
- /**
14
- * @param {string} url
15
- * @param {string} [accessToken]
16
- */
17
- export function normalizeSourceURL(url: string, accessToken?: string): string;
18
- /**
19
- * @param {string} url
20
- * @param {'' | '@2x'} format
21
- * @param {'.png' | '.json'} extension
22
- * @param {string} [accessToken]
23
- */
24
- export function normalizeSpriteURL(url: string, format: "" | "@2x", extension: ".png" | ".json", accessToken?: string): string;
25
- /**
26
- * @param {any} tileURL
27
- * @param {string} sourceURL
28
- * @param {256 | 512} [tileSize]
29
- * @param {{ devicePixelRatio?: number; supportsWebp?: boolean; }} [opts]
30
- */
31
- export function normalizeTileURL(tileURL: any, sourceURL: string, tileSize?: 256 | 512, { devicePixelRatio, supportsWebp }?: {
32
- devicePixelRatio?: number;
33
- supportsWebp?: boolean;
34
- }): any;
35
- export const API_URL: "https://api.mapbox.com";
36
- export type URLObject = {
37
- protocol: string;
38
- authority: string;
39
- path: string;
40
- params: string[];
41
- };
package/lib/download.js DELETED
@@ -1,114 +0,0 @@
1
- import { Transform } from 'readable-stream'
2
- import { pipeline } from 'stream/promises'
3
-
4
- import Writer from '../lib/writer.js'
5
- import StyleDownloader from './style-downloader.js'
6
-
7
- /**
8
- * @typedef {object} DownloadProgress
9
- * @property {import('./tile-downloader.js').TileDownloadStats & { done: boolean }} tiles
10
- * @property {{ done: boolean }} style
11
- * @property {{ downloaded: number, done: boolean }} sprites
12
- * @property {import('./style-downloader.js').GlyphDownloadStats & { done: boolean }} glyphs
13
- * @property {{ totalBytes: number, done: boolean }} output
14
- * @property {number} elapsedMs
15
- */
16
-
17
- /**
18
- * Download a map style and its resources for a given bounding box and max zoom
19
- * level. Returns a readable stream of a "styled map package", a zip file
20
- * containing all the resources needed to serve the style offline.
21
- *
22
- * @param {object} opts
23
- * @param {import("./utils/geo.js").BBox} opts.bbox Bounding box to download tiles for
24
- * @param {number} opts.maxzoom Max zoom level to download tiles for
25
- * @param {string} opts.styleUrl URL of the style to download
26
- * @param { (progress: DownloadProgress) => void } [opts.onprogress] Optional callback for reporting progress
27
- * @param {string} [opts.accessToken]
28
- * @returns {import('./types.js').DownloadStream} Readable stream of the output styled map file
29
- */
30
- export default function download({
31
- bbox,
32
- maxzoom,
33
- styleUrl,
34
- onprogress,
35
- accessToken,
36
- }) {
37
- const downloader = new StyleDownloader(styleUrl, {
38
- concurrency: 24,
39
- mapboxAccessToken: accessToken,
40
- })
41
-
42
- let start = Date.now()
43
- /** @type {DownloadProgress} */
44
- let progress = {
45
- tiles: { downloaded: 0, totalBytes: 0, total: 0, skipped: 0, done: false },
46
- style: { done: false },
47
- sprites: { downloaded: 0, done: false },
48
- glyphs: { downloaded: 0, total: 0, totalBytes: 0, done: false },
49
- output: { totalBytes: 0, done: false },
50
- elapsedMs: 0,
51
- }
52
-
53
- const sizeCounter = new Transform({
54
- transform(chunk, encoding, cb) {
55
- handleProgress({
56
- output: {
57
- totalBytes: progress.output.totalBytes + chunk.length,
58
- done: false,
59
- },
60
- })
61
- cb(null, chunk)
62
- },
63
- final(cb) {
64
- handleProgress({ output: { ...progress.output, done: true } })
65
- cb()
66
- },
67
- })
68
-
69
- /** @param {Partial<DownloadProgress>} update */
70
- function handleProgress(update) {
71
- progress = { ...progress, ...update, elapsedMs: Date.now() - start }
72
- onprogress?.(progress)
73
- }
74
-
75
- ;(async () => {
76
- const style = await downloader.getStyle()
77
- const writer = new Writer(style)
78
- handleProgress({ style: { done: true } })
79
- writer.outputStream.pipe(sizeCounter)
80
- writer.on('error', (err) => sizeCounter.destroy(err))
81
-
82
- try {
83
- for await (const spriteInfo of downloader.getSprites()) {
84
- await writer.addSprite(spriteInfo)
85
- handleProgress({
86
- sprites: { downloaded: progress.sprites.downloaded + 1, done: false },
87
- })
88
- }
89
- handleProgress({ sprites: { ...progress.sprites, done: true } })
90
-
91
- const tiles = downloader.getTiles({
92
- bounds: bbox,
93
- maxzoom,
94
- onprogress: (tileStats) =>
95
- handleProgress({ tiles: { ...tileStats, done: false } }),
96
- })
97
- await pipeline(tiles, writer.createTileWriteStream({ concurrency: 24 }))
98
- handleProgress({ tiles: { ...progress.tiles, done: true } })
99
-
100
- const glyphs = downloader.getGlyphs({
101
- onprogress: (glyphStats) =>
102
- handleProgress({ glyphs: { ...glyphStats, done: false } }),
103
- })
104
- await pipeline(glyphs, writer.createGlyphWriteStream())
105
- handleProgress({ glyphs: { ...progress.glyphs, done: true } })
106
-
107
- writer.finish()
108
- } catch (err) {
109
- writer.outputStream.destroy(/** @type {Error} */ (err))
110
- }
111
- })()
112
-
113
- return sizeCounter
114
- }
@@ -1,83 +0,0 @@
1
- import { MBTiles } from 'mbtiles-reader'
2
- import SMPWriter from 'styled-map-package/writer'
3
-
4
- import fs from 'node:fs'
5
- import { Transform } from 'node:stream'
6
- import { pipeline } from 'node:stream'
7
- import { pipeline as pipelinePromise } from 'node:stream/promises'
8
-
9
- const SOURCE_ID = 'mbtiles-source'
10
-
11
- /**
12
- * @overload
13
- * @param {string} mbtilesPath
14
- * @returns {import('stream').Readable}
15
- */
16
-
17
- /**
18
- * @overload
19
- * @param {string} mbtilesPath
20
- * @param {string} outputPath
21
- * @returns {Promise<void>}
22
- */
23
-
24
- /**
25
- * @param {string} mbtilesPath
26
- * @param {string} [outputPath]
27
- * @returns {Promise<void> | import('stream').Readable}
28
- */
29
- export default function fromMBTiles(mbtilesPath, outputPath) {
30
- const reader = new MBTiles(mbtilesPath)
31
- if (reader.metadata.format === 'pbf') {
32
- throw new Error('Vector MBTiles are not yet supported')
33
- }
34
- const style = {
35
- version: 8,
36
- name: reader.metadata.name,
37
- sources: {
38
- [SOURCE_ID]: {
39
- ...reader.metadata,
40
- type: 'raster',
41
- },
42
- },
43
- layers: [
44
- {
45
- id: 'background',
46
- type: 'background',
47
- paint: {
48
- 'background-color': 'white',
49
- },
50
- },
51
- {
52
- id: 'raster',
53
- type: 'raster',
54
- source: SOURCE_ID,
55
- paint: {
56
- 'raster-opacity': 1,
57
- },
58
- },
59
- ],
60
- }
61
-
62
- const writer = new SMPWriter(style)
63
-
64
- const returnValue = outputPath
65
- ? pipelinePromise(writer.outputStream, fs.createWriteStream(outputPath))
66
- : writer.outputStream
67
-
68
- const tileWriteStream = writer.createTileWriteStream()
69
-
70
- const transform = new Transform({
71
- objectMode: true,
72
- transform({ z, x, y, data, format }, encoding, callback) {
73
- callback(null, [data, { z, x, y, format, sourceId: SOURCE_ID }])
74
- },
75
- })
76
-
77
- pipeline(reader, transform, tileWriteStream, (err) => {
78
- if (err) return writer.outputStream.destroy(err)
79
- writer.finish()
80
- })
81
-
82
- return returnValue
83
- }
package/lib/index.js DELETED
@@ -1,11 +0,0 @@
1
- /** @typedef {import('./types.js').SMPSource} SMPSource */
2
- /** @typedef {import('./types.js').SMPStyle} SMPStyle */
3
-
4
- export { default as Reader } from './reader.js'
5
- export { default as ReaderWatch } from './reader-watch.js'
6
- export { default as Writer } from './writer.js'
7
- export { default as Server } from './server.js'
8
- export { default as StyleDownloader } from './style-downloader.js'
9
- export { downloadTiles } from './tile-downloader.js'
10
- export { default as download } from './download.js'
11
- export { default as fromMBTiles } from './from-mbtiles.js'
@@ -1,133 +0,0 @@
1
- import { once } from 'events'
2
-
3
- import fs from 'node:fs'
4
- import fsPromises from 'node:fs/promises'
5
-
6
- import Reader from './reader.js'
7
- import { ENOENT, isFileNotThereError } from './utils/errors.js'
8
- import { noop } from './utils/misc.js'
9
-
10
- /** @implements {Pick<Reader, keyof Reader>} */
11
- export default class ReaderWatch {
12
- /** @type {Reader | undefined} */
13
- #reader
14
- /** @type {Reader | undefined} */
15
- #maybeReader
16
- /** @type {Promise<Reader> | undefined} */
17
- #readerOpeningPromise
18
- #filepath
19
- /** @type {fs.FSWatcher | undefined} */
20
- #watch
21
-
22
- /**
23
- * @param {string} filepath
24
- */
25
- constructor(filepath) {
26
- this.#filepath = filepath
27
- // Call this now to catch any synchronous errors
28
- this.#tryToWatchFile()
29
- // eagerly open Reader
30
- this.#get().catch(noop)
31
- }
32
-
33
- #tryToWatchFile() {
34
- if (this.#watch) return
35
- try {
36
- this.#watch = fs
37
- .watch(this.#filepath, { persistent: false }, () => {
38
- this.#reader?.close().catch(noop)
39
- this.#reader = undefined
40
- this.#maybeReader = undefined
41
- this.#readerOpeningPromise = undefined
42
- // Close the watcher (which on some platforms will continue watching
43
- // the previous file) so on the next request we will start watching
44
- // the new file
45
- this.#watch?.close()
46
- this.#watch = undefined
47
- })
48
- .on('error', noop)
49
- } catch (error) {
50
- if (isFileNotThereError(error)) {
51
- // Ignore: File does not exist yet, but we'll try to open it later
52
- } else {
53
- throw error
54
- }
55
- }
56
- }
57
-
58
- async #get() {
59
- if (isWin() && (this.#reader || this.#readerOpeningPromise)) {
60
- // On Windows, the file watcher does not recognize file deletions, so we
61
- // need to check if the file still exists each time
62
- try {
63
- await fsPromises.stat(this.#filepath)
64
- } catch {
65
- this.#watch?.close()
66
- this.#watch = undefined
67
- this.#reader?.close().catch(noop)
68
- this.#reader = undefined
69
- this.#maybeReader = undefined
70
- this.#readerOpeningPromise = undefined
71
- }
72
- }
73
- // Need to retry this each time in case it failed initially because the file
74
- // was not present, or if the file was moved or deleted.
75
- this.#tryToWatchFile()
76
- // A lovely promise tangle to confuse future readers... sorry.
77
- //
78
- // 1. If the reader is already open, return it.
79
- // 2. If the reader is in the process of opening, return a promise that will
80
- // return the reader instance if it opened without error, or throw.
81
- // 3. If the reader threw an error during opening, try to open it again next
82
- // time this is called.
83
- if (this.#reader) return this.#reader
84
- if (this.#readerOpeningPromise) return this.#readerOpeningPromise
85
- this.#maybeReader = new Reader(this.#filepath)
86
- this.#readerOpeningPromise = this.#maybeReader
87
- .opened()
88
- .then(() => {
89
- if (!this.#maybeReader) {
90
- throw new ENOENT(this.#filepath)
91
- }
92
- this.#reader = this.#maybeReader
93
- return this.#reader
94
- })
95
- .finally(() => {
96
- this.#maybeReader = undefined
97
- this.#readerOpeningPromise = undefined
98
- })
99
- return this.#readerOpeningPromise
100
- }
101
-
102
- /** @type {Reader['opened']} */
103
- async opened() {
104
- const reader = await this.#get()
105
- return reader.opened()
106
- }
107
-
108
- /** @type {Reader['getStyle']} */
109
- async getStyle(baseUrl = null) {
110
- const reader = await this.#get()
111
- return reader.getStyle(baseUrl)
112
- }
113
-
114
- /** @type {Reader['getResource']} */
115
- async getResource(path) {
116
- const reader = await this.#get()
117
- return reader.getResource(path)
118
- }
119
-
120
- async close() {
121
- const reader = await this.#get()
122
- if (this.#watch) {
123
- this.#watch.close()
124
- await once(this.#watch, 'close')
125
- }
126
- await reader.close()
127
- }
128
- }
129
-
130
- /** @returns {boolean} */
131
- function isWin() {
132
- return process.platform === 'win32'
133
- }
package/lib/reader.js DELETED
@@ -1,165 +0,0 @@
1
- import intoStream from 'into-stream'
2
- import { open } from 'yauzl-promise'
3
-
4
- import { json } from 'node:stream/consumers'
5
-
6
- import { ENOENT } from './utils/errors.js'
7
- import { noop } from './utils/misc.js'
8
- import { validateStyle } from './utils/style.js'
9
- import {
10
- getContentType,
11
- getResourceType,
12
- STYLE_FILE,
13
- URI_BASE,
14
- } from './utils/templates.js'
15
-
16
- /**
17
- * @typedef {object} Resource
18
- * @property {string} resourceType
19
- * @property {string} contentType
20
- * @property {number} contentLength
21
- * @property {import('stream').Readable} stream
22
- * @property {'gzip'} [contentEncoding]
23
- */
24
-
25
- /**
26
- * A low-level reader for styled map packages. Returns resources in the package
27
- * as readable streams, for serving over HTTP for example.
28
- */
29
- export default class Reader {
30
- /** @type {Promise<import('yauzl-promise').ZipFile>} */
31
- #zipPromise
32
- #entriesPromise
33
- /** @type {undefined | Promise<void>} */
34
- #closePromise
35
-
36
- /**
37
- * @param {string | import('yauzl-promise').ZipFile} filepathOrZip Path to styled map package (`.styledmap`) file, or an instance of yauzl ZipFile
38
- */
39
- constructor(filepathOrZip) {
40
- const zipPromise = (this.#zipPromise =
41
- typeof filepathOrZip === 'string'
42
- ? open(filepathOrZip)
43
- : Promise.resolve(filepathOrZip))
44
- zipPromise.catch(noop)
45
- this.#entriesPromise = (async () => {
46
- /** @type {Map<string, import('yauzl-promise').Entry>} */
47
- const entries = new Map()
48
- if (this.#closePromise) return entries
49
- const zip = await zipPromise
50
- if (this.#closePromise) return entries
51
- for await (const entry of zip) {
52
- if (this.#closePromise) return entries
53
- entries.set(entry.filename, entry)
54
- }
55
- return entries
56
- })()
57
- this.#entriesPromise.catch(noop)
58
- }
59
-
60
- /**
61
- * Resolves when the styled map package has been opened and the entries have
62
- * been read. Throws any error that occurred during opening.
63
- */
64
- async opened() {
65
- await this.#entriesPromise
66
- }
67
-
68
- /**
69
- * Get the style JSON from the styled map package. The URLs in the style JSON
70
- * will be transformed to use the provided base URL.
71
- *
72
- * @param {string | null} [baseUrl] Base URL where you plan to serve the resources in this styled map package, e.g. `http://localhost:3000/maps/styleA`
73
- * @returns {Promise<import('./types.js').SMPStyle>}
74
- */
75
- async getStyle(baseUrl = null) {
76
- const styleEntry = (await this.#entriesPromise).get(STYLE_FILE)
77
- if (!styleEntry) throw new ENOENT(STYLE_FILE)
78
- const stream = await styleEntry.openReadStream()
79
- const style = await json(stream)
80
- if (!validateStyle(style)) {
81
- throw new AggregateError(validateStyle.errors, 'Invalid style')
82
- }
83
- if (typeof style.glyphs === 'string') {
84
- style.glyphs = getUrl(style.glyphs, baseUrl)
85
- }
86
- if (typeof style.sprite === 'string') {
87
- style.sprite = getUrl(style.sprite, baseUrl)
88
- } else if (Array.isArray(style.sprite)) {
89
- style.sprite = style.sprite.map(({ id, url }) => {
90
- return { id, url: getUrl(url, baseUrl) }
91
- })
92
- }
93
- for (const source of Object.values(style.sources)) {
94
- if ('tiles' in source && source.tiles) {
95
- source.tiles = source.tiles.map((tile) => getUrl(tile, baseUrl))
96
- }
97
- }
98
- // Hard to get this type-safe without a validation function. Instead we
99
- // trust the Writer and the tests for now.
100
- return /** @type {import('./types.js').SMPStyle} */ (style)
101
- }
102
-
103
- /**
104
- * Get a resource from the styled map package. The path should be relative to
105
- * the root of the package.
106
- *
107
- * @param {string} path
108
- * @returns {Promise<Resource>}
109
- */
110
- async getResource(path) {
111
- if (path[0] === '/') path = path.slice(1)
112
- if (path === STYLE_FILE) {
113
- const styleJSON = JSON.stringify(await this.getStyle())
114
- return {
115
- contentType: 'application/json; charset=utf-8',
116
- contentLength: Buffer.byteLength(styleJSON, 'utf8'),
117
- resourceType: 'style',
118
- stream: intoStream(styleJSON),
119
- }
120
- }
121
- const entry = (await this.#entriesPromise).get(path)
122
- if (!entry) throw new ENOENT(path)
123
- const resourceType = getResourceType(path)
124
- const contentType = getContentType(path)
125
- const stream = await entry.openReadStream()
126
- /** @type {Resource} */
127
- const resource = {
128
- resourceType,
129
- contentType,
130
- contentLength: entry.uncompressedSize,
131
- stream,
132
- }
133
- if (path.endsWith('.gz')) {
134
- resource.contentEncoding = 'gzip'
135
- }
136
- return resource
137
- }
138
-
139
- /**
140
- * Close the styled map package file (should be called after reading the file to avoid memory leaks)
141
- */
142
- async close() {
143
- if (this.#closePromise) return this.#closePromise
144
- this.#closePromise = (async () => {
145
- const zip = await this.#zipPromise
146
- await zip.close()
147
- })()
148
- return this.#closePromise
149
- }
150
- }
151
-
152
- /**
153
- * @param {string} smpUri
154
- * @param {string | null} baseUrl
155
- */
156
- function getUrl(smpUri, baseUrl) {
157
- if (!smpUri.startsWith(URI_BASE)) {
158
- throw new Error(`Invalid SMP URI: ${smpUri}`)
159
- }
160
- if (typeof baseUrl !== 'string') return smpUri
161
- if (!baseUrl.endsWith('/')) {
162
- baseUrl += '/'
163
- }
164
- return smpUri.replace(URI_BASE, baseUrl)
165
- }
package/lib/reporters.js DELETED
@@ -1,92 +0,0 @@
1
- import chalk, { chalkStderr } from 'chalk'
2
- import logSymbols from 'log-symbols'
3
- import ora from 'ora'
4
- import prettyBytes from 'pretty-bytes'
5
- import prettyMilliseconds from 'pretty-ms'
6
- import { Writable } from 'readable-stream'
7
-
8
- chalk.level = chalkStderr.level
9
-
10
- const TASKS = /** @type {const} */ ([
11
- 'style',
12
- 'sprites',
13
- 'tiles',
14
- 'glyphs',
15
- 'output',
16
- ])
17
-
18
- const TASK_LABEL = /** @type {const} */ ({
19
- style: 'Downloading Map Style',
20
- sprites: 'Downloading Sprites',
21
- tiles: 'Downloading Tiles',
22
- glyphs: 'Downloading Glyphs',
23
- output: 'Writing Styled Map Package',
24
- })
25
-
26
- const TASK_SUFFIX =
27
- /** @type {{ [K in (typeof TASKS)[number]]: (progress: import('./download.js').DownloadProgress[K]) => string }} */ ({
28
- style: () => '',
29
- sprites: ({ downloaded }) => `${downloaded}`,
30
- tiles: ({ total, skipped, totalBytes, downloaded }) => {
31
- const formattedTotal = total.toLocaleString()
32
- const formattedCompleted = (downloaded + skipped)
33
- .toLocaleString()
34
- .padStart(formattedTotal.length)
35
- return `${formattedCompleted}/${formattedTotal} (${prettyBytes(totalBytes)})`
36
- },
37
- glyphs: ({ total, downloaded, totalBytes }) =>
38
- `${downloaded}/${total} (${prettyBytes(totalBytes)})`,
39
- output: ({ totalBytes }) => `${prettyBytes(totalBytes)}`,
40
- })
41
-
42
- /**
43
- * A writable stream to reporting download progress to a TTY terminal. Write
44
- * progress messages to this stream for a pretty-printed progress task-list in
45
- * the terminal.
46
- */
47
- export function ttyReporter() {
48
- /** @type {import('./download.js').DownloadProgress | undefined} */
49
- let stats
50
- let current = 0
51
- /** @type {import('ora').Ora} */
52
- let spinner
53
- return new Writable({
54
- objectMode: true,
55
- // @ts-ignore - missing type def
56
- construct(cb) {
57
- process.stderr.write('\n')
58
- spinner = ora(TASK_LABEL[TASKS[current]]).start()
59
- cb()
60
- },
61
- /** @param {ArrayLike<{ chunk: import('./download.js').DownloadProgress, encoding: string }>} chunks */
62
- writev(chunks, cb) {
63
- stats = chunks[chunks.length - 1].chunk
64
- while (current < TASKS.length && stats[TASKS[current]].done) {
65
- spinner.suffixText = chalk.dim(
66
- TASK_SUFFIX[TASKS[current]](
67
- // @ts-ignore - too complicated for TS
68
- stats[TASKS[current]],
69
- ),
70
- )
71
- spinner.succeed()
72
- if (++current < TASKS.length) {
73
- spinner = ora(TASK_LABEL[TASKS[current]]).start()
74
- }
75
- }
76
- if (current < TASKS.length) {
77
- spinner.suffixText = chalk.dim(
78
- TASK_SUFFIX[TASKS[current]](
79
- // @ts-ignore - too complicated for TS
80
- stats[TASKS[current]],
81
- ),
82
- )
83
- } else {
84
- process.stderr.write(
85
- `${chalk.green(logSymbols.success)} Completed in ${prettyMilliseconds(stats.elapsedMs)}\n`,
86
- )
87
- }
88
-
89
- cb()
90
- },
91
- })
92
- }