@finbheara/archive-client 0.31.0 → 0.32.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 |
@@ -573,6 +575,25 @@ competition beside its own fields: `competition`, `event`, `year`, `tier`, `comp
573
575
  `(file, line)` is one row, where an `address` may carry one row per section). Rows come in the
574
576
  release's own order, a competition's rows together.
575
577
 
578
+ THE COMPETITION FIELDS, where the release carries them (`rel.capability("results.stream.competition")`,
579
+ `"majors.stream.competition"` for scope `majors`), are on every row, null meaning no value:
580
+
581
+ | field | what it is |
582
+ | --- | --- |
583
+ | `competition_line` | the competition's own line in `file`: `(file, competition_line)` is one competition of the release exactly |
584
+ | `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 |
585
+ | `dance` | the competition's one dance as the release reads it (`reel`), null where it reads none |
586
+ | `recalled_only` | true where the page printed the recalled dancers only |
587
+ | `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 |
588
+ | `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 |
589
+
590
+ A release without them streams every row with the six fields ABSENT, never null.
591
+
592
+ THE COMPETITION'S KEY is `competition` (`<event-year>.<comp>` at a major, `<platform>:<host>.<comp>`
593
+ at a feis), which holds across releases. Inside one release `(file, competition_line)` is exact: a
594
+ competition captured twice (two files of one platform) carries one `competition` in both and two
595
+ `(file, competition_line)` keys, and `primary_source` names the copy another source stands in for.
596
+
576
597
  The scopes: `majors` reads the majors (tiers T1 to T3), `transcribed` the transcribed rows of
577
598
  every tier, `platform` every row a results platform published, `all` every row.
578
599
 
@@ -586,13 +607,20 @@ the place the address's recall row carries.
586
607
 
587
608
  ```ts
588
609
  await rel.dancers.records(); // every dancer: display, appearances, birth_lo, birth_hi,
589
- // birth_source, birth_conflict, genders, scope
610
+ // birth_source, birth_conflict, genders, scope,
611
+ // names_printed (dancers.records.names)
590
612
  await rel.dancers.records({ ids: [1001] });
591
613
  await rel.dancers.years({ ids: [1001], year: 2025 }); // region, region_source, nationals, geohash
592
614
  await rel.feises.dates(); // every feis id with its first and last day
593
615
  await rel.majors.tabulated({ min_true_round: 3 }); // the majors addresses marked to round 3 or later
594
616
  ```
595
617
 
618
+ `dancers.records` covers EVERY dancer the release gives an id: a majors dancer, a feis row attached
619
+ to one, and a feis-local dancer (`scope: "per-release"`, valid in its release only). It is not the
620
+ majors' dancers only. A caller who wants those keeps the `dancer_id`s a `scope: "majors"` stream
621
+ carries. `names_printed` is every distinct name the dancer's rows print, sorted, over every row of
622
+ the dancer, feis rows included.
623
+
596
624
  ## Marks sheets and panels
597
625
 
598
626
  ```ts
@@ -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: [
@@ -1225,9 +1257,12 @@ var COLUMNS = `r.address AS address, r.competition AS competition, c.event AS ev
1225
1257
  r.yob_printed AS yob_printed, r.points_printed AS points_printed,
1226
1258
  r.platform_dancer_id AS platform_dancer_id, r.primary_source AS primary_source,
1227
1259
  r.file AS file, r.line AS line`;
1260
+ var COMPETITION_COLUMNS = `, r.competition_line AS competition_line,
1261
+ c.dance_printed AS dance_printed, c.dance AS dance, c.recalled_only AS recalled_only,
1262
+ c.flags AS comp_flags, c.competitors_basis AS competitors_basis`;
1228
1263
  var TRANSCRIBED = "('transcript', 'manifested')";
1229
1264
  var scopeDb = (s) => s === "majors" ? "majors" : "results";
1230
- function streamSql(q) {
1265
+ function streamSql(q, shape = {}) {
1231
1266
  const where = [];
1232
1267
  const args = [];
1233
1268
  const list = (col, v, nocase = false) => {
@@ -1268,7 +1303,7 @@ function streamSql(q) {
1268
1303
  if (q.primary === true)
1269
1304
  where.push("(r.primary_source IS NULL OR r.primary_source = '')");
1270
1305
  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`,
1306
+ 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
1307
  args
1273
1308
  };
1274
1309
  }
@@ -1280,10 +1315,16 @@ function feisDates(db) {
1280
1315
  }
1281
1316
  var text = (v) => v === null || v === "" ? null : v;
1282
1317
  function rowOf(r, dates) {
1283
- const { atts, withdrawn, ...rest } = r;
1318
+ const { atts, withdrawn, recalled_only, ...rest } = r;
1284
1319
  const d = dates.get(r.event);
1285
1320
  return {
1286
1321
  ...rest,
1322
+ ..."competition_line" in r ? {
1323
+ dance_printed: text(r.dance_printed ?? null),
1324
+ dance: text(r.dance ?? null),
1325
+ recalled_only: recalled_only === 1,
1326
+ competitors_basis: text(r.competitors_basis ?? null)
1327
+ } : {},
1287
1328
  feis_id: d === undefined ? null : r.event,
1288
1329
  feis_date_lo: d?.lo ?? null,
1289
1330
  feis_date_hi: d?.hi ?? null,
@@ -1294,9 +1335,9 @@ function rowOf(r, dates) {
1294
1335
  dancer_scope: r.dancer_id === null ? null : r.dancer_id >= FEIS_LOCAL_ID_FLOOR2 ? "per-release" : "stable"
1295
1336
  };
1296
1337
  }
1297
- async function* streamRows(db, q) {
1338
+ async function* streamRows(db, q, shape = {}) {
1298
1339
  const dates = feisDates(db);
1299
- const { sql, args } = streamSql(q);
1340
+ const { sql, args } = streamSql(q, shape);
1300
1341
  const stmt = db.prepare(sql);
1301
1342
  try {
1302
1343
  for (const r of stmt.iterate(...args))
@@ -1305,9 +1346,9 @@ async function* streamRows(db, q) {
1305
1346
  stmt.finalize();
1306
1347
  }
1307
1348
  }
1308
- async function* entryRows(db, q) {
1349
+ async function* entryRows(db, q, shape = {}) {
1309
1350
  const dates = feisDates(db);
1310
- const { sql, args } = streamSql(q);
1351
+ const { sql, args } = streamSql(q, shape);
1311
1352
  const stmt = db.prepare(sql);
1312
1353
  let file = null;
1313
1354
  let held = new Map;
@@ -1403,13 +1444,16 @@ class ResultFinder2 {
1403
1444
  const db = scopeDb(q.scope);
1404
1445
  await this.rel.fetch(db);
1405
1446
  await this.rel.require(db === "majors" ? "majors.stream" : "results.stream");
1406
- return this.rel.db(db);
1447
+ const competition = (await this.rel.capability(`${db}.stream.competition`)).state === "full";
1448
+ return { db: this.rel.db(db), shape: { competition } };
1407
1449
  }
1408
1450
  async* stream(q) {
1409
- yield* streamRows(await this.streamDb(q), q);
1451
+ const { db, shape } = await this.streamDb(q);
1452
+ yield* streamRows(db, q, shape);
1410
1453
  }
1411
1454
  async* entries(q) {
1412
- yield* entryRows(await this.streamDb(q), q);
1455
+ const { db, shape } = await this.streamDb(q);
1456
+ yield* entryRows(db, q, shape);
1413
1457
  }
1414
1458
  }
1415
1459
 
@@ -2065,16 +2109,18 @@ class DancerResolver2 {
2065
2109
  await this.rel.fetch("results");
2066
2110
  await this.rel.require("dancers.records");
2067
2111
  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";
2112
+ const names = (await this.rel.capability("dancers.records.names")).state === "full";
2113
+ const sql = `SELECT dancer_id, display, appearances, birth_lo, birth_hi, birth_source, birth_conflict, genders${names ? ", names_printed" : ""} FROM dancers`;
2069
2114
  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
2115
  rows.sort((a, b) => a.dancer_id - b.dancer_id);
2071
2116
  return {
2072
2117
  tag: this.rel.tag,
2073
- records: rows.map((r) => ({
2118
+ records: rows.map(({ names_printed, ...r }) => ({
2074
2119
  ...r,
2075
2120
  scope: r.dancer_id >= FEIS_LOCAL_ID_FLOOR2 ? "per-release" : "stable",
2076
2121
  birth_conflict: r.birth_conflict === 1,
2077
- genders: r.genders === "" ? [] : r.genders.split(",")
2122
+ genders: r.genders === "" ? [] : r.genders.split(","),
2123
+ ...names_printed === undefined ? {} : { names_printed: JSON.parse(names_printed) }
2078
2124
  }))
2079
2125
  };
2080
2126
  }
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-dy6yvpgk.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-dy6yvpgk.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>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finbheara/archive-client",
3
- "version": "0.31.0",
3
+ "version": "0.32.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",