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