@digitaldefiance/branded-enum 0.0.1 → 0.0.3

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 (79) hide show
  1. package/dist/cjs/index.cjs +149 -0
  2. package/dist/cjs/index.cjs.map +1 -0
  3. package/dist/cjs/lib/accessors.cjs +55 -0
  4. package/dist/cjs/lib/accessors.cjs.map +1 -0
  5. package/dist/cjs/lib/advanced.cjs +558 -0
  6. package/dist/cjs/lib/advanced.cjs.map +1 -0
  7. package/dist/cjs/lib/branded-enum.cjs +15 -0
  8. package/dist/cjs/lib/branded-enum.cjs.map +1 -0
  9. package/dist/cjs/lib/decorators.cjs +202 -0
  10. package/dist/cjs/lib/decorators.cjs.map +1 -0
  11. package/dist/cjs/lib/factory.cjs +45 -0
  12. package/dist/cjs/lib/factory.cjs.map +1 -0
  13. package/dist/cjs/lib/guards.cjs +119 -0
  14. package/dist/cjs/lib/guards.cjs.map +1 -0
  15. package/dist/cjs/lib/merge.cjs +47 -0
  16. package/dist/cjs/lib/merge.cjs.map +1 -0
  17. package/dist/cjs/lib/registry.cjs +85 -0
  18. package/dist/cjs/lib/registry.cjs.map +1 -0
  19. package/dist/cjs/lib/types.cjs +38 -0
  20. package/dist/cjs/lib/types.cjs.map +1 -0
  21. package/dist/cjs/lib/utils.cjs +80 -0
  22. package/dist/cjs/lib/utils.cjs.map +1 -0
  23. package/dist/esm/index.d.ts +21 -0
  24. package/dist/esm/index.d.ts.map +1 -0
  25. package/dist/esm/index.js.map +1 -0
  26. package/dist/esm/lib/accessors.d.ts +79 -0
  27. package/dist/esm/lib/accessors.d.ts.map +1 -0
  28. package/dist/esm/lib/accessors.js.map +1 -0
  29. package/dist/esm/lib/advanced.d.ts +1422 -0
  30. package/dist/esm/lib/advanced.d.ts.map +1 -0
  31. package/dist/esm/lib/advanced.js.map +1 -0
  32. package/dist/esm/lib/branded-enum.d.ts +2 -0
  33. package/dist/esm/lib/branded-enum.d.ts.map +1 -0
  34. package/dist/esm/lib/branded-enum.js.map +1 -0
  35. package/dist/esm/lib/decorators.d.ts +147 -0
  36. package/dist/esm/lib/decorators.d.ts.map +1 -0
  37. package/dist/esm/lib/decorators.js.map +1 -0
  38. package/dist/esm/lib/factory.d.ts +52 -0
  39. package/dist/esm/lib/factory.d.ts.map +1 -0
  40. package/dist/esm/lib/factory.js.map +1 -0
  41. package/dist/esm/lib/guards.d.ts +297 -0
  42. package/dist/esm/lib/guards.d.ts.map +1 -0
  43. package/dist/esm/lib/guards.js.map +1 -0
  44. package/dist/esm/lib/merge.d.ts +64 -0
  45. package/dist/esm/lib/merge.d.ts.map +1 -0
  46. package/dist/esm/lib/merge.js.map +1 -0
  47. package/dist/esm/lib/registry.d.ts +93 -0
  48. package/dist/esm/lib/registry.d.ts.map +1 -0
  49. package/dist/esm/lib/registry.js.map +1 -0
  50. package/dist/esm/lib/types.d.ts +258 -0
  51. package/dist/esm/lib/types.d.ts.map +1 -0
  52. package/dist/esm/lib/types.js.map +1 -0
  53. package/dist/esm/lib/utils.d.ts +121 -0
  54. package/dist/esm/lib/utils.d.ts.map +1 -0
  55. package/dist/esm/lib/utils.js.map +1 -0
  56. package/package.json +31 -18
  57. package/dist/index.js.map +0 -1
  58. package/dist/lib/accessors.js.map +0 -1
  59. package/dist/lib/advanced.js.map +0 -1
  60. package/dist/lib/branded-enum.js.map +0 -1
  61. package/dist/lib/decorators.js.map +0 -1
  62. package/dist/lib/factory.js.map +0 -1
  63. package/dist/lib/guards.js.map +0 -1
  64. package/dist/lib/merge.js.map +0 -1
  65. package/dist/lib/registry.js.map +0 -1
  66. package/dist/lib/types.js.map +0 -1
  67. package/dist/lib/utils.js.map +0 -1
  68. package/dist/package.json +0 -120
  69. /package/dist/{index.js → esm/index.js} +0 -0
  70. /package/dist/{lib → esm/lib}/accessors.js +0 -0
  71. /package/dist/{lib → esm/lib}/advanced.js +0 -0
  72. /package/dist/{lib → esm/lib}/branded-enum.js +0 -0
  73. /package/dist/{lib → esm/lib}/decorators.js +0 -0
  74. /package/dist/{lib → esm/lib}/factory.js +0 -0
  75. /package/dist/{lib → esm/lib}/guards.js +0 -0
  76. /package/dist/{lib → esm/lib}/merge.js +0 -0
  77. /package/dist/{lib → esm/lib}/registry.js +0 -0
  78. /package/dist/{lib → esm/lib}/types.js +0 -0
  79. /package/dist/{lib → esm/lib}/utils.js +0 -0
@@ -0,0 +1,297 @@
1
+ /**
2
+ * Type guards for branded enums.
3
+ *
4
+ * Provides runtime type checking and assertion functions
5
+ * for validating values against branded enums.
6
+ */
7
+ import { AnyBrandedEnum, EnumValues } from './types.js';
8
+ /**
9
+ * Checks if a value belongs to a specific branded enum.
10
+ *
11
+ * Returns true if and only if:
12
+ * - enumObj is a valid branded enum (has Symbol metadata)
13
+ * - value is a string
14
+ * - value exists in the enum's value Set
15
+ *
16
+ * This function provides TypeScript type narrowing - when it returns true,
17
+ * the value's type is narrowed to the enum's value type.
18
+ *
19
+ * @template E - The branded enum type
20
+ * @param value - The value to check. Can be any type; non-strings return false.
21
+ * @param enumObj - The branded enum to check against
22
+ * @returns `true` if value is in the enum (with type narrowing), `false` otherwise.
23
+ * Returns `false` for non-string values, null, undefined, or if enumObj
24
+ * is not a branded enum.
25
+ *
26
+ * @example
27
+ * // Basic type guard usage
28
+ * const Status = createBrandedEnum('status', { Active: 'active', Inactive: 'inactive' } as const);
29
+ *
30
+ * function handleStatus(value: unknown) {
31
+ * if (isFromEnum(value, Status)) {
32
+ * // value is narrowed to 'active' | 'inactive'
33
+ * console.log('Valid status:', value);
34
+ * } else {
35
+ * console.log('Invalid status');
36
+ * }
37
+ * }
38
+ *
39
+ * @example
40
+ * // Returns false for non-string values
41
+ * isFromEnum(123, Status); // false
42
+ * isFromEnum(null, Status); // false
43
+ * isFromEnum(undefined, Status); // false
44
+ *
45
+ * @example
46
+ * // Returns false for non-branded enum objects
47
+ * isFromEnum('active', { Active: 'active' }); // false (not a branded enum)
48
+ */
49
+ export declare function isFromEnum<E extends AnyBrandedEnum>(value: unknown, enumObj: E): value is EnumValues<E>;
50
+ /**
51
+ * Asserts that a value belongs to a branded enum, throwing if not.
52
+ *
53
+ * Use this function when you want to validate a value and throw an error
54
+ * if it's invalid, rather than handling the false case manually.
55
+ *
56
+ * @template E - The branded enum type
57
+ * @param value - The value to check. Can be any type.
58
+ * @param enumObj - The branded enum to check against
59
+ * @returns The value with narrowed type if valid
60
+ * @throws {Error} Throws `Error` with message `Second argument is not a branded enum`
61
+ * if enumObj is not a valid branded enum.
62
+ * @throws {Error} Throws `Error` with message `Value "${value}" is not a member of enum "${enumId}"`
63
+ * if the value is not found in the enum.
64
+ *
65
+ * @example
66
+ * // Successful assertion
67
+ * const Status = createBrandedEnum('status', { Active: 'active', Inactive: 'inactive' } as const);
68
+ * const validated = assertFromEnum('active', Status);
69
+ * // validated is typed as 'active' | 'inactive'
70
+ *
71
+ * @example
72
+ * // Throws for invalid value
73
+ * try {
74
+ * assertFromEnum('unknown', Status);
75
+ * } catch (e) {
76
+ * console.log(e.message); // 'Value "unknown" is not a member of enum "status"'
77
+ * }
78
+ *
79
+ * @example
80
+ * // Throws for non-branded enum
81
+ * try {
82
+ * assertFromEnum('active', { Active: 'active' });
83
+ * } catch (e) {
84
+ * console.log(e.message); // 'Second argument is not a branded enum'
85
+ * }
86
+ */
87
+ export declare function assertFromEnum<E extends AnyBrandedEnum>(value: unknown, enumObj: E): EnumValues<E>;
88
+ /**
89
+ * Safely parses a value against a branded enum, returning a default if invalid.
90
+ *
91
+ * This is a non-throwing alternative to `assertFromEnum`. Instead of throwing
92
+ * an error when the value is not in the enum, it returns the provided default value.
93
+ *
94
+ * Use this function when you want to handle invalid values gracefully without
95
+ * try/catch blocks, such as when parsing user input or external data.
96
+ *
97
+ * @template E - The branded enum type
98
+ * @param value - The value to parse. Can be any type; non-strings will return the default.
99
+ * @param enumObj - The branded enum to validate against
100
+ * @param defaultValue - The value to return if parsing fails. Must be a valid enum value.
101
+ * @returns The original value if it exists in the enum, otherwise the default value.
102
+ * The return type is narrowed to the enum's value type.
103
+ *
104
+ * @example
105
+ * // Basic usage with default fallback
106
+ * const Status = createBrandedEnum('status', {
107
+ * Active: 'active',
108
+ * Inactive: 'inactive',
109
+ * Pending: 'pending',
110
+ * } as const);
111
+ *
112
+ * // Valid value returns as-is
113
+ * parseEnum('active', Status, Status.Pending); // 'active'
114
+ *
115
+ * // Invalid value returns default
116
+ * parseEnum('unknown', Status, Status.Pending); // 'pending'
117
+ *
118
+ * // Non-string value returns default
119
+ * parseEnum(null, Status, Status.Inactive); // 'inactive'
120
+ * parseEnum(123, Status, Status.Inactive); // 'inactive'
121
+ *
122
+ * @example
123
+ * // Parsing user input safely
124
+ * function handleUserStatus(input: unknown): void {
125
+ * const status = parseEnum(input, Status, Status.Pending);
126
+ * // status is guaranteed to be 'active' | 'inactive' | 'pending'
127
+ * console.log('Processing status:', status);
128
+ * }
129
+ *
130
+ * @example
131
+ * // Parsing API response with fallback
132
+ * interface ApiResponse {
133
+ * status?: string;
134
+ * }
135
+ *
136
+ * function processResponse(response: ApiResponse) {
137
+ * const status = parseEnum(response.status, Status, Status.Inactive);
138
+ * // Handles undefined, null, or invalid status values gracefully
139
+ * return { status };
140
+ * }
141
+ *
142
+ * @example
143
+ * // Chaining with optional values
144
+ * const userStatus = parseEnum(
145
+ * localStorage.getItem('userStatus'),
146
+ * Status,
147
+ * Status.Active
148
+ * );
149
+ */
150
+ export declare function parseEnum<E extends AnyBrandedEnum>(value: unknown, enumObj: E, defaultValue: EnumValues<E>): EnumValues<E>;
151
+ /**
152
+ * Represents a successful parse result from safeParseEnum.
153
+ *
154
+ * @template T - The type of the successfully parsed value
155
+ */
156
+ export interface SafeParseSuccess<T> {
157
+ /** Indicates the parse was successful */
158
+ readonly success: true;
159
+ /** The validated enum value */
160
+ readonly value: T;
161
+ }
162
+ /**
163
+ * Represents a failed parse result from safeParseEnum.
164
+ *
165
+ * Contains detailed error information for debugging and user feedback.
166
+ */
167
+ export interface SafeParseFailure {
168
+ /** Indicates the parse failed */
169
+ readonly success: false;
170
+ /** Detailed error information */
171
+ readonly error: SafeParseError;
172
+ }
173
+ /**
174
+ * Detailed error information for a failed parse.
175
+ */
176
+ export interface SafeParseError {
177
+ /** Human-readable error message */
178
+ readonly message: string;
179
+ /** The code identifying the type of error */
180
+ readonly code: SafeParseErrorCode;
181
+ /** The input value that failed validation */
182
+ readonly input: unknown;
183
+ /** The enum ID (if available) */
184
+ readonly enumId?: string;
185
+ /** The valid values for the enum (if available) */
186
+ readonly validValues?: readonly string[];
187
+ }
188
+ /**
189
+ * Error codes for safe parse failures.
190
+ */
191
+ export type SafeParseErrorCode = 'INVALID_ENUM_OBJECT' | 'INVALID_VALUE_TYPE' | 'VALUE_NOT_IN_ENUM';
192
+ /**
193
+ * Union type representing the result of safeParseEnum.
194
+ *
195
+ * @template T - The type of the successfully parsed value
196
+ */
197
+ export type SafeParseResult<T> = SafeParseSuccess<T> | SafeParseFailure;
198
+ /**
199
+ * Safely parses a value against a branded enum, returning a result object.
200
+ *
201
+ * This function provides validated deserialization with detailed error information.
202
+ * Unlike `parseEnum` which returns a default value on failure, or `assertFromEnum`
203
+ * which throws an error, `safeParseEnum` returns a discriminated union result
204
+ * that allows for explicit success/failure handling.
205
+ *
206
+ * The result is either:
207
+ * - `{ success: true, value: T }` - The value is valid and typed correctly
208
+ * - `{ success: false, error: SafeParseError }` - The value is invalid with details
209
+ *
210
+ * Error codes:
211
+ * - `INVALID_ENUM_OBJECT` - The enumObj is not a valid branded enum
212
+ * - `INVALID_VALUE_TYPE` - The value is not a string
213
+ * - `VALUE_NOT_IN_ENUM` - The value is a string but not in the enum
214
+ *
215
+ * @template E - The branded enum type
216
+ * @param value - The value to parse. Can be any type.
217
+ * @param enumObj - The branded enum to validate against
218
+ * @returns A SafeParseResult containing either the validated value or error details
219
+ *
220
+ * @example
221
+ * // Basic usage with success
222
+ * const Status = createBrandedEnum('status', {
223
+ * Active: 'active',
224
+ * Inactive: 'inactive',
225
+ * } as const);
226
+ *
227
+ * const result = safeParseEnum('active', Status);
228
+ * if (result.success) {
229
+ * console.log('Valid status:', result.value); // 'active'
230
+ * } else {
231
+ * console.log('Error:', result.error.message);
232
+ * }
233
+ *
234
+ * @example
235
+ * // Handling invalid value
236
+ * const result = safeParseEnum('unknown', Status);
237
+ * if (!result.success) {
238
+ * console.log(result.error.code); // 'VALUE_NOT_IN_ENUM'
239
+ * console.log(result.error.message); // 'Value "unknown" is not a member of enum "status"'
240
+ * console.log(result.error.validValues); // ['active', 'inactive']
241
+ * }
242
+ *
243
+ * @example
244
+ * // Handling non-string input
245
+ * const result = safeParseEnum(123, Status);
246
+ * if (!result.success) {
247
+ * console.log(result.error.code); // 'INVALID_VALUE_TYPE'
248
+ * console.log(result.error.message); // 'Expected a string value, received number'
249
+ * }
250
+ *
251
+ * @example
252
+ * // Parsing API response
253
+ * interface ApiResponse {
254
+ * status?: string;
255
+ * }
256
+ *
257
+ * function processResponse(response: ApiResponse) {
258
+ * const result = safeParseEnum(response.status, Status);
259
+ * if (result.success) {
260
+ * return { status: result.value };
261
+ * } else {
262
+ * // Log detailed error for debugging
263
+ * console.error('Invalid status:', result.error);
264
+ * return { status: Status.Inactive }; // fallback
265
+ * }
266
+ * }
267
+ *
268
+ * @example
269
+ * // Form validation with detailed errors
270
+ * function validateForm(data: Record<string, unknown>) {
271
+ * const statusResult = safeParseEnum(data.status, Status);
272
+ * const errors: string[] = [];
273
+ *
274
+ * if (!statusResult.success) {
275
+ * errors.push(`Status: ${statusResult.error.message}`);
276
+ * }
277
+ *
278
+ * return {
279
+ * isValid: errors.length === 0,
280
+ * errors,
281
+ * data: statusResult.success ? { status: statusResult.value } : null,
282
+ * };
283
+ * }
284
+ *
285
+ * @example
286
+ * // Type narrowing with result
287
+ * const result = safeParseEnum(userInput, Status);
288
+ * if (result.success) {
289
+ * // result.value is typed as 'active' | 'inactive'
290
+ * handleStatus(result.value);
291
+ * } else {
292
+ * // result.error is typed as SafeParseError
293
+ * showError(result.error.message);
294
+ * }
295
+ */
296
+ export declare function safeParseEnum<E extends AnyBrandedEnum>(value: unknown, enumObj: E): SafeParseResult<EnumValues<E>>;
297
+ //# sourceMappingURL=guards.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guards.d.ts","sourceRoot":"","sources":["../../../src/lib/guards.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,cAAc,EAKd,UAAU,EACX,MAAM,YAAY,CAAC;AAmBpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,cAAc,EACjD,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,CAAC,GACT,KAAK,IAAI,UAAU,CAAC,CAAC,CAAC,CAaxB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,cAAc,EACrD,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,CAAC,GACT,UAAU,CAAC,CAAC,CAAC,CAaf;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6DG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,cAAc,EAChD,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,CAAC,EACV,YAAY,EAAE,UAAU,CAAC,CAAC,CAAC,GAC1B,UAAU,CAAC,CAAC,CAAC,CAQf;AAMD;;;;GAIG;AACH,MAAM,WAAW,gBAAgB,CAAC,CAAC;IACjC,yCAAyC;IACzC,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC;IACvB,+BAA+B;IAC/B,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;CACnB;AAED;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,iCAAiC;IACjC,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC;IACxB,iCAAiC;IACjC,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,mCAAmC;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,6CAA6C;IAC7C,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAClC,6CAA6C;IAC7C,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,iCAAiC;IACjC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,mDAAmD;IACnD,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1C;AAED;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAC1B,qBAAqB,GACrB,oBAAoB,GACpB,mBAAmB,CAAC;AAExB;;;;GAIG;AACH,MAAM,MAAM,eAAe,CAAC,CAAC,IAAI,gBAAgB,CAAC,CAAC,CAAC,GAAG,gBAAgB,CAAC;AAExE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiGG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,cAAc,EACpD,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,CAAC,GACT,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAkDhC"}
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/lib/guards.ts"],"sourcesContent":["/**\n * Type guards for branded enums.\n *\n * Provides runtime type checking and assertion functions\n * for validating values against branded enums.\n */\n\nimport {\n AnyBrandedEnum,\n BrandedEnum,\n BrandedEnumValue,\n ENUM_ID,\n ENUM_VALUES,\n EnumValues,\n} from './types.js';\n\n/**\n * Checks if an object is a branded enum (has Symbol metadata).\n *\n * @param obj - The object to check\n * @returns true if obj is a branded enum\n */\nfunction isBrandedEnum(obj: unknown): obj is AnyBrandedEnum {\n return (\n obj !== null &&\n typeof obj === 'object' &&\n ENUM_ID in obj &&\n ENUM_VALUES in obj &&\n typeof (obj as AnyBrandedEnum)[ENUM_ID] === 'string' &&\n (obj as AnyBrandedEnum)[ENUM_VALUES] instanceof Set\n );\n}\n\n/**\n * Checks if a value belongs to a specific branded enum.\n *\n * Returns true if and only if:\n * - enumObj is a valid branded enum (has Symbol metadata)\n * - value is a string\n * - value exists in the enum's value Set\n *\n * This function provides TypeScript type narrowing - when it returns true,\n * the value's type is narrowed to the enum's value type.\n *\n * @template E - The branded enum type\n * @param value - The value to check. Can be any type; non-strings return false.\n * @param enumObj - The branded enum to check against\n * @returns `true` if value is in the enum (with type narrowing), `false` otherwise.\n * Returns `false` for non-string values, null, undefined, or if enumObj\n * is not a branded enum.\n *\n * @example\n * // Basic type guard usage\n * const Status = createBrandedEnum('status', { Active: 'active', Inactive: 'inactive' } as const);\n *\n * function handleStatus(value: unknown) {\n * if (isFromEnum(value, Status)) {\n * // value is narrowed to 'active' | 'inactive'\n * console.log('Valid status:', value);\n * } else {\n * console.log('Invalid status');\n * }\n * }\n *\n * @example\n * // Returns false for non-string values\n * isFromEnum(123, Status); // false\n * isFromEnum(null, Status); // false\n * isFromEnum(undefined, Status); // false\n *\n * @example\n * // Returns false for non-branded enum objects\n * isFromEnum('active', { Active: 'active' }); // false (not a branded enum)\n */\nexport function isFromEnum<E extends AnyBrandedEnum>(\n value: unknown,\n enumObj: E\n): value is EnumValues<E> {\n // Return false for non-string values\n if (typeof value !== 'string') {\n return false;\n }\n\n // Return false if enumObj is not a branded enum\n if (!isBrandedEnum(enumObj)) {\n return false;\n }\n\n // Check if value exists in enum's value Set\n return enumObj[ENUM_VALUES].has(value);\n}\n\n/**\n * Asserts that a value belongs to a branded enum, throwing if not.\n *\n * Use this function when you want to validate a value and throw an error\n * if it's invalid, rather than handling the false case manually.\n *\n * @template E - The branded enum type\n * @param value - The value to check. Can be any type.\n * @param enumObj - The branded enum to check against\n * @returns The value with narrowed type if valid\n * @throws {Error} Throws `Error` with message `Second argument is not a branded enum`\n * if enumObj is not a valid branded enum.\n * @throws {Error} Throws `Error` with message `Value \"${value}\" is not a member of enum \"${enumId}\"`\n * if the value is not found in the enum.\n *\n * @example\n * // Successful assertion\n * const Status = createBrandedEnum('status', { Active: 'active', Inactive: 'inactive' } as const);\n * const validated = assertFromEnum('active', Status);\n * // validated is typed as 'active' | 'inactive'\n *\n * @example\n * // Throws for invalid value\n * try {\n * assertFromEnum('unknown', Status);\n * } catch (e) {\n * console.log(e.message); // 'Value \"unknown\" is not a member of enum \"status\"'\n * }\n *\n * @example\n * // Throws for non-branded enum\n * try {\n * assertFromEnum('active', { Active: 'active' });\n * } catch (e) {\n * console.log(e.message); // 'Second argument is not a branded enum'\n * }\n */\nexport function assertFromEnum<E extends AnyBrandedEnum>(\n value: unknown,\n enumObj: E\n): EnumValues<E> {\n // Check if enumObj is a branded enum\n if (!isBrandedEnum(enumObj)) {\n throw new Error('Second argument is not a branded enum');\n }\n\n // Check if value is in the enum\n if (!isFromEnum(value, enumObj)) {\n const enumId = enumObj[ENUM_ID];\n throw new Error(`Value \"${value}\" is not a member of enum \"${enumId}\"`);\n }\n\n return value as EnumValues<E>;\n}\n\n/**\n * Safely parses a value against a branded enum, returning a default if invalid.\n *\n * This is a non-throwing alternative to `assertFromEnum`. Instead of throwing\n * an error when the value is not in the enum, it returns the provided default value.\n *\n * Use this function when you want to handle invalid values gracefully without\n * try/catch blocks, such as when parsing user input or external data.\n *\n * @template E - The branded enum type\n * @param value - The value to parse. Can be any type; non-strings will return the default.\n * @param enumObj - The branded enum to validate against\n * @param defaultValue - The value to return if parsing fails. Must be a valid enum value.\n * @returns The original value if it exists in the enum, otherwise the default value.\n * The return type is narrowed to the enum's value type.\n *\n * @example\n * // Basic usage with default fallback\n * const Status = createBrandedEnum('status', {\n * Active: 'active',\n * Inactive: 'inactive',\n * Pending: 'pending',\n * } as const);\n *\n * // Valid value returns as-is\n * parseEnum('active', Status, Status.Pending); // 'active'\n *\n * // Invalid value returns default\n * parseEnum('unknown', Status, Status.Pending); // 'pending'\n *\n * // Non-string value returns default\n * parseEnum(null, Status, Status.Inactive); // 'inactive'\n * parseEnum(123, Status, Status.Inactive); // 'inactive'\n *\n * @example\n * // Parsing user input safely\n * function handleUserStatus(input: unknown): void {\n * const status = parseEnum(input, Status, Status.Pending);\n * // status is guaranteed to be 'active' | 'inactive' | 'pending'\n * console.log('Processing status:', status);\n * }\n *\n * @example\n * // Parsing API response with fallback\n * interface ApiResponse {\n * status?: string;\n * }\n *\n * function processResponse(response: ApiResponse) {\n * const status = parseEnum(response.status, Status, Status.Inactive);\n * // Handles undefined, null, or invalid status values gracefully\n * return { status };\n * }\n *\n * @example\n * // Chaining with optional values\n * const userStatus = parseEnum(\n * localStorage.getItem('userStatus'),\n * Status,\n * Status.Active\n * );\n */\nexport function parseEnum<E extends AnyBrandedEnum>(\n value: unknown,\n enumObj: E,\n defaultValue: EnumValues<E>\n): EnumValues<E> {\n // If the value is valid, return it\n if (isFromEnum(value, enumObj)) {\n return value;\n }\n\n // Otherwise return the default\n return defaultValue;\n}\n\n// =============================================================================\n// Safe Parse Result Types\n// =============================================================================\n\n/**\n * Represents a successful parse result from safeParseEnum.\n *\n * @template T - The type of the successfully parsed value\n */\nexport interface SafeParseSuccess<T> {\n /** Indicates the parse was successful */\n readonly success: true;\n /** The validated enum value */\n readonly value: T;\n}\n\n/**\n * Represents a failed parse result from safeParseEnum.\n *\n * Contains detailed error information for debugging and user feedback.\n */\nexport interface SafeParseFailure {\n /** Indicates the parse failed */\n readonly success: false;\n /** Detailed error information */\n readonly error: SafeParseError;\n}\n\n/**\n * Detailed error information for a failed parse.\n */\nexport interface SafeParseError {\n /** Human-readable error message */\n readonly message: string;\n /** The code identifying the type of error */\n readonly code: SafeParseErrorCode;\n /** The input value that failed validation */\n readonly input: unknown;\n /** The enum ID (if available) */\n readonly enumId?: string;\n /** The valid values for the enum (if available) */\n readonly validValues?: readonly string[];\n}\n\n/**\n * Error codes for safe parse failures.\n */\nexport type SafeParseErrorCode =\n | 'INVALID_ENUM_OBJECT'\n | 'INVALID_VALUE_TYPE'\n | 'VALUE_NOT_IN_ENUM';\n\n/**\n * Union type representing the result of safeParseEnum.\n *\n * @template T - The type of the successfully parsed value\n */\nexport type SafeParseResult<T> = SafeParseSuccess<T> | SafeParseFailure;\n\n/**\n * Safely parses a value against a branded enum, returning a result object.\n *\n * This function provides validated deserialization with detailed error information.\n * Unlike `parseEnum` which returns a default value on failure, or `assertFromEnum`\n * which throws an error, `safeParseEnum` returns a discriminated union result\n * that allows for explicit success/failure handling.\n *\n * The result is either:\n * - `{ success: true, value: T }` - The value is valid and typed correctly\n * - `{ success: false, error: SafeParseError }` - The value is invalid with details\n *\n * Error codes:\n * - `INVALID_ENUM_OBJECT` - The enumObj is not a valid branded enum\n * - `INVALID_VALUE_TYPE` - The value is not a string\n * - `VALUE_NOT_IN_ENUM` - The value is a string but not in the enum\n *\n * @template E - The branded enum type\n * @param value - The value to parse. Can be any type.\n * @param enumObj - The branded enum to validate against\n * @returns A SafeParseResult containing either the validated value or error details\n *\n * @example\n * // Basic usage with success\n * const Status = createBrandedEnum('status', {\n * Active: 'active',\n * Inactive: 'inactive',\n * } as const);\n *\n * const result = safeParseEnum('active', Status);\n * if (result.success) {\n * console.log('Valid status:', result.value); // 'active'\n * } else {\n * console.log('Error:', result.error.message);\n * }\n *\n * @example\n * // Handling invalid value\n * const result = safeParseEnum('unknown', Status);\n * if (!result.success) {\n * console.log(result.error.code); // 'VALUE_NOT_IN_ENUM'\n * console.log(result.error.message); // 'Value \"unknown\" is not a member of enum \"status\"'\n * console.log(result.error.validValues); // ['active', 'inactive']\n * }\n *\n * @example\n * // Handling non-string input\n * const result = safeParseEnum(123, Status);\n * if (!result.success) {\n * console.log(result.error.code); // 'INVALID_VALUE_TYPE'\n * console.log(result.error.message); // 'Expected a string value, received number'\n * }\n *\n * @example\n * // Parsing API response\n * interface ApiResponse {\n * status?: string;\n * }\n *\n * function processResponse(response: ApiResponse) {\n * const result = safeParseEnum(response.status, Status);\n * if (result.success) {\n * return { status: result.value };\n * } else {\n * // Log detailed error for debugging\n * console.error('Invalid status:', result.error);\n * return { status: Status.Inactive }; // fallback\n * }\n * }\n *\n * @example\n * // Form validation with detailed errors\n * function validateForm(data: Record<string, unknown>) {\n * const statusResult = safeParseEnum(data.status, Status);\n * const errors: string[] = [];\n *\n * if (!statusResult.success) {\n * errors.push(`Status: ${statusResult.error.message}`);\n * }\n *\n * return {\n * isValid: errors.length === 0,\n * errors,\n * data: statusResult.success ? { status: statusResult.value } : null,\n * };\n * }\n *\n * @example\n * // Type narrowing with result\n * const result = safeParseEnum(userInput, Status);\n * if (result.success) {\n * // result.value is typed as 'active' | 'inactive'\n * handleStatus(result.value);\n * } else {\n * // result.error is typed as SafeParseError\n * showError(result.error.message);\n * }\n */\nexport function safeParseEnum<E extends AnyBrandedEnum>(\n value: unknown,\n enumObj: E\n): SafeParseResult<EnumValues<E>> {\n // Check if enumObj is a branded enum\n if (!isBrandedEnum(enumObj)) {\n return {\n success: false,\n error: {\n message: 'Second argument is not a branded enum',\n code: 'INVALID_ENUM_OBJECT',\n input: value,\n },\n };\n }\n\n const enumId = enumObj[ENUM_ID];\n const validValues = Array.from(enumObj[ENUM_VALUES]).sort();\n\n // Check if value is a string\n if (typeof value !== 'string') {\n const valueType = value === null ? 'null' : typeof value;\n return {\n success: false,\n error: {\n message: `Expected a string value, received ${valueType}`,\n code: 'INVALID_VALUE_TYPE',\n input: value,\n enumId,\n validValues,\n },\n };\n }\n\n // Check if value is in the enum\n if (!enumObj[ENUM_VALUES].has(value)) {\n return {\n success: false,\n error: {\n message: `Value \"${value}\" is not a member of enum \"${enumId}\"`,\n code: 'VALUE_NOT_IN_ENUM',\n input: value,\n enumId,\n validValues,\n },\n };\n }\n\n // Success case\n return {\n success: true,\n value: value as EnumValues<E>,\n };\n}\n"],"names":["ENUM_ID","ENUM_VALUES","isBrandedEnum","obj","Set","isFromEnum","value","enumObj","has","assertFromEnum","Error","enumId","parseEnum","defaultValue","safeParseEnum","success","error","message","code","input","validValues","Array","from","sort","valueType"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"AAAA;;;;;CAKC,GAED,SAIEA,OAAO,EACPC,WAAW,QAEN,aAAa;AAEpB;;;;;CAKC,GACD,SAASC,cAAcC,GAAY;IACjC,OACEA,QAAQ,QACR,OAAOA,QAAQ,YACfH,WAAWG,OACXF,eAAeE,OACf,OAAO,AAACA,GAAsB,CAACH,QAAQ,KAAK,YAC5C,AAACG,GAAsB,CAACF,YAAY,YAAYG;AAEpD;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwCC,GACD,OAAO,SAASC,WACdC,KAAc,EACdC,OAAU;IAEV,qCAAqC;IACrC,IAAI,OAAOD,UAAU,UAAU;QAC7B,OAAO;IACT;IAEA,gDAAgD;IAChD,IAAI,CAACJ,cAAcK,UAAU;QAC3B,OAAO;IACT;IAEA,4CAA4C;IAC5C,OAAOA,OAAO,CAACN,YAAY,CAACO,GAAG,CAACF;AAClC;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoCC,GACD,OAAO,SAASG,eACdH,KAAc,EACdC,OAAU;IAEV,qCAAqC;IACrC,IAAI,CAACL,cAAcK,UAAU;QAC3B,MAAM,IAAIG,MAAM;IAClB;IAEA,gCAAgC;IAChC,IAAI,CAACL,WAAWC,OAAOC,UAAU;QAC/B,MAAMI,SAASJ,OAAO,CAACP,QAAQ;QAC/B,MAAM,IAAIU,MAAM,CAAC,OAAO,EAAEJ,MAAM,2BAA2B,EAAEK,OAAO,CAAC,CAAC;IACxE;IAEA,OAAOL;AACT;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6DC,GACD,OAAO,SAASM,UACdN,KAAc,EACdC,OAAU,EACVM,YAA2B;IAE3B,mCAAmC;IACnC,IAAIR,WAAWC,OAAOC,UAAU;QAC9B,OAAOD;IACT;IAEA,+BAA+B;IAC/B,OAAOO;AACT;AA6DA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiGC,GACD,OAAO,SAASC,cACdR,KAAc,EACdC,OAAU;IAEV,qCAAqC;IACrC,IAAI,CAACL,cAAcK,UAAU;QAC3B,OAAO;YACLQ,SAAS;YACTC,OAAO;gBACLC,SAAS;gBACTC,MAAM;gBACNC,OAAOb;YACT;QACF;IACF;IAEA,MAAMK,SAASJ,OAAO,CAACP,QAAQ;IAC/B,MAAMoB,cAAcC,MAAMC,IAAI,CAACf,OAAO,CAACN,YAAY,EAAEsB,IAAI;IAEzD,6BAA6B;IAC7B,IAAI,OAAOjB,UAAU,UAAU;QAC7B,MAAMkB,YAAYlB,UAAU,OAAO,SAAS,OAAOA;QACnD,OAAO;YACLS,SAAS;YACTC,OAAO;gBACLC,SAAS,CAAC,kCAAkC,EAAEO,UAAU,CAAC;gBACzDN,MAAM;gBACNC,OAAOb;gBACPK;gBACAS;YACF;QACF;IACF;IAEA,gCAAgC;IAChC,IAAI,CAACb,OAAO,CAACN,YAAY,CAACO,GAAG,CAACF,QAAQ;QACpC,OAAO;YACLS,SAAS;YACTC,OAAO;gBACLC,SAAS,CAAC,OAAO,EAAEX,MAAM,2BAA2B,EAAEK,OAAO,CAAC,CAAC;gBAC/DO,MAAM;gBACNC,OAAOb;gBACPK;gBACAS;YACF;QACF;IACF;IAEA,eAAe;IACf,OAAO;QACLL,SAAS;QACTT,OAAOA;IACT;AACF"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Enum composition functions for branded enums.
3
+ *
4
+ * Enables merging multiple branded enums into a new combined enum
5
+ * while maintaining type safety and registry tracking.
6
+ */
7
+ import { AnyBrandedEnum, BrandedEnum } from './types.js';
8
+ /**
9
+ * Merges multiple branded enums into a new branded enum.
10
+ *
11
+ * Creates a new branded enum that contains all key-value pairs from all
12
+ * source enums. The merged enum is registered in the global registry
13
+ * as a new independent enum.
14
+ *
15
+ * Key collision handling:
16
+ * - Duplicate keys (same key in multiple enums) throw an error
17
+ * - Duplicate values (same value in multiple enums) are allowed
18
+ *
19
+ * @template T - Tuple of branded enum types being merged
20
+ * @param newId - Unique identifier for the merged enum. Must not already
21
+ * be registered.
22
+ * @param enums - One or more branded enums to merge
23
+ * @returns A new branded enum containing all values from source enums
24
+ * @throws {Error} Throws `Error` with message
25
+ * `Cannot merge enums: duplicate key "${key}" found in enums "${enumId1}" and "${enumId2}"`
26
+ * if the same key exists in multiple source enums.
27
+ * @throws {Error} Throws `Error` with message
28
+ * `Branded enum with ID "${newId}" already exists` if newId is already registered.
29
+ * @throws {Error} Throws `Error` with message `All arguments must be branded enums`
30
+ * if any argument is not a valid branded enum.
31
+ *
32
+ * @example
33
+ * // Basic merge
34
+ * const Colors = createBrandedEnum('colors', { Red: 'red', Blue: 'blue' } as const);
35
+ * const Sizes = createBrandedEnum('sizes', { Small: 'small', Large: 'large' } as const);
36
+ *
37
+ * const Combined = mergeEnums('combined', Colors, Sizes);
38
+ * // Combined has: Red, Blue, Small, Large
39
+ *
40
+ * Combined.Red; // 'red'
41
+ * Combined.Small; // 'small'
42
+ *
43
+ * @example
44
+ * // Duplicate values are allowed
45
+ * const Status1 = createBrandedEnum('status1', { Active: 'active' } as const);
46
+ * const Status2 = createBrandedEnum('status2', { Enabled: 'active' } as const);
47
+ *
48
+ * const Merged = mergeEnums('merged', Status1, Status2);
49
+ * // Both Active and Enabled have value 'active' - this is allowed
50
+ *
51
+ * @example
52
+ * // Duplicate keys throw an error
53
+ * const Enum1 = createBrandedEnum('enum1', { Key: 'value1' } as const);
54
+ * const Enum2 = createBrandedEnum('enum2', { Key: 'value2' } as const);
55
+ *
56
+ * try {
57
+ * mergeEnums('merged', Enum1, Enum2);
58
+ * } catch (e) {
59
+ * console.log(e.message);
60
+ * // 'Cannot merge enums: duplicate key "Key" found in enums "enum1" and "enum2"'
61
+ * }
62
+ */
63
+ export declare function mergeEnums<T extends readonly AnyBrandedEnum[]>(newId: string, ...enums: T): BrandedEnum<Record<string, string>>;
64
+ //# sourceMappingURL=merge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../../../src/lib/merge.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,cAAc,EAAE,WAAW,EAAwB,MAAM,YAAY,CAAC;AA2B/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,SAAS,cAAc,EAAE,EAC5D,KAAK,EAAE,MAAM,EACb,GAAG,KAAK,EAAE,CAAC,GACV,WAAW,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CA6BrC"}
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/lib/merge.ts"],"sourcesContent":["/**\n * Enum composition functions for branded enums.\n *\n * Enables merging multiple branded enums into a new combined enum\n * while maintaining type safety and registry tracking.\n */\n\nimport { AnyBrandedEnum, BrandedEnum, ENUM_ID, ENUM_VALUES } from './types.js';\nimport { createBrandedEnum } from './factory.js';\n\n/**\n * Type helper to extract the values type from a branded enum.\n */\ntype ExtractValues<E> = E extends BrandedEnum<infer T> ? T : never;\n\n/**\n * Type helper to merge multiple branded enum value types into one.\n */\ntype MergedValues<T extends AnyBrandedEnum[]> = {\n [K in keyof T]: ExtractValues<T[K]>;\n}[number];\n\n/**\n * Checks if an object is a branded enum.\n */\nfunction isBrandedEnum(obj: unknown): obj is AnyBrandedEnum {\n return (\n typeof obj === 'object' &&\n obj !== null &&\n ENUM_ID in obj &&\n ENUM_VALUES in obj\n );\n}\n\n/**\n * Merges multiple branded enums into a new branded enum.\n *\n * Creates a new branded enum that contains all key-value pairs from all\n * source enums. The merged enum is registered in the global registry\n * as a new independent enum.\n *\n * Key collision handling:\n * - Duplicate keys (same key in multiple enums) throw an error\n * - Duplicate values (same value in multiple enums) are allowed\n *\n * @template T - Tuple of branded enum types being merged\n * @param newId - Unique identifier for the merged enum. Must not already\n * be registered.\n * @param enums - One or more branded enums to merge\n * @returns A new branded enum containing all values from source enums\n * @throws {Error} Throws `Error` with message\n * `Cannot merge enums: duplicate key \"${key}\" found in enums \"${enumId1}\" and \"${enumId2}\"`\n * if the same key exists in multiple source enums.\n * @throws {Error} Throws `Error` with message\n * `Branded enum with ID \"${newId}\" already exists` if newId is already registered.\n * @throws {Error} Throws `Error` with message `All arguments must be branded enums`\n * if any argument is not a valid branded enum.\n *\n * @example\n * // Basic merge\n * const Colors = createBrandedEnum('colors', { Red: 'red', Blue: 'blue' } as const);\n * const Sizes = createBrandedEnum('sizes', { Small: 'small', Large: 'large' } as const);\n *\n * const Combined = mergeEnums('combined', Colors, Sizes);\n * // Combined has: Red, Blue, Small, Large\n *\n * Combined.Red; // 'red'\n * Combined.Small; // 'small'\n *\n * @example\n * // Duplicate values are allowed\n * const Status1 = createBrandedEnum('status1', { Active: 'active' } as const);\n * const Status2 = createBrandedEnum('status2', { Enabled: 'active' } as const);\n *\n * const Merged = mergeEnums('merged', Status1, Status2);\n * // Both Active and Enabled have value 'active' - this is allowed\n *\n * @example\n * // Duplicate keys throw an error\n * const Enum1 = createBrandedEnum('enum1', { Key: 'value1' } as const);\n * const Enum2 = createBrandedEnum('enum2', { Key: 'value2' } as const);\n *\n * try {\n * mergeEnums('merged', Enum1, Enum2);\n * } catch (e) {\n * console.log(e.message);\n * // 'Cannot merge enums: duplicate key \"Key\" found in enums \"enum1\" and \"enum2\"'\n * }\n */\nexport function mergeEnums<T extends readonly AnyBrandedEnum[]>(\n newId: string,\n ...enums: T\n): BrandedEnum<Record<string, string>> {\n // Collect all key-value pairs, checking for duplicate keys\n const mergedValues: Record<string, string> = {};\n const seenKeys = new Map<string, string>(); // key -> source enumId\n\n for (const enumObj of enums) {\n if (!isBrandedEnum(enumObj)) {\n throw new Error('All arguments must be branded enums');\n }\n\n const sourceEnumId = enumObj[ENUM_ID];\n\n // Iterate over enumerable properties (user-defined keys only)\n for (const [key, value] of Object.entries(enumObj)) {\n // Check for duplicate keys\n if (seenKeys.has(key)) {\n const originalEnumId = seenKeys.get(key);\n throw new Error(\n `Cannot merge enums: duplicate key \"${key}\" found in enums \"${originalEnumId}\" and \"${sourceEnumId}\"`\n );\n }\n\n seenKeys.set(key, sourceEnumId);\n mergedValues[key] = value as string;\n }\n }\n\n // Create and return the new branded enum (this handles registration)\n return createBrandedEnum(newId, mergedValues) as BrandedEnum<MergedValues<[...T]>>;\n}\n"],"names":["ENUM_ID","ENUM_VALUES","createBrandedEnum","isBrandedEnum","obj","mergeEnums","newId","enums","mergedValues","seenKeys","Map","enumObj","Error","sourceEnumId","key","value","Object","entries","has","originalEnumId","get","set"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"AAAA;;;;;CAKC,GAED,SAAsCA,OAAO,EAAEC,WAAW,QAAQ,aAAa;AAC/E,SAASC,iBAAiB,QAAQ,eAAe;AAcjD;;CAEC,GACD,SAASC,cAAcC,GAAY;IACjC,OACE,OAAOA,QAAQ,YACfA,QAAQ,QACRJ,WAAWI,OACXH,eAAeG;AAEnB;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsDC,GACD,OAAO,SAASC,WACdC,KAAa,EACb,GAAGC,KAAQ;IAEX,2DAA2D;IAC3D,MAAMC,eAAuC,CAAC;IAC9C,MAAMC,WAAW,IAAIC,OAAuB,uBAAuB;IAEnE,KAAK,MAAMC,WAAWJ,MAAO;QAC3B,IAAI,CAACJ,cAAcQ,UAAU;YAC3B,MAAM,IAAIC,MAAM;QAClB;QAEA,MAAMC,eAAeF,OAAO,CAACX,QAAQ;QAErC,8DAA8D;QAC9D,KAAK,MAAM,CAACc,KAAKC,MAAM,IAAIC,OAAOC,OAAO,CAACN,SAAU;YAClD,2BAA2B;YAC3B,IAAIF,SAASS,GAAG,CAACJ,MAAM;gBACrB,MAAMK,iBAAiBV,SAASW,GAAG,CAACN;gBACpC,MAAM,IAAIF,MACR,CAAC,mCAAmC,EAAEE,IAAI,kBAAkB,EAAEK,eAAe,OAAO,EAAEN,aAAa,CAAC,CAAC;YAEzG;YAEAJ,SAASY,GAAG,CAACP,KAAKD;YAClBL,YAAY,CAACM,IAAI,GAAGC;QACtB;IACF;IAEA,qEAAqE;IACrE,OAAOb,kBAAkBI,OAAOE;AAClC"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Global registry for branded enums.
3
+ *
4
+ * Uses globalThis to ensure cross-bundle compatibility - all instances
5
+ * of the library share the same registry regardless of how they're bundled.
6
+ */
7
+ import { BrandedEnum, BrandedEnumRegistry } from './types.js';
8
+ /**
9
+ * Gets the global registry, initializing it lazily if needed.
10
+ * Uses globalThis for cross-bundle compatibility.
11
+ *
12
+ * The registry is shared across all instances of the library, even when
13
+ * bundled separately or loaded as different module formats (ESM/CJS).
14
+ *
15
+ * @returns The global branded enum registry containing all registered enums
16
+ * and a value index for reverse lookups.
17
+ *
18
+ * @example
19
+ * const registry = getRegistry();
20
+ * console.log(registry.enums.size); // Number of registered enums
21
+ */
22
+ export declare function getRegistry(): BrandedEnumRegistry;
23
+ /**
24
+ * Registers a branded enum in the global registry.
25
+ * Also updates the value index for reverse lookups.
26
+ *
27
+ * @param enumObj - The branded enum to register
28
+ * @throws Error if an enum with the same ID is already registered
29
+ */
30
+ export declare function registerEnum<T extends Record<string, string>>(enumObj: BrandedEnum<T>): void;
31
+ /**
32
+ * Gets all registered enum IDs.
33
+ *
34
+ * Returns an array of all enum IDs that have been registered via
35
+ * `createBrandedEnum`. Useful for debugging or introspection.
36
+ *
37
+ * @returns Array of all registered enum IDs. Returns empty array if no
38
+ * enums have been registered.
39
+ *
40
+ * @example
41
+ * createBrandedEnum('colors', { Red: 'red' } as const);
42
+ * createBrandedEnum('sizes', { Small: 'small' } as const);
43
+ *
44
+ * getAllEnumIds(); // ['colors', 'sizes']
45
+ */
46
+ export declare function getAllEnumIds(): string[];
47
+ /**
48
+ * Gets a branded enum by its ID.
49
+ *
50
+ * Retrieves a previously registered branded enum from the global registry.
51
+ * Useful when you need to access an enum dynamically by its ID.
52
+ *
53
+ * @param enumId - The enum ID to look up
54
+ * @returns The branded enum object if found, or `undefined` if no enum
55
+ * with the given ID has been registered.
56
+ *
57
+ * @example
58
+ * const Status = createBrandedEnum('status', { Active: 'active' } as const);
59
+ *
60
+ * const retrieved = getEnumById('status');
61
+ * console.log(retrieved === Status); // true
62
+ *
63
+ * const notFound = getEnumById('nonexistent');
64
+ * console.log(notFound); // undefined
65
+ */
66
+ export declare function getEnumById(enumId: string): BrandedEnum<Record<string, string>> | undefined;
67
+ /**
68
+ * Finds all enum IDs that contain a given value.
69
+ *
70
+ * Performs a reverse lookup to find which enums contain a specific value.
71
+ * Useful for debugging value collisions or routing values to handlers.
72
+ *
73
+ * @param value - The string value to search for
74
+ * @returns Array of enum IDs that contain the value. Returns empty array
75
+ * if no enums contain the value.
76
+ *
77
+ * @example
78
+ * // Single enum containing value
79
+ * createBrandedEnum('colors', { Red: 'red', Blue: 'blue' } as const);
80
+ * findEnumSources('red'); // ['colors']
81
+ *
82
+ * @example
83
+ * // Multiple enums with same value (collision detection)
84
+ * createBrandedEnum('status1', { Active: 'active' } as const);
85
+ * createBrandedEnum('status2', { Enabled: 'active' } as const);
86
+ * findEnumSources('active'); // ['status1', 'status2']
87
+ *
88
+ * @example
89
+ * // Value not found
90
+ * findEnumSources('nonexistent'); // []
91
+ */
92
+ export declare function findEnumSources(value: string): string[];
93
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/lib/registry.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,WAAW,EACX,mBAAmB,EAKpB,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,IAAI,mBAAmB,CAajD;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC3D,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,GACtB,IAAI,CA6BN;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,IAAI,MAAM,EAAE,CAGxC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,MAAM,GACb,WAAW,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,SAAS,CAIjD;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAIvD"}
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/lib/registry.ts"],"sourcesContent":["/**\n * Global registry for branded enums.\n *\n * Uses globalThis to ensure cross-bundle compatibility - all instances\n * of the library share the same registry regardless of how they're bundled.\n */\n\nimport {\n BrandedEnum,\n BrandedEnumRegistry,\n RegistryEntry,\n REGISTRY_KEY,\n ENUM_ID,\n ENUM_VALUES,\n} from './types.js';\n\n/**\n * Gets the global registry, initializing it lazily if needed.\n * Uses globalThis for cross-bundle compatibility.\n *\n * The registry is shared across all instances of the library, even when\n * bundled separately or loaded as different module formats (ESM/CJS).\n *\n * @returns The global branded enum registry containing all registered enums\n * and a value index for reverse lookups.\n *\n * @example\n * const registry = getRegistry();\n * console.log(registry.enums.size); // Number of registered enums\n */\nexport function getRegistry(): BrandedEnumRegistry {\n const global = globalThis as typeof globalThis & {\n [REGISTRY_KEY]?: BrandedEnumRegistry;\n };\n\n if (!(REGISTRY_KEY in global) || !global[REGISTRY_KEY]) {\n global[REGISTRY_KEY] = {\n enums: new Map<string, RegistryEntry>(),\n valueIndex: new Map<string, Set<string>>(),\n };\n }\n\n return global[REGISTRY_KEY];\n}\n\n/**\n * Registers a branded enum in the global registry.\n * Also updates the value index for reverse lookups.\n *\n * @param enumObj - The branded enum to register\n * @throws Error if an enum with the same ID is already registered\n */\nexport function registerEnum<T extends Record<string, string>>(\n enumObj: BrandedEnum<T>\n): void {\n const registry = getRegistry();\n const enumId = enumObj[ENUM_ID];\n const values = enumObj[ENUM_VALUES];\n\n // Check for duplicate ID\n if (registry.enums.has(enumId)) {\n throw new Error(`Branded enum with ID \"${enumId}\" already exists`);\n }\n\n // Create registry entry\n const entry: RegistryEntry = {\n enumId,\n enumObj: enumObj as BrandedEnum<Record<string, string>>,\n values,\n };\n\n // Add to enums map\n registry.enums.set(enumId, entry);\n\n // Update value index for reverse lookups\n for (const value of values) {\n let enumIds = registry.valueIndex.get(value);\n if (!enumIds) {\n enumIds = new Set<string>();\n registry.valueIndex.set(value, enumIds);\n }\n enumIds.add(enumId);\n }\n}\n\n/**\n * Gets all registered enum IDs.\n *\n * Returns an array of all enum IDs that have been registered via\n * `createBrandedEnum`. Useful for debugging or introspection.\n *\n * @returns Array of all registered enum IDs. Returns empty array if no\n * enums have been registered.\n *\n * @example\n * createBrandedEnum('colors', { Red: 'red' } as const);\n * createBrandedEnum('sizes', { Small: 'small' } as const);\n *\n * getAllEnumIds(); // ['colors', 'sizes']\n */\nexport function getAllEnumIds(): string[] {\n const registry = getRegistry();\n return Array.from(registry.enums.keys());\n}\n\n/**\n * Gets a branded enum by its ID.\n *\n * Retrieves a previously registered branded enum from the global registry.\n * Useful when you need to access an enum dynamically by its ID.\n *\n * @param enumId - The enum ID to look up\n * @returns The branded enum object if found, or `undefined` if no enum\n * with the given ID has been registered.\n *\n * @example\n * const Status = createBrandedEnum('status', { Active: 'active' } as const);\n *\n * const retrieved = getEnumById('status');\n * console.log(retrieved === Status); // true\n *\n * const notFound = getEnumById('nonexistent');\n * console.log(notFound); // undefined\n */\nexport function getEnumById(\n enumId: string\n): BrandedEnum<Record<string, string>> | undefined {\n const registry = getRegistry();\n const entry = registry.enums.get(enumId);\n return entry?.enumObj;\n}\n\n/**\n * Finds all enum IDs that contain a given value.\n *\n * Performs a reverse lookup to find which enums contain a specific value.\n * Useful for debugging value collisions or routing values to handlers.\n *\n * @param value - The string value to search for\n * @returns Array of enum IDs that contain the value. Returns empty array\n * if no enums contain the value.\n *\n * @example\n * // Single enum containing value\n * createBrandedEnum('colors', { Red: 'red', Blue: 'blue' } as const);\n * findEnumSources('red'); // ['colors']\n *\n * @example\n * // Multiple enums with same value (collision detection)\n * createBrandedEnum('status1', { Active: 'active' } as const);\n * createBrandedEnum('status2', { Enabled: 'active' } as const);\n * findEnumSources('active'); // ['status1', 'status2']\n *\n * @example\n * // Value not found\n * findEnumSources('nonexistent'); // []\n */\nexport function findEnumSources(value: string): string[] {\n const registry = getRegistry();\n const enumIds = registry.valueIndex.get(value);\n return enumIds ? Array.from(enumIds) : [];\n}\n"],"names":["REGISTRY_KEY","ENUM_ID","ENUM_VALUES","getRegistry","global","globalThis","enums","Map","valueIndex","registerEnum","enumObj","registry","enumId","values","has","Error","entry","set","value","enumIds","get","Set","add","getAllEnumIds","Array","from","keys","getEnumById","findEnumSources"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"AAAA;;;;;CAKC,GAED,SAIEA,YAAY,EACZC,OAAO,EACPC,WAAW,QACN,aAAa;AAEpB;;;;;;;;;;;;;CAaC,GACD,OAAO,SAASC;IACd,MAAMC,SAASC;IAIf,IAAI,CAAEL,CAAAA,gBAAgBI,MAAK,KAAM,CAACA,MAAM,CAACJ,aAAa,EAAE;QACtDI,MAAM,CAACJ,aAAa,GAAG;YACrBM,OAAO,IAAIC;YACXC,YAAY,IAAID;QAClB;IACF;IAEA,OAAOH,MAAM,CAACJ,aAAa;AAC7B;AAEA;;;;;;CAMC,GACD,OAAO,SAASS,aACdC,OAAuB;IAEvB,MAAMC,WAAWR;IACjB,MAAMS,SAASF,OAAO,CAACT,QAAQ;IAC/B,MAAMY,SAASH,OAAO,CAACR,YAAY;IAEnC,yBAAyB;IACzB,IAAIS,SAASL,KAAK,CAACQ,GAAG,CAACF,SAAS;QAC9B,MAAM,IAAIG,MAAM,CAAC,sBAAsB,EAAEH,OAAO,gBAAgB,CAAC;IACnE;IAEA,wBAAwB;IACxB,MAAMI,QAAuB;QAC3BJ;QACAF,SAASA;QACTG;IACF;IAEA,mBAAmB;IACnBF,SAASL,KAAK,CAACW,GAAG,CAACL,QAAQI;IAE3B,yCAAyC;IACzC,KAAK,MAAME,SAASL,OAAQ;QAC1B,IAAIM,UAAUR,SAASH,UAAU,CAACY,GAAG,CAACF;QACtC,IAAI,CAACC,SAAS;YACZA,UAAU,IAAIE;YACdV,SAASH,UAAU,CAACS,GAAG,CAACC,OAAOC;QACjC;QACAA,QAAQG,GAAG,CAACV;IACd;AACF;AAEA;;;;;;;;;;;;;;CAcC,GACD,OAAO,SAASW;IACd,MAAMZ,WAAWR;IACjB,OAAOqB,MAAMC,IAAI,CAACd,SAASL,KAAK,CAACoB,IAAI;AACvC;AAEA;;;;;;;;;;;;;;;;;;CAkBC,GACD,OAAO,SAASC,YACdf,MAAc;IAEd,MAAMD,WAAWR;IACjB,MAAMa,QAAQL,SAASL,KAAK,CAACc,GAAG,CAACR;IACjC,OAAOI,OAAON;AAChB;AAEA;;;;;;;;;;;;;;;;;;;;;;;;CAwBC,GACD,OAAO,SAASkB,gBAAgBV,KAAa;IAC3C,MAAMP,WAAWR;IACjB,MAAMgB,UAAUR,SAASH,UAAU,CAACY,GAAG,CAACF;IACxC,OAAOC,UAAUK,MAAMC,IAAI,CAACN,WAAW,EAAE;AAC3C"}