@finbheara/domain 0.6.2 → 0.8.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 +16 -3
- package/dist/age.js +32 -5
- package/dist/school.js +8 -1
- package/dist/types/age-types.d.ts +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -265,12 +265,25 @@ Age.fromTitle("Junior Boys 12 Years").token; // "u13"
|
|
|
265
265
|
Age.fromTitle("10-11 Single Jig").token; // "10-11", a band, not a number
|
|
266
266
|
Age.fromTitle("Competition 001-013 - Reel").token; // "", a competition number
|
|
267
267
|
Age.fromTitle("Senior Boys Reel 15&16").token; // "15-16": a range beats the word
|
|
268
|
-
Age.fromTitle("Beginner Adults Reel U40").token; // "adult":
|
|
268
|
+
Age.fromTitle("Beginner Adults Reel U40").token; // "adult u40": the adult band under 40
|
|
269
|
+
Age.fromTitle("Solo Championship - Adult O40").token; // "adult o40": adult, and 40 or over
|
|
270
|
+
Age.fromTitle("Adult Championship 18 - 29").token; // "adult 18-29": adult, and the range
|
|
269
271
|
Age.fromTitle("Adult / Child Reel Under 10").token; // "u10": no adult dances in it
|
|
272
|
+
Age.fromTitle("Senior Adult Ladies 19 & Over").token; // "o19": senior or adult is not settled
|
|
270
273
|
```
|
|
271
274
|
|
|
272
|
-
An adult word beside an `Under N`
|
|
273
|
-
floor
|
|
275
|
+
An adult word beside an `Under N` above 18 reads both: `adult u40` is adult (`isAdult()`), with
|
|
276
|
+
the adult floor of 18 and a ceiling of 40, so `Age.from("adult u40", { year: 2026 })` is born
|
|
277
|
+
1986 to 2007. At `Under 18` the ceiling meets the floor and the word alone is read. Below 18 the
|
|
278
|
+
youth band stands.
|
|
279
|
+
|
|
280
|
+
An adult word beside an and-over from 18 or a range from 18 reads both as well: `adult o40` and
|
|
281
|
+
`adult 18-29` are adult (`isAdult()`), and their `kind`, interval and birth years are the ones the
|
|
282
|
+
band alone reads on the same ladder, a range floored at 18 where no rung sits below it. So
|
|
283
|
+
`Age.from("adult o40", { year: 2026 })` is born 1985 or earlier, as `o40` is. An and-over under
|
|
284
|
+
18 or a range opening under 18 (`Teen & Adult 13 & Over`) admits youth, and the band stands. A
|
|
285
|
+
title naming the senior rung beside the word (`Senior Adult Ladies 19 & Over`) reads the band
|
|
286
|
+
alone.
|
|
274
287
|
|
|
275
288
|
An age group is the age on the first of January, so a title that admits the dancer who is
|
|
276
289
|
`N` (`7 & Under`, `7/U`, `7 Years`) is the `Under N+1` group.
|
package/dist/age.js
CHANGED
|
@@ -99,12 +99,22 @@ function ageTitleToken(title) {
|
|
|
99
99
|
return words.token === "" && adultAside !== null ? adultAside : adultOverUnder(title, s, lead, words);
|
|
100
100
|
}
|
|
101
101
|
var ADULT_UNDER_MIN = 18;
|
|
102
|
+
var SENIOR_ADULT = /\bseniors?\s+adults?\b/i;
|
|
102
103
|
function adultOverUnder(title, s, lead, t) {
|
|
104
|
+
const a = ADULT.exec(s);
|
|
105
|
+
if (a === null)
|
|
106
|
+
return t;
|
|
103
107
|
const n = /^u(\d+)$/.exec(t.token)?.[1];
|
|
104
|
-
if (n
|
|
108
|
+
if (n !== undefined) {
|
|
109
|
+
if (Number(n) < ADULT_UNDER_MIN)
|
|
110
|
+
return t;
|
|
111
|
+
const token = Number(n) > ADULT_UNDER_MIN ? `adult u${n}` : "adult";
|
|
112
|
+
return at(title, lead, a.index, a.index + a[0].length, token);
|
|
113
|
+
}
|
|
114
|
+
const floor = /^o(\d+)$/.exec(t.token)?.[1] ?? /^(\d+)-\d+$/.exec(t.token)?.[1];
|
|
115
|
+
if (floor === undefined || Number(floor) < ADULT_UNDER_MIN || SENIOR_ADULT.test(s))
|
|
105
116
|
return t;
|
|
106
|
-
|
|
107
|
-
return a === null ? t : at(title, lead, a.index, a.index + a[0].length, "adult");
|
|
117
|
+
return { token: `adult ${t.token}`, span: t.span };
|
|
108
118
|
}
|
|
109
119
|
|
|
110
120
|
var YOUNGEST_FLOOR = 0;
|
|
@@ -125,6 +135,8 @@ var OVER = /^(?:o|over\s*)(\d+)$/i;
|
|
|
125
135
|
var AND_OVER2 = /^(\d+)\s*(?:&|and\b)\s*(?:over|o)$/i;
|
|
126
136
|
var RANGE2 = /^u?(\d+)\s*[-–]\s*(\d+)$/i;
|
|
127
137
|
var ADULT_WORD = /^(?:adults?|ad|advanced|masters?)$/i;
|
|
138
|
+
var ADULT_UNDER = /^(?:adults?|ad)[\s-]*(?:u|under\s*)(\d+)$/i;
|
|
139
|
+
var ADULT_BAND = /^(?:adults?|ad)[\s-]+(\S.*)$/i;
|
|
128
140
|
var SENIOR_WORD = /^seniors?$/i;
|
|
129
141
|
var NO_LADDER = Object.freeze({ caps: [], overs: [], ranges: [] });
|
|
130
142
|
function maxWhere(xs, pred) {
|
|
@@ -182,7 +194,22 @@ function read(token, ladder, top) {
|
|
|
182
194
|
const r = RANGE2.exec(token);
|
|
183
195
|
if (r !== null)
|
|
184
196
|
return readRange(Number(r[1]), Number(r[2]), ladder);
|
|
185
|
-
|
|
197
|
+
const au = ADULT_UNDER.exec(token);
|
|
198
|
+
if (au !== null && Number(au[1]) > ADULT_FLOOR)
|
|
199
|
+
return {
|
|
200
|
+
kind: "adult",
|
|
201
|
+
interval: { lo: ADULT_FLOOR, hi: Number(au[1]) },
|
|
202
|
+
refusal: null,
|
|
203
|
+
clauses: ["adult-under-ceiling"],
|
|
204
|
+
parts: [Number(au[1])]
|
|
205
|
+
};
|
|
206
|
+
const ab = au === null ? ADULT_BAND.exec(token) : null;
|
|
207
|
+
if (ab !== null) {
|
|
208
|
+
const inner = read(ab[1].trim(), { ...ladder, adult: true }, top);
|
|
209
|
+
if (inner.kind === "over" || inner.kind === "range")
|
|
210
|
+
return { ...inner, clauses: [...inner.clauses, "adult-band"], adult: true };
|
|
211
|
+
}
|
|
212
|
+
if (au !== null || ADULT_WORD.test(token))
|
|
186
213
|
return {
|
|
187
214
|
kind: "adult",
|
|
188
215
|
interval: { lo: ADULT_FLOOR, hi: Number.POSITIVE_INFINITY },
|
|
@@ -299,7 +326,7 @@ function fromPrint(print, context = {}) {
|
|
|
299
326
|
return b.from !== null && b.from === b.to;
|
|
300
327
|
},
|
|
301
328
|
isAndOver: () => iv !== null && iv.hi === Number.POSITIVE_INFINITY,
|
|
302
|
-
isAdult: () => reading.kind === "adult",
|
|
329
|
+
isAdult: () => reading.kind === "adult" || reading.adult === true,
|
|
303
330
|
interval: () => iv,
|
|
304
331
|
birthYears: (y) => {
|
|
305
332
|
const at = y ?? year;
|
package/dist/school.js
CHANGED
|
@@ -1019,6 +1019,13 @@ function createSchoolNameReader(options = {}) {
|
|
|
1019
1019
|
const CP1252_HIGH = "€‚ƒ„…†‡ˆ‰Š‹Œ" + "Ž‘’“”•–—˜™š›" + "œžŸ";
|
|
1020
1020
|
const BYTE_OF_CP1252 = new Map([...CP1252_HIGH].map((ch, i) => [ch.codePointAt(0), 128 + i]));
|
|
1021
1021
|
const MOJIBAKE = /[Â-ßà-ï][-¿‚-›ŒŠŽ]/u;
|
|
1022
|
+
const MOJIBAKE_ALL = new RegExp(MOJIBAKE.source, "gu");
|
|
1023
|
+
const NON_LATIN_LETTER = /(?!\p{Script=Latin})\p{L}/u;
|
|
1024
|
+
function isRepair(before, after) {
|
|
1025
|
+
if (!NON_LATIN_LETTER.test(after))
|
|
1026
|
+
return true;
|
|
1027
|
+
return (before.match(MOJIBAKE_ALL)?.length ?? 0) >= 2;
|
|
1028
|
+
}
|
|
1022
1029
|
function undoMojibake(s) {
|
|
1023
1030
|
const bytes = new Uint8Array(s.length);
|
|
1024
1031
|
for (let i = 0;i < s.length; i++) {
|
|
@@ -1038,7 +1045,7 @@ function createSchoolNameReader(options = {}) {
|
|
|
1038
1045
|
let s = print;
|
|
1039
1046
|
for (let round = 0;round < 3 && MOJIBAKE.test(s); round++) {
|
|
1040
1047
|
const undone = undoMojibake(s);
|
|
1041
|
-
if (undone === undefined || undone === s)
|
|
1048
|
+
if (undone === undefined || undone === s || !isRepair(s, undone))
|
|
1042
1049
|
return s;
|
|
1043
1050
|
s = undone;
|
|
1044
1051
|
}
|
|
@@ -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" | "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" | "youngest-absorbs" | "over-ceiling-from-next-over" | "over-ceiling-refused-gap" | "and-over-no-ceiling" | "range-overlapping" | "range-as-under" | "range-top-meets-over" | "range-adult-floor";
|
|
28
|
+
export type AgeClause = "under-floored-on-rung" | "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";
|
|
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@finbheara/domain",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Places, CLRG regions, school names, ages and dances as they are printed: read, compared and told apart",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"places",
|
|
@@ -73,6 +73,6 @@
|
|
|
73
73
|
"prepublishOnly": "bun scripts/build.ts && bun scripts/guard-tarball.ts"
|
|
74
74
|
},
|
|
75
75
|
"dependencies": {
|
|
76
|
-
"@finbheara/names": "0.
|
|
76
|
+
"@finbheara/names": "0.20.0"
|
|
77
77
|
}
|
|
78
78
|
}
|