@khgtrn/lib 1.0.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.
Files changed (37) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +113 -0
  3. package/dist/cjs/base-enum.d.ts +106 -0
  4. package/dist/cjs/base-enum.js +137 -0
  5. package/dist/cjs/func.d.ts +227 -0
  6. package/dist/cjs/func.js +574 -0
  7. package/dist/cjs/index.d.ts +4 -0
  8. package/dist/cjs/index.js +20 -0
  9. package/dist/cjs/number-to-words/helpers.d.ts +17 -0
  10. package/dist/cjs/number-to-words/helpers.js +72 -0
  11. package/dist/cjs/number-to-words/index.d.ts +30 -0
  12. package/dist/cjs/number-to-words/index.js +50 -0
  13. package/dist/cjs/number-to-words/locales.d.ts +7 -0
  14. package/dist/cjs/number-to-words/locales.js +106 -0
  15. package/dist/cjs/number-to-words/types.d.ts +35 -0
  16. package/dist/cjs/number-to-words/types.js +2 -0
  17. package/dist/cjs/package.json +4 -0
  18. package/dist/cjs/round.d.ts +13 -0
  19. package/dist/cjs/round.js +19 -0
  20. package/dist/esm/base-enum.d.ts +106 -0
  21. package/dist/esm/base-enum.js +133 -0
  22. package/dist/esm/func.d.ts +227 -0
  23. package/dist/esm/func.js +547 -0
  24. package/dist/esm/index.d.ts +4 -0
  25. package/dist/esm/index.js +4 -0
  26. package/dist/esm/number-to-words/helpers.d.ts +17 -0
  27. package/dist/esm/number-to-words/helpers.js +67 -0
  28. package/dist/esm/number-to-words/index.d.ts +30 -0
  29. package/dist/esm/number-to-words/index.js +47 -0
  30. package/dist/esm/number-to-words/locales.d.ts +7 -0
  31. package/dist/esm/number-to-words/locales.js +103 -0
  32. package/dist/esm/number-to-words/types.d.ts +35 -0
  33. package/dist/esm/number-to-words/types.js +1 -0
  34. package/dist/esm/package.json +4 -0
  35. package/dist/esm/round.d.ts +13 -0
  36. package/dist/esm/round.js +16 -0
  37. package/package.json +50 -0
@@ -0,0 +1,30 @@
1
+ import { NumberToWordsLocale } from "./types";
2
+ export { NumberToWordsLocale };
3
+ /**
4
+ * Spells out a number as words, following Vietnamese or English reading
5
+ * conventions (e.g. `1005` -> `"một nghìn không trăm linh năm"` in Vietnamese,
6
+ * `"one thousand five"` in English).
7
+ *
8
+ * Decimals are supported by passing `num` as a numeric **string** (e.g.
9
+ * `"1.05"`) rather than a `number` — a JS `number` can only exactly
10
+ * represent an integer here, so a non-integer `number` is rejected rather
11
+ * than silently read out with floating-point rounding artifacts (e.g.
12
+ * `0.1 + 0.2`). The fractional part is read one digit at a time after the
13
+ * locale's decimal separator word (e.g. `"1.05"` -> `"một phẩy không năm"`,
14
+ * `"one point zero five"`), keeping `1.05` distinct from `1.5` since a
15
+ * leading fractional zero changes the value. Trailing fractional zeros
16
+ * don't (`1.50 === 1.5`), so they're dropped — `"1.50"` reads the same as
17
+ * `"1.5"`.
18
+ *
19
+ * @param num - Number to convert, as an integer `number` or a numeric string
20
+ * (optionally with a decimal point, e.g. `"1.05"`). The integer part must be
21
+ * finite and within the supported range (currently up to just under 10^18,
22
+ * comfortably covering `Number.MAX_SAFE_INTEGER`).
23
+ * @param locale - Target language. Defaults to `"vi"`. To support another
24
+ * language, add an entry to the `definitions` registry in `locales.ts`.
25
+ * @returns The number spelled out in words.
26
+ * @throws {Error} If `num` is a non-integer `number`, an invalid numeric
27
+ * string, `locale` isn't registered, or the integer part exceeds the
28
+ * locale's supported range.
29
+ */
30
+ export declare function numberToWords(num: number | string, locale?: NumberToWordsLocale): string;
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.numberToWords = numberToWords;
4
+ const helpers_1 = require("./helpers");
5
+ const locales_1 = require("./locales");
6
+ /**
7
+ * Spells out a number as words, following Vietnamese or English reading
8
+ * conventions (e.g. `1005` -> `"một nghìn không trăm linh năm"` in Vietnamese,
9
+ * `"one thousand five"` in English).
10
+ *
11
+ * Decimals are supported by passing `num` as a numeric **string** (e.g.
12
+ * `"1.05"`) rather than a `number` — a JS `number` can only exactly
13
+ * represent an integer here, so a non-integer `number` is rejected rather
14
+ * than silently read out with floating-point rounding artifacts (e.g.
15
+ * `0.1 + 0.2`). The fractional part is read one digit at a time after the
16
+ * locale's decimal separator word (e.g. `"1.05"` -> `"một phẩy không năm"`,
17
+ * `"one point zero five"`), keeping `1.05` distinct from `1.5` since a
18
+ * leading fractional zero changes the value. Trailing fractional zeros
19
+ * don't (`1.50 === 1.5`), so they're dropped — `"1.50"` reads the same as
20
+ * `"1.5"`.
21
+ *
22
+ * @param num - Number to convert, as an integer `number` or a numeric string
23
+ * (optionally with a decimal point, e.g. `"1.05"`). The integer part must be
24
+ * finite and within the supported range (currently up to just under 10^18,
25
+ * comfortably covering `Number.MAX_SAFE_INTEGER`).
26
+ * @param locale - Target language. Defaults to `"vi"`. To support another
27
+ * language, add an entry to the `definitions` registry in `locales.ts`.
28
+ * @returns The number spelled out in words.
29
+ * @throws {Error} If `num` is a non-integer `number`, an invalid numeric
30
+ * string, `locale` isn't registered, or the integer part exceeds the
31
+ * locale's supported range.
32
+ */
33
+ function numberToWords(num, locale = "vi") {
34
+ const definition = locales_1.definitions[locale];
35
+ if (!definition) {
36
+ throw new Error(`numberToWords: unsupported locale "${locale}"`);
37
+ }
38
+ const normalized = (0, helpers_1.normalizeNumericInput)(num);
39
+ const isNegative = normalized.startsWith("-");
40
+ const numStr = isNegative ? normalized.slice(1) : normalized;
41
+ const [integerStr, rawFractionalStr] = numStr.split(".");
42
+ // Trailing zeros never change the fractional value (1.50 === 1.5), unlike
43
+ // leading zeros (0.05 !== 0.5), so only trailing zeros are safe to drop.
44
+ const fractionalStr = rawFractionalStr === null || rawFractionalStr === void 0 ? void 0 : rawFractionalStr.replace(/0+$/, "");
45
+ const integerWords = (0, helpers_1.convertIntegerPart)(Number(integerStr), definition, num, locale);
46
+ const words = fractionalStr
47
+ ? `${integerWords} ${definition.decimalSeparator} ${(0, helpers_1.convertFractionalPart)(fractionalStr, definition)}`
48
+ : integerWords;
49
+ return isNegative ? `${definition.negativePrefix}${words}` : words;
50
+ }
@@ -0,0 +1,7 @@
1
+ import { NumberToWordsDefinition, NumberToWordsLocale } from "./types";
2
+ /**
3
+ * Registry of per-language spelling rules. Add a new language by adding a
4
+ * key here (and, if its word lists warrant it, a dedicated block above) —
5
+ * no other file needs to change.
6
+ */
7
+ export declare const definitions: Record<NumberToWordsLocale, NumberToWordsDefinition>;
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.definitions = void 0;
4
+ const enOnes = [
5
+ "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
6
+ ];
7
+ const enTeens = [
8
+ "ten", "eleven", "twelve", "thirteen", "fourteen",
9
+ "fifteen", "sixteen", "seventeen", "eighteen", "nineteen",
10
+ ];
11
+ const enTens = [
12
+ "", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety",
13
+ ];
14
+ function convertGroupEn(n) {
15
+ const hundredsDigit = Math.floor(n / 100);
16
+ const remainder = n % 100;
17
+ const parts = [];
18
+ if (hundredsDigit > 0) {
19
+ parts.push(`${enOnes[hundredsDigit]} hundred`);
20
+ }
21
+ if (remainder > 0) {
22
+ if (remainder < 10) {
23
+ parts.push(enOnes[remainder]);
24
+ }
25
+ else if (remainder < 20) {
26
+ parts.push(enTeens[remainder - 10]);
27
+ }
28
+ else {
29
+ const tensDigit = Math.floor(remainder / 10);
30
+ const unitDigit = remainder % 10;
31
+ parts.push(unitDigit === 0 ? enTens[tensDigit] : `${enTens[tensDigit]}-${enOnes[unitDigit]}`);
32
+ }
33
+ }
34
+ return parts.join(" ");
35
+ }
36
+ const viOnes = [
37
+ "không", "một", "hai", "ba", "bốn", "năm", "sáu", "bảy", "tám", "chín",
38
+ ];
39
+ function convertGroupVi(n, isLeadingGroup) {
40
+ const hundredsDigit = Math.floor(n / 100);
41
+ const remainder = n % 100;
42
+ const tensDigit = Math.floor(remainder / 10);
43
+ const unitDigit = remainder % 10;
44
+ const parts = [];
45
+ if (hundredsDigit > 0) {
46
+ parts.push(`${viOnes[hundredsDigit]} trăm`);
47
+ }
48
+ else if (!isLeadingGroup) {
49
+ // A zero hundreds digit is still read out ("không trăm") in every group
50
+ // except the leading one, e.g. 1005 -> "một nghìn không trăm linh năm".
51
+ parts.push("không trăm");
52
+ }
53
+ if (remainder === 0) {
54
+ // Nothing more to add; a fully-zero group is filtered out by the caller.
55
+ }
56
+ else if (tensDigit === 0) {
57
+ // "linh" (roughly "and") introduces a lone unit digit whenever a tens
58
+ // (or hundreds) part was already read, e.g. "trăm linh năm" (105) or
59
+ // "không trăm linh năm" (...005); a bare leading unit needs no "linh".
60
+ parts.push(hundredsDigit > 0 || !isLeadingGroup ? `linh ${viOnes[unitDigit]}` : viOnes[unitDigit]);
61
+ }
62
+ else if (tensDigit === 1) {
63
+ // 10-19: "mười" [+ unit], with the "năm" -> "lăm" exception for 15.
64
+ parts.push(unitDigit === 0 ? "mười" : unitDigit === 5 ? "mười lăm" : `mười ${viOnes[unitDigit]}`);
65
+ }
66
+ else {
67
+ // 20-99: "{tensDigit} mươi" [+ unit], with "một" -> "mốt" and "năm" -> "lăm".
68
+ const tensWord = `${viOnes[tensDigit]} mươi`;
69
+ if (unitDigit === 0) {
70
+ parts.push(tensWord);
71
+ }
72
+ else if (unitDigit === 1) {
73
+ parts.push(`${tensWord} mốt`);
74
+ }
75
+ else if (unitDigit === 5) {
76
+ parts.push(`${tensWord} lăm`);
77
+ }
78
+ else {
79
+ parts.push(`${tensWord} ${viOnes[unitDigit]}`);
80
+ }
81
+ }
82
+ return parts.join(" ");
83
+ }
84
+ /**
85
+ * Registry of per-language spelling rules. Add a new language by adding a
86
+ * key here (and, if its word lists warrant it, a dedicated block above) —
87
+ * no other file needs to change.
88
+ */
89
+ exports.definitions = {
90
+ en: {
91
+ zero: "zero",
92
+ negativePrefix: "negative ",
93
+ decimalSeparator: "point",
94
+ digits: enOnes,
95
+ scaleWords: ["", "thousand", "million", "billion", "trillion", "quadrillion"],
96
+ convertGroup: convertGroupEn,
97
+ },
98
+ vi: {
99
+ zero: "không",
100
+ negativePrefix: "âm ",
101
+ decimalSeparator: "phẩy",
102
+ digits: viOnes,
103
+ scaleWords: ["", "nghìn", "triệu", "tỷ", "nghìn tỷ", "triệu tỷ"],
104
+ convertGroup: convertGroupVi,
105
+ },
106
+ };
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Locale code supported by {@link numberToWords}. Extend by adding a new key
3
+ * to the `definitions` registry in `locales.ts` (see
4
+ * {@link NumberToWordsDefinition}) — no changes to `numberToWords` itself
5
+ * are needed to add a language.
6
+ */
7
+ export type NumberToWordsLocale = "vi" | "en";
8
+ /**
9
+ * Per-locale rules needed to spell out numbers.
10
+ *
11
+ * Numbers are split into groups of 3 digits (thousands grouping), most
12
+ * significant group first. `scaleWords[i]` is the word placed after a group
13
+ * at position `i` counting from the right (`0` = units group, which gets no
14
+ * scale word; `1` = thousand-level; `2` = million-level; and so on).
15
+ */
16
+ export interface NumberToWordsDefinition {
17
+ /** Word for the number `0` on its own. */
18
+ zero: string;
19
+ /** Prefix used for negative numbers (including trailing space, if any). */
20
+ negativePrefix: string;
21
+ /** Word placed between the integer and fractional parts, e.g. "phẩy"/"point". */
22
+ decimalSeparator: string;
23
+ /** Word for each digit `0`-`9`, used to read the fractional part one digit at a time. */
24
+ digits: string[];
25
+ /** Scale words indexed by group position: `["", "thousand", "million", ...]`. */
26
+ scaleWords: string[];
27
+ /**
28
+ * Spells out a single 0-999 group.
29
+ * @param n - Group value, `1`-`999` (the caller never invokes this for `0`).
30
+ * @param isLeadingGroup - `true` for the most significant non-zero group of
31
+ * the whole number. Some languages (e.g. Vietnamese) read a zero hundreds
32
+ * digit explicitly ("không trăm") in every group except the leading one.
33
+ */
34
+ convertGroup(n: number, isLeadingGroup: boolean): string;
35
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,4 @@
1
+ {
2
+ "type": "commonjs",
3
+ "sideEffects": false
4
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Rounds `value` to `precision` decimal places, mainly to clean up binary
3
+ * floating-point noise from arithmetic (e.g. `491.66999999999996` instead of
4
+ * `491.67`) rather than to reduce genuine precision. `precision` defaults to
5
+ * 10, which is generous enough to preserve real fractional input while still
6
+ * clearing noise that typically appears around the 15th-17th significant
7
+ * digit.
8
+ *
9
+ * @param value - Number to round.
10
+ * @param precision - Number of decimal places to keep. Defaults to `10`.
11
+ * @returns `value` rounded to `precision` decimal places.
12
+ */
13
+ export declare function round(value: number, precision?: number): number;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.round = round;
4
+ /**
5
+ * Rounds `value` to `precision` decimal places, mainly to clean up binary
6
+ * floating-point noise from arithmetic (e.g. `491.66999999999996` instead of
7
+ * `491.67`) rather than to reduce genuine precision. `precision` defaults to
8
+ * 10, which is generous enough to preserve real fractional input while still
9
+ * clearing noise that typically appears around the 15th-17th significant
10
+ * digit.
11
+ *
12
+ * @param value - Number to round.
13
+ * @param precision - Number of decimal places to keep. Defaults to `10`.
14
+ * @returns `value` rounded to `precision` decimal places.
15
+ */
16
+ function round(value, precision = 10) {
17
+ const factor = Math.pow(10, precision);
18
+ return Math.round(value * factor) / factor;
19
+ }
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Base class for simulating Java-style enums in TypeScript.
3
+ *
4
+ * Subclasses only need to `extends BaseEnum<...>` and declare constants as
5
+ * `static readonly Xxx = new SubClass(value, label, opts?)`, without
6
+ * redeclaring a constructor. Because `BaseEnum`'s constructor is `protected`,
7
+ * subclasses inherit it while keeping the same protection — instances can't
8
+ * be `new`-ed from outside the class, which preserves enum singleton/identity
9
+ * semantics (`===` comparisons always work as expected).
10
+ *
11
+ * @typeParam T - Type of the `value` field (defaults to `number`).
12
+ */
13
+ export declare abstract class BaseEnum<T = number> {
14
+ readonly value: T;
15
+ readonly label: string;
16
+ readonly opts?: Record<string, any> | undefined;
17
+ /**
18
+ * Registry of every instance created, keyed per subclass and per `value`.
19
+ * The outer key is the subclass constructor (so each subclass has its own
20
+ * list), the inner key is each constant's `value`. Backs `values()`,
21
+ * `fromValue()` and `equals()`.
22
+ */
23
+ private static readonly registry;
24
+ /**
25
+ * Creates an enum constant. Only callable from within a subclass
26
+ * (the constructor is `protected`), typically from a `static readonly`
27
+ * field declaration.
28
+ *
29
+ * @param value - Identifying value of the constant (used by `fromValue()`, `equals()`).
30
+ * @param label - Display label/description of the constant.
31
+ * @param opts - Optional extra data, freely defined by the subclass as needed.
32
+ */
33
+ protected constructor(value: T, label: string, opts?: Record<string, any> | undefined);
34
+ /**
35
+ * Returns the names of the `static readonly` fields declared on the
36
+ * subclass, in declaration order. Mirrors the idea of an enum constant's
37
+ * name in Java, but here returns the names for every constant at once.
38
+ *
39
+ * @returns Array of constant names, e.g. `['Admin', 'User']`.
40
+ */
41
+ static names(this: Function): string[];
42
+ /**
43
+ * Returns every instance (constant) created on the subclass, similar to
44
+ * Java's `Enum.values()`.
45
+ *
46
+ * @returns Array of the subclass's instances, in creation order.
47
+ */
48
+ static values<T extends BaseEnum<any>>(this: Function & {
49
+ prototype: T;
50
+ }): T[];
51
+ /**
52
+ * Looks up a constant by its declared field name (matches Java's standard
53
+ * `Enum.valueOf(String)`). Unlike `fromValue()`, which looks up by `value`,
54
+ * this looks up by the static field's name (key).
55
+ *
56
+ * @param name - Name of the constant to look up, e.g. `'Admin'`.
57
+ * @returns The matching instance.
58
+ * @throws {Error} If no constant with that name exists.
59
+ */
60
+ static valueOf<T extends BaseEnum<any>>(this: Function & {
61
+ prototype: T;
62
+ }, name: string): T;
63
+ /**
64
+ * Looks up a constant by its `value` field.
65
+ *
66
+ * Note: `value`'s type isn't tied to the subclass's own `value` type
67
+ * parameter (unlike a conditional type would give) — that syntax requires
68
+ * TypeScript 2.8+, and this library targets TypeScript 2.7 and up.
69
+ *
70
+ * @param value - Value to look up (of the subclass's `T` type).
71
+ * @returns The matching instance, or `undefined` if none is found.
72
+ */
73
+ static fromValue<T extends BaseEnum<any>>(this: Function & {
74
+ prototype: T;
75
+ }, value: any): T | undefined;
76
+ /**
77
+ * Compares this constant against an arbitrary value.
78
+ *
79
+ * - If `other` is a `BaseEnum` instance: compares identity (`===`)
80
+ * directly, even if `other` belongs to a different enum class (always
81
+ * `false` in that case).
82
+ * - If `other` is a raw value (number/string/...): looks up the matching
83
+ * constant by `value` within this instance's own subclass, then compares
84
+ * identity.
85
+ * - Anything else (wrong type, no match, `null`/`undefined`...): returns
86
+ * `false`.
87
+ *
88
+ * @param other - Value or enum instance to compare against.
89
+ * @returns `true` if both refer to the same enum constant, otherwise `false`.
90
+ */
91
+ equals(other: any): boolean;
92
+ /**
93
+ * Returns the field name this instance was assigned to, similar to Java's
94
+ * `Enum.name()`.
95
+ *
96
+ * @returns The constant's name, e.g. `'Admin'`; an empty string if not
97
+ * found (a theoretical case that shouldn't occur under normal usage).
98
+ */
99
+ name(): string;
100
+ /**
101
+ * Converts the constant to a display string, defaulting to `label`.
102
+ *
103
+ * @returns The constant's display label.
104
+ */
105
+ toString(): string;
106
+ }
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Base class for simulating Java-style enums in TypeScript.
3
+ *
4
+ * Subclasses only need to `extends BaseEnum<...>` and declare constants as
5
+ * `static readonly Xxx = new SubClass(value, label, opts?)`, without
6
+ * redeclaring a constructor. Because `BaseEnum`'s constructor is `protected`,
7
+ * subclasses inherit it while keeping the same protection — instances can't
8
+ * be `new`-ed from outside the class, which preserves enum singleton/identity
9
+ * semantics (`===` comparisons always work as expected).
10
+ *
11
+ * @typeParam T - Type of the `value` field (defaults to `number`).
12
+ */
13
+ export class BaseEnum {
14
+ /**
15
+ * Creates an enum constant. Only callable from within a subclass
16
+ * (the constructor is `protected`), typically from a `static readonly`
17
+ * field declaration.
18
+ *
19
+ * @param value - Identifying value of the constant (used by `fromValue()`, `equals()`).
20
+ * @param label - Display label/description of the constant.
21
+ * @param opts - Optional extra data, freely defined by the subclass as needed.
22
+ */
23
+ constructor(value, label, opts) {
24
+ this.value = value;
25
+ this.label = label;
26
+ this.opts = opts;
27
+ let map = BaseEnum.registry.get(this.constructor);
28
+ if (!map) {
29
+ map = new Map();
30
+ BaseEnum.registry.set(this.constructor, map);
31
+ }
32
+ map.set(value, this);
33
+ }
34
+ /**
35
+ * Returns the names of the `static readonly` fields declared on the
36
+ * subclass, in declaration order. Mirrors the idea of an enum constant's
37
+ * name in Java, but here returns the names for every constant at once.
38
+ *
39
+ * @returns Array of constant names, e.g. `['Admin', 'User']`.
40
+ */
41
+ static names() {
42
+ return Object.getOwnPropertyNames(this).filter((key) => key !== "prototype" && this[key] instanceof BaseEnum);
43
+ }
44
+ /**
45
+ * Returns every instance (constant) created on the subclass, similar to
46
+ * Java's `Enum.values()`.
47
+ *
48
+ * @returns Array of the subclass's instances, in creation order.
49
+ */
50
+ static values() {
51
+ var _a;
52
+ return Array.from(((_a = BaseEnum.registry.get(this)) !== null && _a !== void 0 ? _a : new Map()).values());
53
+ }
54
+ /**
55
+ * Looks up a constant by its declared field name (matches Java's standard
56
+ * `Enum.valueOf(String)`). Unlike `fromValue()`, which looks up by `value`,
57
+ * this looks up by the static field's name (key).
58
+ *
59
+ * @param name - Name of the constant to look up, e.g. `'Admin'`.
60
+ * @returns The matching instance.
61
+ * @throws {Error} If no constant with that name exists.
62
+ */
63
+ static valueOf(name) {
64
+ const constant = this[name];
65
+ if (!(constant instanceof BaseEnum)) {
66
+ throw new Error(`No enum constant ${this.name}.${name}`);
67
+ }
68
+ return constant;
69
+ }
70
+ /**
71
+ * Looks up a constant by its `value` field.
72
+ *
73
+ * Note: `value`'s type isn't tied to the subclass's own `value` type
74
+ * parameter (unlike a conditional type would give) — that syntax requires
75
+ * TypeScript 2.8+, and this library targets TypeScript 2.7 and up.
76
+ *
77
+ * @param value - Value to look up (of the subclass's `T` type).
78
+ * @returns The matching instance, or `undefined` if none is found.
79
+ */
80
+ static fromValue(value) {
81
+ var _a;
82
+ return (_a = BaseEnum.registry.get(this)) === null || _a === void 0 ? void 0 : _a.get(value);
83
+ }
84
+ /**
85
+ * Compares this constant against an arbitrary value.
86
+ *
87
+ * - If `other` is a `BaseEnum` instance: compares identity (`===`)
88
+ * directly, even if `other` belongs to a different enum class (always
89
+ * `false` in that case).
90
+ * - If `other` is a raw value (number/string/...): looks up the matching
91
+ * constant by `value` within this instance's own subclass, then compares
92
+ * identity.
93
+ * - Anything else (wrong type, no match, `null`/`undefined`...): returns
94
+ * `false`.
95
+ *
96
+ * @param other - Value or enum instance to compare against.
97
+ * @returns `true` if both refer to the same enum constant, otherwise `false`.
98
+ */
99
+ equals(other) {
100
+ var _a;
101
+ if (other instanceof BaseEnum) {
102
+ return this === other;
103
+ }
104
+ return ((_a = BaseEnum.registry.get(this.constructor)) === null || _a === void 0 ? void 0 : _a.get(other)) === this;
105
+ }
106
+ /**
107
+ * Returns the field name this instance was assigned to, similar to Java's
108
+ * `Enum.name()`.
109
+ *
110
+ * @returns The constant's name, e.g. `'Admin'`; an empty string if not
111
+ * found (a theoretical case that shouldn't occur under normal usage).
112
+ */
113
+ name() {
114
+ const ctor = this.constructor;
115
+ const key = Object.getOwnPropertyNames(ctor).find((k) => k !== "prototype" && ctor[k] === this);
116
+ return key !== null && key !== void 0 ? key : "";
117
+ }
118
+ /**
119
+ * Converts the constant to a display string, defaulting to `label`.
120
+ *
121
+ * @returns The constant's display label.
122
+ */
123
+ toString() {
124
+ return this.label;
125
+ }
126
+ }
127
+ /**
128
+ * Registry of every instance created, keyed per subclass and per `value`.
129
+ * The outer key is the subclass constructor (so each subclass has its own
130
+ * list), the inner key is each constant's `value`. Backs `values()`,
131
+ * `fromValue()` and `equals()`.
132
+ */
133
+ BaseEnum.registry = new Map();