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.
- package/bin/smp-download.js +3 -3
- package/bin/smp-mbtiles.js +1 -1
- package/bin/smp-view.js +2 -2
- package/dist/download.cjs +101 -0
- package/dist/download.d.cts +65 -0
- package/dist/{lib/download.d.ts → download.d.ts} +22 -6
- package/dist/download.js +77 -0
- package/dist/from-mbtiles.cjs +91 -0
- package/dist/from-mbtiles.d.cts +17 -0
- package/dist/from-mbtiles.d.ts +17 -0
- package/dist/from-mbtiles.js +57 -0
- package/dist/index.cjs +49 -0
- package/dist/index.d.cts +27 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +18 -0
- package/dist/reader-watch.cjs +135 -0
- package/dist/reader-watch.d.cts +24 -0
- package/dist/reader-watch.d.ts +24 -0
- package/dist/reader-watch.js +101 -0
- package/dist/reader.cjs +167 -0
- package/dist/reader.d.cts +62 -0
- package/dist/{lib/reader.d.ts → reader.d.ts} +16 -5
- package/dist/reader.js +138 -0
- package/dist/reporters.cjs +122 -0
- package/dist/reporters.d.cts +10 -0
- package/dist/{lib/reporters.d.ts → reporters.d.ts} +5 -2
- package/dist/reporters.js +88 -0
- package/dist/server.cjs +79 -0
- package/dist/server.d.cts +37 -0
- package/dist/server.d.ts +37 -0
- package/dist/server.js +45 -0
- package/dist/style-downloader.cjs +312 -0
- package/dist/style-downloader.d.cts +120 -0
- package/dist/{lib/style-downloader.d.ts → style-downloader.d.ts} +23 -13
- package/dist/style-downloader.js +288 -0
- package/dist/tile-downloader.cjs +158 -0
- package/dist/tile-downloader.d.cts +84 -0
- package/dist/{lib/tile-downloader.d.ts → tile-downloader.d.ts} +22 -10
- package/dist/tile-downloader.js +126 -0
- package/dist/{lib/writer.d.ts → types-B4Xn1F9K.d.cts} +74 -14
- package/dist/types-B4Xn1F9K.d.ts +189 -0
- package/dist/utils/errors.cjs +41 -0
- package/dist/utils/errors.d.cts +18 -0
- package/dist/{lib/utils → utils}/errors.d.ts +4 -2
- package/dist/utils/errors.js +16 -0
- package/dist/utils/fetch.cjs +96 -0
- package/dist/utils/fetch.d.cts +51 -0
- package/dist/{lib/utils → utils}/fetch.d.ts +10 -4
- package/dist/utils/fetch.js +62 -0
- package/dist/utils/file-formats.cjs +98 -0
- package/dist/utils/file-formats.d.cts +35 -0
- package/dist/{lib/utils → utils}/file-formats.d.ts +13 -3
- package/dist/utils/file-formats.js +62 -0
- package/dist/utils/geo.cjs +84 -0
- package/dist/utils/geo.d.cts +46 -0
- package/dist/{lib/utils → utils}/geo.d.ts +8 -6
- package/dist/utils/geo.js +56 -0
- package/dist/utils/mapbox.cjs +121 -0
- package/dist/utils/mapbox.d.cts +43 -0
- package/dist/utils/mapbox.d.ts +43 -0
- package/dist/utils/mapbox.js +91 -0
- package/dist/utils/misc.cjs +39 -0
- package/{lib/utils/misc.js → dist/utils/misc.d.cts} +5 -9
- package/dist/{lib/utils → utils}/misc.d.ts +5 -3
- package/dist/utils/misc.js +13 -0
- package/dist/utils/streams.cjs +130 -0
- package/dist/utils/streams.d.cts +73 -0
- package/dist/{lib/utils → utils}/streams.d.ts +14 -10
- package/dist/utils/streams.js +103 -0
- package/dist/utils/style.cjs +126 -0
- package/dist/utils/style.d.cts +69 -0
- package/dist/{lib/utils → utils}/style.d.ts +19 -9
- package/dist/utils/style.js +98 -0
- package/dist/utils/templates.cjs +114 -0
- package/dist/utils/templates.d.cts +78 -0
- package/dist/{lib/utils → utils}/templates.d.ts +24 -14
- package/dist/utils/templates.js +79 -0
- package/dist/writer.cjs +401 -0
- package/dist/writer.d.cts +7 -0
- package/dist/writer.d.ts +7 -0
- package/dist/writer.js +374 -0
- package/package.json +87 -33
- package/dist/lib/from-mbtiles.d.ts +0 -13
- package/dist/lib/index.d.ts +0 -10
- package/dist/lib/reader-watch.d.ts +0 -13
- package/dist/lib/server.d.ts +0 -15
- package/dist/lib/types.d.ts +0 -64
- package/dist/lib/utils/mapbox.d.ts +0 -41
- package/lib/download.js +0 -114
- package/lib/from-mbtiles.js +0 -83
- package/lib/index.js +0 -11
- package/lib/reader-watch.js +0 -133
- package/lib/reader.js +0 -165
- package/lib/reporters.js +0 -92
- package/lib/server.js +0 -81
- package/lib/style-downloader.js +0 -363
- package/lib/tile-downloader.js +0 -188
- package/lib/types.ts +0 -104
- package/lib/utils/errors.js +0 -24
- package/lib/utils/fetch.js +0 -100
- package/lib/utils/file-formats.js +0 -85
- package/lib/utils/geo.js +0 -87
- package/lib/utils/mapbox.js +0 -155
- package/lib/utils/streams.js +0 -165
- package/lib/utils/style.js +0 -174
- package/lib/utils/templates.js +0 -136
- 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
|
-
}
|
package/lib/from-mbtiles.js
DELETED
|
@@ -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'
|
package/lib/reader-watch.js
DELETED
|
@@ -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
|
-
}
|