deep-case-crafter 3.2.0 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.cts +25 -8
- package/dist/index.d.mts +25 -8
- package/dist/index.d.ts +25 -8
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
- package/readme.md +30 -6
package/dist/index.d.cts
CHANGED
|
@@ -67,6 +67,23 @@ interface TransformOptions extends TransformKeyOptions {
|
|
|
67
67
|
* @default []
|
|
68
68
|
*/
|
|
69
69
|
exclude?: readonly string[] | undefined;
|
|
70
|
+
/**
|
|
71
|
+
* Whether an object reached through several references gives one result, as in the
|
|
72
|
+
* input: shared objects stay shared, and circular references point back into the
|
|
73
|
+
* result. Pass `false` for data that cannot contain such references, such as the
|
|
74
|
+
* output of `JSON.parse`: the transform then skips this bookkeeping, which makes inputs
|
|
75
|
+
* with many objects and arrays up to about twice as fast.
|
|
76
|
+
*
|
|
77
|
+
* Use it only for tree-shaped data. An object is then copied once for every path that
|
|
78
|
+
* leads to it, so shared objects inside shared objects multiply. Data with more than
|
|
79
|
+
* 256 nested objects, arrays, Maps and Sets on one path, as circular references always
|
|
80
|
+
* have, is converted again from the start with the bookkeeping, so it takes longer and
|
|
81
|
+
* getters can run twice. With a finite `depth`, circular references stop at the depth
|
|
82
|
+
* limit before that, and are copied once per path down to it.
|
|
83
|
+
*
|
|
84
|
+
* @default true
|
|
85
|
+
*/
|
|
86
|
+
keepReferences?: boolean | undefined;
|
|
70
87
|
/**
|
|
71
88
|
* @deprecated Has no effect. Every key's case is detected from the key itself, both at
|
|
72
89
|
* runtime and in the result type, so there is nothing to declare.
|
|
@@ -176,7 +193,7 @@ declare const ACRONYM_PLURAL_SUFFIX = "s";
|
|
|
176
193
|
* (tail-recursive, so long keys compile).
|
|
177
194
|
*/
|
|
178
195
|
type HasOnlyKeyChars<S extends string> = S extends `${infer C}${infer Rest}` ? C extends KeyChar ? HasOnlyKeyChars<Rest> : false : true;
|
|
179
|
-
/** The canonical UUID form, where `x` stands for a hexadecimal digit (`
|
|
196
|
+
/** The canonical UUID form, where `x` stands for a hexadecimal digit (`isUuid`) */
|
|
180
197
|
type UuidPattern = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx';
|
|
181
198
|
/**
|
|
182
199
|
* `true` when `S` is a UUID in canonical form. `S` is compared with `UuidPattern` one
|
|
@@ -185,14 +202,14 @@ type UuidPattern = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx';
|
|
|
185
202
|
*/
|
|
186
203
|
type IsUuid<S extends string, Pattern extends string = UuidPattern> = Pattern extends `${infer P}${infer PatternRest}` ? S extends `${infer C}${infer Rest}` ? C extends (P extends 'x' ? HexDigit : P) ? IsUuid<Rest, PatternRest> : false : false : S extends '' ? true : false;
|
|
187
204
|
/**
|
|
188
|
-
* Keys the runtime leaves unchanged (`
|
|
205
|
+
* Keys the runtime leaves unchanged (`transformCase`): keys containing a
|
|
189
206
|
* character outside `A-Z a-z 0-9 _ -`, keys not starting with a letter, keys ending with
|
|
190
207
|
* a separator, and UUIDs. A UUID that starts with a digit is already preserved, so only
|
|
191
208
|
* keys that the other rules do not preserve are compared with the UUID form.
|
|
192
209
|
*/
|
|
193
210
|
type IsPreservedKey<K extends string> = HasOnlyKeyChars<K> extends false ? true : K extends `${LowerLetter | UpperLetter}${string}` ? K extends `${string}${Separator}` ? true : IsUuid<K> : true;
|
|
194
211
|
/**
|
|
195
|
-
* The case the runtime detects for a key that is not preserved (`
|
|
212
|
+
* The case the runtime detects for a key that is not preserved (`transformCase`), or
|
|
196
213
|
* `'none'` when it cannot be detected (the key is then left unchanged). A string is used
|
|
197
214
|
* rather than `null`, which every type accepts without `strictNullChecks`.
|
|
198
215
|
*/
|
|
@@ -216,13 +233,13 @@ type ToSeparatedCase<S extends string, Sep extends Separator, Prev extends strin
|
|
|
216
233
|
* (`XAxis` -> `xAxis`)
|
|
217
234
|
*/
|
|
218
235
|
type PascalToCamelCase<S extends string, Prev extends string = '', Acc extends string = ''> = S extends `${infer C}${infer Rest}` ? IsWordBoundary<C, Prev, Rest> extends true ? `${Acc}${S}` : PascalToCamelCase<Rest, C, `${Acc}${Lowercase<C>}`> : Acc;
|
|
219
|
-
/** snake_case/kebab-case to camelCase (`
|
|
236
|
+
/** snake_case/kebab-case to camelCase (`separatedToCamelCase`) */
|
|
220
237
|
type SeparatedToCamelCase<S extends string, Sep extends Separator, Acc extends string = ''> = S extends `${infer Word}${Sep}${infer Next}${infer Rest}` ? SeparatedToCamelCase<Rest, Sep, `${Acc}${Word}${Uppercase<Next>}`> : `${Acc}${S}`;
|
|
221
|
-
/** Replaces every `From` separator with `To` (`
|
|
238
|
+
/** Replaces every `From` separator with `To` (`replaceSeparator`) */
|
|
222
239
|
type ReplaceSeparator<S extends string, From extends Separator, To extends Separator, Acc extends string = ''> = S extends `${infer Head}${From}${infer Rest}` ? ReplaceSeparator<Rest, From, To, `${Acc}${Head}${To}`> : `${Acc}${S}`;
|
|
223
|
-
/** A single word in the target case (`
|
|
240
|
+
/** A single word in the target case (`singleWordToCase`) */
|
|
224
241
|
type SingleWordToCase<K extends string, To extends StringCase> = To extends 'pascal' ? Capitalize<K> : To extends 'camel' ? Uncapitalize<K> : Lowercase<K>;
|
|
225
|
-
/** A key of the detected case `From` in the target case (`
|
|
242
|
+
/** A key of the detected case `From` in the target case (the formatters `transformCase` picks) */
|
|
226
243
|
type FormatKey<K extends string, From extends StringCase, To extends StringCase> = From extends To ? K : From extends 'snake' | 'kebab' ? To extends 'camel' ? SeparatedToCamelCase<K, From extends 'snake' ? '_' : '-'> : To extends 'pascal' ? Capitalize<SeparatedToCamelCase<K, From extends 'snake' ? '_' : '-'>> : From extends 'snake' ? ReplaceSeparator<K, '_', '-'> : ReplaceSeparator<K, '-', '_'> : To extends 'pascal' ? Capitalize<K> : To extends 'camel' ? PascalToCamelCase<K> : ToSeparatedCase<K, To extends 'snake' ? '_' : '-'>;
|
|
227
244
|
/** Applies the detected case of `K` (`transformCase`) */
|
|
228
245
|
type TransformDetectedKey<K extends string, Detected, To extends StringCase> = Detected extends 'single' ? SingleWordToCase<K, To> : Detected extends StringCase ? FormatKey<K, Detected, To> : K;
|
|
@@ -501,7 +518,7 @@ declare function transform<T, To extends StringCase | undefined = StringCase | u
|
|
|
501
518
|
*
|
|
502
519
|
* @deprecated `sourceCase` has no effect: leave out the type arguments and `sourceCase`,
|
|
503
520
|
* and the result type is inferred, `depth` and `exclude` included. This form does not
|
|
504
|
-
* accept `depth` or `
|
|
521
|
+
* accept `depth`, `exclude` or `keepReferences`.
|
|
505
522
|
*/
|
|
506
523
|
declare function transform<T, From extends StringCase, To extends StringCase>(data: T, options: {
|
|
507
524
|
sourceCase: From;
|
package/dist/index.d.mts
CHANGED
|
@@ -67,6 +67,23 @@ interface TransformOptions extends TransformKeyOptions {
|
|
|
67
67
|
* @default []
|
|
68
68
|
*/
|
|
69
69
|
exclude?: readonly string[] | undefined;
|
|
70
|
+
/**
|
|
71
|
+
* Whether an object reached through several references gives one result, as in the
|
|
72
|
+
* input: shared objects stay shared, and circular references point back into the
|
|
73
|
+
* result. Pass `false` for data that cannot contain such references, such as the
|
|
74
|
+
* output of `JSON.parse`: the transform then skips this bookkeeping, which makes inputs
|
|
75
|
+
* with many objects and arrays up to about twice as fast.
|
|
76
|
+
*
|
|
77
|
+
* Use it only for tree-shaped data. An object is then copied once for every path that
|
|
78
|
+
* leads to it, so shared objects inside shared objects multiply. Data with more than
|
|
79
|
+
* 256 nested objects, arrays, Maps and Sets on one path, as circular references always
|
|
80
|
+
* have, is converted again from the start with the bookkeeping, so it takes longer and
|
|
81
|
+
* getters can run twice. With a finite `depth`, circular references stop at the depth
|
|
82
|
+
* limit before that, and are copied once per path down to it.
|
|
83
|
+
*
|
|
84
|
+
* @default true
|
|
85
|
+
*/
|
|
86
|
+
keepReferences?: boolean | undefined;
|
|
70
87
|
/**
|
|
71
88
|
* @deprecated Has no effect. Every key's case is detected from the key itself, both at
|
|
72
89
|
* runtime and in the result type, so there is nothing to declare.
|
|
@@ -176,7 +193,7 @@ declare const ACRONYM_PLURAL_SUFFIX = "s";
|
|
|
176
193
|
* (tail-recursive, so long keys compile).
|
|
177
194
|
*/
|
|
178
195
|
type HasOnlyKeyChars<S extends string> = S extends `${infer C}${infer Rest}` ? C extends KeyChar ? HasOnlyKeyChars<Rest> : false : true;
|
|
179
|
-
/** The canonical UUID form, where `x` stands for a hexadecimal digit (`
|
|
196
|
+
/** The canonical UUID form, where `x` stands for a hexadecimal digit (`isUuid`) */
|
|
180
197
|
type UuidPattern = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx';
|
|
181
198
|
/**
|
|
182
199
|
* `true` when `S` is a UUID in canonical form. `S` is compared with `UuidPattern` one
|
|
@@ -185,14 +202,14 @@ type UuidPattern = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx';
|
|
|
185
202
|
*/
|
|
186
203
|
type IsUuid<S extends string, Pattern extends string = UuidPattern> = Pattern extends `${infer P}${infer PatternRest}` ? S extends `${infer C}${infer Rest}` ? C extends (P extends 'x' ? HexDigit : P) ? IsUuid<Rest, PatternRest> : false : false : S extends '' ? true : false;
|
|
187
204
|
/**
|
|
188
|
-
* Keys the runtime leaves unchanged (`
|
|
205
|
+
* Keys the runtime leaves unchanged (`transformCase`): keys containing a
|
|
189
206
|
* character outside `A-Z a-z 0-9 _ -`, keys not starting with a letter, keys ending with
|
|
190
207
|
* a separator, and UUIDs. A UUID that starts with a digit is already preserved, so only
|
|
191
208
|
* keys that the other rules do not preserve are compared with the UUID form.
|
|
192
209
|
*/
|
|
193
210
|
type IsPreservedKey<K extends string> = HasOnlyKeyChars<K> extends false ? true : K extends `${LowerLetter | UpperLetter}${string}` ? K extends `${string}${Separator}` ? true : IsUuid<K> : true;
|
|
194
211
|
/**
|
|
195
|
-
* The case the runtime detects for a key that is not preserved (`
|
|
212
|
+
* The case the runtime detects for a key that is not preserved (`transformCase`), or
|
|
196
213
|
* `'none'` when it cannot be detected (the key is then left unchanged). A string is used
|
|
197
214
|
* rather than `null`, which every type accepts without `strictNullChecks`.
|
|
198
215
|
*/
|
|
@@ -216,13 +233,13 @@ type ToSeparatedCase<S extends string, Sep extends Separator, Prev extends strin
|
|
|
216
233
|
* (`XAxis` -> `xAxis`)
|
|
217
234
|
*/
|
|
218
235
|
type PascalToCamelCase<S extends string, Prev extends string = '', Acc extends string = ''> = S extends `${infer C}${infer Rest}` ? IsWordBoundary<C, Prev, Rest> extends true ? `${Acc}${S}` : PascalToCamelCase<Rest, C, `${Acc}${Lowercase<C>}`> : Acc;
|
|
219
|
-
/** snake_case/kebab-case to camelCase (`
|
|
236
|
+
/** snake_case/kebab-case to camelCase (`separatedToCamelCase`) */
|
|
220
237
|
type SeparatedToCamelCase<S extends string, Sep extends Separator, Acc extends string = ''> = S extends `${infer Word}${Sep}${infer Next}${infer Rest}` ? SeparatedToCamelCase<Rest, Sep, `${Acc}${Word}${Uppercase<Next>}`> : `${Acc}${S}`;
|
|
221
|
-
/** Replaces every `From` separator with `To` (`
|
|
238
|
+
/** Replaces every `From` separator with `To` (`replaceSeparator`) */
|
|
222
239
|
type ReplaceSeparator<S extends string, From extends Separator, To extends Separator, Acc extends string = ''> = S extends `${infer Head}${From}${infer Rest}` ? ReplaceSeparator<Rest, From, To, `${Acc}${Head}${To}`> : `${Acc}${S}`;
|
|
223
|
-
/** A single word in the target case (`
|
|
240
|
+
/** A single word in the target case (`singleWordToCase`) */
|
|
224
241
|
type SingleWordToCase<K extends string, To extends StringCase> = To extends 'pascal' ? Capitalize<K> : To extends 'camel' ? Uncapitalize<K> : Lowercase<K>;
|
|
225
|
-
/** A key of the detected case `From` in the target case (`
|
|
242
|
+
/** A key of the detected case `From` in the target case (the formatters `transformCase` picks) */
|
|
226
243
|
type FormatKey<K extends string, From extends StringCase, To extends StringCase> = From extends To ? K : From extends 'snake' | 'kebab' ? To extends 'camel' ? SeparatedToCamelCase<K, From extends 'snake' ? '_' : '-'> : To extends 'pascal' ? Capitalize<SeparatedToCamelCase<K, From extends 'snake' ? '_' : '-'>> : From extends 'snake' ? ReplaceSeparator<K, '_', '-'> : ReplaceSeparator<K, '-', '_'> : To extends 'pascal' ? Capitalize<K> : To extends 'camel' ? PascalToCamelCase<K> : ToSeparatedCase<K, To extends 'snake' ? '_' : '-'>;
|
|
227
244
|
/** Applies the detected case of `K` (`transformCase`) */
|
|
228
245
|
type TransformDetectedKey<K extends string, Detected, To extends StringCase> = Detected extends 'single' ? SingleWordToCase<K, To> : Detected extends StringCase ? FormatKey<K, Detected, To> : K;
|
|
@@ -501,7 +518,7 @@ declare function transform<T, To extends StringCase | undefined = StringCase | u
|
|
|
501
518
|
*
|
|
502
519
|
* @deprecated `sourceCase` has no effect: leave out the type arguments and `sourceCase`,
|
|
503
520
|
* and the result type is inferred, `depth` and `exclude` included. This form does not
|
|
504
|
-
* accept `depth` or `
|
|
521
|
+
* accept `depth`, `exclude` or `keepReferences`.
|
|
505
522
|
*/
|
|
506
523
|
declare function transform<T, From extends StringCase, To extends StringCase>(data: T, options: {
|
|
507
524
|
sourceCase: From;
|
package/dist/index.d.ts
CHANGED
|
@@ -67,6 +67,23 @@ interface TransformOptions extends TransformKeyOptions {
|
|
|
67
67
|
* @default []
|
|
68
68
|
*/
|
|
69
69
|
exclude?: readonly string[] | undefined;
|
|
70
|
+
/**
|
|
71
|
+
* Whether an object reached through several references gives one result, as in the
|
|
72
|
+
* input: shared objects stay shared, and circular references point back into the
|
|
73
|
+
* result. Pass `false` for data that cannot contain such references, such as the
|
|
74
|
+
* output of `JSON.parse`: the transform then skips this bookkeeping, which makes inputs
|
|
75
|
+
* with many objects and arrays up to about twice as fast.
|
|
76
|
+
*
|
|
77
|
+
* Use it only for tree-shaped data. An object is then copied once for every path that
|
|
78
|
+
* leads to it, so shared objects inside shared objects multiply. Data with more than
|
|
79
|
+
* 256 nested objects, arrays, Maps and Sets on one path, as circular references always
|
|
80
|
+
* have, is converted again from the start with the bookkeeping, so it takes longer and
|
|
81
|
+
* getters can run twice. With a finite `depth`, circular references stop at the depth
|
|
82
|
+
* limit before that, and are copied once per path down to it.
|
|
83
|
+
*
|
|
84
|
+
* @default true
|
|
85
|
+
*/
|
|
86
|
+
keepReferences?: boolean | undefined;
|
|
70
87
|
/**
|
|
71
88
|
* @deprecated Has no effect. Every key's case is detected from the key itself, both at
|
|
72
89
|
* runtime and in the result type, so there is nothing to declare.
|
|
@@ -176,7 +193,7 @@ declare const ACRONYM_PLURAL_SUFFIX = "s";
|
|
|
176
193
|
* (tail-recursive, so long keys compile).
|
|
177
194
|
*/
|
|
178
195
|
type HasOnlyKeyChars<S extends string> = S extends `${infer C}${infer Rest}` ? C extends KeyChar ? HasOnlyKeyChars<Rest> : false : true;
|
|
179
|
-
/** The canonical UUID form, where `x` stands for a hexadecimal digit (`
|
|
196
|
+
/** The canonical UUID form, where `x` stands for a hexadecimal digit (`isUuid`) */
|
|
180
197
|
type UuidPattern = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx';
|
|
181
198
|
/**
|
|
182
199
|
* `true` when `S` is a UUID in canonical form. `S` is compared with `UuidPattern` one
|
|
@@ -185,14 +202,14 @@ type UuidPattern = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx';
|
|
|
185
202
|
*/
|
|
186
203
|
type IsUuid<S extends string, Pattern extends string = UuidPattern> = Pattern extends `${infer P}${infer PatternRest}` ? S extends `${infer C}${infer Rest}` ? C extends (P extends 'x' ? HexDigit : P) ? IsUuid<Rest, PatternRest> : false : false : S extends '' ? true : false;
|
|
187
204
|
/**
|
|
188
|
-
* Keys the runtime leaves unchanged (`
|
|
205
|
+
* Keys the runtime leaves unchanged (`transformCase`): keys containing a
|
|
189
206
|
* character outside `A-Z a-z 0-9 _ -`, keys not starting with a letter, keys ending with
|
|
190
207
|
* a separator, and UUIDs. A UUID that starts with a digit is already preserved, so only
|
|
191
208
|
* keys that the other rules do not preserve are compared with the UUID form.
|
|
192
209
|
*/
|
|
193
210
|
type IsPreservedKey<K extends string> = HasOnlyKeyChars<K> extends false ? true : K extends `${LowerLetter | UpperLetter}${string}` ? K extends `${string}${Separator}` ? true : IsUuid<K> : true;
|
|
194
211
|
/**
|
|
195
|
-
* The case the runtime detects for a key that is not preserved (`
|
|
212
|
+
* The case the runtime detects for a key that is not preserved (`transformCase`), or
|
|
196
213
|
* `'none'` when it cannot be detected (the key is then left unchanged). A string is used
|
|
197
214
|
* rather than `null`, which every type accepts without `strictNullChecks`.
|
|
198
215
|
*/
|
|
@@ -216,13 +233,13 @@ type ToSeparatedCase<S extends string, Sep extends Separator, Prev extends strin
|
|
|
216
233
|
* (`XAxis` -> `xAxis`)
|
|
217
234
|
*/
|
|
218
235
|
type PascalToCamelCase<S extends string, Prev extends string = '', Acc extends string = ''> = S extends `${infer C}${infer Rest}` ? IsWordBoundary<C, Prev, Rest> extends true ? `${Acc}${S}` : PascalToCamelCase<Rest, C, `${Acc}${Lowercase<C>}`> : Acc;
|
|
219
|
-
/** snake_case/kebab-case to camelCase (`
|
|
236
|
+
/** snake_case/kebab-case to camelCase (`separatedToCamelCase`) */
|
|
220
237
|
type SeparatedToCamelCase<S extends string, Sep extends Separator, Acc extends string = ''> = S extends `${infer Word}${Sep}${infer Next}${infer Rest}` ? SeparatedToCamelCase<Rest, Sep, `${Acc}${Word}${Uppercase<Next>}`> : `${Acc}${S}`;
|
|
221
|
-
/** Replaces every `From` separator with `To` (`
|
|
238
|
+
/** Replaces every `From` separator with `To` (`replaceSeparator`) */
|
|
222
239
|
type ReplaceSeparator<S extends string, From extends Separator, To extends Separator, Acc extends string = ''> = S extends `${infer Head}${From}${infer Rest}` ? ReplaceSeparator<Rest, From, To, `${Acc}${Head}${To}`> : `${Acc}${S}`;
|
|
223
|
-
/** A single word in the target case (`
|
|
240
|
+
/** A single word in the target case (`singleWordToCase`) */
|
|
224
241
|
type SingleWordToCase<K extends string, To extends StringCase> = To extends 'pascal' ? Capitalize<K> : To extends 'camel' ? Uncapitalize<K> : Lowercase<K>;
|
|
225
|
-
/** A key of the detected case `From` in the target case (`
|
|
242
|
+
/** A key of the detected case `From` in the target case (the formatters `transformCase` picks) */
|
|
226
243
|
type FormatKey<K extends string, From extends StringCase, To extends StringCase> = From extends To ? K : From extends 'snake' | 'kebab' ? To extends 'camel' ? SeparatedToCamelCase<K, From extends 'snake' ? '_' : '-'> : To extends 'pascal' ? Capitalize<SeparatedToCamelCase<K, From extends 'snake' ? '_' : '-'>> : From extends 'snake' ? ReplaceSeparator<K, '_', '-'> : ReplaceSeparator<K, '-', '_'> : To extends 'pascal' ? Capitalize<K> : To extends 'camel' ? PascalToCamelCase<K> : ToSeparatedCase<K, To extends 'snake' ? '_' : '-'>;
|
|
227
244
|
/** Applies the detected case of `K` (`transformCase`) */
|
|
228
245
|
type TransformDetectedKey<K extends string, Detected, To extends StringCase> = Detected extends 'single' ? SingleWordToCase<K, To> : Detected extends StringCase ? FormatKey<K, Detected, To> : K;
|
|
@@ -501,7 +518,7 @@ declare function transform<T, To extends StringCase | undefined = StringCase | u
|
|
|
501
518
|
*
|
|
502
519
|
* @deprecated `sourceCase` has no effect: leave out the type arguments and `sourceCase`,
|
|
503
520
|
* and the result type is inferred, `depth` and `exclude` included. This form does not
|
|
504
|
-
* accept `depth` or `
|
|
521
|
+
* accept `depth`, `exclude` or `keepReferences`.
|
|
505
522
|
*/
|
|
506
523
|
declare function transform<T, From extends StringCase, To extends StringCase>(data: T, options: {
|
|
507
524
|
sourceCase: From;
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";const e=/^[
|
|
1
|
+
"use strict";const e=/^[^.]+(?:\.[^.]+)*$/;function t(e){if("string"==typeof e)return JSON.stringify(e);if("bigint"==typeof e)return`${e}n`;if(null===e)return"null";if("object"==typeof e||"function"==typeof e)try{return Object.prototype.toString.call(e)}catch(e){return"an object"}return String(e)}function n(e){const t=e.indexOf(".");return-1===t?[e]:[e.slice(0,t),e.slice(t+1)]}const r=[];function o(e){let t,n=!1;for(const r of e)void 0===r?n=!0:(null!=t?t:t=[]).push(r);return{keepKey:n,pathsBelow:t?s(t):r,scope:void 0}}function s(e){return 1===e.length?e:[...new Set(e)].sort()}function c(e,t){const r=[];let s;for(const t of e){const[e,o]=n(t);if("*"===e)r.push(o);else{null!=s||(s=new Map);const t=s.get(e);t?t.push(o):s.set(e,[o])}}const c=new Map;for(const[e,t]of null!=s?s:[])c.set(e,o(r.length?[...t,...r]:t));const{targetCase:i,depth:u,keepReferences:f}=t.plain.options,a={targetCase:i,depth:u,keepReferences:f,exclude:null},l={options:a,visitedRefs:[],named:c,other:o(r),root:t};return a.exclude=l,l}function i(e,t){const n="string"==typeof t?e.named.get(t):void 0;return null!=n?n:e.other}function u(e,t){var n;return null!==(n=t.scope)&&void 0!==n?n:t.scope=0===t.pathsBelow.length?e.root.plain:function(e,t){const n=JSON.stringify(e);let r=t.states.get(n);return r||(r=c(e,t),t.states.set(n,r)),r}(t.pathsBelow,e.root)}const f="[object Object]";function a(e,t,n){"__proto__"===t?Object.defineProperty(e,t,{value:n,enumerable:!0,writable:!0,configurable:!0}):e[t]=n}const l={};let p=!1;function d(e,t,n,r){let o=e[t];o.size>=16e6&&(p=!0,o=e[t]=new Map([[l,o]])),o.set(n,r)}function h(e){return e>=48&&e<=57}function y(e){return e>=97&&e<=122}function g(e){return e>=65&&e<=90}const w="s".charCodeAt(0);function v(e,t){if(!g(e.charCodeAt(t)))return!1;const n=e.charCodeAt(t-1);if(y(n)||h(n))return!0;const r=e.charCodeAt(t+1);return!(!g(n)||!y(r))&&(r!==w||y(e.charCodeAt(t+2)))}function b(e,t,n){return e.split(t).join(n)}function C(e,t,n){let r=n?e[0].toUpperCase():e[0],o=1,s=e.indexOf(t,1);for(;-1!==s;)r+=e.slice(o,s)+e[s+1].toUpperCase(),o=s+2,s=e.indexOf(t,o);return r+e.slice(o)}function m(e,t){let n="",r=0;for(let o=1;o<e.length;o++)v(e,o)&&(n+=e.slice(r,o)+t,r=o);return(n+e.slice(r)).toLowerCase()}function k(e,t){const n=e.length;if(0===n)return e;let r=e.charCodeAt(0);const o=g(r);if(!o&&!y(r))return e;let s=o?1:0,c=!o,i=0,u=0,f=!1,a=r;for(let t=1;t<n;t++){if(r=e.charCodeAt(t),y(r))c=!0;else if(g(r))s++;else if(95===r)i++,95===a&&(f=!0);else if(45===r)u++,45===a&&(f=!0);else if(!h(r))return e;a=r}if(95===a||45===a)return e;if(0===i&&0===u){if(0===s||1===s&&o)return function(e,t){const n=g(e.charCodeAt(0));switch(t){case"pascal":return n?e:e[0].toUpperCase()+e.slice(1);case"camel":return n?e[0].toLowerCase()+e.slice(1):e;default:return n?e.toLowerCase():e}}(e,t);if(!o)switch(t){case"snake":return m(e,"_");case"kebab":return m(e,"-");case"pascal":return e[0].toUpperCase()+e.slice(1);default:return e}if(!c)return e;switch(t){case"snake":return m(e,"_");case"kebab":return m(e,"-");case"camel":return function(e){let t=1;for(;t<e.length&&!v(e,t);)t++;return e.slice(0,t).toLowerCase()+e.slice(t)}(e);default:return e}}if(s>0||f||i>0&&u>0)return e;if(i>0)switch(t){case"camel":return C(e,"_",!1);case"pascal":return C(e,"_",!0);case"kebab":return b(e,"_","-");default:return e}if(function(e){if(36!==e.length)return!1;for(let t=0;t<36;t++){const n=e.charCodeAt(t);if(8===t||13===t||18===t||23===t){if(45!==n)return!1}else if(!(h(n)||n>=97&&n<=102||n>=65&&n<=70))return!1}return!0}(e))return e;switch(t){case"camel":return C(e,"-",!1);case"pascal":return C(e,"-",!0);case"snake":return b(e,"-","_");default:return e}}const j=new Map;let U,x={current:new Map,previous:new Map},M=0;function O(e,t){if(e.length>100)return k(e,t);if(t!==U){let e=j.get(t);e||(e={current:new Map,previous:new Map},j.set(t,e)),U=t,x=e}const n=x;let r=n.current.get(e);if(void 0!==r)return M=0,r;if(r=n.previous.get(e),void 0===r){if(r=k(e,t),++M>256&&M%8!=0)return r}else M=0;return n.current.size>=1e3&&(n.previous=n.current,n.current=new Map),n.current.set(e,r),r}const A={reason:"nesting limit reached"};function R(e,t,n=[],r=0,o=0){var s;if(null===e||"object"!=typeof e)return e;if(r>=t.depth)return e;const c=t.depth===1/0?0:r,h=t.keepReferences;if(h){const t=function(e,t){const n=e.get(t);if(void 0!==n||!p)return n;let r=e.get(l);for(;void 0!==r;){const e=r.get(t);if(void 0!==e)return e;r=r.get(l)}}(null!==(s=n[c])&&void 0!==s?s:n[c]=new Map,e);if(void 0!==t)return t}else if(o>=256)throw A;if(Array.isArray(e)){const s=[];return h&&d(n,c,e,s),function(e,t,n,r,o,s){for(const c of e)t.push(null!==c&&"object"==typeof c?R(c,n,r,o,s+1):c)}(e,s,t,n,r,o),s}const y=r+1,g=function(e){try{return Object.prototype.toString.call(e)}catch(e){return f}}(e);if(function(e,t){if(!(e instanceof Map)&&"[object Map]"!==t)return!1;try{return Map.prototype.has.call(e,void 0),!0}catch(e){return!1}}(e,g)){const r=new Map;return h&&d(n,c,e,r),null===t.exclude?function(e,t,n,r,o,s){for(const[c,i]of e)t.set("string"==typeof c?k(c,n.targetCase):c,R(i,n,r,o,s+1))}(e,r,t,n,y,o):function(e,t,n,r,o){for(const[s,c]of e){const e=i(n,s),f=u(n,e);t.set("string"!=typeof s||e.keepKey?s:k(s,n.options.targetCase),R(c,f.options,f.visitedRefs,r,o+1))}}(e,r,t.exclude,y,o),r}if(function(e,t){if(!(e instanceof Set)&&"[object Set]"!==t)return!1;try{return Set.prototype.has.call(e,void 0),!0}catch(e){return!1}}(e,g)){const r=new Set;return h&&d(n,c,e,r),function(e,t,n,r,o,s){for(const c of e)t.add(R(c,n,r,o,s+1))}(e,r,t,n,y,o),r}if(g===f){const r=e,s={};if(h&&d(n,c,e,s),null===t.exclude)for(const e in r)Object.prototype.hasOwnProperty.call(r,e)&&a(s,O(e,t.targetCase),R(r[e],t,n,y,o+1));else!function(e,t,n,r,o){for(const s in e)if(Object.prototype.hasOwnProperty.call(e,s)){const c=i(n,s),f=u(n,c);a(t,c.keepKey?s:O(s,n.options.targetCase),R(e[s],f.options,f.visitedRefs,r,o+1))}}(r,s,t.exclude,y,o);for(const e of Object.getOwnPropertySymbols(r))s[e]=r[e];return s}return e}const S=["camel","snake","pascal","kebab"];function $(e){const n=e||"camel";if(r=n,!S.includes(r))throw new TypeError(`Unsupported targetCase ${t(n)}. Use one of: ${S.join(", ")}.`);var r;return n}function _(n,r,o){const i=void 0===o?null:function(n,r){if(null==n)return null;if(!Array.isArray(n))throw new TypeError(`Unsupported exclude ${t(n)}. Use an array of paths, such as ["metadata.*"].`);for(const r of n)if("string"!=typeof r||!e.test(r))throw new TypeError(`Unsupported exclude path ${t(r)}. Use non-empty keys joined with dots, such as "metadata.*".`);if(0===n.length)return null;const o={plain:{options:r,visitedRefs:[]},states:new Map};return c(s(n),o)}(o,r);return null===i?R(n,r):R(n,i.options,i.visitedRefs)}exports.transform=function(e,n){var r,o;const s=null==n?void 0:n.targetCase,c=null!==(r=null==n?void 0:n.depth)&&void 0!==r?r:1/0,i=$(s);if("number"!=typeof c||Number.isNaN(c))throw new TypeError(`Unsupported depth ${t(c)}. Use a number, or omit depth to transform every level.`);const u=null==n?void 0:n.exclude,f=null===(o=null==n?void 0:n.keepReferences)||void 0===o||o;if("boolean"!=typeof f)throw new TypeError(`Unsupported keepReferences ${t(f)}. Use true or false.`);if(!f)try{return _(e,{targetCase:i,depth:c,keepReferences:f,exclude:null},u)}catch(e){if(e!==A)throw e}return _(e,{targetCase:i,depth:c,keepReferences:!0,exclude:null},u)},exports.transformKey=function(e,n){if("string"!=typeof e)throw new TypeError(`Unsupported key ${t(e)}. Use a string.`);if("string"==typeof n)throw new TypeError(`Unsupported options ${t(n)}. Use an options object: { targetCase: ${t(n)} }.`);return k(e,$(null==n?void 0:n.targetCase))};
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const e=/^[
|
|
1
|
+
const e=/^[^.]+(?:\.[^.]+)*$/;function t(e){if("string"==typeof e)return JSON.stringify(e);if("bigint"==typeof e)return`${e}n`;if(null===e)return"null";if("object"==typeof e||"function"==typeof e)try{return Object.prototype.toString.call(e)}catch(e){return"an object"}return String(e)}function n(e){const t=e.indexOf(".");return-1===t?[e]:[e.slice(0,t),e.slice(t+1)]}const r=[];function o(e){let t,n=!1;for(const r of e)void 0===r?n=!0:(null!=t?t:t=[]).push(r);return{keepKey:n,pathsBelow:t?s(t):r,scope:void 0}}function s(e){return 1===e.length?e:[...new Set(e)].sort()}function c(e,t){const r=[];let s;for(const t of e){const[e,o]=n(t);if("*"===e)r.push(o);else{null!=s||(s=new Map);const t=s.get(e);t?t.push(o):s.set(e,[o])}}const c=new Map;for(const[e,t]of null!=s?s:[])c.set(e,o(r.length?[...t,...r]:t));const{targetCase:i,depth:u,keepReferences:f}=t.plain.options,a={targetCase:i,depth:u,keepReferences:f,exclude:null},l={options:a,visitedRefs:[],named:c,other:o(r),root:t};return a.exclude=l,l}function i(e,t){const n="string"==typeof t?e.named.get(t):void 0;return null!=n?n:e.other}function u(e,t){var n;return null!==(n=t.scope)&&void 0!==n?n:t.scope=0===t.pathsBelow.length?e.root.plain:function(e,t){const n=JSON.stringify(e);let r=t.states.get(n);return r||(r=c(e,t),t.states.set(n,r)),r}(t.pathsBelow,e.root)}const f="[object Object]";function a(e,t,n){"__proto__"===t?Object.defineProperty(e,t,{value:n,enumerable:!0,writable:!0,configurable:!0}):e[t]=n}const l={};let p=!1;function d(e,t,n,r){let o=e[t];o.size>=16e6&&(p=!0,o=e[t]=new Map([[l,o]])),o.set(n,r)}function h(e){return e>=48&&e<=57}function y(e){return e>=97&&e<=122}function g(e){return e>=65&&e<=90}const w="s".charCodeAt(0);function v(e,t){if(!g(e.charCodeAt(t)))return!1;const n=e.charCodeAt(t-1);if(y(n)||h(n))return!0;const r=e.charCodeAt(t+1);return!(!g(n)||!y(r))&&(r!==w||y(e.charCodeAt(t+2)))}function b(e,t,n){return e.split(t).join(n)}function C(e,t,n){let r=n?e[0].toUpperCase():e[0],o=1,s=e.indexOf(t,1);for(;-1!==s;)r+=e.slice(o,s)+e[s+1].toUpperCase(),o=s+2,s=e.indexOf(t,o);return r+e.slice(o)}function k(e,t){let n="",r=0;for(let o=1;o<e.length;o++)v(e,o)&&(n+=e.slice(r,o)+t,r=o);return(n+e.slice(r)).toLowerCase()}function m(e,t){const n=e.length;if(0===n)return e;let r=e.charCodeAt(0);const o=g(r);if(!o&&!y(r))return e;let s=o?1:0,c=!o,i=0,u=0,f=!1,a=r;for(let t=1;t<n;t++){if(r=e.charCodeAt(t),y(r))c=!0;else if(g(r))s++;else if(95===r)i++,95===a&&(f=!0);else if(45===r)u++,45===a&&(f=!0);else if(!h(r))return e;a=r}if(95===a||45===a)return e;if(0===i&&0===u){if(0===s||1===s&&o)return function(e,t){const n=g(e.charCodeAt(0));switch(t){case"pascal":return n?e:e[0].toUpperCase()+e.slice(1);case"camel":return n?e[0].toLowerCase()+e.slice(1):e;default:return n?e.toLowerCase():e}}(e,t);if(!o)switch(t){case"snake":return k(e,"_");case"kebab":return k(e,"-");case"pascal":return e[0].toUpperCase()+e.slice(1);default:return e}if(!c)return e;switch(t){case"snake":return k(e,"_");case"kebab":return k(e,"-");case"camel":return function(e){let t=1;for(;t<e.length&&!v(e,t);)t++;return e.slice(0,t).toLowerCase()+e.slice(t)}(e);default:return e}}if(s>0||f||i>0&&u>0)return e;if(i>0)switch(t){case"camel":return C(e,"_",!1);case"pascal":return C(e,"_",!0);case"kebab":return b(e,"_","-");default:return e}if(function(e){if(36!==e.length)return!1;for(let t=0;t<36;t++){const n=e.charCodeAt(t);if(8===t||13===t||18===t||23===t){if(45!==n)return!1}else if(!(h(n)||n>=97&&n<=102||n>=65&&n<=70))return!1}return!0}(e))return e;switch(t){case"camel":return C(e,"-",!1);case"pascal":return C(e,"-",!0);case"snake":return b(e,"-","_");default:return e}}const j=new Map;let U,x={current:new Map,previous:new Map},M=0;function O(e,t){if(e.length>100)return m(e,t);if(t!==U){let e=j.get(t);e||(e={current:new Map,previous:new Map},j.set(t,e)),U=t,x=e}const n=x;let r=n.current.get(e);if(void 0!==r)return M=0,r;if(r=n.previous.get(e),void 0===r){if(r=m(e,t),++M>256&&M%8!=0)return r}else M=0;return n.current.size>=1e3&&(n.previous=n.current,n.current=new Map),n.current.set(e,r),r}const A={reason:"nesting limit reached"};function R(e,t,n=[],r=0,o=0){var s;if(null===e||"object"!=typeof e)return e;if(r>=t.depth)return e;const c=t.depth===1/0?0:r,h=t.keepReferences;if(h){const t=function(e,t){const n=e.get(t);if(void 0!==n||!p)return n;let r=e.get(l);for(;void 0!==r;){const e=r.get(t);if(void 0!==e)return e;r=r.get(l)}}(null!==(s=n[c])&&void 0!==s?s:n[c]=new Map,e);if(void 0!==t)return t}else if(o>=256)throw A;if(Array.isArray(e)){const s=[];return h&&d(n,c,e,s),function(e,t,n,r,o,s){for(const c of e)t.push(null!==c&&"object"==typeof c?R(c,n,r,o,s+1):c)}(e,s,t,n,r,o),s}const y=r+1,g=function(e){try{return Object.prototype.toString.call(e)}catch(e){return f}}(e);if(function(e,t){if(!(e instanceof Map)&&"[object Map]"!==t)return!1;try{return Map.prototype.has.call(e,void 0),!0}catch(e){return!1}}(e,g)){const r=new Map;return h&&d(n,c,e,r),null===t.exclude?function(e,t,n,r,o,s){for(const[c,i]of e)t.set("string"==typeof c?m(c,n.targetCase):c,R(i,n,r,o,s+1))}(e,r,t,n,y,o):function(e,t,n,r,o){for(const[s,c]of e){const e=i(n,s),f=u(n,e);t.set("string"!=typeof s||e.keepKey?s:m(s,n.options.targetCase),R(c,f.options,f.visitedRefs,r,o+1))}}(e,r,t.exclude,y,o),r}if(function(e,t){if(!(e instanceof Set)&&"[object Set]"!==t)return!1;try{return Set.prototype.has.call(e,void 0),!0}catch(e){return!1}}(e,g)){const r=new Set;return h&&d(n,c,e,r),function(e,t,n,r,o,s){for(const c of e)t.add(R(c,n,r,o,s+1))}(e,r,t,n,y,o),r}if(g===f){const r=e,s={};if(h&&d(n,c,e,s),null===t.exclude)for(const e in r)Object.prototype.hasOwnProperty.call(r,e)&&a(s,O(e,t.targetCase),R(r[e],t,n,y,o+1));else!function(e,t,n,r,o){for(const s in e)if(Object.prototype.hasOwnProperty.call(e,s)){const c=i(n,s),f=u(n,c);a(t,c.keepKey?s:O(s,n.options.targetCase),R(e[s],f.options,f.visitedRefs,r,o+1))}}(r,s,t.exclude,y,o);for(const e of Object.getOwnPropertySymbols(r))s[e]=r[e];return s}return e}const S=["camel","snake","pascal","kebab"];function $(e){const n=e||"camel";if(r=n,!S.includes(r))throw new TypeError(`Unsupported targetCase ${t(n)}. Use one of: ${S.join(", ")}.`);var r;return n}function _(e,n){var r,o;const s=null==n?void 0:n.targetCase,c=null!==(r=null==n?void 0:n.depth)&&void 0!==r?r:1/0,i=$(s);if("number"!=typeof c||Number.isNaN(c))throw new TypeError(`Unsupported depth ${t(c)}. Use a number, or omit depth to transform every level.`);const u=null==n?void 0:n.exclude,f=null===(o=null==n?void 0:n.keepReferences)||void 0===o||o;if("boolean"!=typeof f)throw new TypeError(`Unsupported keepReferences ${t(f)}. Use true or false.`);if(!f)try{return E(e,{targetCase:i,depth:c,keepReferences:f,exclude:null},u)}catch(e){if(e!==A)throw e}return E(e,{targetCase:i,depth:c,keepReferences:!0,exclude:null},u)}function E(n,r,o){const i=void 0===o?null:function(n,r){if(null==n)return null;if(!Array.isArray(n))throw new TypeError(`Unsupported exclude ${t(n)}. Use an array of paths, such as ["metadata.*"].`);for(const r of n)if("string"!=typeof r||!e.test(r))throw new TypeError(`Unsupported exclude path ${t(r)}. Use non-empty keys joined with dots, such as "metadata.*".`);if(0===n.length)return null;const o={plain:{options:r,visitedRefs:[]},states:new Map};return c(s(n),o)}(o,r);return null===i?R(n,r):R(n,i.options,i.visitedRefs)}function T(e,n){if("string"!=typeof e)throw new TypeError(`Unsupported key ${t(e)}. Use a string.`);if("string"==typeof n)throw new TypeError(`Unsupported options ${t(n)}. Use an options object: { targetCase: ${t(n)} }.`);return m(e,$(null==n?void 0:n.targetCase))}export{_ as transform,T as transformKey};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "deep-case-crafter",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"description": "Convert object keys between camelCase, snake_case, kebab-case and PascalCase, deeply (objects, arrays, Map, Set), with exact TypeScript types",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
package/readme.md
CHANGED
|
@@ -210,7 +210,7 @@ Where the others are better or different:
|
|
|
210
210
|
|
|
211
211
|
- **Bundle size**: bundled and minified with esbuild, ts-case-convert's `objectToCamel`
|
|
212
212
|
and `objectToSnake` take about 1.1 KB gzipped, deep-case-crafter's `transform` about
|
|
213
|
-
2.
|
|
213
|
+
2.8 KB.
|
|
214
214
|
- **More output**: ts-case-convert and es-toolkit also convert to SCREAMING_SNAKE_CASE,
|
|
215
215
|
which deep-case-crafter does not.
|
|
216
216
|
- **Single strings**: like `transformKey`, ts-case-convert's `toSnake('userId')` and
|
|
@@ -298,12 +298,14 @@ The result **types** match all of this, so `result.get_http_response` and
|
|
|
298
298
|
- `targetCase` (optional): `'camel' | 'snake' | 'pascal' | 'kebab'` (default: `'camel'`, also used for any falsy value such as `''`, `null` or `0`). Any other value throws a `TypeError`.
|
|
299
299
|
- `depth` (optional): **How many nesting levels to transform** (default: `Infinity` — the whole structure; `null` means the same). `1` transforms only the top-level keys, `2` the keys one level down as well, and so on. The values of objects, Maps and Sets are one level deeper; array items stay on their array's level. A value that is not a number (`NaN`, `'3'`, `3n`) throws a `TypeError`.
|
|
300
300
|
- `exclude` (optional): **Keys to leave unchanged**, as paths of keys joined with dots, such as `['users_by_id.*', 'metadata.*']` (default: none; `null` means the same). See [Leaving keys unchanged](#leaving-keys-unchanged). Anything but an array of such paths throws a `TypeError`.
|
|
301
|
-
- `
|
|
301
|
+
- `keepReferences` (optional): **Whether shared and circular references keep their shape** (default: `true`; `null` means the same). An object reached through several references then gives one result, and a circular reference points back into the result. Pass `false` for data that cannot contain such references, such as the output of `JSON.parse`, to skip that bookkeeping: inputs with many objects and arrays convert up to about twice as fast. Use it for tree-shaped data only: an object is then copied once for every path that leads to it, so shared objects inside shared objects multiply. Data with more than 256 nested objects, arrays, Maps and Sets on one path, as circular references always have, is converted again from the start with the bookkeeping, so it takes longer and getters can run twice. With a finite `depth`, circular references stop at the depth limit before that, and are copied once per path down to it. Anything but a boolean throws a `TypeError`.
|
|
302
|
+
- `sourceCase` (**deprecated**): has no effect. Every key's case is detected from the key itself, at runtime and in the result type. It is still accepted, and so is the 2.x form with explicit type arguments, `transform<Data, 'snake', 'camel'>(data, { sourceCase: 'snake', targetCase: 'camel' })`, except together with `depth`, `exclude` or `keepReferences` (leave the type arguments out to use them); the key types follow the runtime, not `sourceCase` (see [3.0.0 in the changelog](https://github.com/gyrgy/deep-case-crafter/blob/main/CHANGELOG.md#300---2026-10-25)).
|
|
302
303
|
|
|
303
304
|
Circular references are resolved to the transformed object. With a finite `depth`, a
|
|
304
305
|
cycle is transformed once per level down to the limit instead: the result grows with the
|
|
305
|
-
depth, and a depth of more than about a thousand levels on cyclic data
|
|
306
|
-
stack and
|
|
306
|
+
depth, and a depth of more than about a thousand levels on cyclic data can exceed the
|
|
307
|
+
call stack and throw a `RangeError` (see [Limits](#limits)). Omit `depth` for cyclic
|
|
308
|
+
data, or keep it small.
|
|
307
309
|
|
|
308
310
|
### Leaving keys unchanged
|
|
309
311
|
|
|
@@ -476,14 +478,36 @@ Worth knowing:
|
|
|
476
478
|
`userId`), the last one wins.
|
|
477
479
|
- Every property is read once, in order, and its value is transformed before the next one
|
|
478
480
|
is read; getters run during the transformation. Maps are read through their iterator.
|
|
481
|
+
With `keepReferences: false`, data with more than 256 nested objects, arrays, Maps and
|
|
482
|
+
Sets on one path is read a second time.
|
|
479
483
|
- With `depth`, values below the limit are returned as they are: the result shares them
|
|
480
484
|
with the input instead of copying them. An object reached at several levels is
|
|
481
485
|
transformed once per level.
|
|
482
486
|
- With `exclude`, an object reached through several keys is transformed once for each
|
|
483
487
|
set of paths that match below them: one result when the same paths continue below
|
|
484
488
|
every key, one per set otherwise.
|
|
485
|
-
- The transformation is recursive
|
|
486
|
-
|
|
489
|
+
- The transformation is recursive, so very deeply nested data hits a limit: see
|
|
490
|
+
[Limits](#limits).
|
|
491
|
+
|
|
492
|
+
---
|
|
493
|
+
|
|
494
|
+
## Limits
|
|
495
|
+
|
|
496
|
+
Measured with Node 24 on an Apple M4 Pro, with Node's default stack and memory sizes:
|
|
497
|
+
|
|
498
|
+
- **Nesting depth.** The conversion is recursive. Depending on the shape, the first call
|
|
499
|
+
in a process converts 1,300 to 2,400 levels of nesting (nested Maps the fewest, nested
|
|
500
|
+
objects the most), and once the code is warm about 2,600 (nested arrays) to 4,400;
|
|
501
|
+
deeper data throws a `RangeError`. API data nests far less, but untrusted input can nest
|
|
502
|
+
deeper (`JSON.parse` accepts millions of levels), so catch the error where such input is
|
|
503
|
+
converted.
|
|
504
|
+
- **Size.** There is no fixed limit on keys, objects or array items. The result is a full
|
|
505
|
+
copy, so the input and the result must both fit in memory: converting 1 million records
|
|
506
|
+
with 30 million keys (725 MB in memory) needed 1.5 GB more at its peak, and 1.1 GB with
|
|
507
|
+
`keepReferences: false`. When memory runs out, Node ends the process, which cannot be
|
|
508
|
+
caught.
|
|
509
|
+
- **Long keys.** Conversion time grows linearly with a key's length: about 20 ms for a key
|
|
510
|
+
of a million characters.
|
|
487
511
|
|
|
488
512
|
---
|
|
489
513
|
|