@finbheara/archive-client 0.31.0 → 0.33.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
@@ -88,8 +88,10 @@ c.features; // [{ feature: "dancers", state: "full" | "missing", missing: [...
88
88
  | `feises`, `comps` | `feises.find`, `feises.series`, `comps.*`, `registrations.resolve` | 265 |
89
89
  | `comps.dance` | the `dance` a comp of `comps.*` carries: null, never refused, without it | 270 |
90
90
  | `results.stream`, `majors.stream` | `results.stream`, `results.entries` | 255 |
91
+ | `results.stream.competition`, `majors.stream.competition` | the competition fields a streamed row carries (`competition_line`, `dance_printed`, `dance`, `recalled_only`, `comp_flags`, `competitors_basis`); absent, never refused, without them | 270 |
91
92
  | `feises.dates` | `feises.dates` | 245 |
92
93
  | `dancers.records` | `dancers.records` | 215 |
94
+ | `dancers.records.names` | the `names_printed` a record of `dancers.records` carries; absent, never refused, without it | 215 |
93
95
  | `dancers.years` | `dancers.years` | 245 |
94
96
  | `majors.tabs` | `majors.tabulated` | 215 |
95
97
  | `teams` | `teams.results`, `teams.rosters`, `teams.resultRosters` | 260 |
@@ -97,6 +99,7 @@ c.features; // [{ feature: "dancers", state: "full" | "missing", missing: [...
97
99
  | `events` | `events.years` | 260 |
98
100
  | `upcoming.feises` | `upcoming.feises`, and the `next` of `feises.series` (null with the reason without it) | 285 |
99
101
  | `upcoming.fields`, `upcoming.entrants` | `upcoming.fields`, `upcoming.entrants` (285 and 290 carry the tables empty) | 291 |
102
+ | `upcoming.fields.instance` | the `feis_instance_id` `upcoming.fields` carries; absent, never refused, without it | 320 |
100
103
  | `tabs`, `panels` | `tabs.query`, `tabs.stream`; `panels.query` | 184 |
101
104
  | `feis-panels` | `feisPanels.query` | 260 |
102
105
  | `staff.adjudicators` | `staff.adjudicators` | 230 |
@@ -573,6 +576,25 @@ competition beside its own fields: `competition`, `event`, `year`, `tier`, `comp
573
576
  `(file, line)` is one row, where an `address` may carry one row per section). Rows come in the
574
577
  release's own order, a competition's rows together.
575
578
 
579
+ THE COMPETITION FIELDS, where the release carries them (`rel.capability("results.stream.competition")`,
580
+ `"majors.stream.competition"` for scope `majors`), are on every row, null meaning no value:
581
+
582
+ | field | what it is |
583
+ | --- | --- |
584
+ | `competition_line` | the competition's own line in `file`: `(file, competition_line)` is one competition of the release exactly |
585
+ | `dance_printed` | the dance field as the page printed it. A platform that prints no title prints only this, so a filter on the kind of competition reads `title` and `dance_printed` together |
586
+ | `dance` | the competition's one dance as the release reads it (`reel`), null where it reads none |
587
+ | `recalled_only` | true where the page printed the recalled dancers only |
588
+ | `comp_flags` | what the page printed about itself, int-packed, one bit a flag (bit 10: recalled only). The release's `DATA-DICTIONARY.md` carries the register of every bit, and a published bit never moves |
589
+ | `competitors_basis` | what `competitors` stands on: `printed` (the page printed a competed count), `counted` (the page printed the whole field, its cards counted), `entered` or `registered` (only the top was printed: the page's entered or registered count), null where there is no number |
590
+
591
+ A release without them streams every row with the six fields ABSENT, never null.
592
+
593
+ THE COMPETITION'S KEY is `competition` (`<event-year>.<comp>` at a major, `<platform>:<host>.<comp>`
594
+ at a feis), which holds across releases. Inside one release `(file, competition_line)` is exact: a
595
+ competition captured twice (two files of one platform) carries one `competition` in both and two
596
+ `(file, competition_line)` keys, and `primary_source` names the copy another source stands in for.
597
+
576
598
  The scopes: `majors` reads the majors (tiers T1 to T3), `transcribed` the transcribed rows of
577
599
  every tier, `platform` every row a results platform published, `all` every row.
578
600
 
@@ -586,13 +608,20 @@ the place the address's recall row carries.
586
608
 
587
609
  ```ts
588
610
  await rel.dancers.records(); // every dancer: display, appearances, birth_lo, birth_hi,
589
- // birth_source, birth_conflict, genders, scope
611
+ // birth_source, birth_conflict, genders, scope,
612
+ // names_printed (dancers.records.names)
590
613
  await rel.dancers.records({ ids: [1001] });
591
614
  await rel.dancers.years({ ids: [1001], year: 2025 }); // region, region_source, nationals, geohash
592
615
  await rel.feises.dates(); // every feis id with its first and last day
593
616
  await rel.majors.tabulated({ min_true_round: 3 }); // the majors addresses marked to round 3 or later
594
617
  ```
595
618
 
619
+ `dancers.records` covers EVERY dancer the release gives an id: a majors dancer, a feis row attached
620
+ to one, and a feis-local dancer (`scope: "per-release"`, valid in its release only). It is not the
621
+ majors' dancers only. A caller who wants those keeps the `dancer_id`s a `scope: "majors"` stream
622
+ carries. `names_printed` is every distinct name the dancer's rows print, sorted, over every row of
623
+ the dancer, feis rows included.
624
+
596
625
  ## Marks sheets and panels
597
626
 
598
627
  ```ts
@@ -669,7 +698,8 @@ o.feises; // [{ occurrence_id, occurrence_ids, name_printed, date_lo, date_hi
669
698
  // feis_brand_id, feis_instance_id, series_basis, brand_candidates, captured_id, state }]
670
699
  const f = await rel.upcoming.fields({ state: "pending" }); // state, feis, field_id, from, to
671
700
  f.fields; // [{ field_id, feis_id, source, host_id, competition_code, count_only, occurrence_id,
672
- // name_printed, date_lo, date_hi, year, feis_brand_id, comp, state, flags,
701
+ // name_printed, date_lo, date_hi, year, feis_brand_id, feis_instance_id, comp,
702
+ // state, flags,
673
703
  // results_competitions, results_feis_ids, held_out, merged_into, version, versions,
674
704
  // captured_date, sha256,
675
705
  // limits, entrants, stated_rows, printed_total }]
@@ -684,7 +714,10 @@ d.departures; // [{ field_id, feis_id, occurrence_id, feis_brand_id, competition
684
714
  `upcoming.feises` is every occurrence of the cut's feis-index the release holds no results of:
685
715
  `upcoming` (dated on or after the cut's day) or `no-results` (before it), each with every
686
716
  platform's or calendar's listing (`hosts`) and its feis series where one was named (`series_basis`
687
- says how; `none` leaves `feis_brand_id` null, the unjoined ones).
717
+ says how; `none` leaves `feis_brand_id` null, the unjoined ones). An occurrence whose series one
718
+ filed brand answers is given its `feis_instance_id` when it is ANNOUNCED, before any capture of it
719
+ is held (`series_basis` `announced` on every later release), and its captures land on that
720
+ instance when they arrive, so a field's `feis_instance_id` is the id its results will carry.
688
721
 
689
722
  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`
690
723
  (a capture id; `versions` lists them), never a new field. `state` says how it stands on the cut:
@@ -465,6 +465,32 @@ var FEATURES2 = [
465
465
  since: 255,
466
466
  calls: "results.stream, results.entries (scope majors)"
467
467
  },
468
+ {
469
+ feature: "results.stream.competition",
470
+ needs: [
471
+ "results:results.competition_line",
472
+ "results:competitions.dance_printed",
473
+ "results:competitions.dance",
474
+ "results:competitions.recalled_only",
475
+ "results:competitions.flags",
476
+ "results:competitions.competitors_basis"
477
+ ],
478
+ since: 270,
479
+ calls: "the competition fields a row of results.stream and results.entries carries (competition_line, dance_printed, dance, recalled_only, comp_flags, competitors_basis; scopes transcribed, platform, all): absent, never refused, on a release without them"
480
+ },
481
+ {
482
+ feature: "majors.stream.competition",
483
+ needs: [
484
+ "majors:results.competition_line",
485
+ "majors:competitions.dance_printed",
486
+ "majors:competitions.dance",
487
+ "majors:competitions.recalled_only",
488
+ "majors:competitions.flags",
489
+ "majors:competitions.competitors_basis"
490
+ ],
491
+ since: 270,
492
+ calls: "the same competition fields on a row of results.stream and results.entries (scope majors): absent, never refused, on a release without them"
493
+ },
468
494
  {
469
495
  feature: "majors.tabs",
470
496
  needs: ["majors:tabs.address", "majors:tabs.true_round"],
@@ -491,6 +517,12 @@ var FEATURES2 = [
491
517
  since: 215,
492
518
  calls: "dancers.records"
493
519
  },
520
+ {
521
+ feature: "dancers.records.names",
522
+ needs: ["results:dancers.names_printed"],
523
+ since: 215,
524
+ calls: "the `names_printed` a record of dancers.records carries: absent, never refused, on a release without it"
525
+ },
494
526
  {
495
527
  feature: "dancers.years",
496
528
  needs: [
@@ -650,6 +682,12 @@ var FEATURES2 = [
650
682
  since: 291,
651
683
  calls: "upcoming.fields (285 and 290 carry the table empty)"
652
684
  },
685
+ {
686
+ feature: "upcoming.fields.instance",
687
+ needs: ["results:upcoming_fields.feis_instance_id"],
688
+ since: 320,
689
+ calls: "upcoming.fields' `feis_instance_id` (absent, never null, without it)"
690
+ },
653
691
  {
654
692
  feature: "upcoming.entrants",
655
693
  needs: [
@@ -1225,9 +1263,12 @@ var COLUMNS = `r.address AS address, r.competition AS competition, c.event AS ev
1225
1263
  r.yob_printed AS yob_printed, r.points_printed AS points_printed,
1226
1264
  r.platform_dancer_id AS platform_dancer_id, r.primary_source AS primary_source,
1227
1265
  r.file AS file, r.line AS line`;
1266
+ var COMPETITION_COLUMNS = `, r.competition_line AS competition_line,
1267
+ c.dance_printed AS dance_printed, c.dance AS dance, c.recalled_only AS recalled_only,
1268
+ c.flags AS comp_flags, c.competitors_basis AS competitors_basis`;
1228
1269
  var TRANSCRIBED = "('transcript', 'manifested')";
1229
1270
  var scopeDb = (s) => s === "majors" ? "majors" : "results";
1230
- function streamSql(q) {
1271
+ function streamSql(q, shape = {}) {
1231
1272
  const where = [];
1232
1273
  const args = [];
1233
1274
  const list = (col, v, nocase = false) => {
@@ -1268,7 +1309,7 @@ function streamSql(q) {
1268
1309
  if (q.primary === true)
1269
1310
  where.push("(r.primary_source IS NULL OR r.primary_source = '')");
1270
1311
  return {
1271
- sql: `SELECT ${COLUMNS} FROM results r JOIN competitions c ON c.file = r.file AND c.line = r.competition_line${where.length === 0 ? "" : ` WHERE ${where.join(" AND ")}`} ORDER BY r.file, r.line`,
1312
+ sql: `SELECT ${COLUMNS}${shape.competition === true ? COMPETITION_COLUMNS : ""} FROM results r JOIN competitions c ON c.file = r.file AND c.line = r.competition_line${where.length === 0 ? "" : ` WHERE ${where.join(" AND ")}`} ORDER BY r.file, r.line`,
1272
1313
  args
1273
1314
  };
1274
1315
  }
@@ -1280,10 +1321,16 @@ function feisDates(db) {
1280
1321
  }
1281
1322
  var text = (v) => v === null || v === "" ? null : v;
1282
1323
  function rowOf(r, dates) {
1283
- const { atts, withdrawn, ...rest } = r;
1324
+ const { atts, withdrawn, recalled_only, ...rest } = r;
1284
1325
  const d = dates.get(r.event);
1285
1326
  return {
1286
1327
  ...rest,
1328
+ ..."competition_line" in r ? {
1329
+ dance_printed: text(r.dance_printed ?? null),
1330
+ dance: text(r.dance ?? null),
1331
+ recalled_only: recalled_only === 1,
1332
+ competitors_basis: text(r.competitors_basis ?? null)
1333
+ } : {},
1287
1334
  feis_id: d === undefined ? null : r.event,
1288
1335
  feis_date_lo: d?.lo ?? null,
1289
1336
  feis_date_hi: d?.hi ?? null,
@@ -1294,9 +1341,9 @@ function rowOf(r, dates) {
1294
1341
  dancer_scope: r.dancer_id === null ? null : r.dancer_id >= FEIS_LOCAL_ID_FLOOR2 ? "per-release" : "stable"
1295
1342
  };
1296
1343
  }
1297
- async function* streamRows(db, q) {
1344
+ async function* streamRows(db, q, shape = {}) {
1298
1345
  const dates = feisDates(db);
1299
- const { sql, args } = streamSql(q);
1346
+ const { sql, args } = streamSql(q, shape);
1300
1347
  const stmt = db.prepare(sql);
1301
1348
  try {
1302
1349
  for (const r of stmt.iterate(...args))
@@ -1305,9 +1352,9 @@ async function* streamRows(db, q) {
1305
1352
  stmt.finalize();
1306
1353
  }
1307
1354
  }
1308
- async function* entryRows(db, q) {
1355
+ async function* entryRows(db, q, shape = {}) {
1309
1356
  const dates = feisDates(db);
1310
- const { sql, args } = streamSql(q);
1357
+ const { sql, args } = streamSql(q, shape);
1311
1358
  const stmt = db.prepare(sql);
1312
1359
  let file = null;
1313
1360
  let held = new Map;
@@ -1403,13 +1450,16 @@ class ResultFinder2 {
1403
1450
  const db = scopeDb(q.scope);
1404
1451
  await this.rel.fetch(db);
1405
1452
  await this.rel.require(db === "majors" ? "majors.stream" : "results.stream");
1406
- return this.rel.db(db);
1453
+ const competition = (await this.rel.capability(`${db}.stream.competition`)).state === "full";
1454
+ return { db: this.rel.db(db), shape: { competition } };
1407
1455
  }
1408
1456
  async* stream(q) {
1409
- yield* streamRows(await this.streamDb(q), q);
1457
+ const { db, shape } = await this.streamDb(q);
1458
+ yield* streamRows(db, q, shape);
1410
1459
  }
1411
1460
  async* entries(q) {
1412
- yield* entryRows(await this.streamDb(q), q);
1461
+ const { db, shape } = await this.streamDb(q);
1462
+ yield* entryRows(db, q, shape);
1413
1463
  }
1414
1464
  }
1415
1465
 
@@ -2065,16 +2115,18 @@ class DancerResolver2 {
2065
2115
  await this.rel.fetch("results");
2066
2116
  await this.rel.require("dancers.records");
2067
2117
  const db = this.rel.db("results");
2068
- const sql = "SELECT dancer_id, display, appearances, birth_lo, birth_hi, birth_source, birth_conflict, genders FROM dancers";
2118
+ const names = (await this.rel.capability("dancers.records.names")).state === "full";
2119
+ const sql = `SELECT dancer_id, display, appearances, birth_lo, birth_hi, birth_source, birth_conflict, genders${names ? ", names_printed" : ""} FROM dancers`;
2069
2120
  const rows = q.ids === undefined ? db.query(`${sql} ORDER BY dancer_id`).all() : idChunks(q.ids).flatMap((c) => db.query(`${sql} WHERE dancer_id IN (${c.map(() => "?").join(",")})`).all(...c));
2070
2121
  rows.sort((a, b) => a.dancer_id - b.dancer_id);
2071
2122
  return {
2072
2123
  tag: this.rel.tag,
2073
- records: rows.map((r) => ({
2124
+ records: rows.map(({ names_printed, ...r }) => ({
2074
2125
  ...r,
2075
2126
  scope: r.dancer_id >= FEIS_LOCAL_ID_FLOOR2 ? "per-release" : "stable",
2076
2127
  birth_conflict: r.birth_conflict === 1,
2077
- genders: r.genders === "" ? [] : r.genders.split(",")
2128
+ genders: r.genders === "" ? [] : r.genders.split(","),
2129
+ ...names_printed === undefined ? {} : { names_printed: JSON.parse(names_printed) }
2078
2130
  }))
2079
2131
  };
2080
2132
  }
@@ -4089,6 +4141,7 @@ function fieldOf(r) {
4089
4141
  date_hi: r.date_hi ?? null,
4090
4142
  year: r.year,
4091
4143
  feis_brand_id: r.feis_brand_id ?? null,
4144
+ ...r.feis_instance_id === undefined ? {} : { feis_instance_id: r.feis_instance_id ?? null },
4092
4145
  comp: r.comp_arm === null ? null : {
4093
4146
  arm: r.comp_arm,
4094
4147
  source: r.comp_source ?? null,
package/dist/cli.js CHANGED
@@ -21,7 +21,7 @@ import {
21
21
  latestCheck2,
22
22
  within,
23
23
  staleWarning2
24
- } from "./chunks/index-cgsefd2y.js";
24
+ } from "./chunks/index-g8ab43kz.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]";
package/dist/index.js CHANGED
@@ -76,7 +76,7 @@ import {
76
76
  readLatestCheck2,
77
77
  latestCheck2,
78
78
  staleWarning2
79
- } from "./chunks/index-cgsefd2y.js";
79
+ } from "./chunks/index-g8ab43kz.js";
80
80
  import {
81
81
  cacheRoot2,
82
82
  repoOf2
@@ -193,6 +193,7 @@ export interface DancerBulkRecord {
193
193
  birth_source: string;
194
194
  birth_conflict: boolean;
195
195
  genders: string[];
196
+ names_printed?: string[];
196
197
  }
197
198
  export interface DancerYear {
198
199
  dancer_id: number;
@@ -22,7 +22,7 @@ export { type SchoolAnswer, type SchoolCandidate, type SchoolQuery, SchoolResolv
22
22
  export { LINEAGE_FILE, type SchoolLineAnswer, type SchoolLinksAnswer, linksOfLineage, releaseSchoolGraph, } from "./school-links.js";
23
23
  export type { LineCandidate, LinkPath, LinkPolicy, LinkQuery, LinkStep, PathFlags, SchoolLink, StepFlags, StepPolicy, } from "@finbheara/domain/school-graph";
24
24
  export { type Capability, FEATURES, type Feature, type SchemaProbe, capabilityOf, columnsOf, needsRelease, } from "./schema.js";
25
- export { type EntryRow, type StreamQuery, type StreamRow, type StreamScope, entryRank, } from "./stream.js";
25
+ export { type EntryRow, type StreamCompetition, type StreamQuery, type StreamRow, type StreamScope, entryRank, } from "./stream.js";
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";
@@ -11,7 +11,15 @@ export interface StreamQuery {
11
11
  school_id?: string;
12
12
  primary?: boolean;
13
13
  }
14
- export interface StreamRow {
14
+ export interface StreamCompetition {
15
+ competition_line: number;
16
+ dance_printed: string | null;
17
+ dance: string | null;
18
+ recalled_only: boolean;
19
+ comp_flags: number;
20
+ competitors_basis: string | null;
21
+ }
22
+ export interface StreamRow extends Partial<StreamCompetition> {
15
23
  address: string;
16
24
  competition: string;
17
25
  event: string;
@@ -60,8 +68,11 @@ export interface EntryRow extends StreamRow {
60
68
  recall_place: number | null;
61
69
  }
62
70
  export declare function entryRank(section: string): number;
71
+ export interface StreamShape {
72
+ competition?: boolean;
73
+ }
63
74
  export declare const scopeDb: (s: StreamScope) => "majors" | "results";
64
- export declare function streamSql(q: StreamQuery): {
75
+ export declare function streamSql(q: StreamQuery, shape?: StreamShape): {
65
76
  sql: string;
66
77
  args: (string | number)[];
67
78
  };
@@ -69,5 +80,5 @@ export declare function feisDates(db: Database): Map<string, {
69
80
  lo: string | null;
70
81
  hi: string | null;
71
82
  }>;
72
- export declare function streamRows(db: Database, q: StreamQuery): AsyncGenerator<StreamRow>;
73
- export declare function entryRows(db: Database, q: StreamQuery): AsyncGenerator<EntryRow>;
83
+ export declare function streamRows(db: Database, q: StreamQuery, shape?: StreamShape): AsyncGenerator<StreamRow>;
84
+ export declare function entryRows(db: Database, q: StreamQuery, shape?: StreamShape): AsyncGenerator<EntryRow>;
@@ -57,6 +57,7 @@ export interface UpcomingField {
57
57
  date_hi: string | null;
58
58
  year: number;
59
59
  feis_brand_id: string | null;
60
+ feis_instance_id?: string | null;
60
61
  comp: FieldComp | null;
61
62
  state: FieldState;
62
63
  flags: string[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finbheara/archive-client",
3
- "version": "0.31.0",
3
+ "version": "0.33.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",