@finbheara/domain 0.7.0 → 0.9.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 +21 -0
- package/dist/age.js +51 -11
- package/dist/types/age-title.d.ts +1 -0
- package/dist/types/age-types.d.ts +1 -1
- package/dist/types/age.d.ts +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -266,7 +266,15 @@ Age.fromTitle("10-11 Single Jig").token; // "10-11", a band, not
|
|
|
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
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; // "adult o19": "Senior Adult" is adult
|
|
273
|
+
Age.fromTitle("Senior Adult Men 17 & Over").token; // "o17": it admits under-18s
|
|
274
|
+
Age.fromTitle("Championship (Adult) (A40U)").token; // "adult u40": a split adult code
|
|
275
|
+
Age.fromTitle("Traditional Set (A40O)").token; // "adult o40"
|
|
276
|
+
adultBeside("o18"); // "adult o18": a band beside the adult word
|
|
277
|
+
adultBeside("o17"); // undefined: it admits youth
|
|
270
278
|
```
|
|
271
279
|
|
|
272
280
|
An adult word beside an `Under N` above 18 reads both: `adult u40` is adult (`isAdult()`), with
|
|
@@ -274,6 +282,19 @@ the adult floor of 18 and a ceiling of 40, so `Age.from("adult u40", { year: 202
|
|
|
274
282
|
1986 to 2007. At `Under 18` the ceiling meets the floor and the word alone is read. Below 18 the
|
|
275
283
|
youth band stands.
|
|
276
284
|
|
|
285
|
+
An adult word beside an and-over from 18 or a range from 18 reads both as well: `adult o40` and
|
|
286
|
+
`adult 18-29` are adult (`isAdult()`), and their `kind`, interval and birth years are the ones the
|
|
287
|
+
band alone reads on the same ladder, a range floored at 18 where no rung sits below it. So
|
|
288
|
+
`Age.from("adult o40", { year: 2026 })` is born 1985 or earlier, as `o40` is. An and-over under
|
|
289
|
+
18 or a range opening under 18 (`Teen & Adult 13 & Over`) admits youth, and the band stands. A
|
|
290
|
+
title naming the senior word beside it reads the same: `Senior Adult` is the adult band, so
|
|
291
|
+
`Senior Adult Ladies 19 & Over` is `adult o19`. A closing split adult code keeps its split:
|
|
292
|
+
`(A40U)` is `adult u40`, `(A40O)` and `(A40&O)` are `adult o40`.
|
|
293
|
+
|
|
294
|
+
`adultBeside(band)` is that reading as one function, the band beside the adult word as one token,
|
|
295
|
+
or `undefined` where the band admits youth. A caller holding a band from another field asks it: a
|
|
296
|
+
platform's printed `18&O` beside a title that reads adult is `adult o18`.
|
|
297
|
+
|
|
277
298
|
An age group is the age on the first of January, so a title that admits the dancer who is
|
|
278
299
|
`N` (`7 & Under`, `7/U`, `7 Years`) is the `Under N+1` group.
|
|
279
300
|
|
package/dist/age.js
CHANGED
|
@@ -16,7 +16,7 @@ var YEARS = /(?<![a-z0-9])(?<!\d\s*(?:&|\/|\band\b|[-\u0096–—]|\bto\b)\s*)(\
|
|
|
16
16
|
var RANGE_DASH = String.raw`(?:\u00c2?[-\u0096–—]|\bto\b)`;
|
|
17
17
|
var RANGE = new RegExp(String.raw`(?<![0-9])(?<![0-9][a-z]*)(?<!(?:^|[^a-z0-9])[a-z])(?:u\s*)?(\d{1,2})\s*${RANGE_DASH}\s*(\d{1,2})(?![a-z0-9])`, "i");
|
|
18
18
|
var LEAD_RANGE = new RegExp(String.raw`^\s*([1-9]\d?)\s*${RANGE_DASH}\s*([1-9]\d?)(?![a-z0-9])(?!\s*[/&])`, "i");
|
|
19
|
-
var ADULT_CODE = /^\s*a(
|
|
19
|
+
var ADULT_CODE = /^\s*a(?:(\d{1,2})(u|o|&o))?\s*$/i;
|
|
20
20
|
var ADULT = /(?<!non[\s-])(?<!-)\b(?:adults?|advanced\s+adults?|masters?)\b/i;
|
|
21
21
|
var SENIOR = /(?<!-)\bseniors?\b/i;
|
|
22
22
|
var TITLE_RANGE_MIN = 3;
|
|
@@ -81,31 +81,63 @@ function ageTitleToken(title) {
|
|
|
81
81
|
const lead = title.length - title.trimStart().length;
|
|
82
82
|
PAREN.lastIndex = 0;
|
|
83
83
|
let adultAside = null;
|
|
84
|
+
let adultWord = null;
|
|
84
85
|
for (const m of s.matchAll(PAREN)) {
|
|
85
86
|
const inner = m[1] ?? "";
|
|
86
87
|
const from = lead + (m.index ?? 0) + 1;
|
|
87
88
|
const i = m.index ?? 0;
|
|
88
89
|
const tail = i + m[0].length === s.length && !/[a-z0-9]/i.test(s[i - 1] ?? "");
|
|
89
|
-
|
|
90
|
+
const code = tail ? ADULT_CODE.exec(inner) : null;
|
|
91
|
+
if (code !== null) {
|
|
92
|
+
const split = code[1] === undefined ? "" : `${/u/i.test(code[2] ?? "") ? "u" : "o"}${code[1]}`;
|
|
90
93
|
const a = inner.length - inner.trimStart().length;
|
|
91
|
-
adultAside = at(title, from, a, inner.trimEnd().length, "adult");
|
|
94
|
+
adultAside = at(title, from, a, inner.trimEnd().length, adultBeside(split) ?? "adult");
|
|
92
95
|
continue;
|
|
93
96
|
}
|
|
97
|
+
if (adultWord !== null)
|
|
98
|
+
continue;
|
|
94
99
|
const t = readAnchored(title, inner, from, true);
|
|
95
|
-
if (t.token
|
|
96
|
-
|
|
100
|
+
if (t.token === "")
|
|
101
|
+
continue;
|
|
102
|
+
if (t.token === "adult") {
|
|
103
|
+
adultWord = t;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
return adultOverUnder(title, s, lead, t);
|
|
97
107
|
}
|
|
108
|
+
if (adultWord !== null)
|
|
109
|
+
return adultAside ?? adultWord;
|
|
98
110
|
const words = readAnchored(title, s, lead);
|
|
99
|
-
return words.token === ""
|
|
111
|
+
return adultAside !== null && (words.token === "" || words.token === "adult") ? adultAside : adultOverUnder(title, s, lead, words);
|
|
100
112
|
}
|
|
101
113
|
var ADULT_UNDER_MIN = 18;
|
|
114
|
+
function adultBeside(band) {
|
|
115
|
+
const b = band.trim().toLowerCase();
|
|
116
|
+
if (b === "")
|
|
117
|
+
return "adult";
|
|
118
|
+
const n = /^u(\d+)$/.exec(b)?.[1];
|
|
119
|
+
if (n !== undefined) {
|
|
120
|
+
if (Number(n) < ADULT_UNDER_MIN)
|
|
121
|
+
return;
|
|
122
|
+
return Number(n) > ADULT_UNDER_MIN ? `adult u${Number(n)}` : "adult";
|
|
123
|
+
}
|
|
124
|
+
const floor = /^o(\d+)$/.exec(b)?.[1] ?? /^(\d+)-\d+$/.exec(b)?.[1];
|
|
125
|
+
if (floor === undefined)
|
|
126
|
+
return band;
|
|
127
|
+
return Number(floor) < ADULT_UNDER_MIN ? undefined : `adult ${b}`;
|
|
128
|
+
}
|
|
102
129
|
function adultOverUnder(title, s, lead, t) {
|
|
103
|
-
|
|
104
|
-
if (n === undefined || Number(n) < ADULT_UNDER_MIN)
|
|
130
|
+
if (t.token === "")
|
|
105
131
|
return t;
|
|
106
132
|
const a = ADULT.exec(s);
|
|
107
|
-
|
|
108
|
-
|
|
133
|
+
if (a === null)
|
|
134
|
+
return t;
|
|
135
|
+
const token = adultBeside(t.token);
|
|
136
|
+
if (token === undefined || token === t.token)
|
|
137
|
+
return t;
|
|
138
|
+
if (/^u\d+$/.test(t.token))
|
|
139
|
+
return at(title, lead, a.index, a.index + a[0].length, token);
|
|
140
|
+
return { token, span: t.span };
|
|
109
141
|
}
|
|
110
142
|
|
|
111
143
|
var YOUNGEST_FLOOR = 0;
|
|
@@ -127,6 +159,7 @@ var AND_OVER2 = /^(\d+)\s*(?:&|and\b)\s*(?:over|o)$/i;
|
|
|
127
159
|
var RANGE2 = /^u?(\d+)\s*[-–]\s*(\d+)$/i;
|
|
128
160
|
var ADULT_WORD = /^(?:adults?|ad|advanced|masters?)$/i;
|
|
129
161
|
var ADULT_UNDER = /^(?:adults?|ad)[\s-]*(?:u|under\s*)(\d+)$/i;
|
|
162
|
+
var ADULT_BAND = /^(?:adults?|ad)[\s-]+(\S.*)$/i;
|
|
130
163
|
var SENIOR_WORD = /^seniors?$/i;
|
|
131
164
|
var NO_LADDER = Object.freeze({ caps: [], overs: [], ranges: [] });
|
|
132
165
|
function maxWhere(xs, pred) {
|
|
@@ -193,6 +226,12 @@ function read(token, ladder, top) {
|
|
|
193
226
|
clauses: ["adult-under-ceiling"],
|
|
194
227
|
parts: [Number(au[1])]
|
|
195
228
|
};
|
|
229
|
+
const ab = au === null ? ADULT_BAND.exec(token) : null;
|
|
230
|
+
if (ab !== null) {
|
|
231
|
+
const inner = read(ab[1].trim(), { ...ladder, adult: true }, top);
|
|
232
|
+
if (inner.kind === "over" || inner.kind === "range")
|
|
233
|
+
return { ...inner, clauses: [...inner.clauses, "adult-band"], adult: true };
|
|
234
|
+
}
|
|
196
235
|
if (au !== null || ADULT_WORD.test(token))
|
|
197
236
|
return {
|
|
198
237
|
kind: "adult",
|
|
@@ -310,7 +349,7 @@ function fromPrint(print, context = {}) {
|
|
|
310
349
|
return b.from !== null && b.from === b.to;
|
|
311
350
|
},
|
|
312
351
|
isAndOver: () => iv !== null && iv.hi === Number.POSITIVE_INFINITY,
|
|
313
|
-
isAdult: () => reading.kind === "adult",
|
|
352
|
+
isAdult: () => reading.kind === "adult" || reading.adult === true,
|
|
314
353
|
interval: () => iv,
|
|
315
354
|
birthYears: (y) => {
|
|
316
355
|
const at = y ?? year;
|
|
@@ -570,6 +609,7 @@ export {
|
|
|
570
609
|
YOUNGEST_BAND_TOP,
|
|
571
610
|
YOUNGEST_FLOOR,
|
|
572
611
|
YOUNG_FLOOR,
|
|
612
|
+
adultBeside,
|
|
573
613
|
ageOnJanuary1,
|
|
574
614
|
ageRelation,
|
|
575
615
|
ageTitleToken,
|
|
@@ -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" | "adult-under-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/dist/types/age.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type AgeTitleSpan } from "./age-title.ts";
|
|
2
2
|
import type { AgeArm, AgeClause, AgeComparison, AgeContext, AgeInterval, AgeKind, AgeLadder, AgeMisuse, AgeRefusal, AgeRelation, AgeRelationReason, AgeSource, AgeSpan, BirthYears, YOBContext, YoungFloorVerdict } from "./age-types.ts";
|
|
3
3
|
import { type EmptyReason, type Validated } from "./validated.ts";
|
|
4
|
-
export { type AgeTitleSpan, type AgeTitleToken, TITLE_RANGE_MAX, TITLE_RANGE_MIN, ageTitleToken, } from "./age-title.ts";
|
|
4
|
+
export { type AgeTitleSpan, type AgeTitleToken, TITLE_RANGE_MAX, TITLE_RANGE_MIN, adultBeside, ageTitleToken, } from "./age-title.ts";
|
|
5
5
|
export type { AgeArm, AgeClause, AgeComparison, AgeContext, AgeInterval, AgeKind, AgeLadder, AgeMisuse, AgeRefusal, AgeRelation, AgeRelationReason, AgeSource, AgeSpan, BirthYears, EmptyReason, Validated, YOBContext, YoungFloorVerdict, };
|
|
6
6
|
export declare const YOUNGEST_FLOOR = 0;
|
|
7
7
|
export declare const ADULT_FLOOR = 18;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@finbheara/domain",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.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
|
}
|