@finbheara/domain 0.8.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 CHANGED
@@ -269,7 +269,12 @@ Age.fromTitle("Beginner Adults Reel U40").token; // "adult u40": the adul
269
269
  Age.fromTitle("Solo Championship - Adult O40").token; // "adult o40": adult, and 40 or over
270
270
  Age.fromTitle("Adult Championship 18 - 29").token; // "adult 18-29": adult, and the range
271
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
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
273
278
  ```
274
279
 
275
280
  An adult word beside an `Under N` above 18 reads both: `adult u40` is adult (`isAdult()`), with
@@ -282,8 +287,13 @@ An adult word beside an and-over from 18 or a range from 18 reads both as well:
282
287
  band alone reads on the same ladder, a range floored at 18 where no rung sits below it. So
283
288
  `Age.from("adult o40", { year: 2026 })` is born 1985 or earlier, as `o40` is. An and-over under
284
289
  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.
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`.
287
297
 
288
298
  An age group is the age on the first of January, so a title that admits the dancer who is
289
299
  `N` (`7 & Under`, `7/U`, `7 Years`) is the `Under N+1` group.
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(?:\d{1,2}(?:u|o|&o))?\s*$/i;
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,40 +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
- if (tail && ADULT_CODE.test(inner)) {
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
- return adultOverUnder(title, s, lead, t);
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 === "" && adultAside !== null ? adultAside : adultOverUnder(title, s, lead, words);
111
+ return adultAside !== null && (words.token === "" || words.token === "adult") ? adultAside : adultOverUnder(title, s, lead, words);
100
112
  }
101
113
  var ADULT_UNDER_MIN = 18;
102
- var SENIOR_ADULT = /\bseniors?\s+adults?\b/i;
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
+ }
103
129
  function adultOverUnder(title, s, lead, t) {
130
+ if (t.token === "")
131
+ return t;
104
132
  const a = ADULT.exec(s);
105
133
  if (a === null)
106
134
  return t;
107
- const n = /^u(\d+)$/.exec(t.token)?.[1];
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))
135
+ const token = adultBeside(t.token);
136
+ if (token === undefined || token === t.token)
116
137
  return t;
117
- return { token: `adult ${t.token}`, span: t.span };
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 };
118
141
  }
119
142
 
120
143
  var YOUNGEST_FLOOR = 0;
@@ -586,6 +609,7 @@ export {
586
609
  YOUNGEST_BAND_TOP,
587
610
  YOUNGEST_FLOOR,
588
611
  YOUNG_FLOOR,
612
+ adultBeside,
589
613
  ageOnJanuary1,
590
614
  ageRelation,
591
615
  ageTitleToken,
@@ -10,3 +10,4 @@ export interface AgeTitleToken {
10
10
  readonly span: AgeTitleSpan | null;
11
11
  }
12
12
  export declare function ageTitleToken(title: string): AgeTitleToken;
13
+ export declare function adultBeside(band: string): string | undefined;
@@ -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.8.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",