@finbheara/archive-client 0.34.0 → 0.35.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 CHANGED
@@ -101,6 +101,8 @@ c.features; // [{ feature: "dancers", state: "full" | "missing", missing: [...
101
101
  | `upcoming.fields`, `upcoming.entrants` | `upcoming.fields`, `upcoming.entrants` (285 and 290 carry the tables empty) | 291 |
102
102
  | `upcoming.fields.instance` | the `feis_instance_id` `upcoming.fields` carries; absent, never refused, without it | 320 |
103
103
  | `calendar` | `calendar.get`, `calendar.list` | 320 |
104
+ | `calendar.expected` | each calendar stage's `expected`; absent, never refused, without it | 320 |
105
+ | `calendar.next` | `calendar.next` | 320 |
104
106
  | `tabs`, `panels` | `tabs.query`, `tabs.stream`; `panels.query` | 184 |
105
107
  | `feis-panels` | `feisPanels.query` | 260 |
106
108
  | `staff.adjudicators` | `staff.adjudicators` | 230 |
@@ -762,8 +764,14 @@ g.via; // "instance" | "formerly" | "feis_id" | "occurrence_id" | null
762
764
  g.entries; // [{ feis_instance_id, feis_brand_id, tier, name_printed, date_lo, date_hi, date_source,
763
765
  // occurrence_ids, feis_ids, stages: { announced, syllabus, entrants, run, results },
764
766
  // terminal, carried, formerly }]
767
+ g.entries[0].stages.results.expected;
768
+ // { date, basis, n, platform } or null: "announced" | "brand" | "platform" |
769
+ // "pooled" | "roster-window" | "unmeasured"
765
770
  const l = await rel.calendar.list({ stage: "entrants", state: "observed", from: "2026-11-01" });
766
771
  // brand, tier, from, to, stage, state, terminal ("none": still ahead)
772
+ const n = await rel.calendar.next({ brand: "plf" });
773
+ // [{ feis_brand_id, year, run_expected, run_expected_basis, run_expected_n,
774
+ // from_instance_id, from_date }]: a series' next edition no listing names yet
767
775
  ```
768
776
 
769
777
  ONE ROW PER FEIS INSTANCE, on the id its results carry: a feis-series instance (`<brand>.<year>`),
@@ -780,6 +788,20 @@ order: only results after the run is a belief. `get` tries the instance id, then
780
788
  a capture, then an occurrence id, and says which met (`via`). On a release without the table each
781
789
  call refuses by name.
782
790
 
791
+ EXPECTED DAYS (`calendar.expected`): each stage carries `expected`, `{ date, basis, n, platform }`
792
+ or null. `announced` is the listing's own first day; `brand` the series' edition the year before
793
+ plus 364 days, `n` its held years; `platform` a platform's capture lag (the P90 of first results
794
+ capture less last day) or list lead (the median of first day less first list capture), measured
795
+ over the feiseanna run in the 180 days before the cut, `n` at least 10; `pooled` every platform's
796
+ results lags together, for a feis whose hosts have no capture form (`platform` `all`) and, NAMED, in
797
+ place of a platform measured on fewer than 10 (`platform` the host's); `roster-window` a majors event-year
798
+ no capture dates; `unmeasured` no day. A stage still ahead always carries one; an observed stage
799
+ carries the day its estimator gives it where one applies, so the misses can be read. The lags are
800
+ capture's own scrape cadence, re-measured every release. `next` lists each series with two or more
801
+ held years, the last the release's year or the one before, whose next edition is not on the
802
+ calendar: its year and expected first day. It is never an instance: an id is minted only once a
803
+ listing names the edition.
804
+
783
805
  ## Teachers and adjudicators
784
806
 
785
807
  ```ts
@@ -958,6 +980,7 @@ is passed to the dancer resolver as asked (`rel.dancers`, above).
958
980
  idq calendar [--release <spec>] <instance|feis id|occurrence id>
959
981
  idq calendar [--brand <series>] [--tier <t>] [--from <date>] [--to <date>] [--stage <s>] [--state <s>]
960
982
  [--terminal results|withdrawn|passed-no-results|none]
983
+ idq calendar --next [--brand <series>]
961
984
  idq teacher [--release <spec>] <name|TD number>
962
985
  idq adjudicator [--release <spec>] <name|TD number>
963
986
  idq comp [--release <spec>] <feis> <code> [--year y] [--title t]
@@ -722,6 +722,26 @@ var FEATURES2 = [
722
722
  since: 320,
723
723
  calls: "calendar.get, calendar.list"
724
724
  },
725
+ {
726
+ feature: "calendar.expected",
727
+ needs: ["announced", "syllabus", "entrants", "run", "results"].flatMap((st) => ["", "_basis", "_n", "_platform"].map((x) => `results:feis_calendar.${st}_expected${x}`)),
728
+ since: 320,
729
+ calls: "each calendar stage's `expected` (absent, never refused, without it)"
730
+ },
731
+ {
732
+ feature: "calendar.next",
733
+ needs: [
734
+ "feis_brand_id",
735
+ "year",
736
+ "run_expected",
737
+ "run_expected_basis",
738
+ "run_expected_n",
739
+ "from_instance_id",
740
+ "from_date"
741
+ ].map((c) => `results:feis_series_next.${c}`),
742
+ since: 320,
743
+ calls: "calendar.next"
744
+ },
725
745
  {
726
746
  feature: "upcoming.entrants",
727
747
  needs: [
@@ -3877,13 +3897,26 @@ var CALENDAR_TERMINALS2 = [
3877
3897
  "passed-no-results"
3878
3898
  ];
3879
3899
  var json = (s, empty) => typeof s === "string" && s !== "" ? JSON.parse(s) : empty;
3880
- function entryOf2(r) {
3900
+ function expectedOf(r, s) {
3901
+ const basis = r[`${s}_expected_basis`];
3902
+ if (basis === null || basis === undefined)
3903
+ return null;
3904
+ const n = r[`${s}_expected_n`];
3905
+ return {
3906
+ date: r[`${s}_expected`] ?? null,
3907
+ basis,
3908
+ n: n === null || n === undefined ? null : Number(n),
3909
+ platform: r[`${s}_expected_platform`] ?? null
3910
+ };
3911
+ }
3912
+ function entryOf2(r, expected) {
3881
3913
  const stages = Object.fromEntries(CALENDAR_STAGES2.map((s) => [
3882
3914
  s,
3883
3915
  {
3884
3916
  state: r[`${s}_state`],
3885
3917
  date: r[`${s}_date`] ?? null,
3886
- cite: r[`${s}_cite`] ?? null
3918
+ cite: r[`${s}_cite`] ?? null,
3919
+ ...expected ? { expected: expectedOf(r, s) } : {}
3887
3920
  }
3888
3921
  ]));
3889
3922
  return {
@@ -3914,6 +3947,27 @@ class CalendarFinder2 {
3914
3947
  await this.rel.require("calendar");
3915
3948
  return this.rel.db("results");
3916
3949
  }
3950
+ async expected() {
3951
+ return (await this.rel.capability("calendar.expected")).state === "full";
3952
+ }
3953
+ async next(q = {}) {
3954
+ await this.rel.fetch("results");
3955
+ await this.rel.require("calendar.next");
3956
+ const db = this.rel.db("results");
3957
+ const rows = q.brand === undefined ? db.query("SELECT * FROM feis_series_next ORDER BY run_expected, feis_brand_id").all() : db.query("SELECT * FROM feis_series_next WHERE feis_brand_id = ? ORDER BY run_expected").all(q.brand);
3958
+ return {
3959
+ tag: this.rel.tag,
3960
+ next: rows.map((r) => ({
3961
+ feis_brand_id: r.feis_brand_id,
3962
+ year: Number(r.year),
3963
+ run_expected: r.run_expected,
3964
+ run_expected_basis: "brand",
3965
+ run_expected_n: Number(r.run_expected_n),
3966
+ from_instance_id: r.from_instance_id,
3967
+ from_date: r.from_date
3968
+ }))
3969
+ };
3970
+ }
3917
3971
  async get(id) {
3918
3972
  const db = await this.db();
3919
3973
  const ways = [
@@ -3924,8 +3978,10 @@ class CalendarFinder2 {
3924
3978
  ];
3925
3979
  for (const [via, where] of ways) {
3926
3980
  const rows = db.query(`SELECT * FROM feis_calendar WHERE ${where} ORDER BY feis_instance_id`).all(id);
3927
- if (rows.length > 0)
3928
- return { tag: this.rel.tag, asked: id, via, entries: rows.map(entryOf2) };
3981
+ if (rows.length > 0) {
3982
+ const e = await this.expected();
3983
+ return { tag: this.rel.tag, asked: id, via, entries: rows.map((r) => entryOf2(r, e)) };
3984
+ }
3929
3985
  }
3930
3986
  return {
3931
3987
  tag: this.rel.tag,
@@ -3967,7 +4023,8 @@ class CalendarFinder2 {
3967
4023
  add("terminal = ?", q.terminal);
3968
4024
  const sql = `SELECT * FROM feis_calendar${where.length > 0 ? ` WHERE ${where.join(" AND ")}` : ""} ORDER BY date_lo IS NULL, date_lo, feis_instance_id`;
3969
4025
  const rows = db.query(sql).all(...args);
3970
- return { tag: this.rel.tag, entries: rows.map(entryOf2) };
4026
+ const e = await this.expected();
4027
+ return { tag: this.rel.tag, entries: rows.map((r) => entryOf2(r, e)) };
3971
4028
  }
3972
4029
  }
3973
4030
 
package/dist/cli.js CHANGED
@@ -21,7 +21,7 @@ import {
21
21
  latestCheck2,
22
22
  within,
23
23
  staleWarning2
24
- } from "./chunks/index-cwhyrcyx.js";
24
+ } from "./chunks/index-gt7yxxp8.js";
25
25
  import"./chunks/index-hghdy980.js";
26
26
 
27
27
  var USAGE_HEADER = "usage: idq <verb> [--cache <dir>] [--repo <owner/name>] [--json] [--live]";
@@ -395,7 +395,7 @@ var VERBS = [
395
395
  {
396
396
  verb: "calendar",
397
397
  value: ["--release", "--brand", "--tier", "--from", "--to", "--stage", "--state", "--terminal"],
398
- bool: [],
398
+ bool: ["--next"],
399
399
  usage: [
400
400
  " calendar [--release <spec>] <instance|feis id|occurrence id>",
401
401
  " one feis instance's stages: announced, syllabus, entrants,",
@@ -404,7 +404,11 @@ var VERBS = [
404
404
  " calendar [--brand <series>] [--tier T1..T4] [--from <date>] [--to <date>]",
405
405
  " [--stage announced|syllabus|entrants|run|results] [--state observed|pending|not-captured]",
406
406
  " [--terminal results|withdrawn|passed-no-results|none]",
407
- " the instances a query names, by first day"
407
+ " the instances a query names, by first day; each stage's",
408
+ " expected day with its basis and n where the release has them",
409
+ " calendar --next [--brand <series>]",
410
+ " each series' next edition no listing names yet: its year",
411
+ " and expected first day from the year before (basis brand)"
408
412
  ]
409
413
  },
410
414
  {
@@ -523,6 +527,7 @@ function parseArgs(argv) {
523
527
  return { positional, flags, bools };
524
528
  }
525
529
  var mb = (n) => `${(n / 1e6).toFixed(1)} MB`;
530
+ var expectedText = (e) => e === null || e === undefined ? "" : ` expected ${e.date ?? "-"} (${e.basis}${e.platform !== null ? ` ${e.platform}` : ""}${e.n !== null ? `, n ${e.n}` : ""})`;
526
531
  function printPlan(p, say) {
527
532
  for (const k of p.keep)
528
533
  say(`keep ${k.tag} (${k.why})`);
@@ -1553,13 +1558,23 @@ async function main(argv, io = {}) {
1553
1558
  const rel = await open(spec, { ...opts, assets: [] });
1554
1559
  try {
1555
1560
  const line = (e) => ` ${e.date_lo ?? "?"} ${e.feis_instance_id} ${e.tier} ${["announced", "syllabus", "entrants", "run", "results"].map((s) => `${s}:${e.stages[s].state === "observed" ? e.stages[s].date ?? "seen" : e.stages[s].state}`).join(" ")} [${e.terminal ?? "ahead"}]${e.name_printed !== null ? ` "${e.name_printed}"` : ""}`;
1561
+ if (p.bools.has("--next")) {
1562
+ const brand = p.flags.get("--brand");
1563
+ const a = await rel.calendar.next(brand === undefined ? {} : { brand });
1564
+ out(a, () => {
1565
+ for (const n of a.next)
1566
+ say(` ${n.run_expected} ${n.feis_brand_id} ${n.year} brand n ${n.run_expected_n} from ${n.from_instance_id} (${n.from_date})`);
1567
+ say(`${a.next.length} next edition(s) in ${a.tag}`);
1568
+ });
1569
+ return a.next.length === 0 ? 1 : 0;
1570
+ }
1556
1571
  if (rest.length > 0) {
1557
1572
  const a = await rel.calendar.get(rest.join(" "));
1558
1573
  out(a, () => {
1559
1574
  for (const e of a.entries) {
1560
1575
  say(line(e));
1561
1576
  for (const s of ["announced", "syllabus", "entrants", "run", "results"])
1562
- say(` ${s} ${e.stages[s].state} ${e.stages[s].date ?? "-"} ${e.stages[s].cite ?? "-"}${e.carried.includes(s) ? " (carried)" : ""}`);
1577
+ say(` ${s} ${e.stages[s].state} ${e.stages[s].date ?? "-"} ${e.stages[s].cite ?? "-"}${e.carried.includes(s) ? " (carried)" : ""}${expectedText(e.stages[s].expected)}`);
1563
1578
  }
1564
1579
  say(`${a.entries.length} instance(s) for "${a.asked}"${a.via !== null ? ` by ${a.via}` : ""}${a.why !== undefined ? ` (${a.why})` : ""} in ${a.tag}`);
1565
1580
  });
package/dist/index.js CHANGED
@@ -80,7 +80,7 @@ import {
80
80
  readLatestCheck2,
81
81
  latestCheck2,
82
82
  staleWarning2
83
- } from "./chunks/index-cwhyrcyx.js";
83
+ } from "./chunks/index-gt7yxxp8.js";
84
84
  import {
85
85
  cacheRoot2,
86
86
  repoOf2
@@ -5,10 +5,18 @@ export type CalendarStageState = "observed" | "pending" | "not-captured";
5
5
  export declare const CALENDAR_STAGE_STATES: readonly CalendarStageState[];
6
6
  export type CalendarTerminal = "results" | "withdrawn" | "passed-no-results";
7
7
  export declare const CALENDAR_TERMINALS: readonly CalendarTerminal[];
8
+ export type CalendarExpectedBasis = "announced" | "brand" | "platform" | "pooled" | "roster-window" | "unmeasured";
9
+ export interface CalendarExpected {
10
+ date: string | null;
11
+ basis: CalendarExpectedBasis;
12
+ n: number | null;
13
+ platform: string | null;
14
+ }
8
15
  export interface CalendarStageValue {
9
16
  state: CalendarStageState;
10
17
  date: string | null;
11
18
  cite: string | null;
19
+ expected?: CalendarExpected | null;
12
20
  }
13
21
  export interface CalendarEntry {
14
22
  feis_instance_id: string;
@@ -38,6 +46,19 @@ export interface CalendarAnswer {
38
46
  tag: string;
39
47
  entries: CalendarEntry[];
40
48
  }
49
+ export interface CalendarNext {
50
+ feis_brand_id: string;
51
+ year: number;
52
+ run_expected: string;
53
+ run_expected_basis: "brand";
54
+ run_expected_n: number;
55
+ from_instance_id: string;
56
+ from_date: string;
57
+ }
58
+ export interface CalendarNextAnswer {
59
+ tag: string;
60
+ next: CalendarNext[];
61
+ }
41
62
  export interface CalendarGetAnswer {
42
63
  tag: string;
43
64
  asked: string;
@@ -49,6 +70,10 @@ export declare class CalendarFinder {
49
70
  private readonly rel;
50
71
  constructor(rel: Release);
51
72
  private db;
73
+ private expected;
74
+ next(q?: {
75
+ brand?: string;
76
+ }): Promise<CalendarNextAnswer>;
52
77
  get(id: string): Promise<CalendarGetAnswer>;
53
78
  list(q?: CalendarQuery): Promise<CalendarAnswer>;
54
79
  }
@@ -26,5 +26,5 @@ export { type EntryRow, type StreamCompetition, type StreamQuery, type StreamRow
26
26
  export { type FeisPanelSeat, FeisPanelFinder, PanelFinder, type PanelSeat, type RegisterRow, type TabAnswer, TabFinder, type TabQuery, type TabRow, type TabStreamQuery, } from "./tabs.js";
27
27
  export { EventFinder, type EventYear, type EventYearQuery, type ResultRoster, type RosterMember, type RosterTeam, TeamFinder, type TeamResult, } from "./teams.js";
28
28
  export { type EntrantCandidate, FIELD_STATES, type FieldComp, DEPARTURE_REASONS, type DepartureReason, type FieldState, type HeldOut, type UpcomingDeparture, type UpcomingDepartureAnswer, type UpcomingDepartureQuery, type UpcomingEntrant, type UpcomingEntrantAnswer, type UpcomingEntrantQuery, type UpcomingFeis, type UpcomingFeisAnswer, type UpcomingFeisQuery, type UpcomingFeisState, type UpcomingField, type UpcomingFieldAnswer, type UpcomingFieldQuery, UpcomingFinder, type UpcomingHost, } from "./upcoming.js";
29
- export { CALENDAR_STAGES, CALENDAR_STAGE_STATES, CALENDAR_TERMINALS, type CalendarAnswer, type CalendarEntry, CalendarFinder, type CalendarGetAnswer, type CalendarQuery, type CalendarStage, type CalendarStageState, type CalendarStageValue, type CalendarTerminal, } from "./calendar.js";
29
+ export { CALENDAR_STAGES, CALENDAR_STAGE_STATES, CALENDAR_TERMINALS, type CalendarAnswer, type CalendarEntry, type CalendarExpected, type CalendarExpectedBasis, type CalendarNext, type CalendarNextAnswer, CalendarFinder, type CalendarGetAnswer, type CalendarQuery, type CalendarStage, type CalendarStageState, type CalendarStageValue, type CalendarTerminal, } from "./calendar.js";
30
30
  export { ARCHIVE_TAG, type ArchiveTag, type ReleaseSpec, compareTagsNewestFirst, cutOf, parseArchiveTag, parseReleaseSpec, pickTag, renderArchiveTag, } from "./tag.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finbheara/archive-client",
3
- "version": "0.34.0",
3
+ "version": "0.35.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",