@finbheara/domain 0.31.0 → 0.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -16,6 +16,10 @@ person names.
16
16
  team's `averageAge`.
17
17
  - **Dances**: a spoken or printed set dance or céilí dance in, the dances it may name out,
18
18
  with tempo rules and hand counts.
19
+ - **Competition titles**: a spoken or printed title (`girls 16 to 17`, `U17 BOYS R3`) in, the
20
+ syllabus entries it names and its round out, read through `Age` on the entries' own ladder.
21
+ - **Schedules**: which entries, and which round, a hall runs at a time, and a printed running
22
+ order read into that shape.
19
23
  - **A database-backed index** of any of these nouns: the slots an in-memory name map hashes
20
24
  on, stored as indexed columns of a SQLite table you own.
21
25
 
@@ -45,6 +49,8 @@ ESM, Node ≥ 22 or Bun ≥ 1.4.2. MIT. Not affiliated with or endorsed by any d
45
49
  | `@finbheara/domain/dance-types` | `danceTypes()`, `DANCE_TYPES`, `rhythmOfTime`, and the shared `DanceMatch`/`DanceRelation`/`SetRhythm`/`TimeSignature` types |
46
50
  | `@finbheara/domain/promotion` | `samePromotedRow`, `samePromotedSchool`, `samePromotedHeir`, `samePromotedName`, `questionMarksRead`, `foldCharSynonyms`: is a row the same row after a promotion (below) |
47
51
  | `@finbheara/domain/person` | `seatName`, `dancerName`, `teacherName`, `roleName`, `roleSentinelOf`, `rolePrintsNoName`, `dancerPrintsNoName`, the role lists and `SHARED_ROW_SENTINELS`: a person's print read in its role (below) |
52
+ | `@finbheara/domain/title` | `compTitle`, `titleRelation`, `matchTitle`, `ladderOf`, `populationIn`, `populationRelation`, `roundNumbersIn`: a competition title read and matched onto syllabus entries (below) |
53
+ | `@finbheara/domain/schedule` | `scheduleAt`, `sameHall`, `readRunningOrder`, the `ScheduleSlot` type: which entries a hall runs (below) |
48
54
 
49
55
  The library does no I/O and installs nothing process-wide. Every reader has a default and a
50
56
  factory: build your own with options when the default is not what you want.
@@ -370,6 +376,66 @@ team.fitsIn(Age.from("U40", { year: 2026 })); // true
370
376
  averageAge([u12]); // throws AgeMisuseError "not-exact"
371
377
  ```
372
378
 
379
+ ## Match a competition title
380
+
381
+ An announcer names a competition by its title more often than by its number, and never quite
382
+ as the syllabus prints it. `matchTitle` reads a title's age band with `Age.fromTitle`, reads every
383
+ entry's band on the same ladder (the rungs the entries of the title's population offer), and
384
+ answers the entries whose band is the same and whose population (girls and ladies one line, boys
385
+ and men another) the title does not contradict. The entries are yours, as you hold them: a
386
+ `title`, an optional `age` where the syllabus prints the band apart, and anything else you keep.
387
+
388
+ ```ts
389
+ import { compTitle, matchTitle } from "@finbheara/domain/title";
390
+
391
+ const entries = [
392
+ { code: "27", title: "Girls 15 - 16", age: "15-16" },
393
+ { code: "28", title: "Girls 14 - 15", age: "14-15" },
394
+ { code: "29", title: "Girls 13 - 14", age: "13-14" },
395
+ { code: "8", title: "Boys 14 - 15", age: "14-15" },
396
+ ];
397
+ matchTitle("under 15 girls", entries).entries; // [{ code: "28", ... }]: a rung at 14
398
+ matchTitle("girls 14 to 15", entries).entries; // the same entry
399
+ matchTitle("boys 14-15 R2", entries).round; // 2
400
+
401
+ const t = compTitle("U17 BOYS R3");
402
+ t.age.ag1; // "u17"
403
+ t.population; // "boys"
404
+ t.round; // 3
405
+ compTitle("Girls Under 13 B").split; // "b"
406
+ compTitle("Treble Reel Special").invalidReason(); // "no-age"
407
+ ```
408
+
409
+ A spoken dash is read as a space: `boys u-17`, `Boys U-17` and `boys u- 17` read as `boys u17`,
410
+ `girls o-16` as `girls o16`, and `under-15` as `under 15`.
411
+
412
+ A band split into parallel competitions (`Girls Under 13 A`, `Girls Under 13 B`) answers every
413
+ split, unless the title prints the letter. A title naming no band names nothing. Hand in the
414
+ entries of the kind you mean: a solo championship's ages are often a team event's too.
415
+
416
+ ## Ask a schedule
417
+
418
+ A split band's competitions share their card numbers, so a card heard on a stage fits each of
419
+ them. Only the schedule says which split runs in which hall. A schedule is slots: an entry, a
420
+ round where one is printed, a hall, a day, and a start and an end in whatever clock you keep.
421
+
422
+ ```ts
423
+ import { readRunningOrder, scheduleAt } from "@finbheara/domain/schedule";
424
+
425
+ const slots = [
426
+ { hall: "Hall A", day: 1, start: 540, end: 660, round: 1, entry: girlsU13A },
427
+ { hall: "Hall B", day: 1, start: 540, end: 660, round: 1, entry: girlsU13B },
428
+ ];
429
+ scheduleAt(slots, { hall: "HALL B", day: 1, at: 600 }); // the B split
430
+
431
+ // a running order printed for one hall: titles cut after each round mark and at each comma,
432
+ // an age list sharing the population before it
433
+ readRunningOrder("U17 BOYS R3 U13 GIRLS B R1 & R2", entries, { hall: "Hall B", day: 2 });
434
+ ```
435
+
436
+ A slot with no times is its hall's whole day. `sameHall` reads `Hall A`, `HALL A` and `hall-a` as
437
+ one hall. The module reads no file: a full schedule, with its times, is yours to transcribe.
438
+
373
439
  ## Index names in a database
374
440
 
375
441
  `createNameMap` (from `@finbheara/names`) and `mapOfSchools` hold their names in memory. An