@reharik/smart-enum 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +878 -0
  2. package/package.json +2 -2
package/README.md ADDED
@@ -0,0 +1,878 @@
1
+ # Smart Enums
2
+
3
+ A TypeScript library for creating type-safe, feature-rich enumerations with built-in utility methods. Stop juggling between constants, arrays, objects, and string unions - use Smart Enums instead.
4
+
5
+ ## CAVEAT?
6
+
7
+ While I believe this library is super useful, I am currently refining it via dogfood, trying to get as smooth a process down as possible. This is leading to somewhat frequent releases with possibly breaking changes. As it is a new library, and I presume no one is using it I"m not being very considerate. If you discover this library and want to use it, please tell me and I will stop breaking it :) and employ better release practices.
8
+
9
+ ## Changelog
10
+
11
+ See [CHANGELOG.md](packages/core/CHANGELOG.md) for a detailed list of changes and version history.
12
+
13
+ ## Why Smart Enums?
14
+
15
+ Traditional approaches to enums in JavaScript/TypeScript have limitations:
16
+
17
+ - Plain objects `{ME: "me", YOU: "you"}` lack utility methods
18
+ - Constants `const ME = "me"` are scattered and hard to iterate
19
+ - Arrays `["me", "you"]` don't provide lookups
20
+ - String unions `type No = "please" | "pretty please"` are compile-time only
21
+
22
+ Smart Enums give you the best of all worlds:
23
+
24
+ ```typescript
25
+ const Colors = enumeration('Colors', {
26
+ input: ['red', 'blue', 'green'] as const,
27
+ });
28
+
29
+ // You get a rich object structure:
30
+ Colors.red; // { key: "red", value: "RED", display: "Red", index: 0 }
31
+ Colors.blue; // { key: "blue", value: "BLUE", display: "Blue", index: 1 }
32
+
33
+ // Plus powerful utility methods:
34
+ Colors.fromValue('RED'); // Returns Colors.red
35
+ Colors.items(); // Returns all enum items
36
+ Colors.values(); // Returns ['RED', 'BLUE', 'GREEN']
37
+ ```
38
+
39
+ ## Installation
40
+
41
+ ```bash
42
+ npm install ts-smart-enum
43
+ # or
44
+ yarn add ts-smart-enum
45
+ # or
46
+ pnpm add ts-smart-enum
47
+ ```
48
+
49
+ The npm package name is `**ts-smart-enum**` (it was previously published as `smart-enums`).
50
+
51
+ **Installing from GitHub:** point your dependency at this repo with npm’s `path` option (for example `github:YOUR_ORG/YOUR_REPO#path:packages/core`). The package’s **`prepare`** script runs on install and builds `dist/`, so built files do not need to be committed. If you depend on **`path:packages/knex`**, its `prepare` builds **`packages/core` first** (then the Knex package) so declaration files exist before the Knex build runs.
52
+
53
+ ## Tree-Shaking Support
54
+
55
+ Smart Enums supports tree-shaking with multiple entry points for optimal bundle sizes:
56
+
57
+ ```typescript
58
+ // Core functionality only (smallest bundle)
59
+ import { enumeration, isSmartEnumItem } from 'ts-smart-enum/core';
60
+
61
+ // Core + API transport utilities
62
+ import {
63
+ enumeration,
64
+ serializeForTransport,
65
+ reviveAfterTransport,
66
+ } from 'ts-smart-enum/transport';
67
+
68
+ // Core + database utilities
69
+ import {
70
+ enumeration,
71
+ prepareForDatabase,
72
+ reviveRowFromDatabase,
73
+ } from 'ts-smart-enum/database';
74
+
75
+ // Full API (backward compatible)
76
+ import {
77
+ enumeration,
78
+ serializeSmartEnums,
79
+ prepareForDatabase,
80
+ } from 'ts-smart-enum';
81
+ ```
82
+
83
+ **Bundle Size Comparison:**
84
+
85
+ - `ts-smart-enum/core`: ~149 bytes (minimal)
86
+ - `ts-smart-enum/transport`: ~406 bytes (core + serialization)
87
+ - `ts-smart-enum/database`: ~379 bytes (core + database utilities)
88
+ - `ts-smart-enum`: ~598 bytes (everything)
89
+
90
+ ### Knex (optional)
91
+
92
+ For Knex, use [`@reharik/smart-enum-knex`](https://www.npmjs.com/package/@reharik/smart-enum-knex): it adds `withEnumRevival` and `createSmartEnumPostProcessResponse` on top of the same explicit `FieldEnumMapping` + `reviveRowFromDatabase` flow as `ts-smart-enum/database`. Revival is **not** inferred from the schema; you attach mapping per query via `queryContext`.
93
+
94
+ ## Quick Start
95
+
96
+ ### Creating Enums from Arrays
97
+
98
+ The simplest way to create a Smart Enum:
99
+
100
+ ```typescript
101
+ import { enumeration, Enumeration } from 'ts-smart-enum';
102
+
103
+ const input = ['pending', 'active', 'completed', 'archived'] as const;
104
+
105
+ const Status = enumeration('Status', { input });
106
+ type Status = Enumeration<typeof Status>;
107
+
108
+ // Use it:
109
+ console.log(Status.active.value); // "ACTIVE"
110
+ console.log(Status.active.display); // "Active"
111
+ ```
112
+
113
+ ### Creating Enums from Objects
114
+
115
+ For more control over the values:
116
+
117
+ ```typescript
118
+ import { enumeration, Enumeration } from 'ts-smart-enum';
119
+
120
+ const input = {
121
+ low: { value: 'LOW', display: 'Low Priority' },
122
+ medium: { value: 'MIDDLE', display: 'Medium Priority' },
123
+ high: { value: 'HIGH', display: 'High Priority' },
124
+ urgent: { value: 'URGENT', display: 'Urgent!!!' },
125
+ };
126
+
127
+ const Priority = enumeration('Priority', { input });
128
+ type Priority = Enumeration<typeof Priority>;
129
+
130
+ console.log(Priority.urgent.value); // "URGENT"
131
+ ```
132
+
133
+ ## Core Features
134
+
135
+ ### Type Safety
136
+
137
+ Smart Enums are fully type-safe:
138
+
139
+ ```typescript
140
+ function processColor(color: Color) {
141
+ console.log(color.display);
142
+ }
143
+
144
+ processColor(Colors.red); // ✅ Works
145
+ processColor(Status.active); // ❌ Type error
146
+ ```
147
+
148
+ ### Auto-Generated Properties
149
+
150
+ Each enum item automatically gets:
151
+
152
+ - `key` - The original key (e.g., "red")
153
+ - `value` - Constant case version (e.g., "RED")
154
+ - `display` - Human-readable version (e.g., "Red")
155
+ - `index` - Position in the enum (e.g., 0, 1, 2)
156
+
157
+ ### Custom Property Formatters
158
+
159
+ Override or add custom auto-formatting:
160
+
161
+ ```typescript
162
+ const input = ['userProfile', 'adminDashboard', 'settingsPage'] as const;
163
+ const propertyAutoFormatters = [
164
+ { key: 'path', format: k => `/${k}` },
165
+ { key: 'slug', format: k => k.toLowerCase().replace(/([A-Z])/g, '-$1') },
166
+ { key: 'value', format: k => k.toLowerCase() },
167
+ ];
168
+
169
+ const Routes = enumeration('Routes', { input, propertyAutoFormatters });
170
+ type Routes = Enumeration<typeof Routes>;
171
+
172
+ console.log(Routes.userProfile.path); // "/userProfile"
173
+ console.log(Routes.userProfile.slug); // "/user-profile"
174
+ console.log(Routes.userProfile.value); // "/user-profile"
175
+ ```
176
+
177
+ ## Utility Methods
178
+
179
+ ### Lookup Methods
180
+
181
+ ```typescript
182
+ // Get enum item by value (throws if not found)
183
+ const item = Colors.fromValue('RED');
184
+
185
+ // Safe lookup (returns undefined if not found)
186
+ const maybeItem = Colors.tryFromValue('PURPLE');
187
+
188
+ // Lookup by key
189
+ const keyItem = Colors.fromKey('red');
190
+ ```
191
+
192
+ ### Conversion Methods
193
+
194
+ ```typescript
195
+ // Get all values
196
+ const values = Colors.values(); // ['RED', 'BLUE', 'GREEN']
197
+
198
+ // Get all keys
199
+ const keys = Colors.keys(); // ['red', 'blue', 'green']
200
+
201
+ // Get all enum items as array
202
+ const items = Colors.items();
203
+ ```
204
+
205
+ ## Advanced Usage
206
+
207
+ ### Custom Properties
208
+
209
+ ```typescript
210
+ const input = {
211
+ red: { hex: '#FF0000', rgb: [255, 0, 0] },
212
+ blue: { hex: '#0000FF', rgb: [0, 0, 255] },
213
+ green: { hex: '#00FF00', rgb: [0, 255, 0] },
214
+ };
215
+
216
+ const Colors = enumeration('Colors', {
217
+ input,
218
+ });
219
+
220
+ console.log(Colors.red.hex); // '#FF0000'
221
+ console.log(Colors.blue.rgb); // [0, 0, 255]
222
+ ```
223
+
224
+ ### Custom Field Values
225
+
226
+ Extract custom values from enum items:
227
+
228
+ ```typescript
229
+ type PageExtensions = { slug: string; title: string };
230
+ const input = {
231
+ home: { slug: '/', title: 'Home Page' },
232
+ about: { slug: '/about', title: 'About Us' },
233
+ contact: { slug: '/contact', title: 'Contact' },
234
+ };
235
+
236
+ const Pages = enumeration('Pages', { input });
237
+ type Pages = Enumeration<typeof Pages>;
238
+
239
+ const slug = Pages.about.slug; // '/about'
240
+
241
+ // Get all slugs
242
+ const slugs = Pages.items().map(item => item.slug);
243
+ // Returns: ['/', '/about', '/contact']
244
+
245
+ // Filter out undefined values
246
+ const titles = Pages.items()
247
+ .map(item => item.title)
248
+ .filter(Boolean);
249
+ ```
250
+
251
+ ### React/Frontend Usage
252
+
253
+ Perfect for form selects and dropdowns:
254
+
255
+ ```tsx
256
+ function ColorSelector() {
257
+ const [selected, setSelected] = useState(Colors.red);
258
+
259
+ return (
260
+ <select
261
+ value={selected.value}
262
+ onChange={e => setSelected(Colors.fromValue(e.target.value))}
263
+ >
264
+ {Colors.items().map(item => (
265
+ <option key={item.value} value={item.value}>
266
+ {item.display}
267
+ </option>
268
+ ))}
269
+ </select>
270
+ );
271
+ }
272
+ ```
273
+
274
+ ### Serialization and Reviving (Transformations)
275
+
276
+ When sending data over the wire or persisting to a database, replace enum items with strings and revive them back.
277
+
278
+ ```ts
279
+ import {
280
+ serializeSmartEnums,
281
+ reviveSmartEnums,
282
+ enumeration,
283
+ type Enumeration,
284
+ } from 'ts-smart-enum';
285
+
286
+ const statusInput = ['pending', 'active', 'completed'] as const;
287
+ const Status = enumeration('Status', { input: statusInput });
288
+ type Status = Enumeration<typeof Status>;
289
+
290
+ const colorInput = ['red', 'blue', 'green'] as const;
291
+ const Color = enumeration('Color', { input: colorInput });
292
+ type Color = Enumeration<typeof Color>;
293
+
294
+ const dto = {
295
+ id: '123',
296
+ status: Status.active,
297
+ favoriteColor: Color.red,
298
+ history: [Status.pending, Status.completed],
299
+ };
300
+
301
+ // Serialize enum items to self-describing objects
302
+ const wire = serializeSmartEnums(dto);
303
+ // Result: {
304
+ // id: '123',
305
+ // status: { __smart_enum_type: 'Status', value: 'ACTIVE' },
306
+ // favoriteColor: { __smart_enum_type: 'Color', value: 'RED' },
307
+ // history: [
308
+ // { __smart_enum_type: 'Status', value: 'PENDING' },
309
+ // { __smart_enum_type: 'Status', value: 'COMPLETED' }
310
+ // ]
311
+ // }
312
+
313
+ // Revive using registry
314
+ const revived = reviveSmartEnums(wire, { Status, Color });
315
+ // Result: {
316
+ // id: '123',
317
+ // status: Status.active, // Full enum item restored
318
+ // favoriteColor: Color.red, // Full enum item restored
319
+ // history: [Status.pending, Status.completed]
320
+ // }
321
+ ```
322
+
323
+ Notes:
324
+
325
+ - All enums now require an `enumType` parameter for serialization/revival
326
+ - Serialization creates self-describing objects with `__smart_enum_type` and `value`
327
+ - Reviving uses a registry to map enum types back to their instances
328
+ - Cyclic references are preserved during serialization
329
+ - Use `as const` on the registry object for best type inference
330
+
331
+ ## Transport Utilities
332
+
333
+ For API communication, use the transport utilities to serialize enums for sending over the wire and revive them on the receiving end.
334
+
335
+ ### Basic Transport Usage
336
+
337
+ ```typescript
338
+ import { enumeration } from 'ts-smart-enum/core';
339
+ import {
340
+ initializeSmartEnumMappings,
341
+ serializeForTransport,
342
+ reviveAfterTransport,
343
+ } from 'ts-smart-enum';
344
+
345
+ // Create enums
346
+ const UserStatus = enumeration('UserStatus', {
347
+ input: ['pending', 'active', 'suspended'] as const,
348
+ });
349
+
350
+ const Priority = enumeration('Priority', {
351
+ input: ['low', 'medium', 'high', 'urgent'] as const,
352
+ });
353
+
354
+ // Required once before calling reviveAfterTransport
355
+ initializeSmartEnumMappings({
356
+ enumRegistry: { UserStatus, Priority },
357
+ });
358
+
359
+ // API endpoint - serialize for sending
360
+ const apiData = {
361
+ user: {
362
+ id: '123',
363
+ status: UserStatus.active,
364
+ profile: {
365
+ priority: Priority.high,
366
+ },
367
+ },
368
+ orders: [
369
+ { id: 'o1', status: UserStatus.pending },
370
+ { id: 'o2', status: UserStatus.active },
371
+ ],
372
+ };
373
+
374
+ // Serialize for transport (creates self-describing objects)
375
+ const wireData = serializeForTransport(apiData);
376
+ // Result: {
377
+ // user: {
378
+ // id: '123',
379
+ // status: { __smart_enum_type: 'UserStatus', value: 'ACTIVE' },
380
+ // profile: {
381
+ // priority: { __smart_enum_type: 'Priority', value: 'HIGH' }
382
+ // }
383
+ // },
384
+ // orders: [
385
+ // { id: 'o1', status: { __smart_enum_type: 'UserStatus', value: 'PENDING' } },
386
+ // { id: 'o2', status: { __smart_enum_type: 'UserStatus', value: 'ACTIVE' } }
387
+ // ]
388
+ // }
389
+
390
+ // Client-side - revive after receiving
391
+ const revivedData = reviveAfterTransport(wireData);
392
+ // Result: Original apiData with full enum items restored
393
+ ```
394
+
395
+ ### Express.js Integration
396
+
397
+ ```typescript
398
+ import express from 'express';
399
+ import {
400
+ initializeSmartEnumMappings,
401
+ serializeForTransport,
402
+ reviveAfterTransport,
403
+ } from 'ts-smart-enum';
404
+
405
+ const app = express();
406
+
407
+ initializeSmartEnumMappings({
408
+ enumRegistry: { UserStatus, Priority },
409
+ });
410
+
411
+ // Middleware to revive enums from incoming requests
412
+ app.use(express.json());
413
+ app.use((req, res, next) => {
414
+ if (req.body) {
415
+ req.body = reviveAfterTransport(req.body);
416
+ }
417
+ next();
418
+ });
419
+
420
+ // API endpoint
421
+ app.get('/api/users/:id', (req, res) => {
422
+ const user = getUserById(req.params.id);
423
+
424
+ // Serialize for response
425
+ res.json(serializeForTransport(user));
426
+ });
427
+
428
+ // POST endpoint
429
+ app.post('/api/users', (req, res) => {
430
+ // req.body is automatically revived with enum items
431
+ const { status, priority } = req.body;
432
+
433
+ // Use enum items directly
434
+ if (status === UserStatus.active) {
435
+ // Handle active user
436
+ }
437
+
438
+ const newUser = createUser(req.body);
439
+ res.json(serializeForTransport(newUser));
440
+ });
441
+ ```
442
+
443
+ ### React/Fetch Integration
444
+
445
+ ```typescript
446
+ // API client with automatic enum handling
447
+ class ApiClient {
448
+ async get<T>(url: string): Promise<T> {
449
+ const response = await fetch(url);
450
+ const data = await response.json();
451
+
452
+ // Revive enums in response
453
+ return reviveAfterTransport(data) as T;
454
+ }
455
+
456
+ async post<T>(url: string, data: any): Promise<T> {
457
+ // Serialize enums for sending
458
+ const serializedData = serializeForTransport(data);
459
+
460
+ const response = await fetch(url, {
461
+ method: 'POST',
462
+ headers: { 'Content-Type': 'application/json' },
463
+ body: JSON.stringify(serializedData),
464
+ });
465
+
466
+ const result = await response.json();
467
+ return reviveAfterTransport(result) as T;
468
+ }
469
+ }
470
+
471
+ // Usage in React component
472
+ function UserProfile({ userId }: { userId: string }) {
473
+ const [user, setUser] = useState<User | null>(null);
474
+ const apiClient = new ApiClient();
475
+
476
+ useEffect(() => {
477
+ apiClient.get<User>(`/api/users/${userId}`).then(setUser);
478
+ }, [userId]);
479
+
480
+ const updateStatus = async (newStatus: typeof UserStatus.active) => {
481
+ const updatedUser = await apiClient.post<User>(`/api/users/${userId}`, {
482
+ status: newStatus, // Enum item sent directly
483
+ });
484
+ setUser(updatedUser);
485
+ };
486
+
487
+ return (
488
+ <div>
489
+ <h2>{user?.name}</h2>
490
+ <p>Status: {user?.status.display}</p>
491
+ <button onClick={() => updateStatus(UserStatus.active)}>
492
+ Activate User
493
+ </button>
494
+ </div>
495
+ );
496
+ }
497
+ ```
498
+
499
+ ## Database utilities
500
+
501
+ **Outbound serialization is generic:** `prepareForDatabase` walks your payload and replaces smart enum items with their `.value` strings. You can also bind `someItem.toPostgres()` for PostgreSQL drivers that honor that hook (outbound only; it does not revive reads).
502
+
503
+ **Inbound deserialization is not automatic:** a plain string column does not say which enum type it belongs to, and the same property name (e.g. `status`) is not unique across your schema. The previous **learned / global-registry database revival model has been removed**.
504
+
505
+ **Recommended approach:** at the repository or query boundary, pass **explicit metadata**: map column names (or full paths for nested JSON) to the **actual enum object** (anything with `tryFromValue`), not to type name strings.
506
+
507
+ ### `reviveRowFromDatabase` (flat rows)
508
+
509
+ Shallow-clones the row. For each entry in `fieldEnumMapping`, if the value is a string, `tryFromValue` is called; on success the string is replaced by the enum item. `strict: true` throws `EnumRevivalError` when a mapped string is invalid; otherwise the raw value is kept.
510
+
511
+ ```typescript
512
+ import { enumeration } from 'ts-smart-enum/core';
513
+ import { reviveRowFromDatabase } from 'ts-smart-enum/database';
514
+
515
+ const UserStatus = enumeration('UserStatus', {
516
+ input: ['pending', 'active'] as const,
517
+ });
518
+
519
+ const row = { id: '1', status: 'ACTIVE' };
520
+ const revived = reviveRowFromDatabase(row, {
521
+ fieldEnumMapping: { status: UserStatus },
522
+ strict: true,
523
+ });
524
+ ```
525
+
526
+ ### `revivePayloadFromDatabase` (explicit paths only)
527
+
528
+ Deep-clones with `structuredClone`, then revives only the paths you list (e.g. `status`, `profile.priority`, `items[].kind`). No leaf-name guessing and no registry.
529
+
530
+ ```typescript
531
+ import { revivePayloadFromDatabase } from 'ts-smart-enum/database';
532
+
533
+ const doc = await loadJsonColumn();
534
+ const out = revivePayloadFromDatabase(doc, {
535
+ pathEnumMapping: {
536
+ 'user.status': UserStatus,
537
+ 'lines[].kind': LineKind,
538
+ },
539
+ });
540
+ ```
541
+
542
+ ### Transport vs database
543
+
544
+ `initializeSmartEnumMappings` / `reviveAfterTransport` are for **wire payloads** that include `__smart_enum_type`. They are exported from `ts-smart-enum/transport` (and the root package), not from the old database utilities.
545
+
546
+ ## Logging
547
+
548
+ The library includes a flexible logging system that allows you to integrate with your preferred logging solution. By default, it uses console logging.
549
+
550
+ ### Basic Usage
551
+
552
+ ```typescript
553
+ import { enumeration, initializeSmartEnumMappings } from 'ts-smart-enum';
554
+
555
+ // Console logging is enabled by default with 'error' level (minimal output)
556
+ initializeSmartEnumMappings({
557
+ enumRegistry: { UserStatus, Priority },
558
+ });
559
+
560
+ // Enable debug logging for development
561
+ initializeSmartEnumMappings({
562
+ enumRegistry: { UserStatus, Priority },
563
+ logLevel: 'debug',
564
+ });
565
+ // [ts-smart-enum:info] Initialized smart enum mappings { enumCount: 2, enumTypes: ['UserStatus', 'Priority'], logLevel: 'debug' }
566
+ ```
567
+
568
+ ### Log Level Configuration
569
+
570
+ ```typescript
571
+ import { initializeSmartEnumMappings, type LogLevel } from 'ts-smart-enum';
572
+
573
+ // Available log levels (default: 'error')
574
+ const logLevels: LogLevel[] = ['debug', 'info', 'warn', 'error'];
575
+
576
+ // Production: minimal logging (errors only)
577
+ initializeSmartEnumMappings({
578
+ enumRegistry: { UserStatus, Priority },
579
+ logLevel: 'error', // default
580
+ });
581
+
582
+ // Development: verbose logging
583
+ initializeSmartEnumMappings({
584
+ enumRegistry: { UserStatus, Priority },
585
+ logLevel: 'debug',
586
+ });
587
+
588
+ // Custom logger with log level filtering
589
+ initializeSmartEnumMappings({
590
+ enumRegistry: { UserStatus, Priority },
591
+ logLevel: 'info',
592
+ logger: {
593
+ debug: (msg, ...args) => console.debug(`[my-app] ${msg}`, ...args),
594
+ info: (msg, ...args) => console.info(`[my-app] ${msg}`, ...args),
595
+ warn: (msg, ...args) => console.warn(`[my-app] ${msg}`, ...args),
596
+ error: (msg, ...args) => console.error(`[my-app] ${msg}`, ...args),
597
+ },
598
+ });
599
+ ```
600
+
601
+ ### Custom Logger Integration
602
+
603
+ ```typescript
604
+ import { initializeSmartEnumMappings, type Logger } from 'ts-smart-enum';
605
+ import winston from 'winston';
606
+
607
+ // Create your logger
608
+ const logger = winston.createLogger({
609
+ level: 'info',
610
+ format: winston.format.json(),
611
+ transports: [new winston.transports.Console()],
612
+ });
613
+
614
+ // Use custom logger with ts-smart-enum
615
+ initializeSmartEnumMappings({
616
+ enumRegistry: { UserStatus, Priority },
617
+ logLevel: 'debug',
618
+ logger: {
619
+ debug: (msg, ...args) => logger.debug(`[ts-smart-enum] ${msg}`, ...args),
620
+ info: (msg, ...args) => logger.info(`[ts-smart-enum] ${msg}`, ...args),
621
+ warn: (msg, ...args) => logger.warn(`[ts-smart-enum] ${msg}`, ...args),
622
+ error: (msg, ...args) => logger.error(`[ts-smart-enum] ${msg}`, ...args),
623
+ },
624
+ });
625
+ ```
626
+
627
+ ### Log Levels
628
+
629
+ - `**debug**`: Reserved for future verbose transport diagnostics
630
+ - `**info**`: General information (e.g. registry initialization)
631
+ - `**warn**`: Warning messages (missing configurations, failed operations)
632
+ - `**error**`: Error conditions (missing enum types, invalid data)
633
+
634
+ ### Disabling Logging
635
+
636
+ To disable logging in production, you can set a no-op logger:
637
+
638
+ ```typescript
639
+ import { initializeSmartEnumMappings } from 'ts-smart-enum';
640
+
641
+ // Disable logging for production
642
+ initializeSmartEnumMappings({
643
+ enumRegistry: { UserStatus, Priority },
644
+ logLevel: 'error', // minimal logging
645
+ logger: {
646
+ debug: () => {},
647
+ info: () => {},
648
+ warn: () => {},
649
+ error: () => {},
650
+ },
651
+ });
652
+ ```
653
+
654
+ ### Logging events
655
+
656
+ - **Initialization**: When `initializeSmartEnumMappings` is called (transport registry)
657
+ - **Warnings / errors**: Emitted by other helpers when misconfigured (see JSDoc per API)
658
+
659
+ ### Prisma Integration
660
+
661
+ ```typescript
662
+ import { PrismaClient } from '@prisma/client';
663
+ import {
664
+ prepareForDatabase,
665
+ reviveRowFromDatabase,
666
+ } from 'ts-smart-enum/database';
667
+
668
+ const prisma = new PrismaClient();
669
+
670
+ // Create user with enum data
671
+ async function createUser(userData: {
672
+ name: string;
673
+ status: typeof UserStatus.active;
674
+ priority: typeof Priority.high;
675
+ }) {
676
+ // Convert enums to strings for database
677
+ const dbData = prepareForDatabase(userData);
678
+
679
+ return prisma.user.create({
680
+ data: {
681
+ name: dbData.name,
682
+ status: dbData.status, // 'ACTIVE'
683
+ priority: dbData.priority, // 'HIGH'
684
+ },
685
+ });
686
+ }
687
+
688
+ // Get user and revive enums
689
+ async function getUser(id: string) {
690
+ const dbUser = await prisma.user.findUnique({
691
+ where: { id },
692
+ });
693
+
694
+ if (!dbUser) return null;
695
+
696
+ // Revive enums from database strings
697
+ return reviveRowFromDatabase(dbUser as Record<string, unknown>, {
698
+ fieldEnumMapping: {
699
+ status: UserStatus,
700
+ priority: Priority,
701
+ },
702
+ });
703
+ }
704
+
705
+ // Usage
706
+ const user = await getUser('123');
707
+ if (user) {
708
+ console.log(user.status.display); // "Active"
709
+ console.log(user.priority.value); // "HIGH"
710
+ }
711
+ ```
712
+
713
+ ### TypeORM Integration
714
+
715
+ ```typescript
716
+ import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
717
+ import {
718
+ prepareForDatabase,
719
+ reviveRowFromDatabase,
720
+ } from 'ts-smart-enum/database';
721
+
722
+ @Entity()
723
+ export class User {
724
+ @PrimaryGeneratedColumn()
725
+ id: number;
726
+
727
+ @Column()
728
+ name: string;
729
+
730
+ @Column()
731
+ status: string; // Stores enum value as string
732
+
733
+ @Column()
734
+ priority: string; // Stores enum value as string
735
+ }
736
+
737
+ // Repository with enum handling
738
+ export class UserRepository {
739
+ constructor(private repository: Repository<User>) {}
740
+
741
+ async save(userData: {
742
+ name: string;
743
+ status: typeof UserStatus.active;
744
+ priority: typeof Priority.high;
745
+ }) {
746
+ const dbData = prepareForDatabase(userData);
747
+ return this.repository.save(dbData);
748
+ }
749
+
750
+ async findById(id: number) {
751
+ const dbUser = await this.repository.findOne({ where: { id } });
752
+ if (!dbUser) return null;
753
+
754
+ return reviveRowFromDatabase(dbUser as Record<string, unknown>, {
755
+ fieldEnumMapping: {
756
+ status: UserStatus,
757
+ priority: Priority,
758
+ },
759
+ });
760
+ }
761
+ }
762
+ ```
763
+
764
+ ### Validation & Type Guards
765
+
766
+ ```typescript
767
+ function processStatus(statusValue: string) {
768
+ const status = Status.tryFromValue(statusValue);
769
+
770
+ if (!status) {
771
+ throw new Error(`Invalid status: ${statusValue}`);
772
+ }
773
+
774
+ // Now status is typed as a valid enum item
775
+ switch (status.key) {
776
+ case 'pending':
777
+ // Handle pending
778
+ break;
779
+ case 'active':
780
+ // Handle active
781
+ break;
782
+ // TypeScript ensures all cases are handled
783
+ }
784
+ }
785
+ ```
786
+
787
+ ## JSDoc and IDE Support
788
+
789
+ All public APIs are documented with JSDoc. In supported editors (VS Code, WebStorm, etc.) you get:
790
+
791
+ - **Hover tooltips** with descriptions and parameter/return types
792
+ - `**@example` blocks** on key functions (e.g. `enumeration`, `serializeSmartEnums`, `reviveSmartEnums`, `isSmartEnumItem`, `isSmartEnum`, `prepareForDatabase`, `reviveRowFromDatabase`, transport and database helpers) so you can copy-paste or follow patterns without leaving the editor
793
+
794
+ Import from the entry point you use (`ts-smart-enum`, `ts-smart-enum/core`, `ts-smart-enum/transport`, or `ts-smart-enum/database`) and hover over symbols to see the inline docs and examples.
795
+
796
+ ## API Reference
797
+
798
+ ### `enumeration(enumType, props)`
799
+
800
+ Main factory function for creating Smart Enums.
801
+
802
+ **Parameters:**
803
+
804
+ - `enumType` - String identifier for the enum type (required for serialization/revival)
805
+ - `props.input` - Array of strings, object with BaseEnum values, or existing enum
806
+ - `props.propertyAutoFormatters` - Optional custom formatters for auto-generated properties
807
+
808
+ **Returns:** An enum object with all items as properties plus extension methods
809
+
810
+ ### Extension Methods
811
+
812
+
813
+ | Method | Description | Returns |
814
+ | --------------------- | --------------------------------------- | ------------------------- |
815
+ | `fromValue(value)` | Get item by value (throws if not found) | `Enumeration` |
816
+ | `tryFromValue(value)` | Get item by value (safe) | `Enumeration | undefined` |
817
+ | `fromKey(key)` | Get item by key (throws if not found) | `Enumeration` |
818
+ | `tryFromKey(key)` | Get item by key (safe) | `Enumeration | undefined` |
819
+ | `items()` | Get all items as array | `Enumeration[]` |
820
+ | `values()` | Get all values | `string[]` |
821
+ | `keys()` | Get all keys | `string[]` |
822
+
823
+
824
+ ## Migration Guide
825
+
826
+ ### From Plain Objects
827
+
828
+ ```typescript
829
+ // Before
830
+ const COLORS = {
831
+ RED: 'red',
832
+ BLUE: 'blue',
833
+ GREEN: 'green',
834
+ };
835
+
836
+ // After
837
+ const Colors = enumeration('Colors', {
838
+ input: ['red', 'blue', 'green'] as const,
839
+ });
840
+
841
+ // Usage changes from COLORS.RED to Colors.red.value
842
+ ```
843
+
844
+ ### From String Unions
845
+
846
+ ```typescript
847
+ // Before
848
+ type Status = 'pending' | 'active' | 'completed';
849
+
850
+ // After
851
+ const Status = enumeration('Status', {
852
+ input: ['pending', 'active', 'completed'] as const,
853
+ });
854
+ type Status = Enumeration<typeof Status>;
855
+ ```
856
+
857
+ ## Best Practices
858
+
859
+ 1. **Always use `as const`** for array inputs to preserve literal types
860
+ 2. **Always provide `enumType`** parameter for serialization/revival support
861
+ 3. **Use `tryFrom`* methods** when dealing with external data
862
+ 4. **Leverage filtering** to hide deprecated items from users
863
+ 5. **Add custom properties** for domain-specific needs
864
+
865
+ ## Contributing
866
+
867
+ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
868
+
869
+ ## License
870
+
871
+ MIT
872
+
873
+ ## Support
874
+
875
+ - 📧 Email: [harik.raif@gmail.com](mailto:harik.raif@gmail.com)
876
+ - 🐛 Issues: [GitHub Issues](https://github.com/reharik/smart-enums/issues)
877
+ - 📖 Docs: [Full Documentation](https://docs.reharik.com/ts-smart-enum)
878
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reharik/smart-enum",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
@@ -36,7 +36,7 @@
36
36
  "scripts": {
37
37
  "clean": "rm -rf dist",
38
38
  "build": "npm run clean && tsup",
39
- "prepublishOnly": "npm run build",
39
+ "prepublishOnly": "node -e \"require('fs').copyFileSync('../../README.md', 'README.md')\" && npm run build",
40
40
  "test": "jest --config jest.config.cjs",
41
41
  "dance": "npm run build && npm run test"
42
42
  },