@superrouteca/smap 1.0.5 → 1.1.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/CHANGELOG.md CHANGED
@@ -1,5 +1,35 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.1.0 — 2026-10-27
4
+
5
+ - Style layers: sources and layers of the Style Specification v8 are added as they are (`addSource`, `addStyleLayer`,
6
+ `setPaint`, `setLayout`, `setFilter`, `getStyleLayer`, `removeSource`), so layers copied from Mapbox Studio,
7
+ Maputnik or another map page work unchanged, and they are kept when the scheme or the language changes.
8
+ - Slots: a layer can be drawn over land and water (`bottom`), under the roads (`roads`), under the labels (`labels`)
9
+ or on top (`top`), or under a layer you name (`before`). The slots stay in place when the basemap changes. A layer
10
+ without a slot draws on top as before.
11
+ - Basemap options: `setBasemap` shows, hides and recolours water, land, parks, roads (by level), rail, buildings,
12
+ boundaries, points of interest and labels, and changes road widths; kept when the scheme or the language changes.
13
+ - Saved custom styles: `style: 'custom:<id>'` shows a style saved in the developer center, in every label language.
14
+ - Files: `addData` reads KML, KMZ, GPX, encoded polylines, WKT, WKB, CSV, TopoJSON and GeoJSON on the device and
15
+ draws their points, lines and areas, with the colours a file gives (KML styles and simplestyle properties) and a
16
+ popup of each feature's name and description. `toGeoJSON` and `toKML` write a layer out; `formats.read` reads a
17
+ file without a map; `convert` has sMap convert large files.
18
+ - External layers: raster tiles and TileJSON, OGC WMS and WMTS, vector tiles and PMTiles archives from any address,
19
+ and ArcGIS REST tile and vector tile services, each with its attribution beside the map's credit.
20
+ - `toJSON` and `loadLayers` save a map's basemap, sources and layers as one JSON document that every sMap SDK opens.
21
+ - deck.gl layers are drawn among the map's own layers (`addDeckLayer`, `removeDeckLayer`) when deck.gl is loaded.
22
+ - `rasterUrl` gives sMap's raster tiles of a scheme or custom style for tools without vector tiles; `map.rasterUrl`
23
+ gives them on the map's own node and key, in its style and language.
24
+ - `map.deckOverlay(props)` gives the map's deck.gl overlay and sets its other deck.gl props (tooltips, clicks).
25
+ - The JS client's `demote(url)` moves a node that did not answer to the end of the list, so tools that read tiles
26
+ themselves follow the same node order; `apiKey` and `renewsToken` read how the client was set up.
27
+
28
+ ## 1.0.6 — 2026-10-27
29
+
30
+ - Close up (from zoom 13, `bakeZoom`) each pin's number is drawn into the pin, so where pins overlap the pin on top covers the one below completely.
31
+ - Marker shape `vehicle`: the route map's live position, a white circle with a truck in each feature's own colour, in one layer for any number of drivers. Point labels can sit to the right (`labelPosition: 'right'`).
32
+
3
33
  ## 1.0.5 — 2026-10-27
4
34
 
5
35
  - Auto Zoom has a switch: the map option `autoZoom` and `map.setAutoZoom(on)`. In 3D, W / S tilt and A / D turn the view (`keyboard3D`).
package/README.md CHANGED
@@ -7,10 +7,11 @@
7
7
 
8
8
  Maps from Superroute's sMap service for the web: five colour schemes, labels in any language, your points, lines and
9
9
  areas drawn on the GPU (hundreds of thousands of points, clustered or not), 3D buildings and areas, and tokens that
10
- renew themselves. Also the JS client (no map view) for servers and workers: tokens, the nearest node, styles, tiles
10
+ renew themselves; standard style layers, basemap options, KML / GPX / CSV and other files, WMS, WMTS, PMTiles and
11
+ ArcGIS layers, deck.gl layers, and maps saved as JSON. Also the JS client (no map view) for servers and workers: tokens, the nearest node, styles, tiles
11
12
  and raw reads.
12
13
 
13
- - Version 1.0.5, MIT licence (see `LICENSE` at the repository root)
14
+ - Version 1.1.0, MIT licence (see `LICENSE` at the repository root)
14
15
  - Works with bundlers (Vite, webpack, Next.js and others): ES module and CommonJS entry points with types
15
16
  - Nothing else to install: the map renderer is bundled in the package
16
17
  - Importing is safe on a server; a map is created in the browser only
@@ -159,7 +160,7 @@ with `default` for features without it) or an engine expression (an array such a
159
160
 
160
161
  | Option | Meaning |
161
162
  |---|---|
162
- | `shape` | `'pin'` (default; the tip is at the position) or `'circle'` |
163
+ | `shape` | `'pin'` (default; the tip is at the position), `'circle'`, or `'vehicle'` (a white circle with a truck in the `border` colour, the route map's live position) |
163
164
  | `fill` | inside colour (default white) |
164
165
  | `border` | border colour (default the layer's `color`) |
165
166
  | `borderWidth` | pixels (default 3) |
@@ -169,6 +170,7 @@ with `default` for features without it) or an engine expression (an array such a
169
170
  | `badge` | a red count at the upper right, shown when more than 1 |
170
171
  | `opacity` | of the pin, its number and its badge together (e.g. settled stops paler) |
171
172
  | `overlap` | Auto Zoom for this layer (map option `autoZoom`, `map.setAutoZoom(on)`, on by default): pins piled up under a click: 4 or more within 12 px zoom the map in by 2; close up (zoom 17) or at one spot a chooser lists them, `label(properties)` names each row; `false` turns it off |
173
+ | `bakeZoom` | from this zoom on (default 13) each pin's number is drawn into the pin, so an upper pin covers a lower one completely; `false` keeps numbers a layer of their own |
172
174
  | `hoverRaise` | the pin under the pointer comes to the front, so overlapping pins can be read and clicked (default `true`) |
173
175
  | `scale` | size factor of the pin, its number and its badge together; a number or an expression of the zoom, e.g. `['interpolate', ['linear'], ['zoom'], 9, 0.5, 14, 1]` |
174
176
  | `textMinzoom`, `badgeMinzoom` | show numbers / badges only from this zoom on, so an overview of many stops stays readable |
@@ -199,6 +201,173 @@ map.addPoints('stops', stops, { // {lng, lat, stop_number, status_colo
199
201
  Numbers and badges are drawn over all pins of the layer, so where pins overlap a lower pin's number can show through
200
202
  an upper pin.
201
203
 
204
+ ## Style layers and sources
205
+
206
+ Sources and layers of the Style Specification v8 are passed to the map as they are, so a layer copied from Mapbox
207
+ Studio, Maputnik or another map page works unchanged. They are kept when the scheme or the language changes.
208
+
209
+ ```js
210
+ map.addSource('parcels', { type: 'vector', tiles: ['https://tiles.example/{z}/{x}/{y}.pbf'], attribution: '© Example' });
211
+ map.addStyleLayer({ id: 'parcel-lines', type: 'line', source: 'parcels', 'source-layer': 'parcels',
212
+ paint: { 'line-color': '#64748b' } }, { slot: 'roads' });
213
+ map.setPaint('parcel-lines', 'line-width', 2);
214
+ map.setLayout('parcel-lines', 'visibility', 'none');
215
+ map.setFilter('parcel-lines', ['==', ['get', 'zoning'], 'R1']);
216
+ map.getStyleLayer('parcel-lines'); // the layer as a Style Specification object
217
+ map.removeSource('parcels'); // with the layers that read it
218
+ ```
219
+
220
+ `setPaint`, `setLayout` and `setFilter` also work on the basemap's own layers. `removeLayer(id)` removes a layer of
221
+ any kind.
222
+
223
+ ## Slots
224
+
225
+ A layer is placed relative to the basemap by a slot, or under a layer you name. A layer without either draws as in
226
+ 1.0: on top, in the order added.
227
+
228
+ | Slot | Where |
229
+ |---|---|
230
+ | `bottom` | over land and water, under everything else |
231
+ | `roads` | under the roads |
232
+ | `labels` | under the labels |
233
+ | `top` | on top |
234
+
235
+ ```js
236
+ map.addPolygons('zones', zones, { slot: 'bottom' });
237
+ map.addLines('routes', routes, { slot: 'labels' }); // road names stay readable over the routes
238
+ map.addStyleLayer(layer, { before: 'routes' }); // under one of your layers, or a basemap layer
239
+ ```
240
+
241
+ The slots are found from the basemap's classes, so they stay in place when the basemap changes. `map.slots()` gives
242
+ the layer each slot begins at. `zIndex` orders layers inside one slot.
243
+
244
+ ## Basemap options
245
+
246
+ `setBasemap` shows, hides and recolours the basemap by class. The classes are `land`, `landcover`, `parks`, `landuse`,
247
+ `airports`, `water`, `roads` (and `roads.highway`, `roads.major`, `roads.minor`, `roads.paths`), `rail`, `buildings`,
248
+ `boundaries`, `labels` (and `labels.places`, `labels.roads`, `labels.water`, `labels.addresses`) and `pois`.
249
+
250
+ ```js
251
+ map.setBasemap({
252
+ water: { color: '#9cc0e6' },
253
+ roads: { color: '#ffffff', width: 1.2 }, // every roads.* class
254
+ 'roads.highway': { color: '#f6c26b' }, // the class itself wins over roads
255
+ pois: { visible: false },
256
+ labels: { halo: '#ffffff' },
257
+ });
258
+ map.basemap(); // the overrides in force
259
+ map.setBasemap({}); // back to the scheme
260
+ ```
261
+
262
+ | Field | Meaning |
263
+ |---|---|
264
+ | `visible` | show or hide |
265
+ | `color` | fill, line or background colour; the text colour for labels |
266
+ | `casing` | the casing colour of roads |
267
+ | `halo` | the halo colour of labels |
268
+ | `opacity` | 0 to 1 |
269
+ | `width` | a line width factor, 0.25 to 4, of the scheme's own width |
270
+
271
+ Each call replaces the overrides set before. They are kept when the scheme or the language changes. An unknown class
272
+ or field throws an error that names it. `applyBasemapOverrides(style, overrides)` applies the same rules to a style
273
+ document (for example on a server).
274
+
275
+ **Saved custom styles.** A style saved in the Superroute developer center is used by its id:
276
+ `new Map({ style: 'custom:sms_…' })` or `map.setStyle('custom:sms_…')`. It is served in every label language, with
277
+ your key or token, like the five schemes.
278
+
279
+ ## Files: KML, GPX, CSV and more
280
+
281
+ `addData` reads a file on the device and draws it: points as the layer `<id>`, lines as `<id>:lines`, areas as
282
+ `<id>:areas`.
283
+
284
+ ```js
285
+ await map.addData('trip', file); // a File from an <input type="file">
286
+ await map.addData('stores', csvText, { format: 'csv', lat: 'Latitude', lng: 'Longitude', slot: 'top' });
287
+ await map.addData('route', '_p~iF~ps|U_ulLnnqC', { format: 'polyline', width: 5 });
288
+ map.toGeoJSON('trip'); // the features as GeoJSON
289
+ map.toKML('trip'); // the features as a KML document
290
+ map.removeLayer('trip'); // all three layers
291
+ ```
292
+
293
+ | Format | Reads |
294
+ |---|---|
295
+ | `kml`, `kmz` | Google Earth and My Maps files: placemarks, extended data, styles as simplestyle colours, tracks |
296
+ | `gpx` | waypoints, routes, tracks (with elevation) |
297
+ | `polyline`, `polyline6` | encoded polylines, precision 5 (Google, OSRM) and 6 (Valhalla) |
298
+ | `wkt`, `wkb` | Well-Known Text and Binary (also EWKB and hexadecimal text) |
299
+ | `csv` | latitude and longitude columns, named or found from common headings |
300
+ | `topojson`, `geojson` | TopoJSON topologies, GeoJSON (RFC 7946) |
301
+
302
+ The format comes from the file name or the content when not given (polylines are always named). A layer with no
303
+ colour of its own draws the simplestyle properties of its features (`marker-color`, `marker-size`, `stroke`,
304
+ `stroke-width`, `stroke-opacity`, `fill`, `fill-opacity`), so files from geojson.io, GitHub or Mapbox look as they do
305
+ there. A click opens a popup with the feature's title (or name) and description, unless `popup` is given.
306
+
307
+ `formats.read(input, { format, lat, lng })` gives the FeatureCollection without a map. `convert(file, { format })`
308
+ (and `map.convert`) has sMap convert a file instead: for large files (up to 20 MB). The answer is the same.
309
+
310
+ ## External layers
311
+
312
+ The attribution of each source is shown beside the map's credit. Google and Bing tiles are refused, as their licences
313
+ do not allow them.
314
+
315
+ ```js
316
+ map.addRasterLayer('osm', { tiles: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', attribution: '© OpenStreetMap', opacity: 0.6 });
317
+ map.addRasterLayer('imagery', { url: 'https://tiles.example/imagery.json' }); // TileJSON
318
+ map.addWmsLayer('flood', { url: 'https://wms.example/ows', layers: 'flood_zones', slot: 'labels' });
319
+ await map.addWmtsLayer('ortho', { url: 'https://wmts.example/WMTSCapabilities.xml', layer: 'ortho' });
320
+ map.addVectorTiles('parcels', { pmtiles: 'https://data.example/parcels.pmtiles',
321
+ layers: [{ id: 'parcel-fill', type: 'fill', 'source-layer': 'parcels', paint: { 'fill-color': '#fde68a' } }] });
322
+ await map.addArcGisLayer('esri', { url: 'https://services.example/arcgis/rest/services/World/MapServer' });
323
+ ```
324
+
325
+ - `addRasterLayer`: XYZ templates (`tiles`) or a TileJSON address (`url`); `tileSize`, `opacity`, `minzoom`, `maxzoom`.
326
+ - `addWmsLayer`: OGC WMS 1.3.0 (or 1.1.1), one GetMap per tile in Web Mercator; `layers`, `styles`, `format`,
327
+ `transparent`, `params`.
328
+ - `addWmtsLayer`: OGC WMTS from the GetCapabilities address (KVP or REST, Web Mercator matrix sets), or a REST
329
+ template with `{TileMatrix}`, `{TileRow}` and `{TileCol}`. Returns a promise.
330
+ - `addVectorTiles`: Mapbox Vector Tiles from templates (`tiles`), a TileJSON address (`url`) or a PMTiles archive
331
+ (`pmtiles`, or a `url` ending in `.pmtiles`), drawn by your style layers.
332
+ - `addArcGisLayer`: an ArcGIS REST MapServer / ImageServer (raster tiles) or VectorTileServer (drawn by `layers`, or
333
+ by the service's own style); the service's copyright text is its attribution. Returns a promise.
334
+
335
+ Every external layer takes `slot`, `before` and `zIndex`. `rasterUrl(style, { lang, retina })` gives sMap's own
336
+ raster tiles as an XYZ template (`rasterTileJsonUrl` their TileJSON), for tools without vector tiles such as QGIS;
337
+ `map.rasterUrl(style?, { lang, retina })` gives them on the map's own node and key, in its style and language.
338
+
339
+ ## A map as JSON
340
+
341
+ `map.toJSON()` writes the map's basemap (scheme, language, overrides), sources and layers as one JSON document, and
342
+ `map.loadLayers(json)` loads it, on the web and in every other sMap SDK. A map built on a page opens the same in an
343
+ app. The shape is described in `shared/LAYER-JSON.md` of the repository, with a JSON Schema. Popups are written as
344
+ templates (`popup: '<b>{name}</b>'`); popup functions, click handlers and deck.gl layers are code and are not
345
+ written.
346
+
347
+ ## deck.gl layers
348
+
349
+ deck.gl layers are drawn among the map's own layers, under the labels if you like, through deck.gl's map overlay.
350
+ deck.gl is optional and loaded only by you (deck.gl 9; `@deck.gl/mapbox` and the layer packages you use):
351
+
352
+ ```js
353
+ import { useDeck } from '@superrouteca/smap';
354
+ import * as deckMapbox from '@deck.gl/mapbox';
355
+ import { ArcLayer } from '@deck.gl/layers';
356
+
357
+ useDeck(deckMapbox);
358
+ map.addDeckLayer(new ArcLayer({ id: 'flows', data, getSourcePosition: (d) => d.from, getTargetPosition: (d) => d.to }), { slot: 'labels' });
359
+ map.removeDeckLayer('flows');
360
+ ```
361
+
362
+ On a page without a bundler, the script build of deck.gl (its global `deck`) is used without `useDeck`.
363
+
364
+ `map.deckOverlay(props)` gives the map's deck.gl overlay (made on first use) and sets its other deck.gl props, such as
365
+ tooltips and clicks; its layers stay the ones added with `addDeckLayer`:
366
+
367
+ ```js
368
+ map.deckOverlay({ getTooltip: ({ object }) => object && object.name });
369
+ ```
370
+
202
371
  ## Map keys and tokens
203
372
 
204
373
  - **Map key** (`apiKey: 'smk_…'`): created in the Superroute developer center, with the sites (and apps) it may be
@@ -223,6 +392,10 @@ const tile = await client.tile('basemap', 12, 1143, 1493); // Uint8Array, or n
223
392
 
224
393
  Runs wherever `fetch` exists (browsers, Node.js 18+, workers); elsewhere pass `fetch` in the options.
225
394
 
395
+ `client.nodes()` lists the nodes nearest first; `client.demote(url)` moves a node that did not answer to the end, so a
396
+ tool that reads tiles itself follows the same order as the client. `client.apiKey` and `client.renewsToken` (a
397
+ `getToken` was given) say how the client was set up.
398
+
226
399
  ## Data attribution
227
400
 
228
401
  The map data credit stays visible on every map, as the data licence requires. Every map shows a small sMap badge in
@@ -230,7 +403,8 @@ its bottom-right corner, left of the attribution (above it when the map is too n
230
403
 
231
404
  ## Where the code comes from
232
405
 
233
- The sources are in `src/` (the Web SDK `smap.js` and `smap.css`, the JS client `smap-client.js` and its types).
406
+ The sources are in `src/` (the Web SDK `smap.js` and `smap.css`, the parts that need no map `smap-interop.js`:
407
+ formats, basemap options, slots, URLs, PMTiles; the JS client `smap-client.js` and its types).
234
408
  `npm run build` writes `dist/` from them and the rendering engine (an exact devDependency); `dist/SOURCE.json`
235
409
  records the version, the engine and the hash of every file. `dist/` is committed and never edited by hand. The type
236
410
  declarations of the map SDK are in `types/`.
package/dist/SOURCE.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
- "sdkVersion": "1.0.5",
2
+ "sdkVersion": "1.1.0",
3
3
  "engine": "maplibre-gl 6.16.0",
4
4
  "files": {
5
- "smap.js": "3b8d6eaa6a2ddd6fb14b1465a9c6ef46a09d550093bb567b613fda3b26fa1fe5",
5
+ "smap.js": "f941ddea5c9145bc0fa66c4764971443540c66a551a693027f480245c6dcb794",
6
6
  "smap.css": "5e3bdcb00b8fc8805f545b76cdeea5e783c89984f48ec2fee66c4319af4ada0b",
7
- "smap-client.js": "1ae5f49b4be515efa69570a110b862ac8f325a6b45a9438a5c8011fa9278f598",
8
- "smap-client.d.ts": "ad701612630a290b2dc0e49250ae25cea965ecb3a145c016bb462cd30f108cf7",
9
- "smap.mjs": "7ea12152fa21a7d3e2c235646998cd8e36fded1db808945289123dc19c11424e",
10
- "smap.cjs": "668b85f95674a22b7072566ec91f499de3623cddb10afb28aa32c528a39313da",
11
- "smap-client.cjs": "6c54aa4a7676c26e7c4569203afb19f6c20233dc96f3e1400087b035f4e7d4f0",
12
- "smap-client.mjs": "207bcb78118603359d73aecbea3f422d101b729f5e8462c1bb05225ad886d1f5",
13
- "smap-client.d.cts": "80b96f5a907caf6295ca073816dc526e8d285f22670c809adaab5242a018e686",
14
- "smap-client.d.mts": "a66834cab868a6a0eaf943c1b185a236916cb00ecf32a871f972ab3213eddeba",
7
+ "smap-client.js": "3c31e433df68578f53bf0a85fa2d8772ae3f7d77663c59593bed04b0635bb5ce",
8
+ "smap-client.d.ts": "5f0df11bf60c5a9ced34522a284de81260c27871678ab772f16da9ccf6883f58",
9
+ "smap.mjs": "60225e3eac69195e29ec120ed24abd94b636f630ec4e1434af5b961ad91ce469",
10
+ "smap.cjs": "0593cd1b26cb27140ca0353dcd34093d5d6be7781f43d9e20ae7b4c9d74302a3",
11
+ "smap-client.cjs": "2dc2391b3d8c222edf42d781a23bd818222069f28e98b7715adf39908e3bbd09",
12
+ "smap-client.mjs": "1e4500b6e28899072dd27f6dd8ee3a1a84c4b697e40fb1ba0463c0bf6cae004c",
13
+ "smap-client.d.cts": "e0fe4eb54cd66b831116082f42bd2f7de260f9f12a2741f4004f8cf10c252116",
14
+ "smap-client.d.mts": "96830d316ddb33b4ff8edb8b6f810337479636114f75c4170d95038ffda74103",
15
15
  "vendor-licenses.txt": "ee5fc05a0677eaf69601d2c7db0d9ecd6cc27c3abc1d0733bc9ed34707cf8ef2"
16
16
  }
17
17
  }
@@ -1,5 +1,5 @@
1
1
  /* Generated by js/build.mjs from js/src (see SOURCE.json). Do not edit. */
2
- /*! sMap JS client 1.0.5 — tokens, the nearest node, styles, tiles and raw reads, without a map view. */
2
+ /*! sMap JS client 1.1.0 — tokens, the nearest node, styles, tiles and raw reads, without a map view. */
3
3
  /*
4
4
  * Runs wherever `fetch` exists: browsers, Node.js 18+, workers; elsewhere (mini programs, older runtimes) pass your
5
5
  * own `fetch`. UMD: `window.SMapClient`, `require('smap-client')`, or an AMD module.
@@ -19,7 +19,7 @@
19
19
  }(typeof self !== 'undefined' ? self : this, function () {
20
20
  'use strict';
21
21
 
22
- var VERSION = '1.0.5';
22
+ var VERSION = '1.1.0';
23
23
  var NODE_TTL = 3600 * 1000; // the measured order
24
24
  var KNOWN_TTL = 30 * 86400 * 1000; // the last node list received, used when none can be read
25
25
 
@@ -234,11 +234,25 @@
234
234
  });
235
235
  };
236
236
 
237
- SMapClient.prototype._demote = function (url) {
237
+ /**
238
+ * Reports that a node did not answer: it is tried last from now on (by this client's requests and by anything
239
+ * reading nodes()). Used by tools that read tiles themselves (the Leaflet, OpenLayers, Mapbox GL JS, deck.gl, ArcGIS
240
+ * and CesiumJS adapters) so that their requests and the client's agree.
241
+ *
242
+ * @param {string} url a node URL as nodes() lists it
243
+ */
244
+ SMapClient.prototype.demote = function (url) {
238
245
  if (!this._nodes || this._nodes.length < 2) { return; }
246
+ url = String(url).replace(/\/$/, '');
239
247
  var i = this._nodes.indexOf(url);
240
248
  if (i >= 0) { this._nodes.splice(i, 1); this._nodes.push(url); }
241
249
  };
250
+ // The 1.0 name, kept for callers that used it.
251
+ SMapClient.prototype._demote = SMapClient.prototype.demote;
252
+
253
+ // The map key given to the client (null without one), and whether it renews its token (a getToken was given).
254
+ Object.defineProperty(SMapClient.prototype, 'apiKey', { get: function () { return this._apiKey; }, configurable: true });
255
+ Object.defineProperty(SMapClient.prototype, 'renewsToken', { get: function () { return !!this._getToken; }, configurable: true });
242
256
 
243
257
  function bytes(res) { return res.arrayBuffer().then(function (b) { return new Uint8Array(b); }); }
244
258
 
@@ -32,6 +32,12 @@ declare class SMapClient {
32
32
  nodes(): Promise<string[]>;
33
33
  resetNodes(): void;
34
34
  request(path: string, headers?: Record<string, string>): Promise<Response>;
35
+ /** The map key given to the client, or null. */
36
+ readonly apiKey: string | null;
37
+ /** True when the client renews its token (a getToken was given). */
38
+ readonly renewsToken: boolean;
39
+ /** Reports that a node did not answer: it is tried last from now on. */
40
+ demote(url: string): void;
35
41
  style(flavor?: SMapClient.StyleFlavor, language?: string): Promise<Record<string, unknown>>;
36
42
  tileJSON(source?: string): Promise<Record<string, unknown>>;
37
43
  tile(source: string, z: number, x: number, y: number): Promise<Uint8Array | null>;
@@ -32,6 +32,12 @@ declare class SMapClient {
32
32
  nodes(): Promise<string[]>;
33
33
  resetNodes(): void;
34
34
  request(path: string, headers?: Record<string, string>): Promise<Response>;
35
+ /** The map key given to the client, or null. */
36
+ readonly apiKey: string | null;
37
+ /** True when the client renews its token (a getToken was given). */
38
+ readonly renewsToken: boolean;
39
+ /** Reports that a node did not answer: it is tried last from now on. */
40
+ demote(url: string): void;
35
41
  style(flavor?: SMapClient.StyleFlavor, language?: string): Promise<Record<string, unknown>>;
36
42
  tileJSON(source?: string): Promise<Record<string, unknown>>;
37
43
  tile(source: string, z: number, x: number, y: number): Promise<Uint8Array | null>;
@@ -31,6 +31,12 @@ declare class SMapClient {
31
31
  nodes(): Promise<string[]>;
32
32
  resetNodes(): void;
33
33
  request(path: string, headers?: Record<string, string>): Promise<Response>;
34
+ /** The map key given to the client, or null. */
35
+ readonly apiKey: string | null;
36
+ /** True when the client renews its token (a getToken was given). */
37
+ readonly renewsToken: boolean;
38
+ /** Reports that a node did not answer: it is tried last from now on. */
39
+ demote(url: string): void;
34
40
  style(flavor?: SMapClient.StyleFlavor, language?: string): Promise<Record<string, unknown>>;
35
41
  tileJSON(source?: string): Promise<Record<string, unknown>>;
36
42
  tile(source: string, z: number, x: number, y: number): Promise<Uint8Array | null>;
@@ -1,4 +1,4 @@
1
- /*! sMap JS client 1.0.5 — tokens, the nearest node, styles, tiles and raw reads, without a map view. */
1
+ /*! sMap JS client 1.1.0 — tokens, the nearest node, styles, tiles and raw reads, without a map view. */
2
2
  /*
3
3
  * Runs wherever `fetch` exists: browsers, Node.js 18+, workers; elsewhere (mini programs, older runtimes) pass your
4
4
  * own `fetch`. UMD: `window.SMapClient`, `require('smap-client')`, or an AMD module.
@@ -18,7 +18,7 @@
18
18
  }(typeof self !== 'undefined' ? self : this, function () {
19
19
  'use strict';
20
20
 
21
- var VERSION = '1.0.5';
21
+ var VERSION = '1.1.0';
22
22
  var NODE_TTL = 3600 * 1000; // the measured order
23
23
  var KNOWN_TTL = 30 * 86400 * 1000; // the last node list received, used when none can be read
24
24
 
@@ -233,11 +233,25 @@
233
233
  });
234
234
  };
235
235
 
236
- SMapClient.prototype._demote = function (url) {
236
+ /**
237
+ * Reports that a node did not answer: it is tried last from now on (by this client's requests and by anything
238
+ * reading nodes()). Used by tools that read tiles themselves (the Leaflet, OpenLayers, Mapbox GL JS, deck.gl, ArcGIS
239
+ * and CesiumJS adapters) so that their requests and the client's agree.
240
+ *
241
+ * @param {string} url a node URL as nodes() lists it
242
+ */
243
+ SMapClient.prototype.demote = function (url) {
237
244
  if (!this._nodes || this._nodes.length < 2) { return; }
245
+ url = String(url).replace(/\/$/, '');
238
246
  var i = this._nodes.indexOf(url);
239
247
  if (i >= 0) { this._nodes.splice(i, 1); this._nodes.push(url); }
240
248
  };
249
+ // The 1.0 name, kept for callers that used it.
250
+ SMapClient.prototype._demote = SMapClient.prototype.demote;
251
+
252
+ // The map key given to the client (null without one), and whether it renews its token (a getToken was given).
253
+ Object.defineProperty(SMapClient.prototype, 'apiKey', { get: function () { return this._apiKey; }, configurable: true });
254
+ Object.defineProperty(SMapClient.prototype, 'renewsToken', { get: function () { return !!this._getToken; }, configurable: true });
241
255
 
242
256
  function bytes(res) { return res.arrayBuffer().then(function (b) { return new Uint8Array(b); }); }
243
257
 
@@ -1,7 +1,7 @@
1
1
  /* Generated by js/build.mjs from js/src (see SOURCE.json). Do not edit. */
2
2
  var smapClientModule = { exports: {} };
3
3
  (function (module, exports, define) {
4
- /*! sMap JS client 1.0.5 — tokens, the nearest node, styles, tiles and raw reads, without a map view. */
4
+ /*! sMap JS client 1.1.0 — tokens, the nearest node, styles, tiles and raw reads, without a map view. */
5
5
  /*
6
6
  * Runs wherever `fetch` exists: browsers, Node.js 18+, workers; elsewhere (mini programs, older runtimes) pass your
7
7
  * own `fetch`. UMD: `window.SMapClient`, `require('smap-client')`, or an AMD module.
@@ -21,7 +21,7 @@ var smapClientModule = { exports: {} };
21
21
  }(typeof self !== 'undefined' ? self : this, function () {
22
22
  'use strict';
23
23
 
24
- var VERSION = '1.0.5';
24
+ var VERSION = '1.1.0';
25
25
  var NODE_TTL = 3600 * 1000; // the measured order
26
26
  var KNOWN_TTL = 30 * 86400 * 1000; // the last node list received, used when none can be read
27
27
 
@@ -236,11 +236,25 @@ var smapClientModule = { exports: {} };
236
236
  });
237
237
  };
238
238
 
239
- SMapClient.prototype._demote = function (url) {
239
+ /**
240
+ * Reports that a node did not answer: it is tried last from now on (by this client's requests and by anything
241
+ * reading nodes()). Used by tools that read tiles themselves (the Leaflet, OpenLayers, Mapbox GL JS, deck.gl, ArcGIS
242
+ * and CesiumJS adapters) so that their requests and the client's agree.
243
+ *
244
+ * @param {string} url a node URL as nodes() lists it
245
+ */
246
+ SMapClient.prototype.demote = function (url) {
240
247
  if (!this._nodes || this._nodes.length < 2) { return; }
248
+ url = String(url).replace(/\/$/, '');
241
249
  var i = this._nodes.indexOf(url);
242
250
  if (i >= 0) { this._nodes.splice(i, 1); this._nodes.push(url); }
243
251
  };
252
+ // The 1.0 name, kept for callers that used it.
253
+ SMapClient.prototype._demote = SMapClient.prototype.demote;
254
+
255
+ // The map key given to the client (null without one), and whether it renews its token (a getToken was given).
256
+ Object.defineProperty(SMapClient.prototype, 'apiKey', { get: function () { return this._apiKey; }, configurable: true });
257
+ Object.defineProperty(SMapClient.prototype, 'renewsToken', { get: function () { return !!this._getToken; }, configurable: true });
244
258
 
245
259
  function bytes(res) { return res.arrayBuffer().then(function (b) { return new Uint8Array(b); }); }
246
260