pmtiles-swarm 0.55.3 → 0.58.1
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 +116 -0
- package/docs/configuration.md +25 -3
- package/docs/internals.md +82 -0
- package/docs/running-as-a-service.md +31 -2
- package/docs/tilejson.md +16 -1
- package/package.json +1 -1
- package/src/api.js +33 -149
- package/src/catalog.js +12 -62
- package/src/config.js +47 -81
- package/src/engines/composite.js +12 -2
- package/src/engines/libtorrent.js +9 -2
- package/src/index.js +15 -0
- package/src/library.js +39 -74
- package/src/pmtiles-probe.js +16 -39
- package/src/sources.js +3 -0
- package/src/subscriptions.js +4 -0
- package/src/web/index.html +125 -35
- package/src/web/public.html +15 -1
- package/swarm.config.json.sample +10 -54
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,122 @@
|
|
|
7
7
|
### 🐞 Bug fixes
|
|
8
8
|
- _...Add new stuff here..._
|
|
9
9
|
|
|
10
|
+
## 0.58.1
|
|
11
|
+
### 🐞 Bug fixes
|
|
12
|
+
- **The sample configuration put every piece of state under the config file.** `"dataDir": "./data"`,
|
|
13
|
+
`"savePath": "./data/torrents-data"` and `"resumeDir": "./data/resume"` all resolve against the
|
|
14
|
+
config file — and the service guide puts that file in `/etc`. Anyone following both documents ended
|
|
15
|
+
up with a catalog, a resume directory and their archives on the partition meant for configuration,
|
|
16
|
+
having done nothing wrong. The sample now uses absolute paths, a test enforces it, and the node
|
|
17
|
+
warns at startup if state resolves under `/etc` anyway.
|
|
18
|
+
|
|
19
|
+
It is also shorter. A first config should get a node running, not demonstrate the whole surface —
|
|
20
|
+
`docs/configuration.md` is where the rest lives.
|
|
21
|
+
|
|
22
|
+
- **Moving state out of `/etc` had a trap in the instructions.** `mv OLD/data NEW/data` nests when the
|
|
23
|
+
destination exists, which it does after the documented setup — so the real directory ends up one
|
|
24
|
+
level too deep, the node writes a fresh empty catalog beside it, and an intact library reads as
|
|
25
|
+
lost. `docs/running-as-a-service.md` now gives a form that cannot nest, says to repoint
|
|
26
|
+
`libtorrent.resumeDir` as well as `dataDir`, and has you count catalog entries before and after.
|
|
27
|
+
|
|
28
|
+
## 0.58.0
|
|
29
|
+
### ✨ Features and improvements
|
|
30
|
+
- **A stable name for every kind of import, not just watched folders.** `latestLink` and
|
|
31
|
+
`latestLinkType` are now offered on watched web locations, RSS feeds and remote nodes as well — one
|
|
32
|
+
path a consumer can hold while the build behind it changes.
|
|
33
|
+
|
|
34
|
+
A scheduled source had the feature all along and no way to ask for it: the console never showed the
|
|
35
|
+
column. It was also ignoring `latestLinkType`, so a source asking for a hard link quietly got a
|
|
36
|
+
symlink. A subscription could not ask at all.
|
|
37
|
+
|
|
38
|
+
For a subscription the name is pointed at the archive **when the download finishes**. Until then
|
|
39
|
+
there is a marker file, or a sparse one still filling in, and a name resolving to either is worse
|
|
40
|
+
than no name: whatever opens it reads zeroes rather than failing.
|
|
41
|
+
|
|
42
|
+
- **The node says something when its state has landed in `/etc`.** Nobody chooses that — the
|
|
43
|
+
documented service layout puts the config file there, every path resolves relative to that file,
|
|
44
|
+
and the sample reads `"dataDir": "./data"`. So the catalog and the resume directory end up on the
|
|
45
|
+
partition meant for configuration. Warned rather than corrected: it is a real path that works, and
|
|
46
|
+
moving a running node's data would be worse than saying so.
|
|
47
|
+
|
|
48
|
+
### 🐞 Bug fixes
|
|
49
|
+
|
|
50
|
+
## 0.57.0
|
|
51
|
+
### ✨ Features and improvements
|
|
52
|
+
- **The three publishing switches are one **Local file** column on the import tables.** Three
|
|
53
|
+
dropdowns per row cost more width than the choice is worth, on tables that already scroll
|
|
54
|
+
sideways — and as an import default these are almost always decided together:
|
|
55
|
+
|
|
56
|
+
| option | serves the file | web seed | on the public page |
|
|
57
|
+
| --- | --- | --- | --- |
|
|
58
|
+
| `node` | — | — | — |
|
|
59
|
+
| `off` | no | — | — |
|
|
60
|
+
| `http` | yes | no | no |
|
|
61
|
+
| `http + catalog` | yes | no | yes |
|
|
62
|
+
| `http + web seed` | yes | yes | no |
|
|
63
|
+
| `http + web seed + catalog` | yes | yes | yes |
|
|
64
|
+
|
|
65
|
+
`http + catalog` is there rather than left out to keep a tidier ladder: a dropdown that could not
|
|
66
|
+
say it would round that state up to the nearest option it had, turning a web seed on — and
|
|
67
|
+
publishing the node to the swarm — because somebody re-saved an unrelated row. Every combination
|
|
68
|
+
survives being written and read back, which is what the round-trip test checks.
|
|
69
|
+
|
|
70
|
+
The archive details panel still offers the three separately. A row here sets a policy for what
|
|
71
|
+
arrives; the panel is where one archive gets picked over.
|
|
72
|
+
|
|
73
|
+
## 0.56.2
|
|
74
|
+
### 🐞 Bug fixes
|
|
75
|
+
- **Adding a save location in the console did nothing.** The row was read correctly and then thrown
|
|
76
|
+
away: the settings pane below renders every config key it does not explicitly skip as a raw-JSON
|
|
77
|
+
textarea, the skip list named `watch`, `sources` and `subscriptions` but not `locations`, and in
|
|
78
|
+
`saveSettings` the textarea loop runs after the row editors — so a copy of the list as it was when
|
|
79
|
+
the pane was drawn overwrote the one with the new row in it.
|
|
80
|
+
|
|
81
|
+
The skip is now derived from the registered row editors rather than listed by hand, so this cannot
|
|
82
|
+
happen again to the next editor somebody adds. The list was the bug; keeping a list and adding one
|
|
83
|
+
more name to it would have been the same bug waiting.
|
|
84
|
+
|
|
85
|
+
## 0.56.1
|
|
86
|
+
### ✨ Features and improvements
|
|
87
|
+
- **Comments trimmed back to house style, and the reasoning moved into the docs where it belongs.**
|
|
88
|
+
Recent work had been leaving 15–25 line explanatory blocks in the source; measured across
|
|
89
|
+
`catalog.js` that was 85 new comment lines against 76 new code lines. The argument for a decision
|
|
90
|
+
drifts out of date faster in a comment than in prose, and buries the code while it does it.
|
|
91
|
+
|
|
92
|
+
Two new sections in [docs/internals.md](docs/internals.md) give the displaced reasoning a home:
|
|
93
|
+
**Re-reading a summary an older prober wrote**, and **A validator for a URL that stays put** —
|
|
94
|
+
which is where the ETag story now lives, including why the infohash was the wrong choice and what
|
|
95
|
+
a PMTiles reader needs to see for `If-Range` and cross-origin `ETag` to work.
|
|
96
|
+
|
|
97
|
+
No behaviour change.
|
|
98
|
+
|
|
99
|
+
## 0.56.0
|
|
100
|
+
### ✨ Features and improvements
|
|
101
|
+
- **MapLibre Tiles are recognised.** PMTiles tile type `6` is MLT, and the tile-type table stopped at
|
|
102
|
+
`5` — so an MLT archive probed as `unknown` and was refused a tile endpoint it could have served,
|
|
103
|
+
even though the extension map had known about `.mlt` for a while.
|
|
104
|
+
|
|
105
|
+
No new TileJSON key was needed for it. MapLibre spells the tile encoding of a vector source
|
|
106
|
+
`encoding`, exactly as it does the elevation packing of a raster-dem one, and applies TileJSON
|
|
107
|
+
members to both after the source is constructed — so `encoding: "mlt"` reaches a vector source the
|
|
108
|
+
same way `encoding: "terrarium"` reaches a raster one. One key, two meanings, told apart by the
|
|
109
|
+
source type. The MLT value is read from the header rather than the metadata, because an archive
|
|
110
|
+
whose tile type says MapLibre Tiles is MLT-encoded and nothing needs to say so twice.
|
|
111
|
+
|
|
112
|
+
- **A resume-data shortfall is now reported.** The sidecar has been answering with how many torrents
|
|
113
|
+
were asked to write and how many managed it before the deadline, and both numbers were discarded —
|
|
114
|
+
by the engine wrapper, and again by the timer that called it. A torrent that does not write is one
|
|
115
|
+
that gets re-hashed on the next start, which for a 700 GiB archive is the difference between
|
|
116
|
+
seeding in seconds and seeding in half an hour. That is what "why is everything at 0%" looks like
|
|
117
|
+
from outside, and the silence here is part of why it was hard to see.
|
|
118
|
+
|
|
119
|
+
### 🐞 Bug fixes
|
|
120
|
+
- **The two pages disagreed about how large an archive was.** The console rounded to whole units
|
|
121
|
+
above ten and the public page always kept a decimal, so the same archive read as `81 GiB` on one
|
|
122
|
+
and `80.6 GiB` on the other. Both now keep a decimal from KiB up — these are mostly archive sizes,
|
|
123
|
+
and half a gigabyte is worth seeing — and a test holds the two helpers character-for-character
|
|
124
|
+
identical, since nothing about a duplicated function announces when it stops being a copy.
|
|
125
|
+
|
|
10
126
|
## 0.55.3
|
|
11
127
|
### 🐞 Bug fixes
|
|
12
128
|
- **A `/latest/` document could never be updated once a client had cached it.** The `ETag` was the
|
package/docs/configuration.md
CHANGED
|
@@ -488,9 +488,19 @@ moment a download finishes.
|
|
|
488
488
|
|
|
489
489
|
The node's setting is the default. A [watched folder](#watched-folders), a
|
|
490
490
|
[scheduled source](#scheduled-sources), an [RSS feed](#subscriptions) and a
|
|
491
|
-
remote node may each carry their own — the **
|
|
492
|
-
|
|
493
|
-
|
|
491
|
+
remote node may each carry their own — the **Local file** column on those
|
|
492
|
+
tables, which offers the three as one choice:
|
|
493
|
+
|
|
494
|
+
| option | `serveArchive` | `selfWebSeed` | `publicDownload` |
|
|
495
|
+
| --------------------------- | -------------- | ------------- | ---------------- |
|
|
496
|
+
| `node` | unset | unset | unset |
|
|
497
|
+
| `off` | `false` | — | — |
|
|
498
|
+
| `http` | `true` | — | — |
|
|
499
|
+
| `http + catalog` | `true` | — | `true` |
|
|
500
|
+
| `http + web seed` | `true` | `true` | — |
|
|
501
|
+
| `http + web seed + catalog` | `true` | `true` | `true` |
|
|
502
|
+
|
|
503
|
+
`node` means "no opinion" rather than "off". And any individual archive can be switched in the console, under **HTTP
|
|
494
504
|
sources** in its details.
|
|
495
505
|
|
|
496
506
|
An archive that says nothing goes on following the node, so changing the node's
|
|
@@ -586,6 +596,17 @@ it, and what turns a cold tile read from tens of seconds into well under one.
|
|
|
586
596
|
`webSeedBase` on its own assumes the watched folder is already the web root, since
|
|
587
597
|
nothing is moved.
|
|
588
598
|
|
|
599
|
+
**Every table that imports an archive offers this**, not just watched folders:
|
|
600
|
+
a [scheduled source](#scheduled-sources), an [RSS feed](#subscriptions) and a
|
|
601
|
+
remote node each take `latestLink` and `latestLinkType` too. The point is the
|
|
602
|
+
same in all four — a consumer holds one path and never learns the name of any
|
|
603
|
+
particular build.
|
|
604
|
+
|
|
605
|
+
For a subscription the name is pointed at the archive **when the download
|
|
606
|
+
finishes**, not when it is joined: until then there is a marker file or a sparse
|
|
607
|
+
one still filling in, and a name resolving to either is worse than no name at
|
|
608
|
+
all, because whatever opens it reads zeroes rather than failing.
|
|
609
|
+
|
|
589
610
|
`latestLinkType: 'hard'` is for a name something reads the archive _through_. A
|
|
590
611
|
hard link still resolves after the build it names is retired, where a symlink is
|
|
591
612
|
left pointing at nothing. The other kind stays the fallback in both directions,
|
|
@@ -625,6 +646,7 @@ entry gives either a `url` template or an `index` directory:
|
|
|
625
646
|
| `everyHours` | an interval instead |
|
|
626
647
|
| `md5` | overrides the node's [`md5`](#md5) for this source alone |
|
|
627
648
|
| `serveArchive`, `selfWebSeed`, `publicDownload` | override what this node offers of the archives this source fetches |
|
|
649
|
+
| `latestLink`, `latestLinkType` | a stable second name for the newest build, as on a watched folder |
|
|
628
650
|
|
|
629
651
|
Prefer a template where the naming is predictable: it asks a direct question,
|
|
630
652
|
gets a direct answer, and needs the upstream to publish no listing at all.
|
package/docs/internals.md
CHANGED
|
@@ -18,6 +18,8 @@ Operator-facing documentation is elsewhere — see [publishing](publishing.md),
|
|
|
18
18
|
- [Serving an MBTiles archive](#serving-an-mbtiles-archive)
|
|
19
19
|
- [Answering for a tile that is not there](#answering-for-a-tile-that-is-not-there)
|
|
20
20
|
- [Reading an archive that is still arriving](#reading-an-archive-that-is-still-arriving)
|
|
21
|
+
- [Re-reading a summary an older prober wrote](#re-reading-a-summary-an-older-prober-wrote)
|
|
22
|
+
- [A validator for a URL that stays put](#a-validator-for-a-url-that-stays-put)
|
|
21
23
|
- [The health endpoint](#the-health-endpoint)
|
|
22
24
|
- [The externally visible base URL](#the-externally-visible-base-url)
|
|
23
25
|
- [Scheduled sources](#scheduled-sources)
|
|
@@ -310,6 +312,86 @@ nothing to read yet and the same archive at 100% will, so a permanent
|
|
|
310
312
|
would put a swarm read behind each one. The limiter is in memory on purpose: a
|
|
311
313
|
restart is a reasonable moment to try again.
|
|
312
314
|
|
|
315
|
+
## Re-reading a summary an older prober wrote
|
|
316
|
+
|
|
317
|
+
An archive's summary — format, zoom range, bounds, vector layers, encoding — is
|
|
318
|
+
read once, written into the catalog, and never questioned again. That is right
|
|
319
|
+
as far as it goes: re-reading a header out of the swarm is not free, and for a
|
|
320
|
+
given infohash the answer cannot change.
|
|
321
|
+
|
|
322
|
+
It goes wrong the moment the prober learns to read something new. Every archive
|
|
323
|
+
probed before that keeps a summary with a hole in it, permanently, and the only
|
|
324
|
+
way out is to remove and re-add the archive by hand.
|
|
325
|
+
|
|
326
|
+
`encoding` was the case that made this plain. The key had been sitting in the
|
|
327
|
+
metadata of archives a node had been serving for months; teaching the prober to
|
|
328
|
+
read it changed nothing at all, because nothing ever asked again.
|
|
329
|
+
|
|
330
|
+
So `summarize()` stamps a `summaryVersion`, and a summary older than the current
|
|
331
|
+
prober is re-read once in the background — on a request for the archive's
|
|
332
|
+
TileJSON, rate-limited to once a minute per archive. Raise `SUMMARY_VERSION`
|
|
333
|
+
whenever a field is added and every archive in the catalog picks it up on its
|
|
334
|
+
own, with nothing to do on upgrade.
|
|
335
|
+
|
|
336
|
+
Two things this needs to get right, both learned the hard way:
|
|
337
|
+
|
|
338
|
+
- **Every route that serves a TileJSON has to trigger it**, not only
|
|
339
|
+
`/archives/<infohash>/tiles.json`. `/latest/<category>/tiles.json` is the URL
|
|
340
|
+
a style points at, so leaving it out missed exactly the archives that were
|
|
341
|
+
being consumed the documented way.
|
|
342
|
+
- **The write-back must not be conditioned on one field.** It began as a
|
|
343
|
+
vector-layer backfill and returned early unless layers turned up, which would
|
|
344
|
+
have discarded the encoding it had just gone to fetch.
|
|
345
|
+
|
|
346
|
+
## A validator for a URL that stays put
|
|
347
|
+
|
|
348
|
+
Everything under `/archives/` is addressed by infohash: the URL changes when the
|
|
349
|
+
content does, so it can be cached for a year and marked `immutable`. A `/latest/`
|
|
350
|
+
URL is the opposite — stable on purpose, with the content moving underneath it —
|
|
351
|
+
so it needs a validator, and a short TTL alone is a guess.
|
|
352
|
+
|
|
353
|
+
The obvious validator is the infohash of the archive the category resolved to.
|
|
354
|
+
It is wrong, and it fails in a way that has no bottom: these documents carry more
|
|
355
|
+
than which build they name. A TileJSON also carries the archive's summary; a
|
|
356
|
+
magnet also carries its web seeds and trackers; the feed carries both. Enrich a
|
|
357
|
+
summary or add a web seed and the body changes while the infohash does not — so
|
|
358
|
+
every cache in the path revalidates, is told `304`, and goes on serving the old
|
|
359
|
+
document. Not stale for a minute: unable to be updated again for as long as the
|
|
360
|
+
archive exists.
|
|
361
|
+
|
|
362
|
+
So these are tagged over the body they are sending. Two nodes answering
|
|
363
|
+
identically still produce identical tags, which is the property the infohash was
|
|
364
|
+
chosen for; two nodes answering differently no longer claim otherwise.
|
|
365
|
+
|
|
366
|
+
`/latest/<category>/archive.pmtiles` and the `.torrent` redirect keep the
|
|
367
|
+
infohash, because for those the infohash really is the whole content.
|
|
368
|
+
|
|
369
|
+
### Where the ranges have to be careful
|
|
370
|
+
|
|
371
|
+
A PMTiles reader does not fetch a file. It fetches a header, then a root
|
|
372
|
+
directory, then leaf directories, then tiles, over minutes or hours. If a
|
|
373
|
+
rebuild lands partway through, the offsets it read from the old build address
|
|
374
|
+
bytes in the new one — which does not fail loudly. It decodes as the wrong tile,
|
|
375
|
+
or as nothing, with no error anywhere naming the cause.
|
|
376
|
+
|
|
377
|
+
`If-Range` is therefore honoured on the category range endpoint: a range
|
|
378
|
+
conditioned on a build that is no longer current is refused _as a range_ and
|
|
379
|
+
answered in full, which a reader survives. `Last-Modified` is suppressed there so
|
|
380
|
+
a client cannot condition on a date instead — a build restored from a backup can
|
|
381
|
+
be newer while looking older.
|
|
382
|
+
|
|
383
|
+
The official reader closes the loop from its side, comparing the `ETag` of every
|
|
384
|
+
response against the one it saw first and re-reading the header when they differ.
|
|
385
|
+
That only works if it can see the tag: `ETag` is not exposed to cross-origin
|
|
386
|
+
JavaScript by default, so these routes send `Access-Control-Expose-Headers`.
|
|
387
|
+
Unexposed, the reader compares against `null`, the comparison never fires, and it
|
|
388
|
+
splices two builds in silence.
|
|
389
|
+
|
|
390
|
+
A weak validator is no better. The reader discards any tag beginning with `W/`,
|
|
391
|
+
which is exactly what Express derives from a file's size and mtime — and that
|
|
392
|
+
tag also differs per node, so two nodes behind a load balancer would hand a
|
|
393
|
+
reader two tags for byte-identical archives.
|
|
394
|
+
|
|
313
395
|
## The health endpoint
|
|
314
396
|
|
|
315
397
|
For a load balancer, which needs three things: no credential, a cheap answer, and
|
|
@@ -256,8 +256,10 @@ directory:
|
|
|
256
256
|
-> /etc/pmtiles-swarm/data
|
|
257
257
|
```
|
|
258
258
|
|
|
259
|
-
That is rarely what you want for a service
|
|
260
|
-
|
|
259
|
+
That is rarely what you want for a service, and the node says so on startup —
|
|
260
|
+
`/etc` is for configuration, and a catalog, a resume directory and possibly an
|
|
261
|
+
archive do not belong on that partition. The shipped sample uses absolute paths
|
|
262
|
+
for exactly this reason. Use them for anything holding data:
|
|
261
263
|
|
|
262
264
|
```json
|
|
263
265
|
{
|
|
@@ -267,6 +269,33 @@ holding data:
|
|
|
267
269
|
}
|
|
268
270
|
```
|
|
269
271
|
|
|
272
|
+
#### Moving state that already landed in the wrong place
|
|
273
|
+
|
|
274
|
+
Repoint **every** path that lived under the directory being moved, not just
|
|
275
|
+
`dataDir`: `libtorrent.resumeDir` is the one that is easy to miss, because it is
|
|
276
|
+
absolute and sits inside it.
|
|
277
|
+
|
|
278
|
+
And move with a destination that cannot nest. `mv /etc/pmtiles-swarm/data
|
|
279
|
+
/var/lib/pmtiles-swarm/data` puts the source _inside_ the destination when the
|
|
280
|
+
destination already exists — which it does, since the setup above creates it —
|
|
281
|
+
leaving `/var/lib/pmtiles-swarm/data/data`. The node then finds no catalog where
|
|
282
|
+
it was told to look, writes a fresh empty one, and every archive appears to have
|
|
283
|
+
been lost.
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
sudo systemctl stop pmtiles-swarm
|
|
287
|
+
sudo python3 -c "import json;print(len(json.load(open('OLD/catalog.json'))['entries']))"
|
|
288
|
+
|
|
289
|
+
sudo mv /etc/pmtiles-swarm/data /var/lib/pmtiles-swarm/ # note: no second 'data'
|
|
290
|
+
# repoint dataDir *and* libtorrent.resumeDir, then
|
|
291
|
+
|
|
292
|
+
sudo systemctl start pmtiles-swarm
|
|
293
|
+
sudo python3 -c "import json;print(len(json.load(open('NEW/catalog.json'))['entries']))"
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
The entry count before and after is the check that matters: an empty new catalog
|
|
297
|
+
is indistinguishable from a lost library until you count.
|
|
298
|
+
|
|
270
299
|
### 2. Every one of them in `ReadWritePaths`
|
|
271
300
|
|
|
272
301
|
`ProtectSystem=strict` presents the whole filesystem as read-only inside the
|
package/docs/tilejson.md
CHANGED
|
@@ -172,7 +172,22 @@ MapLibre applies every TileJSON member to the source after the source is
|
|
|
172
172
|
constructed, so an `encoding` here **overrides** one written in the style. That
|
|
173
173
|
is the intended direction: the archive is the thing that knows.
|
|
174
174
|
|
|
175
|
-
|
|
175
|
+
### `mlt`
|
|
176
|
+
|
|
177
|
+
The same key carries one more value, for a different kind of source. MapLibre
|
|
178
|
+
spells the tile encoding of a vector source `encoding` too, and `mlt` there means
|
|
179
|
+
the tiles are [MapLibre Tiles](https://maplibre.org/maplibre-tile-spec/) rather
|
|
180
|
+
than MVT — so there was no new key to invent for it, only a value to allow
|
|
181
|
+
through.
|
|
182
|
+
|
|
183
|
+
This one comes from the header rather than the metadata. PMTiles has a tile type
|
|
184
|
+
for MLT (`6`), and an archive whose tile type says MapLibre Tiles _is_
|
|
185
|
+
MLT-encoded; nothing needs to be written in the metadata to make it so.
|
|
186
|
+
Elevation packing is the opposite case — the header knows the tile is WebP and
|
|
187
|
+
nothing about what its three channels mean — which is why the two are read from
|
|
188
|
+
different places despite sharing a key.
|
|
189
|
+
|
|
190
|
+
Anything other than the values the style specification defines is dropped
|
|
176
191
|
rather than passed on. A client handed an encoding it does not recognise is
|
|
177
192
|
worse off than one handed nothing, because nothing at least leaves it free to
|
|
178
193
|
use its own default.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pmtiles-swarm",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.58.1",
|
|
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",
|