@finbheara/archive-client 0.28.0 → 0.30.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
@@ -662,11 +662,15 @@ o.feises; // [{ occurrence_id, occurrence_ids, name_printed, date_lo, date_hi
662
662
  const f = await rel.upcoming.fields({ state: "pending" }); // state, feis, field_id, from, to
663
663
  f.fields; // [{ field_id, feis_id, source, host_id, competition_code, count_only, occurrence_id,
664
664
  // name_printed, date_lo, date_hi, year, feis_brand_id, comp, state, flags,
665
- // results_competitions, results_feis_ids, held_out, version, versions, captured_date, sha256,
665
+ // results_competitions, results_feis_ids, held_out, merged_into, version, versions,
666
+ // captured_date, sha256,
666
667
  // limits, entrants, stated_rows, printed_total }]
667
668
  const e = await rel.upcoming.entrants({ field_id: "fweb:ABC26/101" }); // version: an older capture
668
669
  e.entrants; // [{ name_printed, school_printed, school_id, yob, arm, candidates, flags,
669
670
  // competitions, field_ids, captured_id, row_id, ... }]
671
+ const d = await rel.upcoming.departures({ reason: ["results", "merged"] }); // feis, field_id, from, to
672
+ d.departures; // [{ field_id, feis_id, occurrence_id, feis_brand_id, competition_code, date_lo, date_hi,
673
+ // reason, competitions, feis_ids, merged_into, held_out, last_version }]
670
674
  ```
671
675
 
672
676
  `upcoming.feises` is every occurrence of the cut's feis-index the release holds no results of:
@@ -677,7 +681,9 @@ says how; `none` leaves `feis_brand_id` null, the unjoined ones).
677
681
  A FIELD is one competition a competitor list names, `<feis_id>/<code>`, the same id on every cut. A later capture of the host is a new `version`
678
682
  (a capture id; `versions` lists them), never a new field. `state` says how it stands on the cut:
679
683
  `pending` (on the list), `results` (the release holds its results, `results_competitions` the
680
- join), `held-out` (the results exist and the release holds them out: the platform door's knockout
684
+ join), `merged` (the host registered it apart and ran it as one competition with another field: its
685
+ results are that field's, and `merged_into` names the survivor, the first by `field_id`, which
686
+ reads `results`), `held-out` (the results exist and the release holds them out: the platform door's knockout
681
687
  lists the competition, a team, music or arts event, and `held_out` names each competition with the
682
688
  knockout's reason, so there is nothing to score against), `passed` (its first day is before the
683
689
  cut's day and no result is held) or `withdrawn` (the host's newest capture no longer lists it). `comp` is the competition reading of its code (`arm`,
@@ -687,6 +693,13 @@ QuickFeis registrant list's field reads it off the list's own by-event label (`s
687
693
  list's two documents (`no-event-section`, `event-rows-differ`); `[]` on any other field.
688
694
  `limits` names what the capture cannot show (`registered-not-danced`, `paid-in-full-only`, ...).
689
695
 
696
+ THE DEPARTURE RECORD IS THE STATE. Every state but `pending` is how the field left the list, and
697
+ `upcoming.departures` answers one record per departed field: its `reason`, what it became
698
+ (`competitions`, the results join; `merged_into`; `held_out`) and `last_version`. It is
699
+ re-derived on every cut from what the release holds, so a field that left keeps its record on every
700
+ later cut while the corpus keeps its capture, and a `passed` field whose results arrive later reads
701
+ `results` on that cut: a consumer that skips a release still sees it.
702
+
690
703
  `upcoming.entrants` answers a field's rows in one version, its current one by default. A
691
704
  registrant carries the resolver's `arm` and `candidates`, NEVER A `dancer_id`: a list shows who
692
705
  registered, not who danced, and a `unique` row's one candidate is read with its `flags`
@@ -764,7 +777,11 @@ codes, a plausible answer and a wrong one. So an unheld feis-year there answers
764
777
  over the newest earlier syllabus, and a code with no title is `none`, saying why. The code of a
765
778
  held feis-year is still read from that feis-year's own competitions.
766
779
 
767
- A feis the release holds names itself, its year and its series. A series instance or brand id
780
+ A feis the release holds names itself, its year and its series. A platform feis id
781
+ (`<token>:<host>`) the release holds competitions of and no `feises` line names (a source that
782
+ carried a feis's results and not the feis itself) is held too, on its own id and its competitions'
783
+ year (`via: "id"`, as a feis with its line is), its series the one its host code reaches if
784
+ exactly one brand matches the stem, else null. A majors or championship event id is never held so. A series instance or brand id
768
785
  names the series. A feis id the release does not hold is placed in a series by its host code
769
786
  (`<token>:<stem><yy>` where the held codes of that stem carry their own year), and a stem two
770
787
  series share answers `ambiguous`. A host id of digits alone (`qf:926`, `fw:1001`) is a sequence
@@ -2114,6 +2114,7 @@ var entryOf = (c) => ({
2114
2114
  ...c.age !== null ? { age: c.age } : {},
2115
2115
  comp: c
2116
2116
  });
2117
+ var PLATFORM_FEIS_ID = /^[a-z]+:[^:\s]+$/;
2117
2118
 
2118
2119
  class CompResolver2 {
2119
2120
  rel;
@@ -2156,9 +2157,19 @@ class CompResolver2 {
2156
2157
  return this.ofSeries(asked, only, y, "series");
2157
2158
  }
2158
2159
  const m = /^([^:]+):(.*?)(\d{2})$/.exec(asked);
2159
- if (m !== null && !/^\d+$/.test(`${m[2]}${m[3]}`)) {
2160
- const [, token, stem, yy] = m;
2161
- const brands = await this.rel.feises.codeStem(token, stem);
2160
+ const stemmed = m !== null && !/^\d+$/.test(`${m[2]}${m[3]}`);
2161
+ const [, token, stem, yy] = m ?? [];
2162
+ const brands = stemmed ? await this.rel.feises.codeStem(token, stem) : [];
2163
+ const own = PLATFORM_FEIS_ID.test(asked) ? await this.compYears(asked) : [];
2164
+ if (own.length === 1) {
2165
+ const held = own[0];
2166
+ const y = year ?? held;
2167
+ const brand = brands.length === 1 ? brands[0] : null;
2168
+ if (y === held || brand === null)
2169
+ return { asked, feis_ids: [asked], brand_id: brand, year: y, held: y === held, via: "id" };
2170
+ return this.ofSeries(asked, brand, y, "id");
2171
+ }
2172
+ if (stemmed) {
2162
2173
  const y = year ?? 2000 + Number(yy);
2163
2174
  if (brands.length === 1)
2164
2175
  return this.ofSeries(asked, brands[0], y, "code-stem");
@@ -2184,6 +2195,10 @@ class CompResolver2 {
2184
2195
  const ids = year === null ? [] : db.query("SELECT feis_id FROM feis WHERE brand_id = ? AND year = ? ORDER BY feis_id").all(brand, year).map((r) => r.feis_id);
2185
2196
  return { asked, feis_ids: ids, brand_id: brand, year, held: ids.length > 0, via };
2186
2197
  }
2198
+ async compYears(feisId) {
2199
+ const db = await this.rel.indexDb("comps");
2200
+ return db.query("SELECT DISTINCT year FROM comp WHERE feis_id = ? AND year IS NOT NULL ORDER BY year").all(feisId).map((r) => r.year);
2201
+ }
2187
2202
  async years(brand) {
2188
2203
  if (brand === null)
2189
2204
  return [];
@@ -3953,10 +3968,12 @@ function needsRelease2(tag, c) {
3953
3968
  var FIELD_STATES2 = [
3954
3969
  "pending",
3955
3970
  "results",
3971
+ "merged",
3956
3972
  "held-out",
3957
3973
  "passed",
3958
3974
  "withdrawn"
3959
3975
  ];
3976
+ var DEPARTURE_REASONS2 = FIELD_STATES2.filter((s) => s !== "pending");
3960
3977
  var json = (s, empty) => typeof s === "string" && s !== "" ? JSON.parse(s) : empty;
3961
3978
  function fieldOf(r) {
3962
3979
  const countOnly = r.count_only === 1;
@@ -3989,6 +4006,7 @@ function fieldOf(r) {
3989
4006
  results_competitions: json(r.results_competitions, []),
3990
4007
  results_feis_ids: json(r.results_feis_ids, []),
3991
4008
  held_out: json(r.held_out, []),
4009
+ merged_into: r.merged_into ?? null,
3992
4010
  version: r.version,
3993
4011
  versions: json(r.versions, []),
3994
4012
  captured_date: r.captured_date,
@@ -4107,6 +4125,37 @@ class UpcomingFinder2 {
4107
4125
  const rows = db.query(sql).all(...args);
4108
4126
  return { tag: this.rel.tag, fields: rows.map(fieldOf) };
4109
4127
  }
4128
+ async departures(q = {}) {
4129
+ const reasons = q.reason === undefined ? DEPARTURE_REASONS2 : typeof q.reason === "string" ? [q.reason] : [...q.reason];
4130
+ const unknown = reasons.filter((r) => !DEPARTURE_REASONS2.includes(r));
4131
+ if (unknown.length > 0)
4132
+ throw new Error(`upcoming.departures: no departure reason ${unknown.map((r) => JSON.stringify(r)).join(", ")} (the reasons are ${DEPARTURE_REASONS2.join(", ")})`);
4133
+ const { tag, fields } = await this.fields({
4134
+ state: reasons,
4135
+ ...q.feis !== undefined ? { feis: q.feis } : {},
4136
+ ...q.field_id !== undefined ? { field_id: q.field_id } : {},
4137
+ ...q.from !== undefined ? { from: q.from } : {},
4138
+ ...q.to !== undefined ? { to: q.to } : {}
4139
+ });
4140
+ return {
4141
+ tag,
4142
+ departures: fields.map((f) => ({
4143
+ field_id: f.field_id,
4144
+ feis_id: f.feis_id,
4145
+ occurrence_id: f.occurrence_id,
4146
+ feis_brand_id: f.feis_brand_id,
4147
+ competition_code: f.competition_code,
4148
+ date_lo: f.date_lo,
4149
+ date_hi: f.date_hi,
4150
+ reason: f.state,
4151
+ competitions: f.results_competitions,
4152
+ feis_ids: f.state === "results" && f.count_only ? f.results_feis_ids : [],
4153
+ merged_into: f.merged_into,
4154
+ held_out: f.held_out,
4155
+ last_version: f.version
4156
+ }))
4157
+ };
4158
+ }
4110
4159
  async entrants(q) {
4111
4160
  const [field] = (await this.fields({ field_id: q.field_id })).fields;
4112
4161
  const db = await this.db("upcoming.entrants");
@@ -4944,4 +4993,4 @@ function staleWarning2(answered, latest, floor, tags = [], command = "idq") {
4944
4993
  return `answered from ${answered}. ${floorTag ?? `cut ${floor.cut}`} is required${why}: run \`${command} use ${floor.cut}\` to get it`;
4945
4994
  }
4946
4995
 
4947
- export { DATABASES2, planAsset2, sha256File2, unzstdTo2, NAME_LEVELS2, nameLevel2, NO_CALENDAR2, NO_UPCOMING2, FeisFinder2, ADVANCEMENT_DANCES2, entryRank2, FEIS_LOCAL_ID_FLOOR2, ResultFinder2, MajorsFinder2, LINEAGE_FILE2, linksOfLineage2, releaseSchoolGraph2, readsSpanishNames2, SchoolResolver2, initialsOnly2, nameOrderOf, DancerResolver2, readPopulation2, syllabusSourceOf2, canonicalOf2, CompResolver2, TabFinder2, PanelFinder2, FeisPanelFinder2, StaffFinder2, TeamFinder2, EventFinder2, defaultBuilders2, indexPath2, indexKey2, isCurrent2, buildIndex2, ensureIndex2, listIndexes2, readPin2, youngest2, dancerResolverOf2, RegistrationResolver2, ghRemote2, httpsRemote2, fallbackRemote2, setRemote2, defaultRemote, columnsOf2, FEATURES2, capabilityOf2, needsRelease2, FIELD_STATES2, UpcomingFinder2, resolveTag2, Release2, openRelease2, openCached2, DEFAULT_KEEP2, ReleaseManager2, releases2, LOCAL_DIR2, localRoot2, newerThan2, incomplete2, releaseVersion2, pickBuild2, buildTag2, lacks, openBuild2, sweepBuilds2, LATEST_CHECK_MS2, LATEST_CHECK_BUDGET_MS2, latestCheckPath2, readLatestCheck2, latestCheck2, within, staleWarning2 };
4996
+ export { DATABASES2, planAsset2, sha256File2, unzstdTo2, NAME_LEVELS2, nameLevel2, NO_CALENDAR2, NO_UPCOMING2, FeisFinder2, ADVANCEMENT_DANCES2, entryRank2, FEIS_LOCAL_ID_FLOOR2, ResultFinder2, MajorsFinder2, LINEAGE_FILE2, linksOfLineage2, releaseSchoolGraph2, readsSpanishNames2, SchoolResolver2, initialsOnly2, nameOrderOf, DancerResolver2, readPopulation2, syllabusSourceOf2, canonicalOf2, CompResolver2, TabFinder2, PanelFinder2, FeisPanelFinder2, StaffFinder2, TeamFinder2, EventFinder2, defaultBuilders2, indexPath2, indexKey2, isCurrent2, buildIndex2, ensureIndex2, listIndexes2, readPin2, youngest2, dancerResolverOf2, RegistrationResolver2, ghRemote2, httpsRemote2, fallbackRemote2, setRemote2, defaultRemote, columnsOf2, FEATURES2, capabilityOf2, needsRelease2, FIELD_STATES2, DEPARTURE_REASONS2, UpcomingFinder2, resolveTag2, Release2, openRelease2, openCached2, DEFAULT_KEEP2, ReleaseManager2, releases2, LOCAL_DIR2, localRoot2, newerThan2, incomplete2, releaseVersion2, pickBuild2, buildTag2, lacks, openBuild2, sweepBuilds2, LATEST_CHECK_MS2, LATEST_CHECK_BUDGET_MS2, latestCheckPath2, readLatestCheck2, latestCheck2, within, staleWarning2 };
package/dist/cli.js CHANGED
@@ -21,7 +21,7 @@ import {
21
21
  latestCheck2,
22
22
  within,
23
23
  staleWarning2
24
- } from "./chunks/index-5rnb63kw.js";
24
+ } from "./chunks/index-db578n4y.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]";
@@ -372,9 +372,9 @@ var VERBS = [
372
372
  "--version",
373
373
  "--brand"
374
374
  ],
375
- bool: ["--feises"],
375
+ bool: ["--feises", "--departures"],
376
376
  usage: [
377
- " upcoming [--release <spec>] [--state pending,results,held-out,passed,withdrawn] [--feis <id>]",
377
+ " upcoming [--release <spec>] [--state pending,results,merged,held-out,passed,withdrawn] [--feis <id>]",
378
378
  " [--from <date>] [--to <date>]",
379
379
  " the competitor lists' fields, each with its state, version",
380
380
  " and competition reading; --feis a feis, series or",
@@ -383,7 +383,12 @@ var VERBS = [
383
383
  " a field's rows (default its current version): each",
384
384
  " registrant's arm and candidates",
385
385
  " upcoming --feises [--state upcoming|no-results] [--brand <series>] [--from <date>] [--to <date>]",
386
- " the feis-index's occurrences the release holds no results of"
386
+ " the feis-index's occurrences the release holds no results of",
387
+ " upcoming --departures [--state results,merged,held-out,passed,withdrawn] [--feis <id>]",
388
+ " [--from <date>] [--to <date>]",
389
+ " how each field left the list: its reason and what it",
390
+ " became (results competitions, the surviving field, the",
391
+ " held-out competitions)"
387
392
  ]
388
393
  },
389
394
  {
@@ -1499,6 +1504,19 @@ async function main(argv, io = {}) {
1499
1504
  return a.feises.length === 0 ? 1 : 0;
1500
1505
  }
1501
1506
  const feis = p.flags.get("--feis");
1507
+ if (p.bools.has("--departures")) {
1508
+ const a = await rel.upcoming.departures({
1509
+ ...state !== undefined ? { reason: state.split(",") } : {},
1510
+ ...feis !== undefined ? { feis } : {},
1511
+ ...range
1512
+ });
1513
+ out(a, () => {
1514
+ for (const d of a.departures)
1515
+ say(` ${d.date_lo ?? "?"} ${d.field_id} ${d.reason} ${d.reason === "merged" ? `into ${d.merged_into}` : d.reason === "held-out" ? d.held_out.map((h) => `${h.competition} (${h.reason})`).join(", ") : d.competitions.join(", ")}`);
1516
+ say(`${a.departures.length} departure(s) in ${a.tag}`);
1517
+ });
1518
+ return a.departures.length === 0 ? 1 : 0;
1519
+ }
1502
1520
  const a = await rel.upcoming.fields({
1503
1521
  ...state !== undefined ? { state: state.split(",") } : {},
1504
1522
  ...feis !== undefined ? { feis } : {},
package/dist/index.js CHANGED
@@ -51,6 +51,7 @@ import {
51
51
  capabilityOf2,
52
52
  needsRelease2,
53
53
  FIELD_STATES2,
54
+ DEPARTURE_REASONS2,
54
55
  UpcomingFinder2,
55
56
  resolveTag2,
56
57
  Release2,
@@ -74,7 +75,7 @@ import {
74
75
  readLatestCheck2,
75
76
  latestCheck2,
76
77
  staleWarning2
77
- } from "./chunks/index-5rnb63kw.js";
78
+ } from "./chunks/index-db578n4y.js";
78
79
  import {
79
80
  cacheRoot2,
80
81
  repoOf2
@@ -94,6 +95,7 @@ export {
94
95
  CompResolver2 as CompResolver,
95
96
  DATABASES2 as DATABASES,
96
97
  DEFAULT_KEEP2 as DEFAULT_KEEP,
98
+ DEPARTURE_REASONS2 as DEPARTURE_REASONS,
97
99
  DancerResolver2 as DancerResolver,
98
100
  EventFinder2 as EventFinder,
99
101
  FEATURES2 as FEATURES,
@@ -130,6 +130,7 @@ export declare class CompResolver {
130
130
  constructor(rel: Release);
131
131
  place(feis: string, year?: number): Promise<FeisPlace>;
132
132
  private ofSeries;
133
+ private compYears;
133
134
  private years;
134
135
  private compsOf;
135
136
  private idsOf;
@@ -24,5 +24,5 @@ export { type Capability, FEATURES, type Feature, type SchemaProbe, capabilityOf
24
24
  export { type EntryRow, type StreamQuery, type StreamRow, type StreamScope, entryRank, } from "./stream.js";
25
25
  export { type FeisPanelSeat, FeisPanelFinder, PanelFinder, type PanelSeat, type RegisterRow, type TabAnswer, TabFinder, type TabQuery, type TabRow, type TabStreamQuery, } from "./tabs.js";
26
26
  export { EventFinder, type EventYear, type EventYearQuery, type ResultRoster, type RosterMember, type RosterTeam, TeamFinder, type TeamResult, } from "./teams.js";
27
- export { type EntrantCandidate, FIELD_STATES, type FieldComp, type FieldState, type HeldOut, 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";
27
+ 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";
28
28
  export { ARCHIVE_TAG, type ArchiveTag, type ReleaseSpec, compareTagsNewestFirst, cutOf, parseArchiveTag, parseReleaseSpec, pickTag, renderArchiveTag, } from "./tag.js";
@@ -1,6 +1,8 @@
1
1
  import type { Release } from "./release.js";
2
- export type FieldState = "pending" | "results" | "held-out" | "passed" | "withdrawn";
2
+ export type FieldState = "pending" | "results" | "merged" | "held-out" | "passed" | "withdrawn";
3
3
  export declare const FIELD_STATES: readonly FieldState[];
4
+ export type DepartureReason = Exclude<FieldState, "pending">;
5
+ export declare const DEPARTURE_REASONS: readonly DepartureReason[];
4
6
  export interface HeldOut {
5
7
  competition: string;
6
8
  reason: string;
@@ -61,6 +63,7 @@ export interface UpcomingField {
61
63
  results_competitions: string[];
62
64
  results_feis_ids: string[];
63
65
  held_out: HeldOut[];
66
+ merged_into: string | null;
64
67
  version: string;
65
68
  versions: string[];
66
69
  captured_date: string;
@@ -115,6 +118,32 @@ export interface UpcomingFieldQuery {
115
118
  from?: string;
116
119
  to?: string;
117
120
  }
121
+ export interface UpcomingDepartureQuery {
122
+ reason?: DepartureReason | readonly DepartureReason[];
123
+ feis?: string;
124
+ field_id?: string;
125
+ from?: string;
126
+ to?: string;
127
+ }
128
+ export interface UpcomingDeparture {
129
+ field_id: string;
130
+ feis_id: string;
131
+ occurrence_id: string | null;
132
+ feis_brand_id: string | null;
133
+ competition_code: string | null;
134
+ date_lo: string | null;
135
+ date_hi: string | null;
136
+ reason: DepartureReason;
137
+ competitions: string[];
138
+ feis_ids: string[];
139
+ merged_into: string | null;
140
+ held_out: HeldOut[];
141
+ last_version: string;
142
+ }
143
+ export interface UpcomingDepartureAnswer {
144
+ tag: string;
145
+ departures: UpcomingDeparture[];
146
+ }
118
147
  export interface UpcomingEntrantQuery {
119
148
  field_id: string;
120
149
  version?: string;
@@ -140,5 +169,6 @@ export declare class UpcomingFinder {
140
169
  private db;
141
170
  feises(q?: UpcomingFeisQuery): Promise<UpcomingFeisAnswer>;
142
171
  fields(q?: UpcomingFieldQuery): Promise<UpcomingFieldAnswer>;
172
+ departures(q?: UpcomingDepartureQuery): Promise<UpcomingDepartureAnswer>;
143
173
  entrants(q: UpcomingEntrantQuery): Promise<UpcomingEntrantAnswer>;
144
174
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finbheara/archive-client",
3
- "version": "0.28.0",
3
+ "version": "0.30.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",