pmtiles-swarm 0.89.0 β 0.90.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 +76 -0
- package/README.md +1 -0
- package/docs/tile-stacks.md +213 -14
- package/package.json +1 -1
- package/src/api.js +192 -94
- package/src/bake-jobs.js +5 -3
- package/src/stack-feed.js +68 -1
- package/src/stack-tile.js +83 -22
- package/src/stacks.js +154 -5
- package/src/tiles.js +109 -1
- package/src/url-sources.js +249 -0
- package/src/web/index.html +307 -17
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,82 @@
|
|
|
7
7
|
### π Bug fixes
|
|
8
8
|
- _...Add new stuff here..._
|
|
9
9
|
|
|
10
|
+
## 0.90.0
|
|
11
|
+
### β¨ Features and improvements
|
|
12
|
+
- **A stack source may name a URL instead of a category, archive or stack.** `{ "url": "https://β¦" }`,
|
|
13
|
+
read straight over HTTP with no torrent involved β for an archive published as a plain download,
|
|
14
|
+
like Mapterhorn's terrain: a global base plus hundreds of regional patches, 11.8 TiB in total and
|
|
15
|
+
never meant to be downloaded whole. `FetchSource`, from the `pmtiles` package this project already
|
|
16
|
+
depends on, asks for byte ranges the way a swarm-backed source does; a tile costs the header once,
|
|
17
|
+
the directory once, and the tile itself.
|
|
18
|
+
|
|
19
|
+
Parent climbing works the same way it does for a category source β a shallow archive upscales for a
|
|
20
|
+
deeper request through the same code, since reading one is now only a question of which store method
|
|
21
|
+
answers. New per-source `minzoom` and `maxzoom` fields, checked before anything is opened, are what
|
|
22
|
+
make hundreds of these practical: a tile request outside a source's stated box or zoom range skips it
|
|
23
|
+
without a request leaving this node, which for a stack built from a provider's whole file list is
|
|
24
|
+
most of them, on every tile. A URL source always decodes rather than passing through raw, having no
|
|
25
|
+
infohash to answer that question with, and is not seeded, retired or rebuilt β none of the mechanisms
|
|
26
|
+
built for an archive this node actually holds apply to one it does not.
|
|
27
|
+
|
|
28
|
+
- **A provider's list of PMTiles URLs can be imported as sources.** **Stacks β Import URL listβ¦**,
|
|
29
|
+
or `POST /api/stacks/<id>/import`. Mapterhorn's `download_urls.json` names 458 files with the box
|
|
30
|
+
and zoom range of each; naming those by hand is not work anybody should do once, let alone again
|
|
31
|
+
when the provider adds one. An index with an `items` list is read, and so is a plain list of
|
|
32
|
+
addresses for a provider that publishes no index β which shape it is is worked out from the
|
|
33
|
+
document, since somebody pasting an address has no reason to know.
|
|
34
|
+
|
|
35
|
+
The global file becomes `sources[0]` and `required`: a stack is painted bottom-first, so the thing
|
|
36
|
+
covering everywhere has to sit under everything patching it, and the index does not list it first.
|
|
37
|
+
Every other entry keeps its box and zoom range, which is what lets a tile outside one skip it
|
|
38
|
+
without a request leaving the node. The encoding is asked for rather than read β an index rarely
|
|
39
|
+
states it, Mapterhorn's files are all terrarium and its JSON never says so, and a terrain source
|
|
40
|
+
read with the wrong one is a cliff face.
|
|
41
|
+
|
|
42
|
+
The list is fetched **by the node**, not the browser: the console is often on a different network,
|
|
43
|
+
and what matters is that the machine which will read the archives can reach them. **Check** shows
|
|
44
|
+
what an import would write before it writes anything.
|
|
45
|
+
|
|
46
|
+
In the editor an imported batch is one row rather than several hundred cards, with **Re-import**
|
|
47
|
+
and **Remove all**. A re-import replaces only what came from that same address β anything typed by
|
|
48
|
+
hand is left alone β and puts the batch back **where it already was** rather than on the end:
|
|
49
|
+
painting order is the whole meaning of a stack, and a batch that moved each time would quietly bury
|
|
50
|
+
a local override, a day later, on a schedule, with nothing to say why the map had changed.
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
- **A stack can follow a provider's file list on a timer.** A stack feed row with **Into stack** set
|
|
54
|
+
reads a URL list rather than another node's recipes, and keeps the named stack level with it.
|
|
55
|
+
Mapterhorn's index has grown through several versions, and a node that imported it once is a node
|
|
56
|
+
serving whenever that was. The row says which of the two it is by whether **Into stack** is filled
|
|
57
|
+
in β a feed of recipes names its own stacks and an index cannot, being a list of files with no
|
|
58
|
+
opinion about what they are for, so the row supplies the stack and the encoding as well.
|
|
59
|
+
|
|
60
|
+
It reconciles exactly as **Re-import** does: hand-written sources untouched, the batch back where it
|
|
61
|
+
was, a withdrawn file dropped rather than left to 404 per tile. A poll where nothing changed writes
|
|
62
|
+
nothing at all β not the same recipe again, which would move its revision and with it every tile
|
|
63
|
+
cached against it. A stack that does not exist yet is created; one that does keeps everything it had.
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
### π Bug fixes
|
|
67
|
+
- **A stack reported no encoding unless its recipe stated one outright.** Reading `output.encoding`
|
|
68
|
+
alone reports null for the ordinary recipe β the one that re-encodes to nothing and writes whatever
|
|
69
|
+
its base source is written in, which is every imported stack, since an import sets the encoding per
|
|
70
|
+
source. Three places read it that way and all three were wrong about terrain: the console offered no
|
|
71
|
+
terrain preview, the TileJSON told clients nothing so terrarium heights rendered through the mapbox
|
|
72
|
+
formula, and a baked archive was written with no encoding metadata at all β an export that had to be
|
|
73
|
+
corrected by hand afterwards to be readable.
|
|
74
|
+
|
|
75
|
+
- **A stack's TileJSON listed every source.** For a stack imported from a provider's index that is 458
|
|
76
|
+
addresses and 55 KB, in a document every map load fetches, describing files a client cannot use and
|
|
77
|
+
will never ask for β it reads tiles from the XYZ endpoint. It now names 25 with a count of the rest.
|
|
78
|
+
The extension it advertises is the one the endpoint actually serves, too: a stack whose sources state
|
|
79
|
+
no format has no coverage format to read, and the document said `.bin` for tiles answered as webp.
|
|
80
|
+
|
|
81
|
+
- **Cancel asked you to fill the form in first.** A `<button>` in a form submits, and a submit is
|
|
82
|
+
checked against the form's required fields before it goes anywhere β so cancelling the import or
|
|
83
|
+
warm dialog answered "Please fill out this field" about a field that was about to be discarded.
|
|
84
|
+
|
|
85
|
+
|
|
10
86
|
## 0.89.0
|
|
11
87
|
### β¨ Features and improvements
|
|
12
88
|
- **Both feeds are linked at the foot of the public page.** `RSS feed` is `archive RSS feed` now and
|
package/README.md
CHANGED
|
@@ -807,6 +807,7 @@ which the endpoint answers 501.
|
|
|
807
807
|
| `GET` | `/api/torrents/:infoHash/stacks` | Which stacks would break if this archive were removed, and how |
|
|
808
808
|
| `GET`, `PUT`, `DELETE` | `/api/stacks/:id/raw`, `/api/stacks/:id` | Read a recipe as written, save one, or remove it. `/raw` is the recipe; `/api/stacks` is what it resolved to |
|
|
809
809
|
|
|
810
|
+
| `POST` | `/api/stacks/:id/import` | Import a provider's list of PMTiles URLs as sources on this stack. An index with an `items` list, or a plain list of addresses; `dryRun` says what it would write without writing it |
|
|
810
811
|
| `POST`, `DELETE` | `/api/stacks/:id/bake` | Run the stack over its sources and write the result as an archive, or stop one that is running. Answers as soon as the job starts; watch it on `/api/stacks` |
|
|
811
812
|
| `GET` | `/stacks/:id/preview` | A map of a stack, for looking at it β **public** |
|
|
812
813
|
| `GET` | `/stacks/:id/tiles.json` | TileJSON for a stack β **public**. `maxzoom` is the maximum over its sources, not the minimum |
|
package/docs/tile-stacks.md
CHANGED
|
@@ -53,6 +53,8 @@ of its parts.
|
|
|
53
53
|
- [What a mask has to match](#what-a-mask-has-to-match)
|
|
54
54
|
- [Feathering a seam](#feathering-a-seam)
|
|
55
55
|
- [A stack as a source](#a-stack-as-a-source)
|
|
56
|
+
- [A source read straight from a URL](#a-source-read-straight-from-a-url)
|
|
57
|
+
- [Importing a list of URLs](#importing-a-list-of-urls)
|
|
56
58
|
- [Exporting on a schedule](#exporting-on-a-schedule)
|
|
57
59
|
- [Finding a stack](#finding-a-stack)
|
|
58
60
|
- [Syncing a stack to another node](#syncing-a-stack-to-another-node)
|
|
@@ -255,20 +257,23 @@ it differs from the snake_case rio-rgbify-merge uses.
|
|
|
255
257
|
|
|
256
258
|
### Source fields
|
|
257
259
|
|
|
258
|
-
| Field | Meaning
|
|
259
|
-
| ---------------------- |
|
|
260
|
-
| `category` | Resolve to the newest build in this category.
|
|
261
|
-
| `archive` | Or pin one infohash.
|
|
262
|
-
| `
|
|
263
|
-
| `
|
|
264
|
-
| `
|
|
265
|
-
| `
|
|
266
|
-
| `
|
|
267
|
-
| `
|
|
268
|
-
| `
|
|
269
|
-
| `
|
|
270
|
-
| `
|
|
271
|
-
| `
|
|
260
|
+
| Field | Meaning |
|
|
261
|
+
| ---------------------- | -------------------------------------------------------------------------------------------------------------- |
|
|
262
|
+
| `category` | Resolve to the newest build in this category. |
|
|
263
|
+
| `archive` | Or pin one infohash. |
|
|
264
|
+
| `stack` | Or another stack, merged as heights. See "A stack as a source". |
|
|
265
|
+
| `url` | Or an archive read straight from an http(s) address, no torrent involved. Exactly one of the four. |
|
|
266
|
+
| `minzoom` / `maxzoom` | Skip a `url` source outside this zoom range before opening it. Optional; the header answers anyway without it. |
|
|
267
|
+
| `required` | A tile fails rather than being served without this source. Defaults true for the bottom-most, false above. |
|
|
268
|
+
| `encoding` | `mapbox` or `terrarium`. Elevation space only. |
|
|
269
|
+
| `baseVal` / `interval` | Mapbox decode offset and step. Default `-10000` and `0.1`. |
|
|
270
|
+
| `maskValues` | Decoded heights meaning "no data here". Elevation space only. |
|
|
271
|
+
| `maskRange` | `[low, high]` in metres, or a list of them. Everything inside is nodata. Elevation space only. |
|
|
272
|
+
| `heightAdjustment` | Metres, added **after** masking. Elevation space only. |
|
|
273
|
+
| `feather` | Pixels to fade in over wherever the source stops: a `cutline`, `bounds`, or the holes a mask leaves. Max 64. |
|
|
274
|
+
| `featherMetres` | The same fade written as metres of ground, worked out per tile. Usually the one to reach for. |
|
|
275
|
+
| `opacity` | `0`β`1`, scales the source alpha. RGBA space only. |
|
|
276
|
+
| `blend` | `normal`, `multiply`, `screen`, `overlay`, `darken`, `lighten`. RGBA space only. |
|
|
272
277
|
|
|
273
278
|
`attribution` is not optional in practice. A stack is a derived work of every
|
|
274
279
|
source in it, and the thing that reliably gets lost when tiles are combined is
|
|
@@ -1631,6 +1636,200 @@ would wait a day before showing it had ever run.
|
|
|
1631
1636
|
That is what a second node serving the same stacks wants: only one of them
|
|
1632
1637
|
should be the node that bakes.
|
|
1633
1638
|
|
|
1639
|
+
## A source read straight from a URL
|
|
1640
|
+
|
|
1641
|
+
A source may name a URL instead of a category, an archive or a stack:
|
|
1642
|
+
|
|
1643
|
+
```json
|
|
1644
|
+
{
|
|
1645
|
+
"id": "mapterhorn",
|
|
1646
|
+
"sources": [
|
|
1647
|
+
{
|
|
1648
|
+
"url": "https://download.mapterhorn.com/planet.pmtiles",
|
|
1649
|
+
"encoding": "terrarium",
|
|
1650
|
+
"minzoom": 0,
|
|
1651
|
+
"maxzoom": 12
|
|
1652
|
+
},
|
|
1653
|
+
{
|
|
1654
|
+
"url": "https://download.mapterhorn.com/6-32-31.pmtiles",
|
|
1655
|
+
"encoding": "terrarium",
|
|
1656
|
+
"bounds": [0, 0, 5.625, 5.625],
|
|
1657
|
+
"minzoom": 13,
|
|
1658
|
+
"maxzoom": 13
|
|
1659
|
+
}
|
|
1660
|
+
]
|
|
1661
|
+
}
|
|
1662
|
+
```
|
|
1663
|
+
|
|
1664
|
+
Mapterhorn publishes terrain this way: a global base to about z12 and several
|
|
1665
|
+
hundred regional patches reaching higher, each one a plain HTTPS download and
|
|
1666
|
+
none of it in a swarm this node is part of. Downloading 11.8 TiB of it first,
|
|
1667
|
+
to get it into a category the ordinary way, is not a step anybody wants β the
|
|
1668
|
+
whole point is to read a tile at a time, from wherever it already is.
|
|
1669
|
+
|
|
1670
|
+
### Read the same way a swarm archive is
|
|
1671
|
+
|
|
1672
|
+
`FetchSource`, from the `pmtiles` package this project already depends on,
|
|
1673
|
+
asks for byte ranges the way `TorrentSource` does β a request for a tile costs
|
|
1674
|
+
the header once, the directory once, and the tile itself, not the file. A URL
|
|
1675
|
+
source keeps its own small cache of open readers, separate from the one
|
|
1676
|
+
catalog archives share, so a stack naming hundreds of them is not competing
|
|
1677
|
+
with the archives this node actually seeds for the same budget.
|
|
1678
|
+
|
|
1679
|
+
Parent climbing works the same way it does for a category source: a shallow
|
|
1680
|
+
archive β Mapterhorn's own base stops at z12 β is upscaled for a deeper
|
|
1681
|
+
request exactly as GEBCO is, through the same code, because reading one is now
|
|
1682
|
+
a question of which store method answers and nothing else.
|
|
1683
|
+
|
|
1684
|
+
### Cheap to have hundreds of
|
|
1685
|
+
|
|
1686
|
+
The one thing that makes hundreds of these practical rather than merely
|
|
1687
|
+
possible: `bounds` and the two new per-source fields, `minzoom` and `maxzoom`,
|
|
1688
|
+
are checked **before** anything is opened. A tile request outside a source's
|
|
1689
|
+
stated box, or at a zoom no climb from here could reach into its stated range,
|
|
1690
|
+
skips that source without a request leaving this node. For a stack built from
|
|
1691
|
+
Mapterhorn's own file list β one tile mostly intersects a global base and one
|
|
1692
|
+
or two regional patches β that is most of several hundred sources, on every
|
|
1693
|
+
tile, settled from the recipe alone.
|
|
1694
|
+
|
|
1695
|
+
Without a stated zoom range this still works, just later: `FetchSource`'s own
|
|
1696
|
+
header read answers "no tile" cheaply once opened, the same way a catalog
|
|
1697
|
+
archive's does. The static check is what avoids opening most of them at all,
|
|
1698
|
+
not what makes opening one safe.
|
|
1699
|
+
|
|
1700
|
+
### What it does not do
|
|
1701
|
+
|
|
1702
|
+
**Never passed through raw.** A URL source has no infohash to answer the
|
|
1703
|
+
byte-for-byte question with, so a stack containing one always decodes β
|
|
1704
|
+
`needsCodec` says so from the recipe, the same answer a nested stack gets and
|
|
1705
|
+
for the same reason.
|
|
1706
|
+
|
|
1707
|
+
**Not seeded, not retired, not rebuilt.** This node does not hold a copy, so
|
|
1708
|
+
none of the mechanisms that apply to an archive apply here. The URL is not
|
|
1709
|
+
content-addressed either β nothing stops whoever publishes it editing the file
|
|
1710
|
+
in place β so it is treated as unstable for caching, the same as a category
|
|
1711
|
+
and for a related reason: a category is unstable because the build under it
|
|
1712
|
+
moves, a URL because the operator does not control what is at the other end
|
|
1713
|
+
of it.
|
|
1714
|
+
|
|
1715
|
+
**Feathering a mask edge only partly.** A URL source that both masks and fades
|
|
1716
|
+
needs its parents read to measure the ramp, exactly as a catalog source does β
|
|
1717
|
+
and that path is wired up. What is not: a parent read that fails is caught and
|
|
1718
|
+
skipped silently, the same forgiving rule `readMaskEdges` already applies to a
|
|
1719
|
+
catalog source whose parent cannot be reached, so a patchy host degrades the
|
|
1720
|
+
ramp rather than failing the tile.
|
|
1721
|
+
|
|
1722
|
+
## Importing a list of URLs
|
|
1723
|
+
|
|
1724
|
+
Naming 458 sources by hand is not work anybody should do once, let alone again
|
|
1725
|
+
when the provider adds a file. A provider that publishes terrain as many
|
|
1726
|
+
separate archives generally publishes an index of them as well, and that index
|
|
1727
|
+
already carries exactly what the merge needs in order to skip a source without
|
|
1728
|
+
opening it.
|
|
1729
|
+
|
|
1730
|
+
**Stacks β Import URL listβ¦** takes the address of one. Mapterhorn's is
|
|
1731
|
+
`https://download.mapterhorn.com/download_urls.json`.
|
|
1732
|
+
|
|
1733
|
+
### Two shapes, detected rather than declared
|
|
1734
|
+
|
|
1735
|
+
An **index**: an object with an `items` list, or a bare array of the same
|
|
1736
|
+
entries. Each entry needs a `url` and the six numbers Mapterhorn's carries β
|
|
1737
|
+
`min_lon`, `min_lat`, `max_lon`, `max_lat`, `min_zoom`, `max_zoom` β which
|
|
1738
|
+
become the source's `bounds` and its `minzoom`/`maxzoom`.
|
|
1739
|
+
|
|
1740
|
+
A **plain list** of addresses, one per line or as a JSON array of strings, for
|
|
1741
|
+
a provider that publishes no index. Blank lines and `#` comments are ignored.
|
|
1742
|
+
Nothing states a box or a zoom range, so every source is opened for every tile
|
|
1743
|
+
it might cover β which is the cost of having no index, and the reason the index
|
|
1744
|
+
path is worth preferring.
|
|
1745
|
+
|
|
1746
|
+
Which one it is is worked out from the document, because somebody pasting an
|
|
1747
|
+
address has no reason to know which their provider answers with, and guessing
|
|
1748
|
+
wrong would be a silent import of nothing rather than an error.
|
|
1749
|
+
|
|
1750
|
+
### The global file becomes the base
|
|
1751
|
+
|
|
1752
|
+
An entry whose box covers the world β Mapterhorn's `planet.pmtiles` says -180
|
|
1753
|
+
to 180 and Β±85.0511 β is made `sources[0]` and marked `required`. A stack is
|
|
1754
|
+
painted bottom-first, so the thing covering everywhere has to sit underneath
|
|
1755
|
+
everything patching it, and the index does not list it first.
|
|
1756
|
+
|
|
1757
|
+
It is given no `bounds`: a clip excluding nothing is a rasterise on every
|
|
1758
|
+
partial tile for an answer that was never in doubt. Where several entries claim
|
|
1759
|
+
to be global, the one reaching deepest wins and the rest become ordinary
|
|
1760
|
+
layers.
|
|
1761
|
+
|
|
1762
|
+
### The encoding is the importer's to state
|
|
1763
|
+
|
|
1764
|
+
An index rarely says. Mapterhorn's files are all terrarium and its JSON never
|
|
1765
|
+
mentions it, so the dialog asks and sets it on every imported source β a
|
|
1766
|
+
terrain source read with the wrong encoding is a cliff face rather than a
|
|
1767
|
+
mistake anybody has to guess at.
|
|
1768
|
+
|
|
1769
|
+
### Re-importing
|
|
1770
|
+
|
|
1771
|
+
Every imported source carries `importedFrom`, naming the list it came from.
|
|
1772
|
+
That is what lets the editor draw one row for several hundred sources, and what
|
|
1773
|
+
a re-import uses to know which are its to replace. A source typed by hand
|
|
1774
|
+
carries none and is never touched.
|
|
1775
|
+
|
|
1776
|
+
The batch also goes back **where it already was**, rather than on the end.
|
|
1777
|
+
Painting order is the whole meaning of a stack: a batch that moved on every
|
|
1778
|
+
re-import would quietly bury a local override somebody had deliberately placed
|
|
1779
|
+
above it β a day later, on a schedule, with nothing to say why the map changed.
|
|
1780
|
+
|
|
1781
|
+
### Fetched by the node, not the browser
|
|
1782
|
+
|
|
1783
|
+
The console is often on a different network from the node, and a list is only
|
|
1784
|
+
useful if the machine that will actually read those archives can reach them. A
|
|
1785
|
+
458-entry index is also not something a browser should be parsing and posting
|
|
1786
|
+
back a megabyte of.
|
|
1787
|
+
|
|
1788
|
+
`POST /api/stacks/<id>/import` takes `{ url, encoding }`. With `dryRun` it
|
|
1789
|
+
answers what it would write without writing it, which is what **Check** in the
|
|
1790
|
+
dialog shows and what the editor's **Re-import** uses to update an unsaved
|
|
1791
|
+
draft. A draft can send its own `sources` along, so the batch comes back merged
|
|
1792
|
+
into the order the operator is holding rather than into the stored one.
|
|
1793
|
+
|
|
1794
|
+
### Following the list instead of importing it once
|
|
1795
|
+
|
|
1796
|
+
Mapterhorn's index has grown through several versions, and a node that imported
|
|
1797
|
+
it in March is a node serving March. The same address goes in **Settings β
|
|
1798
|
+
Feeds β Stack feeds** with **Into stack** naming the stack to keep level, and
|
|
1799
|
+
the poll that already checks other nodes for recipes checks this for files.
|
|
1800
|
+
|
|
1801
|
+
The row says which of the two it is by whether **Into stack** is filled in. A
|
|
1802
|
+
feed of recipes names its own stacks and this one cannot: an index is a list of
|
|
1803
|
+
files with no opinion about what they are for, so the row supplies the stack
|
|
1804
|
+
and the encoding the index does not state.
|
|
1805
|
+
|
|
1806
|
+
Reconciliation is the same one **Re-import** does, for the same reasons and
|
|
1807
|
+
with the same guarantees: hand-written sources are left alone, the batch stays
|
|
1808
|
+
where it was put, and a file the provider withdrew stops being asked for. A
|
|
1809
|
+
poll where nothing changed writes nothing at all β not the same recipe again,
|
|
1810
|
+
which would move its revision and with it every tile cached against it.
|
|
1811
|
+
|
|
1812
|
+
A stack named here that does not exist yet is created. One that does keeps
|
|
1813
|
+
everything it had; following a list never amounts to taking a recipe over.
|
|
1814
|
+
|
|
1815
|
+
### What a bulk import cannot say
|
|
1816
|
+
|
|
1817
|
+
Every imported source gets a box and a zoom range, because the index states
|
|
1818
|
+
them. It gets no **mask** β no per-source clip beyond its bounding box, no
|
|
1819
|
+
feathered edge β because there is nowhere in an index for one to come from.
|
|
1820
|
+
|
|
1821
|
+
That is fine where the provider has already merged for you. Mapterhorn's files
|
|
1822
|
+
are cut so that a patch and the planet agree at the seam, so a rectangular clip
|
|
1823
|
+
is the right clip. It is not fine where two sources genuinely overlap at
|
|
1824
|
+
different quality and the edge between them matters, and there the answer is to
|
|
1825
|
+
import the batch and then write the overlapping source by hand, above it.
|
|
1826
|
+
|
|
1827
|
+
Note what happens if you edit an imported source instead: a mask typed onto one
|
|
1828
|
+
is wiped by the next re-import, which replaces the batch entire. On a schedule
|
|
1829
|
+
that is a map changing at 4am with nothing to say why. A hand-written source
|
|
1830
|
+
carries no `importedFrom`, is never replaced, and is the place for anything the
|
|
1831
|
+
index could not have told us.
|
|
1832
|
+
|
|
1634
1833
|
## Finding a stack
|
|
1635
1834
|
|
|
1636
1835
|
A stack has no infohash and appears in no feed, so nothing about it is
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pmtiles-swarm",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.90.0",
|
|
4
4
|
"description": "BitTorrent distribution for PMTiles map archives: create torrents, watch folders, publish and subscribe to RSS feeds, and seed through qBittorrent or an embedded client",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|