@budiauktioner/buildi-primitives 0.2.0 → 0.3.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 (144) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/PARITY.md +7 -7
  3. package/README.md +18 -1
  4. package/dist/cjs/contact/phone-number-test-data.js +1 -1
  5. package/dist/cjs/contact/phone-number-test-data.js.map +1 -1
  6. package/dist/cjs/contact/phone-number.js +36 -14
  7. package/dist/cjs/contact/phone-number.js.map +2 -2
  8. package/dist/cjs/internal/scan-boundaries.js +26 -0
  9. package/dist/cjs/internal/scan-boundaries.js.map +7 -0
  10. package/dist/cjs/measurement/area.js +2 -1
  11. package/dist/cjs/measurement/area.js.map +2 -2
  12. package/dist/cjs/measurement/data-size.js +1 -1
  13. package/dist/cjs/measurement/data-size.js.map +1 -1
  14. package/dist/cjs/measurement/electric-charge.js +1 -1
  15. package/dist/cjs/measurement/electric-charge.js.map +1 -1
  16. package/dist/cjs/measurement/electric-current.js +1 -1
  17. package/dist/cjs/measurement/electric-current.js.map +1 -1
  18. package/dist/cjs/measurement/energy.js +1 -1
  19. package/dist/cjs/measurement/energy.js.map +1 -1
  20. package/dist/cjs/measurement/flow-rate.js +1 -1
  21. package/dist/cjs/measurement/flow-rate.js.map +1 -1
  22. package/dist/cjs/measurement/frequency.js +1 -1
  23. package/dist/cjs/measurement/frequency.js.map +1 -1
  24. package/dist/cjs/measurement/length.js +1 -1
  25. package/dist/cjs/measurement/length.js.map +1 -1
  26. package/dist/cjs/measurement/luminance.js +1 -1
  27. package/dist/cjs/measurement/luminance.js.map +1 -1
  28. package/dist/cjs/measurement/luminous-flux.js +4 -1
  29. package/dist/cjs/measurement/luminous-flux.js.map +2 -2
  30. package/dist/cjs/measurement/percentage.js +1 -1
  31. package/dist/cjs/measurement/percentage.js.map +1 -1
  32. package/dist/cjs/measurement/power.js +7 -3
  33. package/dist/cjs/measurement/power.js.map +2 -2
  34. package/dist/cjs/measurement/pressure.js +1 -1
  35. package/dist/cjs/measurement/pressure.js.map +1 -1
  36. package/dist/cjs/measurement/rotational-speed.js +1 -1
  37. package/dist/cjs/measurement/rotational-speed.js.map +1 -1
  38. package/dist/cjs/measurement/sound-level.js +5 -1
  39. package/dist/cjs/measurement/sound-level.js.map +2 -2
  40. package/dist/cjs/measurement/speed.js +1 -1
  41. package/dist/cjs/measurement/speed.js.map +1 -1
  42. package/dist/cjs/measurement/temperature.js +1 -1
  43. package/dist/cjs/measurement/temperature.js.map +2 -2
  44. package/dist/cjs/measurement/torque.js +1 -1
  45. package/dist/cjs/measurement/torque.js.map +1 -1
  46. package/dist/cjs/measurement/voltage.js +1 -1
  47. package/dist/cjs/measurement/voltage.js.map +1 -1
  48. package/dist/cjs/measurement/volume.js +1 -1
  49. package/dist/cjs/measurement/volume.js.map +1 -1
  50. package/dist/cjs/measurement/weight.js +1 -1
  51. package/dist/cjs/measurement/weight.js.map +1 -1
  52. package/dist/cjs/organization/swedish-organization-number.js.map +2 -2
  53. package/dist/cjs/register-default-text-scanners.js +2 -0
  54. package/dist/cjs/register-default-text-scanners.js.map +2 -2
  55. package/dist/cjs/sample-data/index.js +18 -1
  56. package/dist/cjs/sample-data/index.js.map +2 -2
  57. package/dist/cjs/text-scanning/text-scan-result.js +3 -0
  58. package/dist/cjs/text-scanning/text-scan-result.js.map +2 -2
  59. package/dist/cjs/validation/validation-error-reason.js +6 -1
  60. package/dist/cjs/validation/validation-error-reason.js.map +2 -2
  61. package/dist/cjs/vehicle/index.js +4 -0
  62. package/dist/cjs/vehicle/index.js.map +2 -2
  63. package/dist/cjs/vehicle/watercraft-identification-number.js +332 -0
  64. package/dist/cjs/vehicle/watercraft-identification-number.js.map +7 -0
  65. package/dist/esm/contact/phone-number-test-data.js +1 -1
  66. package/dist/esm/contact/phone-number-test-data.js.map +1 -1
  67. package/dist/esm/contact/phone-number.js +36 -14
  68. package/dist/esm/contact/phone-number.js.map +2 -2
  69. package/dist/esm/internal/scan-boundaries.js +6 -0
  70. package/dist/esm/internal/scan-boundaries.js.map +7 -0
  71. package/dist/esm/measurement/area.js +2 -1
  72. package/dist/esm/measurement/area.js.map +2 -2
  73. package/dist/esm/measurement/data-size.js +1 -1
  74. package/dist/esm/measurement/data-size.js.map +1 -1
  75. package/dist/esm/measurement/electric-charge.js +1 -1
  76. package/dist/esm/measurement/electric-charge.js.map +1 -1
  77. package/dist/esm/measurement/electric-current.js +1 -1
  78. package/dist/esm/measurement/electric-current.js.map +1 -1
  79. package/dist/esm/measurement/energy.js +1 -1
  80. package/dist/esm/measurement/energy.js.map +1 -1
  81. package/dist/esm/measurement/flow-rate.js +1 -1
  82. package/dist/esm/measurement/flow-rate.js.map +1 -1
  83. package/dist/esm/measurement/frequency.js +1 -1
  84. package/dist/esm/measurement/frequency.js.map +1 -1
  85. package/dist/esm/measurement/length.js +1 -1
  86. package/dist/esm/measurement/length.js.map +1 -1
  87. package/dist/esm/measurement/luminance.js +1 -1
  88. package/dist/esm/measurement/luminance.js.map +1 -1
  89. package/dist/esm/measurement/luminous-flux.js +4 -1
  90. package/dist/esm/measurement/luminous-flux.js.map +2 -2
  91. package/dist/esm/measurement/percentage.js +1 -1
  92. package/dist/esm/measurement/percentage.js.map +1 -1
  93. package/dist/esm/measurement/power.js +7 -3
  94. package/dist/esm/measurement/power.js.map +2 -2
  95. package/dist/esm/measurement/pressure.js +1 -1
  96. package/dist/esm/measurement/pressure.js.map +1 -1
  97. package/dist/esm/measurement/rotational-speed.js +1 -1
  98. package/dist/esm/measurement/rotational-speed.js.map +1 -1
  99. package/dist/esm/measurement/sound-level.js +5 -1
  100. package/dist/esm/measurement/sound-level.js.map +2 -2
  101. package/dist/esm/measurement/speed.js +1 -1
  102. package/dist/esm/measurement/speed.js.map +1 -1
  103. package/dist/esm/measurement/temperature.js +1 -1
  104. package/dist/esm/measurement/temperature.js.map +2 -2
  105. package/dist/esm/measurement/torque.js +1 -1
  106. package/dist/esm/measurement/torque.js.map +1 -1
  107. package/dist/esm/measurement/voltage.js +1 -1
  108. package/dist/esm/measurement/voltage.js.map +1 -1
  109. package/dist/esm/measurement/volume.js +1 -1
  110. package/dist/esm/measurement/volume.js.map +1 -1
  111. package/dist/esm/measurement/weight.js +1 -1
  112. package/dist/esm/measurement/weight.js.map +1 -1
  113. package/dist/esm/organization/swedish-organization-number.js.map +2 -2
  114. package/dist/esm/register-default-text-scanners.js +2 -0
  115. package/dist/esm/register-default-text-scanners.js.map +2 -2
  116. package/dist/esm/sample-data/index.js +18 -1
  117. package/dist/esm/sample-data/index.js.map +2 -2
  118. package/dist/esm/text-scanning/text-scan-result.js +3 -0
  119. package/dist/esm/text-scanning/text-scan-result.js.map +2 -2
  120. package/dist/esm/validation/validation-error-reason.js +6 -1
  121. package/dist/esm/validation/validation-error-reason.js.map +2 -2
  122. package/dist/esm/vehicle/index.js +8 -0
  123. package/dist/esm/vehicle/index.js.map +2 -2
  124. package/dist/esm/vehicle/watercraft-identification-number.js +315 -0
  125. package/dist/esm/vehicle/watercraft-identification-number.js.map +7 -0
  126. package/dist/types/banking/index.d.cts +2 -2
  127. package/dist/types/banking/index.d.ts +2 -2
  128. package/dist/types/contact/index.d.cts +1 -1
  129. package/dist/types/contact/index.d.ts +1 -1
  130. package/dist/types/index.d.cts +2 -1
  131. package/dist/types/index.d.ts +2 -1
  132. package/dist/types/sample-data/index.d.cts +10 -2
  133. package/dist/types/sample-data/index.d.ts +10 -2
  134. package/dist/types/text-scanning/index.d.cts +3 -1
  135. package/dist/types/text-scanning/index.d.ts +3 -1
  136. package/dist/types/validation/index.d.cts +4 -0
  137. package/dist/types/validation/index.d.ts +4 -0
  138. package/dist/types/vehicle/index.d.cts +2 -0
  139. package/dist/types/vehicle/index.d.ts +2 -0
  140. package/dist/types/watercraft-identification-number-Db1I2LQM.d.cts +123 -0
  141. package/dist/types/watercraft-identification-number-o8B3C6Nx.d.ts +123 -0
  142. package/package.json +18 -13
  143. package/dist/types/{swedish-swish-number-CL6Gx_h3.d.cts → swedish-swish-number-Bh-UbVoN.d.cts} +136 -136
  144. package/dist/types/{swedish-swish-number-DDG3mNDg.d.ts → swedish-swish-number-C2Wccuyr.d.ts} +136 -136
package/CHANGELOG.md CHANGED
@@ -4,6 +4,62 @@ All notable changes to the TypeScript port of `Buildi.Primitives` will be
4
4
  documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
5
5
  and this package adheres to [Semantic Versioning](https://semver.org/).
6
6
 
7
+ ## [0.3.0] - 2026-10-28
8
+
9
+ Tracks `Buildi.Primitives` 0.22.0.
10
+
11
+ ### Added
12
+
13
+ - `WatercraftIdentificationNumber` in the `Vehicle` namespace: the hull
14
+ marking of a small craft (`skrovnummer`), in either the ISO 10087 format
15
+ (CIN/WIN, e.g. `NL-HXAB7A33G506`) or the twelve-character US hull
16
+ identification number (HIN, e.g. `ZZZ00001A020`). Input may be lower case,
17
+ contain whitespace, and omit the hyphen after the ISO country code; any other
18
+ hyphen or punctuation is rejected. `normalize()` returns the number without
19
+ separators, `format()` returns it as marked on the hull. The parsed value
20
+ exposes `scheme`, `countryCode`, `country`, `manufacturerCode`,
21
+ `serialNumber`, `productionMonthCode`, `productionMonth`,
22
+ `productionYearDigit` and `modelYearDigits`. Years stay as the encoded digits
23
+ because the number does not carry the century.
24
+ - `WatercraftIdentificationNumber.validate()` reports one issue per invalid
25
+ segment, using four new `ValidationErrorReason` values:
26
+ `InvalidManufacturerCode`, `InvalidSerialNumber`, `InvalidMonthCode` and
27
+ `InvalidYearDigits`.
28
+ - `maskWatercraftIdentificationNumber`, which keeps the country and
29
+ manufacturer codes visible, e.g. `NL-HXA*********`.
30
+ - `defaultTextScanner` finds watercraft identification numbers, exposed as
31
+ `TextScanResult.watercraftIdentificationNumbers`. A hyphenated ISO number is
32
+ reported with `Medium` confidence; an unseparated one and a US HIN with
33
+ `Low`.
34
+ - `VehicleSampleData.WatercraftIdentificationNumber` in `sample-data`, holding
35
+ the ISO 10087 worked example and a US HIN constructed for this library.
36
+
37
+ ### Fixed
38
+
39
+ - `PhoneNumber.formatted`, `toLocalString()` and `format()` put the hyphen
40
+ after the area code based on the number's length instead of the numbering
41
+ plan, so every mobile number and most landlines were split in the wrong
42
+ place (`0701-74 06 35` instead of `070-174 06 35`, `0846-50 04 56` instead
43
+ of `08-465 004 56`). The area code is now looked up in PTS's numbering plan
44
+ and the subscriber number is grouped by its own length, as Svenska
45
+ skrivregler recommends. Swedish numbers formatted internationally get the
46
+ same split, e.g. `+46 8 465 004 56`. `maskPhoneNumberDetailed` follows the
47
+ new area code, e.g. `070-*** ** 33`. The normalized form is unchanged.
48
+ - `ContactSampleData.PhoneNumber.swedishFixedTest` is now `+46846500400`, from the
49
+ PTS-reserved Stockholm range, instead of a number outside it.
50
+
51
+ ## [0.2.1] - 2026-08-07
52
+
53
+ ### Fixed
54
+
55
+ - The 21 `Measurement` scanners matched on ASCII word boundaries, so a unit
56
+ abbreviation followed by a non-ASCII letter ended the match and produced a
57
+ false positive. In Swedish prose this was common enough to matter:
58
+ `24 månader` scanned as `24 m`, `3 tågresor` as `3 t`, and `123 väger` as
59
+ `123 V`. They now use Unicode-aware boundaries, matching .NET, where `\b`
60
+ is Unicode-aware already. `Vehicle` and `Product` scanners were already
61
+ correct; this closes the same gap in `Measurement`.
62
+
7
63
  ## [0.2.0] - 2026-08-07
8
64
 
9
65
  ### Added
package/PARITY.md CHANGED
@@ -7,10 +7,10 @@ exactly how much of that surface is implemented here.
7
7
 
8
8
  | | |
9
9
  | ----------------- | -------------------------- |
10
- | .NET baseline | `Buildi.Primitives` 0.21.0 |
11
- | This package | `0.2.0` |
10
+ | .NET baseline | `Buildi.Primitives` 0.22.0 |
11
+ | This package | `0.3.0` |
12
12
  | Namespaces ported | 13 of 13 |
13
- | Types ported | 200 of 200 |
13
+ | Types ported | 201 of 201 |
14
14
 
15
15
  Type counts follow the catalog in the .NET README — the value-object types a
16
16
  consumer works with directly, excluding extension-method containers and internal
@@ -37,7 +37,7 @@ commit that changes the .NET baseline.
37
37
  | `Validation` | Complete | Shared `ValidationResult` infrastructure |
38
38
  | `TextScanning` | Complete | Scanner, candidates, overlap resolution, typed accessors |
39
39
  | `Measurement` | Full parity | 19 dimensions, 133 units, 5 standalone types, plus `TemperatureDelta` |
40
- | `Vehicle` | Full parity | 29 types, plus the `SwedishDrivingLicenseVehicleGroup` enum |
40
+ | `Vehicle` | Full parity | 30 types, plus the `SwedishDrivingLicenseVehicleGroup` enum |
41
41
  | `Product` | Full parity | 36 types, plus 6 nested enums |
42
42
 
43
43
  ## Cross-cutting features
@@ -46,9 +46,9 @@ commit that changes the .NET baseline.
46
46
  | -------------------------------------------------------------------------- | --------------------------------------------------- |
47
47
  | `tryParse` / `parse` / `isValid` / `format` / `normalize` / `isNormalized` | Complete for every value-object type |
48
48
  | `typeInfo` metadata (English + Swedish name, emoji, sources) | Complete |
49
- | `validate()` structured validation | Complete — all 14 types that have it in .NET |
50
- | `findCandidatesInText()` text scanning | Complete — all 70 types that have it in .NET |
51
- | `TextScanResult` typed accessors | Complete — all 67 .NET accessors, plus `luminances` |
49
+ | `validate()` structured validation | Complete — all 15 types that have it in .NET |
50
+ | `findCandidatesInText()` text scanning | Complete — all 71 types that have it in .NET |
51
+ | `TextScanResult` typed accessors | Complete — all 68 .NET accessors, plus `luminances` |
52
52
  | Masking (`toMaskedString` → `mask*` functions) | Complete |
53
53
  | Lookup and external-link helpers | Complete |
54
54
  | `PrimitivesDefaults` (culture, UI culture, country, calling code) | Complete |
package/README.md CHANGED
@@ -101,6 +101,22 @@ result.issues.map((i) => i.reason); // ["InvalidCheckDigit"]
101
101
  result.issues[0].description; // "Ogiltiga IBAN-kontrollsiffror."
102
102
  ```
103
103
 
104
+ Where a value is made of independent segments, every failing segment gets its
105
+ own issue:
106
+
107
+ ```ts
108
+ import { WatercraftIdentificationNumber } from "@budiauktioner/buildi-primitives/vehicle";
109
+
110
+ WatercraftIdentificationNumber.validate("NL-H0AB7A33M506").issues.map((i) => i.reason);
111
+ // ["InvalidManufacturerCode", "InvalidMonthCode"]
112
+
113
+ const win = WatercraftIdentificationNumber.parse("nlhxab7a33g506");
114
+ win.toString(); // "NL-HXAB7A33G506" — as marked on the hull
115
+ win.toNormalizedString(); // "NLHXAB7A33G506"
116
+ win.productionMonth; // 7
117
+ win.modelYearDigits; // "06" — the century is not encoded
118
+ ```
119
+
104
120
  `reason` is a stable `ValidationErrorReason` enum value safe to branch on.
105
121
  `description` is human-readable and follows the display language described
106
122
  under [Defaults and display language](#defaults-and-display-language); each
@@ -264,6 +280,7 @@ import {
264
280
  FuelType,
265
281
  TireDimension,
266
282
  VehicleIdentificationNumber,
283
+ WatercraftIdentificationNumber,
267
284
  } from "@budiauktioner/buildi-primitives/vehicle";
268
285
  import { Gtin13, ScreenSize, ClothingSize } from "@budiauktioner/buildi-primitives/product";
269
286
  import { ValidationErrorReason } from "@budiauktioner/buildi-primitives/validation";
@@ -274,7 +291,7 @@ particular a `defaultTextScanner` that already knows every scannable type.
274
291
 
275
292
  ## Runtime targets
276
293
 
277
- - Node.js 20.10 or newer (see [`.nvmrc`](.nvmrc)).
294
+ - Node.js 20.10 or newer.
278
295
  - Bun 1.1 or newer.
279
296
  - Modern browsers (current Chromium, Firefox, Safari).
280
297
  - No Node-only runtime APIs and no runtime dependencies; ES2022 plus standard
@@ -41,7 +41,7 @@ const PhoneNumberTestData = {
41
41
  get landlineMalmo() {
42
42
  return generateRange("04062804", 0, 99);
43
43
  },
44
- /** Landline Stockholm: 08-4650 04 00 through 08-4650 04 99 (100 numbers). */
44
+ /** Landline Stockholm: 08-465 004 00 through 08-465 004 99 (100 numbers). */
45
45
  get landlineStockholm() {
46
46
  return generateRange("08465004", 0, 99);
47
47
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/contact/phone-number-test-data.ts"],
4
- "sourcesContent": ["/**\n * PTS (Post- och telestyrelsen) reserves phone number ranges for use in fiction\n * and testing \u2014 these numbers are never assigned to real subscribers.\n *\n * {@link PhoneNumberTestData} enumerates every reserved number;\n * `PhoneNumber.isSwedishTestPhoneNumber` checks whether a parsed number falls in\n * a reserved range.\n *\n * Source: https://pts.se/internet-och-telefoni/telefonnummer-och-adressering/telefonnummer-till-bocker-och-filmer/\n */\nfunction generateRange(prefix: string, from: number, to: number): readonly string[] {\n const result: string[] = [];\n for (let i = from; i <= to; i++) {\n result.push(prefix + String(i).padStart(2, \"0\"));\n }\n return Object.freeze(result);\n}\n\nexport const PhoneNumberTestData = {\n /** Mobile: 070-174 06 05 through 070-174 06 99 (95 numbers). */\n get mobile(): readonly string[] {\n return generateRange(\"07017406\", 5, 99);\n },\n\n /** Landline Gothenburg: 031-390 06 00 through 031-390 06 99 (100 numbers). */\n get landlineGothenburg(): readonly string[] {\n return generateRange(\"03139006\", 0, 99);\n },\n\n /** Landline Malm\u00F6: 040-628 04 00 through 040-628 04 99 (100 numbers). */\n get landlineMalmo(): readonly string[] {\n return generateRange(\"04062804\", 0, 99);\n },\n\n /** Landline Stockholm: 08-4650 04 00 through 08-4650 04 99 (100 numbers). */\n get landlineStockholm(): readonly string[] {\n return generateRange(\"08465004\", 0, 99);\n },\n\n /** Landline Kiruna: 0980-31 92 00 through 0980-31 92 99 (100 numbers). */\n get landlineKiruna(): readonly string[] {\n return generateRange(\"09803192\", 0, 99);\n },\n\n /** All reserved test phone numbers across all ranges (495 numbers). */\n get all(): readonly string[] {\n return Object.freeze([\n ...PhoneNumberTestData.mobile,\n ...PhoneNumberTestData.landlineGothenburg,\n ...PhoneNumberTestData.landlineMalmo,\n ...PhoneNumberTestData.landlineStockholm,\n ...PhoneNumberTestData.landlineKiruna,\n ]);\n },\n};\n"],
4
+ "sourcesContent": ["/**\n * PTS (Post- och telestyrelsen) reserves phone number ranges for use in fiction\n * and testing \u2014 these numbers are never assigned to real subscribers.\n *\n * {@link PhoneNumberTestData} enumerates every reserved number;\n * `PhoneNumber.isSwedishTestPhoneNumber` checks whether a parsed number falls in\n * a reserved range.\n *\n * Source: https://pts.se/internet-och-telefoni/telefonnummer-och-adressering/telefonnummer-till-bocker-och-filmer/\n */\nfunction generateRange(prefix: string, from: number, to: number): readonly string[] {\n const result: string[] = [];\n for (let i = from; i <= to; i++) {\n result.push(prefix + String(i).padStart(2, \"0\"));\n }\n return Object.freeze(result);\n}\n\nexport const PhoneNumberTestData = {\n /** Mobile: 070-174 06 05 through 070-174 06 99 (95 numbers). */\n get mobile(): readonly string[] {\n return generateRange(\"07017406\", 5, 99);\n },\n\n /** Landline Gothenburg: 031-390 06 00 through 031-390 06 99 (100 numbers). */\n get landlineGothenburg(): readonly string[] {\n return generateRange(\"03139006\", 0, 99);\n },\n\n /** Landline Malm\u00F6: 040-628 04 00 through 040-628 04 99 (100 numbers). */\n get landlineMalmo(): readonly string[] {\n return generateRange(\"04062804\", 0, 99);\n },\n\n /** Landline Stockholm: 08-465 004 00 through 08-465 004 99 (100 numbers). */\n get landlineStockholm(): readonly string[] {\n return generateRange(\"08465004\", 0, 99);\n },\n\n /** Landline Kiruna: 0980-31 92 00 through 0980-31 92 99 (100 numbers). */\n get landlineKiruna(): readonly string[] {\n return generateRange(\"09803192\", 0, 99);\n },\n\n /** All reserved test phone numbers across all ranges (495 numbers). */\n get all(): readonly string[] {\n return Object.freeze([\n ...PhoneNumberTestData.mobile,\n ...PhoneNumberTestData.landlineGothenburg,\n ...PhoneNumberTestData.landlineMalmo,\n ...PhoneNumberTestData.landlineStockholm,\n ...PhoneNumberTestData.landlineKiruna,\n ]);\n },\n};\n"],
5
5
  "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAUA,SAAS,cAAc,QAAgB,MAAc,IAA+B;AAClF,QAAM,SAAmB,CAAC;AAC1B,WAAS,IAAI,MAAM,KAAK,IAAI,KAAK;AAC/B,WAAO,KAAK,SAAS,OAAO,CAAC,EAAE,SAAS,GAAG,GAAG,CAAC;AAAA,EACjD;AACA,SAAO,OAAO,OAAO,MAAM;AAC7B;AAEO,MAAM,sBAAsB;AAAA;AAAA,EAEjC,IAAI,SAA4B;AAC9B,WAAO,cAAc,YAAY,GAAG,EAAE;AAAA,EACxC;AAAA;AAAA,EAGA,IAAI,qBAAwC;AAC1C,WAAO,cAAc,YAAY,GAAG,EAAE;AAAA,EACxC;AAAA;AAAA,EAGA,IAAI,gBAAmC;AACrC,WAAO,cAAc,YAAY,GAAG,EAAE;AAAA,EACxC;AAAA;AAAA,EAGA,IAAI,oBAAuC;AACzC,WAAO,cAAc,YAAY,GAAG,EAAE;AAAA,EACxC;AAAA;AAAA,EAGA,IAAI,iBAAoC;AACtC,WAAO,cAAc,YAAY,GAAG,EAAE;AAAA,EACxC;AAAA;AAAA,EAGA,IAAI,MAAyB;AAC3B,WAAO,OAAO,OAAO;AAAA,MACnB,GAAG,oBAAoB;AAAA,MACvB,GAAG,oBAAoB;AAAA,MACvB,GAAG,oBAAoB;AAAA,MACvB,GAAG,oBAAoB;AAAA,MACvB,GAAG,oBAAoB;AAAA,IACzB,CAAC;AAAA,EACH;AACF;",
6
6
  "names": []
7
7
  }
@@ -221,7 +221,7 @@ class PhoneNumber {
221
221
  return this.countryCallingCode.value === dcc ? this.toLocalString() : this.toDisplayInternationalString();
222
222
  }
223
223
  const subscriber = this.digits.slice(2 + this.countryCallingCode.value.length);
224
- if (this.isSwedish) return formatSwedishLocal("0" + subscriber);
224
+ if (this.isSwedish) return formatSwedishLocal(subscriber);
225
225
  return "0" + subscriber;
226
226
  }
227
227
  toE164String() {
@@ -232,6 +232,7 @@ class PhoneNumber {
232
232
  }
233
233
  toDisplayInternationalString() {
234
234
  const subscriber = this.digits.slice(2 + this.countryCallingCode.value.length);
235
+ if (this.isSwedish) return formatSwedishInternational(subscriber);
235
236
  return "+" + this.countryCallingCode.value + " " + formatInternationalSubscriberDigits(this.countryCallingCode.value, subscriber);
236
237
  }
237
238
  toString() {
@@ -305,21 +306,42 @@ function formatInternationalSubscriberDigits(callingCode, digits) {
305
306
  return digits;
306
307
  }
307
308
  }
308
- function formatSwedishLocal(number) {
309
- const digits = number.replace(/^0+/, "");
310
- if (digits.length === 9) {
311
- return `0${digits.slice(0, 3)}-${digits.slice(3, 5)} ${digits.slice(5, 7)} ${digits.slice(7, 9)}`;
309
+ const SWEDISH_TWO_DIGIT_DESTINATION_CODES = /* @__PURE__ */ new Set([
310
+ ..."11 13 16 18 19 21 23 26 31 33 35 36 40 42 44 46 54 60 63 90".split(" "),
311
+ ..."10 20 70 71 72 73 74 75 76 77 79 99".split(" ")
312
+ ]);
313
+ const SWEDISH_THREE_DIGIT_DESTINATION_CODES_OVERRIDING_TWO_DIGIT = /* @__PURE__ */ new Set(["900"]);
314
+ function swedishDestinationCodeLength(nationalSignificantNumber) {
315
+ if (nationalSignificantNumber.startsWith("8")) return 1;
316
+ if (SWEDISH_THREE_DIGIT_DESTINATION_CODES_OVERRIDING_TWO_DIGIT.has(
317
+ nationalSignificantNumber.slice(0, 3)
318
+ )) {
319
+ return 3;
312
320
  }
313
- if (digits.length === 7) {
314
- const area = digits.slice(0, 1);
315
- const rest = digits.slice(1);
316
- return `0${area}-${rest.slice(0, 2)} ${rest.slice(2, 4)} ${rest.slice(4, 6)}`;
321
+ if (SWEDISH_TWO_DIGIT_DESTINATION_CODES.has(nationalSignificantNumber.slice(0, 2))) return 2;
322
+ return 3;
323
+ }
324
+ function groupSwedishSubscriberNumber(subscriber) {
325
+ if (subscriber.length === 8) {
326
+ return `${subscriber.slice(0, 3)} ${subscriber.slice(3, 6)} ${subscriber.slice(6)}`;
317
327
  }
318
- if (digits.length === 8) {
319
- const area = digits.slice(0, 1);
320
- const rest = digits.slice(1);
321
- return `0${area}-${rest.slice(0, 2)} ${rest.slice(2, 4)} ${rest.slice(4, 7)}`;
328
+ const firstGroupLength = subscriber.length % 2 === 1 ? 3 : 2;
329
+ const groups = [subscriber.slice(0, firstGroupLength)];
330
+ for (let i = firstGroupLength; i < subscriber.length; i += 2) {
331
+ groups.push(subscriber.slice(i, i + 2));
322
332
  }
323
- return number;
333
+ return groups.join(" ");
334
+ }
335
+ function splitSwedishNationalNumber(nationalSignificantNumber) {
336
+ const length = swedishDestinationCodeLength(nationalSignificantNumber);
337
+ return [nationalSignificantNumber.slice(0, length), nationalSignificantNumber.slice(length)];
338
+ }
339
+ function formatSwedishLocal(nationalSignificantNumber) {
340
+ const [destinationCode, subscriber] = splitSwedishNationalNumber(nationalSignificantNumber);
341
+ return `0${destinationCode}-${groupSwedishSubscriberNumber(subscriber)}`;
342
+ }
343
+ function formatSwedishInternational(nationalSignificantNumber) {
344
+ const [destinationCode, subscriber] = splitSwedishNationalNumber(nationalSignificantNumber);
345
+ return `+46 ${destinationCode} ${groupSwedishSubscriberNumber(subscriber)}`;
324
346
  }
325
347
  //# sourceMappingURL=phone-number.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/contact/phone-number.ts"],
4
- "sourcesContent": ["import { PrimitiveTypeInfo } from \"../primitive-type-info.js\";\nimport { PrimitivesDefaults } from \"../primitives-defaults.js\";\nimport { sanitizeInput } from \"../internal/input-sanitization.js\";\nimport { TextCandidateCategory } from \"../text-scanning/text-candidate-category.js\";\nimport { TextCandidate } from \"../text-scanning/text-candidate.js\";\nimport { TextMatchConfidence } from \"../text-scanning/text-match-confidence.js\";\nimport { Country } from \"../geography/index.js\";\nimport { PhoneCallingCode } from \"./phone-calling-code.js\";\n\nconst MAX_INPUT_LENGTH = 50;\nconst URI_PREFIXES = [\"callto:\", \"tel:\", \"sms:\", \"call:\"];\n\nconst TWO_DIGIT_CODES = new Set([\n \"20\",\n \"27\",\n \"30\",\n \"31\",\n \"32\",\n \"33\",\n \"34\",\n \"36\",\n \"39\",\n \"40\",\n \"41\",\n \"43\",\n \"44\",\n \"45\",\n \"46\",\n \"47\",\n \"48\",\n \"49\",\n \"51\",\n \"52\",\n \"53\",\n \"54\",\n \"55\",\n \"56\",\n \"57\",\n \"58\",\n \"60\",\n \"61\",\n \"62\",\n \"63\",\n \"64\",\n \"65\",\n \"66\",\n \"81\",\n \"82\",\n \"84\",\n \"86\",\n \"90\",\n \"91\",\n \"92\",\n \"93\",\n \"94\",\n \"95\",\n \"98\",\n]);\n\nconst LEADING_ZERO_SIGNIFICANT = new Set([\"39\"]);\n\nconst SCAN =\n /(?:(?:\\+|00)\\d{1,3}[\\s-]\\(?\\d{1,4}\\)?[\\s-]?\\d[\\d\\s-]{5,12}\\d)|(?:(?:\\+|00)\\d{7,15})|(?:0\\d{1,3}[\\s-]?\\d[\\d\\s-]{5,10}\\d)/g;\n\n/**\n * A phone number (`telefonnummer`). Stored normalized as `00` + country code +\n * subscriber digits (e.g. `0046701740633`). Use {@link toE164String} for the\n * `+`-prefixed E.164 form.\n */\nexport class PhoneNumber {\n static readonly typeInfo: PrimitiveTypeInfo = Object.freeze({\n englishName: \"Phone Number\",\n localizedName: \"Telefonnummer\",\n emoji: \"\\u{1F4DE}\",\n sources: Object.freeze([\n \"https://pts.se/\",\n \"https://www.itu.int/rec/T-REC-E.164/\",\n \"https://en.wikipedia.org/wiki/E.164\",\n ]),\n });\n\n readonly digits: string;\n readonly countryCallingCode: PhoneCallingCode;\n readonly country: Country | null;\n readonly isSwedish: boolean;\n readonly isMobile: boolean;\n\n private constructor(digits: string, isSwedish: boolean, isMobile: boolean) {\n this.digits = digits;\n this.isSwedish = isSwedish;\n this.isMobile = isMobile;\n this.countryCallingCode = PhoneCallingCode.fromResolvedDigits(resolveCallingCode(digits));\n this.country = Country.tryFindByCallingCode(this.countryCallingCode.value);\n }\n\n get isSwedishTestPhoneNumber(): boolean {\n if (!this.isSwedish) return false;\n const local = this.digits.slice(4);\n return (\n isInPtsRange(local, \"7017406\", 5, 99) ||\n isInPtsRange(local, \"3139006\", 0, 99) ||\n isInPtsRange(local, \"4062804\", 0, 99) ||\n isInPtsRange(local, \"8465004\", 0, 99) ||\n isInPtsRange(local, \"9803192\", 0, 99)\n );\n }\n\n get formatted(): string {\n return this.isSwedish ? this.toLocalString() : this.toDisplayInternationalString();\n }\n\n static tryParse(\n input: string | null | undefined,\n defaultCallingCode?: string | PhoneCallingCode | Country | null,\n ): PhoneNumber | null {\n if (input == null || input.trim() === \"\") return null;\n const dcc = resolveDefaultCallingCode(defaultCallingCode);\n if (!dcc) return null;\n\n let cleaned = sanitizeInput(input).trim();\n if (cleaned.length > MAX_INPUT_LENGTH) return null;\n\n for (const prefix of URI_PREFIXES) {\n if (cleaned.toLowerCase().startsWith(prefix)) {\n cleaned = cleaned.slice(prefix.length).trimStart();\n break;\n }\n }\n\n const hasPlus = cleaned.startsWith(\"+\");\n const withOAs0 = cleaned.replace(/O/g, \"0\");\n const digitsOnly = withOAs0.replace(/[^\\d]/g, \"\");\n if (digitsOnly === \"\") return null;\n\n let e164: string;\n if (hasPlus) {\n if (digitsOnly.length < 7 || digitsOnly.length > 15) return null;\n e164 = stripTrunkZeroAfterCallingCode(digitsOnly);\n } else if (digitsOnly.startsWith(\"00\")) {\n const cn = digitsOnly.slice(2);\n if (cn.length < 7 || cn.length > 15) return null;\n e164 = stripTrunkZeroAfterCallingCode(cn);\n } else if (digitsOnly.startsWith(dcc) && digitsOnly.length >= dcc.length + 7) {\n const subscriber = digitsOnly.slice(dcc.length);\n e164 = dcc + stripSingleTrunkZero(dcc, subscriber);\n } else if (digitsOnly.startsWith(\"0\")) {\n const subscriber = stripSingleTrunkZero(dcc, digitsOnly);\n const total = dcc.length + subscriber.length;\n if (total < 7 || total > 15) return null;\n e164 = dcc + subscriber;\n } else if (dcc === \"46\" && digitsOnly.length === 9 && digitsOnly.startsWith(\"7\")) {\n e164 = \"46\" + digitsOnly;\n } else {\n return null;\n }\n\n if (e164.length < 7 || e164.length > 15) return null;\n const isSwedish = e164.startsWith(\"46\");\n if (isSwedish) {\n const local = e164.slice(2);\n if (local.length < 7 || local.length > 9) return null;\n }\n const isMobile = isSwedish && e164.slice(2).startsWith(\"7\");\n return new PhoneNumber(\"00\" + e164, isSwedish, isMobile);\n }\n\n static parse(\n input: string,\n defaultCallingCode?: string | PhoneCallingCode | Country | null,\n ): PhoneNumber {\n const r = PhoneNumber.tryParse(input, defaultCallingCode);\n if (r === null) throw new Error(\"Invalid phone number.\");\n return r;\n }\n\n static isValid(\n input: string | null | undefined,\n defaultCallingCode?: string | PhoneCallingCode | Country | null,\n ): boolean {\n return PhoneNumber.tryParse(input, defaultCallingCode) !== null;\n }\n\n static format(\n input: string | null | undefined,\n options?: {\n defaultCallingCode?: string | PhoneCallingCode | Country | null;\n fallbackToTrimmedInputWhenInvalid?: boolean;\n },\n ): string | null {\n const dcc =\n resolveDefaultCallingCode(options?.defaultCallingCode) ??\n PrimitivesDefaults.defaultCallingCode;\n const r = PhoneNumber.tryParse(input, dcc);\n if (r) {\n return r.countryCallingCode.value === dcc\n ? r.toLocalString()\n : r.toDisplayInternationalString();\n }\n if (options?.fallbackToTrimmedInputWhenInvalid && input != null && input.trim() !== \"\") {\n return input.trim();\n }\n return null;\n }\n\n static normalize(\n input: string | null | undefined,\n options?: {\n defaultCallingCode?: string | PhoneCallingCode | Country | null;\n fallbackToTrimmedInputWhenInvalid?: boolean;\n },\n ): string | null {\n const r = PhoneNumber.tryParse(input, options?.defaultCallingCode);\n if (r) return r.digits;\n if (options?.fallbackToTrimmedInputWhenInvalid && input != null) {\n const t = input.trim();\n return t.length > 0 ? t : null;\n }\n return null;\n }\n\n static isNormalized(input: string | null | undefined): boolean {\n return input != null && PhoneNumber.normalize(input) === input;\n }\n\n static findCandidatesInText(text: string): readonly TextCandidate<PhoneNumber>[] {\n if (!text) return [];\n const out: TextCandidate<PhoneNumber>[] = [];\n SCAN.lastIndex = 0;\n let m: RegExpExecArray | null;\n while ((m = SCAN.exec(text)) !== null) {\n const phone = PhoneNumber.tryParse(m[0]);\n if (!phone) continue;\n out.push(\n new TextCandidate<PhoneNumber>({\n startIndex: m.index,\n length: m[0].length,\n originalText: m[0],\n typeName: \"PhoneNumber\",\n category: TextCandidateCategory.Contact,\n normalizedForm: phone.toNormalizedString(),\n formattedForm: phone.toString(),\n maskedForm: maskPhoneNumber(phone),\n confidence: TextMatchConfidence.Medium,\n value: phone,\n }),\n );\n }\n return out;\n }\n\n toNormalizedString(): string {\n return this.digits;\n }\n\n toLocalString(defaultCallingCode?: PhoneCallingCode | Country): string {\n if (defaultCallingCode) {\n const dcc =\n defaultCallingCode instanceof PhoneCallingCode\n ? defaultCallingCode.value\n : defaultCallingCode.callingCode;\n return this.countryCallingCode.value === dcc\n ? this.toLocalString()\n : this.toDisplayInternationalString();\n }\n const subscriber = this.digits.slice(2 + this.countryCallingCode.value.length);\n if (this.isSwedish) return formatSwedishLocal(\"0\" + subscriber);\n return \"0\" + subscriber;\n }\n\n toE164String(): string {\n return \"+\" + this.digits.slice(2);\n }\n toInternationalString(): string {\n return this.toE164String();\n }\n\n toDisplayInternationalString(): string {\n const subscriber = this.digits.slice(2 + this.countryCallingCode.value.length);\n return (\n \"+\" +\n this.countryCallingCode.value +\n \" \" +\n formatInternationalSubscriberDigits(this.countryCallingCode.value, subscriber)\n );\n }\n\n toString(): string {\n return this.formatted;\n }\n\n equals(other: PhoneNumber | null | undefined): boolean {\n return other != null && this.digits === other.digits;\n }\n compareTo(other: PhoneNumber | null | undefined): number {\n if (other == null) return 1;\n return this.digits < other.digits ? -1 : this.digits > other.digits ? 1 : 0;\n }\n}\n\n/** Mask a phone number, keeping country code and last 4 digits visible. */\nexport function maskPhoneNumber(p: PhoneNumber): string {\n const f = p.toE164String();\n if (f.length <= 5) return f.replace(/\\d/g, \"*\");\n const cc = \"+\" + p.countryCallingCode.value;\n const subscriber = f.slice(cc.length);\n if (subscriber.length <= 4) return cc + \"*\".repeat(subscriber.length);\n return cc + \"*\".repeat(subscriber.length - 4) + subscriber.slice(-4);\n}\n\nfunction resolveDefaultCallingCode(\n d: string | PhoneCallingCode | Country | null | undefined,\n): string | null {\n if (d == null) return PrimitivesDefaults.defaultCallingCode;\n if (typeof d === \"string\") return d;\n if (d instanceof PhoneCallingCode) return d.value;\n return d.callingCode;\n}\n\nfunction resolveCallingCode(digits: string): string {\n return resolveCallingCodeFromE164(digits.slice(2));\n}\n\nfunction resolveCallingCodeFromE164(e164: string): string {\n if (e164.length === 0) return \"\";\n if (e164[0] === \"1\" || e164[0] === \"7\") return e164.slice(0, 1);\n if (e164.length >= 2 && TWO_DIGIT_CODES.has(e164.slice(0, 2))) return e164.slice(0, 2);\n return e164.length >= 3 ? e164.slice(0, 3) : e164;\n}\n\nfunction stripTrunkZeroAfterCallingCode(e164: string): string {\n const cc = resolveCallingCodeFromE164(e164);\n if (cc.length === 0 || cc.length >= e164.length) return e164;\n const subscriber = e164.slice(cc.length);\n return cc + stripSingleTrunkZero(cc, subscriber);\n}\n\nfunction stripSingleTrunkZero(callingCode: string, subscriber: string): string {\n if (subscriber.length === 0 || subscriber[0] !== \"0\") return subscriber;\n if (LEADING_ZERO_SIGNIFICANT.has(callingCode)) return subscriber;\n return subscriber.slice(1);\n}\n\nfunction isInPtsRange(local: string, prefix: string, from: number, to: number): boolean {\n if (!local.startsWith(prefix)) return false;\n const suffix = local.slice(prefix.length);\n if (suffix.length !== 2) return false;\n const n = parseInt(suffix, 10);\n return Number.isFinite(n) && n >= from && n <= to;\n}\n\nfunction formatInternationalSubscriberDigits(callingCode: string, digits: string): string {\n if (callingCode === \"1\" && digits.length === 10) {\n return `${digits.slice(0, 3)} ${digits.slice(3, 6)} ${digits.slice(6, 10)}`;\n }\n switch (digits.length) {\n case 7:\n return `${digits.slice(0, 3)} ${digits.slice(3, 5)} ${digits.slice(5, 7)}`;\n case 8:\n return `${digits.slice(0, 2)} ${digits.slice(2, 4)} ${digits.slice(4, 6)} ${digits.slice(6, 8)}`;\n case 9:\n return `${digits.slice(0, 2)} ${digits.slice(2, 5)} ${digits.slice(5, 7)} ${digits.slice(7, 9)}`;\n case 10:\n return `${digits.slice(0, 2)} ${digits.slice(2, 6)} ${digits.slice(6, 10)}`;\n case 11:\n return `${digits.slice(0, 3)} ${digits.slice(3, 7)} ${digits.slice(7, 11)}`;\n default:\n return digits;\n }\n}\n\nfunction formatSwedishLocal(number: string): string {\n const digits = number.replace(/^0+/, \"\");\n if (digits.length === 9) {\n return `0${digits.slice(0, 3)}-${digits.slice(3, 5)} ${digits.slice(5, 7)} ${digits.slice(7, 9)}`;\n }\n if (digits.length === 7) {\n const area = digits.slice(0, 1);\n const rest = digits.slice(1);\n return `0${area}-${rest.slice(0, 2)} ${rest.slice(2, 4)} ${rest.slice(4, 6)}`;\n }\n if (digits.length === 8) {\n const area = digits.slice(0, 1);\n const rest = digits.slice(1);\n return `0${area}-${rest.slice(0, 2)} ${rest.slice(2, 4)} ${rest.slice(4, 7)}`;\n }\n return number;\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { PrimitiveTypeInfo } from \"../primitive-type-info.js\";\nimport { PrimitivesDefaults } from \"../primitives-defaults.js\";\nimport { sanitizeInput } from \"../internal/input-sanitization.js\";\nimport { TextCandidateCategory } from \"../text-scanning/text-candidate-category.js\";\nimport { TextCandidate } from \"../text-scanning/text-candidate.js\";\nimport { TextMatchConfidence } from \"../text-scanning/text-match-confidence.js\";\nimport { Country } from \"../geography/index.js\";\nimport { PhoneCallingCode } from \"./phone-calling-code.js\";\n\nconst MAX_INPUT_LENGTH = 50;\nconst URI_PREFIXES = [\"callto:\", \"tel:\", \"sms:\", \"call:\"];\n\nconst TWO_DIGIT_CODES = new Set([\n \"20\",\n \"27\",\n \"30\",\n \"31\",\n \"32\",\n \"33\",\n \"34\",\n \"36\",\n \"39\",\n \"40\",\n \"41\",\n \"43\",\n \"44\",\n \"45\",\n \"46\",\n \"47\",\n \"48\",\n \"49\",\n \"51\",\n \"52\",\n \"53\",\n \"54\",\n \"55\",\n \"56\",\n \"57\",\n \"58\",\n \"60\",\n \"61\",\n \"62\",\n \"63\",\n \"64\",\n \"65\",\n \"66\",\n \"81\",\n \"82\",\n \"84\",\n \"86\",\n \"90\",\n \"91\",\n \"92\",\n \"93\",\n \"94\",\n \"95\",\n \"98\",\n]);\n\nconst LEADING_ZERO_SIGNIFICANT = new Set([\"39\"]);\n\nconst SCAN =\n /(?:(?:\\+|00)\\d{1,3}[\\s-]\\(?\\d{1,4}\\)?[\\s-]?\\d[\\d\\s-]{5,12}\\d)|(?:(?:\\+|00)\\d{7,15})|(?:0\\d{1,3}[\\s-]?\\d[\\d\\s-]{5,10}\\d)/g;\n\n/**\n * A phone number (`telefonnummer`). Stored normalized as `00` + country code +\n * subscriber digits (e.g. `0046701740633`). Use {@link toE164String} for the\n * `+`-prefixed E.164 form.\n */\nexport class PhoneNumber {\n static readonly typeInfo: PrimitiveTypeInfo = Object.freeze({\n englishName: \"Phone Number\",\n localizedName: \"Telefonnummer\",\n emoji: \"\\u{1F4DE}\",\n sources: Object.freeze([\n \"https://pts.se/\",\n \"https://www.itu.int/rec/T-REC-E.164/\",\n \"https://en.wikipedia.org/wiki/E.164\",\n ]),\n });\n\n readonly digits: string;\n readonly countryCallingCode: PhoneCallingCode;\n readonly country: Country | null;\n readonly isSwedish: boolean;\n readonly isMobile: boolean;\n\n private constructor(digits: string, isSwedish: boolean, isMobile: boolean) {\n this.digits = digits;\n this.isSwedish = isSwedish;\n this.isMobile = isMobile;\n this.countryCallingCode = PhoneCallingCode.fromResolvedDigits(resolveCallingCode(digits));\n this.country = Country.tryFindByCallingCode(this.countryCallingCode.value);\n }\n\n get isSwedishTestPhoneNumber(): boolean {\n if (!this.isSwedish) return false;\n const local = this.digits.slice(4);\n return (\n isInPtsRange(local, \"7017406\", 5, 99) ||\n isInPtsRange(local, \"3139006\", 0, 99) ||\n isInPtsRange(local, \"4062804\", 0, 99) ||\n isInPtsRange(local, \"8465004\", 0, 99) ||\n isInPtsRange(local, \"9803192\", 0, 99)\n );\n }\n\n get formatted(): string {\n return this.isSwedish ? this.toLocalString() : this.toDisplayInternationalString();\n }\n\n static tryParse(\n input: string | null | undefined,\n defaultCallingCode?: string | PhoneCallingCode | Country | null,\n ): PhoneNumber | null {\n if (input == null || input.trim() === \"\") return null;\n const dcc = resolveDefaultCallingCode(defaultCallingCode);\n if (!dcc) return null;\n\n let cleaned = sanitizeInput(input).trim();\n if (cleaned.length > MAX_INPUT_LENGTH) return null;\n\n for (const prefix of URI_PREFIXES) {\n if (cleaned.toLowerCase().startsWith(prefix)) {\n cleaned = cleaned.slice(prefix.length).trimStart();\n break;\n }\n }\n\n const hasPlus = cleaned.startsWith(\"+\");\n const withOAs0 = cleaned.replace(/O/g, \"0\");\n const digitsOnly = withOAs0.replace(/[^\\d]/g, \"\");\n if (digitsOnly === \"\") return null;\n\n let e164: string;\n if (hasPlus) {\n if (digitsOnly.length < 7 || digitsOnly.length > 15) return null;\n e164 = stripTrunkZeroAfterCallingCode(digitsOnly);\n } else if (digitsOnly.startsWith(\"00\")) {\n const cn = digitsOnly.slice(2);\n if (cn.length < 7 || cn.length > 15) return null;\n e164 = stripTrunkZeroAfterCallingCode(cn);\n } else if (digitsOnly.startsWith(dcc) && digitsOnly.length >= dcc.length + 7) {\n const subscriber = digitsOnly.slice(dcc.length);\n e164 = dcc + stripSingleTrunkZero(dcc, subscriber);\n } else if (digitsOnly.startsWith(\"0\")) {\n const subscriber = stripSingleTrunkZero(dcc, digitsOnly);\n const total = dcc.length + subscriber.length;\n if (total < 7 || total > 15) return null;\n e164 = dcc + subscriber;\n } else if (dcc === \"46\" && digitsOnly.length === 9 && digitsOnly.startsWith(\"7\")) {\n e164 = \"46\" + digitsOnly;\n } else {\n return null;\n }\n\n if (e164.length < 7 || e164.length > 15) return null;\n const isSwedish = e164.startsWith(\"46\");\n if (isSwedish) {\n const local = e164.slice(2);\n if (local.length < 7 || local.length > 9) return null;\n }\n const isMobile = isSwedish && e164.slice(2).startsWith(\"7\");\n return new PhoneNumber(\"00\" + e164, isSwedish, isMobile);\n }\n\n static parse(\n input: string,\n defaultCallingCode?: string | PhoneCallingCode | Country | null,\n ): PhoneNumber {\n const r = PhoneNumber.tryParse(input, defaultCallingCode);\n if (r === null) throw new Error(\"Invalid phone number.\");\n return r;\n }\n\n static isValid(\n input: string | null | undefined,\n defaultCallingCode?: string | PhoneCallingCode | Country | null,\n ): boolean {\n return PhoneNumber.tryParse(input, defaultCallingCode) !== null;\n }\n\n static format(\n input: string | null | undefined,\n options?: {\n defaultCallingCode?: string | PhoneCallingCode | Country | null;\n fallbackToTrimmedInputWhenInvalid?: boolean;\n },\n ): string | null {\n const dcc =\n resolveDefaultCallingCode(options?.defaultCallingCode) ??\n PrimitivesDefaults.defaultCallingCode;\n const r = PhoneNumber.tryParse(input, dcc);\n if (r) {\n return r.countryCallingCode.value === dcc\n ? r.toLocalString()\n : r.toDisplayInternationalString();\n }\n if (options?.fallbackToTrimmedInputWhenInvalid && input != null && input.trim() !== \"\") {\n return input.trim();\n }\n return null;\n }\n\n static normalize(\n input: string | null | undefined,\n options?: {\n defaultCallingCode?: string | PhoneCallingCode | Country | null;\n fallbackToTrimmedInputWhenInvalid?: boolean;\n },\n ): string | null {\n const r = PhoneNumber.tryParse(input, options?.defaultCallingCode);\n if (r) return r.digits;\n if (options?.fallbackToTrimmedInputWhenInvalid && input != null) {\n const t = input.trim();\n return t.length > 0 ? t : null;\n }\n return null;\n }\n\n static isNormalized(input: string | null | undefined): boolean {\n return input != null && PhoneNumber.normalize(input) === input;\n }\n\n static findCandidatesInText(text: string): readonly TextCandidate<PhoneNumber>[] {\n if (!text) return [];\n const out: TextCandidate<PhoneNumber>[] = [];\n SCAN.lastIndex = 0;\n let m: RegExpExecArray | null;\n while ((m = SCAN.exec(text)) !== null) {\n const phone = PhoneNumber.tryParse(m[0]);\n if (!phone) continue;\n out.push(\n new TextCandidate<PhoneNumber>({\n startIndex: m.index,\n length: m[0].length,\n originalText: m[0],\n typeName: \"PhoneNumber\",\n category: TextCandidateCategory.Contact,\n normalizedForm: phone.toNormalizedString(),\n formattedForm: phone.toString(),\n maskedForm: maskPhoneNumber(phone),\n confidence: TextMatchConfidence.Medium,\n value: phone,\n }),\n );\n }\n return out;\n }\n\n toNormalizedString(): string {\n return this.digits;\n }\n\n toLocalString(defaultCallingCode?: PhoneCallingCode | Country): string {\n if (defaultCallingCode) {\n const dcc =\n defaultCallingCode instanceof PhoneCallingCode\n ? defaultCallingCode.value\n : defaultCallingCode.callingCode;\n return this.countryCallingCode.value === dcc\n ? this.toLocalString()\n : this.toDisplayInternationalString();\n }\n const subscriber = this.digits.slice(2 + this.countryCallingCode.value.length);\n if (this.isSwedish) return formatSwedishLocal(subscriber);\n return \"0\" + subscriber;\n }\n\n toE164String(): string {\n return \"+\" + this.digits.slice(2);\n }\n toInternationalString(): string {\n return this.toE164String();\n }\n\n toDisplayInternationalString(): string {\n const subscriber = this.digits.slice(2 + this.countryCallingCode.value.length);\n if (this.isSwedish) return formatSwedishInternational(subscriber);\n return (\n \"+\" +\n this.countryCallingCode.value +\n \" \" +\n formatInternationalSubscriberDigits(this.countryCallingCode.value, subscriber)\n );\n }\n\n toString(): string {\n return this.formatted;\n }\n\n equals(other: PhoneNumber | null | undefined): boolean {\n return other != null && this.digits === other.digits;\n }\n compareTo(other: PhoneNumber | null | undefined): number {\n if (other == null) return 1;\n return this.digits < other.digits ? -1 : this.digits > other.digits ? 1 : 0;\n }\n}\n\n/** Mask a phone number, keeping country code and last 4 digits visible. */\nexport function maskPhoneNumber(p: PhoneNumber): string {\n const f = p.toE164String();\n if (f.length <= 5) return f.replace(/\\d/g, \"*\");\n const cc = \"+\" + p.countryCallingCode.value;\n const subscriber = f.slice(cc.length);\n if (subscriber.length <= 4) return cc + \"*\".repeat(subscriber.length);\n return cc + \"*\".repeat(subscriber.length - 4) + subscriber.slice(-4);\n}\n\nfunction resolveDefaultCallingCode(\n d: string | PhoneCallingCode | Country | null | undefined,\n): string | null {\n if (d == null) return PrimitivesDefaults.defaultCallingCode;\n if (typeof d === \"string\") return d;\n if (d instanceof PhoneCallingCode) return d.value;\n return d.callingCode;\n}\n\nfunction resolveCallingCode(digits: string): string {\n return resolveCallingCodeFromE164(digits.slice(2));\n}\n\nfunction resolveCallingCodeFromE164(e164: string): string {\n if (e164.length === 0) return \"\";\n if (e164[0] === \"1\" || e164[0] === \"7\") return e164.slice(0, 1);\n if (e164.length >= 2 && TWO_DIGIT_CODES.has(e164.slice(0, 2))) return e164.slice(0, 2);\n return e164.length >= 3 ? e164.slice(0, 3) : e164;\n}\n\nfunction stripTrunkZeroAfterCallingCode(e164: string): string {\n const cc = resolveCallingCodeFromE164(e164);\n if (cc.length === 0 || cc.length >= e164.length) return e164;\n const subscriber = e164.slice(cc.length);\n return cc + stripSingleTrunkZero(cc, subscriber);\n}\n\nfunction stripSingleTrunkZero(callingCode: string, subscriber: string): string {\n if (subscriber.length === 0 || subscriber[0] !== \"0\") return subscriber;\n if (LEADING_ZERO_SIGNIFICANT.has(callingCode)) return subscriber;\n return subscriber.slice(1);\n}\n\nfunction isInPtsRange(local: string, prefix: string, from: number, to: number): boolean {\n if (!local.startsWith(prefix)) return false;\n const suffix = local.slice(prefix.length);\n if (suffix.length !== 2) return false;\n const n = parseInt(suffix, 10);\n return Number.isFinite(n) && n >= from && n <= to;\n}\n\nfunction formatInternationalSubscriberDigits(callingCode: string, digits: string): string {\n if (callingCode === \"1\" && digits.length === 10) {\n return `${digits.slice(0, 3)} ${digits.slice(3, 6)} ${digits.slice(6, 10)}`;\n }\n switch (digits.length) {\n case 7:\n return `${digits.slice(0, 3)} ${digits.slice(3, 5)} ${digits.slice(5, 7)}`;\n case 8:\n return `${digits.slice(0, 2)} ${digits.slice(2, 4)} ${digits.slice(4, 6)} ${digits.slice(6, 8)}`;\n case 9:\n return `${digits.slice(0, 2)} ${digits.slice(2, 5)} ${digits.slice(5, 7)} ${digits.slice(7, 9)}`;\n case 10:\n return `${digits.slice(0, 2)} ${digits.slice(2, 6)} ${digits.slice(6, 10)}`;\n case 11:\n return `${digits.slice(0, 3)} ${digits.slice(3, 7)} ${digits.slice(7, 11)}`;\n default:\n return digits;\n }\n}\n\n// Two-digit national destination codes (NDCs) from PTS's numbering plan. 8 (Stockholm) is the\n// only one-digit NDC; every NDC not listed here is three digits.\nconst SWEDISH_TWO_DIGIT_DESTINATION_CODES = new Set([\n ...\"11 13 16 18 19 21 23 26 31 33 35 36 40 42 44 46 54 60 63 90\".split(\" \"),\n ...\"10 20 70 71 72 73 74 75 76 77 79 99\".split(\" \"),\n]);\n\n// 900 (premium rate) is the only three-digit NDC that starts with a two-digit NDC (90, Ume\u00E5).\nconst SWEDISH_THREE_DIGIT_DESTINATION_CODES_OVERRIDING_TWO_DIGIT = new Set([\"900\"]);\n\nfunction swedishDestinationCodeLength(nationalSignificantNumber: string): number {\n if (nationalSignificantNumber.startsWith(\"8\")) return 1;\n if (\n SWEDISH_THREE_DIGIT_DESTINATION_CODES_OVERRIDING_TWO_DIGIT.has(\n nationalSignificantNumber.slice(0, 3),\n )\n ) {\n return 3;\n }\n if (SWEDISH_TWO_DIGIT_DESTINATION_CODES.has(nationalSignificantNumber.slice(0, 2))) return 2;\n return 3;\n}\n\n/**\n * Groups a Swedish subscriber number as recommended by Svenska skrivregler:\n * `NN NN`, `NNN NN`, `NN NN NN`, `NNN NN NN` and `NNN NNN NN`.\n */\nfunction groupSwedishSubscriberNumber(subscriber: string): string {\n if (subscriber.length === 8) {\n return `${subscriber.slice(0, 3)} ${subscriber.slice(3, 6)} ${subscriber.slice(6)}`;\n }\n const firstGroupLength = subscriber.length % 2 === 1 ? 3 : 2;\n const groups = [subscriber.slice(0, firstGroupLength)];\n for (let i = firstGroupLength; i < subscriber.length; i += 2) {\n groups.push(subscriber.slice(i, i + 2));\n }\n return groups.join(\" \");\n}\n\nfunction splitSwedishNationalNumber(nationalSignificantNumber: string): [string, string] {\n const length = swedishDestinationCodeLength(nationalSignificantNumber);\n return [nationalSignificantNumber.slice(0, length), nationalSignificantNumber.slice(length)];\n}\n\nfunction formatSwedishLocal(nationalSignificantNumber: string): string {\n const [destinationCode, subscriber] = splitSwedishNationalNumber(nationalSignificantNumber);\n return `0${destinationCode}-${groupSwedishSubscriberNumber(subscriber)}`;\n}\n\nfunction formatSwedishInternational(nationalSignificantNumber: string): string {\n const [destinationCode, subscriber] = splitSwedishNationalNumber(nationalSignificantNumber);\n return `+46 ${destinationCode} ${groupSwedishSubscriberNumber(subscriber)}`;\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var scan_boundaries_exports = {};
20
+ __export(scan_boundaries_exports, {
21
+ WORD_BOUNDARY: () => WORD_BOUNDARY
22
+ });
23
+ module.exports = __toCommonJS(scan_boundaries_exports);
24
+ const WORD = String.raw`[\p{L}\p{N}_]`;
25
+ const WORD_BOUNDARY = String.raw`(?:(?<=${WORD})(?!${WORD})|(?<!${WORD})(?=${WORD}))`;
26
+ //# sourceMappingURL=scan-boundaries.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/internal/scan-boundaries.ts"],
4
+ "sourcesContent": ["/**\n * The Unicode-aware equivalent of a trailing `\\b` for the scanner patterns.\n *\n * JavaScript's `\\b` and `\\w` are ASCII-only even under `/u`, so `24 m` inside\n * `24 m\u00E5nader` looks like a complete match. .NET's are Unicode-aware, and .NET\n * is the behavioural spec.\n *\n * Where a unit ends in a letter or digit \u2014 which is all of them but one \u2014\n * `(?![\\p{L}\\p{N}_])` says the same thing, and the patterns use that directly.\n * This constant exists for the sound level `dB(A)` form, which ends in `)`. A\n * word boundary is a *transition*, not a plain lookahead: after a non-word\n * character it demands that the next character *be* a word character. That is\n * why .NET reads `69 dB(A) vid drift` as `69 dB` rather than `69 dB(A)`, and\n * the vectors generated from it expect exactly that.\n *\n * Internal; not re-exported publicly.\n */\n\nconst WORD = String.raw`[\\p{L}\\p{N}_]`;\n\nexport const WORD_BOUNDARY = String.raw`(?:(?<=${WORD})(?!${WORD})|(?<!${WORD})(?=${WORD}))`;\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAkBA,MAAM,OAAO,OAAO;AAEb,MAAM,gBAAgB,OAAO,aAAa,IAAI,OAAO,IAAI,SAAS,IAAI,OAAO,IAAI;",
6
+ "names": []
7
+ }
@@ -28,8 +28,9 @@ var import_text_candidate = require("../text-scanning/text-candidate.js");
28
28
  var import_text_candidate_category = require("../text-scanning/text-candidate-category.js");
29
29
  var import_text_match_confidence = require("../text-scanning/text-match-confidence.js");
30
30
  var import_area_unit = require("./area-unit.js");
31
+ const B = String.raw`(?![\p{L}\p{N}_])`;
31
32
  const SCAN_PATTERN = new RegExp(
32
- String.raw`(?<!\w)(?:[+-]\s*)?\d+[0-9 .,]*\s*(?:(?:mm|cm|km|m)(?:²|2)|ha\b|hectare\b|hectares\b|hektar\b|acre\b|acres\b|sq\s*ft\b|sq\s*in\b|sq\s*yd\b|kvadrat(?:millimeter|centimeter|meter|kilometer|fot|tum|yard)\b)`,
33
+ String.raw`(?<![\p{L}\p{N}_])(?:[+-]\s*)?\d+[0-9 .,]*\s*(?:(?:mm|cm|km|m)(?:²|2)|ha${B}|hectare${B}|hectares${B}|hektar${B}|acre${B}|acres${B}|sq\s*ft${B}|sq\s*in${B}|sq\s*yd${B}|kvadrat(?:millimeter|centimeter|meter|kilometer|fot|tum|yard)${B})`,
33
34
  "giu"
34
35
  );
35
36
  class Area extends import_quantity.Quantity {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/measurement/area.ts"],
4
- "sourcesContent": ["import { Decimal } from \"../internal/decimal.js\";\nimport type { MeasurementUnitIndex } from \"../internal/measurement-unit.js\";\nimport { Quantity, tryParseQuantity } from \"../internal/quantity.js\";\nimport type { PrimitiveTypeInfo } from \"../primitive-type-info.js\";\nimport { TextCandidate } from \"../text-scanning/text-candidate.js\";\nimport { TextCandidateCategory } from \"../text-scanning/text-candidate-category.js\";\nimport { TextMatchConfidence } from \"../text-scanning/text-match-confidence.js\";\nimport { AreaUnit } from \"./area-unit.js\";\n\n/** Options for {@link Area.format} and {@link Area.normalize}. */\nexport interface AreaFormatOptions {\n fallbackToTrimmedInputWhenInvalid?: boolean;\n /** Convert to this unit instead of preserving the parsed one. */\n unit?: AreaUnit;\n /** Round to this many fractional digits, away from zero. */\n decimals?: number;\n}\n\nconst SCAN_PATTERN = new RegExp(\n String.raw`(?<!\\w)(?:[+-]\\s*)?\\d+[0-9 .,]*\\s*(?:(?:mm|cm|km|m)(?:\u00B2|2)|ha\\b|hectare\\b|hectares\\b|hektar\\b|acre\\b|acres\\b|sq\\s*ft\\b|sq\\s*in\\b|sq\\s*yd\\b|kvadrat(?:millimeter|centimeter|meter|kilometer|fot|tum|yard)\\b)`,\n \"giu\",\n);\n\n/**\n * A area stored internally in square meters,\n * parsed from any supported unit and convertible between all of them.\n */\nexport class Area extends Quantity<AreaUnit> {\n static readonly typeInfo: PrimitiveTypeInfo = Object.freeze({\n englishName: \"Area\",\n localizedName: \"Yta\",\n emoji: \"\uD83D\uDCD0\",\n sources: Object.freeze([\n \"https://www.bipm.org/en/measurement-units/si-base-units\",\n \"https://en.wikipedia.org/wiki/Conversion_of_units\",\n ]),\n });\n\n protected get units(): MeasurementUnitIndex<AreaUnit> {\n return AreaUnit.index;\n }\n\n protected withBaseValue(baseValue: Decimal, unit: AreaUnit): this {\n return new Area(baseValue, unit) as this;\n }\n\n get squareMillimeters(): number {\n return this.in(AreaUnit.SquareMillimeter);\n }\n get squareCentimeters(): number {\n return this.in(AreaUnit.SquareCentimeter);\n }\n get squareMeters(): number {\n return this.in(AreaUnit.SquareMeter);\n }\n get squareKilometers(): number {\n return this.in(AreaUnit.SquareKilometer);\n }\n get hectares(): number {\n return this.in(AreaUnit.Hectare);\n }\n get acres(): number {\n return this.in(AreaUnit.Acre);\n }\n get squareFeet(): number {\n return this.in(AreaUnit.SquareFoot);\n }\n get squareInches(): number {\n return this.in(AreaUnit.SquareInch);\n }\n get squareYards(): number {\n return this.in(AreaUnit.SquareYard);\n }\n\n /** Creates a area from a value and unit. */\n static create(value: number | Decimal, unit: AreaUnit): Area {\n AreaUnit.index.assertOwns(unit);\n const amount = value instanceof Decimal ? value : Area.decimalOf(value);\n return new Area(unit.toBase(amount), unit);\n }\n\n static fromSquareMillimeters(value: number | Decimal): Area {\n return Area.create(value, AreaUnit.SquareMillimeter);\n }\n static fromSquareCentimeters(value: number | Decimal): Area {\n return Area.create(value, AreaUnit.SquareCentimeter);\n }\n static fromSquareMeters(value: number | Decimal): Area {\n return Area.create(value, AreaUnit.SquareMeter);\n }\n static fromSquareKilometers(value: number | Decimal): Area {\n return Area.create(value, AreaUnit.SquareKilometer);\n }\n static fromHectares(value: number | Decimal): Area {\n return Area.create(value, AreaUnit.Hectare);\n }\n static fromAcres(value: number | Decimal): Area {\n return Area.create(value, AreaUnit.Acre);\n }\n static fromSquareFeet(value: number | Decimal): Area {\n return Area.create(value, AreaUnit.SquareFoot);\n }\n static fromSquareInches(value: number | Decimal): Area {\n return Area.create(value, AreaUnit.SquareInch);\n }\n static fromSquareYards(value: number | Decimal): Area {\n return Area.create(value, AreaUnit.SquareYard);\n }\n\n static tryParse(input: string | null | undefined): Area | null {\n const parsed = tryParseQuantity(input, AreaUnit.index);\n return parsed === null ? null : new Area(parsed.baseValue, parsed.unit);\n }\n\n static parse(input: string): Area {\n const result = Area.tryParse(input);\n if (result === null) throw new Error(\"Invalid area.\");\n return result;\n }\n\n static isValid(input: string | null | undefined): boolean {\n return Area.tryParse(input) !== null;\n }\n\n /** Display form in the parsed unit, or `null` when the input is invalid. */\n static format(input: string | null | undefined, options?: AreaFormatOptions): string | null {\n const parsed = Area.tryParse(input);\n if (parsed !== null) {\n if (options?.unit !== undefined || options?.decimals !== undefined) {\n return parsed.toStringInUnit(options.unit ?? parsed.originalUnit, options.decimals);\n }\n return parsed.toString();\n }\n return Area.fallback(input, options);\n }\n\n /** The value in square meters, or `null` when the input is invalid. */\n static normalize(input: string | null | undefined, options?: AreaFormatOptions): string | null {\n const parsed = Area.tryParse(input);\n if (parsed !== null) return parsed.toNormalizedString();\n return Area.fallback(input, options);\n }\n\n static isNormalized(input: string | null | undefined): boolean {\n return input != null && Area.normalize(input) === input;\n }\n\n static findCandidatesInText(text: string): readonly TextCandidate<Area>[] {\n if (!text) return [];\n\n const out: TextCandidate<Area>[] = [];\n const pattern = new RegExp(SCAN_PATTERN.source, SCAN_PATTERN.flags);\n let match: RegExpExecArray | null;\n\n while ((match = pattern.exec(text)) !== null) {\n const value = Area.tryParse(match[0]);\n if (value === null) continue;\n out.push(\n new TextCandidate<Area>({\n startIndex: match.index,\n length: match[0].length,\n originalText: match[0],\n typeName: \"Area\",\n category: TextCandidateCategory.Measurement,\n normalizedForm: value.toNormalizedString(),\n formattedForm: value.toString(),\n maskedForm: maskArea(value),\n confidence: TextMatchConfidence.Medium,\n value,\n }),\n );\n }\n\n return out;\n }\n\n private static fallback(\n input: string | null | undefined,\n options?: AreaFormatOptions,\n ): string | null {\n if (options?.fallbackToTrimmedInputWhenInvalid !== true) return null;\n if (input == null) return null;\n const trimmed = input.trim();\n return trimmed.length > 0 ? trimmed : null;\n }\n\n private static decimalOf(value: number): Decimal {\n const parsed = Decimal.fromNumber(value);\n if (parsed === null) throw new Error(`Value cannot be represented as a area: ${value}`);\n return parsed;\n }\n}\n\n/** Returns a masked area, e.g. `10 m\u00B2` becomes `*** m\u00B2`. */\nexport function maskArea(value: Area): string {\n return `*** ${value.originalUnit.symbol}`;\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { Decimal } from \"../internal/decimal.js\";\nimport type { MeasurementUnitIndex } from \"../internal/measurement-unit.js\";\nimport { Quantity, tryParseQuantity } from \"../internal/quantity.js\";\nimport type { PrimitiveTypeInfo } from \"../primitive-type-info.js\";\nimport { TextCandidate } from \"../text-scanning/text-candidate.js\";\nimport { TextCandidateCategory } from \"../text-scanning/text-candidate-category.js\";\nimport { TextMatchConfidence } from \"../text-scanning/text-match-confidence.js\";\nimport { AreaUnit } from \"./area-unit.js\";\n\n/** Options for {@link Area.format} and {@link Area.normalize}. */\nexport interface AreaFormatOptions {\n fallbackToTrimmedInputWhenInvalid?: boolean;\n /** Convert to this unit instead of preserving the parsed one. */\n unit?: AreaUnit;\n /** Round to this many fractional digits, away from zero. */\n decimals?: number;\n}\n\n// Only the spelled-out units take a boundary; the `m\u00B2` and `m2` forms end in a\n// character that already terminates the match, as in .NET.\nconst B = String.raw`(?![\\p{L}\\p{N}_])`;\n\nconst SCAN_PATTERN = new RegExp(\n String.raw`(?<![\\p{L}\\p{N}_])(?:[+-]\\s*)?\\d+[0-9 .,]*\\s*(?:(?:mm|cm|km|m)(?:\u00B2|2)|ha${B}|hectare${B}|hectares${B}|hektar${B}|acre${B}|acres${B}|sq\\s*ft${B}|sq\\s*in${B}|sq\\s*yd${B}|kvadrat(?:millimeter|centimeter|meter|kilometer|fot|tum|yard)${B})`,\n \"giu\",\n);\n\n/**\n * A area stored internally in square meters,\n * parsed from any supported unit and convertible between all of them.\n */\nexport class Area extends Quantity<AreaUnit> {\n static readonly typeInfo: PrimitiveTypeInfo = Object.freeze({\n englishName: \"Area\",\n localizedName: \"Yta\",\n emoji: \"\uD83D\uDCD0\",\n sources: Object.freeze([\n \"https://www.bipm.org/en/measurement-units/si-base-units\",\n \"https://en.wikipedia.org/wiki/Conversion_of_units\",\n ]),\n });\n\n protected get units(): MeasurementUnitIndex<AreaUnit> {\n return AreaUnit.index;\n }\n\n protected withBaseValue(baseValue: Decimal, unit: AreaUnit): this {\n return new Area(baseValue, unit) as this;\n }\n\n get squareMillimeters(): number {\n return this.in(AreaUnit.SquareMillimeter);\n }\n get squareCentimeters(): number {\n return this.in(AreaUnit.SquareCentimeter);\n }\n get squareMeters(): number {\n return this.in(AreaUnit.SquareMeter);\n }\n get squareKilometers(): number {\n return this.in(AreaUnit.SquareKilometer);\n }\n get hectares(): number {\n return this.in(AreaUnit.Hectare);\n }\n get acres(): number {\n return this.in(AreaUnit.Acre);\n }\n get squareFeet(): number {\n return this.in(AreaUnit.SquareFoot);\n }\n get squareInches(): number {\n return this.in(AreaUnit.SquareInch);\n }\n get squareYards(): number {\n return this.in(AreaUnit.SquareYard);\n }\n\n /** Creates a area from a value and unit. */\n static create(value: number | Decimal, unit: AreaUnit): Area {\n AreaUnit.index.assertOwns(unit);\n const amount = value instanceof Decimal ? value : Area.decimalOf(value);\n return new Area(unit.toBase(amount), unit);\n }\n\n static fromSquareMillimeters(value: number | Decimal): Area {\n return Area.create(value, AreaUnit.SquareMillimeter);\n }\n static fromSquareCentimeters(value: number | Decimal): Area {\n return Area.create(value, AreaUnit.SquareCentimeter);\n }\n static fromSquareMeters(value: number | Decimal): Area {\n return Area.create(value, AreaUnit.SquareMeter);\n }\n static fromSquareKilometers(value: number | Decimal): Area {\n return Area.create(value, AreaUnit.SquareKilometer);\n }\n static fromHectares(value: number | Decimal): Area {\n return Area.create(value, AreaUnit.Hectare);\n }\n static fromAcres(value: number | Decimal): Area {\n return Area.create(value, AreaUnit.Acre);\n }\n static fromSquareFeet(value: number | Decimal): Area {\n return Area.create(value, AreaUnit.SquareFoot);\n }\n static fromSquareInches(value: number | Decimal): Area {\n return Area.create(value, AreaUnit.SquareInch);\n }\n static fromSquareYards(value: number | Decimal): Area {\n return Area.create(value, AreaUnit.SquareYard);\n }\n\n static tryParse(input: string | null | undefined): Area | null {\n const parsed = tryParseQuantity(input, AreaUnit.index);\n return parsed === null ? null : new Area(parsed.baseValue, parsed.unit);\n }\n\n static parse(input: string): Area {\n const result = Area.tryParse(input);\n if (result === null) throw new Error(\"Invalid area.\");\n return result;\n }\n\n static isValid(input: string | null | undefined): boolean {\n return Area.tryParse(input) !== null;\n }\n\n /** Display form in the parsed unit, or `null` when the input is invalid. */\n static format(input: string | null | undefined, options?: AreaFormatOptions): string | null {\n const parsed = Area.tryParse(input);\n if (parsed !== null) {\n if (options?.unit !== undefined || options?.decimals !== undefined) {\n return parsed.toStringInUnit(options.unit ?? parsed.originalUnit, options.decimals);\n }\n return parsed.toString();\n }\n return Area.fallback(input, options);\n }\n\n /** The value in square meters, or `null` when the input is invalid. */\n static normalize(input: string | null | undefined, options?: AreaFormatOptions): string | null {\n const parsed = Area.tryParse(input);\n if (parsed !== null) return parsed.toNormalizedString();\n return Area.fallback(input, options);\n }\n\n static isNormalized(input: string | null | undefined): boolean {\n return input != null && Area.normalize(input) === input;\n }\n\n static findCandidatesInText(text: string): readonly TextCandidate<Area>[] {\n if (!text) return [];\n\n const out: TextCandidate<Area>[] = [];\n const pattern = new RegExp(SCAN_PATTERN.source, SCAN_PATTERN.flags);\n let match: RegExpExecArray | null;\n\n while ((match = pattern.exec(text)) !== null) {\n const value = Area.tryParse(match[0]);\n if (value === null) continue;\n out.push(\n new TextCandidate<Area>({\n startIndex: match.index,\n length: match[0].length,\n originalText: match[0],\n typeName: \"Area\",\n category: TextCandidateCategory.Measurement,\n normalizedForm: value.toNormalizedString(),\n formattedForm: value.toString(),\n maskedForm: maskArea(value),\n confidence: TextMatchConfidence.Medium,\n value,\n }),\n );\n }\n\n return out;\n }\n\n private static fallback(\n input: string | null | undefined,\n options?: AreaFormatOptions,\n ): string | null {\n if (options?.fallbackToTrimmedInputWhenInvalid !== true) return null;\n if (input == null) return null;\n const trimmed = input.trim();\n return trimmed.length > 0 ? trimmed : null;\n }\n\n private static decimalOf(value: number): Decimal {\n const parsed = Decimal.fromNumber(value);\n if (parsed === null) throw new Error(`Value cannot be represented as a area: ${value}`);\n return parsed;\n }\n}\n\n/** Returns a masked area, e.g. `10 m\u00B2` becomes `*** m\u00B2`. */\nexport function maskArea(value: Area): string {\n return `*** ${value.originalUnit.symbol}`;\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -29,7 +29,7 @@ var import_text_candidate_category = require("../text-scanning/text-candidate-ca
29
29
  var import_text_match_confidence = require("../text-scanning/text-match-confidence.js");
30
30
  var import_data_size_unit = require("./data-size-unit.js");
31
31
  const SCAN_PATTERN = new RegExp(
32
- String.raw`(?<!\w)(?:[+-]\s*)?\d+[0-9 .,]*\s*(?:exbibytes|exbibyte|exabytes|exabyte|pebibytes|pebibyte|petabytes|petabyte|tebibytes|tebibyte|terabytes|terabyte|gibibytes|gibibyte|gigabytes|gigabyte|mebibytes|mebibyte|megabytes|megabyte|kibibytes|kibibyte|kilobytes|kilobyte|EiB|PiB|TiB|GiB|MiB|KiB|EB|PB|TB|GB|MB|KB|bytes|byte|B)\b`,
32
+ String.raw`(?<![\p{L}\p{N}_])(?:[+-]\s*)?\d+[0-9 .,]*\s*(?:exbibytes|exbibyte|exabytes|exabyte|pebibytes|pebibyte|petabytes|petabyte|tebibytes|tebibyte|terabytes|terabyte|gibibytes|gibibyte|gigabytes|gigabyte|mebibytes|mebibyte|megabytes|megabyte|kibibytes|kibibyte|kilobytes|kilobyte|EiB|PiB|TiB|GiB|MiB|KiB|EB|PB|TB|GB|MB|KB|bytes|byte|B)(?![\p{L}\p{N}_])`,
33
33
  "giu"
34
34
  );
35
35
  class DataSize extends import_quantity.Quantity {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/measurement/data-size.ts"],
4
- "sourcesContent": ["import { Decimal } from \"../internal/decimal.js\";\nimport type { MeasurementUnitIndex } from \"../internal/measurement-unit.js\";\nimport { Quantity, tryParseQuantity } from \"../internal/quantity.js\";\nimport type { PrimitiveTypeInfo } from \"../primitive-type-info.js\";\nimport { TextCandidate } from \"../text-scanning/text-candidate.js\";\nimport { TextCandidateCategory } from \"../text-scanning/text-candidate-category.js\";\nimport { TextMatchConfidence } from \"../text-scanning/text-match-confidence.js\";\nimport { DataSizeUnit } from \"./data-size-unit.js\";\n\n/** Options for {@link DataSize.format} and {@link DataSize.normalize}. */\nexport interface DataSizeFormatOptions {\n fallbackToTrimmedInputWhenInvalid?: boolean;\n /** Convert to this unit instead of preserving the parsed one. */\n unit?: DataSizeUnit;\n /** Round to this many fractional digits, away from zero. */\n decimals?: number;\n}\n\nconst SCAN_PATTERN = new RegExp(\n String.raw`(?<!\\w)(?:[+-]\\s*)?\\d+[0-9 .,]*\\s*(?:exbibytes|exbibyte|exabytes|exabyte|pebibytes|pebibyte|petabytes|petabyte|tebibytes|tebibyte|terabytes|terabyte|gibibytes|gibibyte|gigabytes|gigabyte|mebibytes|mebibyte|megabytes|megabyte|kibibytes|kibibyte|kilobytes|kilobyte|EiB|PiB|TiB|GiB|MiB|KiB|EB|PB|TB|GB|MB|KB|bytes|byte|B)\\b`,\n \"giu\",\n);\n\n/**\n * A data size stored internally in bytes,\n * parsed from any supported unit and convertible between all of them.\n */\nexport class DataSize extends Quantity<DataSizeUnit> {\n static readonly typeInfo: PrimitiveTypeInfo = Object.freeze({\n englishName: \"Data Size\",\n localizedName: \"Datastorlek\",\n emoji: \"\uD83D\uDCBE\",\n sources: Object.freeze([\n \"https://www.bipm.org/en/publications/si-brochure\",\n \"https://www.iec.ch/standardsonline/iec-standard/iec-60027-2\",\n ]),\n });\n\n protected get units(): MeasurementUnitIndex<DataSizeUnit> {\n return DataSizeUnit.index;\n }\n\n protected withBaseValue(baseValue: Decimal, unit: DataSizeUnit): this {\n return new DataSize(baseValue, unit) as this;\n }\n\n get bytes(): number {\n return this.in(DataSizeUnit.Byte);\n }\n get kilobytes(): number {\n return this.in(DataSizeUnit.Kilobyte);\n }\n get megabytes(): number {\n return this.in(DataSizeUnit.Megabyte);\n }\n get gigabytes(): number {\n return this.in(DataSizeUnit.Gigabyte);\n }\n get terabytes(): number {\n return this.in(DataSizeUnit.Terabyte);\n }\n get petabytes(): number {\n return this.in(DataSizeUnit.Petabyte);\n }\n get exabytes(): number {\n return this.in(DataSizeUnit.Exabyte);\n }\n get kibibytes(): number {\n return this.in(DataSizeUnit.Kibibyte);\n }\n get mebibytes(): number {\n return this.in(DataSizeUnit.Mebibyte);\n }\n get gibibytes(): number {\n return this.in(DataSizeUnit.Gibibyte);\n }\n get tebibytes(): number {\n return this.in(DataSizeUnit.Tebibyte);\n }\n get pebibytes(): number {\n return this.in(DataSizeUnit.Pebibyte);\n }\n get exbibytes(): number {\n return this.in(DataSizeUnit.Exbibyte);\n }\n\n /** Creates a data size from a value and unit. */\n static create(value: number | Decimal, unit: DataSizeUnit): DataSize {\n DataSizeUnit.index.assertOwns(unit);\n const amount = value instanceof Decimal ? value : DataSize.decimalOf(value);\n return new DataSize(unit.toBase(amount), unit);\n }\n\n static fromBytes(value: number | Decimal): DataSize {\n return DataSize.create(value, DataSizeUnit.Byte);\n }\n static fromKilobytes(value: number | Decimal): DataSize {\n return DataSize.create(value, DataSizeUnit.Kilobyte);\n }\n static fromMegabytes(value: number | Decimal): DataSize {\n return DataSize.create(value, DataSizeUnit.Megabyte);\n }\n static fromGigabytes(value: number | Decimal): DataSize {\n return DataSize.create(value, DataSizeUnit.Gigabyte);\n }\n static fromTerabytes(value: number | Decimal): DataSize {\n return DataSize.create(value, DataSizeUnit.Terabyte);\n }\n static fromPetabytes(value: number | Decimal): DataSize {\n return DataSize.create(value, DataSizeUnit.Petabyte);\n }\n static fromExabytes(value: number | Decimal): DataSize {\n return DataSize.create(value, DataSizeUnit.Exabyte);\n }\n static fromKibibytes(value: number | Decimal): DataSize {\n return DataSize.create(value, DataSizeUnit.Kibibyte);\n }\n static fromMebibytes(value: number | Decimal): DataSize {\n return DataSize.create(value, DataSizeUnit.Mebibyte);\n }\n static fromGibibytes(value: number | Decimal): DataSize {\n return DataSize.create(value, DataSizeUnit.Gibibyte);\n }\n static fromTebibytes(value: number | Decimal): DataSize {\n return DataSize.create(value, DataSizeUnit.Tebibyte);\n }\n static fromPebibytes(value: number | Decimal): DataSize {\n return DataSize.create(value, DataSizeUnit.Pebibyte);\n }\n static fromExbibytes(value: number | Decimal): DataSize {\n return DataSize.create(value, DataSizeUnit.Exbibyte);\n }\n\n static tryParse(input: string | null | undefined): DataSize | null {\n const parsed = tryParseQuantity(input, DataSizeUnit.index);\n return parsed === null ? null : new DataSize(parsed.baseValue, parsed.unit);\n }\n\n static parse(input: string): DataSize {\n const result = DataSize.tryParse(input);\n if (result === null) throw new Error(\"Invalid data size.\");\n return result;\n }\n\n static isValid(input: string | null | undefined): boolean {\n return DataSize.tryParse(input) !== null;\n }\n\n /** Display form in the parsed unit, or `null` when the input is invalid. */\n static format(input: string | null | undefined, options?: DataSizeFormatOptions): string | null {\n const parsed = DataSize.tryParse(input);\n if (parsed !== null) {\n if (options?.unit !== undefined || options?.decimals !== undefined) {\n return parsed.toStringInUnit(options.unit ?? parsed.originalUnit, options.decimals);\n }\n return parsed.toString();\n }\n return DataSize.fallback(input, options);\n }\n\n /** The value in bytes, or `null` when the input is invalid. */\n static normalize(\n input: string | null | undefined,\n options?: DataSizeFormatOptions,\n ): string | null {\n const parsed = DataSize.tryParse(input);\n if (parsed !== null) return parsed.toNormalizedString();\n return DataSize.fallback(input, options);\n }\n\n static isNormalized(input: string | null | undefined): boolean {\n return input != null && DataSize.normalize(input) === input;\n }\n\n static findCandidatesInText(text: string): readonly TextCandidate<DataSize>[] {\n if (!text) return [];\n\n const out: TextCandidate<DataSize>[] = [];\n const pattern = new RegExp(SCAN_PATTERN.source, SCAN_PATTERN.flags);\n let match: RegExpExecArray | null;\n\n while ((match = pattern.exec(text)) !== null) {\n const value = DataSize.tryParse(match[0]);\n if (value === null) continue;\n out.push(\n new TextCandidate<DataSize>({\n startIndex: match.index,\n length: match[0].length,\n originalText: match[0],\n typeName: \"DataSize\",\n category: TextCandidateCategory.Measurement,\n normalizedForm: value.toNormalizedString(),\n formattedForm: value.toString(),\n maskedForm: maskDataSize(value),\n confidence: TextMatchConfidence.Medium,\n value,\n }),\n );\n }\n\n return out;\n }\n\n private static fallback(\n input: string | null | undefined,\n options?: DataSizeFormatOptions,\n ): string | null {\n if (options?.fallbackToTrimmedInputWhenInvalid !== true) return null;\n if (input == null) return null;\n const trimmed = input.trim();\n return trimmed.length > 0 ? trimmed : null;\n }\n\n private static decimalOf(value: number): Decimal {\n const parsed = Decimal.fromNumber(value);\n if (parsed === null) throw new Error(`Value cannot be represented as a data size: ${value}`);\n return parsed;\n }\n}\n\n/** Returns a masked data size, e.g. `10 B` becomes `*** B`. */\nexport function maskDataSize(value: DataSize): string {\n return `*** ${value.originalUnit.symbol}`;\n}\n"],
4
+ "sourcesContent": ["import { Decimal } from \"../internal/decimal.js\";\nimport type { MeasurementUnitIndex } from \"../internal/measurement-unit.js\";\nimport { Quantity, tryParseQuantity } from \"../internal/quantity.js\";\nimport type { PrimitiveTypeInfo } from \"../primitive-type-info.js\";\nimport { TextCandidate } from \"../text-scanning/text-candidate.js\";\nimport { TextCandidateCategory } from \"../text-scanning/text-candidate-category.js\";\nimport { TextMatchConfidence } from \"../text-scanning/text-match-confidence.js\";\nimport { DataSizeUnit } from \"./data-size-unit.js\";\n\n/** Options for {@link DataSize.format} and {@link DataSize.normalize}. */\nexport interface DataSizeFormatOptions {\n fallbackToTrimmedInputWhenInvalid?: boolean;\n /** Convert to this unit instead of preserving the parsed one. */\n unit?: DataSizeUnit;\n /** Round to this many fractional digits, away from zero. */\n decimals?: number;\n}\n\nconst SCAN_PATTERN = new RegExp(\n String.raw`(?<![\\p{L}\\p{N}_])(?:[+-]\\s*)?\\d+[0-9 .,]*\\s*(?:exbibytes|exbibyte|exabytes|exabyte|pebibytes|pebibyte|petabytes|petabyte|tebibytes|tebibyte|terabytes|terabyte|gibibytes|gibibyte|gigabytes|gigabyte|mebibytes|mebibyte|megabytes|megabyte|kibibytes|kibibyte|kilobytes|kilobyte|EiB|PiB|TiB|GiB|MiB|KiB|EB|PB|TB|GB|MB|KB|bytes|byte|B)(?![\\p{L}\\p{N}_])`,\n \"giu\",\n);\n\n/**\n * A data size stored internally in bytes,\n * parsed from any supported unit and convertible between all of them.\n */\nexport class DataSize extends Quantity<DataSizeUnit> {\n static readonly typeInfo: PrimitiveTypeInfo = Object.freeze({\n englishName: \"Data Size\",\n localizedName: \"Datastorlek\",\n emoji: \"\uD83D\uDCBE\",\n sources: Object.freeze([\n \"https://www.bipm.org/en/publications/si-brochure\",\n \"https://www.iec.ch/standardsonline/iec-standard/iec-60027-2\",\n ]),\n });\n\n protected get units(): MeasurementUnitIndex<DataSizeUnit> {\n return DataSizeUnit.index;\n }\n\n protected withBaseValue(baseValue: Decimal, unit: DataSizeUnit): this {\n return new DataSize(baseValue, unit) as this;\n }\n\n get bytes(): number {\n return this.in(DataSizeUnit.Byte);\n }\n get kilobytes(): number {\n return this.in(DataSizeUnit.Kilobyte);\n }\n get megabytes(): number {\n return this.in(DataSizeUnit.Megabyte);\n }\n get gigabytes(): number {\n return this.in(DataSizeUnit.Gigabyte);\n }\n get terabytes(): number {\n return this.in(DataSizeUnit.Terabyte);\n }\n get petabytes(): number {\n return this.in(DataSizeUnit.Petabyte);\n }\n get exabytes(): number {\n return this.in(DataSizeUnit.Exabyte);\n }\n get kibibytes(): number {\n return this.in(DataSizeUnit.Kibibyte);\n }\n get mebibytes(): number {\n return this.in(DataSizeUnit.Mebibyte);\n }\n get gibibytes(): number {\n return this.in(DataSizeUnit.Gibibyte);\n }\n get tebibytes(): number {\n return this.in(DataSizeUnit.Tebibyte);\n }\n get pebibytes(): number {\n return this.in(DataSizeUnit.Pebibyte);\n }\n get exbibytes(): number {\n return this.in(DataSizeUnit.Exbibyte);\n }\n\n /** Creates a data size from a value and unit. */\n static create(value: number | Decimal, unit: DataSizeUnit): DataSize {\n DataSizeUnit.index.assertOwns(unit);\n const amount = value instanceof Decimal ? value : DataSize.decimalOf(value);\n return new DataSize(unit.toBase(amount), unit);\n }\n\n static fromBytes(value: number | Decimal): DataSize {\n return DataSize.create(value, DataSizeUnit.Byte);\n }\n static fromKilobytes(value: number | Decimal): DataSize {\n return DataSize.create(value, DataSizeUnit.Kilobyte);\n }\n static fromMegabytes(value: number | Decimal): DataSize {\n return DataSize.create(value, DataSizeUnit.Megabyte);\n }\n static fromGigabytes(value: number | Decimal): DataSize {\n return DataSize.create(value, DataSizeUnit.Gigabyte);\n }\n static fromTerabytes(value: number | Decimal): DataSize {\n return DataSize.create(value, DataSizeUnit.Terabyte);\n }\n static fromPetabytes(value: number | Decimal): DataSize {\n return DataSize.create(value, DataSizeUnit.Petabyte);\n }\n static fromExabytes(value: number | Decimal): DataSize {\n return DataSize.create(value, DataSizeUnit.Exabyte);\n }\n static fromKibibytes(value: number | Decimal): DataSize {\n return DataSize.create(value, DataSizeUnit.Kibibyte);\n }\n static fromMebibytes(value: number | Decimal): DataSize {\n return DataSize.create(value, DataSizeUnit.Mebibyte);\n }\n static fromGibibytes(value: number | Decimal): DataSize {\n return DataSize.create(value, DataSizeUnit.Gibibyte);\n }\n static fromTebibytes(value: number | Decimal): DataSize {\n return DataSize.create(value, DataSizeUnit.Tebibyte);\n }\n static fromPebibytes(value: number | Decimal): DataSize {\n return DataSize.create(value, DataSizeUnit.Pebibyte);\n }\n static fromExbibytes(value: number | Decimal): DataSize {\n return DataSize.create(value, DataSizeUnit.Exbibyte);\n }\n\n static tryParse(input: string | null | undefined): DataSize | null {\n const parsed = tryParseQuantity(input, DataSizeUnit.index);\n return parsed === null ? null : new DataSize(parsed.baseValue, parsed.unit);\n }\n\n static parse(input: string): DataSize {\n const result = DataSize.tryParse(input);\n if (result === null) throw new Error(\"Invalid data size.\");\n return result;\n }\n\n static isValid(input: string | null | undefined): boolean {\n return DataSize.tryParse(input) !== null;\n }\n\n /** Display form in the parsed unit, or `null` when the input is invalid. */\n static format(input: string | null | undefined, options?: DataSizeFormatOptions): string | null {\n const parsed = DataSize.tryParse(input);\n if (parsed !== null) {\n if (options?.unit !== undefined || options?.decimals !== undefined) {\n return parsed.toStringInUnit(options.unit ?? parsed.originalUnit, options.decimals);\n }\n return parsed.toString();\n }\n return DataSize.fallback(input, options);\n }\n\n /** The value in bytes, or `null` when the input is invalid. */\n static normalize(\n input: string | null | undefined,\n options?: DataSizeFormatOptions,\n ): string | null {\n const parsed = DataSize.tryParse(input);\n if (parsed !== null) return parsed.toNormalizedString();\n return DataSize.fallback(input, options);\n }\n\n static isNormalized(input: string | null | undefined): boolean {\n return input != null && DataSize.normalize(input) === input;\n }\n\n static findCandidatesInText(text: string): readonly TextCandidate<DataSize>[] {\n if (!text) return [];\n\n const out: TextCandidate<DataSize>[] = [];\n const pattern = new RegExp(SCAN_PATTERN.source, SCAN_PATTERN.flags);\n let match: RegExpExecArray | null;\n\n while ((match = pattern.exec(text)) !== null) {\n const value = DataSize.tryParse(match[0]);\n if (value === null) continue;\n out.push(\n new TextCandidate<DataSize>({\n startIndex: match.index,\n length: match[0].length,\n originalText: match[0],\n typeName: \"DataSize\",\n category: TextCandidateCategory.Measurement,\n normalizedForm: value.toNormalizedString(),\n formattedForm: value.toString(),\n maskedForm: maskDataSize(value),\n confidence: TextMatchConfidence.Medium,\n value,\n }),\n );\n }\n\n return out;\n }\n\n private static fallback(\n input: string | null | undefined,\n options?: DataSizeFormatOptions,\n ): string | null {\n if (options?.fallbackToTrimmedInputWhenInvalid !== true) return null;\n if (input == null) return null;\n const trimmed = input.trim();\n return trimmed.length > 0 ? trimmed : null;\n }\n\n private static decimalOf(value: number): Decimal {\n const parsed = Decimal.fromNumber(value);\n if (parsed === null) throw new Error(`Value cannot be represented as a data size: ${value}`);\n return parsed;\n }\n}\n\n/** Returns a masked data size, e.g. `10 B` becomes `*** B`. */\nexport function maskDataSize(value: DataSize): string {\n return `*** ${value.originalUnit.symbol}`;\n}\n"],
5
5
  "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -29,7 +29,7 @@ var import_text_candidate_category = require("../text-scanning/text-candidate-ca
29
29
  var import_text_match_confidence = require("../text-scanning/text-match-confidence.js");
30
30
  var import_electric_charge_unit = require("./electric-charge-unit.js");
31
31
  const SCAN_PATTERN = new RegExp(
32
- String.raw`(?<!\w)(?:[+-]\s*)?\d+[0-9 .,]*\s*(?:mAh|Ah|C)\b`,
32
+ String.raw`(?<![\p{L}\p{N}_])(?:[+-]\s*)?\d+[0-9 .,]*\s*(?:mAh|Ah|C)(?![\p{L}\p{N}_])`,
33
33
  "giu"
34
34
  );
35
35
  class ElectricCharge extends import_quantity.Quantity {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/measurement/electric-charge.ts"],
4
- "sourcesContent": ["import { Decimal } from \"../internal/decimal.js\";\nimport type { MeasurementUnitIndex } from \"../internal/measurement-unit.js\";\nimport { Quantity, tryParseQuantity } from \"../internal/quantity.js\";\nimport type { PrimitiveTypeInfo } from \"../primitive-type-info.js\";\nimport { TextCandidate } from \"../text-scanning/text-candidate.js\";\nimport { TextCandidateCategory } from \"../text-scanning/text-candidate-category.js\";\nimport { TextMatchConfidence } from \"../text-scanning/text-match-confidence.js\";\nimport { ElectricChargeUnit } from \"./electric-charge-unit.js\";\n\n/** Options for {@link ElectricCharge.format} and {@link ElectricCharge.normalize}. */\nexport interface ElectricChargeFormatOptions {\n fallbackToTrimmedInputWhenInvalid?: boolean;\n /** Convert to this unit instead of preserving the parsed one. */\n unit?: ElectricChargeUnit;\n /** Round to this many fractional digits, away from zero. */\n decimals?: number;\n}\n\nconst SCAN_PATTERN = new RegExp(\n String.raw`(?<!\\w)(?:[+-]\\s*)?\\d+[0-9 .,]*\\s*(?:mAh|Ah|C)\\b`,\n \"giu\",\n);\n\n/**\n * A electric charge stored internally in ampere-hours,\n * parsed from any supported unit and convertible between all of them.\n */\nexport class ElectricCharge extends Quantity<ElectricChargeUnit> {\n static readonly typeInfo: PrimitiveTypeInfo = Object.freeze({\n englishName: \"Electric Charge\",\n localizedName: \"Elektrisk laddning\",\n emoji: \"\uD83D\uDD0B\",\n sources: Object.freeze([\n \"https://www.bipm.org/en/measurement-units/si-derived-units\",\n \"https://en.wikipedia.org/wiki/Ampere-hour\",\n ]),\n });\n\n protected get units(): MeasurementUnitIndex<ElectricChargeUnit> {\n return ElectricChargeUnit.index;\n }\n\n protected withBaseValue(baseValue: Decimal, unit: ElectricChargeUnit): this {\n return new ElectricCharge(baseValue, unit) as this;\n }\n\n get milliampereHours(): number {\n return this.in(ElectricChargeUnit.MilliampereHour);\n }\n get ampereHours(): number {\n return this.in(ElectricChargeUnit.AmpereHour);\n }\n get coulombs(): number {\n return this.in(ElectricChargeUnit.Coulomb);\n }\n\n /** Creates a electric charge from a value and unit. */\n static create(value: number | Decimal, unit: ElectricChargeUnit): ElectricCharge {\n ElectricChargeUnit.index.assertOwns(unit);\n const amount = value instanceof Decimal ? value : ElectricCharge.decimalOf(value);\n return new ElectricCharge(unit.toBase(amount), unit);\n }\n\n static fromMilliampereHours(value: number | Decimal): ElectricCharge {\n return ElectricCharge.create(value, ElectricChargeUnit.MilliampereHour);\n }\n static fromAmpereHours(value: number | Decimal): ElectricCharge {\n return ElectricCharge.create(value, ElectricChargeUnit.AmpereHour);\n }\n static fromCoulombs(value: number | Decimal): ElectricCharge {\n return ElectricCharge.create(value, ElectricChargeUnit.Coulomb);\n }\n\n static tryParse(input: string | null | undefined): ElectricCharge | null {\n const parsed = tryParseQuantity(input, ElectricChargeUnit.index);\n return parsed === null ? null : new ElectricCharge(parsed.baseValue, parsed.unit);\n }\n\n static parse(input: string): ElectricCharge {\n const result = ElectricCharge.tryParse(input);\n if (result === null) throw new Error(\"Invalid electric charge.\");\n return result;\n }\n\n static isValid(input: string | null | undefined): boolean {\n return ElectricCharge.tryParse(input) !== null;\n }\n\n /** Display form in the parsed unit, or `null` when the input is invalid. */\n static format(\n input: string | null | undefined,\n options?: ElectricChargeFormatOptions,\n ): string | null {\n const parsed = ElectricCharge.tryParse(input);\n if (parsed !== null) {\n if (options?.unit !== undefined || options?.decimals !== undefined) {\n return parsed.toStringInUnit(options.unit ?? parsed.originalUnit, options.decimals);\n }\n return parsed.toString();\n }\n return ElectricCharge.fallback(input, options);\n }\n\n /** The value in ampere-hours, or `null` when the input is invalid. */\n static normalize(\n input: string | null | undefined,\n options?: ElectricChargeFormatOptions,\n ): string | null {\n const parsed = ElectricCharge.tryParse(input);\n if (parsed !== null) return parsed.toNormalizedString();\n return ElectricCharge.fallback(input, options);\n }\n\n static isNormalized(input: string | null | undefined): boolean {\n return input != null && ElectricCharge.normalize(input) === input;\n }\n\n static findCandidatesInText(text: string): readonly TextCandidate<ElectricCharge>[] {\n if (!text) return [];\n\n const out: TextCandidate<ElectricCharge>[] = [];\n const pattern = new RegExp(SCAN_PATTERN.source, SCAN_PATTERN.flags);\n let match: RegExpExecArray | null;\n\n while ((match = pattern.exec(text)) !== null) {\n const value = ElectricCharge.tryParse(match[0]);\n if (value === null) continue;\n out.push(\n new TextCandidate<ElectricCharge>({\n startIndex: match.index,\n length: match[0].length,\n originalText: match[0],\n typeName: \"ElectricCharge\",\n category: TextCandidateCategory.Measurement,\n normalizedForm: value.toNormalizedString(),\n formattedForm: value.toString(),\n maskedForm: maskElectricCharge(value),\n confidence: TextMatchConfidence.Medium,\n value,\n }),\n );\n }\n\n return out;\n }\n\n private static fallback(\n input: string | null | undefined,\n options?: ElectricChargeFormatOptions,\n ): string | null {\n if (options?.fallbackToTrimmedInputWhenInvalid !== true) return null;\n if (input == null) return null;\n const trimmed = input.trim();\n return trimmed.length > 0 ? trimmed : null;\n }\n\n private static decimalOf(value: number): Decimal {\n const parsed = Decimal.fromNumber(value);\n if (parsed === null)\n throw new Error(`Value cannot be represented as a electric charge: ${value}`);\n return parsed;\n }\n}\n\n/** Returns a masked electric charge, e.g. `10 Ah` becomes `*** Ah`. */\nexport function maskElectricCharge(value: ElectricCharge): string {\n return `*** ${value.originalUnit.symbol}`;\n}\n"],
4
+ "sourcesContent": ["import { Decimal } from \"../internal/decimal.js\";\nimport type { MeasurementUnitIndex } from \"../internal/measurement-unit.js\";\nimport { Quantity, tryParseQuantity } from \"../internal/quantity.js\";\nimport type { PrimitiveTypeInfo } from \"../primitive-type-info.js\";\nimport { TextCandidate } from \"../text-scanning/text-candidate.js\";\nimport { TextCandidateCategory } from \"../text-scanning/text-candidate-category.js\";\nimport { TextMatchConfidence } from \"../text-scanning/text-match-confidence.js\";\nimport { ElectricChargeUnit } from \"./electric-charge-unit.js\";\n\n/** Options for {@link ElectricCharge.format} and {@link ElectricCharge.normalize}. */\nexport interface ElectricChargeFormatOptions {\n fallbackToTrimmedInputWhenInvalid?: boolean;\n /** Convert to this unit instead of preserving the parsed one. */\n unit?: ElectricChargeUnit;\n /** Round to this many fractional digits, away from zero. */\n decimals?: number;\n}\n\nconst SCAN_PATTERN = new RegExp(\n String.raw`(?<![\\p{L}\\p{N}_])(?:[+-]\\s*)?\\d+[0-9 .,]*\\s*(?:mAh|Ah|C)(?![\\p{L}\\p{N}_])`,\n \"giu\",\n);\n\n/**\n * A electric charge stored internally in ampere-hours,\n * parsed from any supported unit and convertible between all of them.\n */\nexport class ElectricCharge extends Quantity<ElectricChargeUnit> {\n static readonly typeInfo: PrimitiveTypeInfo = Object.freeze({\n englishName: \"Electric Charge\",\n localizedName: \"Elektrisk laddning\",\n emoji: \"\uD83D\uDD0B\",\n sources: Object.freeze([\n \"https://www.bipm.org/en/measurement-units/si-derived-units\",\n \"https://en.wikipedia.org/wiki/Ampere-hour\",\n ]),\n });\n\n protected get units(): MeasurementUnitIndex<ElectricChargeUnit> {\n return ElectricChargeUnit.index;\n }\n\n protected withBaseValue(baseValue: Decimal, unit: ElectricChargeUnit): this {\n return new ElectricCharge(baseValue, unit) as this;\n }\n\n get milliampereHours(): number {\n return this.in(ElectricChargeUnit.MilliampereHour);\n }\n get ampereHours(): number {\n return this.in(ElectricChargeUnit.AmpereHour);\n }\n get coulombs(): number {\n return this.in(ElectricChargeUnit.Coulomb);\n }\n\n /** Creates a electric charge from a value and unit. */\n static create(value: number | Decimal, unit: ElectricChargeUnit): ElectricCharge {\n ElectricChargeUnit.index.assertOwns(unit);\n const amount = value instanceof Decimal ? value : ElectricCharge.decimalOf(value);\n return new ElectricCharge(unit.toBase(amount), unit);\n }\n\n static fromMilliampereHours(value: number | Decimal): ElectricCharge {\n return ElectricCharge.create(value, ElectricChargeUnit.MilliampereHour);\n }\n static fromAmpereHours(value: number | Decimal): ElectricCharge {\n return ElectricCharge.create(value, ElectricChargeUnit.AmpereHour);\n }\n static fromCoulombs(value: number | Decimal): ElectricCharge {\n return ElectricCharge.create(value, ElectricChargeUnit.Coulomb);\n }\n\n static tryParse(input: string | null | undefined): ElectricCharge | null {\n const parsed = tryParseQuantity(input, ElectricChargeUnit.index);\n return parsed === null ? null : new ElectricCharge(parsed.baseValue, parsed.unit);\n }\n\n static parse(input: string): ElectricCharge {\n const result = ElectricCharge.tryParse(input);\n if (result === null) throw new Error(\"Invalid electric charge.\");\n return result;\n }\n\n static isValid(input: string | null | undefined): boolean {\n return ElectricCharge.tryParse(input) !== null;\n }\n\n /** Display form in the parsed unit, or `null` when the input is invalid. */\n static format(\n input: string | null | undefined,\n options?: ElectricChargeFormatOptions,\n ): string | null {\n const parsed = ElectricCharge.tryParse(input);\n if (parsed !== null) {\n if (options?.unit !== undefined || options?.decimals !== undefined) {\n return parsed.toStringInUnit(options.unit ?? parsed.originalUnit, options.decimals);\n }\n return parsed.toString();\n }\n return ElectricCharge.fallback(input, options);\n }\n\n /** The value in ampere-hours, or `null` when the input is invalid. */\n static normalize(\n input: string | null | undefined,\n options?: ElectricChargeFormatOptions,\n ): string | null {\n const parsed = ElectricCharge.tryParse(input);\n if (parsed !== null) return parsed.toNormalizedString();\n return ElectricCharge.fallback(input, options);\n }\n\n static isNormalized(input: string | null | undefined): boolean {\n return input != null && ElectricCharge.normalize(input) === input;\n }\n\n static findCandidatesInText(text: string): readonly TextCandidate<ElectricCharge>[] {\n if (!text) return [];\n\n const out: TextCandidate<ElectricCharge>[] = [];\n const pattern = new RegExp(SCAN_PATTERN.source, SCAN_PATTERN.flags);\n let match: RegExpExecArray | null;\n\n while ((match = pattern.exec(text)) !== null) {\n const value = ElectricCharge.tryParse(match[0]);\n if (value === null) continue;\n out.push(\n new TextCandidate<ElectricCharge>({\n startIndex: match.index,\n length: match[0].length,\n originalText: match[0],\n typeName: \"ElectricCharge\",\n category: TextCandidateCategory.Measurement,\n normalizedForm: value.toNormalizedString(),\n formattedForm: value.toString(),\n maskedForm: maskElectricCharge(value),\n confidence: TextMatchConfidence.Medium,\n value,\n }),\n );\n }\n\n return out;\n }\n\n private static fallback(\n input: string | null | undefined,\n options?: ElectricChargeFormatOptions,\n ): string | null {\n if (options?.fallbackToTrimmedInputWhenInvalid !== true) return null;\n if (input == null) return null;\n const trimmed = input.trim();\n return trimmed.length > 0 ? trimmed : null;\n }\n\n private static decimalOf(value: number): Decimal {\n const parsed = Decimal.fromNumber(value);\n if (parsed === null)\n throw new Error(`Value cannot be represented as a electric charge: ${value}`);\n return parsed;\n }\n}\n\n/** Returns a masked electric charge, e.g. `10 Ah` becomes `*** Ah`. */\nexport function maskElectricCharge(value: ElectricCharge): string {\n return `*** ${value.originalUnit.symbol}`;\n}\n"],
5
5
  "mappings": "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",
6
6
  "names": []
7
7
  }