@finbheara/archive-client 0.20.2 → 0.22.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/README.md +33 -1
- package/dist/chunks/{index-6028k52f.js → index-zwecpq08.js} +122 -4
- package/dist/cli.js +121 -26
- package/dist/index.js +16 -2
- package/dist/types/cli.d.ts +9 -1
- package/dist/types/config.d.ts +1 -0
- package/dist/types/freshness.d.ts +18 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/manager.d.ts +25 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -139,13 +139,23 @@ m.unpin();
|
|
|
139
139
|
await m.add("<dir>", "archive-YYYY.MM.DD-N"); // a release built locally, under its own tag
|
|
140
140
|
await m.index("pinned"); // build every index
|
|
141
141
|
m.prune({ keep: 2, dryRun: true }); // the plan: what would go, and its bytes
|
|
142
|
+
await m.use("latest"); // fetch, index, pin, then prune to the newest 2
|
|
142
143
|
m.remove("<tag>"); // refuses the pinned release
|
|
143
144
|
m.dropIndexes(); // the index files only
|
|
144
145
|
await m.verify("<tag>"); // re-hash every cached file against its receipt
|
|
145
146
|
```
|
|
146
147
|
|
|
147
148
|
Every removal answers its plan first (`dryRun`), keeps the pinned release, and touches nothing
|
|
148
|
-
outside the cache root.
|
|
149
|
+
outside the cache root. A release's indexes live under its directory and go with it.
|
|
150
|
+
|
|
151
|
+
THE CACHE KEEPS ITSELF. A release is gigabytes, so a `use` (and so an `update`) that lands its
|
|
152
|
+
release prunes the cache once the pin has moved: it keeps the newest `keep` releases by tag (date,
|
|
153
|
+
cut, fraction), the new pin, and the pin before it, so a reader still on the previous release is
|
|
154
|
+
not cut off. A release fetched with `--release` and never pinned counts like any other. `keep` is
|
|
155
|
+
the call's own (`--keep N`, `use(spec, { keep })`), else the user-local `config keep <N>`, else
|
|
156
|
+
`DEFAULT_KEEP` (2), and a plain `prune` reads the same count. `--no-prune` (`prune: false`) skips
|
|
157
|
+
it, and a fetch or index that fails prunes nothing. The answer carries the plan (`pruned`), and
|
|
158
|
+
the command line prints one line per release removed and the MB freed.
|
|
149
159
|
|
|
150
160
|
## The indexes
|
|
151
161
|
|
|
@@ -707,8 +717,11 @@ is passed to the dancer resolver as asked (`rel.dancers`, above).
|
|
|
707
717
|
## The command line
|
|
708
718
|
|
|
709
719
|
idq config repo <owner/name>
|
|
720
|
+
idq config keep <N>
|
|
710
721
|
idq list [--remote]
|
|
711
722
|
idq fetch [--release <spec>] [--assets results,majors,<file>...]
|
|
723
|
+
idq update [--assets results,majors,<file>...] [--keep N] [--no-prune]
|
|
724
|
+
idq use <spec> [--assets results,majors,<file>...] [--keep N] [--no-prune]
|
|
712
725
|
idq pin <spec> | unpin | current
|
|
713
726
|
idq add <dir> --tag <tag>
|
|
714
727
|
idq index [--release <spec>] [<name>...] [--force]
|
|
@@ -748,6 +761,25 @@ is passed to the dancer resolver as asked (`rel.dancers`, above).
|
|
|
748
761
|
`stream` prints one JSON line per row (or entry), as it reads them. `dancer` prints the results of the one dancer where the answer is unique (every candidate's with
|
|
749
762
|
`--rows`). `--json` prints the call's answer as it is. `--cache` and `--repo` override the defaults.
|
|
750
763
|
|
|
764
|
+
`update` moves to the newest published release, as a package manager's `update` moves to the newest
|
|
765
|
+
version: it fetches it (verified as every download is), builds every index it knows, and only then
|
|
766
|
+
pins it, so a fetch or index that fails leaves the old pin in force, and then prunes the cache to
|
|
767
|
+
the newest releases (above: the pin and the one before it always stay). `use <spec>` does the same for
|
|
768
|
+
the release a spec names, older or newer (`releases().use(spec)` in the API; `update` is
|
|
769
|
+
`use("latest")`).
|
|
770
|
+
|
|
771
|
+
A query on the default spec (`pinned`, given or not) whose release is older than the newest
|
|
772
|
+
published one prints ONE line on stderr, and changes neither the answer nor the exit code:
|
|
773
|
+
|
|
774
|
+
answered from <tag>. <newer tag> is available: run `idq update` to get the latest
|
|
775
|
+
|
|
776
|
+
The remote is asked at most once per hour: what it said and when are kept in `<cache>/LATEST.json`,
|
|
777
|
+
and a call inside the hour reads that file. An offline, failed or slow check says nothing, and a
|
|
778
|
+
call waits for one at most 1.5 s, while its own query runs. A release named with `--release` (a
|
|
779
|
+
tag, a cut, `latest`) is never warned about: it was asked for. `main(argv, { freshness })` takes
|
|
780
|
+
`false` to turn the check off, or a `floor` (a cut the caller requires, and why: where only the
|
|
781
|
+
floor is newer, the line says the cut is required, with the why, and to run `idq use <cut>`), the `command` the warning names and the clock.
|
|
782
|
+
|
|
751
783
|
`resolve` speaks JSON lines: one answer per line out, and with no positional question the
|
|
752
784
|
questions are JSON lines on stdin, one per line (`{feis, code, year?, title?}`,
|
|
753
785
|
`{name, school?, age?, population?, oireachtas?}`,
|
|
@@ -3741,6 +3741,12 @@ function openCached2(tag, opts = {}) {
|
|
|
3741
3741
|
return new Release2(tag, root, JSON.parse(readFileSync3(p, "utf8")), opts);
|
|
3742
3742
|
}
|
|
3743
3743
|
|
|
3744
|
+
var DEFAULT_KEEP2 = 2;
|
|
3745
|
+
function checkKeep(keep) {
|
|
3746
|
+
if (!Number.isInteger(keep) || keep < 0)
|
|
3747
|
+
throw new Error(`prune: --keep ${keep} is not a count`);
|
|
3748
|
+
return keep;
|
|
3749
|
+
}
|
|
3744
3750
|
function duBytes(p) {
|
|
3745
3751
|
if (!existsSync5(p))
|
|
3746
3752
|
return 0;
|
|
@@ -3800,6 +3806,26 @@ class ReleaseManager2 {
|
|
|
3800
3806
|
writePin(this.root, pin);
|
|
3801
3807
|
return pin;
|
|
3802
3808
|
}
|
|
3809
|
+
async use(spec, o = {}) {
|
|
3810
|
+
const keep = o.prune === false ? null : this.keepCount(o.keep);
|
|
3811
|
+
const was = readPin2(this.root)?.tag ?? null;
|
|
3812
|
+
const rel = await this.fetch(spec, o);
|
|
3813
|
+
const tag = rel.tag;
|
|
3814
|
+
rel.close();
|
|
3815
|
+
const { built } = await this.index(tag);
|
|
3816
|
+
const pin = await this.pin(tag);
|
|
3817
|
+
if (keep === null)
|
|
3818
|
+
return { tag, was, pin, built, pruned: null };
|
|
3819
|
+
try {
|
|
3820
|
+
const hold = was === null || was === tag ? [] : [{ tag: was, why: "the pin before this one" }];
|
|
3821
|
+
return { tag, was, pin, built, pruned: this.prune({ keep, hold }) };
|
|
3822
|
+
} catch (e) {
|
|
3823
|
+
return { tag, was, pin, built, pruned: null, pruneError: e.message };
|
|
3824
|
+
}
|
|
3825
|
+
}
|
|
3826
|
+
keepCount(keep) {
|
|
3827
|
+
return checkKeep(keep ?? readConfig2(this.root).keep ?? DEFAULT_KEEP2);
|
|
3828
|
+
}
|
|
3803
3829
|
unpin() {
|
|
3804
3830
|
return clearPin(this.root);
|
|
3805
3831
|
}
|
|
@@ -3810,6 +3836,8 @@ class ReleaseManager2 {
|
|
|
3810
3836
|
config(patch) {
|
|
3811
3837
|
if (patch.repo !== undefined)
|
|
3812
3838
|
repoOf2({ ...this.opts, repo: patch.repo });
|
|
3839
|
+
if (patch.keep !== undefined)
|
|
3840
|
+
checkKeep(patch.keep);
|
|
3813
3841
|
return writeConfig2(this.root, patch);
|
|
3814
3842
|
}
|
|
3815
3843
|
readConfig() {
|
|
@@ -3887,16 +3915,17 @@ class ReleaseManager2 {
|
|
|
3887
3915
|
return p;
|
|
3888
3916
|
}
|
|
3889
3917
|
prune(o = {}) {
|
|
3890
|
-
const keep = o.keep
|
|
3891
|
-
if (!Number.isInteger(keep) || keep < 0)
|
|
3892
|
-
throw new Error(`prune: --keep ${keep} is not a count`);
|
|
3918
|
+
const keep = this.keepCount(o.keep);
|
|
3893
3919
|
const pin = readPin2(this.root);
|
|
3894
3920
|
const p = this.plan(o.dryRun ?? false);
|
|
3895
3921
|
this.cachedTags().forEach((tag, i) => {
|
|
3922
|
+
const held = (o.hold ?? []).filter((h) => h.tag === tag).map((h) => h.why);
|
|
3896
3923
|
if (tag === pin?.tag)
|
|
3897
3924
|
p.keep.push({ tag, why: "pinned" });
|
|
3898
3925
|
else if (i < keep)
|
|
3899
3926
|
p.keep.push({ tag, why: `among the ${keep} newest` });
|
|
3927
|
+
else if (held.length > 0)
|
|
3928
|
+
p.keep.push({ tag, why: [...new Set(held)].join("; ") });
|
|
3900
3929
|
else
|
|
3901
3930
|
this.planRemove(p, tagDir(this.root, tag), `older than the ${keep} newest`);
|
|
3902
3931
|
});
|
|
@@ -3937,4 +3966,93 @@ function releases2(opts = {}) {
|
|
|
3937
3966
|
return new ReleaseManager2(opts);
|
|
3938
3967
|
}
|
|
3939
3968
|
|
|
3940
|
-
|
|
3969
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
|
|
3970
|
+
import { join as join5 } from "path";
|
|
3971
|
+
var LATEST_CHECK_MS2 = 60 * 60 * 1000;
|
|
3972
|
+
var LATEST_CHECK_BUDGET_MS2 = 1500;
|
|
3973
|
+
var latestCheckPath2 = (root) => join5(root, "LATEST.json");
|
|
3974
|
+
function readLatestCheck2(root) {
|
|
3975
|
+
const p = latestCheckPath2(root);
|
|
3976
|
+
if (!existsSync6(p))
|
|
3977
|
+
return null;
|
|
3978
|
+
try {
|
|
3979
|
+
const v = JSON.parse(readFileSync5(p, "utf8"));
|
|
3980
|
+
if (typeof v.repo !== "string" || typeof v.checkedAt !== "string")
|
|
3981
|
+
return null;
|
|
3982
|
+
return {
|
|
3983
|
+
repo: v.repo,
|
|
3984
|
+
checkedAt: v.checkedAt,
|
|
3985
|
+
latest: typeof v.latest === "string" ? v.latest : null,
|
|
3986
|
+
tags: Array.isArray(v.tags) ? v.tags.filter((t) => typeof t === "string") : []
|
|
3987
|
+
};
|
|
3988
|
+
} catch {
|
|
3989
|
+
return null;
|
|
3990
|
+
}
|
|
3991
|
+
}
|
|
3992
|
+
var KEPT_TAGS = 50;
|
|
3993
|
+
async function latestCheck2(root, repo, remote, now = Date.now, intervalMs = LATEST_CHECK_MS2) {
|
|
3994
|
+
const kept = readLatestCheck2(root);
|
|
3995
|
+
const prior = kept !== null && kept.repo === repo ? kept : null;
|
|
3996
|
+
if (prior !== null) {
|
|
3997
|
+
const age = now() - Date.parse(prior.checkedAt);
|
|
3998
|
+
if (age >= 0 && age < intervalMs)
|
|
3999
|
+
return prior;
|
|
4000
|
+
}
|
|
4001
|
+
const checkedAt = new Date(now()).toISOString();
|
|
4002
|
+
const held = {
|
|
4003
|
+
repo,
|
|
4004
|
+
checkedAt,
|
|
4005
|
+
latest: prior?.latest ?? null,
|
|
4006
|
+
tags: prior?.tags ?? []
|
|
4007
|
+
};
|
|
4008
|
+
keep(root, held);
|
|
4009
|
+
let check;
|
|
4010
|
+
try {
|
|
4011
|
+
const tags = (await remote.tags(repo)).filter((t) => parseArchiveTag2(t) !== null).sort(compareTagsNewestFirst2);
|
|
4012
|
+
check = {
|
|
4013
|
+
repo,
|
|
4014
|
+
checkedAt,
|
|
4015
|
+
latest: pickTag2(tags, { kind: "latest" }),
|
|
4016
|
+
tags: tags.slice(0, KEPT_TAGS)
|
|
4017
|
+
};
|
|
4018
|
+
} catch {
|
|
4019
|
+
return held;
|
|
4020
|
+
}
|
|
4021
|
+
keep(root, check);
|
|
4022
|
+
return check;
|
|
4023
|
+
}
|
|
4024
|
+
function keep(root, check) {
|
|
4025
|
+
try {
|
|
4026
|
+
writeJsonAtomic2(latestCheckPath2(root), check);
|
|
4027
|
+
} catch {}
|
|
4028
|
+
}
|
|
4029
|
+
async function within(p, ms) {
|
|
4030
|
+
let timer;
|
|
4031
|
+
const late = new Promise((resolve) => {
|
|
4032
|
+
timer = setTimeout(() => resolve(null), Math.max(0, ms));
|
|
4033
|
+
timer.unref?.();
|
|
4034
|
+
});
|
|
4035
|
+
try {
|
|
4036
|
+
return await Promise.race([p, late]);
|
|
4037
|
+
} finally {
|
|
4038
|
+
clearTimeout(timer);
|
|
4039
|
+
}
|
|
4040
|
+
}
|
|
4041
|
+
function staleWarning2(answered, latest, floor, tags = [], command = "idq") {
|
|
4042
|
+
const a = parseArchiveTag2(answered);
|
|
4043
|
+
if (a === null)
|
|
4044
|
+
return null;
|
|
4045
|
+
const belowFloor = floor !== null && (a.cut === undefined || a.cut < floor.cut);
|
|
4046
|
+
const why = belowFloor ? ` (${floor.why})` : "";
|
|
4047
|
+
if (latest !== null && compareTagsNewestFirst2(answered, latest) > 0) {
|
|
4048
|
+
const lp = parseArchiveTag2(latest);
|
|
4049
|
+
if (!belowFloor || lp?.cut !== undefined && lp.cut >= (floor?.cut ?? 0))
|
|
4050
|
+
return `answered from ${answered}. ${latest} is available${why}: run \`${command} update\` to get the latest`;
|
|
4051
|
+
}
|
|
4052
|
+
if (!belowFloor)
|
|
4053
|
+
return null;
|
|
4054
|
+
const floorTag = pickTag2(tags, { kind: "cut", cut: floor.cut, fraction: 0 });
|
|
4055
|
+
return `answered from ${answered}. ${floorTag ?? `cut ${floor.cut}`} is required${why}: run \`${command} use ${floor.cut}\` to get it`;
|
|
4056
|
+
}
|
|
4057
|
+
|
|
4058
|
+
export { DATABASES2, planAsset2, sha256File2, unzstdTo2, NAME_LEVELS2, nameLevel2, NO_CALENDAR2, FeisFinder2, ADVANCEMENT_DANCES2, entryRank2, FEIS_LOCAL_ID_FLOOR2, ResultFinder2, MajorsFinder2, LINEAGE_FILE2, linksOfLineage2, releaseSchoolGraph2, readsSpanishNames2, SchoolResolver2, initialsOnly2, nameOrderOf, DancerResolver2, readPopulation2, syllabusSourceOf2, canonicalOf2, CompResolver2, StaffFinder2, TeamFinder2, EventFinder2, defaultBuilders2, indexPath2, indexKey2, isCurrent2, buildIndex2, ensureIndex2, listIndexes2, readPin2, youngest2, dancerResolverOf2, RegistrationResolver2, ghRemote2, httpsRemote2, fallbackRemote2, setRemote2, defaultRemote, columnsOf2, FEATURES2, capabilityOf2, needsRelease2, resolveTag2, Release2, openRelease2, openCached2, DEFAULT_KEEP2, ReleaseManager2, releases2, LATEST_CHECK_MS2, LATEST_CHECK_BUDGET_MS2, latestCheckPath2, readLatestCheck2, latestCheck2, within, staleWarning2 };
|
package/dist/cli.js
CHANGED
|
@@ -1,20 +1,36 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
// @bun
|
|
3
|
+
import {
|
|
4
|
+
cacheRoot2,
|
|
5
|
+
repoOf2
|
|
6
|
+
} from "./chunks/index-hne1wadw.js";
|
|
3
7
|
import {
|
|
4
8
|
nameOrderOf,
|
|
9
|
+
defaultRemote,
|
|
5
10
|
openRelease2,
|
|
6
|
-
releases2
|
|
7
|
-
|
|
8
|
-
|
|
11
|
+
releases2,
|
|
12
|
+
LATEST_CHECK_BUDGET_MS2,
|
|
13
|
+
latestCheck2,
|
|
14
|
+
within,
|
|
15
|
+
staleWarning2
|
|
16
|
+
} from "./chunks/index-zwecpq08.js";
|
|
9
17
|
import"./chunks/index-hghdy980.js";
|
|
10
18
|
|
|
11
19
|
var USAGE = `usage: idq <verb> [--cache <dir>] [--repo <owner/name>] [--json]
|
|
12
20
|
|
|
13
21
|
config repo <owner/name> set the user-local repository the releases come from
|
|
22
|
+
config keep <N> how many of the newest releases a prune keeps (default 2)
|
|
14
23
|
list [--remote] the cached releases (size, files, indexes, the pin), and
|
|
15
24
|
with --remote the published tags
|
|
16
25
|
fetch [--release <spec>] [--assets results,majors,<file>...]
|
|
17
26
|
download, verify and cache a release (default: results)
|
|
27
|
+
update [--assets ...] [--keep N] [--no-prune]
|
|
28
|
+
move to the newest published release: fetch it (verified),
|
|
29
|
+
build its indexes, pin it, then prune the cache to the N
|
|
30
|
+
newest releases (default: config keep, else 2), keeping the
|
|
31
|
+
new pin and the one before it
|
|
32
|
+
use <spec> [--assets ...] [--keep N] [--no-prune]
|
|
33
|
+
the same for the release a spec names
|
|
18
34
|
pin <spec> the user-local pin: what a bare open (spec "pinned") opens
|
|
19
35
|
unpin remove the pin (a bare open is then "latest")
|
|
20
36
|
current the pin, and whether it is cached
|
|
@@ -26,8 +42,8 @@ var USAGE = `usage: idq <verb> [--cache <dir>] [--repo <owner/name>] [--json]
|
|
|
26
42
|
capabilities [--release <spec>] what the release carries of what the client reads, from
|
|
27
43
|
its own schema: each feature full or missing (its calls
|
|
28
44
|
refuse, naming the first cut that publishes it)
|
|
29
|
-
prune [--keep N] [--dry-run] remove all but the N newest cached releases (default
|
|
30
|
-
and the pinned one
|
|
45
|
+
prune [--keep N] [--dry-run] remove all but the N newest cached releases (default:
|
|
46
|
+
config keep, else 2) and the pinned one
|
|
31
47
|
rm <tag> [--dry-run] remove one cached release (not the pinned one)
|
|
32
48
|
drop-indexes [<tag>] [--dry-run] remove the index files (of one release, or of every one)
|
|
33
49
|
feis [--release <spec>] [--platform <p>] [--code <c>] [--id <id>] [--name <glob>] [--year <y>]
|
|
@@ -99,6 +115,10 @@ var USAGE = `usage: idq <verb> [--cache <dir>] [--repo <owner/name>] [--json]
|
|
|
99
115
|
population?, oireachtas?}, a registration: {name, school?,
|
|
100
116
|
codes[], feis, year?, population?, oireachtas?})
|
|
101
117
|
|
|
118
|
+
A query on the default spec prints one warning line on stderr when its release is older than
|
|
119
|
+
the newest published one (the remote asked at most once per hour, <cache>/LATEST.json); the
|
|
120
|
+
answer and the exit code are unchanged, and an offline check says nothing.
|
|
121
|
+
|
|
102
122
|
<spec>: pinned (the default) | latest | a cut (42, 42.1) | a tag (archive-YYYY.MM.DD-N)`;
|
|
103
123
|
var VALUE = new Set([
|
|
104
124
|
"--cache",
|
|
@@ -162,6 +182,9 @@ var mb = (n) => `${(n / 1e6).toFixed(1)} MB`;
|
|
|
162
182
|
function printPlan(p, say) {
|
|
163
183
|
for (const k of p.keep)
|
|
164
184
|
say(`keep ${k.tag} (${k.why})`);
|
|
185
|
+
printRemovals(p, say);
|
|
186
|
+
}
|
|
187
|
+
function printRemovals(p, say) {
|
|
165
188
|
for (const r of p.remove)
|
|
166
189
|
say(`${p.dryRun ? "would remove" : "removed"} ${r.path} ${mb(r.bytes)} (${r.why})`);
|
|
167
190
|
say(`${p.dryRun ? "would free" : "freed"} ${mb(p.bytes)}${p.dryRun ? " (dry run: nothing removed)" : ""}`);
|
|
@@ -405,6 +428,27 @@ function eventYearOf(p) {
|
|
|
405
428
|
...year !== undefined ? { year: Number(year) } : {}
|
|
406
429
|
};
|
|
407
430
|
}
|
|
431
|
+
var ANSWERING = new Set([
|
|
432
|
+
"current",
|
|
433
|
+
"capabilities",
|
|
434
|
+
"feis",
|
|
435
|
+
"series",
|
|
436
|
+
"school",
|
|
437
|
+
"dancer",
|
|
438
|
+
"results",
|
|
439
|
+
"majors",
|
|
440
|
+
"stream",
|
|
441
|
+
"records",
|
|
442
|
+
"feis-dates",
|
|
443
|
+
"tabulated",
|
|
444
|
+
"teams",
|
|
445
|
+
"events",
|
|
446
|
+
"teacher",
|
|
447
|
+
"adjudicator",
|
|
448
|
+
"comp",
|
|
449
|
+
"syllabus",
|
|
450
|
+
"resolve"
|
|
451
|
+
]);
|
|
408
452
|
async function main(argv, io = {}) {
|
|
409
453
|
const say = io.say ?? ((s) => console.log(s));
|
|
410
454
|
const log = io.log ?? ((s) => console.error(s));
|
|
@@ -426,6 +470,23 @@ async function main(argv, io = {}) {
|
|
|
426
470
|
};
|
|
427
471
|
const spec = p.flags.get("--release") ?? "pinned";
|
|
428
472
|
const m = releases2(opts);
|
|
473
|
+
const fresh = io.freshness === false || spec.trim() !== "pinned" || verb === undefined || !ANSWERING.has(verb) ? null : io.freshness ?? {};
|
|
474
|
+
const answered = { tag: null };
|
|
475
|
+
const open = async (s, o) => {
|
|
476
|
+
const rel = await openRelease2(s, o);
|
|
477
|
+
answered.tag = rel.tag;
|
|
478
|
+
return rel;
|
|
479
|
+
};
|
|
480
|
+
const now = fresh?.now ?? Date.now;
|
|
481
|
+
const started = now();
|
|
482
|
+
let check = null;
|
|
483
|
+
if (fresh !== null) {
|
|
484
|
+
try {
|
|
485
|
+
const repo = repoOf2(opts);
|
|
486
|
+
if (repo !== null)
|
|
487
|
+
check = latestCheck2(cacheRoot2(opts), repo, opts.remote ?? defaultRemote(), now).catch(() => null);
|
|
488
|
+
} catch {}
|
|
489
|
+
}
|
|
429
490
|
const out = (v, text) => {
|
|
430
491
|
if (json)
|
|
431
492
|
say(JSON.stringify(v, null, 2));
|
|
@@ -435,12 +496,12 @@ async function main(argv, io = {}) {
|
|
|
435
496
|
try {
|
|
436
497
|
switch (verb) {
|
|
437
498
|
case "config": {
|
|
438
|
-
if (rest[0] !== "repo" || rest[1] === undefined) {
|
|
499
|
+
if (rest[0] !== "repo" && rest[0] !== "keep" || rest[1] === undefined) {
|
|
439
500
|
out(m.readConfig(), () => say(JSON.stringify(m.readConfig())));
|
|
440
501
|
return rest.length === 0 ? 0 : 2;
|
|
441
502
|
}
|
|
442
|
-
const c = m.config({ repo: rest[1] });
|
|
443
|
-
out(c, () => say(`repo ${c.repo}`));
|
|
503
|
+
const c = rest[0] === "repo" ? m.config({ repo: rest[1] }) : m.config({ keep: Number(rest[1]) });
|
|
504
|
+
out(c, () => say(rest[0] === "repo" ? `repo ${c.repo}` : `keep ${c.keep}`));
|
|
444
505
|
return 0;
|
|
445
506
|
}
|
|
446
507
|
case "list": {
|
|
@@ -477,6 +538,31 @@ async function main(argv, io = {}) {
|
|
|
477
538
|
out(pin, () => say(`pinned ${pin.tag} (from ${pin.source})`));
|
|
478
539
|
return 0;
|
|
479
540
|
}
|
|
541
|
+
case "update":
|
|
542
|
+
case "use": {
|
|
543
|
+
const target = verb === "update" ? "latest" : rest[0];
|
|
544
|
+
if (target === undefined) {
|
|
545
|
+
log("use <spec>");
|
|
546
|
+
return 2;
|
|
547
|
+
}
|
|
548
|
+
const assets = (p.flags.get("--assets") ?? "results").split(",").filter((a) => a !== "");
|
|
549
|
+
const keep = p.flags.has("--keep") ? Number(p.flags.get("--keep")) : undefined;
|
|
550
|
+
const u = await m.use(target, {
|
|
551
|
+
assets,
|
|
552
|
+
prune: !p.bools.has("--no-prune"),
|
|
553
|
+
...keep !== undefined ? { keep } : {}
|
|
554
|
+
});
|
|
555
|
+
out(u, () => {
|
|
556
|
+
for (const b of u.built)
|
|
557
|
+
say(`${u.tag} ${b.name} ${b.path}`);
|
|
558
|
+
say(u.was === u.tag ? `pinned ${u.tag} (already pinned; its indexes are built)` : `pinned ${u.tag}${u.was === null ? "" : ` (was ${u.was})`}`);
|
|
559
|
+
if (u.pruned !== null && u.pruned.remove.length > 0)
|
|
560
|
+
printRemovals(u.pruned, say);
|
|
561
|
+
});
|
|
562
|
+
if (u.pruneError !== undefined)
|
|
563
|
+
log(`prune after ${verb} failed: ${u.pruneError}`);
|
|
564
|
+
return 0;
|
|
565
|
+
}
|
|
480
566
|
case "unpin": {
|
|
481
567
|
const was = m.unpin();
|
|
482
568
|
out({ unpinned: was }, () => say(was ? "unpinned" : "no pin was set"));
|
|
@@ -484,6 +570,7 @@ async function main(argv, io = {}) {
|
|
|
484
570
|
}
|
|
485
571
|
case "current": {
|
|
486
572
|
const c = m.current();
|
|
573
|
+
answered.tag = c.pin?.tag ?? null;
|
|
487
574
|
out(c, () => say(c.pin === null ? "no pin: a bare open is latest" : `pinned ${c.pin.tag}${c.cached ? "" : " (not cached)"}`));
|
|
488
575
|
return 0;
|
|
489
576
|
}
|
|
@@ -506,7 +593,7 @@ async function main(argv, io = {}) {
|
|
|
506
593
|
return 0;
|
|
507
594
|
}
|
|
508
595
|
case "capabilities": {
|
|
509
|
-
const rel = await
|
|
596
|
+
const rel = await open(spec, { ...opts, assets: [] });
|
|
510
597
|
try {
|
|
511
598
|
const c = await rel.capabilities();
|
|
512
599
|
out(c, () => {
|
|
@@ -550,7 +637,7 @@ async function main(argv, io = {}) {
|
|
|
550
637
|
return 0;
|
|
551
638
|
}
|
|
552
639
|
case "feis": {
|
|
553
|
-
const rel = await
|
|
640
|
+
const rel = await open(spec, { ...opts, assets: [] });
|
|
554
641
|
const year = p.flags.get("--year");
|
|
555
642
|
const a = await rel.feises.find({
|
|
556
643
|
...p.flags.has("--platform") ? { platform: p.flags.get("--platform") } : {},
|
|
@@ -574,7 +661,7 @@ async function main(argv, io = {}) {
|
|
|
574
661
|
log("series <name|host code|feis id>");
|
|
575
662
|
return 2;
|
|
576
663
|
}
|
|
577
|
-
const rel = await
|
|
664
|
+
const rel = await open(spec, { ...opts, assets: [] });
|
|
578
665
|
const a = await rel.feises.series(rest.join(" "));
|
|
579
666
|
out(a, () => {
|
|
580
667
|
say(`${a.arm} "${a.asked}" via ${a.via ?? "-"} in ${a.tag}${a.redirected !== undefined ? ` (${a.redirected.from} -> ${a.redirected.to})` : ""}${a.why !== undefined ? ` (${a.why})` : ""}`);
|
|
@@ -594,7 +681,7 @@ async function main(argv, io = {}) {
|
|
|
594
681
|
log("school <print|school_id> [--region <code>]");
|
|
595
682
|
return 2;
|
|
596
683
|
}
|
|
597
|
-
const rel = await
|
|
684
|
+
const rel = await open(spec, { ...opts, assets: [] });
|
|
598
685
|
const region = p.flags.get("--region");
|
|
599
686
|
const a = await rel.schools.resolve(rest.join(" "), region !== undefined ? { region } : {});
|
|
600
687
|
out(a, () => {
|
|
@@ -606,7 +693,7 @@ async function main(argv, io = {}) {
|
|
|
606
693
|
return a.arm === "none" ? 1 : 0;
|
|
607
694
|
}
|
|
608
695
|
case "dancer": {
|
|
609
|
-
const rel = await
|
|
696
|
+
const rel = await open(spec, { ...opts, assets: [] });
|
|
610
697
|
try {
|
|
611
698
|
const id = p.flags.get("--id");
|
|
612
699
|
if (id !== undefined) {
|
|
@@ -665,7 +752,7 @@ async function main(argv, io = {}) {
|
|
|
665
752
|
}
|
|
666
753
|
}
|
|
667
754
|
case "results": {
|
|
668
|
-
const rel = await
|
|
755
|
+
const rel = await open(spec, { ...opts, assets: [] });
|
|
669
756
|
try {
|
|
670
757
|
const q = {
|
|
671
758
|
...p.flags.has("--dancer") ? { dancer_id: Number(p.flags.get("--dancer")) } : {},
|
|
@@ -683,7 +770,7 @@ async function main(argv, io = {}) {
|
|
|
683
770
|
}
|
|
684
771
|
}
|
|
685
772
|
case "majors": {
|
|
686
|
-
const rel = await
|
|
773
|
+
const rel = await open(spec, { ...opts, assets: [] });
|
|
687
774
|
try {
|
|
688
775
|
const event = p.flags.get("--event");
|
|
689
776
|
const year = p.flags.get("--year");
|
|
@@ -725,7 +812,7 @@ async function main(argv, io = {}) {
|
|
|
725
812
|
}
|
|
726
813
|
case "stream": {
|
|
727
814
|
const q = streamQueryOf(p);
|
|
728
|
-
const rel = await
|
|
815
|
+
const rel = await open(spec, { ...opts, assets: [] });
|
|
729
816
|
try {
|
|
730
817
|
let n = 0;
|
|
731
818
|
const rows = p.bools.has("--entries") ? rel.results.entries(q) : rel.results.stream(q);
|
|
@@ -740,7 +827,7 @@ async function main(argv, io = {}) {
|
|
|
740
827
|
}
|
|
741
828
|
}
|
|
742
829
|
case "records": {
|
|
743
|
-
const rel = await
|
|
830
|
+
const rel = await open(spec, { ...opts, assets: [] });
|
|
744
831
|
try {
|
|
745
832
|
const ids = idsOf(p.flags.get("--dancer"));
|
|
746
833
|
if (p.bools.has("--years")) {
|
|
@@ -768,7 +855,7 @@ async function main(argv, io = {}) {
|
|
|
768
855
|
}
|
|
769
856
|
}
|
|
770
857
|
case "feis-dates": {
|
|
771
|
-
const rel = await
|
|
858
|
+
const rel = await open(spec, { ...opts, assets: [] });
|
|
772
859
|
try {
|
|
773
860
|
const a = await rel.feises.dates();
|
|
774
861
|
out(a, () => {
|
|
@@ -782,7 +869,7 @@ async function main(argv, io = {}) {
|
|
|
782
869
|
}
|
|
783
870
|
}
|
|
784
871
|
case "tabulated": {
|
|
785
|
-
const rel = await
|
|
872
|
+
const rel = await open(spec, { ...opts, assets: [] });
|
|
786
873
|
try {
|
|
787
874
|
const min = p.flags.get("--min-round");
|
|
788
875
|
const a = await rel.majors.tabulated(min !== undefined ? { min_true_round: Number(min) } : {});
|
|
@@ -797,7 +884,7 @@ async function main(argv, io = {}) {
|
|
|
797
884
|
}
|
|
798
885
|
}
|
|
799
886
|
case "teams": {
|
|
800
|
-
const rel = await
|
|
887
|
+
const rel = await open(spec, { ...opts, assets: [] });
|
|
801
888
|
try {
|
|
802
889
|
const q = eventYearOf(p);
|
|
803
890
|
if (p.bools.has("--rosters")) {
|
|
@@ -833,7 +920,7 @@ async function main(argv, io = {}) {
|
|
|
833
920
|
}
|
|
834
921
|
}
|
|
835
922
|
case "events": {
|
|
836
|
-
const rel = await
|
|
923
|
+
const rel = await open(spec, { ...opts, assets: [] });
|
|
837
924
|
try {
|
|
838
925
|
const a = await rel.events.years(eventYearOf(p));
|
|
839
926
|
out(a, () => {
|
|
@@ -852,7 +939,7 @@ async function main(argv, io = {}) {
|
|
|
852
939
|
log(`${verb} <name|TD number>`);
|
|
853
940
|
return 2;
|
|
854
941
|
}
|
|
855
|
-
const rel = await
|
|
942
|
+
const rel = await open(spec, { ...opts, assets: [] });
|
|
856
943
|
try {
|
|
857
944
|
const a = verb === "teacher" ? await rel.staff.teacher(rest.join(" ")) : await rel.staff.adjudicator(rest.join(" "));
|
|
858
945
|
out(a, () => printStaff(a, say));
|
|
@@ -866,7 +953,7 @@ async function main(argv, io = {}) {
|
|
|
866
953
|
log("comp <feis> <code> [--year <y>] [--title <t>]");
|
|
867
954
|
return 2;
|
|
868
955
|
}
|
|
869
|
-
const rel = await
|
|
956
|
+
const rel = await open(spec, { ...opts, assets: [] });
|
|
870
957
|
const year = p.flags.get("--year");
|
|
871
958
|
const a = await rel.comps.resolve({
|
|
872
959
|
feis: rest[0],
|
|
@@ -882,7 +969,7 @@ async function main(argv, io = {}) {
|
|
|
882
969
|
const of = p.flags.get("--for");
|
|
883
970
|
if (of !== undefined) {
|
|
884
971
|
const y = rest[0] ?? p.flags.get("--year");
|
|
885
|
-
const rel = await
|
|
972
|
+
const rel = await open(spec, { ...opts, assets: [] });
|
|
886
973
|
const a = await rel.comps.syllabusFor(of, y !== undefined ? Number(y) : undefined);
|
|
887
974
|
out(a, () => {
|
|
888
975
|
say(`${a.arm} ${a.asked.series} asked ${a.asked.year ?? "-"} read ${a.year ?? "-"} (${a.held ? "held" : "not held"}) from ${a.source ?? "-"} series ${a.series_id ?? "-"} (years ${a.years.join(", ") || "-"}) in ${a.tag}`);
|
|
@@ -901,7 +988,7 @@ async function main(argv, io = {}) {
|
|
|
901
988
|
log("syllabus <feis> [year]");
|
|
902
989
|
return 2;
|
|
903
990
|
}
|
|
904
|
-
const rel = await
|
|
991
|
+
const rel = await open(spec, { ...opts, assets: [] });
|
|
905
992
|
const a = await rel.comps.syllabus(rest[0], rest[1] !== undefined ? Number(rest[1]) : undefined);
|
|
906
993
|
out(a, () => {
|
|
907
994
|
say(`${a.feis.asked} ${a.year ?? "?"} ${a.held ? `held: ${a.feis.feis_ids.join(", ")}` : "not held"} series ${a.feis.brand_id ?? "-"} (years ${a.years.join(", ") || "-"}) in ${a.tag}`);
|
|
@@ -913,7 +1000,7 @@ async function main(argv, io = {}) {
|
|
|
913
1000
|
return a.held ? 0 : 1;
|
|
914
1001
|
}
|
|
915
1002
|
case "resolve": {
|
|
916
|
-
const rel = await
|
|
1003
|
+
const rel = await open(spec, { ...opts, assets: [] });
|
|
917
1004
|
try {
|
|
918
1005
|
return await resolveVerb(rel, rest[0], rest.slice(1), p, say, io.stdin ?? (() => Bun.stdin.text()));
|
|
919
1006
|
} finally {
|
|
@@ -927,6 +1014,14 @@ async function main(argv, io = {}) {
|
|
|
927
1014
|
} catch (e) {
|
|
928
1015
|
log(`idq ${verb}: ${e.message}`);
|
|
929
1016
|
return 1;
|
|
1017
|
+
} finally {
|
|
1018
|
+
if (fresh !== null && answered.tag !== null) {
|
|
1019
|
+
const budget = fresh.budgetMs ?? LATEST_CHECK_BUDGET_MS2;
|
|
1020
|
+
const c = check === null ? null : await within(check, budget - (now() - started));
|
|
1021
|
+
const w = staleWarning2(answered.tag, c?.latest ?? null, fresh.floor ?? null, c?.tags ?? [], fresh.command);
|
|
1022
|
+
if (w !== null)
|
|
1023
|
+
log(w);
|
|
1024
|
+
}
|
|
930
1025
|
}
|
|
931
1026
|
}
|
|
932
1027
|
if (import.meta.main)
|
package/dist/index.js
CHANGED
|
@@ -50,9 +50,16 @@ import {
|
|
|
50
50
|
Release2,
|
|
51
51
|
openRelease2,
|
|
52
52
|
openCached2,
|
|
53
|
+
DEFAULT_KEEP2,
|
|
53
54
|
ReleaseManager2,
|
|
54
|
-
releases2
|
|
55
|
-
|
|
55
|
+
releases2,
|
|
56
|
+
LATEST_CHECK_MS2,
|
|
57
|
+
LATEST_CHECK_BUDGET_MS2,
|
|
58
|
+
latestCheckPath2,
|
|
59
|
+
readLatestCheck2,
|
|
60
|
+
latestCheck2,
|
|
61
|
+
staleWarning2
|
|
62
|
+
} from "./chunks/index-zwecpq08.js";
|
|
56
63
|
import {
|
|
57
64
|
cacheRoot2,
|
|
58
65
|
repoOf2
|
|
@@ -71,11 +78,14 @@ export {
|
|
|
71
78
|
ARCHIVE_TAG2 as ARCHIVE_TAG,
|
|
72
79
|
CompResolver2 as CompResolver,
|
|
73
80
|
DATABASES2 as DATABASES,
|
|
81
|
+
DEFAULT_KEEP2 as DEFAULT_KEEP,
|
|
74
82
|
DancerResolver2 as DancerResolver,
|
|
75
83
|
EventFinder2 as EventFinder,
|
|
76
84
|
FEATURES2 as FEATURES,
|
|
77
85
|
FEIS_LOCAL_ID_FLOOR2 as FEIS_LOCAL_ID_FLOOR,
|
|
78
86
|
FeisFinder2 as FeisFinder,
|
|
87
|
+
LATEST_CHECK_BUDGET_MS2 as LATEST_CHECK_BUDGET_MS,
|
|
88
|
+
LATEST_CHECK_MS2 as LATEST_CHECK_MS,
|
|
79
89
|
LINEAGE_FILE2 as LINEAGE_FILE,
|
|
80
90
|
MajorsFinder2 as MajorsFinder,
|
|
81
91
|
NAME_LEVELS2 as NAME_LEVELS,
|
|
@@ -105,6 +115,8 @@ export {
|
|
|
105
115
|
indexPath2 as indexPath,
|
|
106
116
|
initialsOnly2 as initialsOnly,
|
|
107
117
|
isCurrent2 as isCurrent,
|
|
118
|
+
latestCheck2 as latestCheck,
|
|
119
|
+
latestCheckPath2 as latestCheckPath,
|
|
108
120
|
linksOfLineage2 as linksOfLineage,
|
|
109
121
|
listIndexes2 as listIndexes,
|
|
110
122
|
nameLevel2 as nameLevel,
|
|
@@ -115,6 +127,7 @@ export {
|
|
|
115
127
|
parseReleaseSpec2 as parseReleaseSpec,
|
|
116
128
|
pickTag2 as pickTag,
|
|
117
129
|
planAsset2 as planAsset,
|
|
130
|
+
readLatestCheck2 as readLatestCheck,
|
|
118
131
|
readPin2 as readPin,
|
|
119
132
|
readPopulation2 as readPopulation,
|
|
120
133
|
readsSpanishNames2 as readsSpanishNames,
|
|
@@ -125,6 +138,7 @@ export {
|
|
|
125
138
|
resolveTag2 as resolveTag,
|
|
126
139
|
setRemote2 as setRemote,
|
|
127
140
|
sha256File2 as sha256File,
|
|
141
|
+
staleWarning2 as staleWarning,
|
|
128
142
|
syllabusSourceOf2 as syllabusSourceOf,
|
|
129
143
|
unzstdTo2 as unzstdTo,
|
|
130
144
|
youngest2 as youngest
|
package/dist/types/cli.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
+
import { type FreshnessFloor } from "./freshness.js";
|
|
2
3
|
import type { OpenOptions } from "./release.js";
|
|
3
4
|
import type { StreamQuery } from "./stream.js";
|
|
4
|
-
export declare const USAGE = "usage: idq <verb> [--cache <dir>] [--repo <owner/name>] [--json]\n\n config repo <owner/name> set the user-local repository the releases come from\n list [--remote] the cached releases (size, files, indexes, the pin), and\n with --remote the published tags\n fetch [--release <spec>] [--assets results,majors,<file>...]\n download, verify and cache a release (default: results)\n pin <spec> the user-local pin: what a bare open (spec \"pinned\") opens\n unpin remove the pin (a bare open is then \"latest\")\n current the pin, and whether it is cached\n add <dir> --tag <tag> add a release built locally (its MANIFEST.json and files)\n to the cache under its tag\n index [--release <spec>] [<name>...] [--force]\n build the per-tag indexes (default: every one)\n verify [--release <spec>] re-hash every cached file against its receipt\n capabilities [--release <spec>] what the release carries of what the client reads, from\n its own schema: each feature full or missing (its calls\n refuse, naming the first cut that publishes it)\n prune [--keep N] [--dry-run] remove all but the N newest cached releases (default
|
|
5
|
+
export declare const USAGE = "usage: idq <verb> [--cache <dir>] [--repo <owner/name>] [--json]\n\n config repo <owner/name> set the user-local repository the releases come from\n config keep <N> how many of the newest releases a prune keeps (default 2)\n list [--remote] the cached releases (size, files, indexes, the pin), and\n with --remote the published tags\n fetch [--release <spec>] [--assets results,majors,<file>...]\n download, verify and cache a release (default: results)\n update [--assets ...] [--keep N] [--no-prune]\n move to the newest published release: fetch it (verified),\n build its indexes, pin it, then prune the cache to the N\n newest releases (default: config keep, else 2), keeping the\n new pin and the one before it\n use <spec> [--assets ...] [--keep N] [--no-prune]\n the same for the release a spec names\n pin <spec> the user-local pin: what a bare open (spec \"pinned\") opens\n unpin remove the pin (a bare open is then \"latest\")\n current the pin, and whether it is cached\n add <dir> --tag <tag> add a release built locally (its MANIFEST.json and files)\n to the cache under its tag\n index [--release <spec>] [<name>...] [--force]\n build the per-tag indexes (default: every one)\n verify [--release <spec>] re-hash every cached file against its receipt\n capabilities [--release <spec>] what the release carries of what the client reads, from\n its own schema: each feature full or missing (its calls\n refuse, naming the first cut that publishes it)\n prune [--keep N] [--dry-run] remove all but the N newest cached releases (default:\n config keep, else 2) and the pinned one\n rm <tag> [--dry-run] remove one cached release (not the pinned one)\n drop-indexes [<tag>] [--dry-run] remove the index files (of one release, or of every one)\n feis [--release <spec>] [--platform <p>] [--code <c>] [--id <id>] [--name <glob>] [--year <y>]\n the feises a query names\n series [--release <spec>] <name|host code|feis id>\n the series a name, a host code (fweb:ABC) or any feis id\n names: its years, hosts, codes and next instance\n school [--release <spec>] <print|school_id> [--region <code>]\n a school print or id to the published schools\n dancer [--release <spec>] <name> | --surname <s> | --like <glob> | --id <dancer_id>\n [--order surname-first|given-first] [--school <print|id>]\n [--yob <y> | --band <U10> [--year <y>] | --comp <competition>] [--as-of <date>]\n [--population girls|boys] [--oireachtas <region>[:<year>]] [--rows]\n the dancers a name reaches, each with its evidence\n (name level, age, school, population, region) and the arm;\n --oireachtas is the regional the roster is for, its region in\n the release's spelling (SRO, MAO), its year the --as-of year\n where none is given; the results of\n the one dancer where one answers (or --rows for each)\n results [--release <spec>] [--dancer <id>] [--feis <id>] [--comp <competition>]\n [--school <print|id>] [--year <y>] [--limit <n>]\n the published result rows, prints and ids\n majors [--release <spec>] [--event <e>] [--year <y>] [--comp <competition>] [--dancer <id>]\n the majors: every event and its years; an event's\n competitions; a competition's, an event's or a dancer's rows\n stream [--release <spec>] --scope majors|transcribed|platform|all [--entries] [--event <e,...>]\n [--year <y> | --year <from>..<to>] [--tier <t,...>] [--category <c,...>]\n [--level <feis_level,...>] [--dancer <id,...>] [--school <school_id>] [--primary]\n every row a scope holds, one JSON line each, never the\n whole table in memory; --entries one per address (the row\n it stands for, with its recall order)\n records [--release <spec>] [--dancer <id,...>]\n every dancer's record: display, appearances, birth window,\n populations\n records --years [--release <spec>] [--dancer <id,...>] [--year <y>]\n each dancer's region, nationals and place per year\n feis-dates [--release <spec>] every feis's first and last day\n tabulated [--release <spec>] [--min-round <n>]\n the majors addresses the marks sheets tabulate to round n\n or later (default 3)\n teams [--release <spec>] [--event <e>] [--year <y>] [--rosters | --joins]\n the team result rows of an event-year; with --rosters its\n roster teams and their dancers; with --joins how each roster\n team meets the results\n events [--release <spec>] [--event <e>] [--year <y>]\n each event-year's dates and place\n teacher [--release <spec>] <name|TD number>\n the staff lists' people: where they taught, the register,\n where they sat\n adjudicator [--release <spec>] <name|TD number>\n the register's and the panels' people: the same answer\n comp [--release <spec>] <feis> <code> [--year <y>] [--title <t>]\n a feis's own comp code to the comp it names, through that\n feis's syllabus (the prior year's only where the platform\n numbers every feis alike; elsewhere the title)\n syllabus [--release <spec>] <feis> [year]\n one feis-year's comps, in printed order\n syllabus [--release <spec>] --for <series> [year]\n a series' syllabus for a year: that year's where held,\n else the newest, saying which and what it is read from\n resolve comp [<feis> <code>] [--year <y>] [--title <t>]\n resolve dancer [<name>] [--school <s>] [--comp <feis>/<code> | --yob <y> | --band <b> --year <y>]\n [--order <o>] [--as-of <date>] [--population girls|boys]\n [--oireachtas <region>[:<year>]]\n resolve registrations\n JSONL out, one answer per line; with no positional the\n questions are JSONL on stdin, one per line (a comp:\n {feis, code, year?, title?}, a dancer: {name, school?, age?,\n population?, oireachtas?}, a registration: {name, school?,\n codes[], feis, year?, population?, oireachtas?})\n\n A query on the default spec prints one warning line on stderr when its release is older than\n the newest published one (the remote asked at most once per hour, <cache>/LATEST.json); the\n answer and the exit code are unchanged, and an offline check says nothing.\n\n <spec>: pinned (the default) | latest | a cut (42, 42.1) | a tag (archive-YYYY.MM.DD-N)";
|
|
5
6
|
export interface Parsed {
|
|
6
7
|
positional: string[];
|
|
7
8
|
flags: Map<string, string>;
|
|
@@ -17,10 +18,17 @@ type Line<T> = {
|
|
|
17
18
|
};
|
|
18
19
|
export declare function jsonLines<T>(text: string, check: (v: T) => string | null): Line<T>[];
|
|
19
20
|
export declare function streamQueryOf(p: Parsed): StreamQuery;
|
|
21
|
+
export interface FreshnessIO {
|
|
22
|
+
floor?: FreshnessFloor | null;
|
|
23
|
+
command?: string;
|
|
24
|
+
now?: () => number;
|
|
25
|
+
budgetMs?: number;
|
|
26
|
+
}
|
|
20
27
|
export declare function main(argv: readonly string[], io?: {
|
|
21
28
|
say?: (s: string) => void;
|
|
22
29
|
log?: (s: string) => void;
|
|
23
30
|
opts?: OpenOptions;
|
|
24
31
|
stdin?: () => Promise<string>;
|
|
32
|
+
freshness?: FreshnessIO | false;
|
|
25
33
|
}): Promise<number>;
|
|
26
34
|
export {};
|
package/dist/types/config.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export declare function expandHome(p: string): string;
|
|
|
7
7
|
export declare function cacheRoot(opts?: ClientOptions): string;
|
|
8
8
|
export interface UserConfig {
|
|
9
9
|
repo?: string;
|
|
10
|
+
keep?: number;
|
|
10
11
|
}
|
|
11
12
|
export declare const configPath: (root: string) => string;
|
|
12
13
|
export declare function readConfig(root: string): UserConfig;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Remote } from "./remote.js";
|
|
2
|
+
export declare const LATEST_CHECK_MS: number;
|
|
3
|
+
export declare const LATEST_CHECK_BUDGET_MS = 1500;
|
|
4
|
+
export interface LatestCheck {
|
|
5
|
+
repo: string;
|
|
6
|
+
checkedAt: string;
|
|
7
|
+
latest: string | null;
|
|
8
|
+
tags: string[];
|
|
9
|
+
}
|
|
10
|
+
export interface FreshnessFloor {
|
|
11
|
+
cut: number;
|
|
12
|
+
why: string;
|
|
13
|
+
}
|
|
14
|
+
export declare const latestCheckPath: (root: string) => string;
|
|
15
|
+
export declare function readLatestCheck(root: string): LatestCheck | null;
|
|
16
|
+
export declare function latestCheck(root: string, repo: string, remote: Remote, now?: () => number, intervalMs?: number): Promise<LatestCheck>;
|
|
17
|
+
export declare function within<T>(p: Promise<T>, ms: number): Promise<T | null>;
|
|
18
|
+
export declare function staleWarning(answered: string, latest: string | null, floor: FreshnessFloor | null, tags?: readonly string[], command?: string): string | null;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -7,8 +7,9 @@ export { type Comp, type CompAnswer, type CompDance, type CompKind, type CompQue
|
|
|
7
7
|
export { type ClientOptions, cacheRoot, repoOf } from "./config.js";
|
|
8
8
|
export { type Feis, type FeisAnswer, FeisFinder, type FeisQuery, NO_CALENDAR, type Series, type SeriesAnswer, type SeriesInstance, type SeriesNext, } from "./feises.js";
|
|
9
9
|
export { type IndexBuilder, type IndexFile, buildIndex, defaultBuilders, ensureIndex, indexKey, indexPath, isCurrent, listIndexes, } from "./indexes.js";
|
|
10
|
-
export { type CachedRelease, type ReleaseList, ReleaseManager, type RemovalPlan, releases, } from "./manager.js";
|
|
10
|
+
export { type CachedRelease, DEFAULT_KEEP, type ReleaseList, ReleaseManager, type RemovalPlan, type UseResult, releases, } from "./manager.js";
|
|
11
11
|
export { type Registered, type Sat, type StaffAnswer, StaffFinder, type StaffPerson, type Taught, type TdLink, } from "./people.js";
|
|
12
|
+
export { type FreshnessFloor, LATEST_CHECK_BUDGET_MS, LATEST_CHECK_MS, type LatestCheck, latestCheck, latestCheckPath, readLatestCheck, staleWarning, } from "./freshness.js";
|
|
12
13
|
export { type Pin, readPin } from "./pin.js";
|
|
13
14
|
export { type Remote, type RemoteAsset, fallbackRemote, ghRemote, httpsRemote, setRemote, } from "./remote.js";
|
|
14
15
|
export { type OpenOptions, Release, openCached, openRelease, resolveTag, } from "./release.js";
|
package/dist/types/manager.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type LandedFile } from "./assets.js";
|
|
2
|
+
import { type UserConfig } from "./config.js";
|
|
2
3
|
import { type IndexFile } from "./indexes.js";
|
|
3
4
|
import { type Pin } from "./pin.js";
|
|
4
5
|
import { type OpenOptions, type Release, resolveTag } from "./release.js";
|
|
@@ -35,6 +36,18 @@ export interface RemovalPlan {
|
|
|
35
36
|
}[];
|
|
36
37
|
bytes: number;
|
|
37
38
|
}
|
|
39
|
+
export declare const DEFAULT_KEEP = 2;
|
|
40
|
+
export interface UseResult {
|
|
41
|
+
tag: string;
|
|
42
|
+
was: string | null;
|
|
43
|
+
pin: Pin;
|
|
44
|
+
built: {
|
|
45
|
+
name: string;
|
|
46
|
+
path: string;
|
|
47
|
+
}[];
|
|
48
|
+
pruned: RemovalPlan | null;
|
|
49
|
+
pruneError?: string;
|
|
50
|
+
}
|
|
38
51
|
export declare class ReleaseManager {
|
|
39
52
|
private readonly opts;
|
|
40
53
|
readonly root: string;
|
|
@@ -48,19 +61,19 @@ export declare class ReleaseManager {
|
|
|
48
61
|
assets?: readonly string[];
|
|
49
62
|
}): Promise<Release>;
|
|
50
63
|
pin(spec: string | number): Promise<Pin>;
|
|
64
|
+
use(spec: string | number, o?: {
|
|
65
|
+
assets?: readonly string[];
|
|
66
|
+
prune?: boolean;
|
|
67
|
+
keep?: number;
|
|
68
|
+
}): Promise<UseResult>;
|
|
69
|
+
keepCount(keep?: number): number;
|
|
51
70
|
unpin(): boolean;
|
|
52
71
|
current(): {
|
|
53
72
|
pin: Pin | null;
|
|
54
73
|
cached: boolean;
|
|
55
74
|
};
|
|
56
|
-
config(patch:
|
|
57
|
-
|
|
58
|
-
}): {
|
|
59
|
-
repo?: string;
|
|
60
|
-
};
|
|
61
|
-
readConfig(): {
|
|
62
|
-
repo?: string;
|
|
63
|
-
};
|
|
75
|
+
config(patch: UserConfig): UserConfig;
|
|
76
|
+
readConfig(): UserConfig;
|
|
64
77
|
add(dir: string, tag: string): Promise<Release>;
|
|
65
78
|
index(spec: string | number, names?: readonly string[], o?: {
|
|
66
79
|
force?: boolean;
|
|
@@ -77,6 +90,10 @@ export declare class ReleaseManager {
|
|
|
77
90
|
prune(o?: {
|
|
78
91
|
keep?: number;
|
|
79
92
|
dryRun?: boolean;
|
|
93
|
+
hold?: readonly {
|
|
94
|
+
tag: string;
|
|
95
|
+
why: string;
|
|
96
|
+
}[];
|
|
80
97
|
}): RemovalPlan;
|
|
81
98
|
remove(tag: string, o?: {
|
|
82
99
|
dryRun?: boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@finbheara/archive-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.0",
|
|
4
4
|
"description": "Open a versioned release of an Irish dance results archive: download, verify, decompress and cache it by tag, keep the cache, and resolve feises and schools against it",
|
|
5
5
|
"keywords": ["irish-dance", "results", "release", "cache", "sqlite"],
|
|
6
6
|
"license": "MIT",
|