pmtiles-swarm 0.17.1 → 0.18.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 +8 -0
- package/docs/publishing.md +18 -0
- package/package.json +1 -1
- package/src/config.js +9 -0
- package/src/latest-link.js +22 -8
- package/src/watch.js +4 -0
- package/src/web/index.html +8 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
## master
|
|
4
4
|
### ✨ Features and improvements
|
|
5
|
+
- **A watched folder can ask for its stable name to be a hard link.**
|
|
6
|
+
`latestLinkType: "hard"` reverses the order the two kinds are attempted in,
|
|
7
|
+
for a folder whose archives are read *through* that name rather than followed
|
|
8
|
+
to see where it points. A hard link still resolves after the build it names
|
|
9
|
+
is retired; a symlink is left pointing at nothing, which a tile endpoint
|
|
10
|
+
reports as a missing archive while the bytes are still on the disk. The other
|
|
11
|
+
kind remains the fallback in both directions, and the log now always says
|
|
12
|
+
which was made rather than only mentioning the unexpected one.
|
|
5
13
|
- **The default trackers now include WebSocket ones.** They were two `udp://` entries, so an
|
|
6
14
|
archive created with stock configuration was undiscoverable from a browser — healthy in a
|
|
7
15
|
desktop client, invisible from a page, with nothing in either to say why. A browser speaks
|
package/docs/publishing.md
CHANGED
|
@@ -377,6 +377,24 @@ A new `.pmtiles` appearing in a watched folder is imported automatically.
|
|
|
377
377
|
|
|
378
378
|
Editable in **Settings → Monitored folders** as a table, rather than by hand.
|
|
379
379
|
|
|
380
|
+
### What kind of link the stable name is
|
|
381
|
+
|
|
382
|
+
`latestLink` is made as a symlink by default. Set `latestLinkType: "hard"` on
|
|
383
|
+
the folder when something reads the archive *through* that name — a tile
|
|
384
|
+
server, or anything else that opens it rather than following it to see where it
|
|
385
|
+
points.
|
|
386
|
+
|
|
387
|
+
The difference only shows when the build the name refers to goes away. A hard
|
|
388
|
+
link is another name for the same bytes, so it still resolves; a symlink is
|
|
389
|
+
left pointing at nothing. Retention repoints the name as it retires a build, so
|
|
390
|
+
neither case arises from ordinary housekeeping — but a rebuild that fails
|
|
391
|
+
between the two steps leaves a reader working in one arrangement and broken in
|
|
392
|
+
the other.
|
|
393
|
+
|
|
394
|
+
The other kind stays the fallback either way: Windows refuses symlinks without
|
|
395
|
+
elevation, and a hard link cannot cross a filesystem, so the name is still made
|
|
396
|
+
whichever was asked for. The log says which it got.
|
|
397
|
+
|
|
380
398
|
### One folder, several kinds of build
|
|
381
399
|
|
|
382
400
|
Categories and retention are decided per entry, not per directory. A generator
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pmtiles-swarm",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.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",
|
package/src/config.js
CHANGED
|
@@ -613,6 +613,15 @@ const DEFAULTS = {
|
|
|
613
613
|
* ever touch archives this same folder imported, and never remove the
|
|
614
614
|
* newest build however old it gets.
|
|
615
615
|
*
|
|
616
|
+
* `latestLinkType` picks how `latestLink` is made: `symbolic` (the default),
|
|
617
|
+
* or `hard` for a name something reads the archive through. A hard link
|
|
618
|
+
* still resolves after the build it names is retired, where a symlink is
|
|
619
|
+
* left pointing at nothing — so a tile endpoint opening that name keeps
|
|
620
|
+
* working across a rebuild either way, and only one of them keeps working if
|
|
621
|
+
* the rebuild fails halfway. The other kind stays the fallback in both
|
|
622
|
+
* directions, since Windows refuses symlinks without elevation and a hard
|
|
623
|
+
* link cannot cross a filesystem.
|
|
624
|
+
*
|
|
616
625
|
* `match` is a glob tested against the filename, for a folder holding more
|
|
617
626
|
* than one kind of build. Categories are decided per entry, so several
|
|
618
627
|
* entries can watch one folder and each take only its own archives:
|
package/src/latest-link.js
CHANGED
|
@@ -20,21 +20,35 @@ import path from 'node:path';
|
|
|
20
20
|
* this names is eventually deleted: a symlink is left dangling, while a hard
|
|
21
21
|
* link keeps the bytes alive until it too is gone. Retention never removes the
|
|
22
22
|
* newest build, so neither case arises from this node's own housekeeping.
|
|
23
|
+
*
|
|
24
|
+
* `type` chooses which is preferred, because which one is better depends on
|
|
25
|
+
* what opens the name. Something reading the archive through this link — a
|
|
26
|
+
* tile endpoint, say — is better served by a hard link: if the build it names
|
|
27
|
+
* is removed before the name is repointed, a hard link still resolves to the
|
|
28
|
+
* bytes while a symlink resolves to nothing. The other order is the default
|
|
29
|
+
* only because it is what has always happened here.
|
|
23
30
|
* @param {object} options - What to link and how to say so.
|
|
24
31
|
* @param {string} options.target - The build the name should resolve to.
|
|
25
32
|
* @param {string} options.name - The stable name, absolute or beside the target.
|
|
26
33
|
* @param {string} options.label - How to name the caller in the log.
|
|
34
|
+
* @param {string} [options.type] - 'symbolic' (default), 'hard', or 'auto'.
|
|
27
35
|
* @returns {Promise<string|undefined>} - The link made, or undefined on failure.
|
|
28
36
|
*/
|
|
29
|
-
export async function linkLatest({ target, name, label }) {
|
|
37
|
+
export async function linkLatest({ target, name, label, type = 'symbolic' }) {
|
|
30
38
|
const link = linkPathFor(target, name);
|
|
31
39
|
|
|
32
|
-
const
|
|
40
|
+
const symbolic = [
|
|
33
41
|
// The type is autodetected from the target on Windows and ignored
|
|
34
42
|
// everywhere else, which is right: the target is always a file here.
|
|
35
|
-
|
|
36
|
-
|
|
43
|
+
'symlink',
|
|
44
|
+
() => fs.symlink(target, link),
|
|
37
45
|
];
|
|
46
|
+
const hard = ['hard link', () => fs.link(target, link)];
|
|
47
|
+
|
|
48
|
+
// Both orders keep the other as a fallback rather than failing outright:
|
|
49
|
+
// Windows refuses symlinks without elevation, and a hard link cannot cross a
|
|
50
|
+
// filesystem. Whichever is asked for, the name still gets made.
|
|
51
|
+
const attempts = type === 'hard' ? [hard, symbolic] : [symbolic, hard];
|
|
38
52
|
|
|
39
53
|
for (const [kind, make] of attempts) {
|
|
40
54
|
try {
|
|
@@ -42,10 +56,10 @@ export async function linkLatest({ target, name, label }) {
|
|
|
42
56
|
// per build and the name by definition already exists after the first.
|
|
43
57
|
await fs.rm(link, { force: true });
|
|
44
58
|
await make();
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
);
|
|
59
|
+
// Always says which kind. Reporting only the unexpected one meant the
|
|
60
|
+
// common case was silent, and the only way to learn what you had was to
|
|
61
|
+
// list the directory and read the mode bits.
|
|
62
|
+
console.log(`${label} latest -> ${path.basename(target)} (${kind})`);
|
|
49
63
|
return link;
|
|
50
64
|
} catch (error) {
|
|
51
65
|
// Try the next kind rather than giving up on the first refusal; only the
|
package/src/watch.js
CHANGED
|
@@ -214,6 +214,10 @@ export class WatchManager {
|
|
|
214
214
|
target: entry.retainedAt ?? path.join(entry.savePath, entry.name),
|
|
215
215
|
name: folder.latestLink,
|
|
216
216
|
label: `[watch] ${folder.path}`,
|
|
217
|
+
// Per folder: a folder whose archives are opened through this
|
|
218
|
+
// name wants a hard link, one that is only a convenience for a
|
|
219
|
+
// person browsing the directory does not care.
|
|
220
|
+
type: folder.latestLinkType,
|
|
217
221
|
});
|
|
218
222
|
}
|
|
219
223
|
|
package/src/web/index.html
CHANGED
|
@@ -3427,6 +3427,14 @@
|
|
|
3427
3427
|
label: 'Stable name',
|
|
3428
3428
|
placeholder: 'off',
|
|
3429
3429
|
},
|
|
3430
|
+
{
|
|
3431
|
+
field: 'latestLinkType',
|
|
3432
|
+
label: 'Stable name is a',
|
|
3433
|
+
options: [
|
|
3434
|
+
['', 'symlink'],
|
|
3435
|
+
['hard', 'hard link'],
|
|
3436
|
+
],
|
|
3437
|
+
},
|
|
3430
3438
|
{
|
|
3431
3439
|
field: 'keep',
|
|
3432
3440
|
label: 'Builds to keep',
|