@finbheara/domain 0.47.0 → 0.49.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 +25 -2
- package/dist/age.js +1 -1
- package/dist/chunks/{place-y66f4138.js → place-k0p9af4f.js} +26 -2
- package/dist/chunks/{place-mpeps8tn.js → place-kmh59zkw.js} +6 -4
- package/dist/schedule.js +2 -2
- package/dist/title.js +6 -2
- package/dist/types/age-types.d.ts +1 -1
- package/dist/types/title.d.ts +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -49,7 +49,7 @@ ESM, Node ≥ 22 or Bun ≥ 1.4.2. MIT. Not affiliated with or endorsed by any d
|
|
|
49
49
|
| `@finbheara/domain/dance-types` | `danceTypes()`, `DANCE_TYPES`, `rhythmOfTime`, and the shared `DanceMatch`/`DanceRelation`/`SetRhythm`/`TimeSignature` types |
|
|
50
50
|
| `@finbheara/domain/promotion` | `samePromotedRow`, `samePromotedSchool`, `samePromotedHeir`, `samePromotedName`, `questionMarksRead`, `foldCharSynonyms`: is a row the same row after a promotion (below) |
|
|
51
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) |
|
|
52
|
+
| `@finbheara/domain/title` | `compTitle`, `titleRelation`, `matchTitle`, `ladderOf`, `populationIn`, `populationRelation`, `roundNumbersIn`, `codePopulation`, `secondIn`: a competition title read and matched onto syllabus entries, a code's sex suffix, a second chance (below) |
|
|
53
53
|
| `@finbheara/domain/schedule` | `scheduleAt`, `sameHall`, `readRunningOrder`, the `ScheduleSlot` type: which entries a hall runs (below) |
|
|
54
54
|
|
|
55
55
|
The library does no I/O and installs nothing process-wide. Every reader has a default and a
|
|
@@ -302,6 +302,12 @@ canOverlap(u12, Age.from("U14", { year: 2026 }), dancer); // tru
|
|
|
302
302
|
Age.from("13 & Over", { year: 2026 }).interval(); // { lo: 13, hi: Infinity }, as "Over 13"
|
|
303
303
|
```
|
|
304
304
|
|
|
305
|
+
A range is another way of writing the `Under` band at its top: `11-13` reads as `U13`, and its
|
|
306
|
+
low number bounds nothing, since a youth ladder's range names the rung and not every age in it.
|
|
307
|
+
A range printed from 18 up is an age bracket, and its low number is its floor:
|
|
308
|
+
`Age.from("24-30", { year: 2026 })` is ages 24 to 29, born 1996 to 2001. A range that prints no
|
|
309
|
+
floor of 18 or more, on a ladder that offered the adult rung, floors at 18.
|
|
310
|
+
|
|
305
311
|
A competition title usually carries its age group in its words. `Age.fromTitle` finds the
|
|
306
312
|
phrase, reads it as `Age.from` would, and says where it was. A title that names no age group
|
|
307
313
|
is empty, not refused. Competition numbers are never read as ages.
|
|
@@ -341,7 +347,7 @@ youth band stands.
|
|
|
341
347
|
|
|
342
348
|
An adult word beside an and-over from 18 or a range from 18 reads both as well: `adult o40` and
|
|
343
349
|
`adult 18-29` are adult (`isAdult()`), and their `kind`, interval and birth years are the ones the
|
|
344
|
-
band alone reads on the same ladder
|
|
350
|
+
band alone reads on the same ladder. So
|
|
345
351
|
`Age.from("adult o40", { year: 2026 })` is born 1985 or earlier, as `o40` is. An and-over under
|
|
346
352
|
18 or a range opening under 18 (`Teen & Adult 13 & Over`) admits youth, and the band stands. TIDA's
|
|
347
353
|
`Senior Adult` beside an and-over is its oldest youth championship, an open championship that
|
|
@@ -424,6 +430,23 @@ A band split into parallel competitions (`Girls Under 13 A`, `Girls Under 13 B`)
|
|
|
424
430
|
split, unless the title prints the letter. A title naming no band names nothing. Hand in the
|
|
425
431
|
entries of the kind you mean: a solo championship's ages are often a team event's too.
|
|
426
432
|
|
|
433
|
+
A code may carry the sex where the title does not. `codePopulation` reads a suffix against every
|
|
434
|
+
code the same event printed, as a letter alone also marks sections and merges: `B` beside `G` of
|
|
435
|
+
the same code (and no `A` or `C` of it) is the boys' and the girls' half, and a trailing `M` is
|
|
436
|
+
boys only beside the code without it. `secondIn` says whether a print names a second chance or a
|
|
437
|
+
supplementary competition beside the main one, from the words alone (`2nd Chance`, `2nd Chc`,
|
|
438
|
+
`Second Chance`, `Supplementary`, `2nd` before `Open` or `Prelim`), never from a code's digit.
|
|
439
|
+
|
|
440
|
+
```ts
|
|
441
|
+
import { codePopulation, secondIn } from "@finbheara/domain/title";
|
|
442
|
+
|
|
443
|
+
codePopulation("720OCB", ["720OCB", "720OCG"]); // "boys"
|
|
444
|
+
codePopulation("512M", ["512", "512M"]); // "boys"
|
|
445
|
+
codePopulation("713OCM", ["713OCM", "714OC"]); // "": alone, it may be a merge
|
|
446
|
+
secondIn("Preliminary Champion, Supplementary Prelim (U15)"); // true
|
|
447
|
+
secondIn("Preliminary Champion, Hard Shoe (U10)"); // false
|
|
448
|
+
```
|
|
449
|
+
|
|
427
450
|
## Ask a schedule
|
|
428
451
|
|
|
429
452
|
A split band's competitions share their card numbers, so a card heard on a stage fits each of
|
package/dist/age.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Age2,
|
|
3
3
|
ageRelation2
|
|
4
|
-
} from "./place-
|
|
4
|
+
} from "./place-kmh59zkw.js";
|
|
5
5
|
import {
|
|
6
6
|
validity
|
|
7
7
|
} from "./place-q9kwsmzd.js";
|
|
@@ -24,6 +24,30 @@ function populationIn2(text) {
|
|
|
24
24
|
return "boys";
|
|
25
25
|
return "";
|
|
26
26
|
}
|
|
27
|
+
function codePopulation2(code, codes) {
|
|
28
|
+
const c = code.trim().toUpperCase();
|
|
29
|
+
const all = new Set;
|
|
30
|
+
for (const x of codes)
|
|
31
|
+
all.add(x.trim().toUpperCase());
|
|
32
|
+
all.add(c);
|
|
33
|
+
if (!/\d/.test(c))
|
|
34
|
+
return "";
|
|
35
|
+
const pair = /^(.*\d[A-Z]*?)([BG])$/.exec(c);
|
|
36
|
+
if (pair !== null) {
|
|
37
|
+
const [, stem, letter] = pair;
|
|
38
|
+
const other = letter === "B" ? "G" : "B";
|
|
39
|
+
if (all.has(stem + other) && !all.has(`${stem}A`) && !all.has(`${stem}C`))
|
|
40
|
+
return letter === "B" ? "boys" : "girls";
|
|
41
|
+
}
|
|
42
|
+
const male = /^(.*\d[A-Z]*?)-?M$/.exec(c);
|
|
43
|
+
if (male !== null && all.has(male[1]))
|
|
44
|
+
return "boys";
|
|
45
|
+
return "";
|
|
46
|
+
}
|
|
47
|
+
var SECOND_WORDS = /\b(?:(?:2nd|second)[\s-]*ch(?:ance|c)?\b|supplementary\b|(?:2nd|second)\s+(?:open|prelim|preliminary)\b)/;
|
|
48
|
+
function secondIn2(text) {
|
|
49
|
+
return SECOND_WORDS.test(toAscii(text).toLowerCase());
|
|
50
|
+
}
|
|
27
51
|
function roundNumbersIn2(folded, printed) {
|
|
28
52
|
const out = new Set;
|
|
29
53
|
for (const m of folded.matchAll(/ rounds? ((?:\d+ (?:and )?)+)/g))
|
|
@@ -136,4 +160,4 @@ function matchTitle2(print, entries, ctx = {}) {
|
|
|
136
160
|
});
|
|
137
161
|
}
|
|
138
162
|
|
|
139
|
-
export { populationIn2, roundNumbersIn2, compTitle2, populationRelation2, titleRelation2, ladderOf2, matchTitle2 };
|
|
163
|
+
export { populationIn2, codePopulation2, secondIn2, roundNumbersIn2, compTitle2, populationRelation2, titleRelation2, ladderOf2, matchTitle2 };
|
|
@@ -242,12 +242,14 @@ function readRange(a, b, ladder) {
|
|
|
242
242
|
clauses: ["range-overlapping"],
|
|
243
243
|
parts: [a, b]
|
|
244
244
|
};
|
|
245
|
-
const
|
|
245
|
+
const capBelow = maxWhere(ladder.caps, (c) => c < b);
|
|
246
|
+
const printedFloor = a >= ADULT_FLOOR2 && (capBelow === undefined || capBelow <= a);
|
|
247
|
+
const below = printedFloor ? a : capBelow;
|
|
246
248
|
const adultFloor = below === undefined && ladder.adult === true && b > ADULT_FLOOR2;
|
|
247
249
|
const meetsOver = ladder.overs.includes(b + 1) && !ladder.caps.some((c) => c > b);
|
|
248
250
|
const clauses = [
|
|
249
251
|
"range-as-under",
|
|
250
|
-
below !== undefined ? "under-floored-on-rung" : adultFloor ? "range-adult-floor" : "youngest-absorbs"
|
|
252
|
+
printedFloor ? "range-printed-floor" : below !== undefined ? "under-floored-on-rung" : adultFloor ? "range-adult-floor" : "youngest-absorbs"
|
|
251
253
|
];
|
|
252
254
|
if (meetsOver)
|
|
253
255
|
clauses.push("range-top-meets-over");
|
|
@@ -429,7 +431,7 @@ function fromPrint(print, context = {}) {
|
|
|
429
431
|
throw new Error(`Age.from(${JSON.stringify(print)}): birthYears needs a competition year`);
|
|
430
432
|
return birthYearsOfInterval2(iv, at);
|
|
431
433
|
},
|
|
432
|
-
floorObserved: () => reading.kind !== "under" && reading.kind !== "range" || iv === null ? null : reading.clauses.includes("under-floored-on-rung") || reading.clauses.includes("range-adult-floor") || reading.clauses.includes("under-stride")
|
|
434
|
+
floorObserved: () => reading.kind !== "under" && reading.kind !== "range" || iv === null ? null : reading.clauses.includes("under-floored-on-rung") || reading.clauses.includes("range-adult-floor") || reading.clauses.includes("range-printed-floor") || reading.clauses.includes("under-stride")
|
|
433
435
|
});
|
|
434
436
|
}
|
|
435
437
|
function fromYOB(from, to = from, context = {}) {
|
|
@@ -484,7 +486,7 @@ function sourceOf(r) {
|
|
|
484
486
|
switch (r.kind) {
|
|
485
487
|
case "range":
|
|
486
488
|
case "under":
|
|
487
|
-
if (r.clauses.includes("under-stride"))
|
|
489
|
+
if (r.clauses.includes("under-stride") || r.clauses.includes("range-printed-floor"))
|
|
488
490
|
return "printed-band";
|
|
489
491
|
return iv.lo > YOUNGEST_FLOOR2 ? "sibling-narrowed" : "printed-band";
|
|
490
492
|
case "over":
|
package/dist/schedule.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import"./chunks/place-
|
|
1
|
+
import"./chunks/place-kmh59zkw.js";
|
|
2
2
|
import {
|
|
3
3
|
compTitle2,
|
|
4
4
|
matchTitle2
|
|
5
|
-
} from "./chunks/place-
|
|
5
|
+
} from "./chunks/place-k0p9af4f.js";
|
|
6
6
|
|
|
7
7
|
function hallKey(hall) {
|
|
8
8
|
return hall.normalize("NFKD").replace(/\p{M}/gu, "").toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
|
package/dist/title.js
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
import {
|
|
2
2
|
populationIn2,
|
|
3
|
+
codePopulation2,
|
|
4
|
+
secondIn2,
|
|
3
5
|
roundNumbersIn2,
|
|
4
6
|
compTitle2,
|
|
5
7
|
populationRelation2,
|
|
6
8
|
titleRelation2,
|
|
7
9
|
ladderOf2,
|
|
8
10
|
matchTitle2
|
|
9
|
-
} from "./chunks/place-
|
|
10
|
-
import"./chunks/place-
|
|
11
|
+
} from "./chunks/place-k0p9af4f.js";
|
|
12
|
+
import"./chunks/place-kmh59zkw.js";
|
|
11
13
|
export {
|
|
14
|
+
codePopulation2 as codePopulation,
|
|
12
15
|
compTitle2 as compTitle,
|
|
13
16
|
ladderOf2 as ladderOf,
|
|
14
17
|
matchTitle2 as matchTitle,
|
|
15
18
|
populationIn2 as populationIn,
|
|
16
19
|
populationRelation2 as populationRelation,
|
|
17
20
|
roundNumbersIn2 as roundNumbersIn,
|
|
21
|
+
secondIn2 as secondIn,
|
|
18
22
|
titleRelation2 as titleRelation
|
|
19
23
|
};
|
|
@@ -25,7 +25,7 @@ export type AgeArm = "band" | "yob" | "open" | "empty";
|
|
|
25
25
|
export type AgeKind = "under" | "over" | "range" | "adult" | "senior" | "yob";
|
|
26
26
|
export type AgeRefusal = "not-a-band" | "backwards-range" | "overlapping-ranges" | "senior-without-ladder" | "below-youngest-band" | "above-oldest-band" | "year-out-of-range";
|
|
27
27
|
export type AgeSource = "sibling-narrowed" | "printed-band" | "printed-range" | "printed-yob" | "open";
|
|
28
|
-
export type AgeClause = "under-floored-on-rung" | "under-stride" | "youngest-absorbs" | "over-ceiling-from-next-over" | "over-ceiling-refused-gap" | "and-over-no-ceiling" | "adult-under-ceiling" | "adult-band" | "range-overlapping" | "range-as-under" | "range-top-meets-over" | "range-adult-floor";
|
|
28
|
+
export type AgeClause = "under-floored-on-rung" | "under-stride" | "youngest-absorbs" | "over-ceiling-from-next-over" | "over-ceiling-refused-gap" | "and-over-no-ceiling" | "adult-under-ceiling" | "adult-band" | "range-overlapping" | "range-as-under" | "range-top-meets-over" | "range-adult-floor" | "range-printed-floor";
|
|
29
29
|
export type AgeRelation = "same" | "contains" | "overlaps" | "disjoint" | "silent";
|
|
30
30
|
export type AgeRelationReason = "equal" | "a-contains-b" | "b-contains-a" | "partial" | "a-older" | "a-younger" | "a-not-a-band" | "b-not-a-band";
|
|
31
31
|
export interface AgeComparison {
|
package/dist/types/title.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ export type { EmptyReason, Validated } from "./validated.ts";
|
|
|
5
5
|
export type { AgeComparison, AgeLadder } from "./age.ts";
|
|
6
6
|
export type Population = "girls" | "boys" | "mixed";
|
|
7
7
|
export declare function populationIn(text: string): Population | "";
|
|
8
|
+
export declare function codePopulation(code: string, codes: Iterable<string>): Population | "";
|
|
9
|
+
export declare function secondIn(text: string): boolean;
|
|
8
10
|
export declare function roundNumbersIn(folded: string, printed: boolean): Set<number>;
|
|
9
11
|
export interface TitleContext {
|
|
10
12
|
readonly year?: number | null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@finbheara/domain",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.49.0",
|
|
4
4
|
"description": "Places, CLRG regions, school names, ages, dances, competition titles and schedules, and a person's print in its role, as they are printed: read, compared and told apart",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"places",
|