pmtiles-swarm 0.79.1 → 0.89.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 +270 -0
- package/README.md +3 -1
- package/docs/tile-stacks.md +381 -47
- package/package.json +1 -1
- package/src/api.js +205 -1
- package/src/auth.js +4 -0
- package/src/bake-jobs.js +56 -3
- package/src/config.js +47 -0
- package/src/elevation.js +14 -10
- package/src/feed.js +9 -3
- package/src/index.js +45 -0
- package/src/library.js +9 -1
- package/src/sources.js +1 -1
- package/src/stack-exports.js +285 -0
- package/src/stack-feed.js +416 -0
- package/src/stack-tile.js +191 -28
- package/src/stacks.js +134 -16
- package/src/web/index.html +322 -21
- package/src/web/public.html +39 -9
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,276 @@
|
|
|
7
7
|
### 🐞 Bug fixes
|
|
8
8
|
- _...Add new stuff here..._
|
|
9
9
|
|
|
10
|
+
## 0.89.0
|
|
11
|
+
### ✨ Features and improvements
|
|
12
|
+
- **Both feeds are linked at the foot of the public page.** `RSS feed` is `archive RSS feed` now and
|
|
13
|
+
`stack RSS feed` is beside it. One name was unambiguous while there was one feed; with a second, a
|
|
14
|
+
reader following the old name would have got archives when they wanted recipes and had nothing on
|
|
15
|
+
the page to tell them otherwise.
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### 🐞 Bug fixes
|
|
19
|
+
- **A feed carrying every category.** `/categories.xml`, beside `/feed.xml` and `/stacks.xml` — the
|
|
20
|
+
set was inconsistent, and following all of a node's categories meant adding each by hand and
|
|
21
|
+
remembering to add the next one, which is not following its categories at all. It is what a
|
|
22
|
+
subscriber wants when they want everything this node files: including categories added later.
|
|
23
|
+
|
|
24
|
+
The items are archives, because a category has no bytes of its own and the build it resolves to is
|
|
25
|
+
what a subscriber would join — which also means every existing consumer already knows how to read
|
|
26
|
+
it, magnets and enclosures included. That is what separates it from the whole catalogue: `/feed.xml`
|
|
27
|
+
carries every build a node holds, and this carries the current one of each category. On a node
|
|
28
|
+
keeping four builds apiece the difference is fourfold.
|
|
29
|
+
|
|
30
|
+
In the footer of the public page it replaces `categories JSON`, which was the odd one out among
|
|
31
|
+
three feeds. `/latest/` is untouched and remains the JSON index.
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
## 0.88.0
|
|
35
|
+
### ✨ Features and improvements
|
|
36
|
+
- **An archive card is built like a category's.** Copy buttons for the addresses that belong
|
|
37
|
+
somewhere else — TileJSON, magnet, and a **source URL** of its own, the TileJSON URL with the
|
|
38
|
+
`.torrent` and magnet in its fragment, pinned to that build rather than following the category.
|
|
39
|
+
The preview, `.torrent` and download stay links, because a page is followed and a file is saved.
|
|
40
|
+
The printed TileJSON row went with the button that replaced it: an address in full beside a button
|
|
41
|
+
that copies it is the same fact twice, and it was the longest line on the card.
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
### 🐞 Bug fixes
|
|
45
|
+
- **A terrain stack was offered no hillshade preview on the public page.** The listing reported its
|
|
46
|
+
encoding as whatever `output.encoding` restated, which for a recipe saying "same as the sources" —
|
|
47
|
+
the ordinary case — is nothing. So the page could not tell a terrain stack from an imagery one and
|
|
48
|
+
offered neither the raw preview nor the terrain one. It reports what the stack actually serves now,
|
|
49
|
+
and reports nothing for imagery, where a guess would have offered a hillshade of a photograph.
|
|
50
|
+
|
|
51
|
+
- **A stack whose sources disagreed about their encoding wrote tiles in more than one of them.** With
|
|
52
|
+
no `output.encoding`, the merge took the encoding of whichever source answered first — and which
|
|
53
|
+
source answers varies by tile, since the base is sparse in one place and the layer above covers in
|
|
54
|
+
another. One stack wrote one tile as mapbox and the next as terrarium, with the TileJSON in front
|
|
55
|
+
describing neither: correct in one place, a cliff face in another, for no reason the recipe showed.
|
|
56
|
+
It follows the base source now, which is a property of the recipe rather than of the tile, and the
|
|
57
|
+
listing and the merge are held to the same answer by a test.
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
## 0.87.0
|
|
61
|
+
### ✨ Features and improvements
|
|
62
|
+
- **A feed per stack, and an address to copy for it.** `/stacks/<id>.xml` carries one recipe, for
|
|
63
|
+
following a single map out of somebody's several rather than everything they publish. An **RSS**
|
|
64
|
+
button on the stack's row in the console and beside TileJSON and XYZ on the public page. Copied
|
|
65
|
+
rather than followed: the address is for another node's settings, and a browser shown an RSS
|
|
66
|
+
document mostly offers to download it. A stack this node adopted has no button and its feed 404s —
|
|
67
|
+
somebody else's recipe is theirs to publish.
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
### 🐞 Bug fixes
|
|
71
|
+
- **The copied XYZ address came back percent-encoded.** `http://…/stacks/test/%7Bz%7D/%7Bx%7D/%7By%7D.png`
|
|
72
|
+
rather than `{z}/{x}/{y}`, which no client will take. The public page resolves a relative address
|
|
73
|
+
against the page's own URL to make it absolute, and `new URL` percent-encodes braces because they
|
|
74
|
+
are not legal in a path — and an XYZ template is very little but braces. The TileJSON link was
|
|
75
|
+
unaffected, having none. Resolving still happens; the two sequences it introduces are put back.
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
## 0.86.0
|
|
79
|
+
### ✨ Features and improvements
|
|
80
|
+
- _...Add new stuff here..._
|
|
81
|
+
|
|
82
|
+
- **Stack recipes travel between nodes.** `GET /stacks.xml` is this node's own stacks as a feed;
|
|
83
|
+
**Settings → Feeds → Stack feeds** follows another node's and adopts what it carries. Archives
|
|
84
|
+
already travelled by category — a builder feeding two tile servers gave them the same archives —
|
|
85
|
+
and this is how the recipes that combine them travel with them, so a stack is written once and
|
|
86
|
+
corrected once.
|
|
87
|
+
|
|
88
|
+
This section of the docs used to argue against a feed, on the grounds that a stack is a mutable
|
|
89
|
+
document and syncing one is about conflicts. That holds where two nodes both edit a recipe and is
|
|
90
|
+
not the arrangement anybody runs: one node authors, the rest follow. What survives from the
|
|
91
|
+
objection is handled directly — **a stack made on this node is never overwritten by one arriving
|
|
92
|
+
under the same name**, which is refused and said.
|
|
93
|
+
|
|
94
|
+
A recipe is adopted under the publisher's own id, so `planet-terrain` answers at the same URL on
|
|
95
|
+
the builder and on every replica; namespacing it would have given three nodes three URLs and
|
|
96
|
+
defeated the point. A recipe naming a source this node has not got is adopted anyway and reports
|
|
97
|
+
the missing source until it arrives — refusing it would mean a replica could not be set up until
|
|
98
|
+
every archive had finished downloading, which is backwards.
|
|
99
|
+
|
|
100
|
+
What happens when a feed stops carrying a stack is the feed's own setting: keep it and say so, or
|
|
101
|
+
remove it here too.
|
|
102
|
+
|
|
103
|
+
### 🐞 Bug fixes
|
|
104
|
+
- _...Add new stuff here..._
|
|
105
|
+
|
|
106
|
+
## 0.85.1
|
|
107
|
+
### ✨ Features and improvements
|
|
108
|
+
- _...Add new stuff here..._
|
|
109
|
+
|
|
110
|
+
- **An export no longer pretends a stack has categories.** Both doors fell back to
|
|
111
|
+
`stack.categories` when none were given — a field the typedef never had, validation never checked,
|
|
112
|
+
the editor has no box for and nothing ever wrote. A category is what an *archive* is filed under,
|
|
113
|
+
and a stack has no bytes and no infohash for that to be about.
|
|
114
|
+
|
|
115
|
+
Left empty the archive is unfiled: held and seeded, in no category and no feed. The scheduled
|
|
116
|
+
export row says `unfiled` and the dialog says so in full, rather than offering a default that
|
|
117
|
+
could not exist.
|
|
118
|
+
|
|
119
|
+
### 🐞 Bug fixes
|
|
120
|
+
- _...Add new stuff here..._
|
|
121
|
+
|
|
122
|
+
## 0.85.0
|
|
123
|
+
### ✨ Features and improvements
|
|
124
|
+
- _...Add new stuff here..._
|
|
125
|
+
|
|
126
|
+
- **A scheduled export can be served by the node that baked it.** The **Local file** choice, the same
|
|
127
|
+
one every other row under Feeds offers: http, http + web seed, http + catalog. It is worth more
|
|
128
|
+
here than anywhere else — a baked archive is already on this disk, so serving it costs a route
|
|
129
|
+
rather than a download, and without it a scheduled export produces something only peers can reach.
|
|
130
|
+
A nightly build behind a URL that is always current is usually the point of scheduling one.
|
|
131
|
+
|
|
132
|
+
`publishDir` and `webSeedBase` remain the other half of the same question, for a directory
|
|
133
|
+
something else already serves. The two are not exclusive: publishing to a served directory *and*
|
|
134
|
+
offering this node as a web seed gives a client two places to get the same bytes.
|
|
135
|
+
|
|
136
|
+
### 🐞 Bug fixes
|
|
137
|
+
- _...Add new stuff here..._
|
|
138
|
+
|
|
139
|
+
## 0.84.0
|
|
140
|
+
### ✨ Features and improvements
|
|
141
|
+
- _...Add new stuff here..._
|
|
142
|
+
|
|
143
|
+
- **Scheduled exports are rows, like every other automation beside them.** `stackExports` in the
|
|
144
|
+
config, edited under Settings → Feeds with the same row editor the monitored folders and the
|
|
145
|
+
scheduled sources use: add a row, choose the stack from a dropdown, say when, and fill in the rest
|
|
146
|
+
— categories, builds to keep, keep for how many days, save location, publish directory, web seed
|
|
147
|
+
base, archive name, attribution, description.
|
|
148
|
+
|
|
149
|
+
**Several rows may name one stack.** That is what the two earlier shapes could not say: an `export`
|
|
150
|
+
block on the recipe, and then a table of one row per stack, both hold exactly one schedule — and a
|
|
151
|
+
nightly build to the fast disk beside a weekly one published elsewhere is an ordinary thing to
|
|
152
|
+
want. It also puts the schedule where the other automations already are: a watched folder says what
|
|
153
|
+
*this machine* does, not what a map is, and keeping it out of the recipe means a recipe copied to
|
|
154
|
+
another node does not quietly start baking there.
|
|
155
|
+
|
|
156
|
+
The export dialog no longer offers to repeat — a stack may have several schedules and a dialog
|
|
157
|
+
opened on the stack cannot say which it would edit. It exports once and points at the settings tab.
|
|
158
|
+
A stack's row in the Stacks view still shows the schedules aimed at it.
|
|
159
|
+
|
|
160
|
+
### 🐞 Bug fixes
|
|
161
|
+
- _...Add new stuff here..._
|
|
162
|
+
|
|
163
|
+
## 0.83.0
|
|
164
|
+
### ✨ Features and improvements
|
|
165
|
+
- _...Add new stuff here..._
|
|
166
|
+
|
|
167
|
+
- **A scheduled export is set up like the other automations beside it.** The row under Settings →
|
|
168
|
+
Feeds is the whole of one now, not only the timer: categories, builds to keep, keep for how many
|
|
169
|
+
days, archive name, attribution, description, where the data lands, a publish directory and a web
|
|
170
|
+
seed base. Those are the manual export dialog's fields plus the two retention rules every other
|
|
171
|
+
automation on that tab already had, which is the point — a scheduled export is set up in one place
|
|
172
|
+
rather than half in a settings tab and half in a dialog.
|
|
173
|
+
|
|
174
|
+
Retirement is the part that needed something new underneath. `keep` and `keepDays` are applied by
|
|
175
|
+
the same code a watched folder uses, but retiring needs a *family* — which archives are builds of
|
|
176
|
+
the same map — and a bake marked nothing, so there was nothing to compare. An archive now records
|
|
177
|
+
`source.stack`, and the family is every archive that names this stack. Without it a nightly export
|
|
178
|
+
is a disk that fills at one archive a night.
|
|
179
|
+
|
|
180
|
+
### 🐞 Bug fixes
|
|
181
|
+
- _...Add new stuff here..._
|
|
182
|
+
|
|
183
|
+
## 0.82.0
|
|
184
|
+
### ✨ Features and improvements
|
|
185
|
+
- _...Add new stuff here..._
|
|
186
|
+
|
|
187
|
+
- **Export schedules are set under Settings → Feeds.** Not only the two global settings that landed
|
|
188
|
+
there in 0.81.1 — the schedules themselves. **Scheduled exports** is a row per stack: never, every
|
|
189
|
+
day at a time, or every so many hours, saved onto that stack's recipe. Feeds is where somebody goes
|
|
190
|
+
to say when a thing runs, so it is where they are set; the export dialog's **Repeat** control still
|
|
191
|
+
writes the same schedule, for setting one while you are already there.
|
|
192
|
+
|
|
193
|
+
Turning a stack to _never_ pauses it with `enabled: false` rather than deleting the block. Where it
|
|
194
|
+
lands, what it is called and which categories it is filed under live in the same place, and those
|
|
195
|
+
should survive being paused.
|
|
196
|
+
|
|
197
|
+
### 🐞 Bug fixes
|
|
198
|
+
- _...Add new stuff here..._
|
|
199
|
+
|
|
200
|
+
## 0.81.1
|
|
201
|
+
### ✨ Features and improvements
|
|
202
|
+
- _...Add new stuff here..._
|
|
203
|
+
|
|
204
|
+
- **The scheduled-export settings are under Settings → Feeds.** `stacks.scheduledExports` and
|
|
205
|
+
`stacks.exportIntervalHours` had no schema entry, so they were config-file-only and invisible in
|
|
206
|
+
the console. Feeds rather than a group of their own, because that tab is already where the
|
|
207
|
+
automations that bring a file in on a timer sit — a scheduled source watching an upstream
|
|
208
|
+
directory, a subscription following someone else's feed. A scheduled export is the same kind of
|
|
209
|
+
thing; it just produces the file here instead of fetching it, and it lands in a category and goes
|
|
210
|
+
out over the feed exactly as a fetched one does.
|
|
211
|
+
|
|
212
|
+
### 🐞 Bug fixes
|
|
213
|
+
- _...Add new stuff here..._
|
|
214
|
+
|
|
215
|
+
## 0.81.0
|
|
216
|
+
### ✨ Features and improvements
|
|
217
|
+
- _...Add new stuff here..._
|
|
218
|
+
|
|
219
|
+
- **A stack can export itself on a schedule.** An archive is a snapshot: a stack over categories
|
|
220
|
+
follows a rebuild and a file baked from it does not, so it goes stale the moment its sources move
|
|
221
|
+
and somebody has to notice. An `export` block on the recipe says when — `at` for a time of day in
|
|
222
|
+
UTC, `everyHours` or `everyMinutes` for an interval, the same shape a scheduled source uses and
|
|
223
|
+
read by the same code — along with everything the export dialog collects. In the console, **Repeat**
|
|
224
|
+
turns the Export button into **Save schedule**, and the stack's row shows when it runs.
|
|
225
|
+
|
|
226
|
+
The hard part is remembering across a restart. The source poller keeps last-run times in memory,
|
|
227
|
+
which is fine when a missed poll costs one poll; here it would cost the whole bake, every restart,
|
|
228
|
+
for hours. So it is written to `stack-exports.json`, and written *before* the bake finishes — a
|
|
229
|
+
restart mid-export must not start it from the top, since the checkpoint is what carries it on.
|
|
230
|
+
|
|
231
|
+
A run whose sources have not moved is skipped: `bakeRevision` is recorded beside the time, and an
|
|
232
|
+
identical archive is the same map under a new infohash that then has to be seeded beside the one it
|
|
233
|
+
duplicates. It will not run two at once, will not start one over a bake already running, and does
|
|
234
|
+
not record a refusal as a run — a location that is full is something somebody fixes, and a schedule
|
|
235
|
+
that gave up quietly would hide that it ever ran. `stacks.scheduledExports: false` turns it off,
|
|
236
|
+
which is what a second node serving the same recipes wants.
|
|
237
|
+
|
|
238
|
+
### 🐞 Bug fixes
|
|
239
|
+
- _...Add new stuff here..._
|
|
240
|
+
|
|
241
|
+
## 0.80.0
|
|
242
|
+
### ✨ Features and improvements
|
|
243
|
+
- **A stack can be a source in another stack.** `{ "stack": "jaxa-with-gebco" }` beside `category`
|
|
244
|
+
and `archive`. A base worked out once — terrain over bathymetry, masked at the coast and faded
|
|
245
|
+
across it — is a thing to reuse rather than retype, and a recipe that names it follows every later
|
|
246
|
+
correction to it, exactly as a source over a category follows a rebuild.
|
|
247
|
+
|
|
248
|
+
It is merged as **heights**: the inner stack is evaluated for the tile and handed straight to the
|
|
249
|
+
merge above, with no encode and decode in between. That saves two conversions per tile and, more
|
|
250
|
+
to the point, does not round a value on its way from one merge into the next. So a nested source
|
|
251
|
+
may say anything that acts on heights — `maskValues`, `maskRange`, `heightAdjustment`, `cutline`,
|
|
252
|
+
`bounds`, the fade, `opacity`, `blend` — and nothing that describes stored bytes. `encoding` and
|
|
253
|
+
its parameters are refused, and so is `maskColors`, which compares channels as an archive stored
|
|
254
|
+
them and has none to compare here.
|
|
255
|
+
|
|
256
|
+
A loop is refused by name on the way down, so a stack naming itself and a ring of three are the
|
|
257
|
+
same case and neither needs a depth counter to stop. The depth limit is separate and is four:
|
|
258
|
+
every level is a full merge of everything under it, so a tile's cost multiplies rather than adds.
|
|
259
|
+
|
|
260
|
+
Coverage folds in one level down, and the ETag carries the inner stack's own ETag rather than its
|
|
261
|
+
id — without that, editing the inner recipe would leave the outer one serving from a cache that
|
|
262
|
+
still believed in it, and propagation is the whole point of naming a stack instead of copying it.
|
|
263
|
+
The console offers held stacks in the source picker, alongside categories and archives.
|
|
264
|
+
|
|
265
|
+
- **An export can set its attribution, and starts with the right one.** An archive travels without
|
|
266
|
+
the style that loaded it — seeded, mirrored, opened by people who never saw the stack it came from
|
|
267
|
+
— so its own metadata is the only place the credit survives. The export dialog has an
|
|
268
|
+
**Attribution** field, filled in from the stack: its own where the recipe states one, otherwise
|
|
269
|
+
every source's joined. Editable, because an export may be published under terms the recipe does
|
|
270
|
+
not know; filled in rather than blank, because unlike the description it is not something only the
|
|
271
|
+
person exporting knows.
|
|
272
|
+
|
|
273
|
+
Joined with ` | ` rather than `, `, in the TileJSON as well as in the archive. These strings are
|
|
274
|
+
almost always HTML links and a comma between two anchors renders as part of the last one's text,
|
|
275
|
+
which is why MapLibre, Mapbox and OpenLayers all separate them this way.
|
|
276
|
+
|
|
277
|
+
### 🐞 Bug fixes
|
|
278
|
+
- _...Add new stuff here..._
|
|
279
|
+
|
|
10
280
|
## 0.79.1
|
|
11
281
|
### ✨ Features and improvements
|
|
12
282
|
- _...Add new stuff here..._
|
package/README.md
CHANGED
|
@@ -66,7 +66,7 @@ been sending and receiving, kept in SQLite so it survives a restart; an indicato
|
|
|
66
66
|
peers can reach this node at all; and a settings screen covering monitored folders, watched web
|
|
67
67
|
locations, remote nodes, save locations, access tokens and the external-program hooks.
|
|
68
68
|
|
|
69
|
-
**Publishes RSS.** `/feed.xml
|
|
69
|
+
**Publishes RSS.** `/feed.xml` for every archive, `/categories.xml` for the build each category currently resolves to, `/stacks.xml` for the stack recipes, and `/feed/<category>.xml` per category. Plain RSS 2.0 with
|
|
70
70
|
torrent enclosures, so **qBittorrent's built-in RSS auto-downloader can subscribe today** with no
|
|
71
71
|
new software. Items also carry a namespaced description of the map — format, zoom range, bounds,
|
|
72
72
|
tile count — so a subscriber can decide whether it wants a 72 GiB download before starting one.
|
|
@@ -813,6 +813,8 @@ which the endpoint answers 501.
|
|
|
813
813
|
| `GET` | `/stacks/:id/:size/:z/:x/:y.:ext` | The same tile at 256 or 512 px. A tile's coordinates are an extent rather than a pixel count, so this is the same ground on a finer or coarser grid |
|
|
814
814
|
| `GET` | `/stacks/:id/:z/:x/:y.:ext` | One tile of a stack — **public**. Answered by the topmost source holding it; `X-Stack-Sources` says which were asked and what each said |
|
|
815
815
|
| `GET` | `/feed.xml`, `/feed/:category.xml`, `/latest/:category.xml` | RSS — **public** |
|
|
816
|
+
| `GET` | `/categories.xml` | Every category, as the build each resolves to — **public** |
|
|
817
|
+
| `GET` | `/stacks.xml`, `/stacks/:id.xml` | Stack recipes, for another node to follow — **public** |
|
|
816
818
|
|
|
817
819
|
Everything under `/api/` is guarded once a credential is configured; tiles, TileJSON and the feeds
|
|
818
820
|
never are. A `peer` token may read but not change, and may be narrowed to some categories. See
|