tileflow 0.1.0-alpha.22 → 0.1.0-alpha.23

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 CHANGED
@@ -197,12 +197,12 @@ Only `visual update` changes baseline PNG/receipt pairs. `visual diff` writes wo
197
197
  approval. Changed pixels normally exit 0; `--fail-on changed` exits 2. Missing or incompatible
198
198
  baselines and operational failures exit 1. Never run `visual update` automatically to make CI pass.
199
199
 
200
- Use `visual analyze <scene> --reference <png>` for an external screenshot without a Tileflow
201
- receipt. Use `visual compare` for an authenticated two-style review with matched camera, viewport,
202
- renderer, and data identity. Neither operation approves a baseline. Capture watch mode emits NDJSON
203
- generation events rather than a single JSON document.
200
+ `visual analyze <scene> --reference <png>` checks an external screenshot. `visual compare` runs an
201
+ authenticated two-style review with matched camera, viewport, renderer, and data. Neither approves a
202
+ baseline. Capture watch mode emits NDJSON generation events rather than a single JSON document.
203
+ `visual report` and `visual approve` read and approve a pull request's visual review.
204
204
 
205
- Use `tileflow capture --help` and `tileflow compare --help` for installed command options.
205
+ See `--help` on each command for installed options.
206
206
 
207
207
  ## Publish data and deploy a map
208
208
 
@@ -261,27 +261,40 @@ refuse to overwrite hosted manifests by default, and deploy refuses to replace a
261
261
  manifest without explicit authorization. Keep separate outputs, or use the documented overwrite
262
262
  option only for an intentional migration.
263
263
 
264
- Data publication is separate from map deployment:
264
+ Data publication is separate from map deployment. Build PMTiles locally from GeoJSON, CSV,
265
+ Shapefile, GeoPackage, KML or GPX:
265
266
 
266
267
  ```sh
268
+ npx tileflow tileset build ./data/stores.geojson -o ./data/stores.pmtiles --json
267
269
  npx tileflow tileset inspect ./data/stores.pmtiles --json
268
270
  npx tileflow tileset publish ./data/stores.pmtiles --id stores --team @acme --attribution 'Store data © Example' --json
269
271
  npx tileflow tileset status stores --team @acme --json
270
272
  ```
271
273
 
272
- Inspect without an account; publish your own data with Team data authority and attribution.
273
- Account-backed authorization renews automatically. Interrupted uploads resume; published versions become
274
+ See the
275
+ [reference](https://github.com/tileflow/tileflow-sdk/blob/main/packages/cli/REFERENCE.md) for inputs,
276
+ CRS rules, limits and upload behavior. Publish your own data with Team data authority and attribution.
277
+ Interrupted uploads resume; published versions become
274
278
  current at stable URLs. Purge requires confirmation and may be blocked by retained deployments.
275
279
 
276
- Hashing reuses a 16 MiB buffer. Up to four parts stream concurrently with reusable buffers.
277
-
278
- Administratively disabled uploads return `TF_TILESET_UPLOAD_DISABLED` without retrying.
279
- Temporary service failures retain bounded retries.
280
-
281
280
  `inspect features` samples bounded vector-source features. `icons list` and `icons diff` inspect
282
281
  prepared icon identities; they do not turn package directories into a separate hosted library.
283
282
  Use the installed command's help for exact arguments and supported JSON output.
284
283
 
284
+ ## Restore an earlier version
285
+
286
+ Restoring a deployment publishes its content again as a new version of the Map. Restoring a tileset
287
+ swaps its current and previous versions for every Map that uses it:
288
+
289
+ ```bash
290
+ npx tileflow deployments list --map-id map_AbCdEfGhIjKlMnOp
291
+ npx tileflow deployments restore 12 --map-id map_AbCdEfGhIjKlMnOp
292
+ npx tileflow tileset restore stores --team @acme
293
+ ```
294
+
295
+ See the [reference](https://github.com/tileflow/tileflow-sdk/blob/main/packages/cli/REFERENCE.md#restore)
296
+ for refusals, clearing an abandoned upload and JSON output.
297
+
285
298
  ## Team Icon Sets and repository locks
286
299
 
287
300
  Two intentionally separate families cover shared icons. `icon-set` manages the remote Team catalog
@@ -303,13 +316,6 @@ revisions. `purge <slug> --version <n>` additionally requires `--idempotency-key
303
316
  `--confirm @team/set@version`, and `--acknowledge-unknown-locks`, because repository locks that were
304
317
  never deployed are not discoverable and already-downloaded public bytes cannot be revoked.
305
318
 
306
- The lock commands inspect the `iconSet()` references your config declares. They are the only
307
- commands that resolve `latest`, and the only ones that write the lock. `install` fills in declared
308
- references that have no pin, `update` moves selected references to their current latest revision,
309
- and `pin` selects one exact revision. Every requested reference is resolved before a single
310
- compare-and-swap write of the whole snapshot, so a concurrent writer fails the command instead of
311
- losing its pins. No lock command rewrites `tileflow.config.ts`.
312
-
313
319
  `validate`, `build`, `preview`, `deploy`, `icons list` and `icons diff` read the lock and never
314
320
  resolve a catalog head. `--cache-dir` chooses the verified artifact cache root and `--offline` fails
315
321
  a cache miss instead of hydrating it. Publishing a newer revision changes nothing in a repository
package/REFERENCE.md ADDED
@@ -0,0 +1,429 @@
1
+ # CLI reference
2
+
3
+ Detailed contracts for the `tileflow` CLI. Start with the
4
+ [README](https://github.com/tileflow/tileflow-sdk/blob/main/packages/cli/README.md) to install it.
5
+ For step-by-step guides, see the [Tileflow documentation](https://tileflow.dev/docs).
6
+
7
+ This reference ships with the package. Use the copy included in your installed release for
8
+ version-specific behavior; the GitHub `main` branch can contain unreleased changes.
9
+
10
+ ## tileset build
11
+
12
+ `tileflow tileset build <input>` turns local vector data into a PMTiles archive ready for
13
+ `tileflow tileset publish`. It runs on your machine, needs no account and no extra tools, and sends
14
+ nothing anywhere. Success means the archive already passed the validation `publish` applies. Build
15
+ output is deterministic: the same input, options and CLI version produce the same tiles and metadata on any
16
+ system, and the same bytes as long as Node.js compresses the same way, so republishing unchanged data reports
17
+ `unchanged`.
18
+
19
+ ```sh
20
+ npx tileflow tileset build ./data/stores.geojson -o ./data/stores.pmtiles --json
21
+ ```
22
+
23
+ ### Inputs
24
+
25
+ | Format | Detected from | Notes |
26
+ | ------------------------- | ------------------------------------------------------------ | ---------------------------------------------------------------------------- |
27
+ | GeoJSON | `.geojson`, `.json` | FeatureCollection, Feature or geometry. Streamed. |
28
+ | Newline-delimited GeoJSON | `.ndjson`, `.geojsonl`, `.geojsons`, `.geojsonseq`, `.jsonl` | One Feature or geometry per line; RFC 8142 separators are accepted. |
29
+ | CSV | `.csv`, `.tsv` | UTF-8 with a header row. Comma, semicolon, tab or pipe delimited. |
30
+ | Shapefile | `.shp`, or `.zip` holding one | Reads `.dbf`, `.prj` and `.cpg` beside it. Z and M values are ignored. |
31
+ | GeoPackage | `.gpkg` | One feature table. Needs Node.js 22.13 or later. Z and M values are ignored. |
32
+ | KML | `.kml`, or `.kmz` holding one | Placemarks only. Altitude and styles are ignored. |
33
+ | GPX | `.gpx` | Waypoints, routes and tracks each become a feature. |
34
+
35
+ Pass `--format csv|geojson|geojsonseq|geopackage|gpx|kml|shapefile` when the extension is misleading. A
36
+ zip with several Shapefiles, or a KMZ with several KML documents and no `doc.kml`, needs
37
+ `--zip-entry <path>` to choose one. Other formats inside a zip are not read. Other formats, such as
38
+ FlatGeobuf, are not supported.
39
+
40
+ CSV coordinate columns are found by name: `lon`, `lng`, `long`, `longitude`, `lat`, `latitude`, in
41
+ any case. Use `--lon-column` and `--lat-column` for anything else. Columns are typed as a whole, so a
42
+ value like `01234` keeps a whole column as text, and empty cells are omitted from the feature.
43
+
44
+ A GeoPackage with several feature tables needs `--input-layer <table>`, and the error lists them. The
45
+ source-layer name then defaults to that table unless you pass `--layer`. The integer primary key
46
+ becomes the feature ID, and attributes keep their SQLite types. BLOB and generated columns are skipped
47
+ with a warning. SQL views, curved geometries and the extended GeoPackage binary encoding are not read.
48
+ The file is only read, and edits still pending in a write-ahead log are included.
49
+
50
+ KML features get `name`, `description`, `address`, `phoneNumber`, `timestamp`, `begin`, `end`, and
51
+ `folder` for the nearest enclosing Folder, plus every `ExtendedData` value. Values stay text unless a
52
+ `Schema` declares a numeric or Boolean type, so `--rank-by` needs a declared numeric field. If an
53
+ `ExtendedData` value repeats one of those names, the last one is kept with a warning. A
54
+ `MultiGeometry` stays one feature when its parts share a type. Overlays and network links are ignored
55
+ with a warning, and remote content is never fetched.
56
+
57
+ GPX features get `name`, `desc`, `cmt`, `src`, `sym`, `type`, `time`, and the numeric `ele` and
58
+ `number`. Elevation and time on individual route and track points are ignored with a warning.
59
+
60
+ ### Coordinate reference systems
61
+
62
+ The build never guesses a CRS and never reprojects without `--reproject`:
63
+
64
+ ```sh
65
+ npx tileflow tileset build ./data/parcels.shp --reproject --json
66
+ npx tileflow tileset build ./data/sites.csv --lon-column x --lat-column y --source-crs EPSG:25830 --reproject --json
67
+ ```
68
+
69
+ | Input | Treated as |
70
+ | ----------------------------------- | ----------------------------------------------------------- |
71
+ | GeoJSON without a `crs` member | WGS84 longitude and latitude, as RFC 7946 requires |
72
+ | GeoJSON with a WGS84 `crs` member | WGS84 |
73
+ | GeoJSON with another `crs` member | Refused. Restate it with `--source-crs`, plus `--reproject` |
74
+ | CSV with standard or named columns | WGS84. Any other coordinates fail the range check |
75
+ | CSV with no recognizable columns | Refused until you name the columns |
76
+ | Shapefile with a WGS84 `.prj` | WGS84 |
77
+ | Shapefile with another `.prj` | Refused until you add `--reproject` |
78
+ | Shapefile without a `.prj` | Refused until you pass `--source-crs` |
79
+ | GeoPackage layer in WGS84 | WGS84 |
80
+ | GeoPackage layer in another CRS | Refused until you add `--reproject` |
81
+ | GeoPackage layer with no usable CRS | Refused until you pass `--source-crs` |
82
+ | KML and GPX | WGS84, as both formats define |
83
+
84
+ A GeoPackage layer's CRS comes from `gpkg_spatial_ref_sys`. The reserved `srs_id` values -1 and 0 mean
85
+ undefined, so they count as no usable CRS. A row whose `srs_id` differs from its layer stops the build
86
+ with `TF_TILESET_BUILD_CRS_MIXED`, unless `--source-crs` states the CRS for every row.
87
+
88
+ `--source-crs` states what the coordinates are, and overrides anything the file declares. It accepts
89
+ `EPSG:4326`, `EPSG:3857`, `EPSG:4258`, `EPSG:4269`, UTM zones on WGS84 (`EPSG:326xx`, `EPSG:327xx`),
90
+ ETRS89 (`EPSG:258xx`) or NAD83 (`EPSG:269xx`), a proj string, WKT, or the path to a `.prj` file. Other
91
+ EPSG codes need one of the last three forms.
92
+
93
+ Conversion uses [proj4js](https://github.com/proj4js/proj4js), which applies 3- and 7-parameter datum
94
+ shifts but has no grid shifts:
95
+
96
+ - Datums within about 2 m of WGS84 (ETRS89, RGF93, NAD83, GDA94, GDA2020, NZGD2000, SIRGAS, JGD2000
97
+ and JGD2011) are treated as coincident, with a warning.
98
+ - Datums with a known shift convert with that shift. Accuracy is a few metres at best.
99
+ - Any other datum, such as NAD27 or ED50 without parameters, fails with
100
+ `TF_TILESET_BUILD_DATUM_UNSUPPORTED` rather than misplacing features by up to hundreds of metres.
101
+ Pass a proj string with `+towgs84=` if you know the shift, or reproject first.
102
+
103
+ Every coordinate is range-checked after any conversion. The first coordinate more than 0.001° outside
104
+ longitude ±180 or latitude ±90 stops the build and names the feature, row or record; anything closer
105
+ is moved to the limit and counted in `warnings`. A prime meridian other than Greenwich counts as a
106
+ non-WGS84 CRS. This catches most, but not
107
+ all, wrong statements: a small projected extent can fall in range, and swapped axes are only caught
108
+ past latitude ±90.
109
+
110
+ To make those visible, whenever you state a CRS or the build converts one, `warnings` says where the
111
+ features ended up. Every build also warns if all features sit within 1° of longitude 0, latitude 0.
112
+ Human output always prints the bounds, and `--json` reports them as `archive.bounds`.
113
+
114
+ ### Options
115
+
116
+ | Option | Meaning |
117
+ | ----------------------- | ---------------------------------------------------------------- |
118
+ | `-o, --output <path>` | Archive path. Defaults to the input name with `.pmtiles` |
119
+ | `--force` | Replace an existing output |
120
+ | `--layer <name>` | Source-layer name. Defaults to the input file name |
121
+ | `--minzoom <zoom>` | Lowest zoom to write. Defaults to 0 |
122
+ | `--maxzoom <zoom>` | Highest zoom to write, up to 16. Chosen from the data by default |
123
+ | `--include <fields>` | Keep only these comma-separated attributes, by source name |
124
+ | `--exclude <fields>` | Drop these comma-separated attributes, by source name |
125
+ | `--rank-by <field>` | Thin the lowest values of this numeric attribute first |
126
+ | `--rank-ascending` | With `--rank-by`, thin the highest values first |
127
+ | `--zip-entry <path>` | The `.shp` or `.kml` to read from an archive holding several |
128
+ | `--input-layer <name>` | The feature table to read from a GeoPackage holding several |
129
+ | `--lon-column <name>` | CSV longitude column |
130
+ | `--lat-column <name>` | CSV latitude column |
131
+ | `--source-crs <crs>` | State the input CRS |
132
+ | `--reproject` | Convert a stated non-WGS84 CRS to WGS84 |
133
+ | `--name <name>` | Archive display name |
134
+ | `--description <text>` | Archive description |
135
+ | `--attribution <text>` | Attribution stored in the archive |
136
+ | `--format <format>` | Override format detection |
137
+ | `--feature-id <field>` | Use this attribute as the feature ID |
138
+ | `--hash-feature-id` | With `--feature-id`, hash each value into an integer ID |
139
+ | `--recipe <path>` | Read every build setting from a recipe file |
140
+ | `--print-recipe` | Print the recipe these options make, without building |
141
+ | `--progress <format>` | Report progress on stderr as `text` or `ndjson` |
142
+ | `--inspect` | Report format, CRS and fields as JSON, without tiling |
143
+ | `--sample-features <n>` | Features `--inspect` reads. Defaults to 10000 |
144
+ | `--json` | Print one schema-version-1 JSON document on stdout |
145
+
146
+ `--json` turns text progress off unless you also pass `--progress`. With `--progress ndjson`, each stderr
147
+ line is one object with `stage` (`reading`, `tiling`, `archiving` or `validating`), `message`, and
148
+ `features` or `tiles` when known.
149
+
150
+ `--inspect` reads a sample and prints what is still undecided, instead of failing on the first thing
151
+ a build would refuse. It always prints JSON: `crs.status` is `wgs84`, `needs-reproject`, `unknown`, `unsupported` or
152
+ `out-of-range`; `sample.sourceBounds` and up to 2,000 `sample.coordinates` hold the positions as stored in the file; `fields` and a suggested `recipe`
153
+ follow. A zip or GeoPackage holding several candidates returns `choices`, and a CSV without
154
+ recognizable coordinate columns returns `columns`. Nothing is written beside the input.
155
+
156
+ ### Recipes
157
+
158
+ A recipe is a JSON file that holds every build setting, so a repeatable build can be reviewed in a
159
+ pull request and run the same way anywhere. Options are a shorthand for a recipe:
160
+
161
+ ```sh
162
+ npx tileflow tileset build ./data/parcels.shp --reproject --include ref,area_m2 --print-recipe > ./data/parcels.tileset.json
163
+ npx tileflow tileset build ./data/parcels.shp --recipe ./data/parcels.tileset.json --json
164
+ ```
165
+
166
+ ```json
167
+ {
168
+ "$schema": "https://tileflow.dev/schemas/tileset-recipe/v1.json",
169
+ "layers": {
170
+ "parcels": {
171
+ "featureId": {"field": "gid"},
172
+ "fields": {"include": ["ref", "area_m2"]},
173
+ "maxzoom": "auto",
174
+ "minzoom": 0,
175
+ "rank": {"by": "area_m2", "order": "descending"},
176
+ "source": "parcels"
177
+ }
178
+ },
179
+ "metadata": {"attribution": "© Example", "name": "Parcels"},
180
+ "sources": {
181
+ "parcels": {
182
+ "crs": {"from": "EPSG:25830", "reproject": true},
183
+ "zipEntry": "cadastre/parcels.shp"
184
+ }
185
+ },
186
+ "version": 1
187
+ }
188
+ ```
189
+
190
+ | Recipe field | Option |
191
+ | --------------------------------------------- | ------------------------------------------ |
192
+ | Key of `layers` | `--layer` |
193
+ | `layers.*.minzoom`, `maxzoom` | `--minzoom`, `--maxzoom` |
194
+ | `layers.*.fields.include`, `exclude` | `--include`, `--exclude` |
195
+ | `layers.*.rank` | `--rank-by`, `--rank-ascending` |
196
+ | `layers.*.featureId` | `--feature-id`, `--hash-feature-id` |
197
+ | `sources.*.format`, `layer`, `zipEntry` | `--format`, `--input-layer`, `--zip-entry` |
198
+ | `sources.*.csv.lon`, `lat` | `--lon-column`, `--lat-column` |
199
+ | `sources.*.crs.from`, `reproject` | `--source-crs`, `--reproject` |
200
+ | `metadata.name`, `description`, `attribution` | `--name`, `--description`, `--attribution` |
201
+
202
+ The input file stays on the command line. `sources` and `layers` each hold exactly one entry in version 1.
203
+ Unknown fields are refused, and `--recipe` cannot be combined with an option from this table.
204
+ `sources.*.crs.from` accepts `EPSG:<code>`, a proj string or WKT, never a path. `--print-recipe` replaces a
205
+ `.prj` path with its contents. The schema ships at `schemas/tileset-recipe.v1.json` in this package.
206
+
207
+ Every build reports its resolved recipe as `recipe` in the JSON output, with `maxzoom` as a number. Reusing that
208
+ recipe gives the same zoom even if automatic zoom changes in a later version. The archive metadata records
209
+ `tileflow_recipe_hash` and `tileflow_source_hash`, and `source.sha256` in the JSON output holds the source
210
+ digest. For a Shapefile outside a zip, the digest covers the `.shp`, `.dbf`, `.prj` and `.cpg`.
211
+
212
+ ### Zooms and thinning
213
+
214
+ The automatic maximum zoom is the lowest that resolves most vertices and keeps the densest tile
215
+ manageable, from zoom 10 for points and zoom 4 for lines and polygons, up to zoom 14. `--json` reports
216
+ it as `archive.maxzoom` with `archive.maxzoomSource`.
217
+
218
+ Rings with fewer than three distinct positions and lines with a single position are skipped and
219
+ counted in `warnings`. Every other feature reaches the maximum zoom. Below it, tiles stay within about 500 KiB compressed:
220
+
221
+ - Points that land on the same pixel keep only the first, so sparse areas stay complete while dense
222
+ areas thin out. "First" is input order, or the best value with `--rank-by`. Polygons too small for
223
+ a zoom render as a tiny mark and thin the same way.
224
+ - Lines and polygons are simplified to the tile resolution, and lines smaller than that drop out.
225
+ - A tile still over budget thins its densest cells further.
226
+
227
+ Thinning nests: a feature drawn at one zoom is drawn at every higher zoom, so features appear as you
228
+ zoom in and do not flicker. A tile can exceed the 500 KiB target to honour this. Only a tile that
229
+ would pass 6 MB drops features its parent drew.
230
+
231
+ `--rank-by` accepts any numeric attribute, whether or not it stays in the tiles. Features without a
232
+ number there thin first and are counted in `warnings`; a field with no numbers fails. Features are
233
+ written to tiles in rank order, which also gives the best ones first claim on label placement. Ranking
234
+ holds about 24 extra bytes per feature in memory.
235
+
236
+ `zooms[].thinnedFeatures` counts the features left out of tiles at each zoom. It is always zero at the
237
+ maximum zoom unless a geometry rounds away there. Zooming past the maximum reuses the last tiles.
238
+
239
+ A tile at the maximum zoom that would exceed 6 MB fails with `TF_TILESET_BUILD_TILE_TOO_LARGE`, because
240
+ keeping it would mean losing features silently. Raise `--maxzoom`, or remove attributes.
241
+
242
+ ### Attributes
243
+
244
+ Strings, numbers and booleans map directly. Nested objects and arrays become JSON strings, and `null`
245
+ is omitted. Field names outside letters, digits, `.`, `_`, `:` and `-` are renamed, with accents
246
+ removed and other characters replaced by `_`; `layer.fields` maps each `sourceName` to its `name`, and
247
+ a warning lists the change. Two names that collide stop the build. A field with mixed types is
248
+ declared `Mixed`. Non-negative integer GeoJSON `id` values become vector tile feature IDs; other ids
249
+ are dropped with a warning. Each entry in `layer.fields` also reports `count`, `min` and `max` for numbers,
250
+ up to 64 distinct `values` of up to 64 characters, and `valuesComplete`. The archive metadata carries the same
251
+ values as `tilestats`. `--feature-id` reads the ID from an attribute instead, for any format, and fails if no
252
+ feature has a usable value. Add `--hash-feature-id` to turn text keys into stable integers. Vector tiles store
253
+ only integers, and two different values can collide once a dataset reaches tens of millions of features.
254
+
255
+ ### JSON output
256
+
257
+ ```json
258
+ {
259
+ "archive": {
260
+ "addressedTiles": 341,
261
+ "bounds": [-3.9, 40.2, -3.5, 40.6],
262
+ "byteCount": 48211,
263
+ "maxzoom": 10,
264
+ "maxzoomSource": "auto",
265
+ "minzoom": 0,
266
+ "path": "data/stores.pmtiles",
267
+ "tileContents": 341
268
+ },
269
+ "command": "tileset build",
270
+ "crs": {"authority": "format", "label": "GeoJSON (WGS84 by RFC 7946)", "reprojected": false},
271
+ "input": {
272
+ "features": 1200,
273
+ "format": "geojson",
274
+ "geometryTypes": ["Point"],
275
+ "skipped": {"emptyGeometry": 0, "outsideMercator": 0}
276
+ },
277
+ "layer": {
278
+ "fields": [{"name": "name", "sourceName": "name", "type": "String"}],
279
+ "id": "stores"
280
+ },
281
+ "recipe": {
282
+ "$schema": "...",
283
+ "layers": {"stores": {"maxzoom": 10, "minzoom": 0, "source": "stores"}},
284
+ "sources": {"stores": {}},
285
+ "version": 1
286
+ },
287
+ "schemaVersion": 1,
288
+ "source": {"sha256": "9f2a..."},
289
+ "warnings": [],
290
+ "zooms": [
291
+ {"features": 900, "largestTileBytes": 9120, "thinnedFeatures": 300, "tiles": 1, "zoom": 0}
292
+ ]
293
+ }
294
+ ```
295
+
296
+ On failure the exit code is 1, stdout is empty, and stderr carries
297
+ `{"error": {"code", "message", "path"}, "ok": false, "schemaVersion": 1}`.
298
+
299
+ | Code | Meaning |
300
+ | ------------------------------------------- | -------------------------------------------------------------- |
301
+ | `TF_TILESET_BUILD_INPUT_MISSING` | The input file does not exist |
302
+ | `TF_TILESET_BUILD_INPUT_INVALID` | Malformed data, XML, GeoPackage or zip |
303
+ | `TF_TILESET_BUILD_INPUT_UNSAFE` | The XML has a DOCTYPE or entity declaration |
304
+ | `TF_TILESET_BUILD_INPUT_LIMIT` | The input exceeds a safety limit listed below |
305
+ | `TF_TILESET_BUILD_FORMAT_UNSUPPORTED` | Unknown extension, or geometry with no tile equivalent |
306
+ | `TF_TILESET_BUILD_RUNTIME_UNSUPPORTED` | GeoPackage input needs Node.js 22.13 or later |
307
+ | `TF_TILESET_BUILD_ZIP_ENTRY_INVALID` | Archive has no data file, several, or not the one named |
308
+ | `TF_TILESET_BUILD_INPUT_LAYER_INVALID` | GeoPackage has no feature table, several, or not the one named |
309
+ | `TF_TILESET_BUILD_RANK_INVALID` | `--rank-by` has no numeric values, or is missing |
310
+ | `TF_TILESET_BUILD_FEATURE_ID_INVALID` | `--feature-id` has no usable value, or is missing |
311
+ | `TF_TILESET_BUILD_RECIPE_INVALID` | The recipe is not valid |
312
+ | `TF_TILESET_BUILD_RECIPE_UNREADABLE` | The recipe file cannot be read |
313
+ | `TF_TILESET_BUILD_RECIPE_CONFLICT` | `--recipe` was combined with a setting it already holds |
314
+ | `TF_TILESET_BUILD_PROGRESS_INVALID` | `--progress` is not `text` or `ndjson` |
315
+ | `TF_TILESET_BUILD_OPTION_INVALID` | A numeric option is not a positive integer |
316
+ | `TF_TILESET_BUILD_EMPTY` | No usable features |
317
+ | `TF_TILESET_BUILD_CRS_UNKNOWN` | The input does not state its CRS |
318
+ | `TF_TILESET_BUILD_CRS_NOT_WGS84` | Stated CRS needs `--reproject` |
319
+ | `TF_TILESET_BUILD_CRS_MIXED` | GeoPackage rows use a CRS other than their layer's |
320
+ | `TF_TILESET_BUILD_CRS_UNREADABLE` | `--source-crs` or the declared CRS could not be parsed |
321
+ | `TF_TILESET_BUILD_CRS_UNSUPPORTED` | Geographic CRS measured in something other than degrees |
322
+ | `TF_TILESET_BUILD_DATUM_UNSUPPORTED` | No known shift from the datum to WGS84 |
323
+ | `TF_TILESET_BUILD_COORDINATES_OUT_OF_RANGE` | Coordinates are not WGS84 after any conversion |
324
+ | `TF_TILESET_BUILD_CSV_COLUMNS` | Coordinate columns are missing or ambiguous |
325
+ | `TF_TILESET_BUILD_FIELD_CONFLICT` | Two field names collide after renaming |
326
+ | `TF_TILESET_BUILD_FIELDS_INVALID` | `--include` and `--exclude` were combined |
327
+ | `TF_TILESET_BUILD_TOO_MANY_FIELDS` | More than 512 fields; use `--include` |
328
+ | `TF_TILESET_BUILD_LAYER_INVALID` | The layer name is not valid |
329
+ | `TF_TILESET_BUILD_ZOOM_INVALID` | Zooms are out of range or reversed |
330
+ | `TF_TILESET_BUILD_OUTPUT_EXISTS` | The output exists; pass `--force` |
331
+ | `TF_TILESET_BUILD_OUTPUT_INVALID` | The output equals the input, or its folder is missing |
332
+ | `TF_TILESET_BUILD_TILE_TOO_LARGE` | A tile cannot fit without losing features |
333
+ | `TF_TILESET_BUILD_TOO_MANY_TILES` | More than 16 million tile entries; lower `--maxzoom` |
334
+ | `TF_TILESET_BUILD_ARCHIVE_TOO_LARGE` | The archive exceeds the publishable 8 GB |
335
+ | `TF_TILESET_BUILD_DISK_FULL` | Not enough free disk space |
336
+
337
+ ### Limits
338
+
339
+ Features are spooled to disk rather than held in memory; a 5 million point, 777 MB GeoJSON file peaked
340
+ under 600 MB. A single very large feature must fit in memory on its own. Temporary files sit beside the
341
+ output and are removed on exit, including Ctrl-C. Allow free space of about twice the input, or three times the uncompressed Shapefile for a zip. Tiling
342
+ runs on one thread, with compression on the thread pool, so plan for up to a minute per million simple
343
+ features.
344
+
345
+ If a Shapefile has a DBF, each shape record needs a matching attribute row. A missing or truncated
346
+ row stops the build. Deleted rows are skipped, and extra rows produce a warning.
347
+
348
+ KML and GPX are streamed. A document with a DOCTYPE or entity declaration is refused, because neither
349
+ format needs one and it is how entity-expansion attacks work. Only the five predefined XML entities
350
+ and numeric character references are decoded. One tag can hold 1 MiB and 256 attributes, elements can
351
+ nest 128 levels, and one text value can hold 8 MiB in KML or 1 MiB in GPX. One KML feature can hold
352
+ 64 MiB of attribute text.
353
+
354
+ A KMZ is streamed without being extracted. Its KML document can expand to 4 GiB, an archive can hold
355
+ 65,536 entries, and an entry that inflates past its declared size stops the build.
356
+
357
+ A GeoPackage is opened read-only and runs none of its own SQL. Tables must be plain tables, so views
358
+ and virtual tables are not read. On early Node.js 22 releases, reading a GeoPackage in
359
+ write-ahead-log mode can leave `-wal` and `-shm` files beside it. A GeoPackage with a rollback journal
360
+ left by an interrupted writer is refused until a tool that can write to it recovers it.
361
+
362
+ One feature can hold 5 million positions and parts in every format, and one GeoJSON feature or Shapefile
363
+ record can take 256 MiB. The first bytes of a file must match its format, so a renamed file is refused
364
+ before parsing.
365
+
366
+ The build does not merge adjacent polygons at low zooms, cluster points, or join attributes. Polygons
367
+ are simplified independently, so neighbours that share a border can show hairline gaps at low zooms.
368
+ Thinning does not carry counts for heatmaps. Features crossing the antimeridian must be
369
+ split first; a warning flags segments wider than 180°. Web Mercator stops at latitude ±85.05: polygons
370
+ and lines are cut there, and points past it are skipped.
371
+
372
+ For control beyond this, use any tool that writes PMTiles. `tileflow tileset publish` accepts its
373
+ output after the same validation.
374
+
375
+ ## tileset publish
376
+
377
+ Account-backed authorization renews automatically. Hashing reuses a 16 MiB buffer, and up to four parts stream concurrently with reusable buffers.
378
+ Administratively disabled uploads return `TF_TILESET_UPLOAD_DISABLED` without retrying. Temporary
379
+ service failures retain bounded retries.
380
+
381
+ ## Restore
382
+
383
+ Deployments are immutable. Restoring one publishes its content again as a new version of the Map, so
384
+ the deployment it replaces stays in the history and can be restored in turn:
385
+
386
+ ```bash
387
+ npx tileflow deployments list --map-id map_AbCdEfGhIjKlMnOp
388
+ npx tileflow deployments restore 12 --map-id map_AbCdEfGhIjKlMnOp
389
+ ```
390
+
391
+ Allowed websites are not part of a deployment and do not change. A restore is refused when the data
392
+ the version needs has since changed, and the refusal names the source. Your repository still holds
393
+ what you replaced, so the next `tileflow deploy` publishes that again unless you revert it.
394
+
395
+ A tileset keeps its current and previous versions. Restoring swaps them for every Map that uses the
396
+ tileset, with no deployment, and restoring again undoes it:
397
+
398
+ ```bash
399
+ npx tileflow tileset restore stores --team @acme
400
+ ```
401
+
402
+ The restore is refused while an upload to that tileset is in progress, and while a live Map needs
403
+ data the previous version lacks. Restore or republish that Map first. An upload that nothing is
404
+ still sending can be cleared; it may belong to a teammate or a CI run, so check first:
405
+
406
+ ```bash
407
+ npx tileflow tileset upload abort stores --team @acme
408
+ ```
409
+
410
+ These commands are written for agents as well as people. With `--json`, a success has a `nextSteps`
411
+ array and a failure has `error.code`, `error.message` and `error.nextSteps`. Each step has a
412
+ `description` and, when one exists, the exact `command` to run. A failure blocked by an upload also
413
+ has `error.upload`, so you can see its state and expiry before aborting it. If a tileset restore
414
+ ends with `TF_TILESET_DELIVERY_PENDING` or its outcome is uncertain, follow the retry in `nextSteps`.
415
+ It passes `--expected-current-version <n>` from the original operation. The server can repair pending
416
+ delivery or report that the version has already changed. Check status before starting a new restore.
417
+ To cancel the upload named in a refusal, use its `--upload-id <id>` step. If that upload has ended,
418
+ the command leaves any replacement upload alone. While cancellation is pending, the command reports
419
+ `aborted: false`, the returned upload state and a status check. Recovery commands carry the selected
420
+ API origin and resource. Team-scoped commands keep the selected Team or the opaque tileset ID.
421
+
422
+ ## Icon lock commands
423
+
424
+ The lock commands inspect the `iconSet()` references your config declares. They are the only
425
+ commands that resolve `latest`, and the only ones that write the lock. `install` fills in declared
426
+ references that have no pin, `update` moves selected references to their current latest revision,
427
+ and `pin` selects one exact revision. Every requested reference is resolved before a single
428
+ compare-and-swap write of the whole snapshot, so a concurrent writer fails the command instead of
429
+ losing its pins. No lock command rewrites `tileflow.config.ts`.
@@ -0,0 +1,21 @@
1
+ import {
2
+ buildTileset,
3
+ checkContainerFormat,
4
+ chooseCrsStatement,
5
+ chooseLayer,
6
+ detectFormat,
7
+ isKmz,
8
+ isZip
9
+ } from "./chunk-5U4J6E6Y.js";
10
+ import "./chunk-JVTGXPGV.js";
11
+ import "./chunk-43VQMAQO.js";
12
+ import "./chunk-GPPOECR5.js";
13
+ export {
14
+ buildTileset,
15
+ checkContainerFormat,
16
+ chooseCrsStatement,
17
+ chooseLayer,
18
+ detectFormat,
19
+ isKmz,
20
+ isZip
21
+ };
@@ -0,0 +1,52 @@
1
+ // src/tileset-build/errors.ts
2
+ var TilesetBuildError = class extends Error {
3
+ constructor(code, path, message, choices) {
4
+ super(message);
5
+ this.code = code;
6
+ this.path = path;
7
+ this.choices = choices;
8
+ this.name = "TilesetBuildError";
9
+ }
10
+ code;
11
+ path;
12
+ choices;
13
+ };
14
+ var SampleComplete = class extends TilesetBuildError {
15
+ constructor() {
16
+ super("TF_TILESET_BUILD_SAMPLE_COMPLETE", "input", "The sample is complete.");
17
+ }
18
+ };
19
+ function yieldToEventLoop() {
20
+ return new Promise((resolve) => setImmediate(resolve));
21
+ }
22
+ function listChoices(names) {
23
+ const shown = names.slice(0, 20).join(", ");
24
+ return names.length > 20 ? `${shown}, and ${names.length - 20} more` : shown;
25
+ }
26
+ var maximumFeaturePositions = 5e6;
27
+ var maximumFeatureBytes = 256 * 1024 * 1024;
28
+ function featureTooManyBytes(location) {
29
+ return new TilesetBuildError(
30
+ "TF_TILESET_BUILD_INPUT_LIMIT",
31
+ "input",
32
+ `${location}: the feature takes more than ${maximumFeatureBytes / 1024 ** 2} MiB. Split or simplify it first.`
33
+ );
34
+ }
35
+ function featureTooLarge(location) {
36
+ return new TilesetBuildError(
37
+ "TF_TILESET_BUILD_INPUT_LIMIT",
38
+ "input",
39
+ `${location}: the feature has more than ${maximumFeaturePositions} positions. Split or simplify it first.`
40
+ );
41
+ }
42
+
43
+ export {
44
+ TilesetBuildError,
45
+ SampleComplete,
46
+ yieldToEventLoop,
47
+ listChoices,
48
+ maximumFeaturePositions,
49
+ maximumFeatureBytes,
50
+ featureTooManyBytes,
51
+ featureTooLarge
52
+ };