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