@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 +66 -0
- package/dist/age.js +73 -688
- package/dist/chunks/place-8k74cr5j.js +655 -0
- package/dist/chunks/place-fsxsxdhk.js +139 -0
- package/dist/schedule.js +70 -0
- package/dist/title.js +19 -0
- package/dist/types/schedule.d.ts +21 -0
- package/dist/types/title.d.ts +45 -0
- package/package.json +13 -2
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Age2,
|
|
3
|
+
ageRelation2
|
|
4
|
+
} from "./place-8k74cr5j.js";
|
|
5
|
+
import {
|
|
6
|
+
validity
|
|
7
|
+
} from "./place-q9kwsmzd.js";
|
|
8
|
+
|
|
9
|
+
import { toAscii } from "@finbheara/names/lexicon";
|
|
10
|
+
var GIRLS_WORD = /\b(?:girls?|lad(?:y|ies)|wom[ae]ns?|cailini|mna)(?![a-z])/;
|
|
11
|
+
var BOYS_WORD = /\b(?:boys?|mens?|man|gents?|gentlem[ae]n|buachailli|fir)(?![a-z])/;
|
|
12
|
+
var MIXED_WORD = /\b(?:mixed|mix|meascaithe|measctha)\b/;
|
|
13
|
+
function populationIn2(text) {
|
|
14
|
+
const w = toAscii(text).toLowerCase();
|
|
15
|
+
if (MIXED_WORD.test(w))
|
|
16
|
+
return "mixed";
|
|
17
|
+
const g = GIRLS_WORD.test(w);
|
|
18
|
+
const b = BOYS_WORD.test(w);
|
|
19
|
+
if (g && b)
|
|
20
|
+
return "mixed";
|
|
21
|
+
if (g)
|
|
22
|
+
return "girls";
|
|
23
|
+
if (b)
|
|
24
|
+
return "boys";
|
|
25
|
+
return "";
|
|
26
|
+
}
|
|
27
|
+
function roundNumbersIn2(folded, printed) {
|
|
28
|
+
const out = new Set;
|
|
29
|
+
for (const m of folded.matchAll(/ rounds? ((?:\d+ (?:and )?)+)/g))
|
|
30
|
+
for (const n of m[1].match(/\d+/g) ?? [])
|
|
31
|
+
out.add(Number(n));
|
|
32
|
+
for (const m of folded.matchAll(/ r([1-9])(?= )/g))
|
|
33
|
+
out.add(Number(m[1]));
|
|
34
|
+
if (printed)
|
|
35
|
+
for (const m of folded.matchAll(/ (\d) (?:st|nd|rd|th)(?= )/g))
|
|
36
|
+
out.add(Number(m[1]));
|
|
37
|
+
return out;
|
|
38
|
+
}
|
|
39
|
+
function roundFold(print) {
|
|
40
|
+
return ` ${toAscii(print).toLowerCase().replace(/(\d)(\p{L})/gu, "$1 $2").replace(/[-–—,/()&+:;|.']/g, " ").replace(/\s+/g, " ").trim()} `;
|
|
41
|
+
}
|
|
42
|
+
var SPLIT = / ([a-d]) $|(?:\d|girls?|boys?|lad(?:y|ies)|m[ae]n|mixed) ([a-d])(?= )/;
|
|
43
|
+
var spoken = (print) => print.replace(/\b(under|over|u|o)-(?=[ \t]*\d)/giu, "$1 ");
|
|
44
|
+
function read(print, ctx, agePrint) {
|
|
45
|
+
const actx = { year: ctx.year ?? null, ...ctx.ladder ? { ladder: ctx.ladder } : {} };
|
|
46
|
+
let age;
|
|
47
|
+
let ageSpan = null;
|
|
48
|
+
if (agePrint !== undefined && agePrint.trim() !== "") {
|
|
49
|
+
age = Age2.from(agePrint, actx);
|
|
50
|
+
} else {
|
|
51
|
+
const t = Age2.fromTitle(spoken(print), actx);
|
|
52
|
+
age = t.age;
|
|
53
|
+
ageSpan = t.span;
|
|
54
|
+
}
|
|
55
|
+
const folded = roundFold(print);
|
|
56
|
+
const rounds = [...roundNumbersIn2(folded, / rounds? /.test(folded))].sort((a, b) => a - b);
|
|
57
|
+
const split = SPLIT.exec(folded);
|
|
58
|
+
const empty = print.trim() === "" && (agePrint ?? "").trim() === "";
|
|
59
|
+
const refusal = empty || age.isAge() ? null : "no-age";
|
|
60
|
+
return Object.freeze({
|
|
61
|
+
[Symbol.toStringTag]: "CompTitle",
|
|
62
|
+
...validity(empty, refusal),
|
|
63
|
+
print,
|
|
64
|
+
age,
|
|
65
|
+
ageSpan,
|
|
66
|
+
population: populationIn2(print),
|
|
67
|
+
round: rounds.length === 1 ? rounds[0] : null,
|
|
68
|
+
rounds: Object.freeze(rounds),
|
|
69
|
+
split: split ? split[1] ?? split[2] : null,
|
|
70
|
+
isTitle: () => !empty && refusal === null
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
function compTitle2(print, ctx = {}) {
|
|
74
|
+
return read(print, ctx);
|
|
75
|
+
}
|
|
76
|
+
function populationRelation2(a, b) {
|
|
77
|
+
if (a === "" || b === "")
|
|
78
|
+
return "silent";
|
|
79
|
+
return a === b ? "same" : "disjoint";
|
|
80
|
+
}
|
|
81
|
+
function titleRelation2(a, b) {
|
|
82
|
+
const age = ageRelation2(a.age, b.age);
|
|
83
|
+
const population = populationRelation2(a.population, b.population);
|
|
84
|
+
const relation = population === "disjoint" || age.relation === "disjoint" ? "disjoint" : age.relation;
|
|
85
|
+
return Object.freeze({ relation, age, population });
|
|
86
|
+
}
|
|
87
|
+
function ladderOf2(bands) {
|
|
88
|
+
const caps = new Set;
|
|
89
|
+
const overs = new Set;
|
|
90
|
+
const ranges = [];
|
|
91
|
+
let adult = false;
|
|
92
|
+
for (const b of bands) {
|
|
93
|
+
const bare = Age2.from(b.ag0, { year: null });
|
|
94
|
+
if (!bare.isValid())
|
|
95
|
+
continue;
|
|
96
|
+
if (bare.isAdult())
|
|
97
|
+
adult = true;
|
|
98
|
+
const [p, q] = bare.parts;
|
|
99
|
+
if (bare.kind === "under" && p !== undefined)
|
|
100
|
+
caps.add(p);
|
|
101
|
+
else if (bare.kind === "over" && p !== undefined)
|
|
102
|
+
overs.add(p);
|
|
103
|
+
else if (bare.kind === "range" && p !== undefined && q !== undefined) {
|
|
104
|
+
caps.add(q);
|
|
105
|
+
ranges.push([p, q]);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const asc = (s) => [...s].sort((x, y) => x - y);
|
|
109
|
+
return Object.freeze({
|
|
110
|
+
caps: asc(caps),
|
|
111
|
+
overs: asc(overs),
|
|
112
|
+
ranges: ranges.sort((x, y) => x[0] - y[0] || x[1] - y[1]),
|
|
113
|
+
...adult ? { adult: true } : {}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
function matchTitle2(print, entries, ctx = {}) {
|
|
117
|
+
const bare = read(print, ctx);
|
|
118
|
+
const line = entries.filter((e) => populationRelation2(bare.population, populationIn2(e.title)) !== "disjoint");
|
|
119
|
+
const ladder = ladderOf2(line.map((e) => read(e.title, ctx, e.age).age));
|
|
120
|
+
const title = read(print, { ...ctx, ladder });
|
|
121
|
+
const hits = [];
|
|
122
|
+
if (title.isTitle())
|
|
123
|
+
for (const e of line) {
|
|
124
|
+
const r = titleRelation2(title, read(e.title, { ...ctx, ladder }, e.age));
|
|
125
|
+
if (r.relation === "same")
|
|
126
|
+
hits.push(e);
|
|
127
|
+
}
|
|
128
|
+
const split = title.split;
|
|
129
|
+
const same = split === null ? [] : hits.filter((e) => read(e.title, ctx, e.age).split === split);
|
|
130
|
+
const out = same.length > 0 ? same : hits;
|
|
131
|
+
return Object.freeze({
|
|
132
|
+
title,
|
|
133
|
+
entries: Object.freeze(out),
|
|
134
|
+
round: title.round,
|
|
135
|
+
isSingular: () => out.length === 1
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export { populationIn2, roundNumbersIn2, compTitle2, populationRelation2, titleRelation2, ladderOf2, matchTitle2 };
|
package/dist/schedule.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import"./chunks/place-8k74cr5j.js";
|
|
2
|
+
import {
|
|
3
|
+
compTitle2,
|
|
4
|
+
matchTitle2
|
|
5
|
+
} from "./chunks/place-fsxsxdhk.js";
|
|
6
|
+
|
|
7
|
+
function hallKey(hall) {
|
|
8
|
+
return hall.normalize("NFKD").replace(/\p{M}/gu, "").toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
|
|
9
|
+
}
|
|
10
|
+
function sameHall(a, b) {
|
|
11
|
+
const x = hallKey(a);
|
|
12
|
+
const y = hallKey(b);
|
|
13
|
+
if (x === "" || y === "")
|
|
14
|
+
return false;
|
|
15
|
+
if (x === y)
|
|
16
|
+
return true;
|
|
17
|
+
const [short, long] = x.length <= y.length ? [x, y] : [y, x];
|
|
18
|
+
return short.length >= 4 && long.endsWith(` ${short}`);
|
|
19
|
+
}
|
|
20
|
+
function scheduleAt(slots, where) {
|
|
21
|
+
const day = where.day ?? null;
|
|
22
|
+
const at = where.at ?? null;
|
|
23
|
+
return slots.filter((s) => sameHall(s.hall, where.hall) && (day === null || s.day === null || s.day === day) && (at === null || (s.start === null || s.start <= at) && (s.end === null || at < s.end)));
|
|
24
|
+
}
|
|
25
|
+
var ROUND_END = /\b(?:r ?\d|rounds? \d)(?: ?(?:&|and|,) ?(?:r ?)?\d)*\b/giu;
|
|
26
|
+
function piecesOf(line) {
|
|
27
|
+
const out = [];
|
|
28
|
+
let from = 0;
|
|
29
|
+
for (const m of line.matchAll(ROUND_END)) {
|
|
30
|
+
const to = (m.index ?? 0) + m[0].length;
|
|
31
|
+
out.push(line.slice(from, to));
|
|
32
|
+
from = to;
|
|
33
|
+
}
|
|
34
|
+
out.push(line.slice(from));
|
|
35
|
+
return out.flatMap((p) => p.split(",")).filter((p) => p.trim() !== "");
|
|
36
|
+
}
|
|
37
|
+
function readRunningOrder(text, entries, where, ctx = {}) {
|
|
38
|
+
const out = [];
|
|
39
|
+
const seen = new Set;
|
|
40
|
+
for (const line of text.split(/[\n;]+/)) {
|
|
41
|
+
let population = "";
|
|
42
|
+
for (const piece of piecesOf(line)) {
|
|
43
|
+
const own = compTitle2(piece, ctx).population;
|
|
44
|
+
if (own !== "")
|
|
45
|
+
population = own;
|
|
46
|
+
const said = own === "" && population !== "" ? `${piece} ${population}` : piece;
|
|
47
|
+
const m = matchTitle2(said, entries, ctx);
|
|
48
|
+
for (const entry of m.entries) {
|
|
49
|
+
const key = `${entries.indexOf(entry)}|${m.round}`;
|
|
50
|
+
if (seen.has(key))
|
|
51
|
+
continue;
|
|
52
|
+
seen.add(key);
|
|
53
|
+
out.push(Object.freeze({
|
|
54
|
+
hall: where.hall,
|
|
55
|
+
day: where.day ?? null,
|
|
56
|
+
start: null,
|
|
57
|
+
end: null,
|
|
58
|
+
entry,
|
|
59
|
+
round: m.round
|
|
60
|
+
}));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
export {
|
|
67
|
+
readRunningOrder,
|
|
68
|
+
sameHall,
|
|
69
|
+
scheduleAt
|
|
70
|
+
};
|
package/dist/title.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {
|
|
2
|
+
populationIn2,
|
|
3
|
+
roundNumbersIn2,
|
|
4
|
+
compTitle2,
|
|
5
|
+
populationRelation2,
|
|
6
|
+
titleRelation2,
|
|
7
|
+
ladderOf2,
|
|
8
|
+
matchTitle2
|
|
9
|
+
} from "./chunks/place-fsxsxdhk.js";
|
|
10
|
+
import"./chunks/place-8k74cr5j.js";
|
|
11
|
+
export {
|
|
12
|
+
compTitle2 as compTitle,
|
|
13
|
+
ladderOf2 as ladderOf,
|
|
14
|
+
matchTitle2 as matchTitle,
|
|
15
|
+
populationIn2 as populationIn,
|
|
16
|
+
populationRelation2 as populationRelation,
|
|
17
|
+
roundNumbersIn2 as roundNumbersIn,
|
|
18
|
+
titleRelation2 as titleRelation
|
|
19
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type SyllabusEntry, type TitleContext } from "./title.ts";
|
|
2
|
+
export type { SyllabusEntry, TitleContext } from "./title.ts";
|
|
3
|
+
export interface ScheduleSlot<E> {
|
|
4
|
+
readonly hall: string;
|
|
5
|
+
readonly day: number | null;
|
|
6
|
+
readonly start: number | null;
|
|
7
|
+
readonly end: number | null;
|
|
8
|
+
readonly entry: E;
|
|
9
|
+
readonly round: number | null;
|
|
10
|
+
}
|
|
11
|
+
export interface ScheduleWhere {
|
|
12
|
+
readonly hall: string;
|
|
13
|
+
readonly day?: number | null;
|
|
14
|
+
readonly at?: number | null;
|
|
15
|
+
}
|
|
16
|
+
export declare function sameHall(a: string, b: string): boolean;
|
|
17
|
+
export declare function scheduleAt<E>(slots: readonly ScheduleSlot<E>[], where: ScheduleWhere): ScheduleSlot<E>[];
|
|
18
|
+
export declare function readRunningOrder<E extends SyllabusEntry>(text: string, entries: readonly E[], where: {
|
|
19
|
+
readonly hall: string;
|
|
20
|
+
readonly day?: number | null;
|
|
21
|
+
}, ctx?: Omit<TitleContext, "ladder">): ScheduleSlot<E>[];
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { AgeTitleSpan } from "./age-title.ts";
|
|
2
|
+
import { Age, type AgeComparison, type AgeLadder, type AgeRelation } from "./age.ts";
|
|
3
|
+
import { type Validated } from "./validated.ts";
|
|
4
|
+
export type { EmptyReason, Validated } from "./validated.ts";
|
|
5
|
+
export type { AgeComparison, AgeLadder } from "./age.ts";
|
|
6
|
+
export type Population = "girls" | "boys" | "mixed";
|
|
7
|
+
export declare function populationIn(text: string): Population | "";
|
|
8
|
+
export declare function roundNumbersIn(folded: string, printed: boolean): Set<number>;
|
|
9
|
+
export interface TitleContext {
|
|
10
|
+
readonly year?: number | null;
|
|
11
|
+
readonly ladder?: AgeLadder;
|
|
12
|
+
}
|
|
13
|
+
export type TitleRefusal = "no-age";
|
|
14
|
+
export interface CompTitle extends Validated<TitleRefusal> {
|
|
15
|
+
readonly [Symbol.toStringTag]: "CompTitle";
|
|
16
|
+
readonly print: string;
|
|
17
|
+
readonly age: Age;
|
|
18
|
+
readonly ageSpan: AgeTitleSpan | null;
|
|
19
|
+
readonly population: Population | "";
|
|
20
|
+
readonly round: number | null;
|
|
21
|
+
readonly rounds: readonly number[];
|
|
22
|
+
readonly split: string | null;
|
|
23
|
+
isTitle(): boolean;
|
|
24
|
+
}
|
|
25
|
+
export declare function compTitle(print: string, ctx?: TitleContext): CompTitle;
|
|
26
|
+
export type PopulationRelation = "same" | "disjoint" | "silent";
|
|
27
|
+
export interface TitleComparison {
|
|
28
|
+
readonly relation: AgeRelation;
|
|
29
|
+
readonly age: AgeComparison;
|
|
30
|
+
readonly population: PopulationRelation;
|
|
31
|
+
}
|
|
32
|
+
export declare function populationRelation(a: Population | "", b: Population | ""): PopulationRelation;
|
|
33
|
+
export declare function titleRelation(a: CompTitle, b: CompTitle): TitleComparison;
|
|
34
|
+
export declare function ladderOf(bands: readonly Age[]): AgeLadder;
|
|
35
|
+
export interface SyllabusEntry {
|
|
36
|
+
readonly title: string;
|
|
37
|
+
readonly age?: string;
|
|
38
|
+
}
|
|
39
|
+
export interface TitleMatch<E extends SyllabusEntry> {
|
|
40
|
+
readonly title: CompTitle;
|
|
41
|
+
readonly entries: readonly E[];
|
|
42
|
+
readonly round: number | null;
|
|
43
|
+
isSingular(): boolean;
|
|
44
|
+
}
|
|
45
|
+
export declare function matchTitle<E extends SyllabusEntry>(print: string, entries: readonly E[], ctx?: Omit<TitleContext, "ladder">): TitleMatch<E>;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@finbheara/domain",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Places, CLRG regions, school names, ages, dances and a person's print in its role, as they are printed: read, compared and told apart",
|
|
3
|
+
"version": "0.33.0",
|
|
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",
|
|
7
7
|
"regions",
|
|
@@ -65,6 +65,14 @@
|
|
|
65
65
|
"types": "./dist/types/person.d.ts",
|
|
66
66
|
"default": "./dist/person.js"
|
|
67
67
|
},
|
|
68
|
+
"./title": {
|
|
69
|
+
"types": "./dist/types/title.d.ts",
|
|
70
|
+
"default": "./dist/title.js"
|
|
71
|
+
},
|
|
72
|
+
"./schedule": {
|
|
73
|
+
"types": "./dist/types/schedule.d.ts",
|
|
74
|
+
"default": "./dist/schedule.js"
|
|
75
|
+
},
|
|
68
76
|
"./package.json": "./package.json"
|
|
69
77
|
},
|
|
70
78
|
"files": ["dist/*.js", "dist/chunks/*.js", "dist/types/**/*.d.ts", "README.md", "LICENSE"],
|
|
@@ -82,5 +90,8 @@
|
|
|
82
90
|
},
|
|
83
91
|
"dependencies": {
|
|
84
92
|
"@finbheara/names": "0.20.0"
|
|
93
|
+
},
|
|
94
|
+
"devDependencies": {
|
|
95
|
+
"typescript": "7.0.2"
|
|
85
96
|
}
|
|
86
97
|
}
|