@attrx/role-morphic 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs ADDED
@@ -0,0 +1,4648 @@
1
+ // src/RoleMorphic.ts
2
+ var RoleMorphic = class {
3
+ constructor() {
4
+ /** Registry de roles */
5
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6
+ this.roles = /* @__PURE__ */ new Map();
7
+ /** Registry de morphers (transformações cross-role) */
8
+ this.morphers = /* @__PURE__ */ new Map();
9
+ }
10
+ // ===========================================================================
11
+ // ROLE MANAGEMENT
12
+ // ===========================================================================
13
+ /**
14
+ * Registra uma nova role
15
+ */
16
+ register(roleId, spec) {
17
+ if (this.roles.has(roleId)) {
18
+ throw new Error(`Role "${roleId}" is already registered`);
19
+ }
20
+ if (!spec.variants[spec.base]) {
21
+ throw new Error(
22
+ `Base variant "${spec.base}" not found in role "${roleId}" variants`
23
+ );
24
+ }
25
+ this.roles.set(roleId, spec);
26
+ return this;
27
+ }
28
+ /**
29
+ * Remove uma role registrada
30
+ */
31
+ unregister(roleId) {
32
+ return this.roles.delete(roleId);
33
+ }
34
+ /**
35
+ * Verifica se uma role está registrada
36
+ */
37
+ hasRole(roleId) {
38
+ return this.roles.has(roleId);
39
+ }
40
+ /**
41
+ * Obtém a spec de uma role
42
+ */
43
+ getRole(roleId) {
44
+ return this.roles.get(roleId);
45
+ }
46
+ /**
47
+ * Lista todas as roles registradas
48
+ */
49
+ listRoles() {
50
+ return Array.from(this.roles.keys());
51
+ }
52
+ // ===========================================================================
53
+ // VARIANT UTILITIES
54
+ // ===========================================================================
55
+ /**
56
+ * Parseia um identificador de variante
57
+ *
58
+ * "color:rgb:object" => { role: "color", variant: "rgb:object" }
59
+ * "area:m2" => { role: "area", variant: "m2" }
60
+ */
61
+ parseVariantId(fullId) {
62
+ const colonIndex = fullId.indexOf(":");
63
+ if (colonIndex === -1) {
64
+ throw new Error(
65
+ `Invalid variant identifier "${fullId}". Expected format: "role:variant"`
66
+ );
67
+ }
68
+ return {
69
+ role: fullId.slice(0, colonIndex),
70
+ variant: fullId.slice(colonIndex + 1)
71
+ };
72
+ }
73
+ /**
74
+ * Verifica se uma variante existe
75
+ */
76
+ hasVariant(fullId) {
77
+ try {
78
+ const { role, variant } = this.parseVariantId(fullId);
79
+ const spec = this.roles.get(role);
80
+ return spec ? variant in spec.variants : false;
81
+ } catch {
82
+ return false;
83
+ }
84
+ }
85
+ /**
86
+ * Obtém a spec de uma variante
87
+ */
88
+ getVariant(fullId) {
89
+ const { role, variant } = this.parseVariantId(fullId);
90
+ const spec = this.roles.get(role);
91
+ return spec?.variants[variant];
92
+ }
93
+ /**
94
+ * Lista todas as variantes de uma role
95
+ */
96
+ listVariants(roleId) {
97
+ const spec = this.roles.get(roleId);
98
+ if (!spec) return [];
99
+ return Object.keys(spec.variants);
100
+ }
101
+ /**
102
+ * Lista variantes para as quais uma variante pode converter
103
+ */
104
+ getConvertibleVariants(fullId) {
105
+ const { role, variant } = this.parseVariantId(fullId);
106
+ const spec = this.roles.get(role);
107
+ if (!spec) return [];
108
+ return Object.keys(spec.variants).filter((v) => v !== variant).map((v) => `${role}:${v}`);
109
+ }
110
+ // ===========================================================================
111
+ // CONVERSION
112
+ // ===========================================================================
113
+ /**
114
+ * Converte um valor de uma variante para outra (mesma role)
115
+ *
116
+ * @param from - Identificador da variante de origem (ex: "color:hex")
117
+ * @param to - Identificador da variante de destino (ex: "color:rgb:object")
118
+ * @param value - Valor a converter
119
+ * @returns Valor convertido
120
+ * @throws Error se a conversão não for possível
121
+ */
122
+ convert(from, to, value) {
123
+ const fromParsed = this.parseVariantId(from);
124
+ const toParsed = this.parseVariantId(to);
125
+ if (fromParsed.role !== toParsed.role) {
126
+ throw new Error(
127
+ `Cannot convert between different roles ("${fromParsed.role}" -> "${toParsed.role}"). Use morph() for cross-role transformations.`
128
+ );
129
+ }
130
+ const roleSpec = this.roles.get(fromParsed.role);
131
+ if (!roleSpec) {
132
+ throw new Error(`Role "${fromParsed.role}" is not registered`);
133
+ }
134
+ const fromVariant = roleSpec.variants[fromParsed.variant];
135
+ const toVariant = roleSpec.variants[toParsed.variant];
136
+ if (!fromVariant) {
137
+ throw new Error(
138
+ `Variant "${fromParsed.variant}" not found in role "${fromParsed.role}"`
139
+ );
140
+ }
141
+ if (!toVariant) {
142
+ throw new Error(
143
+ `Variant "${toParsed.variant}" not found in role "${fromParsed.role}"`
144
+ );
145
+ }
146
+ if (fromParsed.variant === toParsed.variant) {
147
+ return value;
148
+ }
149
+ const baseVariant = roleSpec.base;
150
+ const baseValue = fromParsed.variant === baseVariant ? value : fromVariant.toBase(value);
151
+ if (toParsed.variant === baseVariant) {
152
+ return baseValue;
153
+ }
154
+ return toVariant.fromBase(baseValue);
155
+ }
156
+ /**
157
+ * Tenta converter, retornando Result ao invés de throw
158
+ */
159
+ tryConvert(from, to, value) {
160
+ try {
161
+ const result = this.convert(from, to, value);
162
+ return { ok: true, value: result };
163
+ } catch (error) {
164
+ return {
165
+ ok: false,
166
+ error: error instanceof Error ? error.message : String(error)
167
+ };
168
+ }
169
+ }
170
+ // ===========================================================================
171
+ // SAFE CONVERSION (with reversibility check)
172
+ // ===========================================================================
173
+ /**
174
+ * Verifica se uma conversão é reversível (sem perda de dados)
175
+ */
176
+ isReversible(from, to, value) {
177
+ try {
178
+ const converted = this.convert(from, to, value);
179
+ const reconverted = this.convert(to, from, converted);
180
+ return JSON.stringify(value) === JSON.stringify(reconverted);
181
+ } catch {
182
+ return false;
183
+ }
184
+ }
185
+ /**
186
+ * Retorna detalhes sobre a reversibilidade de uma conversão
187
+ */
188
+ checkReversibility(from, to, value) {
189
+ try {
190
+ const converted = this.convert(from, to, value);
191
+ const reconverted = this.convert(to, from, converted);
192
+ const reversible = JSON.stringify(value) === JSON.stringify(reconverted);
193
+ return {
194
+ reversible,
195
+ original: value,
196
+ converted,
197
+ reconverted
198
+ };
199
+ } catch (error) {
200
+ return {
201
+ reversible: false,
202
+ original: value,
203
+ converted: null,
204
+ reconverted: null
205
+ };
206
+ }
207
+ }
208
+ /**
209
+ * Converte apenas se a conversão for reversível
210
+ *
211
+ * @throws Error se a conversão causar perda de dados
212
+ */
213
+ convertSafe(from, to, value) {
214
+ const result = this.checkReversibility(from, to, value);
215
+ if (!result.reversible) {
216
+ throw new Error(
217
+ `Conversion from "${from}" to "${to}" is not reversible. Original: ${JSON.stringify(value)}, Reconverted: ${JSON.stringify(result.reconverted)}`
218
+ );
219
+ }
220
+ return result.converted;
221
+ }
222
+ // ===========================================================================
223
+ // MORPHERS (cross-role transformations)
224
+ // ===========================================================================
225
+ /**
226
+ * Registra um morpher para transformação entre roles diferentes
227
+ */
228
+ registerMorpher(spec) {
229
+ const key = `${spec.from}->${spec.to}`;
230
+ if (this.morphers.has(key)) {
231
+ throw new Error(`Morpher "${key}" is already registered`);
232
+ }
233
+ this.morphers.set(key, spec.transform);
234
+ return this;
235
+ }
236
+ /**
237
+ * Remove um morpher
238
+ */
239
+ unregisterMorpher(from, to) {
240
+ return this.morphers.delete(`${from}->${to}`);
241
+ }
242
+ /**
243
+ * Verifica se existe um morpher para a transformação
244
+ */
245
+ hasMorpher(from, to) {
246
+ return this.morphers.has(`${from}->${to}`);
247
+ }
248
+ /**
249
+ * Lista morphers disponíveis a partir de uma variante
250
+ */
251
+ getMorphersFrom(from) {
252
+ const prefix = `${from}->`;
253
+ const targets = [];
254
+ for (const key of this.morphers.keys()) {
255
+ if (key.startsWith(prefix)) {
256
+ targets.push(key.slice(prefix.length));
257
+ }
258
+ }
259
+ return targets;
260
+ }
261
+ /**
262
+ * Transforma um valor de uma role para outra usando um morpher registrado
263
+ */
264
+ morph(from, to, value) {
265
+ const key = `${from}->${to}`;
266
+ const morpher = this.morphers.get(key);
267
+ if (!morpher) {
268
+ throw new Error(
269
+ `No morpher registered for "${from}" -> "${to}". Register one with registerMorpher().`
270
+ );
271
+ }
272
+ return morpher(value);
273
+ }
274
+ /**
275
+ * Tenta transformar, retornando Result ao invés de throw
276
+ */
277
+ tryMorph(from, to, value) {
278
+ try {
279
+ const result = this.morph(from, to, value);
280
+ return { ok: true, value: result };
281
+ } catch (error) {
282
+ return {
283
+ ok: false,
284
+ error: error instanceof Error ? error.message : String(error)
285
+ };
286
+ }
287
+ }
288
+ // ===========================================================================
289
+ // CONVENIENCE METHODS
290
+ // ===========================================================================
291
+ /**
292
+ * Converte ou transforma automaticamente
293
+ *
294
+ * - Se mesma role: usa convert()
295
+ * - Se roles diferentes: usa morph()
296
+ */
297
+ auto(from, to, value) {
298
+ const fromParsed = this.parseVariantId(from);
299
+ const toParsed = this.parseVariantId(to);
300
+ if (fromParsed.role === toParsed.role) {
301
+ return this.convert(from, to, value);
302
+ }
303
+ return this.morph(from, to, value);
304
+ }
305
+ /**
306
+ * Converte um valor para a variante base da sua role
307
+ */
308
+ toBase(fullId, value) {
309
+ const { role, variant } = this.parseVariantId(fullId);
310
+ const spec = this.roles.get(role);
311
+ if (!spec) {
312
+ throw new Error(`Role "${role}" is not registered`);
313
+ }
314
+ if (variant === spec.base) {
315
+ return value;
316
+ }
317
+ const variantSpec = spec.variants[variant];
318
+ if (!variantSpec) {
319
+ throw new Error(`Variant "${variant}" not found in role "${role}"`);
320
+ }
321
+ return variantSpec.toBase(value);
322
+ }
323
+ /**
324
+ * Converte um valor da base para uma variante específica
325
+ */
326
+ fromBase(fullId, baseValue) {
327
+ const { role, variant } = this.parseVariantId(fullId);
328
+ const spec = this.roles.get(role);
329
+ if (!spec) {
330
+ throw new Error(`Role "${role}" is not registered`);
331
+ }
332
+ if (variant === spec.base) {
333
+ return baseValue;
334
+ }
335
+ const variantSpec = spec.variants[variant];
336
+ if (!variantSpec) {
337
+ throw new Error(`Variant "${variant}" not found in role "${role}"`);
338
+ }
339
+ return variantSpec.fromBase(baseValue);
340
+ }
341
+ };
342
+
343
+ // src/contracts/IVariant.ts
344
+ var BaseVariant = class {
345
+ /**
346
+ * Implementação padrão de tryCast usando cast
347
+ */
348
+ tryCast(input) {
349
+ const result = this.cast(input);
350
+ if (result === null) {
351
+ return {
352
+ ok: false,
353
+ error: `Cannot cast "${String(input)}" to ${this.name}`
354
+ };
355
+ }
356
+ return { ok: true, value: result };
357
+ }
358
+ /**
359
+ * Implementação padrão de isValid usando validate
360
+ */
361
+ isValid(value) {
362
+ return this.validate(value).valid;
363
+ }
364
+ };
365
+
366
+ // src/contracts/SimpleRole.ts
367
+ var SimpleRole = class {
368
+ constructor() {
369
+ // ===========================================================================
370
+ // OPTIONAL PROPERTIES (subclasses podem sobrescrever)
371
+ // ===========================================================================
372
+ /** Aliases para reconhecimento no cast (ex: "ha" → "hectare") */
373
+ this.aliases = {};
374
+ /** Regras de validação */
375
+ this.validation = {};
376
+ }
377
+ // ===========================================================================
378
+ // INTROSPECTION
379
+ // ===========================================================================
380
+ getVariants() {
381
+ return Object.keys(this.units);
382
+ }
383
+ hasVariant(variant) {
384
+ return variant in this.units;
385
+ }
386
+ // ===========================================================================
387
+ // CONVERT (Pilar 1)
388
+ // ===========================================================================
389
+ convert(from, to, value) {
390
+ const numValue = value;
391
+ if (from === to) {
392
+ return value;
393
+ }
394
+ const baseValue = this.toBase(from, numValue);
395
+ return this.fromBase(to, baseValue);
396
+ }
397
+ tryConvert(from, to, value) {
398
+ try {
399
+ if (!this.hasVariant(from)) {
400
+ return { ok: false, error: `Unknown variant: ${from}` };
401
+ }
402
+ if (!this.hasVariant(to)) {
403
+ return { ok: false, error: `Unknown variant: ${to}` };
404
+ }
405
+ const result = this.convert(from, to, value);
406
+ return { ok: true, value: result };
407
+ } catch (err) {
408
+ return { ok: false, error: String(err) };
409
+ }
410
+ }
411
+ toBase(variant, value) {
412
+ const numValue = value;
413
+ const unit = this.units[variant];
414
+ if (!unit) {
415
+ throw new Error(`Unknown variant: ${variant}`);
416
+ }
417
+ if (unit.toBase) {
418
+ return unit.toBase(numValue);
419
+ }
420
+ return numValue * (unit.factor ?? 1);
421
+ }
422
+ fromBase(variant, baseValue) {
423
+ const unit = this.units[variant];
424
+ if (!unit) {
425
+ throw new Error(`Unknown variant: ${variant}`);
426
+ }
427
+ if (unit.fromBase) {
428
+ return unit.fromBase(baseValue);
429
+ }
430
+ return baseValue / (unit.factor ?? 1);
431
+ }
432
+ // ===========================================================================
433
+ // CAST (Pilar 2)
434
+ // ===========================================================================
435
+ cast(variant, input) {
436
+ if (typeof input === "number") {
437
+ return Number.isFinite(input) ? input : null;
438
+ }
439
+ if (typeof input === "string") {
440
+ const parsed = this.parseString(input, variant);
441
+ return parsed !== null ? parsed : null;
442
+ }
443
+ return null;
444
+ }
445
+ tryCast(variant, input) {
446
+ const result = this.cast(variant, input);
447
+ if (result === null) {
448
+ return {
449
+ ok: false,
450
+ error: `Cannot cast "${String(input)}" to ${this.name}:${variant}`
451
+ };
452
+ }
453
+ return { ok: true, value: result };
454
+ }
455
+ /**
456
+ * Parseia string para número, detectando unidade se presente.
457
+ * Subclasses podem sobrescrever para lógica customizada.
458
+ */
459
+ parseString(input, targetVariant) {
460
+ const trimmed = input.trim().toLowerCase();
461
+ const match = trimmed.match(/^([+-]?[\d.,\s]+)\s*(.*)$/);
462
+ if (!match) {
463
+ return null;
464
+ }
465
+ let [, numStr, unitStr] = match;
466
+ const numValue = this.parseNumber(numStr);
467
+ if (numValue === null) {
468
+ return null;
469
+ }
470
+ unitStr = unitStr.trim();
471
+ if (unitStr) {
472
+ const detectedUnit = this.detectUnit(unitStr);
473
+ if (detectedUnit && detectedUnit !== targetVariant) {
474
+ return this.convert(detectedUnit, targetVariant, numValue);
475
+ }
476
+ }
477
+ return numValue;
478
+ }
479
+ /**
480
+ * Parseia string numérica considerando formatos brasileiro e americano
481
+ */
482
+ parseNumber(numStr) {
483
+ numStr = numStr.replace(/\s/g, "");
484
+ const lastComma = numStr.lastIndexOf(",");
485
+ const lastDot = numStr.lastIndexOf(".");
486
+ const hasComma = lastComma !== -1;
487
+ const hasDot = lastDot !== -1;
488
+ if (hasComma && hasDot) {
489
+ if (lastComma > lastDot) {
490
+ numStr = numStr.replace(/\./g, "").replace(",", ".");
491
+ } else {
492
+ numStr = numStr.replace(/,/g, "");
493
+ }
494
+ } else if (hasComma && !hasDot) {
495
+ const afterComma = numStr.split(",").slice(1);
496
+ const isThousandSep = afterComma.every((part) => part.length === 3);
497
+ if (isThousandSep) {
498
+ numStr = numStr.replace(/,/g, "");
499
+ } else {
500
+ numStr = numStr.replace(",", ".");
501
+ }
502
+ } else if (!hasComma && hasDot) {
503
+ const afterDot = numStr.split(".").slice(1);
504
+ if (afterDot.length > 1) {
505
+ numStr = numStr.replace(/\./g, "");
506
+ }
507
+ }
508
+ const value = parseFloat(numStr);
509
+ return isNaN(value) ? null : value;
510
+ }
511
+ /**
512
+ * Detecta unidade a partir de string (símbolo ou alias)
513
+ */
514
+ detectUnit(unitStr) {
515
+ const normalized = unitStr.toLowerCase().trim();
516
+ if (normalized in this.aliases) {
517
+ return this.aliases[normalized];
518
+ }
519
+ for (const [variant, config] of Object.entries(this.units)) {
520
+ if (config.symbol.toLowerCase() === normalized || config.singular?.toLowerCase() === normalized || config.plural?.toLowerCase() === normalized || variant.toLowerCase() === normalized) {
521
+ return variant;
522
+ }
523
+ }
524
+ return null;
525
+ }
526
+ // ===========================================================================
527
+ // VALIDATE (Pilar 3)
528
+ // ===========================================================================
529
+ validate(variant, value) {
530
+ const errors = [];
531
+ if (typeof value !== "number") {
532
+ return { valid: false, errors: ["Value must be a number"] };
533
+ }
534
+ if (!Number.isFinite(value)) {
535
+ return { valid: false, errors: ["Value must be finite"] };
536
+ }
537
+ if (this.validation.min !== void 0 && value < this.validation.min) {
538
+ const msg = this.validation.minError ?? `Value must be at least ${this.validation.min}`;
539
+ errors.push(msg);
540
+ }
541
+ if (this.validation.max !== void 0 && value > this.validation.max) {
542
+ const msg = this.validation.maxError ?? `Value must be at most ${this.validation.max}`;
543
+ errors.push(msg);
544
+ }
545
+ if (this.validation.integer && !Number.isInteger(value)) {
546
+ errors.push("Value must be an integer");
547
+ }
548
+ return { valid: errors.length === 0, errors };
549
+ }
550
+ isValid(variant, value) {
551
+ return this.validate(variant, value).valid;
552
+ }
553
+ // ===========================================================================
554
+ // FORMAT (Pilar 4)
555
+ // ===========================================================================
556
+ format(variant, value, options = {}) {
557
+ const { decimals = 2, verbose = false, locale } = options;
558
+ const unit = this.units[variant];
559
+ if (!unit) {
560
+ throw new Error(`Unknown variant: ${variant}`);
561
+ }
562
+ const numValue = value;
563
+ let formattedValue;
564
+ if (locale) {
565
+ formattedValue = numValue.toLocaleString(locale, {
566
+ minimumFractionDigits: 0,
567
+ maximumFractionDigits: decimals
568
+ });
569
+ } else {
570
+ formattedValue = Number(numValue.toFixed(decimals)).toString();
571
+ }
572
+ const separator = unit.noSpace ? "" : " ";
573
+ if (verbose) {
574
+ const name = numValue === 1 ? unit.singular : unit.plural;
575
+ return `${formattedValue} ${name || unit.symbol}`;
576
+ }
577
+ return `${formattedValue}${separator}${unit.symbol}`;
578
+ }
579
+ // ===========================================================================
580
+ // COMPATIBILITY
581
+ // ===========================================================================
582
+ toSpec() {
583
+ const variants = {};
584
+ for (const [name, config] of Object.entries(this.units)) {
585
+ const factor = config.factor ?? 1;
586
+ const toBaseFn = config.toBase ? (v) => config.toBase(v) : (v) => v * factor;
587
+ const fromBaseFn = config.fromBase ? (v) => config.fromBase(v) : (v) => v / factor;
588
+ variants[name] = {
589
+ type: "number",
590
+ toBase: toBaseFn,
591
+ fromBase: fromBaseFn,
592
+ cast: (input) => this.cast(name, input),
593
+ validate: {
594
+ min: this.validation.min,
595
+ max: this.validation.max,
596
+ integer: this.validation.integer,
597
+ custom: (v) => {
598
+ const result = this.validate(name, v);
599
+ return result.valid ? null : result.errors[0];
600
+ }
601
+ },
602
+ format: {
603
+ symbol: config.symbol,
604
+ singular: config.singular,
605
+ plural: config.plural
606
+ }
607
+ };
608
+ }
609
+ return {
610
+ base: this.base,
611
+ variants
612
+ };
613
+ }
614
+ };
615
+
616
+ // src/contracts/ComplexRole.ts
617
+ var ComplexRole = class {
618
+ constructor() {
619
+ // ===========================================================================
620
+ // VARIANTS
621
+ // ===========================================================================
622
+ /** Cache das variantes */
623
+ this._variants = null;
624
+ }
625
+ /**
626
+ * Retorna mapa de variantes (lazy initialization)
627
+ */
628
+ get variants() {
629
+ if (!this._variants) {
630
+ this._variants = new Map(Object.entries(this.createVariants()));
631
+ }
632
+ return this._variants;
633
+ }
634
+ /**
635
+ * Retorna uma variante específica
636
+ */
637
+ getVariant(name) {
638
+ const variant = this.variants.get(name);
639
+ if (!variant) {
640
+ throw new Error(`Unknown variant: ${name}`);
641
+ }
642
+ return variant;
643
+ }
644
+ // ===========================================================================
645
+ // INTROSPECTION
646
+ // ===========================================================================
647
+ getVariants() {
648
+ return Array.from(this.variants.keys());
649
+ }
650
+ hasVariant(variant) {
651
+ return this.variants.has(variant);
652
+ }
653
+ // ===========================================================================
654
+ // CONVERT (Pilar 1)
655
+ // ===========================================================================
656
+ convert(from, to, value) {
657
+ if (from === to) {
658
+ return value;
659
+ }
660
+ const fromVariant = this.getVariant(from);
661
+ const toVariant = this.getVariant(to);
662
+ const baseValue = fromVariant.toBase(value);
663
+ return toVariant.fromBase(baseValue);
664
+ }
665
+ tryConvert(from, to, value) {
666
+ try {
667
+ if (!this.hasVariant(from)) {
668
+ return { ok: false, error: `Unknown variant: ${from}` };
669
+ }
670
+ if (!this.hasVariant(to)) {
671
+ return { ok: false, error: `Unknown variant: ${to}` };
672
+ }
673
+ const result = this.convert(from, to, value);
674
+ return { ok: true, value: result };
675
+ } catch (err) {
676
+ return { ok: false, error: String(err) };
677
+ }
678
+ }
679
+ toBase(variant, value) {
680
+ return this.getVariant(variant).toBase(value);
681
+ }
682
+ fromBase(variant, baseValue) {
683
+ return this.getVariant(variant).fromBase(baseValue);
684
+ }
685
+ // ===========================================================================
686
+ // CAST (Pilar 2)
687
+ // ===========================================================================
688
+ cast(variant, input) {
689
+ return this.getVariant(variant).cast(input);
690
+ }
691
+ tryCast(variant, input) {
692
+ const result = this.cast(variant, input);
693
+ if (result === null) {
694
+ return {
695
+ ok: false,
696
+ error: `Cannot cast "${String(input)}" to ${this.name}:${variant}`
697
+ };
698
+ }
699
+ return { ok: true, value: result };
700
+ }
701
+ // ===========================================================================
702
+ // VALIDATE (Pilar 3)
703
+ // ===========================================================================
704
+ validate(variant, value) {
705
+ return this.getVariant(variant).validate(value);
706
+ }
707
+ isValid(variant, value) {
708
+ return this.validate(variant, value).valid;
709
+ }
710
+ // ===========================================================================
711
+ // FORMAT (Pilar 4)
712
+ // ===========================================================================
713
+ format(variant, value, options) {
714
+ return this.getVariant(variant).format(value, options);
715
+ }
716
+ // ===========================================================================
717
+ // COMPATIBILITY
718
+ // ===========================================================================
719
+ toSpec() {
720
+ const variants = {};
721
+ for (const [name, variant] of this.variants.entries()) {
722
+ variants[name] = {
723
+ type: variant.type,
724
+ toBase: (v) => variant.toBase(v),
725
+ fromBase: (v) => variant.fromBase(v),
726
+ cast: (input) => variant.cast(input),
727
+ validate: {
728
+ custom: (v) => {
729
+ const result = variant.validate(v);
730
+ return result.valid ? null : result.errors[0];
731
+ }
732
+ },
733
+ format: {
734
+ symbol: name,
735
+ formatter: (v, opts) => variant.format(v, opts)
736
+ }
737
+ };
738
+ }
739
+ return {
740
+ base: this.base,
741
+ variants
742
+ };
743
+ }
744
+ };
745
+
746
+ // src/roles/area/constants.ts
747
+ var AREA_UNITS = {
748
+ // SI Units (todos exatos)
749
+ square_kilometer: {
750
+ factor: 1e6,
751
+ symbol: "km\xB2",
752
+ singular: "square kilometer",
753
+ plural: "square kilometers"
754
+ },
755
+ hectare: {
756
+ factor: 1e4,
757
+ symbol: "ha",
758
+ singular: "hectare",
759
+ plural: "hectares"
760
+ },
761
+ are: {
762
+ factor: 100,
763
+ symbol: "a",
764
+ singular: "are",
765
+ plural: "ares"
766
+ },
767
+ square_meter: {
768
+ factor: 1,
769
+ symbol: "m\xB2",
770
+ singular: "square meter",
771
+ plural: "square meters"
772
+ },
773
+ square_decimeter: {
774
+ factor: 0.01,
775
+ symbol: "dm\xB2",
776
+ singular: "square decimeter",
777
+ plural: "square decimeters"
778
+ },
779
+ square_centimeter: {
780
+ factor: 1e-4,
781
+ symbol: "cm\xB2",
782
+ singular: "square centimeter",
783
+ plural: "square centimeters"
784
+ },
785
+ square_millimeter: {
786
+ factor: 1e-6,
787
+ symbol: "mm\xB2",
788
+ singular: "square millimeter",
789
+ plural: "square millimeters"
790
+ },
791
+ // Imperial/US (todos exatos, derivados de comprimento² desde 1959)
792
+ square_mile: {
793
+ factor: 2589988110336e-6,
794
+ symbol: "mi\xB2",
795
+ singular: "square mile",
796
+ plural: "square miles"
797
+ },
798
+ acre: {
799
+ factor: 4046.8564224,
800
+ symbol: "ac",
801
+ singular: "acre",
802
+ plural: "acres"
803
+ },
804
+ square_yard: {
805
+ factor: 0.83612736,
806
+ symbol: "yd\xB2",
807
+ singular: "square yard",
808
+ plural: "square yards"
809
+ },
810
+ square_foot: {
811
+ factor: 0.09290304,
812
+ symbol: "ft\xB2",
813
+ singular: "square foot",
814
+ plural: "square feet"
815
+ },
816
+ square_inch: {
817
+ factor: 64516e-8,
818
+ symbol: "in\xB2",
819
+ singular: "square inch",
820
+ plural: "square inches"
821
+ }
822
+ };
823
+ var AREA_ALIASES = {
824
+ // square_kilometer
825
+ "km\xB2": "square_kilometer",
826
+ km2: "square_kilometer",
827
+ "sq km": "square_kilometer",
828
+ "square kilometer": "square_kilometer",
829
+ "square kilometers": "square_kilometer",
830
+ "quil\xF4metro quadrado": "square_kilometer",
831
+ "quil\xF4metros quadrados": "square_kilometer",
832
+ // hectare
833
+ ha: "hectare",
834
+ hectare: "hectare",
835
+ hectares: "hectare",
836
+ // are
837
+ a: "are",
838
+ are: "are",
839
+ ares: "are",
840
+ // square_meter
841
+ "m\xB2": "square_meter",
842
+ m2: "square_meter",
843
+ "sq m": "square_meter",
844
+ "square meter": "square_meter",
845
+ "square meters": "square_meter",
846
+ "metro quadrado": "square_meter",
847
+ "metros quadrados": "square_meter",
848
+ // square_decimeter
849
+ "dm\xB2": "square_decimeter",
850
+ dm2: "square_decimeter",
851
+ // square_centimeter
852
+ "cm\xB2": "square_centimeter",
853
+ cm2: "square_centimeter",
854
+ "sq cm": "square_centimeter",
855
+ // square_millimeter
856
+ "mm\xB2": "square_millimeter",
857
+ mm2: "square_millimeter",
858
+ "sq mm": "square_millimeter",
859
+ // square_mile
860
+ "mi\xB2": "square_mile",
861
+ mi2: "square_mile",
862
+ "sq mi": "square_mile",
863
+ "square mile": "square_mile",
864
+ "square miles": "square_mile",
865
+ // acre
866
+ ac: "acre",
867
+ acre: "acre",
868
+ acres: "acre",
869
+ // square_yard
870
+ "yd\xB2": "square_yard",
871
+ yd2: "square_yard",
872
+ "sq yd": "square_yard",
873
+ "square yard": "square_yard",
874
+ "square yards": "square_yard",
875
+ // square_foot
876
+ "ft\xB2": "square_foot",
877
+ ft2: "square_foot",
878
+ "sq ft": "square_foot",
879
+ "square foot": "square_foot",
880
+ "square feet": "square_foot",
881
+ // square_inch
882
+ "in\xB2": "square_inch",
883
+ in2: "square_inch",
884
+ "sq in": "square_inch",
885
+ "square inch": "square_inch",
886
+ "square inches": "square_inch"
887
+ };
888
+ Object.fromEntries(
889
+ Object.entries(AREA_UNITS).map(([unit, config]) => [
890
+ unit,
891
+ config.factor ?? 1
892
+ ])
893
+ );
894
+
895
+ // src/roles/area/AreaRole.ts
896
+ var AreaRole = class extends SimpleRole {
897
+ constructor() {
898
+ super(...arguments);
899
+ this.name = "area";
900
+ this.base = "square_meter";
901
+ this.units = AREA_UNITS;
902
+ this.aliases = AREA_ALIASES;
903
+ this.validation = {
904
+ min: 0,
905
+ minError: "Area cannot be negative"
906
+ };
907
+ }
908
+ };
909
+ var areaRole = new AreaRole();
910
+
911
+ // src/roles/length/constants.ts
912
+ var LENGTH_UNITS = {
913
+ // SI Units (todos exatos)
914
+ kilometer: {
915
+ factor: 1e3,
916
+ symbol: "km",
917
+ singular: "kilometer",
918
+ plural: "kilometers"
919
+ },
920
+ hectometer: {
921
+ factor: 100,
922
+ symbol: "hm",
923
+ singular: "hectometer",
924
+ plural: "hectometers"
925
+ },
926
+ decameter: {
927
+ factor: 10,
928
+ symbol: "dam",
929
+ singular: "decameter",
930
+ plural: "decameters"
931
+ },
932
+ meter: {
933
+ factor: 1,
934
+ symbol: "m",
935
+ singular: "meter",
936
+ plural: "meters"
937
+ },
938
+ decimeter: {
939
+ factor: 0.1,
940
+ symbol: "dm",
941
+ singular: "decimeter",
942
+ plural: "decimeters"
943
+ },
944
+ centimeter: {
945
+ factor: 0.01,
946
+ symbol: "cm",
947
+ singular: "centimeter",
948
+ plural: "centimeters"
949
+ },
950
+ millimeter: {
951
+ factor: 1e-3,
952
+ symbol: "mm",
953
+ singular: "millimeter",
954
+ plural: "millimeters"
955
+ },
956
+ micrometer: {
957
+ factor: 1e-6,
958
+ symbol: "\u03BCm",
959
+ singular: "micrometer",
960
+ plural: "micrometers"
961
+ },
962
+ nanometer: {
963
+ factor: 1e-9,
964
+ symbol: "nm",
965
+ singular: "nanometer",
966
+ plural: "nanometers"
967
+ },
968
+ // Imperial/US (exatos desde 1959)
969
+ inch: {
970
+ factor: 0.0254,
971
+ symbol: "in",
972
+ singular: "inch",
973
+ plural: "inches"
974
+ },
975
+ foot: {
976
+ factor: 0.3048,
977
+ symbol: "ft",
978
+ singular: "foot",
979
+ plural: "feet"
980
+ },
981
+ yard: {
982
+ factor: 0.9144,
983
+ symbol: "yd",
984
+ singular: "yard",
985
+ plural: "yards"
986
+ },
987
+ mile: {
988
+ factor: 1609.344,
989
+ symbol: "mi",
990
+ singular: "mile",
991
+ plural: "miles"
992
+ },
993
+ // Nautical (exato por definição internacional)
994
+ nautical_mile: {
995
+ factor: 1852,
996
+ symbol: "nmi",
997
+ singular: "nautical mile",
998
+ plural: "nautical miles"
999
+ },
1000
+ // Other (derivados, portanto exatos)
1001
+ fathom: {
1002
+ factor: 1.8288,
1003
+ // 6 feet
1004
+ symbol: "ftm",
1005
+ singular: "fathom",
1006
+ plural: "fathoms"
1007
+ },
1008
+ furlong: {
1009
+ factor: 201.168,
1010
+ // 660 feet = 1/8 mile
1011
+ symbol: "fur",
1012
+ singular: "furlong",
1013
+ plural: "furlongs"
1014
+ },
1015
+ league: {
1016
+ factor: 4828.032,
1017
+ // 3 miles
1018
+ symbol: "lea",
1019
+ singular: "league",
1020
+ plural: "leagues"
1021
+ }
1022
+ };
1023
+ var LENGTH_ALIASES = {
1024
+ // kilometer
1025
+ km: "kilometer",
1026
+ kilometer: "kilometer",
1027
+ kilometers: "kilometer",
1028
+ quil\u00F4metro: "kilometer",
1029
+ quil\u00F4metros: "kilometer",
1030
+ // hectometer
1031
+ hm: "hectometer",
1032
+ hectometer: "hectometer",
1033
+ hectometers: "hectometer",
1034
+ // decameter
1035
+ dam: "decameter",
1036
+ decameter: "decameter",
1037
+ decameters: "decameter",
1038
+ // meter
1039
+ m: "meter",
1040
+ meter: "meter",
1041
+ meters: "meter",
1042
+ metro: "meter",
1043
+ metros: "meter",
1044
+ // decimeter
1045
+ dm: "decimeter",
1046
+ decimeter: "decimeter",
1047
+ decimeters: "decimeter",
1048
+ // centimeter
1049
+ cm: "centimeter",
1050
+ centimeter: "centimeter",
1051
+ centimeters: "centimeter",
1052
+ cent\u00EDmetro: "centimeter",
1053
+ cent\u00EDmetros: "centimeter",
1054
+ // millimeter
1055
+ mm: "millimeter",
1056
+ millimeter: "millimeter",
1057
+ millimeters: "millimeter",
1058
+ mil\u00EDmetro: "millimeter",
1059
+ mil\u00EDmetros: "millimeter",
1060
+ // micrometer
1061
+ \u03BCm: "micrometer",
1062
+ um: "micrometer",
1063
+ micrometer: "micrometer",
1064
+ micrometers: "micrometer",
1065
+ micron: "micrometer",
1066
+ microns: "micrometer",
1067
+ // nanometer
1068
+ nm: "nanometer",
1069
+ nanometer: "nanometer",
1070
+ nanometers: "nanometer",
1071
+ // inch
1072
+ in: "inch",
1073
+ '"': "inch",
1074
+ inch: "inch",
1075
+ inches: "inch",
1076
+ polegada: "inch",
1077
+ polegadas: "inch",
1078
+ // foot
1079
+ ft: "foot",
1080
+ "'": "foot",
1081
+ foot: "foot",
1082
+ feet: "foot",
1083
+ p\u00E9: "foot",
1084
+ p\u00E9s: "foot",
1085
+ // yard
1086
+ yd: "yard",
1087
+ yard: "yard",
1088
+ yards: "yard",
1089
+ jarda: "yard",
1090
+ jardas: "yard",
1091
+ // mile
1092
+ mi: "mile",
1093
+ mile: "mile",
1094
+ miles: "mile",
1095
+ milha: "mile",
1096
+ milhas: "mile",
1097
+ // nautical_mile
1098
+ nmi: "nautical_mile",
1099
+ "nautical mile": "nautical_mile",
1100
+ "nautical miles": "nautical_mile",
1101
+ "milha n\xE1utica": "nautical_mile",
1102
+ "milhas n\xE1uticas": "nautical_mile",
1103
+ // fathom
1104
+ ftm: "fathom",
1105
+ fathom: "fathom",
1106
+ fathoms: "fathom",
1107
+ bra\u00E7a: "fathom",
1108
+ bra\u00E7as: "fathom",
1109
+ // furlong
1110
+ fur: "furlong",
1111
+ furlong: "furlong",
1112
+ furlongs: "furlong",
1113
+ // league
1114
+ lea: "league",
1115
+ league: "league",
1116
+ leagues: "league",
1117
+ l\u00E9gua: "league",
1118
+ l\u00E9guas: "league"
1119
+ };
1120
+ Object.fromEntries(
1121
+ Object.entries(LENGTH_UNITS).map(([unit, config]) => [
1122
+ unit,
1123
+ config.factor ?? 1
1124
+ ])
1125
+ );
1126
+
1127
+ // src/roles/length/LengthRole.ts
1128
+ var LengthRole = class extends SimpleRole {
1129
+ constructor() {
1130
+ super(...arguments);
1131
+ this.name = "length";
1132
+ this.base = "meter";
1133
+ this.units = LENGTH_UNITS;
1134
+ this.aliases = LENGTH_ALIASES;
1135
+ }
1136
+ // Length pode ser negativo (posição relativa), então não definimos min
1137
+ };
1138
+ var lengthRole = new LengthRole();
1139
+
1140
+ // src/roles/mass/constants.ts
1141
+ var MASS_UNITS = {
1142
+ // ===========================================================================
1143
+ // SI / Metric
1144
+ // ===========================================================================
1145
+ metric_ton: {
1146
+ factor: 1e3,
1147
+ symbol: "t",
1148
+ singular: "metric ton",
1149
+ plural: "metric tons"
1150
+ },
1151
+ kilogram: {
1152
+ factor: 1,
1153
+ symbol: "kg",
1154
+ singular: "kilogram",
1155
+ plural: "kilograms"
1156
+ },
1157
+ hectogram: {
1158
+ factor: 0.1,
1159
+ symbol: "hg",
1160
+ singular: "hectogram",
1161
+ plural: "hectograms"
1162
+ },
1163
+ decagram: {
1164
+ factor: 0.01,
1165
+ symbol: "dag",
1166
+ singular: "decagram",
1167
+ plural: "decagrams"
1168
+ },
1169
+ gram: {
1170
+ factor: 1e-3,
1171
+ symbol: "g",
1172
+ singular: "gram",
1173
+ plural: "grams"
1174
+ },
1175
+ decigram: {
1176
+ factor: 1e-4,
1177
+ symbol: "dg",
1178
+ singular: "decigram",
1179
+ plural: "decigrams"
1180
+ },
1181
+ centigram: {
1182
+ factor: 1e-5,
1183
+ symbol: "cg",
1184
+ singular: "centigram",
1185
+ plural: "centigrams"
1186
+ },
1187
+ milligram: {
1188
+ factor: 1e-6,
1189
+ symbol: "mg",
1190
+ singular: "milligram",
1191
+ plural: "milligrams"
1192
+ },
1193
+ microgram: {
1194
+ factor: 1e-9,
1195
+ symbol: "\u03BCg",
1196
+ singular: "microgram",
1197
+ plural: "micrograms"
1198
+ },
1199
+ // ===========================================================================
1200
+ // Avoirdupois (US/UK) - Sistema padrão para peso comum
1201
+ // ===========================================================================
1202
+ long_ton: {
1203
+ factor: 1016.0469088,
1204
+ // 2240 lb (exato)
1205
+ symbol: "long tn",
1206
+ singular: "long ton",
1207
+ plural: "long tons"
1208
+ },
1209
+ short_ton: {
1210
+ factor: 907.18474,
1211
+ // 2000 lb (exato)
1212
+ symbol: "sh tn",
1213
+ singular: "short ton",
1214
+ plural: "short tons"
1215
+ },
1216
+ stone: {
1217
+ factor: 6.35029318,
1218
+ // 14 lb (exato)
1219
+ symbol: "st",
1220
+ singular: "stone",
1221
+ plural: "stone"
1222
+ // stone não muda no plural em inglês
1223
+ },
1224
+ pound: {
1225
+ factor: 0.45359237,
1226
+ // Exato desde 1959
1227
+ symbol: "lb",
1228
+ singular: "pound",
1229
+ plural: "pounds"
1230
+ },
1231
+ ounce: {
1232
+ factor: 0.028349523125,
1233
+ // 1/16 lb (exato)
1234
+ symbol: "oz",
1235
+ singular: "ounce",
1236
+ plural: "ounces"
1237
+ },
1238
+ dram: {
1239
+ factor: 0.0017718451953125,
1240
+ // 1/16 oz (exato)
1241
+ symbol: "dr",
1242
+ singular: "dram",
1243
+ plural: "drams"
1244
+ },
1245
+ grain: {
1246
+ factor: 6479891e-11,
1247
+ // 1/7000 lb (exato)
1248
+ symbol: "gr",
1249
+ singular: "grain",
1250
+ plural: "grains"
1251
+ },
1252
+ // ===========================================================================
1253
+ // Troy (metais preciosos)
1254
+ // ===========================================================================
1255
+ troy_pound: {
1256
+ factor: 0.3732417216,
1257
+ // 12 troy oz (exato)
1258
+ symbol: "lb t",
1259
+ singular: "troy pound",
1260
+ plural: "troy pounds"
1261
+ },
1262
+ troy_ounce: {
1263
+ factor: 0.0311034768,
1264
+ // 480 grains (exato)
1265
+ symbol: "oz t",
1266
+ singular: "troy ounce",
1267
+ plural: "troy ounces"
1268
+ },
1269
+ pennyweight: {
1270
+ factor: 0.00155517384,
1271
+ // 1/20 troy oz (exato)
1272
+ symbol: "dwt",
1273
+ singular: "pennyweight",
1274
+ plural: "pennyweights"
1275
+ }
1276
+ };
1277
+ var MASS_ALIASES = {
1278
+ // metric_ton
1279
+ t: "metric_ton",
1280
+ tonne: "metric_ton",
1281
+ tonnes: "metric_ton",
1282
+ "metric ton": "metric_ton",
1283
+ "metric tons": "metric_ton",
1284
+ tonelada: "metric_ton",
1285
+ toneladas: "metric_ton",
1286
+ // kilogram
1287
+ kg: "kilogram",
1288
+ kilo: "kilogram",
1289
+ kilos: "kilogram",
1290
+ kilogram: "kilogram",
1291
+ kilograms: "kilogram",
1292
+ quilograma: "kilogram",
1293
+ quilogramas: "kilogram",
1294
+ // hectogram
1295
+ hg: "hectogram",
1296
+ hectogram: "hectogram",
1297
+ hectograms: "hectogram",
1298
+ // decagram
1299
+ dag: "decagram",
1300
+ decagram: "decagram",
1301
+ decagrams: "decagram",
1302
+ // gram
1303
+ g: "gram",
1304
+ gram: "gram",
1305
+ grams: "gram",
1306
+ grama: "gram",
1307
+ gramas: "gram",
1308
+ // decigram
1309
+ dg: "decigram",
1310
+ decigram: "decigram",
1311
+ decigrams: "decigram",
1312
+ // centigram
1313
+ cg: "centigram",
1314
+ centigram: "centigram",
1315
+ centigrams: "centigram",
1316
+ // milligram
1317
+ mg: "milligram",
1318
+ milligram: "milligram",
1319
+ milligrams: "milligram",
1320
+ miligrama: "milligram",
1321
+ miligramas: "milligram",
1322
+ // microgram
1323
+ \u03BCg: "microgram",
1324
+ ug: "microgram",
1325
+ mcg: "microgram",
1326
+ microgram: "microgram",
1327
+ micrograms: "microgram",
1328
+ // long_ton
1329
+ "long tn": "long_ton",
1330
+ "long ton": "long_ton",
1331
+ "long tons": "long_ton",
1332
+ "imperial ton": "long_ton",
1333
+ // short_ton
1334
+ "sh tn": "short_ton",
1335
+ "short ton": "short_ton",
1336
+ "short tons": "short_ton",
1337
+ "us ton": "short_ton",
1338
+ ton: "short_ton",
1339
+ // Default "ton" para short ton (US)
1340
+ tons: "short_ton",
1341
+ // stone
1342
+ st: "stone",
1343
+ stone: "stone",
1344
+ stones: "stone",
1345
+ // pound
1346
+ lb: "pound",
1347
+ lbs: "pound",
1348
+ pound: "pound",
1349
+ pounds: "pound",
1350
+ libra: "pound",
1351
+ libras: "pound",
1352
+ "#": "pound",
1353
+ // ounce
1354
+ oz: "ounce",
1355
+ ounce: "ounce",
1356
+ ounces: "ounce",
1357
+ on\u00E7a: "ounce",
1358
+ on\u00E7as: "ounce",
1359
+ // dram
1360
+ dr: "dram",
1361
+ dram: "dram",
1362
+ drams: "dram",
1363
+ // grain
1364
+ gr: "grain",
1365
+ grain: "grain",
1366
+ grains: "grain",
1367
+ // troy_pound
1368
+ "lb t": "troy_pound",
1369
+ "troy pound": "troy_pound",
1370
+ "troy pounds": "troy_pound",
1371
+ // troy_ounce
1372
+ "oz t": "troy_ounce",
1373
+ ozt: "troy_ounce",
1374
+ "troy ounce": "troy_ounce",
1375
+ "troy ounces": "troy_ounce",
1376
+ // pennyweight
1377
+ dwt: "pennyweight",
1378
+ pennyweight: "pennyweight",
1379
+ pennyweights: "pennyweight"
1380
+ };
1381
+ Object.fromEntries(
1382
+ Object.entries(MASS_UNITS).map(([unit, config]) => [
1383
+ unit,
1384
+ config.factor ?? 1
1385
+ ])
1386
+ );
1387
+
1388
+ // src/roles/mass/MassRole.ts
1389
+ var MassRole = class extends SimpleRole {
1390
+ constructor() {
1391
+ super(...arguments);
1392
+ this.name = "mass";
1393
+ this.base = "kilogram";
1394
+ this.units = MASS_UNITS;
1395
+ this.aliases = MASS_ALIASES;
1396
+ this.validation = {
1397
+ min: 0,
1398
+ minError: "Mass cannot be negative"
1399
+ };
1400
+ }
1401
+ };
1402
+ var massRole = new MassRole();
1403
+
1404
+ // src/roles/temperature/constants.ts
1405
+ var TEMPERATURE_UNITS = {
1406
+ celsius: {
1407
+ symbol: "\xB0C",
1408
+ singular: "degree Celsius",
1409
+ plural: "degrees Celsius",
1410
+ toBase: (c) => c,
1411
+ fromBase: (c) => c,
1412
+ noSpace: true
1413
+ // 25°C não 25 °C
1414
+ },
1415
+ fahrenheit: {
1416
+ symbol: "\xB0F",
1417
+ singular: "degree Fahrenheit",
1418
+ plural: "degrees Fahrenheit",
1419
+ toBase: (f) => (f - 32) * (5 / 9),
1420
+ fromBase: (c) => c * (9 / 5) + 32,
1421
+ noSpace: true
1422
+ // 77°F não 77 °F
1423
+ },
1424
+ kelvin: {
1425
+ symbol: "K",
1426
+ singular: "kelvin",
1427
+ plural: "kelvins",
1428
+ toBase: (k) => k - 273.15,
1429
+ fromBase: (c) => c + 273.15
1430
+ // Kelvin usa espaço: "273 K" (convenção SI)
1431
+ },
1432
+ rankine: {
1433
+ symbol: "\xB0R",
1434
+ singular: "degree Rankine",
1435
+ plural: "degrees Rankine",
1436
+ toBase: (r) => (r - 491.67) * (5 / 9),
1437
+ fromBase: (c) => (c + 273.15) * (9 / 5),
1438
+ noSpace: true
1439
+ // 500°R não 500 °R
1440
+ }
1441
+ };
1442
+ var TEMPERATURE_ALIASES = {
1443
+ // celsius
1444
+ c: "celsius",
1445
+ "\xB0c": "celsius",
1446
+ celsius: "celsius",
1447
+ centigrade: "celsius",
1448
+ grau: "celsius",
1449
+ graus: "celsius",
1450
+ // fahrenheit
1451
+ f: "fahrenheit",
1452
+ "\xB0f": "fahrenheit",
1453
+ fahrenheit: "fahrenheit",
1454
+ fahr: "fahrenheit",
1455
+ // kelvin
1456
+ k: "kelvin",
1457
+ kelvin: "kelvin",
1458
+ kelvins: "kelvin",
1459
+ // rankine
1460
+ r: "rankine",
1461
+ "\xB0r": "rankine",
1462
+ rankine: "rankine",
1463
+ ra: "rankine"
1464
+ };
1465
+ var TEMPERATURE_CONSTANTS = {
1466
+ /** Zero absoluto em Celsius */
1467
+ ABSOLUTE_ZERO_CELSIUS: -273.15,
1468
+ /** Zero absoluto em Fahrenheit */
1469
+ ABSOLUTE_ZERO_FAHRENHEIT: -459.67};
1470
+
1471
+ // src/roles/temperature/TemperatureRole.ts
1472
+ var TemperatureRole = class extends SimpleRole {
1473
+ constructor() {
1474
+ super(...arguments);
1475
+ this.name = "temperature";
1476
+ this.base = "celsius";
1477
+ this.units = TEMPERATURE_UNITS;
1478
+ this.aliases = TEMPERATURE_ALIASES;
1479
+ // Temperatura tem validação especial: não pode ser abaixo do zero absoluto
1480
+ this.validation = {
1481
+ min: TEMPERATURE_CONSTANTS.ABSOLUTE_ZERO_CELSIUS,
1482
+ minError: "Temperature cannot be below absolute zero (-273.15\xB0C)"
1483
+ };
1484
+ }
1485
+ /**
1486
+ * Override validate para considerar o zero absoluto na escala correta
1487
+ */
1488
+ validate(variant, value) {
1489
+ const errors = [];
1490
+ if (typeof value !== "number") {
1491
+ return { valid: false, errors: ["Value must be a number"] };
1492
+ }
1493
+ if (!Number.isFinite(value)) {
1494
+ return { valid: false, errors: ["Value must be finite"] };
1495
+ }
1496
+ const absoluteZero = this.getAbsoluteZero(variant);
1497
+ if (value < absoluteZero) {
1498
+ errors.push(
1499
+ `Temperature cannot be below absolute zero (${absoluteZero}${this.units[variant]?.symbol || ""})`
1500
+ );
1501
+ }
1502
+ return { valid: errors.length === 0, errors };
1503
+ }
1504
+ /**
1505
+ * Retorna o zero absoluto na escala especificada
1506
+ */
1507
+ getAbsoluteZero(variant) {
1508
+ switch (variant) {
1509
+ case "celsius":
1510
+ return TEMPERATURE_CONSTANTS.ABSOLUTE_ZERO_CELSIUS;
1511
+ case "fahrenheit":
1512
+ return TEMPERATURE_CONSTANTS.ABSOLUTE_ZERO_FAHRENHEIT;
1513
+ case "kelvin":
1514
+ return 0;
1515
+ case "rankine":
1516
+ return 0;
1517
+ default:
1518
+ return TEMPERATURE_CONSTANTS.ABSOLUTE_ZERO_CELSIUS;
1519
+ }
1520
+ }
1521
+ };
1522
+ var temperatureRole = new TemperatureRole();
1523
+
1524
+ // src/roles/angle/constants.ts
1525
+ var DEGREES_PER_RADIAN = 180 / Math.PI;
1526
+ var DEGREES_PER_MILLIRADIAN = 180 / (1e3 * Math.PI);
1527
+ var ANGLE_UNITS = {
1528
+ // Volta completa
1529
+ turn: {
1530
+ factor: 360,
1531
+ symbol: "tr",
1532
+ singular: "turn",
1533
+ plural: "turns"
1534
+ },
1535
+ // Base
1536
+ degree: {
1537
+ factor: 1,
1538
+ symbol: "\xB0",
1539
+ singular: "degree",
1540
+ plural: "degrees",
1541
+ noSpace: true
1542
+ // 45° não 45 °
1543
+ },
1544
+ // Subdivisões do grau
1545
+ arcminute: {
1546
+ factor: 1 / 60,
1547
+ // 0.016666...
1548
+ symbol: "\u2032",
1549
+ singular: "arcminute",
1550
+ plural: "arcminutes",
1551
+ noSpace: true
1552
+ // 30′ não 30 ′
1553
+ },
1554
+ arcsecond: {
1555
+ factor: 1 / 3600,
1556
+ // 0.000277...
1557
+ symbol: "\u2033",
1558
+ singular: "arcsecond",
1559
+ plural: "arcseconds",
1560
+ noSpace: true
1561
+ // 45″ não 45 ″
1562
+ },
1563
+ milliarcsecond: {
1564
+ factor: 1 / 36e5,
1565
+ // 2.777...e-7
1566
+ symbol: "mas",
1567
+ singular: "milliarcsecond",
1568
+ plural: "milliarcseconds"
1569
+ },
1570
+ // Radianos (unidade SI)
1571
+ radian: {
1572
+ factor: DEGREES_PER_RADIAN,
1573
+ // 180/π ≈ 57.2958
1574
+ symbol: "rad",
1575
+ singular: "radian",
1576
+ plural: "radians"
1577
+ },
1578
+ milliradian: {
1579
+ factor: DEGREES_PER_MILLIRADIAN,
1580
+ // 180/(1000π) ≈ 0.0573
1581
+ symbol: "mrad",
1582
+ singular: "milliradian",
1583
+ plural: "milliradians"
1584
+ },
1585
+ // Gradiano (gon)
1586
+ gradian: {
1587
+ factor: 0.9,
1588
+ // 360/400
1589
+ symbol: "gon",
1590
+ singular: "gradian",
1591
+ plural: "gradians"
1592
+ }
1593
+ };
1594
+ var ANGLE_ALIASES = {
1595
+ // turn
1596
+ tr: "turn",
1597
+ turn: "turn",
1598
+ turns: "turn",
1599
+ rev: "turn",
1600
+ revolution: "turn",
1601
+ revolutions: "turn",
1602
+ volta: "turn",
1603
+ voltas: "turn",
1604
+ // degree
1605
+ "\xB0": "degree",
1606
+ deg: "degree",
1607
+ degree: "degree",
1608
+ degrees: "degree",
1609
+ grau: "degree",
1610
+ graus: "degree",
1611
+ // arcminute
1612
+ "\u2032": "arcminute",
1613
+ "'": "arcminute",
1614
+ arcmin: "arcminute",
1615
+ arcminute: "arcminute",
1616
+ arcminutes: "arcminute",
1617
+ "minute of arc": "arcminute",
1618
+ "minutes of arc": "arcminute",
1619
+ // arcsecond
1620
+ "\u2033": "arcsecond",
1621
+ '"': "arcsecond",
1622
+ arcsec: "arcsecond",
1623
+ arcsecond: "arcsecond",
1624
+ arcseconds: "arcsecond",
1625
+ "second of arc": "arcsecond",
1626
+ "seconds of arc": "arcsecond",
1627
+ // milliarcsecond
1628
+ mas: "milliarcsecond",
1629
+ milliarcsecond: "milliarcsecond",
1630
+ milliarcseconds: "milliarcsecond",
1631
+ // radian
1632
+ rad: "radian",
1633
+ radian: "radian",
1634
+ radians: "radian",
1635
+ radiano: "radian",
1636
+ radianos: "radian",
1637
+ // milliradian
1638
+ mrad: "milliradian",
1639
+ milliradian: "milliradian",
1640
+ milliradians: "milliradian",
1641
+ milirradiano: "milliradian",
1642
+ milirredianos: "milliradian",
1643
+ // gradian
1644
+ gon: "gradian",
1645
+ grad: "gradian",
1646
+ grade: "gradian",
1647
+ gradian: "gradian",
1648
+ gradians: "gradian",
1649
+ grado: "gradian",
1650
+ grados: "gradian"
1651
+ };
1652
+ Object.fromEntries(
1653
+ Object.entries(ANGLE_UNITS).map(([unit, config]) => [
1654
+ unit,
1655
+ config.factor ?? 1
1656
+ ])
1657
+ );
1658
+
1659
+ // src/roles/angle/AngleRole.ts
1660
+ var AngleRole = class extends SimpleRole {
1661
+ constructor() {
1662
+ super(...arguments);
1663
+ this.name = "angle";
1664
+ this.base = "degree";
1665
+ this.units = ANGLE_UNITS;
1666
+ this.aliases = ANGLE_ALIASES;
1667
+ }
1668
+ // Ângulo pode ser negativo (rotação anti-horária) e maior que 360° (múltiplas voltas)
1669
+ // Não definimos min/max na validação
1670
+ };
1671
+ var angleRole = new AngleRole();
1672
+
1673
+ // src/roles/energy/constants.ts
1674
+ var ENERGY_UNITS = {
1675
+ // SI Units (todos exatos)
1676
+ gigajoule: {
1677
+ factor: 1e9,
1678
+ symbol: "GJ",
1679
+ singular: "gigajoule",
1680
+ plural: "gigajoules"
1681
+ },
1682
+ megajoule: {
1683
+ factor: 1e6,
1684
+ symbol: "MJ",
1685
+ singular: "megajoule",
1686
+ plural: "megajoules"
1687
+ },
1688
+ kilojoule: {
1689
+ factor: 1e3,
1690
+ symbol: "kJ",
1691
+ singular: "kilojoule",
1692
+ plural: "kilojoules"
1693
+ },
1694
+ joule: {
1695
+ factor: 1,
1696
+ symbol: "J",
1697
+ singular: "joule",
1698
+ plural: "joules"
1699
+ },
1700
+ millijoule: {
1701
+ factor: 1e-3,
1702
+ symbol: "mJ",
1703
+ singular: "millijoule",
1704
+ plural: "millijoules"
1705
+ },
1706
+ // Calories (International Table - IT)
1707
+ // 1 cal (IT) = 4.1868 J (exato por definição)
1708
+ calorie: {
1709
+ factor: 4.1868,
1710
+ symbol: "cal",
1711
+ singular: "calorie",
1712
+ plural: "calories"
1713
+ },
1714
+ // 1 kcal = 1000 cal = 4186.8 J (= 1 "food Calorie")
1715
+ kilocalorie: {
1716
+ factor: 4186.8,
1717
+ symbol: "kcal",
1718
+ singular: "kilocalorie",
1719
+ plural: "kilocalories"
1720
+ },
1721
+ // Watt-hour (exatos)
1722
+ // 1 Wh = 1 W × 3600 s = 3600 J
1723
+ watt_hour: {
1724
+ factor: 3600,
1725
+ symbol: "Wh",
1726
+ singular: "watt-hour",
1727
+ plural: "watt-hours"
1728
+ },
1729
+ kilowatt_hour: {
1730
+ factor: 36e5,
1731
+ symbol: "kWh",
1732
+ singular: "kilowatt-hour",
1733
+ plural: "kilowatt-hours"
1734
+ },
1735
+ megawatt_hour: {
1736
+ factor: 36e8,
1737
+ symbol: "MWh",
1738
+ singular: "megawatt-hour",
1739
+ plural: "megawatt-hours"
1740
+ },
1741
+ gigawatt_hour: {
1742
+ factor: 36e11,
1743
+ symbol: "GWh",
1744
+ singular: "gigawatt-hour",
1745
+ plural: "gigawatt-hours"
1746
+ },
1747
+ // BTU (International Table)
1748
+ // 1 BTU (IT) = 1055.05585262 J (definição)
1749
+ btu: {
1750
+ factor: 1055.05585262,
1751
+ symbol: "BTU",
1752
+ singular: "BTU",
1753
+ plural: "BTUs"
1754
+ },
1755
+ // 1 therm = 100,000 BTU (IT)
1756
+ therm: {
1757
+ factor: 105505585262e-3,
1758
+ symbol: "thm",
1759
+ singular: "therm",
1760
+ plural: "therms"
1761
+ },
1762
+ // Scientific
1763
+ // 1 eV = 1.602176634e-19 J (SI 2019, exato por definição)
1764
+ electronvolt: {
1765
+ factor: 1602176634e-28,
1766
+ symbol: "eV",
1767
+ singular: "electronvolt",
1768
+ plural: "electronvolts"
1769
+ },
1770
+ kiloelectronvolt: {
1771
+ factor: 1602176634e-25,
1772
+ symbol: "keV",
1773
+ singular: "kiloelectronvolt",
1774
+ plural: "kiloelectronvolts"
1775
+ },
1776
+ megaelectronvolt: {
1777
+ factor: 1602176634e-22,
1778
+ symbol: "MeV",
1779
+ singular: "megaelectronvolt",
1780
+ plural: "megaelectronvolts"
1781
+ },
1782
+ // 1 erg = 1e-7 J (CGS, exato)
1783
+ erg: {
1784
+ factor: 1e-7,
1785
+ symbol: "erg",
1786
+ singular: "erg",
1787
+ plural: "ergs"
1788
+ },
1789
+ // Mechanical
1790
+ // 1 ft⋅lbf = 1.3558179483314004 J (derivado de foot e pound-force)
1791
+ foot_pound: {
1792
+ factor: 1.3558179483314003,
1793
+ symbol: "ft\u22C5lbf",
1794
+ singular: "foot-pound",
1795
+ plural: "foot-pounds"
1796
+ }
1797
+ };
1798
+ var ENERGY_ALIASES = {
1799
+ // gigajoule
1800
+ gj: "gigajoule",
1801
+ gigajoule: "gigajoule",
1802
+ gigajoules: "gigajoule",
1803
+ // megajoule
1804
+ mj: "megajoule",
1805
+ megajoule: "megajoule",
1806
+ megajoules: "megajoule",
1807
+ // kilojoule
1808
+ kj: "kilojoule",
1809
+ kilojoule: "kilojoule",
1810
+ kilojoules: "kilojoule",
1811
+ // joule
1812
+ j: "joule",
1813
+ joule: "joule",
1814
+ joules: "joule",
1815
+ // millijoule
1816
+ "mj ": "millijoule",
1817
+ // note: conflict with megajoule, use context
1818
+ millijoule: "millijoule",
1819
+ millijoules: "millijoule",
1820
+ // calorie
1821
+ cal: "calorie",
1822
+ calorie: "calorie",
1823
+ calories: "calorie",
1824
+ caloria: "calorie",
1825
+ calorias: "calorie",
1826
+ // kilocalorie
1827
+ kcal: "kilocalorie",
1828
+ kilocalorie: "kilocalorie",
1829
+ kilocalories: "kilocalorie",
1830
+ quilocaloria: "kilocalorie",
1831
+ quilocalorias: "kilocalorie",
1832
+ caloria_alimentar: "kilocalorie",
1833
+ "food calorie": "kilocalorie",
1834
+ "food calories": "kilocalorie",
1835
+ Calorie: "kilocalorie",
1836
+ // big C = kcal
1837
+ Calories: "kilocalorie",
1838
+ Cal: "kilocalorie",
1839
+ // watt_hour
1840
+ wh: "watt_hour",
1841
+ "watt-hour": "watt_hour",
1842
+ "watt-hours": "watt_hour",
1843
+ "watt hour": "watt_hour",
1844
+ "watt hours": "watt_hour",
1845
+ // kilowatt_hour
1846
+ kwh: "kilowatt_hour",
1847
+ "kw-h": "kilowatt_hour",
1848
+ "kw h": "kilowatt_hour",
1849
+ "kilowatt-hour": "kilowatt_hour",
1850
+ "kilowatt-hours": "kilowatt_hour",
1851
+ "kilowatt hour": "kilowatt_hour",
1852
+ "kilowatt hours": "kilowatt_hour",
1853
+ // megawatt_hour
1854
+ mwh: "megawatt_hour",
1855
+ "megawatt-hour": "megawatt_hour",
1856
+ "megawatt-hours": "megawatt_hour",
1857
+ // gigawatt_hour
1858
+ gwh: "gigawatt_hour",
1859
+ "gigawatt-hour": "gigawatt_hour",
1860
+ "gigawatt-hours": "gigawatt_hour",
1861
+ // btu
1862
+ btu: "btu",
1863
+ btus: "btu",
1864
+ "british thermal unit": "btu",
1865
+ "british thermal units": "btu",
1866
+ // therm
1867
+ thm: "therm",
1868
+ therm: "therm",
1869
+ therms: "therm",
1870
+ // electronvolt
1871
+ ev: "electronvolt",
1872
+ electronvolt: "electronvolt",
1873
+ electronvolts: "electronvolt",
1874
+ "electron-volt": "electronvolt",
1875
+ "electron-volts": "electronvolt",
1876
+ "electron volt": "electronvolt",
1877
+ "electron volts": "electronvolt",
1878
+ // kiloelectronvolt
1879
+ kev: "kiloelectronvolt",
1880
+ kiloelectronvolt: "kiloelectronvolt",
1881
+ kiloelectronvolts: "kiloelectronvolt",
1882
+ // megaelectronvolt
1883
+ mev: "megaelectronvolt",
1884
+ megaelectronvolt: "megaelectronvolt",
1885
+ megaelectronvolts: "megaelectronvolt",
1886
+ // erg
1887
+ erg: "erg",
1888
+ ergs: "erg",
1889
+ // foot_pound
1890
+ "ft-lb": "foot_pound",
1891
+ "ft-lbf": "foot_pound",
1892
+ "ft\u22C5lbf": "foot_pound",
1893
+ "ft\xB7lbf": "foot_pound",
1894
+ "foot-pound": "foot_pound",
1895
+ "foot-pounds": "foot_pound",
1896
+ "foot pound": "foot_pound",
1897
+ "foot pounds": "foot_pound"
1898
+ };
1899
+ Object.fromEntries(
1900
+ Object.entries(ENERGY_UNITS).map(([unit, config]) => [
1901
+ unit,
1902
+ config.factor ?? 1
1903
+ ])
1904
+ );
1905
+
1906
+ // src/roles/energy/EnergyRole.ts
1907
+ var EnergyRole = class extends SimpleRole {
1908
+ constructor() {
1909
+ super(...arguments);
1910
+ this.name = "energy";
1911
+ this.base = "joule";
1912
+ this.units = ENERGY_UNITS;
1913
+ this.aliases = ENERGY_ALIASES;
1914
+ this.validation = {
1915
+ min: 0,
1916
+ minError: "Energy cannot be negative"
1917
+ };
1918
+ }
1919
+ };
1920
+ var energyRole = new EnergyRole();
1921
+
1922
+ // src/roles/power/constants.ts
1923
+ var POWER_UNITS = {
1924
+ // SI Units (todos exatos)
1925
+ gigawatt: {
1926
+ factor: 1e9,
1927
+ symbol: "GW",
1928
+ singular: "gigawatt",
1929
+ plural: "gigawatts"
1930
+ },
1931
+ megawatt: {
1932
+ factor: 1e6,
1933
+ symbol: "MW",
1934
+ singular: "megawatt",
1935
+ plural: "megawatts"
1936
+ },
1937
+ kilowatt: {
1938
+ factor: 1e3,
1939
+ symbol: "kW",
1940
+ singular: "kilowatt",
1941
+ plural: "kilowatts"
1942
+ },
1943
+ watt: {
1944
+ factor: 1,
1945
+ symbol: "W",
1946
+ singular: "watt",
1947
+ plural: "watts"
1948
+ },
1949
+ milliwatt: {
1950
+ factor: 1e-3,
1951
+ symbol: "mW",
1952
+ singular: "milliwatt",
1953
+ plural: "milliwatts"
1954
+ },
1955
+ microwatt: {
1956
+ factor: 1e-6,
1957
+ symbol: "\u03BCW",
1958
+ singular: "microwatt",
1959
+ plural: "microwatts"
1960
+ },
1961
+ // Horsepower variants
1962
+ // Mechanical (imperial): 550 ft⋅lbf/s
1963
+ // = 550 × 0.3048 m × 4.4482216152605 N / s = 745.69987158227 W
1964
+ horsepower_mechanical: {
1965
+ factor: 745.69987158227,
1966
+ symbol: "hp",
1967
+ singular: "horsepower",
1968
+ plural: "horsepower"
1969
+ },
1970
+ // Metric (PS, CV, pk): 75 kgf⋅m/s = 75 × 9.80665 W = 735.49875 W (exato)
1971
+ horsepower_metric: {
1972
+ factor: 735.49875,
1973
+ symbol: "PS",
1974
+ singular: "metric horsepower",
1975
+ plural: "metric horsepower"
1976
+ },
1977
+ // Electrical: 746 W (exato por definição)
1978
+ horsepower_electric: {
1979
+ factor: 746,
1980
+ symbol: "hp(E)",
1981
+ singular: "electric horsepower",
1982
+ plural: "electric horsepower"
1983
+ },
1984
+ // Boiler: 33,475 BTU/h = 9809.5 W
1985
+ horsepower_boiler: {
1986
+ factor: 9809.5,
1987
+ symbol: "hp(S)",
1988
+ singular: "boiler horsepower",
1989
+ plural: "boiler horsepower"
1990
+ },
1991
+ // BTU-based
1992
+ // 1 BTU/h = 1055.05585262 J / 3600 s = 0.29307107017222 W
1993
+ btu_per_hour: {
1994
+ factor: 0.29307107017222,
1995
+ symbol: "BTU/h",
1996
+ singular: "BTU per hour",
1997
+ plural: "BTUs per hour"
1998
+ },
1999
+ // 1 BTU/s = 1055.05585262 W
2000
+ btu_per_second: {
2001
+ factor: 1055.05585262,
2002
+ symbol: "BTU/s",
2003
+ singular: "BTU per second",
2004
+ plural: "BTUs per second"
2005
+ },
2006
+ // Other
2007
+ // 1 ton of refrigeration = 12000 BTU/h = 3516.8528420667 W
2008
+ ton_of_refrigeration: {
2009
+ factor: 3516.8528420667,
2010
+ symbol: "TR",
2011
+ singular: "ton of refrigeration",
2012
+ plural: "tons of refrigeration"
2013
+ },
2014
+ // 1 ft⋅lbf/s = 1.3558179483314004 W
2015
+ foot_pound_per_second: {
2016
+ factor: 1.3558179483314003,
2017
+ symbol: "ft\u22C5lbf/s",
2018
+ singular: "foot-pound per second",
2019
+ plural: "foot-pounds per second"
2020
+ },
2021
+ // 1 cal/s = 4.1868 W
2022
+ calorie_per_second: {
2023
+ factor: 4.1868,
2024
+ symbol: "cal/s",
2025
+ singular: "calorie per second",
2026
+ plural: "calories per second"
2027
+ },
2028
+ // 1 kcal/h = 4186.8 / 3600 = 1.163 W
2029
+ kilocalorie_per_hour: {
2030
+ factor: 1.163,
2031
+ symbol: "kcal/h",
2032
+ singular: "kilocalorie per hour",
2033
+ plural: "kilocalories per hour"
2034
+ }
2035
+ };
2036
+ var POWER_ALIASES = {
2037
+ // gigawatt
2038
+ gw: "gigawatt",
2039
+ gigawatt: "gigawatt",
2040
+ gigawatts: "gigawatt",
2041
+ // megawatt
2042
+ mw: "megawatt",
2043
+ megawatt: "megawatt",
2044
+ megawatts: "megawatt",
2045
+ // kilowatt
2046
+ kw: "kilowatt",
2047
+ kilowatt: "kilowatt",
2048
+ kilowatts: "kilowatt",
2049
+ // watt
2050
+ w: "watt",
2051
+ watt: "watt",
2052
+ watts: "watt",
2053
+ // milliwatt
2054
+ milliwatt: "milliwatt",
2055
+ milliwatts: "milliwatt",
2056
+ // microwatt
2057
+ \u03BCw: "microwatt",
2058
+ uw: "microwatt",
2059
+ microwatt: "microwatt",
2060
+ microwatts: "microwatt",
2061
+ // horsepower_mechanical
2062
+ hp: "horsepower_mechanical",
2063
+ horsepower: "horsepower_mechanical",
2064
+ "horse power": "horsepower_mechanical",
2065
+ "cavalo-vapor": "horsepower_mechanical",
2066
+ // horsepower_metric
2067
+ ps: "horsepower_metric",
2068
+ cv: "horsepower_metric",
2069
+ pk: "horsepower_metric",
2070
+ "metric horsepower": "horsepower_metric",
2071
+ "cavalo-vapor m\xE9trico": "horsepower_metric",
2072
+ pferdest\u00E4rke: "horsepower_metric",
2073
+ // horsepower_electric
2074
+ "hp(e)": "horsepower_electric",
2075
+ "electric horsepower": "horsepower_electric",
2076
+ "electrical horsepower": "horsepower_electric",
2077
+ // horsepower_boiler
2078
+ "hp(s)": "horsepower_boiler",
2079
+ "boiler horsepower": "horsepower_boiler",
2080
+ bhp: "horsepower_boiler",
2081
+ // btu_per_hour
2082
+ "btu/h": "btu_per_hour",
2083
+ "btu/hr": "btu_per_hour",
2084
+ btuh: "btu_per_hour",
2085
+ "btu per hour": "btu_per_hour",
2086
+ "btus per hour": "btu_per_hour",
2087
+ // btu_per_second
2088
+ "btu/s": "btu_per_second",
2089
+ "btu per second": "btu_per_second",
2090
+ // ton_of_refrigeration
2091
+ tr: "ton_of_refrigeration",
2092
+ rt: "ton_of_refrigeration",
2093
+ "ton of refrigeration": "ton_of_refrigeration",
2094
+ "tons of refrigeration": "ton_of_refrigeration",
2095
+ "refrigeration ton": "ton_of_refrigeration",
2096
+ "refrigeration tons": "ton_of_refrigeration",
2097
+ // foot_pound_per_second
2098
+ "ft-lb/s": "foot_pound_per_second",
2099
+ "ft-lbf/s": "foot_pound_per_second",
2100
+ "ft\u22C5lbf/s": "foot_pound_per_second",
2101
+ "ft\xB7lbf/s": "foot_pound_per_second",
2102
+ "foot-pound per second": "foot_pound_per_second",
2103
+ "foot-pounds per second": "foot_pound_per_second",
2104
+ // calorie_per_second
2105
+ "cal/s": "calorie_per_second",
2106
+ "calorie per second": "calorie_per_second",
2107
+ "calories per second": "calorie_per_second",
2108
+ // kilocalorie_per_hour
2109
+ "kcal/h": "kilocalorie_per_hour",
2110
+ "kcal/hr": "kilocalorie_per_hour",
2111
+ "kilocalorie per hour": "kilocalorie_per_hour",
2112
+ "kilocalories per hour": "kilocalorie_per_hour"
2113
+ };
2114
+ Object.fromEntries(
2115
+ Object.entries(POWER_UNITS).map(([unit, config]) => [
2116
+ unit,
2117
+ config.factor ?? 1
2118
+ ])
2119
+ );
2120
+
2121
+ // src/roles/power/PowerRole.ts
2122
+ var PowerRole = class extends SimpleRole {
2123
+ constructor() {
2124
+ super(...arguments);
2125
+ this.name = "power";
2126
+ this.base = "watt";
2127
+ this.units = POWER_UNITS;
2128
+ this.aliases = POWER_ALIASES;
2129
+ this.validation = {
2130
+ min: 0,
2131
+ minError: "Power cannot be negative"
2132
+ };
2133
+ }
2134
+ };
2135
+ var powerRole = new PowerRole();
2136
+
2137
+ // src/roles/speed/constants.ts
2138
+ var SPEED_UNITS = {
2139
+ // SI Units
2140
+ meter_per_second: {
2141
+ factor: 1,
2142
+ symbol: "m/s",
2143
+ singular: "meter per second",
2144
+ plural: "meters per second"
2145
+ },
2146
+ kilometer_per_hour: {
2147
+ factor: 1e3 / 3600,
2148
+ // 0.277777...
2149
+ symbol: "km/h",
2150
+ singular: "kilometer per hour",
2151
+ plural: "kilometers per hour"
2152
+ },
2153
+ // Imperial/US (exatos desde 1959)
2154
+ mile_per_hour: {
2155
+ factor: 0.44704,
2156
+ // 1609.344 / 3600 (exato)
2157
+ symbol: "mph",
2158
+ singular: "mile per hour",
2159
+ plural: "miles per hour"
2160
+ },
2161
+ foot_per_second: {
2162
+ factor: 0.3048,
2163
+ // exato
2164
+ symbol: "ft/s",
2165
+ singular: "foot per second",
2166
+ plural: "feet per second"
2167
+ },
2168
+ // Nautical (exato)
2169
+ knot: {
2170
+ factor: 1852 / 3600,
2171
+ // 0.514444...
2172
+ symbol: "kn",
2173
+ singular: "knot",
2174
+ plural: "knots"
2175
+ },
2176
+ // Scientific
2177
+ mach: {
2178
+ factor: 340.29,
2179
+ // velocidade do som ao nível do mar, 15°C (aproximado)
2180
+ symbol: "Ma",
2181
+ singular: "mach",
2182
+ plural: "mach"
2183
+ },
2184
+ speed_of_light: {
2185
+ factor: 299792458,
2186
+ // exato por definição SI
2187
+ symbol: "c",
2188
+ singular: "speed of light",
2189
+ plural: "speed of light"
2190
+ }
2191
+ };
2192
+ var SPEED_ALIASES = {
2193
+ // meter_per_second
2194
+ "m/s": "meter_per_second",
2195
+ mps: "meter_per_second",
2196
+ "meter per second": "meter_per_second",
2197
+ "meters per second": "meter_per_second",
2198
+ "metro por segundo": "meter_per_second",
2199
+ "metros por segundo": "meter_per_second",
2200
+ // kilometer_per_hour
2201
+ "km/h": "kilometer_per_hour",
2202
+ kmh: "kilometer_per_hour",
2203
+ kph: "kilometer_per_hour",
2204
+ "kilometer per hour": "kilometer_per_hour",
2205
+ "kilometers per hour": "kilometer_per_hour",
2206
+ "kilometre per hour": "kilometer_per_hour",
2207
+ "kilometres per hour": "kilometer_per_hour",
2208
+ "quil\xF4metro por hora": "kilometer_per_hour",
2209
+ "quil\xF4metros por hora": "kilometer_per_hour",
2210
+ // mile_per_hour
2211
+ mph: "mile_per_hour",
2212
+ "mi/h": "mile_per_hour",
2213
+ "mile per hour": "mile_per_hour",
2214
+ "miles per hour": "mile_per_hour",
2215
+ "milha por hora": "mile_per_hour",
2216
+ "milhas por hora": "mile_per_hour",
2217
+ // foot_per_second
2218
+ "ft/s": "foot_per_second",
2219
+ fps: "foot_per_second",
2220
+ "foot per second": "foot_per_second",
2221
+ "feet per second": "foot_per_second",
2222
+ "p\xE9 por segundo": "foot_per_second",
2223
+ "p\xE9s por segundo": "foot_per_second",
2224
+ // knot
2225
+ kn: "knot",
2226
+ kt: "knot",
2227
+ kts: "knot",
2228
+ knot: "knot",
2229
+ knots: "knot",
2230
+ n\u00F3: "knot",
2231
+ n\u00F3s: "knot",
2232
+ // mach
2233
+ ma: "mach",
2234
+ mach: "mach",
2235
+ // speed_of_light
2236
+ c: "speed_of_light",
2237
+ "speed of light": "speed_of_light",
2238
+ "velocidade da luz": "speed_of_light"
2239
+ };
2240
+ Object.fromEntries(
2241
+ Object.entries(SPEED_UNITS).map(([unit, config]) => [
2242
+ unit,
2243
+ config.factor ?? 1
2244
+ ])
2245
+ );
2246
+
2247
+ // src/roles/speed/SpeedRole.ts
2248
+ var SpeedRole = class extends SimpleRole {
2249
+ constructor() {
2250
+ super(...arguments);
2251
+ this.name = "speed";
2252
+ this.base = "meter_per_second";
2253
+ this.units = SPEED_UNITS;
2254
+ this.aliases = SPEED_ALIASES;
2255
+ }
2256
+ // Speed pode ser negativo (movimento em direção oposta)
2257
+ };
2258
+ var speedRole = new SpeedRole();
2259
+
2260
+ // src/roles/date/variants/IsoVariant.ts
2261
+ var ISO_DATE_ONLY_REGEX = /^\d{4}-\d{2}-\d{2}$/;
2262
+ var ISO_DATE_TIME_REGEX = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/;
2263
+ var IsoVariant = class extends BaseVariant {
2264
+ constructor() {
2265
+ super(...arguments);
2266
+ this.name = "iso";
2267
+ this.type = "string";
2268
+ }
2269
+ // ===========================================================================
2270
+ // CONVERT
2271
+ // ===========================================================================
2272
+ toBase(value) {
2273
+ const date = new Date(value);
2274
+ if (isNaN(date.getTime())) {
2275
+ throw new Error(`Invalid ISO date: ${value}`);
2276
+ }
2277
+ return date.getTime();
2278
+ }
2279
+ fromBase(base) {
2280
+ const date = new Date(base);
2281
+ if (isNaN(date.getTime())) {
2282
+ throw new Error(`Invalid timestamp: ${base}`);
2283
+ }
2284
+ return date.toISOString();
2285
+ }
2286
+ // ===========================================================================
2287
+ // CAST
2288
+ // ===========================================================================
2289
+ cast(input) {
2290
+ if (typeof input === "string") {
2291
+ const trimmed = input.trim();
2292
+ const date = new Date(trimmed);
2293
+ if (!isNaN(date.getTime())) {
2294
+ return date.toISOString();
2295
+ }
2296
+ return null;
2297
+ }
2298
+ if (typeof input === "number") {
2299
+ if (!Number.isFinite(input)) return null;
2300
+ const date = new Date(input);
2301
+ if (!isNaN(date.getTime())) {
2302
+ return date.toISOString();
2303
+ }
2304
+ return null;
2305
+ }
2306
+ if (input instanceof Date) {
2307
+ if (isNaN(input.getTime())) return null;
2308
+ return input.toISOString();
2309
+ }
2310
+ return null;
2311
+ }
2312
+ // ===========================================================================
2313
+ // VALIDATE
2314
+ // ===========================================================================
2315
+ validate(value) {
2316
+ const errors = [];
2317
+ if (typeof value !== "string") {
2318
+ errors.push("ISO date must be a string");
2319
+ return { valid: false, errors };
2320
+ }
2321
+ const trimmed = value.trim();
2322
+ if (!ISO_DATE_TIME_REGEX.test(trimmed) && !ISO_DATE_ONLY_REGEX.test(trimmed)) {
2323
+ errors.push("Invalid ISO 8601 format");
2324
+ return { valid: false, errors };
2325
+ }
2326
+ const date = new Date(trimmed);
2327
+ if (isNaN(date.getTime())) {
2328
+ errors.push("Invalid date value");
2329
+ return { valid: false, errors };
2330
+ }
2331
+ return { valid: true, errors: [] };
2332
+ }
2333
+ // ===========================================================================
2334
+ // FORMAT
2335
+ // ===========================================================================
2336
+ format(value, options) {
2337
+ const opts = options;
2338
+ const date = new Date(value);
2339
+ if (isNaN(date.getTime())) {
2340
+ return "Invalid Date";
2341
+ }
2342
+ if (!opts || !opts.dateStyle && !opts.timeStyle && !opts.dateOnly && !opts.timeOnly) {
2343
+ return value;
2344
+ }
2345
+ const locale = opts.locale || "en-US";
2346
+ const formatOptions = {};
2347
+ if (opts.timeZone) {
2348
+ formatOptions.timeZone = opts.timeZone;
2349
+ }
2350
+ if (opts.dateOnly) {
2351
+ formatOptions.dateStyle = opts.dateStyle || "medium";
2352
+ } else if (opts.timeOnly) {
2353
+ formatOptions.timeStyle = opts.timeStyle || "medium";
2354
+ } else {
2355
+ if (opts.dateStyle) formatOptions.dateStyle = opts.dateStyle;
2356
+ if (opts.timeStyle) formatOptions.timeStyle = opts.timeStyle;
2357
+ }
2358
+ return new Intl.DateTimeFormat(locale, formatOptions).format(date);
2359
+ }
2360
+ };
2361
+
2362
+ // src/roles/date/variants/TimestampVariant.ts
2363
+ var TimestampVariant = class extends BaseVariant {
2364
+ constructor() {
2365
+ super(...arguments);
2366
+ this.name = "timestamp";
2367
+ this.type = "number";
2368
+ }
2369
+ // ===========================================================================
2370
+ // CONVERT
2371
+ // ===========================================================================
2372
+ toBase(value) {
2373
+ return value;
2374
+ }
2375
+ fromBase(base) {
2376
+ return base;
2377
+ }
2378
+ // ===========================================================================
2379
+ // CAST
2380
+ // ===========================================================================
2381
+ cast(input) {
2382
+ if (typeof input === "number") {
2383
+ if (!Number.isFinite(input)) return null;
2384
+ return input;
2385
+ }
2386
+ if (typeof input === "string") {
2387
+ const trimmed = input.trim();
2388
+ if (/^-?\d+$/.test(trimmed)) {
2389
+ const num = parseInt(trimmed, 10);
2390
+ if (Number.isFinite(num)) return num;
2391
+ }
2392
+ const date = new Date(trimmed);
2393
+ if (!isNaN(date.getTime())) {
2394
+ return date.getTime();
2395
+ }
2396
+ return null;
2397
+ }
2398
+ if (input instanceof Date) {
2399
+ const ts = input.getTime();
2400
+ return isNaN(ts) ? null : ts;
2401
+ }
2402
+ return null;
2403
+ }
2404
+ // ===========================================================================
2405
+ // VALIDATE
2406
+ // ===========================================================================
2407
+ validate(value) {
2408
+ const errors = [];
2409
+ if (typeof value !== "number") {
2410
+ errors.push("Timestamp must be a number");
2411
+ return { valid: false, errors };
2412
+ }
2413
+ if (!Number.isFinite(value)) {
2414
+ errors.push("Timestamp must be a finite number");
2415
+ return { valid: false, errors };
2416
+ }
2417
+ if (!Number.isInteger(value)) {
2418
+ errors.push("Timestamp must be an integer");
2419
+ return { valid: false, errors };
2420
+ }
2421
+ return { valid: true, errors: [] };
2422
+ }
2423
+ // ===========================================================================
2424
+ // FORMAT
2425
+ // ===========================================================================
2426
+ format(value, options) {
2427
+ const opts = options;
2428
+ const date = new Date(value);
2429
+ if (isNaN(date.getTime())) {
2430
+ return "Invalid Date";
2431
+ }
2432
+ if (!opts || !opts.dateStyle && !opts.timeStyle && !opts.dateOnly && !opts.timeOnly) {
2433
+ return String(value);
2434
+ }
2435
+ const locale = opts.locale || "en-US";
2436
+ const formatOptions = {};
2437
+ if (opts.timeZone) {
2438
+ formatOptions.timeZone = opts.timeZone;
2439
+ }
2440
+ if (opts.dateOnly) {
2441
+ formatOptions.dateStyle = opts.dateStyle || "medium";
2442
+ } else if (opts.timeOnly) {
2443
+ formatOptions.timeStyle = opts.timeStyle || "medium";
2444
+ } else {
2445
+ if (opts.dateStyle) formatOptions.dateStyle = opts.dateStyle;
2446
+ if (opts.timeStyle) formatOptions.timeStyle = opts.timeStyle;
2447
+ }
2448
+ return new Intl.DateTimeFormat(locale, formatOptions).format(date);
2449
+ }
2450
+ };
2451
+
2452
+ // src/roles/date/variants/EpochVariant.ts
2453
+ var EpochVariant = class extends BaseVariant {
2454
+ constructor() {
2455
+ super(...arguments);
2456
+ this.name = "epoch";
2457
+ this.type = "number";
2458
+ }
2459
+ // ===========================================================================
2460
+ // CONVERT
2461
+ // ===========================================================================
2462
+ toBase(value) {
2463
+ return value * 1e3;
2464
+ }
2465
+ fromBase(base) {
2466
+ return Math.floor(base / 1e3);
2467
+ }
2468
+ // ===========================================================================
2469
+ // CAST
2470
+ // ===========================================================================
2471
+ cast(input) {
2472
+ if (typeof input === "number") {
2473
+ if (!Number.isFinite(input)) return null;
2474
+ if (Math.abs(input) > 1e11) {
2475
+ return Math.floor(input / 1e3);
2476
+ }
2477
+ return Math.floor(input);
2478
+ }
2479
+ if (typeof input === "string") {
2480
+ const trimmed = input.trim();
2481
+ if (/^-?\d+$/.test(trimmed)) {
2482
+ const num = parseInt(trimmed, 10);
2483
+ if (Number.isFinite(num)) {
2484
+ if (Math.abs(num) > 1e11) {
2485
+ return Math.floor(num / 1e3);
2486
+ }
2487
+ return num;
2488
+ }
2489
+ }
2490
+ const date = new Date(trimmed);
2491
+ if (!isNaN(date.getTime())) {
2492
+ return Math.floor(date.getTime() / 1e3);
2493
+ }
2494
+ return null;
2495
+ }
2496
+ if (input instanceof Date) {
2497
+ const ts = input.getTime();
2498
+ return isNaN(ts) ? null : Math.floor(ts / 1e3);
2499
+ }
2500
+ return null;
2501
+ }
2502
+ // ===========================================================================
2503
+ // VALIDATE
2504
+ // ===========================================================================
2505
+ validate(value) {
2506
+ const errors = [];
2507
+ if (typeof value !== "number") {
2508
+ errors.push("Epoch must be a number");
2509
+ return { valid: false, errors };
2510
+ }
2511
+ if (!Number.isFinite(value)) {
2512
+ errors.push("Epoch must be a finite number");
2513
+ return { valid: false, errors };
2514
+ }
2515
+ if (!Number.isInteger(value)) {
2516
+ errors.push("Epoch must be an integer");
2517
+ return { valid: false, errors };
2518
+ }
2519
+ return { valid: true, errors: [] };
2520
+ }
2521
+ // ===========================================================================
2522
+ // FORMAT
2523
+ // ===========================================================================
2524
+ format(value, options) {
2525
+ const opts = options;
2526
+ const date = new Date(value * 1e3);
2527
+ if (isNaN(date.getTime())) {
2528
+ return "Invalid Date";
2529
+ }
2530
+ if (!opts || !opts.dateStyle && !opts.timeStyle && !opts.dateOnly && !opts.timeOnly) {
2531
+ return String(value);
2532
+ }
2533
+ const locale = opts.locale || "en-US";
2534
+ const formatOptions = {};
2535
+ if (opts.timeZone) {
2536
+ formatOptions.timeZone = opts.timeZone;
2537
+ }
2538
+ if (opts.dateOnly) {
2539
+ formatOptions.dateStyle = opts.dateStyle || "medium";
2540
+ } else if (opts.timeOnly) {
2541
+ formatOptions.timeStyle = opts.timeStyle || "medium";
2542
+ } else {
2543
+ if (opts.dateStyle) formatOptions.dateStyle = opts.dateStyle;
2544
+ if (opts.timeStyle) formatOptions.timeStyle = opts.timeStyle;
2545
+ }
2546
+ return new Intl.DateTimeFormat(locale, formatOptions).format(date);
2547
+ }
2548
+ };
2549
+
2550
+ // src/roles/date/DateRole.ts
2551
+ var DateRole = class extends ComplexRole {
2552
+ constructor() {
2553
+ super(...arguments);
2554
+ this.name = "date";
2555
+ this.base = "timestamp";
2556
+ }
2557
+ createVariants() {
2558
+ return {
2559
+ iso: new IsoVariant(),
2560
+ timestamp: new TimestampVariant(),
2561
+ epoch: new EpochVariant()
2562
+ };
2563
+ }
2564
+ /**
2565
+ * Override format to accept DateFormatOptions
2566
+ */
2567
+ format(variant, value, options) {
2568
+ return super.format(variant, value, options);
2569
+ }
2570
+ };
2571
+ var dateRole = new DateRole();
2572
+
2573
+ // src/roles/volume/constants.ts
2574
+ var VOLUME_UNITS = {
2575
+ // SI / Métrico
2576
+ cubic_meter: {
2577
+ factor: 1e3,
2578
+ symbol: "m\xB3",
2579
+ singular: "cubic meter",
2580
+ plural: "cubic meters"
2581
+ },
2582
+ cubic_decimeter: {
2583
+ factor: 1,
2584
+ symbol: "dm\xB3",
2585
+ singular: "cubic decimeter",
2586
+ plural: "cubic decimeters"
2587
+ },
2588
+ cubic_centimeter: {
2589
+ factor: 1e-3,
2590
+ symbol: "cm\xB3",
2591
+ singular: "cubic centimeter",
2592
+ plural: "cubic centimeters"
2593
+ },
2594
+ cubic_millimeter: {
2595
+ factor: 1e-6,
2596
+ symbol: "mm\xB3",
2597
+ singular: "cubic millimeter",
2598
+ plural: "cubic millimeters"
2599
+ },
2600
+ hectoliter: {
2601
+ factor: 100,
2602
+ symbol: "hL",
2603
+ singular: "hectoliter",
2604
+ plural: "hectoliters"
2605
+ },
2606
+ decaliter: {
2607
+ factor: 10,
2608
+ symbol: "daL",
2609
+ singular: "decaliter",
2610
+ plural: "decaliters"
2611
+ },
2612
+ liter: {
2613
+ factor: 1,
2614
+ symbol: "L",
2615
+ singular: "liter",
2616
+ plural: "liters"
2617
+ },
2618
+ deciliter: {
2619
+ factor: 0.1,
2620
+ symbol: "dL",
2621
+ singular: "deciliter",
2622
+ plural: "deciliters"
2623
+ },
2624
+ centiliter: {
2625
+ factor: 0.01,
2626
+ symbol: "cL",
2627
+ singular: "centiliter",
2628
+ plural: "centiliters"
2629
+ },
2630
+ milliliter: {
2631
+ factor: 1e-3,
2632
+ symbol: "mL",
2633
+ singular: "milliliter",
2634
+ plural: "milliliters"
2635
+ },
2636
+ microliter: {
2637
+ factor: 1e-6,
2638
+ symbol: "\u03BCL",
2639
+ singular: "microliter",
2640
+ plural: "microliters"
2641
+ },
2642
+ // US Customary (baseado em 1 gallon = 231 in³ = 3.785411784 L)
2643
+ gallon_us: {
2644
+ factor: 3.785411784,
2645
+ symbol: "gal",
2646
+ singular: "gallon",
2647
+ plural: "gallons"
2648
+ },
2649
+ quart_us: {
2650
+ factor: 0.946352946,
2651
+ // gallon/4
2652
+ symbol: "qt",
2653
+ singular: "quart",
2654
+ plural: "quarts"
2655
+ },
2656
+ pint_us: {
2657
+ factor: 0.473176473,
2658
+ // gallon/8
2659
+ symbol: "pt",
2660
+ singular: "pint",
2661
+ plural: "pints"
2662
+ },
2663
+ cup_us: {
2664
+ factor: 0.2365882365,
2665
+ // gallon/16
2666
+ symbol: "cup",
2667
+ singular: "cup",
2668
+ plural: "cups"
2669
+ },
2670
+ fluid_ounce_us: {
2671
+ factor: 0.0295735295625,
2672
+ // gallon/128
2673
+ symbol: "fl oz",
2674
+ singular: "fluid ounce",
2675
+ plural: "fluid ounces"
2676
+ },
2677
+ tablespoon_us: {
2678
+ factor: 0.01478676478125,
2679
+ // fl oz/2
2680
+ symbol: "tbsp",
2681
+ singular: "tablespoon",
2682
+ plural: "tablespoons"
2683
+ },
2684
+ teaspoon_us: {
2685
+ factor: 0.00492892159375,
2686
+ // tbsp/3
2687
+ symbol: "tsp",
2688
+ singular: "teaspoon",
2689
+ plural: "teaspoons"
2690
+ },
2691
+ // Imperial UK (1 gallon UK = 4.54609 L exato)
2692
+ gallon_uk: {
2693
+ factor: 4.54609,
2694
+ symbol: "gal (UK)",
2695
+ singular: "gallon (UK)",
2696
+ plural: "gallons (UK)"
2697
+ },
2698
+ quart_uk: {
2699
+ factor: 1.1365225,
2700
+ // gallon/4
2701
+ symbol: "qt (UK)",
2702
+ singular: "quart (UK)",
2703
+ plural: "quarts (UK)"
2704
+ },
2705
+ pint_uk: {
2706
+ factor: 0.56826125,
2707
+ // gallon/8
2708
+ symbol: "pt (UK)",
2709
+ singular: "pint (UK)",
2710
+ plural: "pints (UK)"
2711
+ },
2712
+ fluid_ounce_uk: {
2713
+ factor: 0.0284130625,
2714
+ // gallon/160
2715
+ symbol: "fl oz (UK)",
2716
+ singular: "fluid ounce (UK)",
2717
+ plural: "fluid ounces (UK)"
2718
+ },
2719
+ // Other
2720
+ barrel_oil: {
2721
+ factor: 158.987294928,
2722
+ // 42 US gallons (petroleum)
2723
+ symbol: "bbl",
2724
+ singular: "barrel",
2725
+ plural: "barrels"
2726
+ },
2727
+ cubic_inch: {
2728
+ factor: 0.016387064,
2729
+ // (0.0254)³ × 1000
2730
+ symbol: "in\xB3",
2731
+ singular: "cubic inch",
2732
+ plural: "cubic inches"
2733
+ },
2734
+ cubic_foot: {
2735
+ factor: 28.316846592,
2736
+ // (0.3048)³ × 1000
2737
+ symbol: "ft\xB3",
2738
+ singular: "cubic foot",
2739
+ plural: "cubic feet"
2740
+ },
2741
+ cubic_yard: {
2742
+ factor: 764.554857984,
2743
+ // (0.9144)³ × 1000
2744
+ symbol: "yd\xB3",
2745
+ singular: "cubic yard",
2746
+ plural: "cubic yards"
2747
+ }
2748
+ };
2749
+ var VOLUME_ALIASES = {
2750
+ // cubic_meter
2751
+ "m\xB3": "cubic_meter",
2752
+ m3: "cubic_meter",
2753
+ "cubic meter": "cubic_meter",
2754
+ "cubic meters": "cubic_meter",
2755
+ "metro c\xFAbico": "cubic_meter",
2756
+ "metros c\xFAbicos": "cubic_meter",
2757
+ // cubic_decimeter
2758
+ "dm\xB3": "cubic_decimeter",
2759
+ dm3: "cubic_decimeter",
2760
+ "cubic decimeter": "cubic_decimeter",
2761
+ "cubic decimeters": "cubic_decimeter",
2762
+ // cubic_centimeter
2763
+ "cm\xB3": "cubic_centimeter",
2764
+ cm3: "cubic_centimeter",
2765
+ cc: "cubic_centimeter",
2766
+ "cubic centimeter": "cubic_centimeter",
2767
+ "cubic centimeters": "cubic_centimeter",
2768
+ // cubic_millimeter
2769
+ "mm\xB3": "cubic_millimeter",
2770
+ mm3: "cubic_millimeter",
2771
+ "cubic millimeter": "cubic_millimeter",
2772
+ "cubic millimeters": "cubic_millimeter",
2773
+ // hectoliter
2774
+ hL: "hectoliter",
2775
+ hl: "hectoliter",
2776
+ hectoliter: "hectoliter",
2777
+ hectoliters: "hectoliter",
2778
+ hectolitre: "hectoliter",
2779
+ hectolitres: "hectoliter",
2780
+ // decaliter
2781
+ daL: "decaliter",
2782
+ dal: "decaliter",
2783
+ decaliter: "decaliter",
2784
+ decaliters: "decaliter",
2785
+ // liter
2786
+ L: "liter",
2787
+ l: "liter",
2788
+ liter: "liter",
2789
+ liters: "liter",
2790
+ litre: "liter",
2791
+ litres: "liter",
2792
+ litro: "liter",
2793
+ litros: "liter",
2794
+ // deciliter
2795
+ dL: "deciliter",
2796
+ dl: "deciliter",
2797
+ deciliter: "deciliter",
2798
+ deciliters: "deciliter",
2799
+ // centiliter
2800
+ cL: "centiliter",
2801
+ cl: "centiliter",
2802
+ centiliter: "centiliter",
2803
+ centiliters: "centiliter",
2804
+ // milliliter
2805
+ mL: "milliliter",
2806
+ ml: "milliliter",
2807
+ milliliter: "milliliter",
2808
+ milliliters: "milliliter",
2809
+ mililitro: "milliliter",
2810
+ mililitros: "milliliter",
2811
+ // microliter
2812
+ \u03BCL: "microliter",
2813
+ uL: "microliter",
2814
+ ul: "microliter",
2815
+ microliter: "microliter",
2816
+ microliters: "microliter",
2817
+ // gallon_us
2818
+ gal: "gallon_us",
2819
+ gallon: "gallon_us",
2820
+ gallons: "gallon_us",
2821
+ "gallon us": "gallon_us",
2822
+ "gallons us": "gallon_us",
2823
+ gal\u00E3o: "gallon_us",
2824
+ gal\u00F5es: "gallon_us",
2825
+ // quart_us
2826
+ qt: "quart_us",
2827
+ quart: "quart_us",
2828
+ quarts: "quart_us",
2829
+ // pint_us
2830
+ pt: "pint_us",
2831
+ pint: "pint_us",
2832
+ pints: "pint_us",
2833
+ // cup_us
2834
+ cup: "cup_us",
2835
+ cups: "cup_us",
2836
+ x\u00EDcara: "cup_us",
2837
+ x\u00EDcaras: "cup_us",
2838
+ // fluid_ounce_us
2839
+ "fl oz": "fluid_ounce_us",
2840
+ floz: "fluid_ounce_us",
2841
+ "fluid ounce": "fluid_ounce_us",
2842
+ "fluid ounces": "fluid_ounce_us",
2843
+ // tablespoon_us
2844
+ tbsp: "tablespoon_us",
2845
+ tablespoon: "tablespoon_us",
2846
+ tablespoons: "tablespoon_us",
2847
+ "colher de sopa": "tablespoon_us",
2848
+ "colheres de sopa": "tablespoon_us",
2849
+ // teaspoon_us
2850
+ tsp: "teaspoon_us",
2851
+ teaspoon: "teaspoon_us",
2852
+ teaspoons: "teaspoon_us",
2853
+ "colher de ch\xE1": "teaspoon_us",
2854
+ "colheres de ch\xE1": "teaspoon_us",
2855
+ // gallon_uk
2856
+ "gal uk": "gallon_uk",
2857
+ "gallon uk": "gallon_uk",
2858
+ "gallons uk": "gallon_uk",
2859
+ "imperial gallon": "gallon_uk",
2860
+ "imperial gallons": "gallon_uk",
2861
+ // quart_uk
2862
+ "qt uk": "quart_uk",
2863
+ "quart uk": "quart_uk",
2864
+ "quarts uk": "quart_uk",
2865
+ // pint_uk
2866
+ "pt uk": "pint_uk",
2867
+ "pint uk": "pint_uk",
2868
+ "pints uk": "pint_uk",
2869
+ "imperial pint": "pint_uk",
2870
+ "imperial pints": "pint_uk",
2871
+ // fluid_ounce_uk
2872
+ "fl oz uk": "fluid_ounce_uk",
2873
+ "fluid ounce uk": "fluid_ounce_uk",
2874
+ "fluid ounces uk": "fluid_ounce_uk",
2875
+ // barrel_oil
2876
+ bbl: "barrel_oil",
2877
+ barrel: "barrel_oil",
2878
+ barrels: "barrel_oil",
2879
+ "oil barrel": "barrel_oil",
2880
+ "oil barrels": "barrel_oil",
2881
+ barril: "barrel_oil",
2882
+ barris: "barrel_oil",
2883
+ // cubic_inch
2884
+ "in\xB3": "cubic_inch",
2885
+ in3: "cubic_inch",
2886
+ "cu in": "cubic_inch",
2887
+ "cubic inch": "cubic_inch",
2888
+ "cubic inches": "cubic_inch",
2889
+ // cubic_foot
2890
+ "ft\xB3": "cubic_foot",
2891
+ ft3: "cubic_foot",
2892
+ "cu ft": "cubic_foot",
2893
+ "cubic foot": "cubic_foot",
2894
+ "cubic feet": "cubic_foot",
2895
+ // cubic_yard
2896
+ "yd\xB3": "cubic_yard",
2897
+ yd3: "cubic_yard",
2898
+ "cu yd": "cubic_yard",
2899
+ "cubic yard": "cubic_yard",
2900
+ "cubic yards": "cubic_yard"
2901
+ };
2902
+ Object.fromEntries(
2903
+ Object.entries(VOLUME_UNITS).map(([unit, config]) => [
2904
+ unit,
2905
+ config.factor ?? 1
2906
+ ])
2907
+ );
2908
+
2909
+ // src/roles/volume/VolumeRole.ts
2910
+ var VolumeRole = class extends SimpleRole {
2911
+ constructor() {
2912
+ super(...arguments);
2913
+ this.name = "volume";
2914
+ this.base = "liter";
2915
+ this.units = VOLUME_UNITS;
2916
+ this.aliases = VOLUME_ALIASES;
2917
+ this.validation = {
2918
+ min: 0,
2919
+ minError: "Volume cannot be negative"
2920
+ };
2921
+ }
2922
+ };
2923
+ var volumeRole = new VolumeRole();
2924
+
2925
+ // src/roles/time/constants.ts
2926
+ var TIME_UNITS = {
2927
+ // SI prefixes (exatos)
2928
+ nanosecond: {
2929
+ factor: 1e-9,
2930
+ symbol: "ns",
2931
+ singular: "nanosecond",
2932
+ plural: "nanoseconds"
2933
+ },
2934
+ microsecond: {
2935
+ factor: 1e-6,
2936
+ symbol: "\u03BCs",
2937
+ singular: "microsecond",
2938
+ plural: "microseconds"
2939
+ },
2940
+ millisecond: {
2941
+ factor: 1e-3,
2942
+ symbol: "ms",
2943
+ singular: "millisecond",
2944
+ plural: "milliseconds"
2945
+ },
2946
+ second: {
2947
+ factor: 1,
2948
+ symbol: "s",
2949
+ singular: "second",
2950
+ plural: "seconds"
2951
+ },
2952
+ // Common (exatos)
2953
+ minute: {
2954
+ factor: 60,
2955
+ symbol: "min",
2956
+ singular: "minute",
2957
+ plural: "minutes"
2958
+ },
2959
+ hour: {
2960
+ factor: 3600,
2961
+ symbol: "h",
2962
+ singular: "hour",
2963
+ plural: "hours"
2964
+ },
2965
+ day: {
2966
+ factor: 86400,
2967
+ symbol: "d",
2968
+ singular: "day",
2969
+ plural: "days"
2970
+ },
2971
+ week: {
2972
+ factor: 604800,
2973
+ symbol: "wk",
2974
+ singular: "week",
2975
+ plural: "weeks"
2976
+ },
2977
+ // Calendar (aproximados - baseados no ano Gregoriano)
2978
+ month: {
2979
+ factor: 2629746,
2980
+ // 31556952 / 12
2981
+ symbol: "mo",
2982
+ singular: "month",
2983
+ plural: "months"
2984
+ },
2985
+ year: {
2986
+ factor: 31556952,
2987
+ // 365.2425 * 86400
2988
+ symbol: "yr",
2989
+ singular: "year",
2990
+ plural: "years"
2991
+ },
2992
+ decade: {
2993
+ factor: 315569520,
2994
+ // 10 * year
2995
+ symbol: "dec",
2996
+ singular: "decade",
2997
+ plural: "decades"
2998
+ },
2999
+ century: {
3000
+ factor: 3155695200,
3001
+ // 100 * year
3002
+ symbol: "c",
3003
+ singular: "century",
3004
+ plural: "centuries"
3005
+ },
3006
+ millennium: {
3007
+ factor: 31556952e3,
3008
+ // 1000 * year
3009
+ symbol: "ky",
3010
+ singular: "millennium",
3011
+ plural: "millennia"
3012
+ }
3013
+ };
3014
+ var TIME_ALIASES = {
3015
+ // nanosecond
3016
+ ns: "nanosecond",
3017
+ nanosecond: "nanosecond",
3018
+ nanoseconds: "nanosecond",
3019
+ nanoseg: "nanosecond",
3020
+ nanossegundo: "nanosecond",
3021
+ nanossegundos: "nanosecond",
3022
+ // microsecond
3023
+ \u03BCs: "microsecond",
3024
+ us: "microsecond",
3025
+ microsecond: "microsecond",
3026
+ microseconds: "microsecond",
3027
+ microseg: "microsecond",
3028
+ microssegundo: "microsecond",
3029
+ microssegundos: "microsecond",
3030
+ // millisecond
3031
+ ms: "millisecond",
3032
+ millisecond: "millisecond",
3033
+ milliseconds: "millisecond",
3034
+ milliseg: "millisecond",
3035
+ milissegundo: "millisecond",
3036
+ milissegundos: "millisecond",
3037
+ // second
3038
+ s: "second",
3039
+ sec: "second",
3040
+ second: "second",
3041
+ seconds: "second",
3042
+ seg: "second",
3043
+ segundo: "second",
3044
+ segundos: "second",
3045
+ // minute
3046
+ min: "minute",
3047
+ minute: "minute",
3048
+ minutes: "minute",
3049
+ minuto: "minute",
3050
+ minutos: "minute",
3051
+ // hour
3052
+ h: "hour",
3053
+ hr: "hour",
3054
+ hrs: "hour",
3055
+ hour: "hour",
3056
+ hours: "hour",
3057
+ hora: "hour",
3058
+ horas: "hour",
3059
+ // day
3060
+ d: "day",
3061
+ day: "day",
3062
+ days: "day",
3063
+ dia: "day",
3064
+ dias: "day",
3065
+ // week
3066
+ wk: "week",
3067
+ week: "week",
3068
+ weeks: "week",
3069
+ semana: "week",
3070
+ semanas: "week",
3071
+ // month
3072
+ mo: "month",
3073
+ month: "month",
3074
+ months: "month",
3075
+ m\u00EAs: "month",
3076
+ mes: "month",
3077
+ meses: "month",
3078
+ // year
3079
+ yr: "year",
3080
+ y: "year",
3081
+ year: "year",
3082
+ years: "year",
3083
+ ano: "year",
3084
+ anos: "year",
3085
+ // decade
3086
+ dec: "decade",
3087
+ decade: "decade",
3088
+ decades: "decade",
3089
+ d\u00E9cada: "decade",
3090
+ decada: "decade",
3091
+ d\u00E9cadas: "decade",
3092
+ decadas: "decade",
3093
+ // century
3094
+ c: "century",
3095
+ century: "century",
3096
+ centuries: "century",
3097
+ s\u00E9culo: "century",
3098
+ seculo: "century",
3099
+ s\u00E9culos: "century",
3100
+ seculos: "century",
3101
+ // millennium
3102
+ ky: "millennium",
3103
+ millennium: "millennium",
3104
+ millennia: "millennium",
3105
+ mil\u00EAnio: "millennium",
3106
+ milenio: "millennium",
3107
+ mil\u00EAnios: "millennium",
3108
+ milenios: "millennium"
3109
+ };
3110
+ Object.fromEntries(
3111
+ Object.entries(TIME_UNITS).map(([unit, config]) => [
3112
+ unit,
3113
+ config.factor ?? 1
3114
+ ])
3115
+ );
3116
+
3117
+ // src/roles/time/TimeRole.ts
3118
+ var TimeRole = class extends SimpleRole {
3119
+ constructor() {
3120
+ super(...arguments);
3121
+ this.name = "time";
3122
+ this.base = "second";
3123
+ this.units = TIME_UNITS;
3124
+ this.aliases = TIME_ALIASES;
3125
+ this.validation = {
3126
+ min: 0,
3127
+ minError: "Duration cannot be negative"
3128
+ };
3129
+ }
3130
+ };
3131
+ var timeRole = new TimeRole();
3132
+
3133
+ // src/roles/digital/constants.ts
3134
+ var DIGITAL_UNITS = {
3135
+ // Fundamental
3136
+ bit: {
3137
+ factor: 0.125,
3138
+ // 1/8 byte
3139
+ symbol: "b",
3140
+ singular: "bit",
3141
+ plural: "bits"
3142
+ },
3143
+ byte: {
3144
+ factor: 1,
3145
+ symbol: "B",
3146
+ singular: "byte",
3147
+ plural: "bytes"
3148
+ },
3149
+ // IEC Binary (base 1024) - RAM, cache, file systems
3150
+ kibibyte: {
3151
+ factor: 1024,
3152
+ // 2^10
3153
+ symbol: "KiB",
3154
+ singular: "kibibyte",
3155
+ plural: "kibibytes"
3156
+ },
3157
+ mebibyte: {
3158
+ factor: 1048576,
3159
+ // 2^20
3160
+ symbol: "MiB",
3161
+ singular: "mebibyte",
3162
+ plural: "mebibytes"
3163
+ },
3164
+ gibibyte: {
3165
+ factor: 1073741824,
3166
+ // 2^30
3167
+ symbol: "GiB",
3168
+ singular: "gibibyte",
3169
+ plural: "gibibytes"
3170
+ },
3171
+ tebibyte: {
3172
+ factor: 1099511627776,
3173
+ // 2^40
3174
+ symbol: "TiB",
3175
+ singular: "tebibyte",
3176
+ plural: "tebibytes"
3177
+ },
3178
+ pebibyte: {
3179
+ factor: 1125899906842624,
3180
+ // 2^50
3181
+ symbol: "PiB",
3182
+ singular: "pebibyte",
3183
+ plural: "pebibytes"
3184
+ },
3185
+ exbibyte: {
3186
+ factor: 1152921504606847e3,
3187
+ // 2^60
3188
+ symbol: "EiB",
3189
+ singular: "exbibyte",
3190
+ plural: "exbibytes"
3191
+ },
3192
+ // SI Decimal (base 1000) - HD marketing, network speeds
3193
+ kilobyte: {
3194
+ factor: 1e3,
3195
+ // 10^3
3196
+ symbol: "kB",
3197
+ singular: "kilobyte",
3198
+ plural: "kilobytes"
3199
+ },
3200
+ megabyte: {
3201
+ factor: 1e6,
3202
+ // 10^6
3203
+ symbol: "MB",
3204
+ singular: "megabyte",
3205
+ plural: "megabytes"
3206
+ },
3207
+ gigabyte: {
3208
+ factor: 1e9,
3209
+ // 10^9
3210
+ symbol: "GB",
3211
+ singular: "gigabyte",
3212
+ plural: "gigabytes"
3213
+ },
3214
+ terabyte: {
3215
+ factor: 1e12,
3216
+ // 10^12
3217
+ symbol: "TB",
3218
+ singular: "terabyte",
3219
+ plural: "terabytes"
3220
+ },
3221
+ petabyte: {
3222
+ factor: 1e15,
3223
+ // 10^15
3224
+ symbol: "PB",
3225
+ singular: "petabyte",
3226
+ plural: "petabytes"
3227
+ },
3228
+ exabyte: {
3229
+ factor: 1e18,
3230
+ // 10^18
3231
+ symbol: "EB",
3232
+ singular: "exabyte",
3233
+ plural: "exabytes"
3234
+ }
3235
+ };
3236
+ var DIGITAL_ALIASES = {
3237
+ // bit
3238
+ b: "bit",
3239
+ bit: "bit",
3240
+ bits: "bit",
3241
+ // byte
3242
+ B: "byte",
3243
+ byte: "byte",
3244
+ bytes: "byte",
3245
+ octet: "byte",
3246
+ octets: "byte",
3247
+ // kibibyte (IEC)
3248
+ KiB: "kibibyte",
3249
+ kibibyte: "kibibyte",
3250
+ kibibytes: "kibibyte",
3251
+ // mebibyte (IEC)
3252
+ MiB: "mebibyte",
3253
+ mebibyte: "mebibyte",
3254
+ mebibytes: "mebibyte",
3255
+ // gibibyte (IEC)
3256
+ GiB: "gibibyte",
3257
+ gibibyte: "gibibyte",
3258
+ gibibytes: "gibibyte",
3259
+ // tebibyte (IEC)
3260
+ TiB: "tebibyte",
3261
+ tebibyte: "tebibyte",
3262
+ tebibytes: "tebibyte",
3263
+ // pebibyte (IEC)
3264
+ PiB: "pebibyte",
3265
+ pebibyte: "pebibyte",
3266
+ pebibytes: "pebibyte",
3267
+ // exbibyte (IEC)
3268
+ EiB: "exbibyte",
3269
+ exbibyte: "exbibyte",
3270
+ exbibytes: "exbibyte",
3271
+ // kilobyte (SI)
3272
+ kB: "kilobyte",
3273
+ kb: "kilobyte",
3274
+ kilobyte: "kilobyte",
3275
+ kilobytes: "kilobyte",
3276
+ // megabyte (SI)
3277
+ MB: "megabyte",
3278
+ megabyte: "megabyte",
3279
+ megabytes: "megabyte",
3280
+ // gigabyte (SI)
3281
+ GB: "gigabyte",
3282
+ gigabyte: "gigabyte",
3283
+ gigabytes: "gigabyte",
3284
+ // terabyte (SI)
3285
+ TB: "terabyte",
3286
+ terabyte: "terabyte",
3287
+ terabytes: "terabyte",
3288
+ // petabyte (SI)
3289
+ PB: "petabyte",
3290
+ petabyte: "petabyte",
3291
+ petabytes: "petabyte",
3292
+ // exabyte (SI)
3293
+ EB: "exabyte",
3294
+ exabyte: "exabyte",
3295
+ exabytes: "exabyte"
3296
+ };
3297
+ Object.fromEntries(
3298
+ Object.entries(DIGITAL_UNITS).map(([unit, config]) => [
3299
+ unit,
3300
+ config.factor ?? 1
3301
+ ])
3302
+ );
3303
+
3304
+ // src/roles/digital/DigitalRole.ts
3305
+ var DigitalRole = class extends SimpleRole {
3306
+ constructor() {
3307
+ super(...arguments);
3308
+ this.name = "digital";
3309
+ this.base = "byte";
3310
+ this.units = DIGITAL_UNITS;
3311
+ this.aliases = DIGITAL_ALIASES;
3312
+ this.validation = {
3313
+ min: 0,
3314
+ minError: "Digital storage cannot be negative"
3315
+ };
3316
+ }
3317
+ };
3318
+ var digitalRole = new DigitalRole();
3319
+
3320
+ // src/roles/frequency/constants.ts
3321
+ var FREQUENCY_UNITS = {
3322
+ // SI Units (todos exatos)
3323
+ terahertz: {
3324
+ factor: 1e12,
3325
+ symbol: "THz",
3326
+ singular: "terahertz",
3327
+ plural: "terahertz"
3328
+ },
3329
+ gigahertz: {
3330
+ factor: 1e9,
3331
+ symbol: "GHz",
3332
+ singular: "gigahertz",
3333
+ plural: "gigahertz"
3334
+ },
3335
+ megahertz: {
3336
+ factor: 1e6,
3337
+ symbol: "MHz",
3338
+ singular: "megahertz",
3339
+ plural: "megahertz"
3340
+ },
3341
+ kilohertz: {
3342
+ factor: 1e3,
3343
+ symbol: "kHz",
3344
+ singular: "kilohertz",
3345
+ plural: "kilohertz"
3346
+ },
3347
+ hertz: {
3348
+ factor: 1,
3349
+ symbol: "Hz",
3350
+ singular: "hertz",
3351
+ plural: "hertz"
3352
+ },
3353
+ millihertz: {
3354
+ factor: 1e-3,
3355
+ symbol: "mHz",
3356
+ singular: "millihertz",
3357
+ plural: "millihertz"
3358
+ },
3359
+ microhertz: {
3360
+ factor: 1e-6,
3361
+ symbol: "\u03BCHz",
3362
+ singular: "microhertz",
3363
+ plural: "microhertz"
3364
+ },
3365
+ // Other units
3366
+ rpm: {
3367
+ factor: 1 / 60,
3368
+ // 1 RPM = 1/60 Hz
3369
+ symbol: "rpm",
3370
+ singular: "revolution per minute",
3371
+ plural: "revolutions per minute"
3372
+ },
3373
+ bpm: {
3374
+ factor: 1 / 60,
3375
+ // 1 BPM = 1/60 Hz
3376
+ symbol: "bpm",
3377
+ singular: "beat per minute",
3378
+ plural: "beats per minute"
3379
+ },
3380
+ radians_per_second: {
3381
+ factor: 1 / (2 * Math.PI),
3382
+ // 1 rad/s = 1/(2π) Hz ≈ 0.159154943
3383
+ symbol: "rad/s",
3384
+ singular: "radian per second",
3385
+ plural: "radians per second"
3386
+ },
3387
+ cycles_per_minute: {
3388
+ factor: 1 / 60,
3389
+ // same as RPM
3390
+ symbol: "cpm",
3391
+ singular: "cycle per minute",
3392
+ plural: "cycles per minute"
3393
+ }
3394
+ };
3395
+ var FREQUENCY_ALIASES = {
3396
+ // terahertz
3397
+ thz: "terahertz",
3398
+ terahertz: "terahertz",
3399
+ // gigahertz
3400
+ ghz: "gigahertz",
3401
+ gigahertz: "gigahertz",
3402
+ // megahertz
3403
+ mhz: "megahertz",
3404
+ megahertz: "megahertz",
3405
+ // kilohertz
3406
+ khz: "kilohertz",
3407
+ kilohertz: "kilohertz",
3408
+ // hertz
3409
+ hz: "hertz",
3410
+ hertz: "hertz",
3411
+ "cycles per second": "hertz",
3412
+ cps: "hertz",
3413
+ // millihertz
3414
+ // Note: "mhz" já é megahertz, usar "milli hz" ou similar
3415
+ millihertz: "millihertz",
3416
+ // microhertz
3417
+ \u03BChz: "microhertz",
3418
+ uhz: "microhertz",
3419
+ microhertz: "microhertz",
3420
+ // rpm
3421
+ rpm: "rpm",
3422
+ "r/min": "rpm",
3423
+ "rev/min": "rpm",
3424
+ "revolutions per minute": "rpm",
3425
+ "revolution per minute": "rpm",
3426
+ rota\u00E7\u00F5es: "rpm",
3427
+ "rota\xE7\xF5es por minuto": "rpm",
3428
+ // bpm
3429
+ bpm: "bpm",
3430
+ "beats per minute": "bpm",
3431
+ "beat per minute": "bpm",
3432
+ batimentos: "bpm",
3433
+ "batimentos por minuto": "bpm",
3434
+ // radians_per_second
3435
+ "rad/s": "radians_per_second",
3436
+ "radian per second": "radians_per_second",
3437
+ "radians per second": "radians_per_second",
3438
+ // cycles_per_minute
3439
+ cpm: "cycles_per_minute",
3440
+ "cycles per minute": "cycles_per_minute",
3441
+ "cycle per minute": "cycles_per_minute"
3442
+ };
3443
+ Object.fromEntries(
3444
+ Object.entries(FREQUENCY_UNITS).map(([unit, config]) => [
3445
+ unit,
3446
+ config.factor ?? 1
3447
+ ])
3448
+ );
3449
+
3450
+ // src/roles/frequency/FrequencyRole.ts
3451
+ var FrequencyRole = class extends SimpleRole {
3452
+ constructor() {
3453
+ super(...arguments);
3454
+ this.name = "frequency";
3455
+ this.base = "hertz";
3456
+ this.units = FREQUENCY_UNITS;
3457
+ this.aliases = FREQUENCY_ALIASES;
3458
+ this.validation = {
3459
+ min: 0,
3460
+ minError: "Frequency cannot be negative"
3461
+ };
3462
+ }
3463
+ };
3464
+ var frequencyRole = new FrequencyRole();
3465
+
3466
+ // src/roles/pressure/constants.ts
3467
+ var PRESSURE_UNITS = {
3468
+ // SI Units (todos exatos)
3469
+ megapascal: {
3470
+ factor: 1e6,
3471
+ symbol: "MPa",
3472
+ singular: "megapascal",
3473
+ plural: "megapascals"
3474
+ },
3475
+ kilopascal: {
3476
+ factor: 1e3,
3477
+ symbol: "kPa",
3478
+ singular: "kilopascal",
3479
+ plural: "kilopascals"
3480
+ },
3481
+ hectopascal: {
3482
+ factor: 100,
3483
+ symbol: "hPa",
3484
+ singular: "hectopascal",
3485
+ plural: "hectopascals"
3486
+ },
3487
+ pascal: {
3488
+ factor: 1,
3489
+ symbol: "Pa",
3490
+ singular: "pascal",
3491
+ plural: "pascals"
3492
+ },
3493
+ // Bar (exatos por definição)
3494
+ bar: {
3495
+ factor: 1e5,
3496
+ symbol: "bar",
3497
+ singular: "bar",
3498
+ plural: "bar"
3499
+ },
3500
+ millibar: {
3501
+ factor: 100,
3502
+ // = 1 hPa
3503
+ symbol: "mbar",
3504
+ singular: "millibar",
3505
+ plural: "millibar"
3506
+ },
3507
+ // Atmosphere (exato, CGPM 1954)
3508
+ atmosphere: {
3509
+ factor: 101325,
3510
+ symbol: "atm",
3511
+ singular: "atmosphere",
3512
+ plural: "atmospheres"
3513
+ },
3514
+ // Mercury column
3515
+ torr: {
3516
+ factor: 101325 / 760,
3517
+ // ≈ 133.322368421
3518
+ symbol: "Torr",
3519
+ singular: "torr",
3520
+ plural: "torr"
3521
+ },
3522
+ mmhg: {
3523
+ factor: 133.322387415,
3524
+ // Convenção NIST
3525
+ symbol: "mmHg",
3526
+ singular: "millimeter of mercury",
3527
+ plural: "millimeters of mercury"
3528
+ },
3529
+ inhg: {
3530
+ factor: 3386.389,
3531
+ // Polegadas de mercúrio
3532
+ symbol: "inHg",
3533
+ singular: "inch of mercury",
3534
+ plural: "inches of mercury"
3535
+ },
3536
+ // Imperial (derivados de lb/in²)
3537
+ psi: {
3538
+ factor: 6894.757293168,
3539
+ // 1 lbf/in²
3540
+ symbol: "psi",
3541
+ singular: "pound per square inch",
3542
+ plural: "pounds per square inch"
3543
+ },
3544
+ ksi: {
3545
+ factor: 6894757293168e-6,
3546
+ // 1000 psi
3547
+ symbol: "ksi",
3548
+ singular: "kilopound per square inch",
3549
+ plural: "kilopounds per square inch"
3550
+ },
3551
+ // Water column
3552
+ cmh2o: {
3553
+ factor: 98.0665,
3554
+ // cm de água a 4°C
3555
+ symbol: "cmH\u2082O",
3556
+ singular: "centimeter of water",
3557
+ plural: "centimeters of water"
3558
+ },
3559
+ inh2o: {
3560
+ factor: 249.08891,
3561
+ // polegadas de água
3562
+ symbol: "inH\u2082O",
3563
+ singular: "inch of water",
3564
+ plural: "inches of water"
3565
+ }
3566
+ };
3567
+ var PRESSURE_ALIASES = {
3568
+ // megapascal
3569
+ mpa: "megapascal",
3570
+ megapascal: "megapascal",
3571
+ megapascals: "megapascal",
3572
+ // kilopascal
3573
+ kpa: "kilopascal",
3574
+ kilopascal: "kilopascal",
3575
+ kilopascals: "kilopascal",
3576
+ // hectopascal
3577
+ hpa: "hectopascal",
3578
+ hectopascal: "hectopascal",
3579
+ hectopascals: "hectopascal",
3580
+ // pascal
3581
+ pa: "pascal",
3582
+ pascal: "pascal",
3583
+ pascals: "pascal",
3584
+ // bar
3585
+ bar: "bar",
3586
+ bars: "bar",
3587
+ // millibar
3588
+ mbar: "millibar",
3589
+ millibar: "millibar",
3590
+ millibars: "millibar",
3591
+ mb: "millibar",
3592
+ // atmosphere
3593
+ atm: "atmosphere",
3594
+ atmosphere: "atmosphere",
3595
+ atmospheres: "atmosphere",
3596
+ atmosfera: "atmosphere",
3597
+ atmosferas: "atmosphere",
3598
+ // torr
3599
+ torr: "torr",
3600
+ // mmhg
3601
+ mmhg: "mmhg",
3602
+ "mm hg": "mmhg",
3603
+ "millimeter of mercury": "mmhg",
3604
+ "millimeters of mercury": "mmhg",
3605
+ "mil\xEDmetro de merc\xFArio": "mmhg",
3606
+ "mil\xEDmetros de merc\xFArio": "mmhg",
3607
+ // inhg
3608
+ inhg: "inhg",
3609
+ "in hg": "inhg",
3610
+ "inch of mercury": "inhg",
3611
+ "inches of mercury": "inhg",
3612
+ // psi
3613
+ psi: "psi",
3614
+ "lbf/in\xB2": "psi",
3615
+ "lb/in\xB2": "psi",
3616
+ "pound per square inch": "psi",
3617
+ "pounds per square inch": "psi",
3618
+ // ksi
3619
+ ksi: "ksi",
3620
+ "kilopound per square inch": "ksi",
3621
+ // cmh2o
3622
+ cmh2o: "cmh2o",
3623
+ "cmh\u2082o": "cmh2o",
3624
+ "cm h2o": "cmh2o",
3625
+ "centimeter of water": "cmh2o",
3626
+ "centimeters of water": "cmh2o",
3627
+ // inh2o
3628
+ inh2o: "inh2o",
3629
+ "inh\u2082o": "inh2o",
3630
+ "in h2o": "inh2o",
3631
+ "inch of water": "inh2o",
3632
+ "inches of water": "inh2o"
3633
+ };
3634
+ Object.fromEntries(
3635
+ Object.entries(PRESSURE_UNITS).map(([unit, config]) => [
3636
+ unit,
3637
+ config.factor ?? 1
3638
+ ])
3639
+ );
3640
+
3641
+ // src/roles/pressure/PressureRole.ts
3642
+ var PressureRole = class extends SimpleRole {
3643
+ constructor() {
3644
+ super(...arguments);
3645
+ this.name = "pressure";
3646
+ this.base = "pascal";
3647
+ this.units = PRESSURE_UNITS;
3648
+ this.aliases = PRESSURE_ALIASES;
3649
+ this.validation = {
3650
+ min: 0,
3651
+ minError: "Pressure cannot be negative"
3652
+ };
3653
+ }
3654
+ };
3655
+ var pressureRole = new PressureRole();
3656
+
3657
+ // src/roles/color/types.ts
3658
+ var NAMED_COLORS = {
3659
+ // Basic colors
3660
+ black: { r: 0, g: 0, b: 0, a: 1 },
3661
+ white: { r: 255, g: 255, b: 255, a: 1 },
3662
+ red: { r: 255, g: 0, b: 0, a: 1 },
3663
+ green: { r: 0, g: 128, b: 0, a: 1 },
3664
+ blue: { r: 0, g: 0, b: 255, a: 1 },
3665
+ yellow: { r: 255, g: 255, b: 0, a: 1 },
3666
+ cyan: { r: 0, g: 255, b: 255, a: 1 },
3667
+ magenta: { r: 255, g: 0, b: 255, a: 1 },
3668
+ // Extended colors
3669
+ orange: { r: 255, g: 165, b: 0, a: 1 },
3670
+ purple: { r: 128, g: 0, b: 128, a: 1 },
3671
+ pink: { r: 255, g: 192, b: 203, a: 1 },
3672
+ brown: { r: 165, g: 42, b: 42, a: 1 },
3673
+ gray: { r: 128, g: 128, b: 128, a: 1 },
3674
+ grey: { r: 128, g: 128, b: 128, a: 1 },
3675
+ // Web colors
3676
+ lime: { r: 0, g: 255, b: 0, a: 1 },
3677
+ aqua: { r: 0, g: 255, b: 255, a: 1 },
3678
+ fuchsia: { r: 255, g: 0, b: 255, a: 1 },
3679
+ silver: { r: 192, g: 192, b: 192, a: 1 },
3680
+ maroon: { r: 128, g: 0, b: 0, a: 1 },
3681
+ olive: { r: 128, g: 128, b: 0, a: 1 },
3682
+ navy: { r: 0, g: 0, b: 128, a: 1 },
3683
+ teal: { r: 0, g: 128, b: 128, a: 1 },
3684
+ // Transparent
3685
+ transparent: { r: 0, g: 0, b: 0, a: 0 }
3686
+ };
3687
+ function clamp(value, min, max) {
3688
+ return Math.min(Math.max(value, min), max);
3689
+ }
3690
+ function normalizeRgbChannel(value) {
3691
+ return clamp(Math.round(value), 0, 255);
3692
+ }
3693
+ function normalizeAlpha(value) {
3694
+ if (value === void 0) return 1;
3695
+ return clamp(value, 0, 1);
3696
+ }
3697
+ function rgbToHsl(r, g, b) {
3698
+ r /= 255;
3699
+ g /= 255;
3700
+ b /= 255;
3701
+ const max = Math.max(r, g, b);
3702
+ const min = Math.min(r, g, b);
3703
+ const l = (max + min) / 2;
3704
+ if (max === min) {
3705
+ return { h: 0, s: 0, l: l * 100 };
3706
+ }
3707
+ const d = max - min;
3708
+ const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
3709
+ let h;
3710
+ switch (max) {
3711
+ case r:
3712
+ h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
3713
+ break;
3714
+ case g:
3715
+ h = ((b - r) / d + 2) / 6;
3716
+ break;
3717
+ default:
3718
+ h = ((r - g) / d + 4) / 6;
3719
+ break;
3720
+ }
3721
+ return {
3722
+ h: Math.round(h * 360),
3723
+ s: Math.round(s * 100),
3724
+ l: Math.round(l * 100)
3725
+ };
3726
+ }
3727
+ function hslToRgb(h, s, l) {
3728
+ h /= 360;
3729
+ s /= 100;
3730
+ l /= 100;
3731
+ if (s === 0) {
3732
+ const gray = Math.round(l * 255);
3733
+ return { r: gray, g: gray, b: gray };
3734
+ }
3735
+ const hue2rgb = (p2, q2, t) => {
3736
+ if (t < 0) t += 1;
3737
+ if (t > 1) t -= 1;
3738
+ if (t < 1 / 6) return p2 + (q2 - p2) * 6 * t;
3739
+ if (t < 1 / 2) return q2;
3740
+ if (t < 2 / 3) return p2 + (q2 - p2) * (2 / 3 - t) * 6;
3741
+ return p2;
3742
+ };
3743
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
3744
+ const p = 2 * l - q;
3745
+ return {
3746
+ r: Math.round(hue2rgb(p, q, h + 1 / 3) * 255),
3747
+ g: Math.round(hue2rgb(p, q, h) * 255),
3748
+ b: Math.round(hue2rgb(p, q, h - 1 / 3) * 255)
3749
+ };
3750
+ }
3751
+
3752
+ // src/roles/color/variants/HexVariant.ts
3753
+ var HEX_REGEX_3 = /^#?([0-9a-f])([0-9a-f])([0-9a-f])$/i;
3754
+ var HEX_REGEX_4 = /^#?([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])$/i;
3755
+ var HEX_REGEX_6 = /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i;
3756
+ var HEX_REGEX_8 = /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i;
3757
+ var HexVariant = class extends BaseVariant {
3758
+ constructor() {
3759
+ super(...arguments);
3760
+ this.name = "hex";
3761
+ this.type = "string";
3762
+ }
3763
+ // ===========================================================================
3764
+ // CONVERT
3765
+ // ===========================================================================
3766
+ toBase(value) {
3767
+ const hex = value.trim();
3768
+ let match = hex.match(HEX_REGEX_8);
3769
+ if (match) {
3770
+ return {
3771
+ r: parseInt(match[1], 16),
3772
+ g: parseInt(match[2], 16),
3773
+ b: parseInt(match[3], 16),
3774
+ a: parseInt(match[4], 16) / 255
3775
+ };
3776
+ }
3777
+ match = hex.match(HEX_REGEX_6);
3778
+ if (match) {
3779
+ return {
3780
+ r: parseInt(match[1], 16),
3781
+ g: parseInt(match[2], 16),
3782
+ b: parseInt(match[3], 16),
3783
+ a: 1
3784
+ };
3785
+ }
3786
+ match = hex.match(HEX_REGEX_4);
3787
+ if (match) {
3788
+ return {
3789
+ r: parseInt(match[1] + match[1], 16),
3790
+ g: parseInt(match[2] + match[2], 16),
3791
+ b: parseInt(match[3] + match[3], 16),
3792
+ a: parseInt(match[4] + match[4], 16) / 255
3793
+ };
3794
+ }
3795
+ match = hex.match(HEX_REGEX_3);
3796
+ if (match) {
3797
+ return {
3798
+ r: parseInt(match[1] + match[1], 16),
3799
+ g: parseInt(match[2] + match[2], 16),
3800
+ b: parseInt(match[3] + match[3], 16),
3801
+ a: 1
3802
+ };
3803
+ }
3804
+ throw new Error(`Invalid hex color: ${value}`);
3805
+ }
3806
+ fromBase(base) {
3807
+ const r = normalizeRgbChannel(base.r).toString(16).padStart(2, "0");
3808
+ const g = normalizeRgbChannel(base.g).toString(16).padStart(2, "0");
3809
+ const b = normalizeRgbChannel(base.b).toString(16).padStart(2, "0");
3810
+ const alpha = normalizeAlpha(base.a);
3811
+ if (alpha < 1) {
3812
+ const a = Math.round(alpha * 255).toString(16).padStart(2, "0");
3813
+ return `#${r}${g}${b}${a}`;
3814
+ }
3815
+ return `#${r}${g}${b}`;
3816
+ }
3817
+ // ===========================================================================
3818
+ // CAST
3819
+ // ===========================================================================
3820
+ cast(input) {
3821
+ if (typeof input === "string") {
3822
+ const trimmed = input.trim().toLowerCase();
3823
+ if (NAMED_COLORS[trimmed]) {
3824
+ return this.fromBase(NAMED_COLORS[trimmed]);
3825
+ }
3826
+ if (HEX_REGEX_8.test(trimmed) || HEX_REGEX_6.test(trimmed) || HEX_REGEX_4.test(trimmed) || HEX_REGEX_3.test(trimmed)) {
3827
+ try {
3828
+ const rgba = this.toBase(trimmed);
3829
+ return this.fromBase(rgba);
3830
+ } catch {
3831
+ return null;
3832
+ }
3833
+ }
3834
+ const rgbMatch = trimmed.match(
3835
+ /^rgba?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*([\d.]+))?\s*\)$/
3836
+ );
3837
+ if (rgbMatch) {
3838
+ const rgba = {
3839
+ r: parseInt(rgbMatch[1], 10),
3840
+ g: parseInt(rgbMatch[2], 10),
3841
+ b: parseInt(rgbMatch[3], 10),
3842
+ a: rgbMatch[4] ? parseFloat(rgbMatch[4]) : 1
3843
+ };
3844
+ return this.fromBase(rgba);
3845
+ }
3846
+ const hslMatch = trimmed.match(
3847
+ /^hsla?\s*\(\s*(\d+)\s*,\s*(\d+)%?\s*,\s*(\d+)%?\s*(?:,\s*([\d.]+))?\s*\)$/
3848
+ );
3849
+ if (hslMatch) {
3850
+ const rgb = hslToRgb(
3851
+ parseInt(hslMatch[1], 10),
3852
+ parseInt(hslMatch[2], 10),
3853
+ parseInt(hslMatch[3], 10)
3854
+ );
3855
+ const rgba = {
3856
+ ...rgb,
3857
+ a: hslMatch[4] ? parseFloat(hslMatch[4]) : 1
3858
+ };
3859
+ return this.fromBase(rgba);
3860
+ }
3861
+ return null;
3862
+ }
3863
+ if (typeof input === "object" && input !== null && "r" in input && "g" in input && "b" in input) {
3864
+ const obj = input;
3865
+ const rgba = {
3866
+ r: obj.r,
3867
+ g: obj.g,
3868
+ b: obj.b,
3869
+ a: obj.a ?? 1
3870
+ };
3871
+ return this.fromBase(rgba);
3872
+ }
3873
+ if (typeof input === "object" && input !== null && "h" in input && "s" in input && "l" in input) {
3874
+ const obj = input;
3875
+ const rgb = hslToRgb(obj.h, obj.s, obj.l);
3876
+ const rgba = {
3877
+ ...rgb,
3878
+ a: obj.a ?? 1
3879
+ };
3880
+ return this.fromBase(rgba);
3881
+ }
3882
+ return null;
3883
+ }
3884
+ // ===========================================================================
3885
+ // VALIDATE
3886
+ // ===========================================================================
3887
+ validate(value) {
3888
+ const errors = [];
3889
+ if (typeof value !== "string") {
3890
+ errors.push("Hex color must be a string");
3891
+ return { valid: false, errors };
3892
+ }
3893
+ const hex = value.trim();
3894
+ if (!HEX_REGEX_8.test(hex) && !HEX_REGEX_6.test(hex) && !HEX_REGEX_4.test(hex) && !HEX_REGEX_3.test(hex)) {
3895
+ errors.push("Invalid hex format. Use #rgb, #rgba, #rrggbb, or #rrggbbaa");
3896
+ return { valid: false, errors };
3897
+ }
3898
+ return { valid: true, errors: [] };
3899
+ }
3900
+ // ===========================================================================
3901
+ // FORMAT
3902
+ // ===========================================================================
3903
+ format(value, options) {
3904
+ const opts = options;
3905
+ try {
3906
+ const rgba = this.toBase(value);
3907
+ let hex = this.fromBase(rgba);
3908
+ if (opts?.uppercase) {
3909
+ hex = hex.toUpperCase();
3910
+ }
3911
+ if (opts?.includeAlpha && rgba.a === 1) {
3912
+ const a = "ff";
3913
+ hex = hex.length === 7 ? `${hex}${opts.uppercase ? "FF" : "ff"}` : hex;
3914
+ }
3915
+ if (opts?.compact && hex.length === 7) {
3916
+ const canShorten = hex[1] === hex[2] && hex[3] === hex[4] && hex[5] === hex[6];
3917
+ if (canShorten) {
3918
+ hex = `#${hex[1]}${hex[3]}${hex[5]}`;
3919
+ if (opts.uppercase) hex = hex.toUpperCase();
3920
+ }
3921
+ }
3922
+ return hex;
3923
+ } catch {
3924
+ return value;
3925
+ }
3926
+ }
3927
+ };
3928
+
3929
+ // src/roles/color/variants/RgbObjectVariant.ts
3930
+ var RgbObjectVariant = class extends BaseVariant {
3931
+ constructor() {
3932
+ super(...arguments);
3933
+ this.name = "rgb_object";
3934
+ this.type = "object";
3935
+ }
3936
+ // ===========================================================================
3937
+ // CONVERT
3938
+ // ===========================================================================
3939
+ toBase(value) {
3940
+ return {
3941
+ r: normalizeRgbChannel(value.r),
3942
+ g: normalizeRgbChannel(value.g),
3943
+ b: normalizeRgbChannel(value.b),
3944
+ a: normalizeAlpha(value.a)
3945
+ };
3946
+ }
3947
+ fromBase(base) {
3948
+ const result = {
3949
+ r: normalizeRgbChannel(base.r),
3950
+ g: normalizeRgbChannel(base.g),
3951
+ b: normalizeRgbChannel(base.b)
3952
+ };
3953
+ if (base.a !== void 0 && base.a < 1) {
3954
+ result.a = normalizeAlpha(base.a);
3955
+ }
3956
+ return result;
3957
+ }
3958
+ // ===========================================================================
3959
+ // CAST
3960
+ // ===========================================================================
3961
+ cast(input) {
3962
+ if (typeof input === "object" && input !== null && "r" in input && "g" in input && "b" in input) {
3963
+ const obj = input;
3964
+ if (typeof obj.r !== "number" || typeof obj.g !== "number" || typeof obj.b !== "number") {
3965
+ return null;
3966
+ }
3967
+ return this.fromBase({
3968
+ r: obj.r,
3969
+ g: obj.g,
3970
+ b: obj.b,
3971
+ a: typeof obj.a === "number" ? obj.a : 1
3972
+ });
3973
+ }
3974
+ if (typeof input === "object" && input !== null && "h" in input && "s" in input && "l" in input) {
3975
+ const obj = input;
3976
+ const rgb = hslToRgb(obj.h, obj.s, obj.l);
3977
+ return this.fromBase({
3978
+ ...rgb,
3979
+ a: obj.a ?? 1
3980
+ });
3981
+ }
3982
+ if (typeof input === "string") {
3983
+ const trimmed = input.trim().toLowerCase();
3984
+ if (NAMED_COLORS[trimmed]) {
3985
+ return this.fromBase(NAMED_COLORS[trimmed]);
3986
+ }
3987
+ const hexMatch = trimmed.match(
3988
+ /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})?$/i
3989
+ );
3990
+ if (hexMatch) {
3991
+ return this.fromBase({
3992
+ r: parseInt(hexMatch[1], 16),
3993
+ g: parseInt(hexMatch[2], 16),
3994
+ b: parseInt(hexMatch[3], 16),
3995
+ a: hexMatch[4] ? parseInt(hexMatch[4], 16) / 255 : 1
3996
+ });
3997
+ }
3998
+ const hexShortMatch = trimmed.match(
3999
+ /^#?([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])?$/i
4000
+ );
4001
+ if (hexShortMatch) {
4002
+ return this.fromBase({
4003
+ r: parseInt(hexShortMatch[1] + hexShortMatch[1], 16),
4004
+ g: parseInt(hexShortMatch[2] + hexShortMatch[2], 16),
4005
+ b: parseInt(hexShortMatch[3] + hexShortMatch[3], 16),
4006
+ a: hexShortMatch[4] ? parseInt(hexShortMatch[4] + hexShortMatch[4], 16) / 255 : 1
4007
+ });
4008
+ }
4009
+ const rgbMatch = trimmed.match(
4010
+ /^rgba?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*([\d.]+))?\s*\)$/
4011
+ );
4012
+ if (rgbMatch) {
4013
+ return this.fromBase({
4014
+ r: parseInt(rgbMatch[1], 10),
4015
+ g: parseInt(rgbMatch[2], 10),
4016
+ b: parseInt(rgbMatch[3], 10),
4017
+ a: rgbMatch[4] ? parseFloat(rgbMatch[4]) : 1
4018
+ });
4019
+ }
4020
+ const hslMatch = trimmed.match(
4021
+ /^hsla?\s*\(\s*(\d+)\s*,\s*(\d+)%?\s*,\s*(\d+)%?\s*(?:,\s*([\d.]+))?\s*\)$/
4022
+ );
4023
+ if (hslMatch) {
4024
+ const rgb = hslToRgb(
4025
+ parseInt(hslMatch[1], 10),
4026
+ parseInt(hslMatch[2], 10),
4027
+ parseInt(hslMatch[3], 10)
4028
+ );
4029
+ return this.fromBase({
4030
+ ...rgb,
4031
+ a: hslMatch[4] ? parseFloat(hslMatch[4]) : 1
4032
+ });
4033
+ }
4034
+ }
4035
+ return null;
4036
+ }
4037
+ // ===========================================================================
4038
+ // VALIDATE
4039
+ // ===========================================================================
4040
+ validate(value) {
4041
+ const errors = [];
4042
+ if (typeof value !== "object" || value === null) {
4043
+ errors.push("RGB color must be an object");
4044
+ return { valid: false, errors };
4045
+ }
4046
+ if (!("r" in value) || !("g" in value) || !("b" in value)) {
4047
+ errors.push("RGB color must have r, g, b properties");
4048
+ return { valid: false, errors };
4049
+ }
4050
+ if (typeof value.r !== "number" || typeof value.g !== "number" || typeof value.b !== "number") {
4051
+ errors.push("r, g, b must be numbers");
4052
+ return { valid: false, errors };
4053
+ }
4054
+ if (value.r < 0 || value.r > 255) {
4055
+ errors.push("r must be between 0 and 255");
4056
+ }
4057
+ if (value.g < 0 || value.g > 255) {
4058
+ errors.push("g must be between 0 and 255");
4059
+ }
4060
+ if (value.b < 0 || value.b > 255) {
4061
+ errors.push("b must be between 0 and 255");
4062
+ }
4063
+ if (value.a !== void 0) {
4064
+ if (typeof value.a !== "number") {
4065
+ errors.push("a must be a number");
4066
+ } else if (value.a < 0 || value.a > 1) {
4067
+ errors.push("a must be between 0 and 1");
4068
+ }
4069
+ }
4070
+ return { valid: errors.length === 0, errors };
4071
+ }
4072
+ // ===========================================================================
4073
+ // FORMAT
4074
+ // ===========================================================================
4075
+ format(value, options) {
4076
+ const opts = options;
4077
+ const rgba = this.toBase(value);
4078
+ const r = rgba.r;
4079
+ const g = rgba.g;
4080
+ const b = rgba.b;
4081
+ const a = rgba.a;
4082
+ if (opts?.includeAlpha || a < 1) {
4083
+ if (opts?.compact) {
4084
+ return `rgba(${r},${g},${b},${a})`;
4085
+ }
4086
+ return `rgba(${r}, ${g}, ${b}, ${a})`;
4087
+ }
4088
+ if (opts?.compact) {
4089
+ return `rgb(${r},${g},${b})`;
4090
+ }
4091
+ return `rgb(${r}, ${g}, ${b})`;
4092
+ }
4093
+ };
4094
+
4095
+ // src/roles/color/variants/RgbStringVariant.ts
4096
+ var RGB_REGEX = /^rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i;
4097
+ var RGBA_REGEX = /^rgba\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*([\d.]+)\s*\)$/i;
4098
+ var RgbStringVariant = class extends BaseVariant {
4099
+ constructor() {
4100
+ super(...arguments);
4101
+ this.name = "rgb_string";
4102
+ this.type = "string";
4103
+ }
4104
+ // ===========================================================================
4105
+ // CONVERT
4106
+ // ===========================================================================
4107
+ toBase(value) {
4108
+ const str = value.trim();
4109
+ let match = str.match(RGBA_REGEX);
4110
+ if (match) {
4111
+ return {
4112
+ r: normalizeRgbChannel(parseInt(match[1], 10)),
4113
+ g: normalizeRgbChannel(parseInt(match[2], 10)),
4114
+ b: normalizeRgbChannel(parseInt(match[3], 10)),
4115
+ a: normalizeAlpha(parseFloat(match[4]))
4116
+ };
4117
+ }
4118
+ match = str.match(RGB_REGEX);
4119
+ if (match) {
4120
+ return {
4121
+ r: normalizeRgbChannel(parseInt(match[1], 10)),
4122
+ g: normalizeRgbChannel(parseInt(match[2], 10)),
4123
+ b: normalizeRgbChannel(parseInt(match[3], 10)),
4124
+ a: 1
4125
+ };
4126
+ }
4127
+ throw new Error(`Invalid rgb string: ${value}`);
4128
+ }
4129
+ fromBase(base) {
4130
+ const r = normalizeRgbChannel(base.r);
4131
+ const g = normalizeRgbChannel(base.g);
4132
+ const b = normalizeRgbChannel(base.b);
4133
+ const a = normalizeAlpha(base.a);
4134
+ if (a < 1) {
4135
+ return `rgba(${r}, ${g}, ${b}, ${a})`;
4136
+ }
4137
+ return `rgb(${r}, ${g}, ${b})`;
4138
+ }
4139
+ // ===========================================================================
4140
+ // CAST
4141
+ // ===========================================================================
4142
+ cast(input) {
4143
+ if (typeof input === "string") {
4144
+ const trimmed = input.trim().toLowerCase();
4145
+ if (NAMED_COLORS[trimmed]) {
4146
+ return this.fromBase(NAMED_COLORS[trimmed]);
4147
+ }
4148
+ if (RGB_REGEX.test(trimmed) || RGBA_REGEX.test(trimmed)) {
4149
+ try {
4150
+ const rgba = this.toBase(trimmed);
4151
+ return this.fromBase(rgba);
4152
+ } catch {
4153
+ return null;
4154
+ }
4155
+ }
4156
+ const hexMatch = trimmed.match(
4157
+ /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})?$/i
4158
+ );
4159
+ if (hexMatch) {
4160
+ return this.fromBase({
4161
+ r: parseInt(hexMatch[1], 16),
4162
+ g: parseInt(hexMatch[2], 16),
4163
+ b: parseInt(hexMatch[3], 16),
4164
+ a: hexMatch[4] ? parseInt(hexMatch[4], 16) / 255 : 1
4165
+ });
4166
+ }
4167
+ const hexShortMatch = trimmed.match(
4168
+ /^#?([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])?$/i
4169
+ );
4170
+ if (hexShortMatch) {
4171
+ return this.fromBase({
4172
+ r: parseInt(hexShortMatch[1] + hexShortMatch[1], 16),
4173
+ g: parseInt(hexShortMatch[2] + hexShortMatch[2], 16),
4174
+ b: parseInt(hexShortMatch[3] + hexShortMatch[3], 16),
4175
+ a: hexShortMatch[4] ? parseInt(hexShortMatch[4] + hexShortMatch[4], 16) / 255 : 1
4176
+ });
4177
+ }
4178
+ const hslMatch = trimmed.match(
4179
+ /^hsla?\s*\(\s*(\d+)\s*,\s*(\d+)%?\s*,\s*(\d+)%?\s*(?:,\s*([\d.]+))?\s*\)$/
4180
+ );
4181
+ if (hslMatch) {
4182
+ const rgb = hslToRgb(
4183
+ parseInt(hslMatch[1], 10),
4184
+ parseInt(hslMatch[2], 10),
4185
+ parseInt(hslMatch[3], 10)
4186
+ );
4187
+ return this.fromBase({
4188
+ ...rgb,
4189
+ a: hslMatch[4] ? parseFloat(hslMatch[4]) : 1
4190
+ });
4191
+ }
4192
+ return null;
4193
+ }
4194
+ if (typeof input === "object" && input !== null && "r" in input && "g" in input && "b" in input) {
4195
+ const obj = input;
4196
+ return this.fromBase({
4197
+ r: obj.r,
4198
+ g: obj.g,
4199
+ b: obj.b,
4200
+ a: obj.a ?? 1
4201
+ });
4202
+ }
4203
+ if (typeof input === "object" && input !== null && "h" in input && "s" in input && "l" in input) {
4204
+ const obj = input;
4205
+ const rgb = hslToRgb(obj.h, obj.s, obj.l);
4206
+ return this.fromBase({
4207
+ ...rgb,
4208
+ a: obj.a ?? 1
4209
+ });
4210
+ }
4211
+ return null;
4212
+ }
4213
+ // ===========================================================================
4214
+ // VALIDATE
4215
+ // ===========================================================================
4216
+ validate(value) {
4217
+ const errors = [];
4218
+ if (typeof value !== "string") {
4219
+ errors.push("RGB string must be a string");
4220
+ return { valid: false, errors };
4221
+ }
4222
+ const str = value.trim();
4223
+ if (!RGB_REGEX.test(str) && !RGBA_REGEX.test(str)) {
4224
+ errors.push('Invalid format. Use "rgb(r, g, b)" or "rgba(r, g, b, a)"');
4225
+ return { valid: false, errors };
4226
+ }
4227
+ try {
4228
+ const rgba = this.toBase(value);
4229
+ if (rgba.r < 0 || rgba.r > 255) {
4230
+ errors.push("Red value must be between 0 and 255");
4231
+ }
4232
+ if (rgba.g < 0 || rgba.g > 255) {
4233
+ errors.push("Green value must be between 0 and 255");
4234
+ }
4235
+ if (rgba.b < 0 || rgba.b > 255) {
4236
+ errors.push("Blue value must be between 0 and 255");
4237
+ }
4238
+ if (rgba.a < 0 || rgba.a > 1) {
4239
+ errors.push("Alpha value must be between 0 and 1");
4240
+ }
4241
+ } catch {
4242
+ errors.push("Invalid RGB string format");
4243
+ }
4244
+ return { valid: errors.length === 0, errors };
4245
+ }
4246
+ // ===========================================================================
4247
+ // FORMAT
4248
+ // ===========================================================================
4249
+ format(value, options) {
4250
+ const opts = options;
4251
+ try {
4252
+ const rgba = this.toBase(value);
4253
+ const r = rgba.r;
4254
+ const g = rgba.g;
4255
+ const b = rgba.b;
4256
+ const a = rgba.a;
4257
+ if (opts?.includeAlpha || a < 1) {
4258
+ if (opts?.compact) {
4259
+ return `rgba(${r},${g},${b},${a})`;
4260
+ }
4261
+ return `rgba(${r}, ${g}, ${b}, ${a})`;
4262
+ }
4263
+ if (opts?.compact) {
4264
+ return `rgb(${r},${g},${b})`;
4265
+ }
4266
+ return `rgb(${r}, ${g}, ${b})`;
4267
+ } catch {
4268
+ return value;
4269
+ }
4270
+ }
4271
+ };
4272
+
4273
+ // src/roles/color/variants/HslObjectVariant.ts
4274
+ var HslObjectVariant = class extends BaseVariant {
4275
+ constructor() {
4276
+ super(...arguments);
4277
+ this.name = "hsl_object";
4278
+ this.type = "object";
4279
+ }
4280
+ // ===========================================================================
4281
+ // CONVERT
4282
+ // ===========================================================================
4283
+ toBase(value) {
4284
+ const h = clamp(value.h, 0, 360);
4285
+ const s = clamp(value.s, 0, 100);
4286
+ const l = clamp(value.l, 0, 100);
4287
+ const a = normalizeAlpha(value.a);
4288
+ const rgb = hslToRgb(h, s, l);
4289
+ return {
4290
+ r: rgb.r,
4291
+ g: rgb.g,
4292
+ b: rgb.b,
4293
+ a
4294
+ };
4295
+ }
4296
+ fromBase(base) {
4297
+ const hsl = rgbToHsl(base.r, base.g, base.b);
4298
+ const result = {
4299
+ h: hsl.h,
4300
+ s: hsl.s,
4301
+ l: hsl.l
4302
+ };
4303
+ if (base.a !== void 0 && base.a < 1) {
4304
+ result.a = normalizeAlpha(base.a);
4305
+ }
4306
+ return result;
4307
+ }
4308
+ // ===========================================================================
4309
+ // CAST
4310
+ // ===========================================================================
4311
+ cast(input) {
4312
+ if (typeof input === "object" && input !== null && "h" in input && "s" in input && "l" in input) {
4313
+ const obj = input;
4314
+ if (typeof obj.h !== "number" || typeof obj.s !== "number" || typeof obj.l !== "number") {
4315
+ return null;
4316
+ }
4317
+ const result = {
4318
+ h: clamp(Math.round(obj.h), 0, 360),
4319
+ s: clamp(Math.round(obj.s), 0, 100),
4320
+ l: clamp(Math.round(obj.l), 0, 100)
4321
+ };
4322
+ if (typeof obj.a === "number") {
4323
+ result.a = normalizeAlpha(obj.a);
4324
+ }
4325
+ return result;
4326
+ }
4327
+ if (typeof input === "object" && input !== null && "r" in input && "g" in input && "b" in input) {
4328
+ const obj = input;
4329
+ return this.fromBase({
4330
+ r: obj.r,
4331
+ g: obj.g,
4332
+ b: obj.b,
4333
+ a: obj.a ?? 1
4334
+ });
4335
+ }
4336
+ if (typeof input === "string") {
4337
+ const trimmed = input.trim().toLowerCase();
4338
+ if (NAMED_COLORS[trimmed]) {
4339
+ return this.fromBase(NAMED_COLORS[trimmed]);
4340
+ }
4341
+ const hexMatch = trimmed.match(
4342
+ /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})?$/i
4343
+ );
4344
+ if (hexMatch) {
4345
+ return this.fromBase({
4346
+ r: parseInt(hexMatch[1], 16),
4347
+ g: parseInt(hexMatch[2], 16),
4348
+ b: parseInt(hexMatch[3], 16),
4349
+ a: hexMatch[4] ? parseInt(hexMatch[4], 16) / 255 : 1
4350
+ });
4351
+ }
4352
+ const hexShortMatch = trimmed.match(
4353
+ /^#?([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])?$/i
4354
+ );
4355
+ if (hexShortMatch) {
4356
+ return this.fromBase({
4357
+ r: parseInt(hexShortMatch[1] + hexShortMatch[1], 16),
4358
+ g: parseInt(hexShortMatch[2] + hexShortMatch[2], 16),
4359
+ b: parseInt(hexShortMatch[3] + hexShortMatch[3], 16),
4360
+ a: hexShortMatch[4] ? parseInt(hexShortMatch[4] + hexShortMatch[4], 16) / 255 : 1
4361
+ });
4362
+ }
4363
+ const rgbMatch = trimmed.match(
4364
+ /^rgba?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*([\d.]+))?\s*\)$/
4365
+ );
4366
+ if (rgbMatch) {
4367
+ return this.fromBase({
4368
+ r: parseInt(rgbMatch[1], 10),
4369
+ g: parseInt(rgbMatch[2], 10),
4370
+ b: parseInt(rgbMatch[3], 10),
4371
+ a: rgbMatch[4] ? parseFloat(rgbMatch[4]) : 1
4372
+ });
4373
+ }
4374
+ const hslMatch = trimmed.match(
4375
+ /^hsla?\s*\(\s*(\d+)\s*,\s*(\d+)%?\s*,\s*(\d+)%?\s*(?:,\s*([\d.]+))?\s*\)$/
4376
+ );
4377
+ if (hslMatch) {
4378
+ const result = {
4379
+ h: clamp(parseInt(hslMatch[1], 10), 0, 360),
4380
+ s: clamp(parseInt(hslMatch[2], 10), 0, 100),
4381
+ l: clamp(parseInt(hslMatch[3], 10), 0, 100)
4382
+ };
4383
+ if (hslMatch[4]) {
4384
+ result.a = normalizeAlpha(parseFloat(hslMatch[4]));
4385
+ }
4386
+ return result;
4387
+ }
4388
+ }
4389
+ return null;
4390
+ }
4391
+ // ===========================================================================
4392
+ // VALIDATE
4393
+ // ===========================================================================
4394
+ validate(value) {
4395
+ const errors = [];
4396
+ if (typeof value !== "object" || value === null) {
4397
+ errors.push("HSL color must be an object");
4398
+ return { valid: false, errors };
4399
+ }
4400
+ if (!("h" in value) || !("s" in value) || !("l" in value)) {
4401
+ errors.push("HSL color must have h, s, l properties");
4402
+ return { valid: false, errors };
4403
+ }
4404
+ if (typeof value.h !== "number" || typeof value.s !== "number" || typeof value.l !== "number") {
4405
+ errors.push("h, s, l must be numbers");
4406
+ return { valid: false, errors };
4407
+ }
4408
+ if (value.h < 0 || value.h > 360) {
4409
+ errors.push("h (hue) must be between 0 and 360");
4410
+ }
4411
+ if (value.s < 0 || value.s > 100) {
4412
+ errors.push("s (saturation) must be between 0 and 100");
4413
+ }
4414
+ if (value.l < 0 || value.l > 100) {
4415
+ errors.push("l (lightness) must be between 0 and 100");
4416
+ }
4417
+ if (value.a !== void 0) {
4418
+ if (typeof value.a !== "number") {
4419
+ errors.push("a must be a number");
4420
+ } else if (value.a < 0 || value.a > 1) {
4421
+ errors.push("a must be between 0 and 1");
4422
+ }
4423
+ }
4424
+ return { valid: errors.length === 0, errors };
4425
+ }
4426
+ // ===========================================================================
4427
+ // FORMAT
4428
+ // ===========================================================================
4429
+ format(value, options) {
4430
+ const opts = options;
4431
+ const h = clamp(Math.round(value.h), 0, 360);
4432
+ const s = clamp(Math.round(value.s), 0, 100);
4433
+ const l = clamp(Math.round(value.l), 0, 100);
4434
+ const a = value.a !== void 0 ? normalizeAlpha(value.a) : 1;
4435
+ if (opts?.includeAlpha || a < 1) {
4436
+ if (opts?.compact) {
4437
+ return `hsla(${h},${s}%,${l}%,${a})`;
4438
+ }
4439
+ return `hsla(${h}, ${s}%, ${l}%, ${a})`;
4440
+ }
4441
+ if (opts?.compact) {
4442
+ return `hsl(${h},${s}%,${l}%)`;
4443
+ }
4444
+ return `hsl(${h}, ${s}%, ${l}%)`;
4445
+ }
4446
+ };
4447
+
4448
+ // src/roles/color/variants/HslStringVariant.ts
4449
+ var HSL_REGEX = /^hsl\s*\(\s*(\d+)\s*,\s*(\d+)%?\s*,\s*(\d+)%?\s*\)$/i;
4450
+ var HSLA_REGEX = /^hsla\s*\(\s*(\d+)\s*,\s*(\d+)%?\s*,\s*(\d+)%?\s*,\s*([\d.]+)\s*\)$/i;
4451
+ var HslStringVariant = class extends BaseVariant {
4452
+ constructor() {
4453
+ super(...arguments);
4454
+ this.name = "hsl_string";
4455
+ this.type = "string";
4456
+ }
4457
+ // ===========================================================================
4458
+ // CONVERT
4459
+ // ===========================================================================
4460
+ toBase(value) {
4461
+ const str = value.trim();
4462
+ let match = str.match(HSLA_REGEX);
4463
+ if (match) {
4464
+ const h = clamp(parseInt(match[1], 10), 0, 360);
4465
+ const s = clamp(parseInt(match[2], 10), 0, 100);
4466
+ const l = clamp(parseInt(match[3], 10), 0, 100);
4467
+ const a = normalizeAlpha(parseFloat(match[4]));
4468
+ const rgb = hslToRgb(h, s, l);
4469
+ return { ...rgb, a };
4470
+ }
4471
+ match = str.match(HSL_REGEX);
4472
+ if (match) {
4473
+ const h = clamp(parseInt(match[1], 10), 0, 360);
4474
+ const s = clamp(parseInt(match[2], 10), 0, 100);
4475
+ const l = clamp(parseInt(match[3], 10), 0, 100);
4476
+ const rgb = hslToRgb(h, s, l);
4477
+ return { ...rgb, a: 1 };
4478
+ }
4479
+ throw new Error(`Invalid hsl string: ${value}`);
4480
+ }
4481
+ fromBase(base) {
4482
+ const hsl = rgbToHsl(base.r, base.g, base.b);
4483
+ const a = normalizeAlpha(base.a);
4484
+ if (a < 1) {
4485
+ return `hsla(${hsl.h}, ${hsl.s}%, ${hsl.l}%, ${a})`;
4486
+ }
4487
+ return `hsl(${hsl.h}, ${hsl.s}%, ${hsl.l}%)`;
4488
+ }
4489
+ // ===========================================================================
4490
+ // CAST
4491
+ // ===========================================================================
4492
+ cast(input) {
4493
+ if (typeof input === "string") {
4494
+ const trimmed = input.trim().toLowerCase();
4495
+ if (NAMED_COLORS[trimmed]) {
4496
+ return this.fromBase(NAMED_COLORS[trimmed]);
4497
+ }
4498
+ if (HSL_REGEX.test(trimmed) || HSLA_REGEX.test(trimmed)) {
4499
+ try {
4500
+ const rgba = this.toBase(trimmed);
4501
+ return this.fromBase(rgba);
4502
+ } catch {
4503
+ return null;
4504
+ }
4505
+ }
4506
+ const hexMatch = trimmed.match(
4507
+ /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})?$/i
4508
+ );
4509
+ if (hexMatch) {
4510
+ return this.fromBase({
4511
+ r: parseInt(hexMatch[1], 16),
4512
+ g: parseInt(hexMatch[2], 16),
4513
+ b: parseInt(hexMatch[3], 16),
4514
+ a: hexMatch[4] ? parseInt(hexMatch[4], 16) / 255 : 1
4515
+ });
4516
+ }
4517
+ const hexShortMatch = trimmed.match(
4518
+ /^#?([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])?$/i
4519
+ );
4520
+ if (hexShortMatch) {
4521
+ return this.fromBase({
4522
+ r: parseInt(hexShortMatch[1] + hexShortMatch[1], 16),
4523
+ g: parseInt(hexShortMatch[2] + hexShortMatch[2], 16),
4524
+ b: parseInt(hexShortMatch[3] + hexShortMatch[3], 16),
4525
+ a: hexShortMatch[4] ? parseInt(hexShortMatch[4] + hexShortMatch[4], 16) / 255 : 1
4526
+ });
4527
+ }
4528
+ const rgbMatch = trimmed.match(
4529
+ /^rgba?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*([\d.]+))?\s*\)$/
4530
+ );
4531
+ if (rgbMatch) {
4532
+ return this.fromBase({
4533
+ r: parseInt(rgbMatch[1], 10),
4534
+ g: parseInt(rgbMatch[2], 10),
4535
+ b: parseInt(rgbMatch[3], 10),
4536
+ a: rgbMatch[4] ? parseFloat(rgbMatch[4]) : 1
4537
+ });
4538
+ }
4539
+ return null;
4540
+ }
4541
+ if (typeof input === "object" && input !== null && "r" in input && "g" in input && "b" in input) {
4542
+ const obj = input;
4543
+ return this.fromBase({
4544
+ r: obj.r,
4545
+ g: obj.g,
4546
+ b: obj.b,
4547
+ a: obj.a ?? 1
4548
+ });
4549
+ }
4550
+ if (typeof input === "object" && input !== null && "h" in input && "s" in input && "l" in input) {
4551
+ const obj = input;
4552
+ const rgb = hslToRgb(obj.h, obj.s, obj.l);
4553
+ return this.fromBase({
4554
+ ...rgb,
4555
+ a: obj.a ?? 1
4556
+ });
4557
+ }
4558
+ return null;
4559
+ }
4560
+ // ===========================================================================
4561
+ // VALIDATE
4562
+ // ===========================================================================
4563
+ validate(value) {
4564
+ const errors = [];
4565
+ if (typeof value !== "string") {
4566
+ errors.push("HSL string must be a string");
4567
+ return { valid: false, errors };
4568
+ }
4569
+ const str = value.trim();
4570
+ if (!HSL_REGEX.test(str) && !HSLA_REGEX.test(str)) {
4571
+ errors.push(
4572
+ 'Invalid format. Use "hsl(h, s%, l%)" or "hsla(h, s%, l%, a)"'
4573
+ );
4574
+ return { valid: false, errors };
4575
+ }
4576
+ try {
4577
+ const rgba = this.toBase(value);
4578
+ const hsl = rgbToHsl(rgba.r, rgba.g, rgba.b);
4579
+ if (hsl.h < 0 || hsl.h > 360) {
4580
+ errors.push("Hue must be between 0 and 360");
4581
+ }
4582
+ if (hsl.s < 0 || hsl.s > 100) {
4583
+ errors.push("Saturation must be between 0 and 100");
4584
+ }
4585
+ if (hsl.l < 0 || hsl.l > 100) {
4586
+ errors.push("Lightness must be between 0 and 100");
4587
+ }
4588
+ if (rgba.a < 0 || rgba.a > 1) {
4589
+ errors.push("Alpha must be between 0 and 1");
4590
+ }
4591
+ } catch {
4592
+ errors.push("Invalid HSL string format");
4593
+ }
4594
+ return { valid: errors.length === 0, errors };
4595
+ }
4596
+ // ===========================================================================
4597
+ // FORMAT
4598
+ // ===========================================================================
4599
+ format(value, options) {
4600
+ const opts = options;
4601
+ try {
4602
+ const rgba = this.toBase(value);
4603
+ const hsl = rgbToHsl(rgba.r, rgba.g, rgba.b);
4604
+ const a = rgba.a;
4605
+ if (opts?.includeAlpha || a < 1) {
4606
+ if (opts?.compact) {
4607
+ return `hsla(${hsl.h},${hsl.s}%,${hsl.l}%,${a})`;
4608
+ }
4609
+ return `hsla(${hsl.h}, ${hsl.s}%, ${hsl.l}%, ${a})`;
4610
+ }
4611
+ if (opts?.compact) {
4612
+ return `hsl(${hsl.h},${hsl.s}%,${hsl.l}%)`;
4613
+ }
4614
+ return `hsl(${hsl.h}, ${hsl.s}%, ${hsl.l}%)`;
4615
+ } catch {
4616
+ return value;
4617
+ }
4618
+ }
4619
+ };
4620
+
4621
+ // src/roles/color/ColorRole.ts
4622
+ var ColorRole = class extends ComplexRole {
4623
+ constructor() {
4624
+ super(...arguments);
4625
+ this.name = "color";
4626
+ this.base = "rgb_object";
4627
+ }
4628
+ createVariants() {
4629
+ return {
4630
+ hex: new HexVariant(),
4631
+ rgb_object: new RgbObjectVariant(),
4632
+ rgb_string: new RgbStringVariant(),
4633
+ hsl_object: new HslObjectVariant(),
4634
+ hsl_string: new HslStringVariant()
4635
+ };
4636
+ }
4637
+ /**
4638
+ * Override format to accept ColorFormatOptions
4639
+ */
4640
+ format(variant, value, options) {
4641
+ return super.format(variant, value, options);
4642
+ }
4643
+ };
4644
+ var colorRole = new ColorRole();
4645
+
4646
+ export { AngleRole, AreaRole, ColorRole, DateRole, DigitalRole, EnergyRole, FrequencyRole, LengthRole, MassRole, PowerRole, PressureRole, RoleMorphic, SpeedRole, TemperatureRole, TimeRole, VolumeRole, angleRole, areaRole, colorRole, dateRole, digitalRole, energyRole, frequencyRole, lengthRole, massRole, powerRole, pressureRole, speedRole, temperatureRole, timeRole, volumeRole };
4647
+ //# sourceMappingURL=index.mjs.map
4648
+ //# sourceMappingURL=index.mjs.map