@forklaunch/common 0.3.14 → 0.4.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/lib/index.d.mts CHANGED
@@ -1,3 +1,200 @@
1
+ /**
2
+ * Utility types for string manipulation and validation
3
+ */
4
+ /**
5
+ * Remove leading separators from a string
6
+ */
7
+ type RemoveLeadingSeparators<S extends string> = S extends `${' ' | '-' | '_' | '.' | '/' | ':'}${infer Rest}` ? RemoveLeadingSeparators<Rest> : S;
8
+ /**
9
+ * Split a string by separators (space, dash, underscore, dot, slash, colon)
10
+ */
11
+ type SplitBySeparators<S extends string> = S extends `${infer First}${' ' | '-' | '_' | '.' | '/' | ':'}${infer Rest}` ? [First, ...SplitBySeparators<Rest>] : S extends '' ? [] : [S];
12
+ /**
13
+ * Capitalize the first letter of a string
14
+ */
15
+ type Capitalize<S extends string> = S extends `${infer First}${infer Rest}` ? `${Uppercase<First>}${Rest}` : S;
16
+ /**
17
+ * Convert the first letter to lowercase
18
+ */
19
+ type Uncapitalize<S extends string> = S extends `${infer First}${infer Rest}` ? `${Lowercase<First>}${Rest}` : S;
20
+ /**
21
+ * Remove all non-alphanumeric characters from a string
22
+ */
23
+ type RemoveNonAlphanumeric<S extends string> = S extends `${infer First}${infer Rest}` ? First extends 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' ? `${First}${RemoveNonAlphanumeric<Rest>}` : RemoveNonAlphanumeric<Rest> : '';
24
+ /**
25
+ * Remove leading numbers from a string to make it a valid identifier
26
+ */
27
+ type RemoveLeadingNumbers<S extends string> = S extends `${infer First}${infer Rest}` ? First extends '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' ? RemoveLeadingNumbers<Rest> : S : S;
28
+ /**
29
+ * Join an array of strings into camelCase
30
+ */
31
+ type JoinCamelCase<T extends readonly string[]> = T extends readonly [
32
+ infer First,
33
+ ...infer Rest
34
+ ] ? First extends string ? Rest extends readonly string[] ? Rest['length'] extends 0 ? Uncapitalize<RemoveNonAlphanumeric<First>> : `${Uncapitalize<RemoveNonAlphanumeric<First>>}${JoinCamelCaseCapitalized<Rest>}` : never : never : '';
35
+ /**
36
+ * Join remaining parts with capitalized first letters
37
+ */
38
+ type JoinCamelCaseCapitalized<T extends readonly string[]> = T extends readonly [infer First, ...infer Rest] ? First extends string ? Rest extends readonly string[] ? Rest['length'] extends 0 ? Capitalize<RemoveNonAlphanumeric<First>> : `${Capitalize<RemoveNonAlphanumeric<First>>}${JoinCamelCaseCapitalized<Rest>}` : never : never : '';
39
+ /**
40
+ * Convert a string to a valid TypeScript identifier in camelCase
41
+ * Always returns a string, defaulting to the input if transformation fails
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * type Example1 = CamelCaseIdentifier<"hello-world">; // "helloWorld"
46
+ * type Example2 = CamelCaseIdentifier<"my_var_name">; // "myVarName"
47
+ * type Example3 = CamelCaseIdentifier<"some.property.name">; // "somePropertyName"
48
+ * type Example4 = CamelCaseIdentifier<"123invalid">; // "invalid"
49
+ * type Example5 = CamelCaseIdentifier<"hello world">; // "helloWorld"
50
+ * type Example6 = CamelCaseIdentifier<"API-Key">; // "aPIKey"
51
+ * type Example7 = CamelCaseIdentifier<"/organization/base">; // "organizationBase"
52
+ * ```
53
+ */
54
+ type CamelCaseIdentifier<S extends string> = RemoveLeadingNumbers<JoinCamelCase<SplitBySeparators<RemoveLeadingSeparators<S>>>> extends infer Result ? Result extends string ? Result extends '' ? S : Result : S : S;
55
+ /**
56
+ * Improved version that handles consecutive uppercase letters better
57
+ * by converting them to proper camelCase
58
+ */
59
+ type LowerCaseWord<S extends string> = Lowercase<S>;
60
+ /**
61
+ * More intuitive camelCase conversion that properly handles abbreviations
62
+ *
63
+ * @example
64
+ * ```typescript
65
+ * type Example1 = PrettyCamelCase<"API-Key">; // "apiKey"
66
+ * type Example2 = PrettyCamelCase<"HTTP-Response">; // "httpResponse"
67
+ * type Example3 = PrettyCamelCase<"user-ID">; // "userId"
68
+ * ```
69
+ */
70
+ type JoinPrettyCamelCase<T extends readonly string[]> = T extends readonly [
71
+ infer First,
72
+ ...infer Rest
73
+ ] ? First extends string ? Rest extends readonly string[] ? Rest['length'] extends 0 ? LowerCaseWord<RemoveNonAlphanumeric<First>> : `${LowerCaseWord<RemoveNonAlphanumeric<First>>}${JoinPrettyCamelCaseCapitalized<Rest>}` : never : never : '';
74
+ type JoinPrettyCamelCaseCapitalized<T extends readonly string[]> = T extends readonly [infer First, ...infer Rest] ? First extends string ? Rest extends readonly string[] ? Rest['length'] extends 0 ? Capitalize<LowerCaseWord<RemoveNonAlphanumeric<First>>> : `${Capitalize<LowerCaseWord<RemoveNonAlphanumeric<First>>>}${JoinPrettyCamelCaseCapitalized<Rest>}` : never : never : '';
75
+ /**
76
+ * Pretty camelCase conversion that handles abbreviations more intuitively
77
+ * Always returns a string, defaulting to the input if transformation fails
78
+ *
79
+ * @example
80
+ * ```typescript
81
+ * type Example1 = PrettyCamelCase<"API-Key">; // "apiKey"
82
+ * type Example2 = PrettyCamelCase<"HTTP-Response">; // "httpResponse"
83
+ * type Example3 = PrettyCamelCase<"user-ID">; // "userId"
84
+ * type Example4 = PrettyCamelCase<"get-user-by-id">; // "getUserById"
85
+ * type Example5 = PrettyCamelCase<"/organization/base">; // "organizationBase"
86
+ * ```
87
+ */
88
+ type PrettyCamelCase<S extends string> = RemoveLeadingNumbers<JoinPrettyCamelCase<SplitBySeparators<RemoveLeadingSeparators<S>>>> extends infer Result ? Result extends string ? Result extends '' ? S : Result : S : S;
89
+ /**
90
+ * Alternative implementation using a more comprehensive approach
91
+ * for complex cases with better handling of edge cases
92
+ */
93
+ type ValidIdentifier<S extends string> = CamelCaseIdentifier<S>;
94
+
95
+ /**
96
+ * Runtime string utility functions that mirror the TypeScript utility types
97
+ */
98
+
99
+ /**
100
+ * Remove leading separators from a string
101
+ */
102
+ declare function removeLeadingSeparators(str: string): string;
103
+ /**
104
+ * Split a string by separators (space, dash, underscore, dot, slash, colon)
105
+ */
106
+ declare function splitBySeparators(str: string): string[];
107
+ /**
108
+ * Remove all non-alphanumeric characters from a string
109
+ */
110
+ declare function removeNonAlphanumeric(str: string): string;
111
+ /**
112
+ * Remove leading numbers from a string to make it a valid identifier
113
+ */
114
+ declare function removeLeadingNumbers(str: string): string;
115
+ /**
116
+ * Capitalize the first letter of a string
117
+ */
118
+ declare function capitalize(str: string): string;
119
+ /**
120
+ * Convert the first letter to lowercase
121
+ */
122
+ declare function uncapitalize(str: string): string;
123
+ /**
124
+ * Convert a string to a valid TypeScript identifier in camelCase
125
+ * Always returns a string, defaulting to the input if transformation fails
126
+ *
127
+ * @param str - The input string to transform
128
+ * @returns The camelCase identifier or the original string if transformation fails
129
+ *
130
+ * @example
131
+ * ```typescript
132
+ * toCamelCaseIdentifier("hello-world"); // "helloWorld"
133
+ * toCamelCaseIdentifier("my_var_name"); // "myVarName"
134
+ * toCamelCaseIdentifier("some.property.name"); // "somePropertyName"
135
+ * toCamelCaseIdentifier("123invalid"); // "invalid"
136
+ * toCamelCaseIdentifier("hello world"); // "helloWorld"
137
+ * toCamelCaseIdentifier("API-Key"); // "aPIKey"
138
+ * toCamelCaseIdentifier("/organization/base"); // "organizationBase"
139
+ * ```
140
+ */
141
+ declare function toCamelCaseIdentifier<T extends string>(str: T): CamelCaseIdentifier<T>;
142
+ /**
143
+ * Pretty camelCase conversion that handles abbreviations more intuitively
144
+ * Always returns a string, defaulting to the input if transformation fails
145
+ *
146
+ * @param str - The input string to transform
147
+ * @returns The pretty camelCase identifier or the original string if transformation fails
148
+ *
149
+ * @example
150
+ * ```typescript
151
+ * toPrettyCamelCase("API-Key"); // "apiKey"
152
+ * toPrettyCamelCase("HTTP-Response"); // "httpResponse"
153
+ * toPrettyCamelCase("user-ID"); // "userId"
154
+ * toPrettyCamelCase("get-user-by-id"); // "getUserById"
155
+ * toPrettyCamelCase("/organization/base"); // "organizationBase"
156
+ * ```
157
+ */
158
+ declare function toPrettyCamelCase<T extends string>(str: T): PrettyCamelCase<T>;
159
+ /**
160
+ * Alternative implementation using a more comprehensive approach
161
+ * for complex cases with better handling of edge cases
162
+ */
163
+ declare function toValidIdentifier<T extends string>(str: T): ValidIdentifier<T>;
164
+ /**
165
+ * Check if a string is a valid JavaScript/TypeScript identifier
166
+ */
167
+ declare function isValidIdentifier(str: string): boolean;
168
+ /**
169
+ * Get detailed information about the transformation process
170
+ * Useful for debugging and understanding how the transformation works
171
+ */
172
+ declare function getTransformationInfo(str: string): {
173
+ original: string;
174
+ withoutLeadingSeparators: string;
175
+ parts: string[];
176
+ camelCase: string;
177
+ prettyCamelCase: string;
178
+ withoutLeadingNumbers: string;
179
+ prettyCamelCaseWithoutLeadingNumbers: string;
180
+ isValid: boolean;
181
+ };
182
+
183
+ /**
184
+ * An empty object literal constant.
185
+ *
186
+ * This constant represents an empty object with no properties.
187
+ * It is commonly used as a default value or placeholder when
188
+ * an empty object is needed.
189
+ *
190
+ * @example
191
+ * ```typescript
192
+ * const defaultConfig = emptyObject;
193
+ * const merged = { ...emptyObject, ...userConfig };
194
+ * ```
195
+ */
196
+ declare const emptyObject: {};
197
+
1
198
  /**
2
199
  * Extracts the names of arguments from a function's string representation.
3
200
  * This is useful for reflection and debugging purposes.
@@ -48,13 +245,9 @@ declare function isRecord(obj: unknown): obj is Record<string, unknown>;
48
245
  */
49
246
  declare function isTrue(value: true): true;
50
247
 
51
- declare class InMemoryFile extends File {
248
+ declare class InMemoryBlob extends Blob {
52
249
  content: string;
53
- constructor(content: string, name: string, { type, endings, lastModified }: {
54
- type?: string;
55
- endings?: 'transparent' | 'native';
56
- lastModified?: number;
57
- });
250
+ constructor(content: string);
58
251
  }
59
252
 
60
253
  /**
@@ -72,6 +265,26 @@ declare class InMemoryFile extends File {
72
265
  */
73
266
  declare function noop(..._args: unknown[]): void;
74
267
 
268
+ /**
269
+ * Converts a path with Express-style parameters to OpenAPI-compliant format.
270
+ *
271
+ * Express-style parameters use the format `:paramName` (e.g., `/users/:id`),
272
+ * while OpenAPI uses curly braces `{paramName}` (e.g., `/users/{id}`).
273
+ *
274
+ * @param path - The path string containing Express-style parameters
275
+ * @returns The path string with OpenAPI-compliant parameter format
276
+ *
277
+ * @example
278
+ * ```typescript
279
+ * openApiCompliantPath('/users/:id/posts/:postId')
280
+ * // Returns: '/users/{id}/posts/{postId}'
281
+ *
282
+ * openApiCompliantPath('/api/v1/products/:productId/reviews/:reviewId')
283
+ * // Returns: '/api/v1/products/{productId}/reviews/{reviewId}'
284
+ * ```
285
+ */
286
+ declare function openApiCompliantPath(path: string): string;
287
+
75
288
  declare function readableStreamToAsyncIterable<T>(stream: ReadableStream<T>): AsyncIterable<T>;
76
289
 
77
290
  declare function safeParse<T>(input: unknown): T;
@@ -104,6 +317,37 @@ declare function safeParse<T>(input: unknown): T;
104
317
  */
105
318
  declare function safeStringify(arg: unknown): string;
106
319
 
320
+ /**
321
+ * Removes a trailing slash from a path string if present.
322
+ *
323
+ * @param path - The path string to process
324
+ * @returns The path string with trailing slash removed
325
+ * @example
326
+ * removeTrailingSlash('/users/'); // '/users'
327
+ * removeTrailingSlash('/users'); // '/users'
328
+ */
329
+ declare function removeTrailingSlash(path: string): string;
330
+ /**
331
+ * Removes a double leading slash from a path string if present.
332
+ *
333
+ * @param path - The path string to process
334
+ * @returns The path string with double leading slash removed
335
+ * @example
336
+ * removeDoubleLeadingSlash('//users'); // '/users'
337
+ * removeDoubleLeadingSlash('/users'); // '/users'
338
+ */
339
+ declare function removeDoubleLeadingSlash(path: string): string;
340
+ /**
341
+ * Sanitizes a path string by removing both trailing slashes and double leading slashes.
342
+ *
343
+ * @param path - The path string to process
344
+ * @returns The sanitized path string
345
+ * @example
346
+ * sanitizePathSlashes('//users/'); // '/users'
347
+ * sanitizePathSlashes('/users'); // '/users'
348
+ */
349
+ declare function sanitizePathSlashes(path: string): string;
350
+
107
351
  /**
108
352
  * Recursively sorts the keys of an object and its nested objects alphabetically.
109
353
  * This is useful for consistent object serialization and comparison.
@@ -125,6 +369,15 @@ declare function sortObjectKeys<T extends Record<string, unknown>>(obj: T): T;
125
369
  */
126
370
  declare function stripUndefinedProperties<T extends Record<string, unknown>>(obj: T): Partial<T>;
127
371
 
372
+ /**
373
+ * Converts an object to a record.
374
+ *
375
+ * @template T - The type of the object to convert.
376
+ * @param obj - The object to convert.
377
+ * @returns The record.
378
+ */
379
+ declare function toRecord<T>(obj: T): Record<string, unknown>;
380
+
128
381
  /**
129
382
  * Type representing a DTO (Data Transfer Object) with an id field.
130
383
  */
@@ -173,6 +426,19 @@ type InstanceTypeRecord<T extends Record<string, new (...args: never[]) => unkno
173
426
  [K in keyof T]: InstanceType<T[K]>;
174
427
  };
175
428
 
429
+ /**
430
+ * Type representing an empty object literal.
431
+ *
432
+ * This type is derived from the `emptyObject` constant and represents
433
+ * an object with no properties.
434
+ *
435
+ * @example
436
+ * ```typescript
437
+ * const obj: EmptyObject = {};
438
+ * ```
439
+ */
440
+ type EmptyObject = typeof emptyObject;
441
+
176
442
  type ExclusiveRecord<T, U> = T extends object ? U extends object ? T & Record<Exclude<keyof T, keyof U>, never> : T : T;
177
443
 
178
444
  /**
@@ -230,6 +496,18 @@ type MakePropertyOptionalIfChildrenOptional<T> = {
230
496
  [K in keyof T as AllPropertiesOptional<T[K]> extends true ? never : K]: T[K];
231
497
  };
232
498
 
499
+ /**
500
+ * Merges two objects by unioning the values for duplicate keys
501
+ */
502
+ type MergeUnionOnCollision<T, U> = {
503
+ [K in keyof T | keyof U]: K extends keyof T ? K extends keyof U ? T[K] | U[K] : T[K] : K extends keyof U ? U[K] : never;
504
+ };
505
+
506
+ /**
507
+ * Recursively merges an array of fetchMap objects
508
+ */
509
+ type MergeArrayOfMaps<T extends readonly Record<string, unknown>[]> = T extends readonly [infer First, ...infer Rest] ? First extends Record<string, unknown> ? Rest extends readonly Record<string, unknown>[] ? Rest extends readonly [] ? First : MergeUnionOnCollision<First, MergeArrayOfMaps<Rest>> : First : Record<string, never> : Record<string, never>;
510
+
233
511
  type MimeType = 'application/json' | 'application/xml' | 'application/x-www-form-urlencoded' | 'multipart/form-data' | 'text/html' | 'text/plain' | 'image/jpeg' | 'image/png' | 'video/mp4' | 'audio/mpeg' | 'application/pdf' | 'application/zip' | 'application/octet-stream' | string;
234
512
 
235
513
  /**
@@ -254,9 +532,35 @@ type Prettify<T> = {
254
532
  * type Route2 = RemoveTrailingSlash<'/users'>; // '/users'
255
533
  */
256
534
  type RemoveTrailingSlash<T extends string> = T extends `${infer Route}/` ? Route : T;
535
+ /**
536
+ * Type that removes a double leading slash from a string type if present.
537
+ *
538
+ * @template T - The string type to process
539
+ * @example
540
+ * type Route1 = RemoveDoubleLeadingSlash<'//users'>; // '/users'
541
+ * type Route2 = RemoveDoubleLeadingSlash<'/users'>; // '/users'
542
+ */
543
+ type RemoveDoubleLeadingSlash<T extends string> = T extends `//${infer Route}` ? `/${Route}` : T;
544
+ /**
545
+ * Type that sanitizes a path string by removing double leading slashes.
546
+ *
547
+ * @template T - The string type to process
548
+ * @example
549
+ * type Route1 = SanitizePathSlashes<'//users'>; // '/users'
550
+ * type Route2 = SanitizePathSlashes<'/users'>; // '/users'
551
+ */
552
+ type SanitizePathSlashes<T extends string> = RemoveDoubleLeadingSlash<RemoveTrailingSlash<T>>;
553
+
554
+ /**
555
+ * Utility type that excludes strings containing forward slashes
556
+ */
557
+ type StringWithoutSlash<T extends string> = T extends `${string}/${string}` ? 'cannot contain forward slashes' : T;
257
558
 
258
559
  type TypeSafeFunction = (...args: never[]) => unknown;
259
560
 
260
561
  type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
562
+ type UnionToIntersectionChildren<U> = {
563
+ [K in keyof U]: UnionToIntersection<U[K]>;
564
+ };
261
565
 
262
- export { type ExclusiveRecord, type Flatten, type FlattenKeys, type FlattenValues, type IdDto, type IdsDto, InMemoryFile, type InstanceTypeRecord, type MakePropertyOptionalIfChildrenOptional, type MimeType, type Prettify, type RecordTimingDto, type RemoveTrailingSlash, type ReturnTypeRecord, type TypeSafeFunction, type UnionToIntersection, extractArgumentNames, getEnvVar, isAsyncGenerator, isNever, isNodeJsWriteableStream, isRecord, isTrue, noop, readableStreamToAsyncIterable, safeParse, safeStringify, sortObjectKeys, stripUndefinedProperties };
566
+ export { type CamelCaseIdentifier, type EmptyObject, type ExclusiveRecord, type Flatten, type FlattenKeys, type FlattenValues, type IdDto, type IdsDto, InMemoryBlob, type InstanceTypeRecord, type MakePropertyOptionalIfChildrenOptional, type MergeArrayOfMaps, type MergeUnionOnCollision, type MimeType, type Prettify, type PrettyCamelCase, type RecordTimingDto, type RemoveDoubleLeadingSlash, type RemoveTrailingSlash, type ReturnTypeRecord, type SanitizePathSlashes, type StringWithoutSlash, type TypeSafeFunction, type UnionToIntersection, type UnionToIntersectionChildren, type ValidIdentifier, capitalize, emptyObject, extractArgumentNames, getEnvVar, getTransformationInfo, isAsyncGenerator, isNever, isNodeJsWriteableStream, isRecord, isTrue, isValidIdentifier, noop, openApiCompliantPath, readableStreamToAsyncIterable, removeDoubleLeadingSlash, removeLeadingNumbers, removeLeadingSeparators, removeNonAlphanumeric, removeTrailingSlash, safeParse, safeStringify, sanitizePathSlashes, sortObjectKeys, splitBySeparators, stripUndefinedProperties, toCamelCaseIdentifier, toPrettyCamelCase, toRecord, toValidIdentifier, uncapitalize };
package/lib/index.d.ts CHANGED
@@ -1,3 +1,200 @@
1
+ /**
2
+ * Utility types for string manipulation and validation
3
+ */
4
+ /**
5
+ * Remove leading separators from a string
6
+ */
7
+ type RemoveLeadingSeparators<S extends string> = S extends `${' ' | '-' | '_' | '.' | '/' | ':'}${infer Rest}` ? RemoveLeadingSeparators<Rest> : S;
8
+ /**
9
+ * Split a string by separators (space, dash, underscore, dot, slash, colon)
10
+ */
11
+ type SplitBySeparators<S extends string> = S extends `${infer First}${' ' | '-' | '_' | '.' | '/' | ':'}${infer Rest}` ? [First, ...SplitBySeparators<Rest>] : S extends '' ? [] : [S];
12
+ /**
13
+ * Capitalize the first letter of a string
14
+ */
15
+ type Capitalize<S extends string> = S extends `${infer First}${infer Rest}` ? `${Uppercase<First>}${Rest}` : S;
16
+ /**
17
+ * Convert the first letter to lowercase
18
+ */
19
+ type Uncapitalize<S extends string> = S extends `${infer First}${infer Rest}` ? `${Lowercase<First>}${Rest}` : S;
20
+ /**
21
+ * Remove all non-alphanumeric characters from a string
22
+ */
23
+ type RemoveNonAlphanumeric<S extends string> = S extends `${infer First}${infer Rest}` ? First extends 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z' | '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' ? `${First}${RemoveNonAlphanumeric<Rest>}` : RemoveNonAlphanumeric<Rest> : '';
24
+ /**
25
+ * Remove leading numbers from a string to make it a valid identifier
26
+ */
27
+ type RemoveLeadingNumbers<S extends string> = S extends `${infer First}${infer Rest}` ? First extends '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' ? RemoveLeadingNumbers<Rest> : S : S;
28
+ /**
29
+ * Join an array of strings into camelCase
30
+ */
31
+ type JoinCamelCase<T extends readonly string[]> = T extends readonly [
32
+ infer First,
33
+ ...infer Rest
34
+ ] ? First extends string ? Rest extends readonly string[] ? Rest['length'] extends 0 ? Uncapitalize<RemoveNonAlphanumeric<First>> : `${Uncapitalize<RemoveNonAlphanumeric<First>>}${JoinCamelCaseCapitalized<Rest>}` : never : never : '';
35
+ /**
36
+ * Join remaining parts with capitalized first letters
37
+ */
38
+ type JoinCamelCaseCapitalized<T extends readonly string[]> = T extends readonly [infer First, ...infer Rest] ? First extends string ? Rest extends readonly string[] ? Rest['length'] extends 0 ? Capitalize<RemoveNonAlphanumeric<First>> : `${Capitalize<RemoveNonAlphanumeric<First>>}${JoinCamelCaseCapitalized<Rest>}` : never : never : '';
39
+ /**
40
+ * Convert a string to a valid TypeScript identifier in camelCase
41
+ * Always returns a string, defaulting to the input if transformation fails
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * type Example1 = CamelCaseIdentifier<"hello-world">; // "helloWorld"
46
+ * type Example2 = CamelCaseIdentifier<"my_var_name">; // "myVarName"
47
+ * type Example3 = CamelCaseIdentifier<"some.property.name">; // "somePropertyName"
48
+ * type Example4 = CamelCaseIdentifier<"123invalid">; // "invalid"
49
+ * type Example5 = CamelCaseIdentifier<"hello world">; // "helloWorld"
50
+ * type Example6 = CamelCaseIdentifier<"API-Key">; // "aPIKey"
51
+ * type Example7 = CamelCaseIdentifier<"/organization/base">; // "organizationBase"
52
+ * ```
53
+ */
54
+ type CamelCaseIdentifier<S extends string> = RemoveLeadingNumbers<JoinCamelCase<SplitBySeparators<RemoveLeadingSeparators<S>>>> extends infer Result ? Result extends string ? Result extends '' ? S : Result : S : S;
55
+ /**
56
+ * Improved version that handles consecutive uppercase letters better
57
+ * by converting them to proper camelCase
58
+ */
59
+ type LowerCaseWord<S extends string> = Lowercase<S>;
60
+ /**
61
+ * More intuitive camelCase conversion that properly handles abbreviations
62
+ *
63
+ * @example
64
+ * ```typescript
65
+ * type Example1 = PrettyCamelCase<"API-Key">; // "apiKey"
66
+ * type Example2 = PrettyCamelCase<"HTTP-Response">; // "httpResponse"
67
+ * type Example3 = PrettyCamelCase<"user-ID">; // "userId"
68
+ * ```
69
+ */
70
+ type JoinPrettyCamelCase<T extends readonly string[]> = T extends readonly [
71
+ infer First,
72
+ ...infer Rest
73
+ ] ? First extends string ? Rest extends readonly string[] ? Rest['length'] extends 0 ? LowerCaseWord<RemoveNonAlphanumeric<First>> : `${LowerCaseWord<RemoveNonAlphanumeric<First>>}${JoinPrettyCamelCaseCapitalized<Rest>}` : never : never : '';
74
+ type JoinPrettyCamelCaseCapitalized<T extends readonly string[]> = T extends readonly [infer First, ...infer Rest] ? First extends string ? Rest extends readonly string[] ? Rest['length'] extends 0 ? Capitalize<LowerCaseWord<RemoveNonAlphanumeric<First>>> : `${Capitalize<LowerCaseWord<RemoveNonAlphanumeric<First>>>}${JoinPrettyCamelCaseCapitalized<Rest>}` : never : never : '';
75
+ /**
76
+ * Pretty camelCase conversion that handles abbreviations more intuitively
77
+ * Always returns a string, defaulting to the input if transformation fails
78
+ *
79
+ * @example
80
+ * ```typescript
81
+ * type Example1 = PrettyCamelCase<"API-Key">; // "apiKey"
82
+ * type Example2 = PrettyCamelCase<"HTTP-Response">; // "httpResponse"
83
+ * type Example3 = PrettyCamelCase<"user-ID">; // "userId"
84
+ * type Example4 = PrettyCamelCase<"get-user-by-id">; // "getUserById"
85
+ * type Example5 = PrettyCamelCase<"/organization/base">; // "organizationBase"
86
+ * ```
87
+ */
88
+ type PrettyCamelCase<S extends string> = RemoveLeadingNumbers<JoinPrettyCamelCase<SplitBySeparators<RemoveLeadingSeparators<S>>>> extends infer Result ? Result extends string ? Result extends '' ? S : Result : S : S;
89
+ /**
90
+ * Alternative implementation using a more comprehensive approach
91
+ * for complex cases with better handling of edge cases
92
+ */
93
+ type ValidIdentifier<S extends string> = CamelCaseIdentifier<S>;
94
+
95
+ /**
96
+ * Runtime string utility functions that mirror the TypeScript utility types
97
+ */
98
+
99
+ /**
100
+ * Remove leading separators from a string
101
+ */
102
+ declare function removeLeadingSeparators(str: string): string;
103
+ /**
104
+ * Split a string by separators (space, dash, underscore, dot, slash, colon)
105
+ */
106
+ declare function splitBySeparators(str: string): string[];
107
+ /**
108
+ * Remove all non-alphanumeric characters from a string
109
+ */
110
+ declare function removeNonAlphanumeric(str: string): string;
111
+ /**
112
+ * Remove leading numbers from a string to make it a valid identifier
113
+ */
114
+ declare function removeLeadingNumbers(str: string): string;
115
+ /**
116
+ * Capitalize the first letter of a string
117
+ */
118
+ declare function capitalize(str: string): string;
119
+ /**
120
+ * Convert the first letter to lowercase
121
+ */
122
+ declare function uncapitalize(str: string): string;
123
+ /**
124
+ * Convert a string to a valid TypeScript identifier in camelCase
125
+ * Always returns a string, defaulting to the input if transformation fails
126
+ *
127
+ * @param str - The input string to transform
128
+ * @returns The camelCase identifier or the original string if transformation fails
129
+ *
130
+ * @example
131
+ * ```typescript
132
+ * toCamelCaseIdentifier("hello-world"); // "helloWorld"
133
+ * toCamelCaseIdentifier("my_var_name"); // "myVarName"
134
+ * toCamelCaseIdentifier("some.property.name"); // "somePropertyName"
135
+ * toCamelCaseIdentifier("123invalid"); // "invalid"
136
+ * toCamelCaseIdentifier("hello world"); // "helloWorld"
137
+ * toCamelCaseIdentifier("API-Key"); // "aPIKey"
138
+ * toCamelCaseIdentifier("/organization/base"); // "organizationBase"
139
+ * ```
140
+ */
141
+ declare function toCamelCaseIdentifier<T extends string>(str: T): CamelCaseIdentifier<T>;
142
+ /**
143
+ * Pretty camelCase conversion that handles abbreviations more intuitively
144
+ * Always returns a string, defaulting to the input if transformation fails
145
+ *
146
+ * @param str - The input string to transform
147
+ * @returns The pretty camelCase identifier or the original string if transformation fails
148
+ *
149
+ * @example
150
+ * ```typescript
151
+ * toPrettyCamelCase("API-Key"); // "apiKey"
152
+ * toPrettyCamelCase("HTTP-Response"); // "httpResponse"
153
+ * toPrettyCamelCase("user-ID"); // "userId"
154
+ * toPrettyCamelCase("get-user-by-id"); // "getUserById"
155
+ * toPrettyCamelCase("/organization/base"); // "organizationBase"
156
+ * ```
157
+ */
158
+ declare function toPrettyCamelCase<T extends string>(str: T): PrettyCamelCase<T>;
159
+ /**
160
+ * Alternative implementation using a more comprehensive approach
161
+ * for complex cases with better handling of edge cases
162
+ */
163
+ declare function toValidIdentifier<T extends string>(str: T): ValidIdentifier<T>;
164
+ /**
165
+ * Check if a string is a valid JavaScript/TypeScript identifier
166
+ */
167
+ declare function isValidIdentifier(str: string): boolean;
168
+ /**
169
+ * Get detailed information about the transformation process
170
+ * Useful for debugging and understanding how the transformation works
171
+ */
172
+ declare function getTransformationInfo(str: string): {
173
+ original: string;
174
+ withoutLeadingSeparators: string;
175
+ parts: string[];
176
+ camelCase: string;
177
+ prettyCamelCase: string;
178
+ withoutLeadingNumbers: string;
179
+ prettyCamelCaseWithoutLeadingNumbers: string;
180
+ isValid: boolean;
181
+ };
182
+
183
+ /**
184
+ * An empty object literal constant.
185
+ *
186
+ * This constant represents an empty object with no properties.
187
+ * It is commonly used as a default value or placeholder when
188
+ * an empty object is needed.
189
+ *
190
+ * @example
191
+ * ```typescript
192
+ * const defaultConfig = emptyObject;
193
+ * const merged = { ...emptyObject, ...userConfig };
194
+ * ```
195
+ */
196
+ declare const emptyObject: {};
197
+
1
198
  /**
2
199
  * Extracts the names of arguments from a function's string representation.
3
200
  * This is useful for reflection and debugging purposes.
@@ -48,13 +245,9 @@ declare function isRecord(obj: unknown): obj is Record<string, unknown>;
48
245
  */
49
246
  declare function isTrue(value: true): true;
50
247
 
51
- declare class InMemoryFile extends File {
248
+ declare class InMemoryBlob extends Blob {
52
249
  content: string;
53
- constructor(content: string, name: string, { type, endings, lastModified }: {
54
- type?: string;
55
- endings?: 'transparent' | 'native';
56
- lastModified?: number;
57
- });
250
+ constructor(content: string);
58
251
  }
59
252
 
60
253
  /**
@@ -72,6 +265,26 @@ declare class InMemoryFile extends File {
72
265
  */
73
266
  declare function noop(..._args: unknown[]): void;
74
267
 
268
+ /**
269
+ * Converts a path with Express-style parameters to OpenAPI-compliant format.
270
+ *
271
+ * Express-style parameters use the format `:paramName` (e.g., `/users/:id`),
272
+ * while OpenAPI uses curly braces `{paramName}` (e.g., `/users/{id}`).
273
+ *
274
+ * @param path - The path string containing Express-style parameters
275
+ * @returns The path string with OpenAPI-compliant parameter format
276
+ *
277
+ * @example
278
+ * ```typescript
279
+ * openApiCompliantPath('/users/:id/posts/:postId')
280
+ * // Returns: '/users/{id}/posts/{postId}'
281
+ *
282
+ * openApiCompliantPath('/api/v1/products/:productId/reviews/:reviewId')
283
+ * // Returns: '/api/v1/products/{productId}/reviews/{reviewId}'
284
+ * ```
285
+ */
286
+ declare function openApiCompliantPath(path: string): string;
287
+
75
288
  declare function readableStreamToAsyncIterable<T>(stream: ReadableStream<T>): AsyncIterable<T>;
76
289
 
77
290
  declare function safeParse<T>(input: unknown): T;
@@ -104,6 +317,37 @@ declare function safeParse<T>(input: unknown): T;
104
317
  */
105
318
  declare function safeStringify(arg: unknown): string;
106
319
 
320
+ /**
321
+ * Removes a trailing slash from a path string if present.
322
+ *
323
+ * @param path - The path string to process
324
+ * @returns The path string with trailing slash removed
325
+ * @example
326
+ * removeTrailingSlash('/users/'); // '/users'
327
+ * removeTrailingSlash('/users'); // '/users'
328
+ */
329
+ declare function removeTrailingSlash(path: string): string;
330
+ /**
331
+ * Removes a double leading slash from a path string if present.
332
+ *
333
+ * @param path - The path string to process
334
+ * @returns The path string with double leading slash removed
335
+ * @example
336
+ * removeDoubleLeadingSlash('//users'); // '/users'
337
+ * removeDoubleLeadingSlash('/users'); // '/users'
338
+ */
339
+ declare function removeDoubleLeadingSlash(path: string): string;
340
+ /**
341
+ * Sanitizes a path string by removing both trailing slashes and double leading slashes.
342
+ *
343
+ * @param path - The path string to process
344
+ * @returns The sanitized path string
345
+ * @example
346
+ * sanitizePathSlashes('//users/'); // '/users'
347
+ * sanitizePathSlashes('/users'); // '/users'
348
+ */
349
+ declare function sanitizePathSlashes(path: string): string;
350
+
107
351
  /**
108
352
  * Recursively sorts the keys of an object and its nested objects alphabetically.
109
353
  * This is useful for consistent object serialization and comparison.
@@ -125,6 +369,15 @@ declare function sortObjectKeys<T extends Record<string, unknown>>(obj: T): T;
125
369
  */
126
370
  declare function stripUndefinedProperties<T extends Record<string, unknown>>(obj: T): Partial<T>;
127
371
 
372
+ /**
373
+ * Converts an object to a record.
374
+ *
375
+ * @template T - The type of the object to convert.
376
+ * @param obj - The object to convert.
377
+ * @returns The record.
378
+ */
379
+ declare function toRecord<T>(obj: T): Record<string, unknown>;
380
+
128
381
  /**
129
382
  * Type representing a DTO (Data Transfer Object) with an id field.
130
383
  */
@@ -173,6 +426,19 @@ type InstanceTypeRecord<T extends Record<string, new (...args: never[]) => unkno
173
426
  [K in keyof T]: InstanceType<T[K]>;
174
427
  };
175
428
 
429
+ /**
430
+ * Type representing an empty object literal.
431
+ *
432
+ * This type is derived from the `emptyObject` constant and represents
433
+ * an object with no properties.
434
+ *
435
+ * @example
436
+ * ```typescript
437
+ * const obj: EmptyObject = {};
438
+ * ```
439
+ */
440
+ type EmptyObject = typeof emptyObject;
441
+
176
442
  type ExclusiveRecord<T, U> = T extends object ? U extends object ? T & Record<Exclude<keyof T, keyof U>, never> : T : T;
177
443
 
178
444
  /**
@@ -230,6 +496,18 @@ type MakePropertyOptionalIfChildrenOptional<T> = {
230
496
  [K in keyof T as AllPropertiesOptional<T[K]> extends true ? never : K]: T[K];
231
497
  };
232
498
 
499
+ /**
500
+ * Merges two objects by unioning the values for duplicate keys
501
+ */
502
+ type MergeUnionOnCollision<T, U> = {
503
+ [K in keyof T | keyof U]: K extends keyof T ? K extends keyof U ? T[K] | U[K] : T[K] : K extends keyof U ? U[K] : never;
504
+ };
505
+
506
+ /**
507
+ * Recursively merges an array of fetchMap objects
508
+ */
509
+ type MergeArrayOfMaps<T extends readonly Record<string, unknown>[]> = T extends readonly [infer First, ...infer Rest] ? First extends Record<string, unknown> ? Rest extends readonly Record<string, unknown>[] ? Rest extends readonly [] ? First : MergeUnionOnCollision<First, MergeArrayOfMaps<Rest>> : First : Record<string, never> : Record<string, never>;
510
+
233
511
  type MimeType = 'application/json' | 'application/xml' | 'application/x-www-form-urlencoded' | 'multipart/form-data' | 'text/html' | 'text/plain' | 'image/jpeg' | 'image/png' | 'video/mp4' | 'audio/mpeg' | 'application/pdf' | 'application/zip' | 'application/octet-stream' | string;
234
512
 
235
513
  /**
@@ -254,9 +532,35 @@ type Prettify<T> = {
254
532
  * type Route2 = RemoveTrailingSlash<'/users'>; // '/users'
255
533
  */
256
534
  type RemoveTrailingSlash<T extends string> = T extends `${infer Route}/` ? Route : T;
535
+ /**
536
+ * Type that removes a double leading slash from a string type if present.
537
+ *
538
+ * @template T - The string type to process
539
+ * @example
540
+ * type Route1 = RemoveDoubleLeadingSlash<'//users'>; // '/users'
541
+ * type Route2 = RemoveDoubleLeadingSlash<'/users'>; // '/users'
542
+ */
543
+ type RemoveDoubleLeadingSlash<T extends string> = T extends `//${infer Route}` ? `/${Route}` : T;
544
+ /**
545
+ * Type that sanitizes a path string by removing double leading slashes.
546
+ *
547
+ * @template T - The string type to process
548
+ * @example
549
+ * type Route1 = SanitizePathSlashes<'//users'>; // '/users'
550
+ * type Route2 = SanitizePathSlashes<'/users'>; // '/users'
551
+ */
552
+ type SanitizePathSlashes<T extends string> = RemoveDoubleLeadingSlash<RemoveTrailingSlash<T>>;
553
+
554
+ /**
555
+ * Utility type that excludes strings containing forward slashes
556
+ */
557
+ type StringWithoutSlash<T extends string> = T extends `${string}/${string}` ? 'cannot contain forward slashes' : T;
257
558
 
258
559
  type TypeSafeFunction = (...args: never[]) => unknown;
259
560
 
260
561
  type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
562
+ type UnionToIntersectionChildren<U> = {
563
+ [K in keyof U]: UnionToIntersection<U[K]>;
564
+ };
261
565
 
262
- export { type ExclusiveRecord, type Flatten, type FlattenKeys, type FlattenValues, type IdDto, type IdsDto, InMemoryFile, type InstanceTypeRecord, type MakePropertyOptionalIfChildrenOptional, type MimeType, type Prettify, type RecordTimingDto, type RemoveTrailingSlash, type ReturnTypeRecord, type TypeSafeFunction, type UnionToIntersection, extractArgumentNames, getEnvVar, isAsyncGenerator, isNever, isNodeJsWriteableStream, isRecord, isTrue, noop, readableStreamToAsyncIterable, safeParse, safeStringify, sortObjectKeys, stripUndefinedProperties };
566
+ export { type CamelCaseIdentifier, type EmptyObject, type ExclusiveRecord, type Flatten, type FlattenKeys, type FlattenValues, type IdDto, type IdsDto, InMemoryBlob, type InstanceTypeRecord, type MakePropertyOptionalIfChildrenOptional, type MergeArrayOfMaps, type MergeUnionOnCollision, type MimeType, type Prettify, type PrettyCamelCase, type RecordTimingDto, type RemoveDoubleLeadingSlash, type RemoveTrailingSlash, type ReturnTypeRecord, type SanitizePathSlashes, type StringWithoutSlash, type TypeSafeFunction, type UnionToIntersection, type UnionToIntersectionChildren, type ValidIdentifier, capitalize, emptyObject, extractArgumentNames, getEnvVar, getTransformationInfo, isAsyncGenerator, isNever, isNodeJsWriteableStream, isRecord, isTrue, isValidIdentifier, noop, openApiCompliantPath, readableStreamToAsyncIterable, removeDoubleLeadingSlash, removeLeadingNumbers, removeLeadingSeparators, removeNonAlphanumeric, removeTrailingSlash, safeParse, safeStringify, sanitizePathSlashes, sortObjectKeys, splitBySeparators, stripUndefinedProperties, toCamelCaseIdentifier, toPrettyCamelCase, toRecord, toValidIdentifier, uncapitalize };
package/lib/index.js CHANGED
@@ -20,23 +20,133 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
- InMemoryFile: () => InMemoryFile,
23
+ InMemoryBlob: () => InMemoryBlob,
24
+ capitalize: () => capitalize,
25
+ emptyObject: () => emptyObject,
24
26
  extractArgumentNames: () => extractArgumentNames,
25
27
  getEnvVar: () => getEnvVar,
28
+ getTransformationInfo: () => getTransformationInfo,
26
29
  isAsyncGenerator: () => isAsyncGenerator,
27
30
  isNever: () => isNever,
28
31
  isNodeJsWriteableStream: () => isNodeJsWriteableStream,
29
32
  isRecord: () => isRecord,
30
33
  isTrue: () => isTrue,
34
+ isValidIdentifier: () => isValidIdentifier,
31
35
  noop: () => noop,
36
+ openApiCompliantPath: () => openApiCompliantPath,
32
37
  readableStreamToAsyncIterable: () => readableStreamToAsyncIterable,
38
+ removeDoubleLeadingSlash: () => removeDoubleLeadingSlash,
39
+ removeLeadingNumbers: () => removeLeadingNumbers,
40
+ removeLeadingSeparators: () => removeLeadingSeparators,
41
+ removeNonAlphanumeric: () => removeNonAlphanumeric,
42
+ removeTrailingSlash: () => removeTrailingSlash,
33
43
  safeParse: () => safeParse,
34
44
  safeStringify: () => safeStringify,
45
+ sanitizePathSlashes: () => sanitizePathSlashes,
35
46
  sortObjectKeys: () => sortObjectKeys,
36
- stripUndefinedProperties: () => stripUndefinedProperties
47
+ splitBySeparators: () => splitBySeparators,
48
+ stripUndefinedProperties: () => stripUndefinedProperties,
49
+ toCamelCaseIdentifier: () => toCamelCaseIdentifier,
50
+ toPrettyCamelCase: () => toPrettyCamelCase,
51
+ toRecord: () => toRecord,
52
+ toValidIdentifier: () => toValidIdentifier,
53
+ uncapitalize: () => uncapitalize
37
54
  });
38
55
  module.exports = __toCommonJS(index_exports);
39
56
 
57
+ // src/camelCase.ts
58
+ var SEPARATOR_REGEX = /[ \-_./:]+/g;
59
+ var LEADING_SEPARATOR_REGEX = /^[ \-_./:]+/;
60
+ var NON_ALPHANUMERIC_REGEX = /[^a-zA-Z0-9]/g;
61
+ var LEADING_NUMBERS_REGEX = /^[0-9]+/;
62
+ function removeLeadingSeparators(str) {
63
+ return str.replace(LEADING_SEPARATOR_REGEX, "");
64
+ }
65
+ function splitBySeparators(str) {
66
+ return str.split(SEPARATOR_REGEX).filter((part) => part.length > 0);
67
+ }
68
+ function removeNonAlphanumeric(str) {
69
+ return str.replace(NON_ALPHANUMERIC_REGEX, "");
70
+ }
71
+ function removeLeadingNumbers(str) {
72
+ return str.replace(LEADING_NUMBERS_REGEX, "");
73
+ }
74
+ function capitalize(str) {
75
+ if (!str) return str;
76
+ return str.charAt(0).toUpperCase() + str.slice(1);
77
+ }
78
+ function uncapitalize(str) {
79
+ if (!str) return str;
80
+ return str.charAt(0).toLowerCase() + str.slice(1);
81
+ }
82
+ function joinCamelCase(parts) {
83
+ if (parts.length === 0) return "";
84
+ const cleanedParts = parts.map(removeNonAlphanumeric).filter((part) => part.length > 0);
85
+ if (cleanedParts.length === 0) return "";
86
+ const [first, ...rest] = cleanedParts;
87
+ return uncapitalize(first) + rest.map(capitalize).join("");
88
+ }
89
+ function joinPrettyCamelCase(parts) {
90
+ if (parts.length === 0) return "";
91
+ const cleanedParts = parts.map((part) => removeNonAlphanumeric(part.toLowerCase())).filter((part) => part.length > 0);
92
+ if (cleanedParts.length === 0) return "";
93
+ const [first, ...rest] = cleanedParts;
94
+ return first + rest.map(capitalize).join("");
95
+ }
96
+ function toCamelCaseIdentifier(str) {
97
+ if (typeof str !== "string") return String(str);
98
+ try {
99
+ const withoutLeadingSeparators = removeLeadingSeparators(str);
100
+ const parts = splitBySeparators(withoutLeadingSeparators);
101
+ const camelCased = joinCamelCase(parts);
102
+ const withoutLeadingNumbers = removeLeadingNumbers(camelCased);
103
+ return withoutLeadingNumbers || str;
104
+ } catch {
105
+ return str;
106
+ }
107
+ }
108
+ function toPrettyCamelCase(str) {
109
+ if (typeof str !== "string") return String(str);
110
+ try {
111
+ const withoutLeadingSeparators = removeLeadingSeparators(str);
112
+ const parts = splitBySeparators(withoutLeadingSeparators);
113
+ const camelCased = joinPrettyCamelCase(parts);
114
+ const withoutLeadingNumbers = removeLeadingNumbers(camelCased);
115
+ return withoutLeadingNumbers || str;
116
+ } catch {
117
+ return str;
118
+ }
119
+ }
120
+ function toValidIdentifier(str) {
121
+ return toCamelCaseIdentifier(str);
122
+ }
123
+ function isValidIdentifier(str) {
124
+ if (typeof str !== "string" || str.length === 0) return false;
125
+ if (!/^[a-zA-Z_$]/.test(str)) return false;
126
+ return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(str);
127
+ }
128
+ function getTransformationInfo(str) {
129
+ const withoutLeadingSeparators = removeLeadingSeparators(str);
130
+ const parts = splitBySeparators(withoutLeadingSeparators);
131
+ const camelCase = joinCamelCase(parts);
132
+ const prettyCamelCase = joinPrettyCamelCase(parts);
133
+ const withoutLeadingNumbers = removeLeadingNumbers(camelCase);
134
+ const prettyCamelCaseWithoutLeadingNumbers = removeLeadingNumbers(prettyCamelCase);
135
+ return {
136
+ original: str,
137
+ withoutLeadingSeparators,
138
+ parts,
139
+ camelCase,
140
+ prettyCamelCase,
141
+ withoutLeadingNumbers,
142
+ prettyCamelCaseWithoutLeadingNumbers,
143
+ isValid: isValidIdentifier(withoutLeadingNumbers)
144
+ };
145
+ }
146
+
147
+ // src/emptyObject.ts
148
+ var emptyObject = {};
149
+
40
150
  // src/extractArgumentNames.ts
41
151
  function extractArgumentNames(func) {
42
152
  const fnStr = func.toString();
@@ -96,18 +206,10 @@ function isTrue(value) {
96
206
  return value;
97
207
  }
98
208
 
99
- // src/InMemoryFile.ts
100
- var InMemoryFile = class extends File {
101
- constructor(content, name, {
102
- type,
103
- endings,
104
- lastModified
105
- }) {
106
- super([Buffer.from(content)], name, {
107
- type,
108
- endings,
109
- lastModified
110
- });
209
+ // src/InMemoryBlob.ts
210
+ var InMemoryBlob = class extends Blob {
211
+ constructor(content) {
212
+ super([Buffer.from(content)]);
111
213
  this.content = content;
112
214
  }
113
215
  };
@@ -116,6 +218,11 @@ var InMemoryFile = class extends File {
116
218
  function noop(..._args) {
117
219
  }
118
220
 
221
+ // src/openApiCompliantPath.ts
222
+ function openApiCompliantPath(path) {
223
+ return path.replaceAll(/:(\w+)/g, "{$1}");
224
+ }
225
+
119
226
  // src/readableStreamToAsyncIterable.ts
120
227
  async function* readableStreamToAsyncIterable(stream) {
121
228
  const reader = stream.getReader();
@@ -212,6 +319,17 @@ function safeStringify(arg) {
212
319
  }
213
320
  }
214
321
 
322
+ // src/slashManipulation.ts
323
+ function removeTrailingSlash(path) {
324
+ return path.replace(/\/$/, "");
325
+ }
326
+ function removeDoubleLeadingSlash(path) {
327
+ return path.replace(/^\/\//, "/");
328
+ }
329
+ function sanitizePathSlashes(path) {
330
+ return removeDoubleLeadingSlash(removeTrailingSlash(path));
331
+ }
332
+
215
333
  // src/sortObjectKeys.ts
216
334
  function sortObjectKeys(obj) {
217
335
  if (typeof obj !== "object" || obj === null) {
@@ -233,20 +351,42 @@ function stripUndefinedProperties(obj) {
233
351
  Object.entries(obj).filter(([, value]) => value !== void 0)
234
352
  );
235
353
  }
354
+
355
+ // src/toRecord.ts
356
+ function toRecord(obj) {
357
+ return obj;
358
+ }
236
359
  // Annotate the CommonJS export names for ESM import in node:
237
360
  0 && (module.exports = {
238
- InMemoryFile,
361
+ InMemoryBlob,
362
+ capitalize,
363
+ emptyObject,
239
364
  extractArgumentNames,
240
365
  getEnvVar,
366
+ getTransformationInfo,
241
367
  isAsyncGenerator,
242
368
  isNever,
243
369
  isNodeJsWriteableStream,
244
370
  isRecord,
245
371
  isTrue,
372
+ isValidIdentifier,
246
373
  noop,
374
+ openApiCompliantPath,
247
375
  readableStreamToAsyncIterable,
376
+ removeDoubleLeadingSlash,
377
+ removeLeadingNumbers,
378
+ removeLeadingSeparators,
379
+ removeNonAlphanumeric,
380
+ removeTrailingSlash,
248
381
  safeParse,
249
382
  safeStringify,
383
+ sanitizePathSlashes,
250
384
  sortObjectKeys,
251
- stripUndefinedProperties
385
+ splitBySeparators,
386
+ stripUndefinedProperties,
387
+ toCamelCaseIdentifier,
388
+ toPrettyCamelCase,
389
+ toRecord,
390
+ toValidIdentifier,
391
+ uncapitalize
252
392
  });
package/lib/index.mjs CHANGED
@@ -1,3 +1,96 @@
1
+ // src/camelCase.ts
2
+ var SEPARATOR_REGEX = /[ \-_./:]+/g;
3
+ var LEADING_SEPARATOR_REGEX = /^[ \-_./:]+/;
4
+ var NON_ALPHANUMERIC_REGEX = /[^a-zA-Z0-9]/g;
5
+ var LEADING_NUMBERS_REGEX = /^[0-9]+/;
6
+ function removeLeadingSeparators(str) {
7
+ return str.replace(LEADING_SEPARATOR_REGEX, "");
8
+ }
9
+ function splitBySeparators(str) {
10
+ return str.split(SEPARATOR_REGEX).filter((part) => part.length > 0);
11
+ }
12
+ function removeNonAlphanumeric(str) {
13
+ return str.replace(NON_ALPHANUMERIC_REGEX, "");
14
+ }
15
+ function removeLeadingNumbers(str) {
16
+ return str.replace(LEADING_NUMBERS_REGEX, "");
17
+ }
18
+ function capitalize(str) {
19
+ if (!str) return str;
20
+ return str.charAt(0).toUpperCase() + str.slice(1);
21
+ }
22
+ function uncapitalize(str) {
23
+ if (!str) return str;
24
+ return str.charAt(0).toLowerCase() + str.slice(1);
25
+ }
26
+ function joinCamelCase(parts) {
27
+ if (parts.length === 0) return "";
28
+ const cleanedParts = parts.map(removeNonAlphanumeric).filter((part) => part.length > 0);
29
+ if (cleanedParts.length === 0) return "";
30
+ const [first, ...rest] = cleanedParts;
31
+ return uncapitalize(first) + rest.map(capitalize).join("");
32
+ }
33
+ function joinPrettyCamelCase(parts) {
34
+ if (parts.length === 0) return "";
35
+ const cleanedParts = parts.map((part) => removeNonAlphanumeric(part.toLowerCase())).filter((part) => part.length > 0);
36
+ if (cleanedParts.length === 0) return "";
37
+ const [first, ...rest] = cleanedParts;
38
+ return first + rest.map(capitalize).join("");
39
+ }
40
+ function toCamelCaseIdentifier(str) {
41
+ if (typeof str !== "string") return String(str);
42
+ try {
43
+ const withoutLeadingSeparators = removeLeadingSeparators(str);
44
+ const parts = splitBySeparators(withoutLeadingSeparators);
45
+ const camelCased = joinCamelCase(parts);
46
+ const withoutLeadingNumbers = removeLeadingNumbers(camelCased);
47
+ return withoutLeadingNumbers || str;
48
+ } catch {
49
+ return str;
50
+ }
51
+ }
52
+ function toPrettyCamelCase(str) {
53
+ if (typeof str !== "string") return String(str);
54
+ try {
55
+ const withoutLeadingSeparators = removeLeadingSeparators(str);
56
+ const parts = splitBySeparators(withoutLeadingSeparators);
57
+ const camelCased = joinPrettyCamelCase(parts);
58
+ const withoutLeadingNumbers = removeLeadingNumbers(camelCased);
59
+ return withoutLeadingNumbers || str;
60
+ } catch {
61
+ return str;
62
+ }
63
+ }
64
+ function toValidIdentifier(str) {
65
+ return toCamelCaseIdentifier(str);
66
+ }
67
+ function isValidIdentifier(str) {
68
+ if (typeof str !== "string" || str.length === 0) return false;
69
+ if (!/^[a-zA-Z_$]/.test(str)) return false;
70
+ return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(str);
71
+ }
72
+ function getTransformationInfo(str) {
73
+ const withoutLeadingSeparators = removeLeadingSeparators(str);
74
+ const parts = splitBySeparators(withoutLeadingSeparators);
75
+ const camelCase = joinCamelCase(parts);
76
+ const prettyCamelCase = joinPrettyCamelCase(parts);
77
+ const withoutLeadingNumbers = removeLeadingNumbers(camelCase);
78
+ const prettyCamelCaseWithoutLeadingNumbers = removeLeadingNumbers(prettyCamelCase);
79
+ return {
80
+ original: str,
81
+ withoutLeadingSeparators,
82
+ parts,
83
+ camelCase,
84
+ prettyCamelCase,
85
+ withoutLeadingNumbers,
86
+ prettyCamelCaseWithoutLeadingNumbers,
87
+ isValid: isValidIdentifier(withoutLeadingNumbers)
88
+ };
89
+ }
90
+
91
+ // src/emptyObject.ts
92
+ var emptyObject = {};
93
+
1
94
  // src/extractArgumentNames.ts
2
95
  function extractArgumentNames(func) {
3
96
  const fnStr = func.toString();
@@ -57,18 +150,10 @@ function isTrue(value) {
57
150
  return value;
58
151
  }
59
152
 
60
- // src/InMemoryFile.ts
61
- var InMemoryFile = class extends File {
62
- constructor(content, name, {
63
- type,
64
- endings,
65
- lastModified
66
- }) {
67
- super([Buffer.from(content)], name, {
68
- type,
69
- endings,
70
- lastModified
71
- });
153
+ // src/InMemoryBlob.ts
154
+ var InMemoryBlob = class extends Blob {
155
+ constructor(content) {
156
+ super([Buffer.from(content)]);
72
157
  this.content = content;
73
158
  }
74
159
  };
@@ -77,6 +162,11 @@ var InMemoryFile = class extends File {
77
162
  function noop(..._args) {
78
163
  }
79
164
 
165
+ // src/openApiCompliantPath.ts
166
+ function openApiCompliantPath(path) {
167
+ return path.replaceAll(/:(\w+)/g, "{$1}");
168
+ }
169
+
80
170
  // src/readableStreamToAsyncIterable.ts
81
171
  async function* readableStreamToAsyncIterable(stream) {
82
172
  const reader = stream.getReader();
@@ -173,6 +263,17 @@ function safeStringify(arg) {
173
263
  }
174
264
  }
175
265
 
266
+ // src/slashManipulation.ts
267
+ function removeTrailingSlash(path) {
268
+ return path.replace(/\/$/, "");
269
+ }
270
+ function removeDoubleLeadingSlash(path) {
271
+ return path.replace(/^\/\//, "/");
272
+ }
273
+ function sanitizePathSlashes(path) {
274
+ return removeDoubleLeadingSlash(removeTrailingSlash(path));
275
+ }
276
+
176
277
  // src/sortObjectKeys.ts
177
278
  function sortObjectKeys(obj) {
178
279
  if (typeof obj !== "object" || obj === null) {
@@ -194,19 +295,41 @@ function stripUndefinedProperties(obj) {
194
295
  Object.entries(obj).filter(([, value]) => value !== void 0)
195
296
  );
196
297
  }
298
+
299
+ // src/toRecord.ts
300
+ function toRecord(obj) {
301
+ return obj;
302
+ }
197
303
  export {
198
- InMemoryFile,
304
+ InMemoryBlob,
305
+ capitalize,
306
+ emptyObject,
199
307
  extractArgumentNames,
200
308
  getEnvVar,
309
+ getTransformationInfo,
201
310
  isAsyncGenerator,
202
311
  isNever,
203
312
  isNodeJsWriteableStream,
204
313
  isRecord,
205
314
  isTrue,
315
+ isValidIdentifier,
206
316
  noop,
317
+ openApiCompliantPath,
207
318
  readableStreamToAsyncIterable,
319
+ removeDoubleLeadingSlash,
320
+ removeLeadingNumbers,
321
+ removeLeadingSeparators,
322
+ removeNonAlphanumeric,
323
+ removeTrailingSlash,
208
324
  safeParse,
209
325
  safeStringify,
326
+ sanitizePathSlashes,
210
327
  sortObjectKeys,
211
- stripUndefinedProperties
328
+ splitBySeparators,
329
+ stripUndefinedProperties,
330
+ toCamelCaseIdentifier,
331
+ toPrettyCamelCase,
332
+ toRecord,
333
+ toValidIdentifier,
334
+ uncapitalize
212
335
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forklaunch/common",
3
- "version": "0.3.14",
3
+ "version": "0.4.1",
4
4
  "description": "Common package for base types, interfaces, implementations.",
5
5
  "homepage": "https://github.com/forklaunch/forklaunch-js#readme",
6
6
  "bugs": {
@@ -28,16 +28,16 @@
28
28
  "lib/**"
29
29
  ],
30
30
  "devDependencies": {
31
- "@eslint/js": "^9.29.0",
32
- "@types/node": "^24.0.3",
33
- "@typescript/native-preview": "7.0.0-dev.20250619.1",
31
+ "@eslint/js": "^9.30.1",
32
+ "@types/node": "^24.0.10",
33
+ "@typescript/native-preview": "7.0.0-dev.20250702.1",
34
34
  "depcheck": "^1.4.7",
35
- "eslint": "^9.29.0",
36
- "globals": "^16.2.0",
35
+ "eslint": "^9.30.1",
36
+ "globals": "^16.3.0",
37
37
  "tsup": "^8.5.0",
38
- "typedoc": "^0.28.5",
38
+ "typedoc": "^0.28.7",
39
39
  "typescript": "^5.8.3",
40
- "typescript-eslint": "^8.34.1",
40
+ "typescript-eslint": "^8.35.1",
41
41
  "vitest": "^3.2.4"
42
42
  },
43
43
  "scripts": {