@finbheara/archive-client 0.20.1 → 0.21.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 +20 -0
- package/dist/chunks/{index-6028k52f.js → index-pfsxe80h.js} +99 -1
- package/dist/cli.js +99 -21
- package/dist/index.js +14 -2
- package/dist/types/cli.d.ts +9 -1
- package/dist/types/freshness.d.ts +18 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/manager.d.ts +11 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -709,6 +709,8 @@ is passed to the dancer resolver as asked (`rel.dancers`, above).
|
|
|
709
709
|
idq config repo <owner/name>
|
|
710
710
|
idq list [--remote]
|
|
711
711
|
idq fetch [--release <spec>] [--assets results,majors,<file>...]
|
|
712
|
+
idq update [--assets results,majors,<file>...]
|
|
713
|
+
idq use <spec> [--assets results,majors,<file>...]
|
|
712
714
|
idq pin <spec> | unpin | current
|
|
713
715
|
idq add <dir> --tag <tag>
|
|
714
716
|
idq index [--release <spec>] [<name>...] [--force]
|
|
@@ -748,6 +750,24 @@ is passed to the dancer resolver as asked (`rel.dancers`, above).
|
|
|
748
750
|
`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
751
|
`--rows`). `--json` prints the call's answer as it is. `--cache` and `--repo` override the defaults.
|
|
750
752
|
|
|
753
|
+
`update` moves to the newest published release, as a package manager's `update` moves to the newest
|
|
754
|
+
version: it fetches it (verified as every download is), builds every index it knows, and only then
|
|
755
|
+
pins it, so a fetch or index that fails leaves the old pin in force. `use <spec>` does the same for
|
|
756
|
+
the release a spec names, older or newer (`releases().use(spec)` in the API; `update` is
|
|
757
|
+
`use("latest")`).
|
|
758
|
+
|
|
759
|
+
A query on the default spec (`pinned`, given or not) whose release is older than the newest
|
|
760
|
+
published one prints ONE line on stderr, and changes neither the answer nor the exit code:
|
|
761
|
+
|
|
762
|
+
answered from <tag>. <newer tag> is available: run `idq update` to get the latest
|
|
763
|
+
|
|
764
|
+
The remote is asked at most once per hour: what it said and when are kept in `<cache>/LATEST.json`,
|
|
765
|
+
and a call inside the hour reads that file. An offline, failed or slow check says nothing, and a
|
|
766
|
+
call waits for one at most 1.5 s, while its own query runs. A release named with `--release` (a
|
|
767
|
+
tag, a cut, `latest`) is never warned about: it was asked for. `main(argv, { freshness })` takes
|
|
768
|
+
`false` to turn the check off, or a `floor` (a cut the caller requires, and why: where only the
|
|
769
|
+
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.
|
|
770
|
+
|
|
751
771
|
`resolve` speaks JSON lines: one answer per line out, and with no positional question the
|
|
752
772
|
questions are JSON lines on stdin, one per line (`{feis, code, year?, title?}`,
|
|
753
773
|
`{name, school?, age?, population?, oireachtas?}`,
|
|
@@ -3800,6 +3800,15 @@ class ReleaseManager2 {
|
|
|
3800
3800
|
writePin(this.root, pin);
|
|
3801
3801
|
return pin;
|
|
3802
3802
|
}
|
|
3803
|
+
async use(spec, o = {}) {
|
|
3804
|
+
const was = readPin2(this.root)?.tag ?? null;
|
|
3805
|
+
const rel = await this.fetch(spec, o);
|
|
3806
|
+
const tag = rel.tag;
|
|
3807
|
+
rel.close();
|
|
3808
|
+
const { built } = await this.index(tag);
|
|
3809
|
+
const pin = await this.pin(tag);
|
|
3810
|
+
return { tag, was, pin, built };
|
|
3811
|
+
}
|
|
3803
3812
|
unpin() {
|
|
3804
3813
|
return clearPin(this.root);
|
|
3805
3814
|
}
|
|
@@ -3937,4 +3946,93 @@ function releases2(opts = {}) {
|
|
|
3937
3946
|
return new ReleaseManager2(opts);
|
|
3938
3947
|
}
|
|
3939
3948
|
|
|
3940
|
-
|
|
3949
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
|
|
3950
|
+
import { join as join5 } from "path";
|
|
3951
|
+
var LATEST_CHECK_MS2 = 60 * 60 * 1000;
|
|
3952
|
+
var LATEST_CHECK_BUDGET_MS2 = 1500;
|
|
3953
|
+
var latestCheckPath2 = (root) => join5(root, "LATEST.json");
|
|
3954
|
+
function readLatestCheck2(root) {
|
|
3955
|
+
const p = latestCheckPath2(root);
|
|
3956
|
+
if (!existsSync6(p))
|
|
3957
|
+
return null;
|
|
3958
|
+
try {
|
|
3959
|
+
const v = JSON.parse(readFileSync5(p, "utf8"));
|
|
3960
|
+
if (typeof v.repo !== "string" || typeof v.checkedAt !== "string")
|
|
3961
|
+
return null;
|
|
3962
|
+
return {
|
|
3963
|
+
repo: v.repo,
|
|
3964
|
+
checkedAt: v.checkedAt,
|
|
3965
|
+
latest: typeof v.latest === "string" ? v.latest : null,
|
|
3966
|
+
tags: Array.isArray(v.tags) ? v.tags.filter((t) => typeof t === "string") : []
|
|
3967
|
+
};
|
|
3968
|
+
} catch {
|
|
3969
|
+
return null;
|
|
3970
|
+
}
|
|
3971
|
+
}
|
|
3972
|
+
var KEPT_TAGS = 50;
|
|
3973
|
+
async function latestCheck2(root, repo, remote, now = Date.now, intervalMs = LATEST_CHECK_MS2) {
|
|
3974
|
+
const kept = readLatestCheck2(root);
|
|
3975
|
+
const prior = kept !== null && kept.repo === repo ? kept : null;
|
|
3976
|
+
if (prior !== null) {
|
|
3977
|
+
const age = now() - Date.parse(prior.checkedAt);
|
|
3978
|
+
if (age >= 0 && age < intervalMs)
|
|
3979
|
+
return prior;
|
|
3980
|
+
}
|
|
3981
|
+
const checkedAt = new Date(now()).toISOString();
|
|
3982
|
+
const held = {
|
|
3983
|
+
repo,
|
|
3984
|
+
checkedAt,
|
|
3985
|
+
latest: prior?.latest ?? null,
|
|
3986
|
+
tags: prior?.tags ?? []
|
|
3987
|
+
};
|
|
3988
|
+
keep(root, held);
|
|
3989
|
+
let check;
|
|
3990
|
+
try {
|
|
3991
|
+
const tags = (await remote.tags(repo)).filter((t) => parseArchiveTag2(t) !== null).sort(compareTagsNewestFirst2);
|
|
3992
|
+
check = {
|
|
3993
|
+
repo,
|
|
3994
|
+
checkedAt,
|
|
3995
|
+
latest: pickTag2(tags, { kind: "latest" }),
|
|
3996
|
+
tags: tags.slice(0, KEPT_TAGS)
|
|
3997
|
+
};
|
|
3998
|
+
} catch {
|
|
3999
|
+
return held;
|
|
4000
|
+
}
|
|
4001
|
+
keep(root, check);
|
|
4002
|
+
return check;
|
|
4003
|
+
}
|
|
4004
|
+
function keep(root, check) {
|
|
4005
|
+
try {
|
|
4006
|
+
writeJsonAtomic2(latestCheckPath2(root), check);
|
|
4007
|
+
} catch {}
|
|
4008
|
+
}
|
|
4009
|
+
async function within(p, ms) {
|
|
4010
|
+
let timer;
|
|
4011
|
+
const late = new Promise((resolve) => {
|
|
4012
|
+
timer = setTimeout(() => resolve(null), Math.max(0, ms));
|
|
4013
|
+
timer.unref?.();
|
|
4014
|
+
});
|
|
4015
|
+
try {
|
|
4016
|
+
return await Promise.race([p, late]);
|
|
4017
|
+
} finally {
|
|
4018
|
+
clearTimeout(timer);
|
|
4019
|
+
}
|
|
4020
|
+
}
|
|
4021
|
+
function staleWarning2(answered, latest, floor, tags = [], command = "idq") {
|
|
4022
|
+
const a = parseArchiveTag2(answered);
|
|
4023
|
+
if (a === null)
|
|
4024
|
+
return null;
|
|
4025
|
+
const belowFloor = floor !== null && (a.cut === undefined || a.cut < floor.cut);
|
|
4026
|
+
const why = belowFloor ? ` (${floor.why})` : "";
|
|
4027
|
+
if (latest !== null && compareTagsNewestFirst2(answered, latest) > 0) {
|
|
4028
|
+
const lp = parseArchiveTag2(latest);
|
|
4029
|
+
if (!belowFloor || lp?.cut !== undefined && lp.cut >= (floor?.cut ?? 0))
|
|
4030
|
+
return `answered from ${answered}. ${latest} is available${why}: run \`${command} update\` to get the latest`;
|
|
4031
|
+
}
|
|
4032
|
+
if (!belowFloor)
|
|
4033
|
+
return null;
|
|
4034
|
+
const floorTag = pickTag2(tags, { kind: "cut", cut: floor.cut, fraction: 0 });
|
|
4035
|
+
return `answered from ${answered}. ${floorTag ?? `cut ${floor.cut}`} is required${why}: run \`${command} use ${floor.cut}\` to get it`;
|
|
4036
|
+
}
|
|
4037
|
+
|
|
4038
|
+
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, ReleaseManager2, releases2, LATEST_CHECK_MS2, LATEST_CHECK_BUDGET_MS2, latestCheckPath2, readLatestCheck2, latestCheck2, within, staleWarning2 };
|
package/dist/cli.js
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
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-pfsxe80h.js";
|
|
9
17
|
import"./chunks/index-hghdy980.js";
|
|
10
18
|
|
|
11
19
|
var USAGE = `usage: idq <verb> [--cache <dir>] [--repo <owner/name>] [--json]
|
|
@@ -15,6 +23,9 @@ var USAGE = `usage: idq <verb> [--cache <dir>] [--repo <owner/name>] [--json]
|
|
|
15
23
|
with --remote the published tags
|
|
16
24
|
fetch [--release <spec>] [--assets results,majors,<file>...]
|
|
17
25
|
download, verify and cache a release (default: results)
|
|
26
|
+
update [--assets ...] move to the newest published release: fetch it (verified),
|
|
27
|
+
build its indexes, then pin it
|
|
28
|
+
use <spec> [--assets ...] the same for the release a spec names
|
|
18
29
|
pin <spec> the user-local pin: what a bare open (spec "pinned") opens
|
|
19
30
|
unpin remove the pin (a bare open is then "latest")
|
|
20
31
|
current the pin, and whether it is cached
|
|
@@ -99,6 +110,10 @@ var USAGE = `usage: idq <verb> [--cache <dir>] [--repo <owner/name>] [--json]
|
|
|
99
110
|
population?, oireachtas?}, a registration: {name, school?,
|
|
100
111
|
codes[], feis, year?, population?, oireachtas?})
|
|
101
112
|
|
|
113
|
+
A query on the default spec prints one warning line on stderr when its release is older than
|
|
114
|
+
the newest published one (the remote asked at most once per hour, <cache>/LATEST.json); the
|
|
115
|
+
answer and the exit code are unchanged, and an offline check says nothing.
|
|
116
|
+
|
|
102
117
|
<spec>: pinned (the default) | latest | a cut (42, 42.1) | a tag (archive-YYYY.MM.DD-N)`;
|
|
103
118
|
var VALUE = new Set([
|
|
104
119
|
"--cache",
|
|
@@ -405,6 +420,27 @@ function eventYearOf(p) {
|
|
|
405
420
|
...year !== undefined ? { year: Number(year) } : {}
|
|
406
421
|
};
|
|
407
422
|
}
|
|
423
|
+
var ANSWERING = new Set([
|
|
424
|
+
"current",
|
|
425
|
+
"capabilities",
|
|
426
|
+
"feis",
|
|
427
|
+
"series",
|
|
428
|
+
"school",
|
|
429
|
+
"dancer",
|
|
430
|
+
"results",
|
|
431
|
+
"majors",
|
|
432
|
+
"stream",
|
|
433
|
+
"records",
|
|
434
|
+
"feis-dates",
|
|
435
|
+
"tabulated",
|
|
436
|
+
"teams",
|
|
437
|
+
"events",
|
|
438
|
+
"teacher",
|
|
439
|
+
"adjudicator",
|
|
440
|
+
"comp",
|
|
441
|
+
"syllabus",
|
|
442
|
+
"resolve"
|
|
443
|
+
]);
|
|
408
444
|
async function main(argv, io = {}) {
|
|
409
445
|
const say = io.say ?? ((s) => console.log(s));
|
|
410
446
|
const log = io.log ?? ((s) => console.error(s));
|
|
@@ -426,6 +462,23 @@ async function main(argv, io = {}) {
|
|
|
426
462
|
};
|
|
427
463
|
const spec = p.flags.get("--release") ?? "pinned";
|
|
428
464
|
const m = releases2(opts);
|
|
465
|
+
const fresh = io.freshness === false || spec.trim() !== "pinned" || verb === undefined || !ANSWERING.has(verb) ? null : io.freshness ?? {};
|
|
466
|
+
const answered = { tag: null };
|
|
467
|
+
const open = async (s, o) => {
|
|
468
|
+
const rel = await openRelease2(s, o);
|
|
469
|
+
answered.tag = rel.tag;
|
|
470
|
+
return rel;
|
|
471
|
+
};
|
|
472
|
+
const now = fresh?.now ?? Date.now;
|
|
473
|
+
const started = now();
|
|
474
|
+
let check = null;
|
|
475
|
+
if (fresh !== null) {
|
|
476
|
+
try {
|
|
477
|
+
const repo = repoOf2(opts);
|
|
478
|
+
if (repo !== null)
|
|
479
|
+
check = latestCheck2(cacheRoot2(opts), repo, opts.remote ?? defaultRemote(), now).catch(() => null);
|
|
480
|
+
} catch {}
|
|
481
|
+
}
|
|
429
482
|
const out = (v, text) => {
|
|
430
483
|
if (json)
|
|
431
484
|
say(JSON.stringify(v, null, 2));
|
|
@@ -477,6 +530,22 @@ async function main(argv, io = {}) {
|
|
|
477
530
|
out(pin, () => say(`pinned ${pin.tag} (from ${pin.source})`));
|
|
478
531
|
return 0;
|
|
479
532
|
}
|
|
533
|
+
case "update":
|
|
534
|
+
case "use": {
|
|
535
|
+
const target = verb === "update" ? "latest" : rest[0];
|
|
536
|
+
if (target === undefined) {
|
|
537
|
+
log("use <spec>");
|
|
538
|
+
return 2;
|
|
539
|
+
}
|
|
540
|
+
const assets = (p.flags.get("--assets") ?? "results").split(",").filter((a) => a !== "");
|
|
541
|
+
const u = await m.use(target, { assets });
|
|
542
|
+
out(u, () => {
|
|
543
|
+
for (const b of u.built)
|
|
544
|
+
say(`${u.tag} ${b.name} ${b.path}`);
|
|
545
|
+
say(u.was === u.tag ? `pinned ${u.tag} (already pinned; its indexes are built)` : `pinned ${u.tag}${u.was === null ? "" : ` (was ${u.was})`}`);
|
|
546
|
+
});
|
|
547
|
+
return 0;
|
|
548
|
+
}
|
|
480
549
|
case "unpin": {
|
|
481
550
|
const was = m.unpin();
|
|
482
551
|
out({ unpinned: was }, () => say(was ? "unpinned" : "no pin was set"));
|
|
@@ -484,6 +553,7 @@ async function main(argv, io = {}) {
|
|
|
484
553
|
}
|
|
485
554
|
case "current": {
|
|
486
555
|
const c = m.current();
|
|
556
|
+
answered.tag = c.pin?.tag ?? null;
|
|
487
557
|
out(c, () => say(c.pin === null ? "no pin: a bare open is latest" : `pinned ${c.pin.tag}${c.cached ? "" : " (not cached)"}`));
|
|
488
558
|
return 0;
|
|
489
559
|
}
|
|
@@ -506,7 +576,7 @@ async function main(argv, io = {}) {
|
|
|
506
576
|
return 0;
|
|
507
577
|
}
|
|
508
578
|
case "capabilities": {
|
|
509
|
-
const rel = await
|
|
579
|
+
const rel = await open(spec, { ...opts, assets: [] });
|
|
510
580
|
try {
|
|
511
581
|
const c = await rel.capabilities();
|
|
512
582
|
out(c, () => {
|
|
@@ -550,7 +620,7 @@ async function main(argv, io = {}) {
|
|
|
550
620
|
return 0;
|
|
551
621
|
}
|
|
552
622
|
case "feis": {
|
|
553
|
-
const rel = await
|
|
623
|
+
const rel = await open(spec, { ...opts, assets: [] });
|
|
554
624
|
const year = p.flags.get("--year");
|
|
555
625
|
const a = await rel.feises.find({
|
|
556
626
|
...p.flags.has("--platform") ? { platform: p.flags.get("--platform") } : {},
|
|
@@ -574,7 +644,7 @@ async function main(argv, io = {}) {
|
|
|
574
644
|
log("series <name|host code|feis id>");
|
|
575
645
|
return 2;
|
|
576
646
|
}
|
|
577
|
-
const rel = await
|
|
647
|
+
const rel = await open(spec, { ...opts, assets: [] });
|
|
578
648
|
const a = await rel.feises.series(rest.join(" "));
|
|
579
649
|
out(a, () => {
|
|
580
650
|
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 +664,7 @@ async function main(argv, io = {}) {
|
|
|
594
664
|
log("school <print|school_id> [--region <code>]");
|
|
595
665
|
return 2;
|
|
596
666
|
}
|
|
597
|
-
const rel = await
|
|
667
|
+
const rel = await open(spec, { ...opts, assets: [] });
|
|
598
668
|
const region = p.flags.get("--region");
|
|
599
669
|
const a = await rel.schools.resolve(rest.join(" "), region !== undefined ? { region } : {});
|
|
600
670
|
out(a, () => {
|
|
@@ -606,7 +676,7 @@ async function main(argv, io = {}) {
|
|
|
606
676
|
return a.arm === "none" ? 1 : 0;
|
|
607
677
|
}
|
|
608
678
|
case "dancer": {
|
|
609
|
-
const rel = await
|
|
679
|
+
const rel = await open(spec, { ...opts, assets: [] });
|
|
610
680
|
try {
|
|
611
681
|
const id = p.flags.get("--id");
|
|
612
682
|
if (id !== undefined) {
|
|
@@ -665,7 +735,7 @@ async function main(argv, io = {}) {
|
|
|
665
735
|
}
|
|
666
736
|
}
|
|
667
737
|
case "results": {
|
|
668
|
-
const rel = await
|
|
738
|
+
const rel = await open(spec, { ...opts, assets: [] });
|
|
669
739
|
try {
|
|
670
740
|
const q = {
|
|
671
741
|
...p.flags.has("--dancer") ? { dancer_id: Number(p.flags.get("--dancer")) } : {},
|
|
@@ -683,7 +753,7 @@ async function main(argv, io = {}) {
|
|
|
683
753
|
}
|
|
684
754
|
}
|
|
685
755
|
case "majors": {
|
|
686
|
-
const rel = await
|
|
756
|
+
const rel = await open(spec, { ...opts, assets: [] });
|
|
687
757
|
try {
|
|
688
758
|
const event = p.flags.get("--event");
|
|
689
759
|
const year = p.flags.get("--year");
|
|
@@ -725,7 +795,7 @@ async function main(argv, io = {}) {
|
|
|
725
795
|
}
|
|
726
796
|
case "stream": {
|
|
727
797
|
const q = streamQueryOf(p);
|
|
728
|
-
const rel = await
|
|
798
|
+
const rel = await open(spec, { ...opts, assets: [] });
|
|
729
799
|
try {
|
|
730
800
|
let n = 0;
|
|
731
801
|
const rows = p.bools.has("--entries") ? rel.results.entries(q) : rel.results.stream(q);
|
|
@@ -740,7 +810,7 @@ async function main(argv, io = {}) {
|
|
|
740
810
|
}
|
|
741
811
|
}
|
|
742
812
|
case "records": {
|
|
743
|
-
const rel = await
|
|
813
|
+
const rel = await open(spec, { ...opts, assets: [] });
|
|
744
814
|
try {
|
|
745
815
|
const ids = idsOf(p.flags.get("--dancer"));
|
|
746
816
|
if (p.bools.has("--years")) {
|
|
@@ -768,7 +838,7 @@ async function main(argv, io = {}) {
|
|
|
768
838
|
}
|
|
769
839
|
}
|
|
770
840
|
case "feis-dates": {
|
|
771
|
-
const rel = await
|
|
841
|
+
const rel = await open(spec, { ...opts, assets: [] });
|
|
772
842
|
try {
|
|
773
843
|
const a = await rel.feises.dates();
|
|
774
844
|
out(a, () => {
|
|
@@ -782,7 +852,7 @@ async function main(argv, io = {}) {
|
|
|
782
852
|
}
|
|
783
853
|
}
|
|
784
854
|
case "tabulated": {
|
|
785
|
-
const rel = await
|
|
855
|
+
const rel = await open(spec, { ...opts, assets: [] });
|
|
786
856
|
try {
|
|
787
857
|
const min = p.flags.get("--min-round");
|
|
788
858
|
const a = await rel.majors.tabulated(min !== undefined ? { min_true_round: Number(min) } : {});
|
|
@@ -797,7 +867,7 @@ async function main(argv, io = {}) {
|
|
|
797
867
|
}
|
|
798
868
|
}
|
|
799
869
|
case "teams": {
|
|
800
|
-
const rel = await
|
|
870
|
+
const rel = await open(spec, { ...opts, assets: [] });
|
|
801
871
|
try {
|
|
802
872
|
const q = eventYearOf(p);
|
|
803
873
|
if (p.bools.has("--rosters")) {
|
|
@@ -833,7 +903,7 @@ async function main(argv, io = {}) {
|
|
|
833
903
|
}
|
|
834
904
|
}
|
|
835
905
|
case "events": {
|
|
836
|
-
const rel = await
|
|
906
|
+
const rel = await open(spec, { ...opts, assets: [] });
|
|
837
907
|
try {
|
|
838
908
|
const a = await rel.events.years(eventYearOf(p));
|
|
839
909
|
out(a, () => {
|
|
@@ -852,7 +922,7 @@ async function main(argv, io = {}) {
|
|
|
852
922
|
log(`${verb} <name|TD number>`);
|
|
853
923
|
return 2;
|
|
854
924
|
}
|
|
855
|
-
const rel = await
|
|
925
|
+
const rel = await open(spec, { ...opts, assets: [] });
|
|
856
926
|
try {
|
|
857
927
|
const a = verb === "teacher" ? await rel.staff.teacher(rest.join(" ")) : await rel.staff.adjudicator(rest.join(" "));
|
|
858
928
|
out(a, () => printStaff(a, say));
|
|
@@ -866,7 +936,7 @@ async function main(argv, io = {}) {
|
|
|
866
936
|
log("comp <feis> <code> [--year <y>] [--title <t>]");
|
|
867
937
|
return 2;
|
|
868
938
|
}
|
|
869
|
-
const rel = await
|
|
939
|
+
const rel = await open(spec, { ...opts, assets: [] });
|
|
870
940
|
const year = p.flags.get("--year");
|
|
871
941
|
const a = await rel.comps.resolve({
|
|
872
942
|
feis: rest[0],
|
|
@@ -882,7 +952,7 @@ async function main(argv, io = {}) {
|
|
|
882
952
|
const of = p.flags.get("--for");
|
|
883
953
|
if (of !== undefined) {
|
|
884
954
|
const y = rest[0] ?? p.flags.get("--year");
|
|
885
|
-
const rel = await
|
|
955
|
+
const rel = await open(spec, { ...opts, assets: [] });
|
|
886
956
|
const a = await rel.comps.syllabusFor(of, y !== undefined ? Number(y) : undefined);
|
|
887
957
|
out(a, () => {
|
|
888
958
|
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 +971,7 @@ async function main(argv, io = {}) {
|
|
|
901
971
|
log("syllabus <feis> [year]");
|
|
902
972
|
return 2;
|
|
903
973
|
}
|
|
904
|
-
const rel = await
|
|
974
|
+
const rel = await open(spec, { ...opts, assets: [] });
|
|
905
975
|
const a = await rel.comps.syllabus(rest[0], rest[1] !== undefined ? Number(rest[1]) : undefined);
|
|
906
976
|
out(a, () => {
|
|
907
977
|
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 +983,7 @@ async function main(argv, io = {}) {
|
|
|
913
983
|
return a.held ? 0 : 1;
|
|
914
984
|
}
|
|
915
985
|
case "resolve": {
|
|
916
|
-
const rel = await
|
|
986
|
+
const rel = await open(spec, { ...opts, assets: [] });
|
|
917
987
|
try {
|
|
918
988
|
return await resolveVerb(rel, rest[0], rest.slice(1), p, say, io.stdin ?? (() => Bun.stdin.text()));
|
|
919
989
|
} finally {
|
|
@@ -927,6 +997,14 @@ async function main(argv, io = {}) {
|
|
|
927
997
|
} catch (e) {
|
|
928
998
|
log(`idq ${verb}: ${e.message}`);
|
|
929
999
|
return 1;
|
|
1000
|
+
} finally {
|
|
1001
|
+
if (fresh !== null && answered.tag !== null) {
|
|
1002
|
+
const budget = fresh.budgetMs ?? LATEST_CHECK_BUDGET_MS2;
|
|
1003
|
+
const c = check === null ? null : await within(check, budget - (now() - started));
|
|
1004
|
+
const w = staleWarning2(answered.tag, c?.latest ?? null, fresh.floor ?? null, c?.tags ?? [], fresh.command);
|
|
1005
|
+
if (w !== null)
|
|
1006
|
+
log(w);
|
|
1007
|
+
}
|
|
930
1008
|
}
|
|
931
1009
|
}
|
|
932
1010
|
if (import.meta.main)
|
package/dist/index.js
CHANGED
|
@@ -51,8 +51,14 @@ import {
|
|
|
51
51
|
openRelease2,
|
|
52
52
|
openCached2,
|
|
53
53
|
ReleaseManager2,
|
|
54
|
-
releases2
|
|
55
|
-
|
|
54
|
+
releases2,
|
|
55
|
+
LATEST_CHECK_MS2,
|
|
56
|
+
LATEST_CHECK_BUDGET_MS2,
|
|
57
|
+
latestCheckPath2,
|
|
58
|
+
readLatestCheck2,
|
|
59
|
+
latestCheck2,
|
|
60
|
+
staleWarning2
|
|
61
|
+
} from "./chunks/index-pfsxe80h.js";
|
|
56
62
|
import {
|
|
57
63
|
cacheRoot2,
|
|
58
64
|
repoOf2
|
|
@@ -76,6 +82,8 @@ export {
|
|
|
76
82
|
FEATURES2 as FEATURES,
|
|
77
83
|
FEIS_LOCAL_ID_FLOOR2 as FEIS_LOCAL_ID_FLOOR,
|
|
78
84
|
FeisFinder2 as FeisFinder,
|
|
85
|
+
LATEST_CHECK_BUDGET_MS2 as LATEST_CHECK_BUDGET_MS,
|
|
86
|
+
LATEST_CHECK_MS2 as LATEST_CHECK_MS,
|
|
79
87
|
LINEAGE_FILE2 as LINEAGE_FILE,
|
|
80
88
|
MajorsFinder2 as MajorsFinder,
|
|
81
89
|
NAME_LEVELS2 as NAME_LEVELS,
|
|
@@ -105,6 +113,8 @@ export {
|
|
|
105
113
|
indexPath2 as indexPath,
|
|
106
114
|
initialsOnly2 as initialsOnly,
|
|
107
115
|
isCurrent2 as isCurrent,
|
|
116
|
+
latestCheck2 as latestCheck,
|
|
117
|
+
latestCheckPath2 as latestCheckPath,
|
|
108
118
|
linksOfLineage2 as linksOfLineage,
|
|
109
119
|
listIndexes2 as listIndexes,
|
|
110
120
|
nameLevel2 as nameLevel,
|
|
@@ -115,6 +125,7 @@ export {
|
|
|
115
125
|
parseReleaseSpec2 as parseReleaseSpec,
|
|
116
126
|
pickTag2 as pickTag,
|
|
117
127
|
planAsset2 as planAsset,
|
|
128
|
+
readLatestCheck2 as readLatestCheck,
|
|
118
129
|
readPin2 as readPin,
|
|
119
130
|
readPopulation2 as readPopulation,
|
|
120
131
|
readsSpanishNames2 as readsSpanishNames,
|
|
@@ -125,6 +136,7 @@ export {
|
|
|
125
136
|
resolveTag2 as resolveTag,
|
|
126
137
|
setRemote2 as setRemote,
|
|
127
138
|
sha256File2 as sha256File,
|
|
139
|
+
staleWarning2 as staleWarning,
|
|
128
140
|
syllabusSourceOf2 as syllabusSourceOf,
|
|
129
141
|
unzstdTo2 as unzstdTo,
|
|
130
142
|
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 1)\n 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 <spec>: pinned (the default) | latest | a cut (42, 42.1) | a tag (archive-YYYY.MM.DD-N)";
|
|
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 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 ...] move to the newest published release: fetch it (verified),\n build its indexes, then pin it\n use <spec> [--assets ...] 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 1)\n 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 {};
|
|
@@ -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
|
@@ -9,6 +9,7 @@ export { type Feis, type FeisAnswer, FeisFinder, type FeisQuery, NO_CALENDAR, ty
|
|
|
9
9
|
export { type IndexBuilder, type IndexFile, buildIndex, defaultBuilders, ensureIndex, indexKey, indexPath, isCurrent, listIndexes, } from "./indexes.js";
|
|
10
10
|
export { type CachedRelease, type ReleaseList, ReleaseManager, type RemovalPlan, 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
|
@@ -48,6 +48,17 @@ export declare class ReleaseManager {
|
|
|
48
48
|
assets?: readonly string[];
|
|
49
49
|
}): Promise<Release>;
|
|
50
50
|
pin(spec: string | number): Promise<Pin>;
|
|
51
|
+
use(spec: string | number, o?: {
|
|
52
|
+
assets?: readonly string[];
|
|
53
|
+
}): Promise<{
|
|
54
|
+
tag: string;
|
|
55
|
+
was: string | null;
|
|
56
|
+
pin: Pin;
|
|
57
|
+
built: {
|
|
58
|
+
name: string;
|
|
59
|
+
path: string;
|
|
60
|
+
}[];
|
|
61
|
+
}>;
|
|
51
62
|
unpin(): boolean;
|
|
52
63
|
current(): {
|
|
53
64
|
pin: Pin | null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@finbheara/archive-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.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",
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
"prepublishOnly": "bun scripts/build.ts && bun ../../tools/checks/archive-client-tarball.ts"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@finbheara/domain": "0.57.
|
|
48
|
-
"@finbheara/names": "0.
|
|
47
|
+
"@finbheara/domain": "0.57.2",
|
|
48
|
+
"@finbheara/names": "0.30.0",
|
|
49
49
|
"hyparquet": "1.31.2"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|