@finbheara/archive-client 0.4.0 → 0.5.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
@@ -20,7 +20,7 @@ type declarations (`.d.ts`), so a TypeScript project needs Bun's types (`"types"
20
20
  `bunx idq <verb>`.
21
21
 
22
22
  The version is 0.x while the API settles: a minor bump may break, so pin a minor
23
- (`"@finbheara/archive-client": "0.4.x"`).
23
+ (`"@finbheara/archive-client": "0.5.x"`).
24
24
 
25
25
  ## Configuration
26
26
 
@@ -85,6 +85,11 @@ c.features; // [{ feature: "dancers", state: "full" | "missing", missing: [...
85
85
  | `staff` | `staff.teacher`, `staff.adjudicator` | 260 |
86
86
  | `majors.order` | `majors.competitions` | 260 |
87
87
  | `feises`, `comps` | `feises.find`, `feises.series`, `comps.*`, `registrations.resolve` | 265 |
88
+ | `results.stream`, `majors.stream` | `results.stream`, `results.entries` | 255 |
89
+ | `feises.dates` | `feises.dates` | 245 |
90
+ | `dancers.records` | `dancers.records` | 215 |
91
+ | `dancers.years` | `dancers.years` | 245 |
92
+ | `majors.tabs` | `majors.tabulated` | 215 |
88
93
  | `teams` | `teams.results`, `teams.rosters`, `teams.resultRosters` | 260 |
89
94
  | `events` | `events.years` | 260 |
90
95
 
@@ -276,6 +281,49 @@ A row carries the prints and the ids that stand for them: the address, the compe
276
281
  and age, the place, the name and school as printed with the dancer and school ids. The majors are
277
282
  read from `majors.sqlite`, fetched on first use.
278
283
 
284
+ ## Every row, streamed
285
+
286
+ ```ts
287
+ for await (const row of rel.results.stream({ scope: "majors" })) { ... }
288
+ for await (const row of rel.results.stream({
289
+ scope: "platform", // "majors" | "transcribed" | "platform" | "all"
290
+ primary: true, // only rows no other source stands in for
291
+ year: [2015, 2024], // a year or an inclusive range
292
+ feis_level: [600, 700], // also: event (one or several), tier, category, dancer_id, school_id
293
+ })) { ... }
294
+ for await (const entry of rel.results.entries({ scope: "all", event: "<event>" })) { ... }
295
+ ```
296
+
297
+ A stream yields one row at a time and never holds the table in memory. A row carries its
298
+ competition beside its own fields: `competition`, `event`, `year`, `tier`, `comp_no`, `title`,
299
+ `category`, `age`, `age_printed`, `gender`, `competitors`, `feis_level`, `level_printed`,
300
+ `date_printed`, `source`, `round`, and at a feis its `feis_id`, `feis_date_lo` and
301
+ `feis_date_hi`. Its own: `address`, `section`, `card`, `place`, `place_printed`, `tie`,
302
+ `relative_place`, `atts`, `withdrawn`, `qualifier`, `name_printed`, `name`, `school_printed`,
303
+ `school_loc`, `school_id`, `dancer_id`, `dancer_scope`, `yob`, `yob_printed`, `points_printed`,
304
+ `platform_dancer_id`, `primary_source`. Rows come in the release's own order, a competition's
305
+ rows together.
306
+
307
+ The scopes: `majors` reads the majors (tiers T1 to T3), `transcribed` the transcribed rows of
308
+ every tier, `platform` every row a results platform published, `all` every row.
309
+
310
+ AN ENTRY is one row per address, the row the card stands for: a card printed in several sections
311
+ (a recall order, the final, a single round's solo placing, a qualifying heat) keeps the final's
312
+ row, then the sectionless page's, the recall order's, the standings', the unplaced row, a heat's
313
+ and last a solo round's (`entryRank`), and two rows of one rank keep the later. `recall_place` is
314
+ the place the address's recall row carries.
315
+
316
+ ## Bulk reads
317
+
318
+ ```ts
319
+ await rel.dancers.records(); // every dancer: display, appearances, birth_lo, birth_hi,
320
+ // birth_source, birth_conflict, genders, scope
321
+ await rel.dancers.records({ ids: [1001] });
322
+ await rel.dancers.years({ ids: [1001], year: 2025 }); // region, region_source, nationals, geohash
323
+ await rel.feises.dates(); // every feis id with its first and last day
324
+ await rel.majors.tabulated({ min_true_round: 3 }); // the majors addresses marked to round 3 or later
325
+ ```
326
+
279
327
  ## Teams and event-years
280
328
 
281
329
  ```ts
@@ -426,6 +474,13 @@ and the competitions still resolve.
426
474
  [--population girls|boys] [--rows]
427
475
  idq results [--release <spec>] [--dancer <id>] [--feis <id>] [--comp <c>] [--school <s>] [--year <y>] [--limit <n>]
428
476
  idq majors [--release <spec>] [--event <e>] [--year <y>] [--comp <c>] [--dancer <id>] [--rows]
477
+ idq stream [--release <spec>] --scope majors|transcribed|platform|all [--entries] [--event <e,...>]
478
+ [--year <y> | --year <from>..<to>] [--tier <t,...>] [--category <c,...>]
479
+ [--level <n,...>] [--dancer <id,...>] [--school <school_id>] [--primary]
480
+ idq records [--release <spec>] [--dancer <id,...>]
481
+ idq records --years [--release <spec>] [--dancer <id,...>] [--year <y>]
482
+ idq feis-dates [--release <spec>]
483
+ idq tabulated [--release <spec>] [--min-round <n>]
429
484
  idq teams [--release <spec>] [--event <e>] [--year <y>] [--rosters | --joins]
430
485
  idq events [--release <spec>] [--event <e>] [--year <y>]
431
486
  idq teacher [--release <spec>] <name|TD number>
@@ -437,7 +492,7 @@ and the competitions still resolve.
437
492
  idq resolve dancer [<name>] [--school s] [--comp <feis>/<code> | --yob y | --band b --year y]
438
493
  idq resolve registrations
439
494
 
440
- `dancer` prints the results of the one dancer where the answer is unique (every candidate's with
495
+ `stream` prints one JSON line per row (or entry), as it reads them. `dancer` prints the results of the one dancer where the answer is unique (every candidate's with
441
496
  `--rows`). `--json` prints the call's answer as it is. `--cache` and `--repo` override the defaults.
442
497
 
443
498
  `resolve` speaks JSON lines: one answer per line out, and with no positional question the
@@ -106,8 +106,149 @@ function nameLevel2(q, c) {
106
106
  return null;
107
107
  }
108
108
 
109
+ var ENTRY_RANK = new Map([
110
+ ["final", 0],
111
+ ["", 1],
112
+ ["recall", 2],
113
+ ["standings", 3],
114
+ ["unplaced", 4],
115
+ ["qual", 5],
116
+ ["qual-a", 5],
117
+ ["qual-b", 5]
118
+ ]);
119
+ function entryRank2(section) {
120
+ const known = ENTRY_RANK.get(section);
121
+ if (known !== undefined)
122
+ return known;
123
+ if (/^solo(?:-r\d+)?$/.test(section))
124
+ return 6;
125
+ return 9;
126
+ }
127
+ var COLUMNS = `r.address AS address, r.competition AS competition, c.event AS event,
128
+ c.year AS year, c.tier AS tier, c.comp_no AS comp_no, c.title AS title, c.category AS category,
129
+ c.age AS age, c.age_printed AS age_printed, c.gender AS gender, c.competitors AS competitors,
130
+ c.feis_level AS feis_level, c.level_printed AS level_printed, c.date_printed AS date_printed,
131
+ c.source AS source, c.round AS round, r.section AS section, r.card AS card, r.place AS place,
132
+ r.place_printed AS place_printed, r.tie AS tie, r.relative_place AS relative_place,
133
+ r.atts AS atts, r.withdrawn AS withdrawn, r.qualifier AS qualifier,
134
+ r.name_printed AS name_printed, r.name AS name, r.school_printed AS school_printed,
135
+ r.school_loc AS school_loc, r.school_id AS school_id, r.dancer_id AS dancer_id, r.yob AS yob,
136
+ r.yob_printed AS yob_printed, r.points_printed AS points_printed,
137
+ r.platform_dancer_id AS platform_dancer_id, r.primary_source AS primary_source,
138
+ r.file AS file`;
139
+ var TRANSCRIBED = "('transcript', 'manifested')";
140
+ var scopeDb = (s) => s === "majors" ? "majors" : "results";
141
+ function streamSql(q) {
142
+ const where = [];
143
+ const args = [];
144
+ const list = (col, v, nocase = false) => {
145
+ if (v.length === 0) {
146
+ where.push("0");
147
+ return;
148
+ }
149
+ where.push(`${col}${nocase ? " COLLATE NOCASE" : ""} IN (${v.map(() => "?").join(", ")})`);
150
+ args.push(...v);
151
+ };
152
+ if (q.scope === "transcribed")
153
+ where.push(`r.source IN ${TRANSCRIBED}`);
154
+ else if (q.scope === "platform")
155
+ where.push(`r.source NOT IN ${TRANSCRIBED}`);
156
+ else if (q.scope !== "majors" && q.scope !== "all")
157
+ throw new Error(`archive-client: no stream scope "${String(q.scope)}"`);
158
+ if (q.event !== undefined)
159
+ list("c.event", typeof q.event === "string" ? [q.event] : q.event, true);
160
+ if (typeof q.year === "number") {
161
+ where.push("c.year = ?");
162
+ args.push(q.year);
163
+ } else if (Array.isArray(q.year)) {
164
+ where.push("c.year BETWEEN ? AND ?");
165
+ args.push(q.year[0], q.year[1]);
166
+ }
167
+ if (q.tier !== undefined)
168
+ list("c.tier", q.tier);
169
+ if (q.category !== undefined)
170
+ list("c.category", q.category);
171
+ if (q.feis_level !== undefined)
172
+ list("c.feis_level", q.feis_level);
173
+ if (q.dancer_id !== undefined)
174
+ list("r.dancer_id", q.dancer_id);
175
+ if (q.school_id !== undefined) {
176
+ where.push("r.school_id = ?");
177
+ args.push(q.school_id);
178
+ }
179
+ if (q.primary === true)
180
+ where.push("(r.primary_source IS NULL OR r.primary_source = '')");
181
+ return {
182
+ 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`,
183
+ args
184
+ };
185
+ }
186
+ function feisDates(db) {
187
+ const out = new Map;
188
+ for (const f of db.query("SELECT feis_id, min(date_lo) AS lo, max(date_hi) AS hi FROM feises GROUP BY feis_id").all())
189
+ out.set(f.feis_id, { lo: f.lo, hi: f.hi });
190
+ return out;
191
+ }
192
+ var text = (v) => v === null || v === "" ? null : v;
193
+ function rowOf(r, dates) {
194
+ const { file: _file, atts, withdrawn, ...rest } = r;
195
+ const d = dates.get(r.event);
196
+ return {
197
+ ...rest,
198
+ feis_id: d === undefined ? null : r.event,
199
+ feis_date_lo: d?.lo ?? null,
200
+ feis_date_hi: d?.hi ?? null,
201
+ atts: atts === "" ? [] : atts.split(","),
202
+ withdrawn: text(withdrawn),
203
+ qualifier: text(rest.qualifier),
204
+ primary_source: text(rest.primary_source),
205
+ dancer_scope: r.dancer_id === null ? null : r.dancer_id >= FEIS_LOCAL_ID_FLOOR2 ? "per-release" : "stable"
206
+ };
207
+ }
208
+ async function* streamRows(db, q) {
209
+ const dates = feisDates(db);
210
+ const { sql, args } = streamSql(q);
211
+ const stmt = db.prepare(sql);
212
+ try {
213
+ for (const r of stmt.iterate(...args))
214
+ yield rowOf(r, dates);
215
+ } finally {
216
+ stmt.finalize();
217
+ }
218
+ }
219
+ async function* entryRows(db, q) {
220
+ const dates = feisDates(db);
221
+ const { sql, args } = streamSql(q);
222
+ const stmt = db.prepare(sql);
223
+ let file = null;
224
+ let held = new Map;
225
+ const flush = function* () {
226
+ for (const h of held.values())
227
+ yield { ...rowOf(h.raw, dates), recall_place: h.recall };
228
+ held = new Map;
229
+ };
230
+ try {
231
+ for (const r of stmt.iterate(...args)) {
232
+ if (r.file !== file) {
233
+ yield* flush();
234
+ file = r.file;
235
+ }
236
+ const rank = entryRank2(r.section);
237
+ const h = held.get(r.address);
238
+ const recall = r.section === "recall" ? r.place : h?.recall ?? null;
239
+ if (h === undefined || rank <= h.rank)
240
+ held.set(r.address, { raw: r, rank, recall });
241
+ else
242
+ h.recall = recall;
243
+ }
244
+ yield* flush();
245
+ } finally {
246
+ stmt.finalize();
247
+ }
248
+ }
249
+
109
250
  var FEIS_LOCAL_ID_FLOOR2 = 17179869184;
110
- var COLUMNS = `r.address AS address, r.competition AS competition, c.event AS event, c.year AS year,
251
+ var COLUMNS2 = `r.address AS address, r.competition AS competition, c.event AS event, c.year AS year,
111
252
  c.tier AS tier, c.title AS title, c.age AS age, c.category AS category, c.gender AS gender,
112
253
  c.feis_level AS feis_level, c.comp_no AS comp_no, r.place AS place,
113
254
  r.place_printed AS place_printed, c.competitors AS competitors, r.name_printed AS name_printed,
@@ -139,7 +280,7 @@ function queryResults(rel, db, q) {
139
280
  args.push(q.year);
140
281
  }
141
282
  const limit = q.limit ?? 500;
142
- const all = rel.db(db).query(`SELECT ${COLUMNS} FROM results r JOIN competitions c ON c.file = r.file AND c.line = r.competition_line
283
+ const all = rel.db(db).query(`SELECT ${COLUMNS2} FROM results r JOIN competitions c ON c.file = r.file AND c.line = r.competition_line
143
284
  WHERE ${where.join(" AND ")}
144
285
  ORDER BY c.year, c.event, r.competition, r.place IS NULL, r.place, r.address`).all(...args);
145
286
  return {
@@ -164,6 +305,18 @@ class ResultFinder2 {
164
305
  await this.rel.require("results.level");
165
306
  return queryResults(this.rel, "results", q);
166
307
  }
308
+ async streamDb(q) {
309
+ const db = scopeDb(q.scope);
310
+ await this.rel.fetch(db);
311
+ await this.rel.require(db === "majors" ? "majors.stream" : "results.stream");
312
+ return this.rel.db(db);
313
+ }
314
+ async* stream(q) {
315
+ yield* streamRows(await this.streamDb(q), q);
316
+ }
317
+ async* entries(q) {
318
+ yield* entryRows(await this.streamDb(q), q);
319
+ }
167
320
  }
168
321
 
169
322
  class MajorsFinder2 {
@@ -208,6 +361,13 @@ class MajorsFinder2 {
208
361
  ORDER BY c.year, c.seq, c.competition`).all(...args).map((c) => ({ ...c, rows: rows.get(c.competition) ?? 0 }));
209
362
  return { tag: this.rel.tag, competitions };
210
363
  }
364
+ async tabulated(q = {}) {
365
+ const db = await this.db();
366
+ await this.rel.require("majors.tabs");
367
+ const min = q.min_true_round ?? 3;
368
+ const addresses = db.query("SELECT DISTINCT address FROM tabs WHERE true_round >= ? ORDER BY address").all(min).map((r) => r.address);
369
+ return { tag: this.rel.tag, min_true_round: min, addresses };
370
+ }
211
371
  async results(q) {
212
372
  await this.db();
213
373
  await this.rel.require("results.level");
@@ -365,6 +525,12 @@ var SCHOOL_WEIGHT = {
365
525
  unresolved: 0,
366
526
  unasked: 0
367
527
  };
528
+ function idChunks(ids) {
529
+ const out = [];
530
+ for (let i = 0;i < ids.length; i += 500)
531
+ out.push(ids.slice(i, i + 500));
532
+ return out;
533
+ }
368
534
 
369
535
  class DancerResolver2 {
370
536
  rel;
@@ -372,6 +538,34 @@ class DancerResolver2 {
372
538
  constructor(rel) {
373
539
  this.rel = rel;
374
540
  }
541
+ async records(q = {}) {
542
+ await this.rel.fetch("results");
543
+ await this.rel.require("dancers.records");
544
+ const db = this.rel.db("results");
545
+ const sql = "SELECT dancer_id, display, appearances, birth_lo, birth_hi, birth_source, birth_conflict, genders FROM dancers";
546
+ 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));
547
+ rows.sort((a, b) => a.dancer_id - b.dancer_id);
548
+ return {
549
+ tag: this.rel.tag,
550
+ records: rows.map((r) => ({
551
+ ...r,
552
+ scope: r.dancer_id >= FEIS_LOCAL_ID_FLOOR2 ? "per-release" : "stable",
553
+ birth_conflict: r.birth_conflict === 1,
554
+ genders: r.genders === "" ? [] : r.genders.split(",")
555
+ }))
556
+ };
557
+ }
558
+ async years(q = {}) {
559
+ await this.rel.fetch("results");
560
+ await this.rel.require("dancers.years");
561
+ const db = this.rel.db("results");
562
+ const sql = "SELECT dancer_id, year, region, region_source, nationals, nationals_source, geohash FROM dancer_years";
563
+ const yearSql = q.year === undefined ? "" : " AND year = ?";
564
+ const yearArgs = q.year === undefined ? [] : [q.year];
565
+ const rows = q.ids === undefined ? db.query(`${sql} WHERE 1${yearSql} ORDER BY dancer_id, year`).all(...yearArgs) : idChunks(q.ids).flatMap((c) => db.query(`${sql} WHERE dancer_id IN (${c.map(() => "?").join(",")})${yearSql}`).all(...c, ...yearArgs));
566
+ rows.sort((a, b) => a.dancer_id - b.dancer_id || a.year - b.year);
567
+ return { tag: this.rel.tag, years: rows };
568
+ }
375
569
  async resolve(q) {
376
570
  const db = await this.rel.indexDb("dancers");
377
571
  const asked = dancerName(q.name, q.order === "unknown" ? undefined : q.order);
@@ -1067,6 +1261,12 @@ class FeisFinder2 {
1067
1261
  constructor(rel) {
1068
1262
  this.rel = rel;
1069
1263
  }
1264
+ async dates() {
1265
+ await this.rel.fetch("results");
1266
+ await this.rel.require("feises.dates");
1267
+ const dates = this.rel.db("results").query("SELECT feis_id, min(date_lo) AS date_lo, max(date_hi) AS date_hi FROM feises GROUP BY feis_id ORDER BY feis_id").all();
1268
+ return { tag: this.rel.tag, dates };
1269
+ }
1070
1270
  async find(q) {
1071
1271
  const db = await this.rel.indexDb("feises");
1072
1272
  const where = [];
@@ -2298,6 +2498,74 @@ var FEATURES2 = [
2298
2498
  since: 265,
2299
2499
  calls: "comps.resolve, comps.syllabus, comps.syllabusFor, registrations.resolve (the comps index)"
2300
2500
  },
2501
+ {
2502
+ feature: "results.stream",
2503
+ needs: [
2504
+ "results:competitions.round",
2505
+ "results:competitions.feis_level",
2506
+ "results:competitions.level_printed",
2507
+ "results:competitions.age_printed",
2508
+ "results:results.yob_printed",
2509
+ "results:results.primary_source",
2510
+ "results:results.platform_dancer_id",
2511
+ "results:results.relative_place",
2512
+ "results:feises.date_lo",
2513
+ "results:feises.date_hi"
2514
+ ],
2515
+ since: 255,
2516
+ calls: "results.stream, results.entries (scopes transcribed, platform, all)"
2517
+ },
2518
+ {
2519
+ feature: "majors.stream",
2520
+ needs: [
2521
+ "majors:competitions.round",
2522
+ "majors:competitions.feis_level",
2523
+ "majors:results.yob_printed",
2524
+ "majors:results.primary_source",
2525
+ "majors:results.relative_place",
2526
+ "majors:feises.date_lo"
2527
+ ],
2528
+ since: 255,
2529
+ calls: "results.stream, results.entries (scope majors)"
2530
+ },
2531
+ {
2532
+ feature: "majors.tabs",
2533
+ needs: ["majors:tabs.address", "majors:tabs.true_round"],
2534
+ since: 215,
2535
+ calls: "majors.tabulated"
2536
+ },
2537
+ {
2538
+ feature: "feises.dates",
2539
+ needs: ["results:feises.feis_id", "results:feises.date_lo", "results:feises.date_hi"],
2540
+ since: 245,
2541
+ calls: "feises.dates"
2542
+ },
2543
+ {
2544
+ feature: "dancers.records",
2545
+ needs: [
2546
+ "results:dancers.display",
2547
+ "results:dancers.appearances",
2548
+ "results:dancers.birth_lo",
2549
+ "results:dancers.birth_hi",
2550
+ "results:dancers.birth_source",
2551
+ "results:dancers.birth_conflict",
2552
+ "results:dancers.genders"
2553
+ ],
2554
+ since: 215,
2555
+ calls: "dancers.records"
2556
+ },
2557
+ {
2558
+ feature: "dancers.years",
2559
+ needs: [
2560
+ "results:dancer_years.region",
2561
+ "results:dancer_years.region_source",
2562
+ "results:dancer_years.nationals",
2563
+ "results:dancer_years.nationals_source",
2564
+ "results:dancer_years.geohash"
2565
+ ],
2566
+ since: 245,
2567
+ calls: "dancers.years"
2568
+ },
2301
2569
  {
2302
2570
  feature: "teams",
2303
2571
  needs: [
@@ -2929,4 +3197,4 @@ function releases2(opts = {}) {
2929
3197
  return new ReleaseManager2(opts);
2930
3198
  }
2931
3199
 
2932
- export { DATABASES2, planAsset2, sha256File2, unzstdTo2, NAME_LEVELS2, nameLevel2, FEIS_LOCAL_ID_FLOOR2, ResultFinder2, MajorsFinder2, nameOrderOf, DancerResolver2, readPopulation2, canonicalOf2, CompResolver2, NO_CALENDAR2, FeisFinder2, 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 };
3200
+ export { DATABASES2, planAsset2, sha256File2, unzstdTo2, NAME_LEVELS2, nameLevel2, entryRank2, FEIS_LOCAL_ID_FLOOR2, ResultFinder2, MajorsFinder2, nameOrderOf, DancerResolver2, readPopulation2, canonicalOf2, CompResolver2, NO_CALENDAR2, FeisFinder2, 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-v8e0qe9f.js";
7
+ } from "./chunks/index-8gna56w2.js";
8
8
  import"./chunks/index-hne1wadw.js";
9
9
  import"./chunks/index-hghdy980.js";
10
10
 
@@ -50,6 +50,21 @@ var USAGE = `usage: idq <verb> [--cache <dir>] [--repo <owner/name>] [--json]
50
50
  majors [--release <spec>] [--event <e>] [--year <y>] [--comp <competition>] [--dancer <id>]
51
51
  the majors: every event and its years; an event's
52
52
  competitions; a competition's, an event's or a dancer's rows
53
+ stream [--release <spec>] --scope majors|transcribed|platform|all [--entries] [--event <e,...>]
54
+ [--year <y> | --year <from>..<to>] [--tier <t,...>] [--category <c,...>]
55
+ [--level <feis_level,...>] [--dancer <id,...>] [--school <school_id>] [--primary]
56
+ every row a scope holds, one JSON line each, never the
57
+ whole table in memory; --entries one per address (the row
58
+ it stands for, with its recall order)
59
+ records [--release <spec>] [--dancer <id,...>]
60
+ every dancer's record: display, appearances, birth window,
61
+ populations
62
+ records --years [--release <spec>] [--dancer <id,...>] [--year <y>]
63
+ each dancer's region, nationals and place per year
64
+ feis-dates [--release <spec>] every feis's first and last day
65
+ tabulated [--release <spec>] [--min-round <n>]
66
+ the majors addresses the marks sheets tabulate to round n
67
+ or later (default 3)
53
68
  teams [--release <spec>] [--event <e>] [--year <y>] [--rosters | --joins]
54
69
  the team result rows of an event-year; with --rosters its
55
70
  roster teams and their dancers; with --joins how each roster
@@ -107,7 +122,12 @@ var VALUE = new Set([
107
122
  "--limit",
108
123
  "--event",
109
124
  "--title",
110
- "--for"
125
+ "--for",
126
+ "--scope",
127
+ "--tier",
128
+ "--category",
129
+ "--level",
130
+ "--min-round"
111
131
  ]);
112
132
  function parseArgs(argv) {
113
133
  const positional = [];
@@ -319,6 +339,45 @@ async function resolveVerb(rel, what, args, p, say, stdin) {
319
339
  throw new Error("resolve comp|dancer|registrations");
320
340
  }
321
341
  }
342
+ var SCOPES = ["majors", "transcribed", "platform", "all"];
343
+ var listOf = (v) => v.split(",").map((x) => x.trim()).filter((x) => x !== "");
344
+ var idsOf = (v) => v === undefined ? undefined : listOf(v).map(Number);
345
+ function streamQueryOf(p) {
346
+ const scope = p.flags.get("--scope");
347
+ if (scope === undefined || !SCOPES.includes(scope))
348
+ throw new Error(`stream takes --scope ${SCOPES.join("|")}`);
349
+ const q = { scope };
350
+ const event = p.flags.get("--event");
351
+ if (event !== undefined)
352
+ q.event = listOf(event);
353
+ const year = p.flags.get("--year");
354
+ if (year !== undefined) {
355
+ const m = /^(\d{4})\.\.(\d{4})$/.exec(year);
356
+ q.year = m === null ? Number(year) : [Number(m[1]), Number(m[2])];
357
+ if (typeof q.year === "number" && !Number.isInteger(q.year))
358
+ throw new Error(`--year takes a year or <from>..<to>, not "${year}"`);
359
+ }
360
+ for (const [flag, key] of [
361
+ ["--tier", "tier"],
362
+ ["--category", "category"]
363
+ ]) {
364
+ const v = p.flags.get(flag);
365
+ if (v !== undefined)
366
+ q[key] = listOf(v);
367
+ }
368
+ const level = p.flags.get("--level");
369
+ if (level !== undefined)
370
+ q.feis_level = listOf(level).map(Number);
371
+ const dancers = idsOf(p.flags.get("--dancer"));
372
+ if (dancers !== undefined)
373
+ q.dancer_id = dancers;
374
+ const school = p.flags.get("--school");
375
+ if (school !== undefined)
376
+ q.school_id = school;
377
+ if (p.bools.has("--primary"))
378
+ q.primary = true;
379
+ return q;
380
+ }
322
381
  function eventYearOf(p) {
323
382
  const event = p.flags.get("--event");
324
383
  const year = p.flags.get("--year");
@@ -644,6 +703,79 @@ async function main(argv, io = {}) {
644
703
  rel.close();
645
704
  }
646
705
  }
706
+ case "stream": {
707
+ const q = streamQueryOf(p);
708
+ const rel = await openRelease2(spec, { ...opts, assets: [] });
709
+ try {
710
+ let n = 0;
711
+ const rows = p.bools.has("--entries") ? rel.results.entries(q) : rel.results.stream(q);
712
+ for await (const r of rows) {
713
+ say(JSON.stringify(r));
714
+ n++;
715
+ }
716
+ log(`${n} ${p.bools.has("--entries") ? "entries" : "rows"} in ${rel.tag}`);
717
+ return n === 0 ? 1 : 0;
718
+ } finally {
719
+ rel.close();
720
+ }
721
+ }
722
+ case "records": {
723
+ const rel = await openRelease2(spec, { ...opts, assets: [] });
724
+ try {
725
+ const ids = idsOf(p.flags.get("--dancer"));
726
+ if (p.bools.has("--years")) {
727
+ const year = p.flags.get("--year");
728
+ const a = await rel.dancers.years({
729
+ ...ids !== undefined ? { ids } : {},
730
+ ...year !== undefined ? { year: Number(year) } : {}
731
+ });
732
+ out(a, () => {
733
+ for (const y of a.years)
734
+ say(` ${y.dancer_id} ${y.year} ${y.region ?? "-"} (${y.region_source ?? "-"}) nationals ${y.nationals ?? "-"} ${y.geohash ?? "-"}`);
735
+ say(`${a.years.length} dancer-year(s) in ${a.tag}`);
736
+ });
737
+ return a.years.length === 0 ? 1 : 0;
738
+ }
739
+ const a = await rel.dancers.records(ids !== undefined ? { ids } : {});
740
+ out(a, () => {
741
+ for (const r of a.records)
742
+ say(` ${r.dancer_id} ${r.display} ${r.appearances} entries born ${r.birth_lo ?? "?"}..${r.birth_hi ?? "?"} (${r.birth_source}${r.birth_conflict ? ", conflict" : ""}) ${r.genders.join(",") || "-"}`);
743
+ say(`${a.records.length} dancer(s) in ${a.tag}`);
744
+ });
745
+ return a.records.length === 0 ? 1 : 0;
746
+ } finally {
747
+ rel.close();
748
+ }
749
+ }
750
+ case "feis-dates": {
751
+ const rel = await openRelease2(spec, { ...opts, assets: [] });
752
+ try {
753
+ const a = await rel.feises.dates();
754
+ out(a, () => {
755
+ for (const d of a.dates)
756
+ say(` ${d.feis_id} ${d.date_lo ?? "?"}..${d.date_hi ?? "?"}`);
757
+ say(`${a.dates.length} feis(es) in ${a.tag}`);
758
+ });
759
+ return a.dates.length === 0 ? 1 : 0;
760
+ } finally {
761
+ rel.close();
762
+ }
763
+ }
764
+ case "tabulated": {
765
+ const rel = await openRelease2(spec, { ...opts, assets: [] });
766
+ try {
767
+ const min = p.flags.get("--min-round");
768
+ const a = await rel.majors.tabulated(min !== undefined ? { min_true_round: Number(min) } : {});
769
+ out(a, () => {
770
+ for (const x of a.addresses)
771
+ say(` ${x}`);
772
+ say(`${a.addresses.length} address(es) tabulated to round ${a.min_true_round} or later in ${a.tag}`);
773
+ });
774
+ return a.addresses.length === 0 ? 1 : 0;
775
+ } finally {
776
+ rel.close();
777
+ }
778
+ }
647
779
  case "teams": {
648
780
  const rel = await openRelease2(spec, { ...opts, assets: [] });
649
781
  try {
@@ -783,5 +915,6 @@ export {
783
915
  USAGE,
784
916
  jsonLines,
785
917
  main,
786
- parseArgs
918
+ parseArgs,
919
+ streamQueryOf
787
920
  };
package/dist/index.js CHANGED
@@ -6,6 +6,7 @@ import {
6
6
  unzstdTo2,
7
7
  NAME_LEVELS2,
8
8
  nameLevel2,
9
+ entryRank2,
9
10
  FEIS_LOCAL_ID_FLOOR2,
10
11
  ResultFinder2,
11
12
  MajorsFinder2,
@@ -44,7 +45,7 @@ import {
44
45
  openCached2,
45
46
  ReleaseManager2,
46
47
  releases2
47
- } from "./chunks/index-v8e0qe9f.js";
48
+ } from "./chunks/index-8gna56w2.js";
48
49
  import {
49
50
  cacheRoot2,
50
51
  repoOf2
@@ -87,6 +88,7 @@ export {
87
88
  dancerResolverOf2 as dancerResolverOf,
88
89
  defaultBuilders2 as defaultBuilders,
89
90
  ensureIndex2 as ensureIndex,
91
+ entryRank2 as entryRank,
90
92
  fallbackRemote2 as fallbackRemote,
91
93
  ghRemote2 as ghRemote,
92
94
  httpsRemote2 as httpsRemote,
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env bun
2
2
  import type { OpenOptions } from "./release.js";
3
- export declare const USAGE = "usage: idq <verb> [--cache <dir>] [--repo <owner/name>] [--json]\n\n config repo <owner/name> set the user-local repository the releases come from\n list [--remote] the cached releases (size, files, indexes, the pin), and\n with --remote the published tags\n fetch [--release <spec>] [--assets results,majors,<file>...]\n download, verify and cache a release (default: results)\n pin <spec> the user-local pin: what a bare open (spec \"pinned\") opens\n unpin remove the pin (a bare open is then \"latest\")\n current the pin, and whether it is cached\n add <dir> --tag <tag> add a release built locally (its MANIFEST.json and files)\n to the cache under its tag\n index [--release <spec>] [<name>...] [--force]\n build the per-tag indexes (default: every one)\n verify [--release <spec>] re-hash every cached file against its receipt\n capabilities [--release <spec>] what the release carries of what the client reads, from\n its own schema: each feature full or missing (its calls\n refuse, naming the first cut that publishes it)\n prune [--keep N] [--dry-run] remove all but the N newest cached releases (default 1)\n and the pinned one\n rm <tag> [--dry-run] remove one cached release (not the pinned one)\n drop-indexes [<tag>] [--dry-run] remove the index files (of one release, or of every one)\n feis [--release <spec>] [--platform <p>] [--code <c>] [--id <id>] [--name <glob>] [--year <y>]\n the feises a query names\n series [--release <spec>] <name|host code|feis id>\n the series a name, a host code (fweb:ABC) or any feis id\n names: its years, hosts, codes and next instance\n school [--release <spec>] <print|school_id> [--region <code>]\n a school print or id to the published schools\n dancer [--release <spec>] <name> | --surname <s> | --like <glob> | --id <dancer_id>\n [--order surname-first|given-first] [--school <print|id>]\n [--yob <y> | --band <U10> [--year <y>] | --comp <competition>] [--as-of <date>]\n [--population girls|boys] [--rows]\n the dancers a name reaches, each with its evidence\n (name level, age, school, population) and the arm; the results of\n the one dancer where one answers (or --rows for each)\n results [--release <spec>] [--dancer <id>] [--feis <id>] [--comp <competition>]\n [--school <print|id>] [--year <y>] [--limit <n>]\n the published result rows, prints and ids\n majors [--release <spec>] [--event <e>] [--year <y>] [--comp <competition>] [--dancer <id>]\n the majors: every event and its years; an event's\n competitions; a competition's, an event's or a dancer's rows\n teams [--release <spec>] [--event <e>] [--year <y>] [--rosters | --joins]\n the team result rows of an event-year; with --rosters its\n roster teams and their dancers; with --joins how each roster\n team meets the results\n events [--release <spec>] [--event <e>] [--year <y>]\n each event-year's dates and place\n teacher [--release <spec>] <name|TD number>\n the staff lists' people: where they taught, the register,\n where they sat\n adjudicator [--release <spec>] <name|TD number>\n the register's and the panels' people: the same answer\n comp [--release <spec>] <feis> <code> [--year <y>] [--title <t>]\n a feis's own comp code to the comp it names, through that\n feis's syllabus (or the prior year's)\n syllabus [--release <spec>] <feis> [year]\n one feis-year's comps, in printed order\n syllabus [--release <spec>] --for <series> [year]\n a series' syllabus for a year: that year's where held,\n else the newest, saying which and what it is read from\n resolve comp [<feis> <code>] [--year <y>] [--title <t>]\n resolve dancer [<name>] [--school <s>] [--comp <feis>/<code> | --yob <y> | --band <b> --year <y>]\n [--order <o>] [--as-of <date>] [--population girls|boys]\n resolve registrations\n JSONL out, one answer per line; with no positional the\n questions are JSONL on stdin, one per line (a comp:\n {feis, code, year?, title?}, a dancer: {name, school?, age?,\n population?}, a registration: {name, school?, codes[], feis,\n year?, population?})\n\n <spec>: pinned (the default) | latest | a cut (42, 42.1) | a tag (archive-YYYY.MM.DD-N)";
3
+ import type { StreamQuery } from "./stream.js";
4
+ export declare const USAGE = "usage: idq <verb> [--cache <dir>] [--repo <owner/name>] [--json]\n\n config repo <owner/name> set the user-local repository the releases come from\n list [--remote] the cached releases (size, files, indexes, the pin), and\n with --remote the published tags\n fetch [--release <spec>] [--assets results,majors,<file>...]\n download, verify and cache a release (default: results)\n pin <spec> the user-local pin: what a bare open (spec \"pinned\") opens\n unpin remove the pin (a bare open is then \"latest\")\n current the pin, and whether it is cached\n add <dir> --tag <tag> add a release built locally (its MANIFEST.json and files)\n to the cache under its tag\n index [--release <spec>] [<name>...] [--force]\n build the per-tag indexes (default: every one)\n verify [--release <spec>] re-hash every cached file against its receipt\n capabilities [--release <spec>] what the release carries of what the client reads, from\n its own schema: each feature full or missing (its calls\n refuse, naming the first cut that publishes it)\n prune [--keep N] [--dry-run] remove all but the N newest cached releases (default 1)\n and the pinned one\n rm <tag> [--dry-run] remove one cached release (not the pinned one)\n drop-indexes [<tag>] [--dry-run] remove the index files (of one release, or of every one)\n feis [--release <spec>] [--platform <p>] [--code <c>] [--id <id>] [--name <glob>] [--year <y>]\n the feises a query names\n series [--release <spec>] <name|host code|feis id>\n the series a name, a host code (fweb:ABC) or any feis id\n names: its years, hosts, codes and next instance\n school [--release <spec>] <print|school_id> [--region <code>]\n a school print or id to the published schools\n dancer [--release <spec>] <name> | --surname <s> | --like <glob> | --id <dancer_id>\n [--order surname-first|given-first] [--school <print|id>]\n [--yob <y> | --band <U10> [--year <y>] | --comp <competition>] [--as-of <date>]\n [--population girls|boys] [--rows]\n the dancers a name reaches, each with its evidence\n (name level, age, school, population) and the arm; the results of\n the one dancer where one answers (or --rows for each)\n results [--release <spec>] [--dancer <id>] [--feis <id>] [--comp <competition>]\n [--school <print|id>] [--year <y>] [--limit <n>]\n the published result rows, prints and ids\n majors [--release <spec>] [--event <e>] [--year <y>] [--comp <competition>] [--dancer <id>]\n the majors: every event and its years; an event's\n competitions; a competition's, an event's or a dancer's rows\n stream [--release <spec>] --scope majors|transcribed|platform|all [--entries] [--event <e,...>]\n [--year <y> | --year <from>..<to>] [--tier <t,...>] [--category <c,...>]\n [--level <feis_level,...>] [--dancer <id,...>] [--school <school_id>] [--primary]\n every row a scope holds, one JSON line each, never the\n whole table in memory; --entries one per address (the row\n it stands for, with its recall order)\n records [--release <spec>] [--dancer <id,...>]\n every dancer's record: display, appearances, birth window,\n populations\n records --years [--release <spec>] [--dancer <id,...>] [--year <y>]\n each dancer's region, nationals and place per year\n feis-dates [--release <spec>] every feis's first and last day\n tabulated [--release <spec>] [--min-round <n>]\n the majors addresses the marks sheets tabulate to round n\n or later (default 3)\n teams [--release <spec>] [--event <e>] [--year <y>] [--rosters | --joins]\n the team result rows of an event-year; with --rosters its\n roster teams and their dancers; with --joins how each roster\n team meets the results\n events [--release <spec>] [--event <e>] [--year <y>]\n each event-year's dates and place\n teacher [--release <spec>] <name|TD number>\n the staff lists' people: where they taught, the register,\n where they sat\n adjudicator [--release <spec>] <name|TD number>\n the register's and the panels' people: the same answer\n comp [--release <spec>] <feis> <code> [--year <y>] [--title <t>]\n a feis's own comp code to the comp it names, through that\n feis's syllabus (or the prior year's)\n syllabus [--release <spec>] <feis> [year]\n one feis-year's comps, in printed order\n syllabus [--release <spec>] --for <series> [year]\n a series' syllabus for a year: that year's where held,\n else the newest, saying which and what it is read from\n resolve comp [<feis> <code>] [--year <y>] [--title <t>]\n resolve dancer [<name>] [--school <s>] [--comp <feis>/<code> | --yob <y> | --band <b> --year <y>]\n [--order <o>] [--as-of <date>] [--population girls|boys]\n resolve registrations\n JSONL out, one answer per line; with no positional the\n questions are JSONL on stdin, one per line (a comp:\n {feis, code, year?, title?}, a dancer: {name, school?, age?,\n population?}, a registration: {name, school?, codes[], feis,\n year?, population?})\n\n <spec>: pinned (the default) | latest | a cut (42, 42.1) | a tag (archive-YYYY.MM.DD-N)";
4
5
  export interface Parsed {
5
6
  positional: string[];
6
7
  flags: Map<string, string>;
@@ -15,6 +16,7 @@ type Line<T> = {
15
16
  error: string;
16
17
  };
17
18
  export declare function jsonLines<T>(text: string, check: (v: T) => string | null): Line<T>[];
19
+ export declare function streamQueryOf(p: Parsed): StreamQuery;
18
20
  export declare function main(argv: readonly string[], io?: {
19
21
  say?: (s: string) => void;
20
22
  log?: (s: string) => void;
@@ -140,10 +140,43 @@ export interface DancerRecord {
140
140
  export declare const PACKAGE_KEY_PARTS: readonly string[];
141
141
  export declare const dancersIndex: IndexBuilder;
142
142
  export declare function yearOf(asOf: string | undefined): number | null;
143
+ export interface DancerBulkRecord {
144
+ dancer_id: number;
145
+ display: string;
146
+ scope: "per-release" | "stable";
147
+ appearances: number;
148
+ birth_lo: number | null;
149
+ birth_hi: number | null;
150
+ birth_source: string;
151
+ birth_conflict: boolean;
152
+ genders: string[];
153
+ }
154
+ export interface DancerYear {
155
+ dancer_id: number;
156
+ year: number;
157
+ region: string | null;
158
+ region_source: string | null;
159
+ nationals: string | null;
160
+ nationals_source: string | null;
161
+ geohash: string | null;
162
+ }
143
163
  export declare class DancerResolver {
144
164
  private readonly rel;
145
165
  private readonly printIds;
146
166
  constructor(rel: Release);
167
+ records(q?: {
168
+ ids?: readonly number[];
169
+ }): Promise<{
170
+ tag: string;
171
+ records: DancerBulkRecord[];
172
+ }>;
173
+ years(q?: {
174
+ ids?: readonly number[];
175
+ year?: number;
176
+ }): Promise<{
177
+ tag: string;
178
+ years: DancerYear[];
179
+ }>;
147
180
  resolve(q: DancerQuery): Promise<DancerAnswer>;
148
181
  search(q: DancerSearchQuery): Promise<DancerAnswer>;
149
182
  private capped;
@@ -77,6 +77,14 @@ export declare const NO_CALENDAR = "the release holds a feis-year once its resul
77
77
  export declare class FeisFinder {
78
78
  private readonly rel;
79
79
  constructor(rel: Release);
80
+ dates(): Promise<{
81
+ tag: string;
82
+ dates: {
83
+ feis_id: string;
84
+ date_lo: string | null;
85
+ date_hi: string | null;
86
+ }[];
87
+ }>;
80
88
  find(q: FeisQuery): Promise<FeisAnswer>;
81
89
  codeStem(token: string, stem: string): Promise<string[]>;
82
90
  seriesOf(brand: string): Promise<Series>;
@@ -1,6 +1,6 @@
1
1
  export { type AssetPlan, DATABASES, type DatabaseName, type LandedFile, type Receipt, type ReleaseManifest, planAsset, } from "./assets.js";
2
2
  export { sha256File, unzstdTo } from "./bytes.js";
3
- export { type AgeAsked, type AgeEvidence, type DancerAnswer, type DancerCandidate, type DancerQuery, type DancerRecord, DancerResolver, type DancerSchool, type DancerSearchQuery, type ExcludedCandidate, FEIS_LOCAL_ID_FLOOR, type PopulationEvidence, type SchoolEvidence, readPopulation, } from "./dancers.js";
3
+ export { type AgeAsked, type AgeEvidence, type DancerAnswer, type DancerBulkRecord, type DancerCandidate, type DancerQuery, type DancerRecord, DancerResolver, type DancerSchool, type DancerSearchQuery, type DancerYear, type ExcludedCandidate, FEIS_LOCAL_ID_FLOOR, type PopulationEvidence, type SchoolEvidence, readPopulation, } from "./dancers.js";
4
4
  export { NAME_LEVELS, type NameLevel, nameLevel } from "./dancers-names.js";
5
5
  export { type Comp, type CompAnswer, type CompKind, type CompQuery, CompResolver, type CompSource, type FeisPlace, type SyllabusAnswer, type SyllabusForAnswer, type SyllabusSource, type TitleCheck, canonicalOf, } from "./comps.js";
6
6
  export { type ClientOptions, cacheRoot, repoOf } from "./config.js";
@@ -15,5 +15,6 @@ export { type MajorCompetition, type MajorEvent, MajorsFinder, type ResultAnswer
15
15
  export { type DancerAnswerLike, type DancerQueryLike, type DancerResolverLike, type DancerUnavailable, type RegistrationRow, type ResolvedDancer, RegistrationResolver, type ResolvedRegistration, dancerResolverOf, youngest, } from "./registrations.js";
16
16
  export { type SchoolAnswer, type SchoolCandidate, type SchoolQuery, SchoolResolver, type SchoolRung, } from "./schools.js";
17
17
  export { type Capability, FEATURES, type Feature, type SchemaProbe, capabilityOf, columnsOf, needsRelease, } from "./schema.js";
18
+ export { type EntryRow, type StreamQuery, type StreamRow, type StreamScope, entryRank, } from "./stream.js";
18
19
  export { EventFinder, type EventYear, type EventYearQuery, type ResultRoster, type RosterMember, type RosterTeam, TeamFinder, type TeamResult, } from "./teams.js";
19
20
  export { ARCHIVE_TAG, type ArchiveTag, type ReleaseSpec, compareTagsNewestFirst, cutOf, parseArchiveTag, parseReleaseSpec, pickTag, renderArchiveTag, } from "./tag.js";
@@ -1,4 +1,5 @@
1
1
  import type { Release } from "./release.js";
2
+ import { type EntryRow, type StreamQuery, type StreamRow } from "./stream.js";
2
3
  export declare const FEIS_LOCAL_ID_FLOOR = 17179869184;
3
4
  export interface ResultQuery {
4
5
  dancer_id?: number;
@@ -43,6 +44,9 @@ export declare class ResultFinder {
43
44
  private readonly rel;
44
45
  constructor(rel: Release);
45
46
  query(q: ResultQuery): Promise<ResultAnswer>;
47
+ private streamDb;
48
+ stream(q: StreamQuery): AsyncGenerator<StreamRow>;
49
+ entries(q: StreamQuery): AsyncGenerator<EntryRow>;
46
50
  }
47
51
  export interface MajorEvent {
48
52
  event: string;
@@ -78,5 +82,12 @@ export declare class MajorsFinder {
78
82
  tag: string;
79
83
  competitions: MajorCompetition[];
80
84
  }>;
85
+ tabulated(q?: {
86
+ min_true_round?: number;
87
+ }): Promise<{
88
+ tag: string;
89
+ min_true_round: number;
90
+ addresses: string[];
91
+ }>;
81
92
  results(q: ResultQuery): Promise<ResultAnswer>;
82
93
  }
@@ -0,0 +1,71 @@
1
+ import type { Database } from "bun:sqlite";
2
+ export type StreamScope = "majors" | "transcribed" | "platform" | "all";
3
+ export interface StreamQuery {
4
+ scope: StreamScope;
5
+ event?: string | string[];
6
+ year?: number | [number, number];
7
+ tier?: string[];
8
+ category?: string[];
9
+ feis_level?: number[];
10
+ dancer_id?: number[];
11
+ school_id?: string;
12
+ primary?: boolean;
13
+ }
14
+ export interface StreamRow {
15
+ address: string;
16
+ competition: string;
17
+ event: string;
18
+ year: number | null;
19
+ tier: string;
20
+ comp_no: string | null;
21
+ title: string | null;
22
+ category: string | null;
23
+ age: string | null;
24
+ age_printed: string | null;
25
+ gender: string | null;
26
+ competitors: number | null;
27
+ feis_level: number | null;
28
+ level_printed: string | null;
29
+ date_printed: string | null;
30
+ source: string;
31
+ round: string | null;
32
+ feis_id: string | null;
33
+ feis_date_lo: string | null;
34
+ feis_date_hi: string | null;
35
+ section: string;
36
+ card: string;
37
+ place: number | null;
38
+ place_printed: string;
39
+ tie: number;
40
+ relative_place: number | null;
41
+ atts: string[];
42
+ withdrawn: string | null;
43
+ qualifier: string | null;
44
+ name_printed: string;
45
+ name: string | null;
46
+ school_printed: string;
47
+ school_loc: string;
48
+ school_id: string | null;
49
+ dancer_id: number | null;
50
+ dancer_scope: "per-release" | "stable" | null;
51
+ yob: number | null;
52
+ yob_printed: string | null;
53
+ points_printed: string | null;
54
+ platform_dancer_id: string | null;
55
+ primary_source: string | null;
56
+ }
57
+ export interface EntryRow extends StreamRow {
58
+ recall_place: number | null;
59
+ }
60
+ export declare function entryRank(section: string): number;
61
+ export declare const scopeDb: (s: StreamScope) => "majors" | "results";
62
+ export declare function streamSql(q: StreamQuery): {
63
+ sql: string;
64
+ args: (string | number)[];
65
+ };
66
+ export declare function feisDates(db: Database): Map<string, {
67
+ lo: string | null;
68
+ hi: string | null;
69
+ }>;
70
+ export declare function streamRows(db: Database, q: StreamQuery): AsyncGenerator<StreamRow>;
71
+ export declare function entryRows(db: Database, q: StreamQuery): AsyncGenerator<EntryRow>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finbheara/archive-client",
3
- "version": "0.4.0",
3
+ "version": "0.5.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",