@finbheara/archive-client 0.11.0 → 0.12.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
@@ -87,7 +87,6 @@ c.features; // [{ feature: "dancers", state: "full" | "missing", missing: [...
87
87
  | `feises`, `comps` | `feises.find`, `feises.series`, `comps.*`, `registrations.resolve` | 265 |
88
88
  | `comps.dance` | the `dance` a comp of `comps.*` carries: null, never refused, without it | 270 |
89
89
  | `results.stream`, `majors.stream` | `results.stream`, `results.entries` | 255 |
90
- | `results.printed_age` | the `printed_age` a row of `results.stream` and `results.entries` carries: null, never refused, without it | 271 |
91
90
  | `feises.dates` | `feises.dates` | 245 |
92
91
  | `dancers.records` | `dancers.records` | 215 |
93
92
  | `dancers.years` | `dancers.years` | 245 |
@@ -282,7 +281,13 @@ words):
282
281
  followed through the redirects, a print read by `rel.schools`), and none of them is the asked
283
282
  school or a school its line had joined by that year (lineage edges dated in or before the asked
284
283
  year). A dancer with no school row, or a print no school reads, is unknown and never refuted. A
285
- dancer that ever danced for the asked school's line is never refuted by the school.
284
+ dancer that ever danced for the asked school's line is never refuted by the school. For an
285
+ adult-category question (the asked band is the adult band, read by `@finbheara/domain`:
286
+ `Adult`, `Advanced Adult`, `Adult Beginner`, `Open Adult`, `Adult Masters`), a row the
287
+ candidate danced under 18 (at every birth year of its window, in the row's latest year)
288
+ refutes nothing where the candidate is 18 or over in the asked year, however long ago the row
289
+ is: many adult dancers danced as children at another school. An and-over band (`O18`,
290
+ `18 & Over`, `18+`), `Senior`, a year of birth and a youth question are unchanged.
286
291
  - `age`: the birth window misses the asked band even with one band's dance-up (`DANCE_UP_YEARS`
287
292
  of `@finbheara/domain/age`: a dancer may enter the band one year older than the dancer's own). A
288
293
  candidate the age set aside that the dance-up holds stays open (`r.initials.heldByDanceUp`).
@@ -389,15 +394,11 @@ competition beside its own fields: `competition`, `event`, `year`, `tier`, `comp
389
394
  `date_printed`, `source`, `round`, and at a feis its `feis_id`, `feis_date_lo` and
390
395
  `feis_date_hi`. Its own: `address`, `section`, `card`, `place`, `place_printed`, `tie`,
391
396
  `relative_place`, `atts`, `withdrawn`, `qualifier`, `name_printed`, `name`, `school_printed`,
392
- `school_loc`, `school_id`, `dancer_id`, `dancer_scope`, `yob`, `yob_printed`, `printed_age`,
393
- `points_printed`, `platform_dancer_id`, `primary_source`, and its key in the release, `file` and `line` (one
397
+ `school_loc`, `school_id`, `dancer_id`, `dancer_scope`, `yob`, `yob_printed`, `points_printed`,
398
+ `platform_dancer_id`, `primary_source`, and its key in the release, `file` and `line` (one
394
399
  `(file, line)` is one row, where an `address` may carry one row per section). Rows come in the
395
400
  release's own order, a competition's rows together.
396
401
 
397
- `printed_age` is the dancer's own age as the row printed it (`9`), verbatim, null where the row
398
- printed none. It is a print, never a reading of a birth year. A release older than the column
399
- answers null for every row, and `rel.capability("results.printed_age")` says which.
400
-
401
402
  The scopes: `majors` reads the majors (tiers T1 to T3), `transcribed` the transcribed rows of
402
403
  every tier, `platform` every row a results platform published, `all` every row.
403
404
 
@@ -71,7 +71,11 @@ import { Age, DANCE_UP_YEARS, birthYearsMeet } from "@finbheara/domain/age";
71
71
  import { createIndex, indexSchema, personEncoder } from "@finbheara/domain/noun-index";
72
72
  import domainPkg from "@finbheara/domain/package.json";
73
73
  import { dancerName } from "@finbheara/domain/person";
74
- import { populationIn, populationRelation } from "@finbheara/domain/title";
74
+ import {
75
+ compTitle,
76
+ populationIn,
77
+ populationRelation
78
+ } from "@finbheara/domain/title";
75
79
  import {
76
80
  foldNameKey,
77
81
  surnameBlock
@@ -339,286 +343,23 @@ function notOnTheFloor(placePrinted, atts, withdrawn) {
339
343
  return withdrawn !== null && DID_NOT_TABULATE.has(withdrawn);
340
344
  }
341
345
 
342
- function columnsOf2(db, table) {
343
- return new Set(db.query("SELECT name FROM pragma_table_info(?)").all(table).map((r) => r.name));
344
- }
345
- var FEATURES2 = [
346
- {
347
- feature: "results.level",
348
- needs: ["results:competitions.feis_level"],
349
- since: 215,
350
- calls: "results.query, majors.results"
351
- },
352
- {
353
- feature: "majors.order",
354
- needs: ["majors:competitions.seq"],
355
- since: 260,
356
- calls: "majors.competitions"
357
- },
358
- {
359
- feature: "dancers",
360
- needs: ["results:results.name_order_resolved"],
361
- since: 260,
362
- calls: "dancers.resolve, dancers.search, dancers.get (the dancers index)"
363
- },
364
- {
365
- feature: "schools",
366
- needs: ["school-registry.jsonl", "school-redirects.jsonl"],
367
- since: 260,
368
- calls: "schools.resolve, a dancer question with a school (the schools index)"
369
- },
370
- {
371
- feature: "feises",
372
- needs: [
373
- "results:feises.date_lo",
374
- "results:feises.date_hi",
375
- "results:feises.feis_instance_id",
376
- "results:feises.feis_brand_id",
377
- "results:competitions.feis_region",
378
- "feis-redirects.jsonl"
379
- ],
380
- since: 265,
381
- calls: "feises.find, feises.series, and comps and registrations through them (the feises index)"
382
- },
383
- {
384
- feature: "comps",
385
- needs: [
386
- "results:competitions.feis_level",
387
- "results:competitions.round",
388
- "results:feises.feis_brand_id"
389
- ],
390
- since: 265,
391
- calls: "comps.resolve, comps.syllabus, comps.syllabusFor, registrations.resolve (the comps index)"
392
- },
393
- {
394
- feature: "comps.dance",
395
- needs: ["results:competitions.dance"],
396
- since: 270,
397
- calls: "the `dance` a comp of comps.resolve, comps.syllabus and comps.syllabusFor carries: null, never refused, on a release without it"
398
- },
399
- {
400
- feature: "schools.lineage",
401
- needs: ["school-lineage.jsonl"],
402
- since: 184,
403
- calls: "the `lineage` school evidence of dancers.resolve and dancers.search: no school is joined by lineage, never refused, on a release without it"
404
- },
405
- {
406
- feature: "results.stream",
407
- needs: [
408
- "results:competitions.round",
409
- "results:competitions.feis_level",
410
- "results:competitions.level_printed",
411
- "results:competitions.age_printed",
412
- "results:results.yob_printed",
413
- "results:results.primary_source",
414
- "results:results.platform_dancer_id",
415
- "results:results.relative_place",
416
- "results:feises.date_lo",
417
- "results:feises.date_hi"
418
- ],
419
- since: 255,
420
- calls: "results.stream, results.entries (scopes transcribed, platform, all)"
421
- },
422
- {
423
- feature: "majors.stream",
424
- needs: [
425
- "majors:competitions.round",
426
- "majors:competitions.feis_level",
427
- "majors:results.yob_printed",
428
- "majors:results.primary_source",
429
- "majors:results.relative_place",
430
- "majors:feises.date_lo"
431
- ],
432
- since: 255,
433
- calls: "results.stream, results.entries (scope majors)"
434
- },
435
- {
436
- feature: "results.printed_age",
437
- needs: ["results:results.printed_age", "majors:results.printed_age"],
438
- since: 271,
439
- calls: "the `printed_age` a row of results.stream and results.entries carries: null, never refused, on a release without it"
440
- },
441
- {
442
- feature: "majors.tabs",
443
- needs: ["majors:tabs.address", "majors:tabs.true_round"],
444
- since: 215,
445
- calls: "majors.tabulated"
446
- },
447
- {
448
- feature: "feises.dates",
449
- needs: ["results:feises.feis_id", "results:feises.date_lo", "results:feises.date_hi"],
450
- since: 245,
451
- calls: "feises.dates"
452
- },
453
- {
454
- feature: "dancers.records",
455
- needs: [
456
- "results:dancers.display",
457
- "results:dancers.appearances",
458
- "results:dancers.birth_lo",
459
- "results:dancers.birth_hi",
460
- "results:dancers.birth_source",
461
- "results:dancers.birth_conflict",
462
- "results:dancers.genders"
463
- ],
464
- since: 215,
465
- calls: "dancers.records"
466
- },
467
- {
468
- feature: "dancers.years",
469
- needs: [
470
- "results:dancer_years.region",
471
- "results:dancer_years.region_source",
472
- "results:dancer_years.nationals",
473
- "results:dancer_years.nationals_source",
474
- "results:dancer_years.geohash"
475
- ],
476
- since: 245,
477
- calls: "dancers.years"
478
- },
479
- {
480
- feature: "teams",
481
- needs: [
482
- ...[
483
- "slug",
484
- "address",
485
- "event",
486
- "year",
487
- "card",
488
- "comp_no",
489
- "title",
490
- "category",
491
- "hands",
492
- "letter",
493
- "label",
494
- "name_printed",
495
- "school_printed",
496
- "school_loc",
497
- "school_id",
498
- "place",
499
- "place_printed",
500
- "tie",
501
- "competitors",
502
- "atts"
503
- ].map((c) => `table:team_results.${c}`),
504
- ...[
505
- "team",
506
- "kind",
507
- "event_year",
508
- "year",
509
- "comp_no",
510
- "title",
511
- "team_index",
512
- "seq",
513
- "school_id",
514
- "school_print_materialized"
515
- ].map((c) => `table:roster_teams.${c}`),
516
- ...["team", "seq", "name_printed", "name", "yob", "dancer_id"].map((c) => `table:roster_members.${c}`),
517
- ...[
518
- "event_year",
519
- "comp_no",
520
- "team",
521
- "result_address",
522
- "school_id",
523
- "join_kind",
524
- "via_split",
525
- "roster_teams",
526
- "result_teams",
527
- "roster_index",
528
- "result_letter"
529
- ].map((c) => `table:team_result_rosters.${c}`),
530
- "table:event_years.event_year",
531
- "table:event_years.event",
532
- "table:event_years.year"
533
- ],
534
- since: 260,
535
- calls: "teams.results, teams.rosters, teams.resultRosters (the teams index)"
536
- },
537
- {
538
- feature: "teams.fields",
539
- needs: [
540
- "table:team_results.age",
541
- "table:team_results.gender",
542
- "table:team_results.printed_status",
543
- "table:roster_teams.dance",
544
- "table:roster_teams.school_print_as_of",
545
- "table:roster_members.gender"
546
- ],
547
- since: 266,
548
- calls: "the fields teams.results (age, gender, printed_status) and teams.rosters (dance, school_printed_as_of, a member's gender) carry where present: absent, never refused, on a release without them"
549
- },
550
- {
551
- feature: "events",
552
- needs: [
553
- "event_year",
554
- "event",
555
- "year",
556
- "date_lo",
557
- "date_hi",
558
- "resolution",
559
- "source",
560
- "date_printed",
561
- "place",
562
- "place_printed"
563
- ].map((c) => `table:event_years.${c}`),
564
- since: 260,
565
- calls: "events.years (the events index)"
566
- },
567
- {
568
- feature: "staff",
569
- needs: [
570
- "results:adjudicators",
571
- "results:feis_panels",
572
- "school-staff.jsonl",
573
- "school-teachers.jsonl",
574
- "school-registry.jsonl"
575
- ],
576
- since: 260,
577
- calls: "staff.teacher, staff.adjudicator (the staff index)"
578
- }
579
- ];
580
- async function hasNeed(probe, n) {
581
- const t = /^table:([A-Za-z_]+)(?:\.([A-Za-z_]+))?$/.exec(n);
582
- if (t !== null) {
583
- if (probe.tableColumns === undefined)
584
- return false;
585
- const cols = await probe.tableColumns(t[1]);
586
- return t[2] === undefined ? cols.size > 0 : cols.has(t[2]);
587
- }
588
- const m = /^(results|majors):([A-Za-z_]+)(?:\.([A-Za-z_]+))?$/.exec(n);
589
- if (m === null)
590
- return probe.publishes(n);
591
- const cols = columnsOf2(await probe.db(m[1]), m[2]);
592
- return m[3] === undefined ? cols.size > 0 : cols.has(m[3]);
593
- }
594
- async function capabilityOf2(probe, f) {
595
- const missing = [];
596
- for (const n of f.needs)
597
- if (!await hasNeed(probe, n))
598
- missing.push(n);
599
- return { ...f, state: missing.length === 0 ? "full" : "missing", missing };
600
- }
601
- function needsRelease2(tag, c) {
602
- return new Error(`archive-client: ${tag} is too old for ${c.feature} (${c.calls}): it lacks ${c.missing.join(", ")}. Open a release of cut ${c.since} or later`);
603
- }
604
-
605
- var ENTRY_RANK = new Map([
606
- ["final", 0],
607
- ["", 1],
608
- ["recall", 2],
609
- ["standings", 3],
610
- ["unplaced", 4],
611
- ["qual", 5],
612
- ["qual-a", 5],
613
- ["qual-b", 5]
614
- ]);
615
- function entryRank2(section) {
616
- const known = ENTRY_RANK.get(section);
617
- if (known !== undefined)
618
- return known;
619
- if (/^solo(?:-r\d+)?$/.test(section))
620
- return 6;
621
- return 9;
346
+ var ENTRY_RANK = new Map([
347
+ ["final", 0],
348
+ ["", 1],
349
+ ["recall", 2],
350
+ ["standings", 3],
351
+ ["unplaced", 4],
352
+ ["qual", 5],
353
+ ["qual-a", 5],
354
+ ["qual-b", 5]
355
+ ]);
356
+ function entryRank2(section) {
357
+ const known = ENTRY_RANK.get(section);
358
+ if (known !== undefined)
359
+ return known;
360
+ if (/^solo(?:-r\d+)?$/.test(section))
361
+ return 6;
362
+ return 9;
622
363
  }
623
364
  var COLUMNS = `r.address AS address, r.competition AS competition, c.event AS event,
624
365
  c.year AS year, c.tier AS tier, c.comp_no AS comp_no, c.title AS title, c.category AS category,
@@ -632,13 +373,9 @@ var COLUMNS = `r.address AS address, r.competition AS competition, c.event AS ev
632
373
  r.yob_printed AS yob_printed, r.points_printed AS points_printed,
633
374
  r.platform_dancer_id AS platform_dancer_id, r.primary_source AS primary_source,
634
375
  r.file AS file, r.line AS line`;
635
- var printedAgeColumn = (carried) => carried ? "r.printed_age AS printed_age" : "NULL AS printed_age";
636
- var streamColumns = (db) => ({
637
- printedAge: columnsOf2(db, "results").has("printed_age")
638
- });
639
376
  var TRANSCRIBED = "('transcript', 'manifested')";
640
377
  var scopeDb = (s) => s === "majors" ? "majors" : "results";
641
- function streamSql(q, carried = { printedAge: false }) {
378
+ function streamSql(q) {
642
379
  const where = [];
643
380
  const args = [];
644
381
  const list = (col, v, nocase = false) => {
@@ -679,7 +416,7 @@ function streamSql(q, carried = { printedAge: false }) {
679
416
  if (q.primary === true)
680
417
  where.push("(r.primary_source IS NULL OR r.primary_source = '')");
681
418
  return {
682
- sql: `SELECT ${COLUMNS}, ${printedAgeColumn(carried.printedAge)} 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`,
419
+ 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`,
683
420
  args
684
421
  };
685
422
  }
@@ -702,13 +439,12 @@ function rowOf(r, dates) {
702
439
  withdrawn: text(withdrawn),
703
440
  qualifier: text(rest.qualifier),
704
441
  primary_source: text(rest.primary_source),
705
- printed_age: text(rest.printed_age),
706
442
  dancer_scope: r.dancer_id === null ? null : r.dancer_id >= FEIS_LOCAL_ID_FLOOR2 ? "per-release" : "stable"
707
443
  };
708
444
  }
709
445
  async function* streamRows(db, q) {
710
446
  const dates = feisDates(db);
711
- const { sql, args } = streamSql(q, streamColumns(db));
447
+ const { sql, args } = streamSql(q);
712
448
  const stmt = db.prepare(sql);
713
449
  try {
714
450
  for (const r of stmt.iterate(...args))
@@ -719,7 +455,7 @@ async function* streamRows(db, q) {
719
455
  }
720
456
  async function* entryRows(db, q) {
721
457
  const dates = feisDates(db);
722
- const { sql, args } = streamSql(q, streamColumns(db));
458
+ const { sql, args } = streamSql(q);
723
459
  const stmt = db.prepare(sql);
724
460
  let file = null;
725
461
  let held = new Map;
@@ -1323,8 +1059,11 @@ class DancerResolver2 {
1323
1059
  const held = [];
1324
1060
  const refuted = [];
1325
1061
  const bySchool = new Set;
1062
+ const adultBy = adultBornBy(year);
1063
+ const adultAsked = adultBy !== null && adultCategory(q.age, age?.age ?? null);
1326
1064
  const schoolFact = async (c) => {
1327
- const fact = await this.schoolRefutes(schoolsOf(db, c.dancer_id), line, year, q.school);
1065
+ const returning = adultAsked && c.birth.hi !== null && c.birth.hi <= adultBy ? c.birth.lo : null;
1066
+ const fact = await this.schoolRefutes(schoolsOf(db, c.dancer_id), line, year, q.school, returning);
1328
1067
  if (fact === null)
1329
1068
  return false;
1330
1069
  refuted.push({ dancer_id: c.dancer_id, why: "school", fact });
@@ -1463,20 +1202,30 @@ class DancerResolver2 {
1463
1202
  }
1464
1203
  return joined ? "lineage" : earlier ? "moved" : "none";
1465
1204
  }
1466
- async schoolRefutes(schools, line, year, asked) {
1205
+ async schoolRefutes(schools, line, year, asked, returning = null) {
1467
1206
  const rows = schools.filter((s) => s.year_lo === null || s.year_lo <= year);
1468
1207
  if (rows.length === 0)
1469
1208
  return null;
1470
1209
  const seen = new Set;
1210
+ const kept = [];
1211
+ let youth = 0;
1471
1212
  for (const s of rows) {
1472
1213
  const own = await this.schoolIdsOf(s);
1473
1214
  if (own.length === 0 || own.some((id) => line.has(id)))
1474
1215
  return null;
1216
+ if (returning !== null && youthRow(s, returning)) {
1217
+ youth++;
1218
+ continue;
1219
+ }
1220
+ kept.push(s);
1475
1221
  for (const id of own)
1476
1222
  seen.add(id);
1477
1223
  }
1478
- const last = latestSchool(rows, year);
1479
- return `every school row to ${year} reads ${[...seen].sort().join(", ")} (the latest ${last?.school_id ?? last?.print} in ${last?.year_hi}), none of them ${asked ?? "the asked school"} or a school its line joined by ${year}`;
1224
+ if (kept.length === 0)
1225
+ return null;
1226
+ const last = latestSchool(kept, year);
1227
+ const set = youth > 0 ? ` (${youth} youth row(s) set aside for an adult-category question)` : "";
1228
+ return `every school row to ${year} reads ${[...seen].sort().join(", ")} (the latest ${last?.school_id ?? last?.print} in ${last?.year_hi}), none of them ${asked ?? "the asked school"} or a school its line joined by ${year}${set}`;
1480
1229
  }
1481
1230
  async schoolIdsOf(s) {
1482
1231
  const key = s.school_id ?? s.print;
@@ -1515,6 +1264,24 @@ class DancerResolver2 {
1515
1264
  return done(Age.from(c.age, { year: c.year }));
1516
1265
  }
1517
1266
  }
1267
+ function adultBornBy(year) {
1268
+ const a = Age.from("adult", { year });
1269
+ return a.isAge() ? a.birthYears().to : null;
1270
+ }
1271
+ function adultCategory(asked, read) {
1272
+ if (read?.isAge() && read.isAdult())
1273
+ return true;
1274
+ if (asked === undefined || !("band" in asked))
1275
+ return false;
1276
+ const t = compTitle(asked.band).age;
1277
+ return t?.isAge() === true && t.isAdult();
1278
+ }
1279
+ function youthRow(s, bornLo) {
1280
+ if (bornLo === null || s.year_hi === null)
1281
+ return false;
1282
+ const by = adultBornBy(s.year_hi);
1283
+ return by !== null && bornLo > by;
1284
+ }
1518
1285
  function birthOf(d) {
1519
1286
  return {
1520
1287
  lo: d.birth_lo,
@@ -1620,7 +1387,7 @@ function contradictionFact(x, age, slack) {
1620
1387
  import { Age as Age2 } from "@finbheara/domain/age";
1621
1388
  import {
1622
1389
  codePopulation,
1623
- compTitle,
1390
+ compTitle as compTitle2,
1624
1391
  matchTitle,
1625
1392
  populationIn as populationIn2,
1626
1393
  secondIn,
@@ -1689,7 +1456,7 @@ function compOf(r, codes = []) {
1689
1456
  const stated = r.gender_source === "level" ? "" : populationIn2(r.gender ?? "");
1690
1457
  const pop = stated || populationIn2(r.title ?? "") || (r.platform === "ifeis" && codes.length > 0 ? codePopulation(r.code, codes) : "") || populationIn2(r.gender ?? "");
1691
1458
  const second = [r.title, r.level_printed, r.dance_printed].some((t) => t != null && secondIn(t));
1692
- const round = r.round ?? (r.title === null ? null : compTitle(r.title).round?.toString() ?? null);
1459
+ const round = r.round ?? (r.title === null ? null : compTitle2(r.title).round?.toString() ?? null);
1693
1460
  return {
1694
1461
  competition: r.competition,
1695
1462
  feis_id: r.feis_id,
@@ -1950,7 +1717,7 @@ class CompResolver2 {
1950
1717
  if (q.title === undefined || q.title.trim() === "")
1951
1718
  return answer("unique", "prior-year", hits, { from, check: "unchecked" });
1952
1719
  const m = matchTitle(q.title, [entryOf(hit)]);
1953
- const r = titleRelation(m.title, compTitle(hit.title ?? "", {}));
1720
+ const r = titleRelation(m.title, compTitle2(hit.title ?? "", {}));
1954
1721
  const check = {
1955
1722
  title: q.title,
1956
1723
  agrees: m.isSingular(),
@@ -2732,7 +2499,7 @@ import {
2732
2499
  import { join as join3 } from "path";
2733
2500
 
2734
2501
  import { Age as Age3 } from "@finbheara/domain/age";
2735
- import { compTitle as compTitle2 } from "@finbheara/domain/title";
2502
+ import { compTitle as compTitle3 } from "@finbheara/domain/title";
2736
2503
  function youngest2(answers) {
2737
2504
  const key = (a) => {
2738
2505
  const iv = Age3.from(a.age, { year: null }).interval();
@@ -2812,7 +2579,7 @@ class RegistrationResolver2 {
2812
2579
  const printed = first !== undefined ? [] : comps.flatMap((c, i) => {
2813
2580
  const code = row.codes[i];
2814
2581
  const t = row.titles?.[code];
2815
- const a = t === undefined ? null : compTitle2(t).age;
2582
+ const a = t === undefined ? null : compTitle3(t).age;
2816
2583
  return a?.isAge() === true && a.kind !== "yob" ? [{ age: a.ag1, year: c.feis.year, code }] : [];
2817
2584
  });
2818
2585
  const byTitle = youngest2(printed);
@@ -3022,6 +2789,263 @@ function defaultRemote() {
3022
2789
  return installed ?? fallbackRemote2(ghRemote2, httpsRemote2());
3023
2790
  }
3024
2791
 
2792
+ function columnsOf2(db, table) {
2793
+ return new Set(db.query("SELECT name FROM pragma_table_info(?)").all(table).map((r) => r.name));
2794
+ }
2795
+ var FEATURES2 = [
2796
+ {
2797
+ feature: "results.level",
2798
+ needs: ["results:competitions.feis_level"],
2799
+ since: 215,
2800
+ calls: "results.query, majors.results"
2801
+ },
2802
+ {
2803
+ feature: "majors.order",
2804
+ needs: ["majors:competitions.seq"],
2805
+ since: 260,
2806
+ calls: "majors.competitions"
2807
+ },
2808
+ {
2809
+ feature: "dancers",
2810
+ needs: ["results:results.name_order_resolved"],
2811
+ since: 260,
2812
+ calls: "dancers.resolve, dancers.search, dancers.get (the dancers index)"
2813
+ },
2814
+ {
2815
+ feature: "schools",
2816
+ needs: ["school-registry.jsonl", "school-redirects.jsonl"],
2817
+ since: 260,
2818
+ calls: "schools.resolve, a dancer question with a school (the schools index)"
2819
+ },
2820
+ {
2821
+ feature: "feises",
2822
+ needs: [
2823
+ "results:feises.date_lo",
2824
+ "results:feises.date_hi",
2825
+ "results:feises.feis_instance_id",
2826
+ "results:feises.feis_brand_id",
2827
+ "results:competitions.feis_region",
2828
+ "feis-redirects.jsonl"
2829
+ ],
2830
+ since: 265,
2831
+ calls: "feises.find, feises.series, and comps and registrations through them (the feises index)"
2832
+ },
2833
+ {
2834
+ feature: "comps",
2835
+ needs: [
2836
+ "results:competitions.feis_level",
2837
+ "results:competitions.round",
2838
+ "results:feises.feis_brand_id"
2839
+ ],
2840
+ since: 265,
2841
+ calls: "comps.resolve, comps.syllabus, comps.syllabusFor, registrations.resolve (the comps index)"
2842
+ },
2843
+ {
2844
+ feature: "comps.dance",
2845
+ needs: ["results:competitions.dance"],
2846
+ since: 270,
2847
+ calls: "the `dance` a comp of comps.resolve, comps.syllabus and comps.syllabusFor carries: null, never refused, on a release without it"
2848
+ },
2849
+ {
2850
+ feature: "schools.lineage",
2851
+ needs: ["school-lineage.jsonl"],
2852
+ since: 184,
2853
+ calls: "the `lineage` school evidence of dancers.resolve and dancers.search: no school is joined by lineage, never refused, on a release without it"
2854
+ },
2855
+ {
2856
+ feature: "results.stream",
2857
+ needs: [
2858
+ "results:competitions.round",
2859
+ "results:competitions.feis_level",
2860
+ "results:competitions.level_printed",
2861
+ "results:competitions.age_printed",
2862
+ "results:results.yob_printed",
2863
+ "results:results.primary_source",
2864
+ "results:results.platform_dancer_id",
2865
+ "results:results.relative_place",
2866
+ "results:feises.date_lo",
2867
+ "results:feises.date_hi"
2868
+ ],
2869
+ since: 255,
2870
+ calls: "results.stream, results.entries (scopes transcribed, platform, all)"
2871
+ },
2872
+ {
2873
+ feature: "majors.stream",
2874
+ needs: [
2875
+ "majors:competitions.round",
2876
+ "majors:competitions.feis_level",
2877
+ "majors:results.yob_printed",
2878
+ "majors:results.primary_source",
2879
+ "majors:results.relative_place",
2880
+ "majors:feises.date_lo"
2881
+ ],
2882
+ since: 255,
2883
+ calls: "results.stream, results.entries (scope majors)"
2884
+ },
2885
+ {
2886
+ feature: "majors.tabs",
2887
+ needs: ["majors:tabs.address", "majors:tabs.true_round"],
2888
+ since: 215,
2889
+ calls: "majors.tabulated"
2890
+ },
2891
+ {
2892
+ feature: "feises.dates",
2893
+ needs: ["results:feises.feis_id", "results:feises.date_lo", "results:feises.date_hi"],
2894
+ since: 245,
2895
+ calls: "feises.dates"
2896
+ },
2897
+ {
2898
+ feature: "dancers.records",
2899
+ needs: [
2900
+ "results:dancers.display",
2901
+ "results:dancers.appearances",
2902
+ "results:dancers.birth_lo",
2903
+ "results:dancers.birth_hi",
2904
+ "results:dancers.birth_source",
2905
+ "results:dancers.birth_conflict",
2906
+ "results:dancers.genders"
2907
+ ],
2908
+ since: 215,
2909
+ calls: "dancers.records"
2910
+ },
2911
+ {
2912
+ feature: "dancers.years",
2913
+ needs: [
2914
+ "results:dancer_years.region",
2915
+ "results:dancer_years.region_source",
2916
+ "results:dancer_years.nationals",
2917
+ "results:dancer_years.nationals_source",
2918
+ "results:dancer_years.geohash"
2919
+ ],
2920
+ since: 245,
2921
+ calls: "dancers.years"
2922
+ },
2923
+ {
2924
+ feature: "teams",
2925
+ needs: [
2926
+ ...[
2927
+ "slug",
2928
+ "address",
2929
+ "event",
2930
+ "year",
2931
+ "card",
2932
+ "comp_no",
2933
+ "title",
2934
+ "category",
2935
+ "hands",
2936
+ "letter",
2937
+ "label",
2938
+ "name_printed",
2939
+ "school_printed",
2940
+ "school_loc",
2941
+ "school_id",
2942
+ "place",
2943
+ "place_printed",
2944
+ "tie",
2945
+ "competitors",
2946
+ "atts"
2947
+ ].map((c) => `table:team_results.${c}`),
2948
+ ...[
2949
+ "team",
2950
+ "kind",
2951
+ "event_year",
2952
+ "year",
2953
+ "comp_no",
2954
+ "title",
2955
+ "team_index",
2956
+ "seq",
2957
+ "school_id",
2958
+ "school_print_materialized"
2959
+ ].map((c) => `table:roster_teams.${c}`),
2960
+ ...["team", "seq", "name_printed", "name", "yob", "dancer_id"].map((c) => `table:roster_members.${c}`),
2961
+ ...[
2962
+ "event_year",
2963
+ "comp_no",
2964
+ "team",
2965
+ "result_address",
2966
+ "school_id",
2967
+ "join_kind",
2968
+ "via_split",
2969
+ "roster_teams",
2970
+ "result_teams",
2971
+ "roster_index",
2972
+ "result_letter"
2973
+ ].map((c) => `table:team_result_rosters.${c}`),
2974
+ "table:event_years.event_year",
2975
+ "table:event_years.event",
2976
+ "table:event_years.year"
2977
+ ],
2978
+ since: 260,
2979
+ calls: "teams.results, teams.rosters, teams.resultRosters (the teams index)"
2980
+ },
2981
+ {
2982
+ feature: "teams.fields",
2983
+ needs: [
2984
+ "table:team_results.age",
2985
+ "table:team_results.gender",
2986
+ "table:team_results.printed_status",
2987
+ "table:roster_teams.dance",
2988
+ "table:roster_teams.school_print_as_of",
2989
+ "table:roster_members.gender"
2990
+ ],
2991
+ since: 266,
2992
+ calls: "the fields teams.results (age, gender, printed_status) and teams.rosters (dance, school_printed_as_of, a member's gender) carry where present: absent, never refused, on a release without them"
2993
+ },
2994
+ {
2995
+ feature: "events",
2996
+ needs: [
2997
+ "event_year",
2998
+ "event",
2999
+ "year",
3000
+ "date_lo",
3001
+ "date_hi",
3002
+ "resolution",
3003
+ "source",
3004
+ "date_printed",
3005
+ "place",
3006
+ "place_printed"
3007
+ ].map((c) => `table:event_years.${c}`),
3008
+ since: 260,
3009
+ calls: "events.years (the events index)"
3010
+ },
3011
+ {
3012
+ feature: "staff",
3013
+ needs: [
3014
+ "results:adjudicators",
3015
+ "results:feis_panels",
3016
+ "school-staff.jsonl",
3017
+ "school-teachers.jsonl",
3018
+ "school-registry.jsonl"
3019
+ ],
3020
+ since: 260,
3021
+ calls: "staff.teacher, staff.adjudicator (the staff index)"
3022
+ }
3023
+ ];
3024
+ async function hasNeed(probe, n) {
3025
+ const t = /^table:([A-Za-z_]+)(?:\.([A-Za-z_]+))?$/.exec(n);
3026
+ if (t !== null) {
3027
+ if (probe.tableColumns === undefined)
3028
+ return false;
3029
+ const cols = await probe.tableColumns(t[1]);
3030
+ return t[2] === undefined ? cols.size > 0 : cols.has(t[2]);
3031
+ }
3032
+ const m = /^(results|majors):([A-Za-z_]+)(?:\.([A-Za-z_]+))?$/.exec(n);
3033
+ if (m === null)
3034
+ return probe.publishes(n);
3035
+ const cols = columnsOf2(await probe.db(m[1]), m[2]);
3036
+ return m[3] === undefined ? cols.size > 0 : cols.has(m[3]);
3037
+ }
3038
+ async function capabilityOf2(probe, f) {
3039
+ const missing = [];
3040
+ for (const n of f.needs)
3041
+ if (!await hasNeed(probe, n))
3042
+ missing.push(n);
3043
+ return { ...f, state: missing.length === 0 ? "full" : "missing", missing };
3044
+ }
3045
+ function needsRelease2(tag, c) {
3046
+ return new Error(`archive-client: ${tag} is too old for ${c.feature} (${c.calls}): it lacks ${c.missing.join(", ")}. Open a release of cut ${c.since} or later`);
3047
+ }
3048
+
3025
3049
  function tagDir(root, tag) {
3026
3050
  if (parseArchiveTag2(tag) === null)
3027
3051
  throw new Error(`archive-client: "${tag}" is not a release tag (archive-YYYY.MM.DD[-N[.k]])`);
@@ -3540,4 +3564,4 @@ function releases2(opts = {}) {
3540
3564
  return new ReleaseManager2(opts);
3541
3565
  }
3542
3566
 
3543
- export { DATABASES2, planAsset2, sha256File2, unzstdTo2, NAME_LEVELS2, nameLevel2, NO_CALENDAR2, FeisFinder2, columnsOf2, FEATURES2, capabilityOf2, needsRelease2, entryRank2, FEIS_LOCAL_ID_FLOOR2, ResultFinder2, MajorsFinder2, initialsOnly2, nameOrderOf, DancerResolver2, readPopulation2, canonicalOf2, CompResolver2, StaffFinder2, SchoolResolver2, TeamFinder2, EventFinder2, defaultBuilders2, indexPath2, indexKey2, isCurrent2, buildIndex2, ensureIndex2, listIndexes2, readPin2, youngest2, dancerResolverOf2, RegistrationResolver2, ghRemote2, httpsRemote2, fallbackRemote2, setRemote2, resolveTag2, Release2, openRelease2, openCached2, ReleaseManager2, releases2 };
3567
+ export { DATABASES2, planAsset2, sha256File2, unzstdTo2, NAME_LEVELS2, nameLevel2, NO_CALENDAR2, FeisFinder2, entryRank2, FEIS_LOCAL_ID_FLOOR2, ResultFinder2, MajorsFinder2, initialsOnly2, nameOrderOf, DancerResolver2, readPopulation2, canonicalOf2, CompResolver2, StaffFinder2, SchoolResolver2, TeamFinder2, EventFinder2, defaultBuilders2, indexPath2, indexKey2, isCurrent2, buildIndex2, ensureIndex2, listIndexes2, readPin2, youngest2, dancerResolverOf2, RegistrationResolver2, ghRemote2, httpsRemote2, fallbackRemote2, setRemote2, columnsOf2, FEATURES2, capabilityOf2, needsRelease2, resolveTag2, Release2, openRelease2, openCached2, ReleaseManager2, releases2 };
package/dist/cli.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  nameOrderOf,
5
5
  openRelease2,
6
6
  releases2
7
- } from "./chunks/index-7hw19tw1.js";
7
+ } from "./chunks/index-xtv455ft.js";
8
8
  import"./chunks/index-hne1wadw.js";
9
9
  import"./chunks/index-hghdy980.js";
10
10
 
package/dist/index.js CHANGED
@@ -8,10 +8,6 @@ import {
8
8
  nameLevel2,
9
9
  NO_CALENDAR2,
10
10
  FeisFinder2,
11
- columnsOf2,
12
- FEATURES2,
13
- capabilityOf2,
14
- needsRelease2,
15
11
  entryRank2,
16
12
  FEIS_LOCAL_ID_FLOOR2,
17
13
  ResultFinder2,
@@ -40,13 +36,17 @@ import {
40
36
  httpsRemote2,
41
37
  fallbackRemote2,
42
38
  setRemote2,
39
+ columnsOf2,
40
+ FEATURES2,
41
+ capabilityOf2,
42
+ needsRelease2,
43
43
  resolveTag2,
44
44
  Release2,
45
45
  openRelease2,
46
46
  openCached2,
47
47
  ReleaseManager2,
48
48
  releases2
49
- } from "./chunks/index-7hw19tw1.js";
49
+ } from "./chunks/index-xtv455ft.js";
50
50
  import {
51
51
  cacheRoot2,
52
52
  repoOf2
@@ -50,7 +50,6 @@ export interface StreamRow {
50
50
  dancer_scope: "per-release" | "stable" | null;
51
51
  yob: number | null;
52
52
  yob_printed: string | null;
53
- printed_age: string | null;
54
53
  points_printed: string | null;
55
54
  platform_dancer_id: string | null;
56
55
  primary_source: string | null;
@@ -61,12 +60,8 @@ export interface EntryRow extends StreamRow {
61
60
  recall_place: number | null;
62
61
  }
63
62
  export declare function entryRank(section: string): number;
64
- export interface StreamColumns {
65
- printedAge: boolean;
66
- }
67
- export declare const streamColumns: (db: Database) => StreamColumns;
68
63
  export declare const scopeDb: (s: StreamScope) => "majors" | "results";
69
- export declare function streamSql(q: StreamQuery, carried?: StreamColumns): {
64
+ export declare function streamSql(q: StreamQuery): {
70
65
  sql: string;
71
66
  args: (string | number)[];
72
67
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finbheara/archive-client",
3
- "version": "0.11.0",
3
+ "version": "0.12.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",