@attrx/role-morphic 0.1.0 → 0.2.1
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 +151 -84
- package/dist/cast.d.mts +678 -0
- package/dist/cast.d.ts +678 -0
- package/dist/cast.js +3569 -0
- package/dist/cast.js.map +1 -0
- package/dist/cast.mjs +3536 -0
- package/dist/cast.mjs.map +1 -0
- package/dist/constants-BZdBwuvJ.d.mts +168 -0
- package/dist/constants-BZdBwuvJ.d.ts +168 -0
- package/dist/convert.d.mts +1157 -0
- package/dist/convert.d.ts +1157 -0
- package/dist/convert.js +2244 -0
- package/dist/convert.js.map +1 -0
- package/dist/convert.mjs +2148 -0
- package/dist/convert.mjs.map +1 -0
- package/dist/format-Be15LzfS.d.ts +668 -0
- package/dist/format-o4Y3jPH-.d.mts +668 -0
- package/dist/format.d.mts +3 -0
- package/dist/format.d.ts +3 -0
- package/dist/format.js +2303 -0
- package/dist/format.js.map +1 -0
- package/dist/format.mjs +2286 -0
- package/dist/format.mjs.map +1 -0
- package/dist/index.d.mts +200 -601
- package/dist/index.d.ts +200 -601
- package/dist/index.js +5536 -2493
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +5349 -2494
- package/dist/index.mjs.map +1 -1
- package/dist/types-mbeS1e-k.d.mts +312 -0
- package/dist/types-mbeS1e-k.d.ts +312 -0
- package/dist/validate.d.mts +884 -0
- package/dist/validate.d.ts +884 -0
- package/dist/validate.js +713 -0
- package/dist/validate.js.map +1 -0
- package/dist/validate.mjs +669 -0
- package/dist/validate.mjs.map +1 -0
- package/package.json +21 -1
package/README.md
CHANGED
|
@@ -23,10 +23,10 @@ Cada role implementa 4 operações fundamentais:
|
|
|
23
23
|
|
|
24
24
|
## Uso Rápido
|
|
25
25
|
|
|
26
|
-
###
|
|
26
|
+
### Import Completo
|
|
27
27
|
|
|
28
28
|
```typescript
|
|
29
|
-
import { areaRole, lengthRole, colorRole, dateRole } from '@attrx/role-morphic';
|
|
29
|
+
import { areaRole, lengthRole, colorRole, dateRole, currencyRole } from '@attrx/role-morphic';
|
|
30
30
|
|
|
31
31
|
// Area
|
|
32
32
|
areaRole.convert('hectare', 'acre', 1); // 2.47105
|
|
@@ -44,7 +44,27 @@ colorRole.format('hex', '#ff0000', { uppercase: true }); // "#FF0000"
|
|
|
44
44
|
|
|
45
45
|
// Date
|
|
46
46
|
dateRole.convert('iso', 'timestamp', '2024-12-05T00:00:00.000Z'); // 1733356800000
|
|
47
|
-
|
|
47
|
+
|
|
48
|
+
// Currency (sem convert - só validate/cast/format)
|
|
49
|
+
currencyRole.cast('R$ 1.500,50'); // 1500.50
|
|
50
|
+
currencyRole.format(1500.50, { currency: 'BRL' }); // "R$1.500,50"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Import por Pilar (Tree-Shaking)
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
// Apenas validate (zero dependências!)
|
|
57
|
+
import { validateArea } from '@attrx/role-morphic/area/validate';
|
|
58
|
+
import { validateDate } from '@attrx/role-morphic/date/validate';
|
|
59
|
+
|
|
60
|
+
// Apenas convert
|
|
61
|
+
import { convertLength, toBaseLength } from '@attrx/role-morphic/length/convert';
|
|
62
|
+
|
|
63
|
+
// Apenas cast
|
|
64
|
+
import { castCurrency, tryCastCurrency } from '@attrx/role-morphic/currency/cast';
|
|
65
|
+
|
|
66
|
+
// Apenas format
|
|
67
|
+
import { formatColor, formatHex } from '@attrx/role-morphic/color/format';
|
|
48
68
|
```
|
|
49
69
|
|
|
50
70
|
### Via RoleMorphic (Registry)
|
|
@@ -67,40 +87,63 @@ if (result.ok) {
|
|
|
67
87
|
}
|
|
68
88
|
```
|
|
69
89
|
|
|
90
|
+
## Estrutura de Arquivos
|
|
91
|
+
|
|
92
|
+
Cada role segue o padrão:
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
src/roles/{role}/
|
|
96
|
+
├── {Role}Role.ts # Classe + singleton
|
|
97
|
+
├── constants.ts # Tipos e configurações
|
|
98
|
+
├── validate.ts # Pilar validate (standalone, zero deps)
|
|
99
|
+
├── convert.ts # Pilar convert
|
|
100
|
+
├── cast.ts # Pilar cast
|
|
101
|
+
├── format.ts # Pilar format
|
|
102
|
+
├── index.ts # Exports organizados
|
|
103
|
+
└── {Role}Role.test.ts # Testes
|
|
104
|
+
```
|
|
105
|
+
|
|
70
106
|
## API
|
|
71
107
|
|
|
108
|
+
### Funções Standalone (por pilar)
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
// Validate
|
|
112
|
+
validateArea(100); // { valid: true, errors: [] }
|
|
113
|
+
isValidArea(100); // true
|
|
114
|
+
|
|
115
|
+
// Convert
|
|
116
|
+
convertArea('hectare', 'acre', 1); // 2.47105
|
|
117
|
+
toBaseArea('hectare', 1); // 10000 (→ m²)
|
|
118
|
+
fromBaseArea('acre', 10000); // 2.47105
|
|
119
|
+
|
|
120
|
+
// Cast
|
|
121
|
+
castArea('100 ha'); // 100
|
|
122
|
+
castArea('100 ha', 'acre'); // 247.105 (converte)
|
|
123
|
+
tryCastArea('invalid'); // { ok: false, error: '...' }
|
|
124
|
+
|
|
125
|
+
// Format
|
|
126
|
+
formatArea('hectare', 2.5); // "2.5 ha"
|
|
127
|
+
formatArea('hectare', 2.5, { verbose: true }); // "2.5 hectares"
|
|
128
|
+
```
|
|
129
|
+
|
|
72
130
|
### Role Instance Methods
|
|
73
131
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
| `getVariants()` | `string[]` | Não |
|
|
86
|
-
| `hasVariant(name)` | `boolean` | Não |
|
|
87
|
-
| `toSpec()` | `RoleSpec` | Não |
|
|
88
|
-
|
|
89
|
-
### RoleMorphic Methods
|
|
90
|
-
|
|
91
|
-
| Método | Retorno | Throws |
|
|
92
|
-
|--------|---------|--------|
|
|
93
|
-
| `register(id, spec)` | `void` | Sim (duplicado) |
|
|
94
|
-
| `convert(from, to, value)` | `T` | Sim |
|
|
95
|
-
| `tryConvert(from, to, value)` | `Result<T>` | Não |
|
|
96
|
-
| `hasRole(id)` | `boolean` | Não |
|
|
97
|
-
| `hasVariant(fullId)` | `boolean` | Não |
|
|
98
|
-
| `listRoles()` | `string[]` | Não |
|
|
99
|
-
| `listVariants(roleId)` | `string[]` | Não |
|
|
132
|
+
```typescript
|
|
133
|
+
import { areaRole } from '@attrx/role-morphic';
|
|
134
|
+
|
|
135
|
+
areaRole.convert('hectare', 'acre', 1);
|
|
136
|
+
areaRole.cast('hectare', '100 ha');
|
|
137
|
+
areaRole.validate('hectare', 100);
|
|
138
|
+
areaRole.format('hectare', 2.5);
|
|
139
|
+
areaRole.getVariants(); // ['square_meter', 'hectare', ...]
|
|
140
|
+
areaRole.hasVariant('hectare'); // true
|
|
141
|
+
areaRole.toSpec(); // RoleSpec para RoleMorphic
|
|
142
|
+
```
|
|
100
143
|
|
|
101
144
|
## Roles Disponíveis
|
|
102
145
|
|
|
103
|
-
### SimpleRoles (Numéricas)
|
|
146
|
+
### SimpleRoles (Numéricas - 4 pilares)
|
|
104
147
|
|
|
105
148
|
| Role | Base | Variantes | Exemplo |
|
|
106
149
|
|------|------|-----------|---------|
|
|
@@ -115,36 +158,32 @@ Cada role exporta uma instância singleton (ex: `areaRole`, `lengthRole`):
|
|
|
115
158
|
| **Power** | watt | 11 | kw, hp, btu/h |
|
|
116
159
|
| **Pressure** | pascal | 12 | bar, psi, atm |
|
|
117
160
|
| **Frequency** | hertz | 9 | khz, mhz, rpm |
|
|
118
|
-
| **Angle** |
|
|
161
|
+
| **Angle** | degree | 7 | radian, turn, grad |
|
|
119
162
|
| **Digital** | byte | 12 | kb, mb, gb, gib |
|
|
120
163
|
|
|
121
|
-
### ComplexRoles (Heterogêneas)
|
|
164
|
+
### ComplexRoles (Heterogêneas - 4 pilares)
|
|
122
165
|
|
|
123
166
|
| Role | Base | Variantes |
|
|
124
167
|
|------|------|-----------|
|
|
125
168
|
| **Color** | rgb_object | hex, rgb_string, hsl_object, hsl_string |
|
|
126
169
|
| **Date** | timestamp | iso, epoch |
|
|
127
170
|
|
|
171
|
+
### MetadataRoles (3 pilares - sem convert)
|
|
172
|
+
|
|
173
|
+
| Role | Pilares | Moedas |
|
|
174
|
+
|------|---------|--------|
|
|
175
|
+
| **Currency** | validate, cast, format | BRL, USD, EUR, GBP, JPY, +15 |
|
|
176
|
+
|
|
128
177
|
## Exemplos por Role
|
|
129
178
|
|
|
130
179
|
### Area
|
|
131
180
|
|
|
132
181
|
```typescript
|
|
133
|
-
import { areaRole } from '@attrx/role-morphic';
|
|
182
|
+
import { areaRole, convertArea, formatArea } from '@attrx/role-morphic';
|
|
134
183
|
|
|
135
184
|
areaRole.convert('hectare', 'acre', 1); // 2.47105
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
### Length
|
|
141
|
-
|
|
142
|
-
```typescript
|
|
143
|
-
import { lengthRole } from '@attrx/role-morphic';
|
|
144
|
-
|
|
145
|
-
lengthRole.convert('mile', 'kilometer', 1); // 1.609344
|
|
146
|
-
lengthRole.convert('inch', 'centimeter', 1); // 2.54
|
|
147
|
-
lengthRole.cast('meter', '100 m'); // 100
|
|
185
|
+
convertArea('square_meter', 'hectare', 10000); // 1
|
|
186
|
+
formatArea('hectare', 2.5, { verbose: true }); // "2.5 hectares"
|
|
148
187
|
```
|
|
149
188
|
|
|
150
189
|
### Temperature
|
|
@@ -160,42 +199,55 @@ temperatureRole.format('celsius', 25); // "25 °C"
|
|
|
160
199
|
### Color
|
|
161
200
|
|
|
162
201
|
```typescript
|
|
163
|
-
import { colorRole } from '@attrx/role-morphic';
|
|
202
|
+
import { colorRole, convertColor, hexToRgb } from '@attrx/role-morphic';
|
|
164
203
|
|
|
165
204
|
// Hex → RGB
|
|
166
|
-
|
|
167
|
-
// { r: 255, g: 0, b: 0
|
|
168
|
-
|
|
169
|
-
//
|
|
170
|
-
|
|
171
|
-
// { h: 0, s: 100, l: 50
|
|
172
|
-
|
|
173
|
-
//
|
|
174
|
-
colorRole.format('hex', '#ff0000', { uppercase: true }); // "#FF0000"
|
|
175
|
-
colorRole.format('rgb_string', { r: 255, g: 0, b: 0, a: 1 }, { compact: true });
|
|
205
|
+
convertColor('hex', 'rgb_object', '#ff0000');
|
|
206
|
+
// { r: 255, g: 0, b: 0 }
|
|
207
|
+
|
|
208
|
+
// Convenience functions
|
|
209
|
+
hexToRgb('#ff0000'); // { r: 255, g: 0, b: 0 }
|
|
210
|
+
hexToHsl('#ff0000'); // { h: 0, s: 100, l: 50 }
|
|
211
|
+
isValidColor('#ff0000'); // true
|
|
212
|
+
isValidColor('red'); // true (named colors)
|
|
176
213
|
```
|
|
177
214
|
|
|
178
215
|
### Date
|
|
179
216
|
|
|
180
217
|
```typescript
|
|
181
|
-
import { dateRole } from '@attrx/role-morphic';
|
|
218
|
+
import { dateRole, convertDate, formatDate } from '@attrx/role-morphic';
|
|
182
219
|
|
|
183
220
|
// ISO → Timestamp
|
|
184
|
-
|
|
221
|
+
convertDate('iso', 'timestamp', '2024-12-05T00:00:00.000Z');
|
|
185
222
|
// 1733356800000
|
|
186
223
|
|
|
187
|
-
//
|
|
188
|
-
|
|
189
|
-
// "2024-12-05T00:00:00.000Z"
|
|
190
|
-
|
|
191
|
-
// Format options
|
|
192
|
-
dateRole.format('iso', '2024-12-05T10:30:00.000Z', {
|
|
224
|
+
// Format with locale
|
|
225
|
+
formatDate('iso', '2024-12-05T10:30:00.000Z', {
|
|
193
226
|
dateStyle: 'long',
|
|
194
|
-
timeStyle: 'short',
|
|
195
227
|
locale: 'pt-BR',
|
|
196
228
|
});
|
|
197
229
|
```
|
|
198
230
|
|
|
231
|
+
### Currency
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
import { currencyRole, castCurrency, formatCurrency } from '@attrx/role-morphic';
|
|
235
|
+
|
|
236
|
+
// Cast (parseia string → número)
|
|
237
|
+
castCurrency('R$ 1.500,50'); // 1500.50
|
|
238
|
+
castCurrency('$1,000.00'); // 1000
|
|
239
|
+
castCurrency('€ 99,99'); // 99.99
|
|
240
|
+
|
|
241
|
+
// Format (número → string com moeda)
|
|
242
|
+
formatCurrency(1500.50, { currency: 'BRL' }); // "R$1.500,50"
|
|
243
|
+
formatCurrency(1000, { currency: 'USD' }); // "$1,000.00"
|
|
244
|
+
|
|
245
|
+
// Validate
|
|
246
|
+
currencyRole.validate(100.50); // { valid: true }
|
|
247
|
+
currencyRole.validate(-50); // { valid: false } (por padrão)
|
|
248
|
+
currencyRole.validate(-50, { allowNegative: true }); // { valid: true }
|
|
249
|
+
```
|
|
250
|
+
|
|
199
251
|
## FormatOptions
|
|
200
252
|
|
|
201
253
|
### Base (todas as roles)
|
|
@@ -231,11 +283,25 @@ type DateFormatOptions = BaseFormatOptions & {
|
|
|
231
283
|
};
|
|
232
284
|
```
|
|
233
285
|
|
|
286
|
+
### Currency
|
|
287
|
+
|
|
288
|
+
```typescript
|
|
289
|
+
type CurrencyFormatOptions = {
|
|
290
|
+
currency: CurrencyCode; // 'BRL', 'USD', 'EUR', etc.
|
|
291
|
+
locale?: string;
|
|
292
|
+
decimals?: number;
|
|
293
|
+
verbose?: boolean; // "1.500,50 Brazilian reais"
|
|
294
|
+
hideSymbol?: boolean;
|
|
295
|
+
showPositiveSign?: boolean;
|
|
296
|
+
};
|
|
297
|
+
```
|
|
298
|
+
|
|
234
299
|
## Arquitetura
|
|
235
300
|
|
|
236
301
|
- **Hub-and-Spoke**: Toda conversão passa pela variante base (2N vs N² funções)
|
|
237
302
|
- **SimpleRole**: Para roles numéricas - usa `factors` para conversão linear
|
|
238
303
|
- **ComplexRole**: Para roles heterogêneas - cada variante implementa `IVariant`
|
|
304
|
+
- **MetadataRole**: Para roles onde o "tipo" é metadata (ex: currency code)
|
|
239
305
|
|
|
240
306
|
Ver [docs/ARCHITECTURE.md](./docs/ARCHITECTURE.md) para detalhes.
|
|
241
307
|
|
|
@@ -246,21 +312,21 @@ Ver [docs/ARCHITECTURE.md](./docs/ARCHITECTURE.md) para detalhes.
|
|
|
246
312
|
```typescript
|
|
247
313
|
import { SimpleRole, SimpleUnitConfig } from '@attrx/role-morphic';
|
|
248
314
|
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
315
|
+
const DISTANCE_UNITS: Record<string, SimpleUnitConfig> = {
|
|
316
|
+
meter: { factor: 1, symbol: 'm' },
|
|
317
|
+
lightyear: { factor: 9.461e15, symbol: 'ly' },
|
|
318
|
+
parsec: { factor: 3.086e16, symbol: 'pc' },
|
|
253
319
|
};
|
|
254
320
|
|
|
255
|
-
class
|
|
256
|
-
readonly name = '
|
|
257
|
-
readonly base = '
|
|
258
|
-
readonly units =
|
|
259
|
-
readonly aliases = {
|
|
321
|
+
class AstronomicalRole extends SimpleRole {
|
|
322
|
+
readonly name = 'astronomical';
|
|
323
|
+
readonly base = 'meter';
|
|
324
|
+
readonly units = DISTANCE_UNITS;
|
|
325
|
+
readonly aliases = { ly: 'lightyear' };
|
|
260
326
|
}
|
|
261
327
|
|
|
262
|
-
const
|
|
263
|
-
|
|
328
|
+
const astroRole = new AstronomicalRole();
|
|
329
|
+
astroRole.convert('lightyear', 'parsec', 1); // 0.3066
|
|
264
330
|
```
|
|
265
331
|
|
|
266
332
|
### Via RoleSpec
|
|
@@ -268,24 +334,25 @@ currencyRole.convert('brl', 'usd', 100); // 20
|
|
|
268
334
|
```typescript
|
|
269
335
|
import { RoleMorphic, RoleSpec } from '@attrx/role-morphic';
|
|
270
336
|
|
|
271
|
-
const
|
|
272
|
-
base: '
|
|
337
|
+
const percentSpec: RoleSpec<number> = {
|
|
338
|
+
base: 'decimal',
|
|
273
339
|
variants: {
|
|
274
|
-
|
|
340
|
+
decimal: {
|
|
275
341
|
type: 'number',
|
|
276
342
|
toBase: (v) => v,
|
|
277
343
|
fromBase: (v) => v,
|
|
278
344
|
},
|
|
279
|
-
|
|
345
|
+
percent: {
|
|
280
346
|
type: 'number',
|
|
281
|
-
toBase: (
|
|
282
|
-
fromBase: (
|
|
347
|
+
toBase: (p) => p / 100,
|
|
348
|
+
fromBase: (d) => d * 100,
|
|
283
349
|
},
|
|
284
350
|
},
|
|
285
351
|
};
|
|
286
352
|
|
|
287
353
|
const morph = new RoleMorphic();
|
|
288
|
-
morph.register('
|
|
354
|
+
morph.register('percent', percentSpec);
|
|
355
|
+
morph.convert('percent:percent', 'percent:decimal', 50); // 0.5
|
|
289
356
|
```
|
|
290
357
|
|
|
291
358
|
## Testes
|
|
@@ -294,7 +361,7 @@ morph.register('currency', currencySpec);
|
|
|
294
361
|
pnpm test
|
|
295
362
|
```
|
|
296
363
|
|
|
297
|
-
**Status:**
|
|
364
|
+
**Status:** 16 roles, 1407 testes
|
|
298
365
|
|
|
299
366
|
## Licença
|
|
300
367
|
|