utilful 2.0.0 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,6 +8,7 @@ A collection of TypeScript utilities that I use across my projects.
8
8
  - [API](#api)
9
9
  - [Array](#array)
10
10
  - [CSV](#csv)
11
+ - [Defu](#defu)
11
12
  - [Emitter](#emitter)
12
13
  - [JSON](#json)
13
14
  - [Module](#module)
@@ -111,6 +112,95 @@ Jane,25`
111
112
  const data = parseCSV<'name' | 'age'>(csv) // [{ name: 'John', age: '30' }, { name: 'Jane', age: '25' }]
112
113
  ```
113
114
 
115
+ ### Defu
116
+
117
+ Recursively assign default properties. Simplified version based on [unjs/defu](https://github.com/unjs/defu).
118
+
119
+ #### `defu`
120
+
121
+ Recursively assigns missing properties from defaults to the source object. The source object takes precedence over defaults.
122
+
123
+ **Key Features:**
124
+
125
+ - **Null/undefined handling**: `null` and `undefined` values in source are replaced with defaults
126
+ - **Array concatenation**: Arrays are concatenated (source + defaults)
127
+ - **Deep merging**: Nested objects are recursively merged
128
+ - **Type safety**: Preserves TypeScript types
129
+ - **Prototype pollution protection**: Ignores `__proto__` and `constructor` keys
130
+
131
+ ```ts
132
+ type PlainObject = Record<PropertyKey, any>
133
+
134
+ declare function defu<T extends PlainObject>(
135
+ source: T,
136
+ ...defaults: PlainObject[]
137
+ ): T
138
+ ```
139
+
140
+ **Example:**
141
+
142
+ ```ts
143
+ import { defu } from 'utilful'
144
+
145
+ const result = defu(
146
+ { a: 1, b: { x: 1 } },
147
+ { a: 2, b: { y: 2 }, c: 3 }
148
+ )
149
+ // Result: { a: 1, b: { x: 1, y: 2 }, c: 3 }
150
+ ```
151
+
152
+ **Array concatenation example:**
153
+
154
+ ```ts
155
+ const result = defu(
156
+ { items: ['a', 'b'] },
157
+ { items: ['c', 'd'] }
158
+ )
159
+ // Result: { items: ['a', 'b', 'c', 'd'] }
160
+ ```
161
+
162
+ **Null/undefined handling:**
163
+
164
+ ```ts
165
+ const result = defu(
166
+ { name: null, age: undefined },
167
+ { name: 'John', age: 30, city: 'NYC' }
168
+ )
169
+ // Result: { name: 'John', age: 30, city: 'NYC' }
170
+ ```
171
+
172
+ #### `createDefu`
173
+
174
+ Creates a custom defu function with a custom merger.
175
+
176
+ ```ts
177
+ type DefuMerger<T extends PlainObject = PlainObject> = (
178
+ target: T,
179
+ key: PropertyKey,
180
+ value: any,
181
+ namespace: string,
182
+ ) => boolean | void
183
+
184
+ declare function createDefu(merger?: DefuMerger): DefuFn
185
+ ```
186
+
187
+ **Example:**
188
+
189
+ ```ts
190
+ import { createDefu } from 'utilful'
191
+
192
+ // Custom merger that adds numbers instead of replacing them
193
+ const addNumbers = createDefu((obj, key, val) => {
194
+ if (typeof val === 'number' && typeof obj[key] === 'number') {
195
+ obj[key] += val
196
+ return true // Indicates the merger handled this property
197
+ }
198
+ })
199
+
200
+ const result = addNumbers({ cost: 15 }, { cost: 10 })
201
+ // Result: { cost: 25 }
202
+ ```
203
+
114
204
  ### Emitter
115
205
 
116
206
  Tiny functional event emitter / pubsub, based on [mitt](https://github.com/developit/mitt).
@@ -386,7 +476,7 @@ Shorthand function to create an `Ok` result. Use it to wrap a successful value.
386
476
  **Type Definition:**
387
477
 
388
478
  ```ts
389
- function ok<T>(value: T): Ok<T>
479
+ declare function ok<T>(value: T): Ok<T>
390
480
  ```
391
481
 
392
482
  #### `err`
@@ -396,8 +486,8 @@ Shorthand function to create an `Err` result. Use it to wrap an error value.
396
486
  **Type Definition:**
397
487
 
398
488
  ```ts
399
- function err<E extends string = string>(err: E): Err<E>
400
- function err<E = unknown>(err: E): Err<E>
489
+ declare function err<E extends string = string>(err: E): Err<E>
490
+ declare function err<E = unknown>(err: E): Err<E>
401
491
  ```
402
492
 
403
493
  #### `toResult`
@@ -407,8 +497,8 @@ Wraps a function that might throw an error and returns a `Result` with the resul
407
497
  **Type Definition:**
408
498
 
409
499
  ```ts
410
- function toResult<T, E = unknown>(fn: () => T): Result<T, E>
411
- function toResult<T, E = unknown>(promise: Promise<T>): Promise<Result<T, E>>
500
+ declare function toResult<T, E = unknown>(fn: () => T): Result<T, E>
501
+ declare function toResult<T, E = unknown>(promise: Promise<T>): Promise<Result<T, E>>
412
502
  ```
413
503
 
414
504
  #### `unwrapResult`
@@ -425,9 +515,9 @@ const { value, error } = unwrapResult(result)
425
515
  **Type Definition:**
426
516
 
427
517
  ```ts
428
- function unwrapResult<T>(result: Ok<T>): { value: T, error: undefined }
429
- function unwrapResult<E>(result: Err<E>): { value: undefined, error: E }
430
- function unwrapResult<T, E>(result: Result<T, E>): { value: T, error: undefined } | { value: undefined, error: E }
518
+ declare function unwrapResult<T>(result: Ok<T>): { value: T, error: undefined }
519
+ declare function unwrapResult<E>(result: Err<E>): { value: undefined, error: E }
520
+ declare function unwrapResult<T, E>(result: Result<T, E>): { value: T, error: undefined } | { value: undefined, error: E }
431
521
  ```
432
522
 
433
523
  #### `tryCatch`
@@ -449,8 +539,8 @@ const { value, error } = await tryCatch(fetch('https://api.example.com/data').th
449
539
  **Type Definition:**
450
540
 
451
541
  ```ts
452
- function tryCatch<T, E = unknown>(fn: () => T): { value: T, error: undefined } | { value: undefined, error: E }
453
- function tryCatch<T, E = unknown>(promise: Promise<T>): Promise<{ value: T, error: undefined } | { value: undefined, error: E }>
542
+ declare function tryCatch<T, E = unknown>(fn: () => T): { value: T, error: undefined } | { value: undefined, error: E }
543
+ declare function tryCatch<T, E = unknown>(promise: Promise<T>): Promise<{ value: T, error: undefined } | { value: undefined, error: E }>
454
544
  ```
455
545
 
456
546
  ### String
package/dist/array.d.ts CHANGED
@@ -1,10 +1,11 @@
1
+ //#region src/array.d.ts
1
2
  /**
2
- * Represents a value that can be either a single value or an array of values.
3
- */
3
+ * Represents a value that can be either a single value or an array of values.
4
+ */
4
5
  type MaybeArray<T> = T | T[];
5
6
  /**
6
- * Converts `MaybeArray<T>` to `Array<T>`.
7
- */
7
+ * Converts `MaybeArray<T>` to `Array<T>`.
8
+ */
8
9
  declare function toArray<T>(array?: MaybeArray<T> | null | undefined): T[];
9
-
10
- export { type MaybeArray, toArray };
10
+ //#endregion
11
+ export { MaybeArray, toArray };
package/dist/array.js ADDED
@@ -0,0 +1,11 @@
1
+ //#region src/array.ts
2
+ /**
3
+ * Converts `MaybeArray<T>` to `Array<T>`.
4
+ */
5
+ function toArray(array) {
6
+ array ??= [];
7
+ return Array.isArray(array) ? array : [array];
8
+ }
9
+
10
+ //#endregion
11
+ export { toArray };
package/dist/csv.d.ts CHANGED
@@ -1,67 +1,68 @@
1
+ //#region src/csv.d.ts
1
2
  /**
2
- * Represents a row in a CSV file with column names of type T.
3
- */
3
+ * Represents a row in a CSV file with column names of type T.
4
+ */
4
5
  type CSVRow<T extends string = string> = Record<T, string>;
5
6
  /**
6
- * Converts an array of objects to a comma-separated values (CSV) string
7
- * that contains only the `columns` specified.
8
- *
9
- * @example
10
- * const data = [
11
- * { name: 'John', age: '30', city: 'New York' },
12
- * { name: 'Jane', age: '25', city: 'Boston' }
13
- * ]
14
- *
15
- * const csv = createCSV(data, ['name', 'age'])
16
- * // name,age
17
- * // John,30
18
- * // Jane,25
19
- */
7
+ * Converts an array of objects to a comma-separated values (CSV) string
8
+ * that contains only the `columns` specified.
9
+ *
10
+ * @example
11
+ * const data = [
12
+ * { name: 'John', age: '30', city: 'New York' },
13
+ * { name: 'Jane', age: '25', city: 'Boston' }
14
+ * ]
15
+ *
16
+ * const csv = createCSV(data, ['name', 'age'])
17
+ * // name,age
18
+ * // John,30
19
+ * // Jane,25
20
+ */
20
21
  declare function createCSV<T extends Record<string, unknown>>(data: T[], columns: (keyof T)[], options?: {
21
- /** @default ',' */
22
- delimiter?: string;
23
- /** @default true */
24
- addHeader?: boolean;
25
- /** @default false */
26
- quoteAll?: boolean;
22
+ /** @default ',' */
23
+ delimiter?: string;
24
+ /** @default true */
25
+ addHeader?: boolean;
26
+ /** @default false */
27
+ quoteAll?: boolean;
27
28
  }): string;
28
29
  /**
29
- * Escapes a value for a CSV string.
30
- *
31
- * @remarks
32
- * Returns an empty string if the value is `null` or `undefined`.
33
- * Values containing delimiters, quotes, or line breaks are quoted.
34
- * Within quoted values, double quotes are escaped by doubling them.
35
- *
36
- * @example
37
- * escapeCSVValue('hello, world') // "hello, world"
38
- * escapeCSVValue('contains "quotes"') // "contains ""quotes"""
39
- */
30
+ * Escapes a value for a CSV string.
31
+ *
32
+ * @remarks
33
+ * Returns an empty string if the value is `null` or `undefined`.
34
+ * Values containing delimiters, quotes, or line breaks are quoted.
35
+ * Within quoted values, double quotes are escaped by doubling them.
36
+ *
37
+ * @example
38
+ * escapeCSVValue('hello, world') // "hello, world"
39
+ * escapeCSVValue('contains "quotes"') // "contains ""quotes"""
40
+ */
40
41
  declare function escapeCSVValue(value: unknown, options?: {
41
- /** @default ',' */
42
- delimiter?: string;
43
- /** @default false */
44
- quoteAll?: boolean;
42
+ /** @default ',' */
43
+ delimiter?: string;
44
+ /** @default false */
45
+ quoteAll?: boolean;
45
46
  }): string;
46
47
  /**
47
- * Parses a comma-separated values (CSV) string into an array of objects.
48
- *
49
- * @remarks
50
- * The first row of the CSV string is used as the header row.
51
- *
52
- * @example
53
- * const csv = `name,age
54
- * John,30
55
- * Jane,25`
56
- *
57
- * const data = parseCSV<'name' | 'age'>(csv)
58
- * // [{ name: 'John', age: '30' }, { name: 'Jane', age: '25' }]
59
- */
48
+ * Parses a comma-separated values (CSV) string into an array of objects.
49
+ *
50
+ * @remarks
51
+ * The first row of the CSV string is used as the header row.
52
+ *
53
+ * @example
54
+ * const csv = `name,age
55
+ * John,30
56
+ * Jane,25`
57
+ *
58
+ * const data = parseCSV<'name' | 'age'>(csv)
59
+ * // [{ name: 'John', age: '30' }, { name: 'Jane', age: '25' }]
60
+ */
60
61
  declare function parseCSV<Header extends string>(csv?: string | null | undefined, options?: {
61
- /** @default ',' */
62
- delimiter?: string;
63
- /** @default true */
64
- trimValues?: boolean;
62
+ /** @default ',' */
63
+ delimiter?: string;
64
+ /** @default true */
65
+ trimValues?: boolean;
65
66
  }): CSVRow<Header>[];
66
-
67
- export { type CSVRow, createCSV, escapeCSVValue, parseCSV };
67
+ //#endregion
68
+ export { CSVRow, createCSV, escapeCSVValue, parseCSV };
package/dist/csv.js ADDED
@@ -0,0 +1,98 @@
1
+ //#region src/csv.ts
2
+ /**
3
+ * Converts an array of objects to a comma-separated values (CSV) string
4
+ * that contains only the `columns` specified.
5
+ *
6
+ * @example
7
+ * const data = [
8
+ * { name: 'John', age: '30', city: 'New York' },
9
+ * { name: 'Jane', age: '25', city: 'Boston' }
10
+ * ]
11
+ *
12
+ * const csv = createCSV(data, ['name', 'age'])
13
+ * // name,age
14
+ * // John,30
15
+ * // Jane,25
16
+ */
17
+ function createCSV(data, columns, options = {}) {
18
+ const { delimiter = ",", addHeader = true, quoteAll = false } = options;
19
+ const escapeAndQuote = (value) => escapeCSVValue(value, {
20
+ delimiter,
21
+ quoteAll
22
+ });
23
+ const rows = data.map((obj) => columns.map((key) => escapeAndQuote(obj[key])).join(delimiter));
24
+ if (addHeader) rows.unshift(columns.map(escapeAndQuote).join(delimiter));
25
+ return rows.join("\n");
26
+ }
27
+ /**
28
+ * Escapes a value for a CSV string.
29
+ *
30
+ * @remarks
31
+ * Returns an empty string if the value is `null` or `undefined`.
32
+ * Values containing delimiters, quotes, or line breaks are quoted.
33
+ * Within quoted values, double quotes are escaped by doubling them.
34
+ *
35
+ * @example
36
+ * escapeCSVValue('hello, world') // "hello, world"
37
+ * escapeCSVValue('contains "quotes"') // "contains ""quotes"""
38
+ */
39
+ function escapeCSVValue(value, options = {}) {
40
+ const { delimiter = ",", quoteAll = false } = options;
41
+ if (value == null) return "";
42
+ const stringValue = String(value);
43
+ const needsQuoting = quoteAll || stringValue.includes(delimiter) || stringValue.includes("\"") || stringValue.includes("\n") || stringValue.includes("\r");
44
+ if (needsQuoting) return `"${stringValue.replaceAll("\"", "\"\"")}"`;
45
+ return stringValue;
46
+ }
47
+ /**
48
+ * Parses a comma-separated values (CSV) string into an array of objects.
49
+ *
50
+ * @remarks
51
+ * The first row of the CSV string is used as the header row.
52
+ *
53
+ * @example
54
+ * const csv = `name,age
55
+ * John,30
56
+ * Jane,25`
57
+ *
58
+ * const data = parseCSV<'name' | 'age'>(csv)
59
+ * // [{ name: 'John', age: '30' }, { name: 'Jane', age: '25' }]
60
+ */
61
+ function parseCSV(csv, options = {}) {
62
+ if (!csv?.trim()) return [];
63
+ const rows = [];
64
+ let currentRow = [];
65
+ let currentField = "";
66
+ let inQuotes = false;
67
+ const { delimiter = ",", trimValues = true } = options;
68
+ for (let i = 0; i < csv.length; i++) {
69
+ const char = csv[i];
70
+ const nextChar = i + 1 < csv.length ? csv[i + 1] : "";
71
+ if (char === "\"") if (inQuotes && nextChar === "\"") {
72
+ currentField += "\"";
73
+ i++;
74
+ } else inQuotes = !inQuotes;
75
+ else if (char === delimiter && !inQuotes) {
76
+ currentRow.push(trimValues ? currentField.trim() : currentField);
77
+ currentField = "";
78
+ } else if ((char === "\n" || char === "\r" && nextChar === "\n") && !inQuotes) {
79
+ if (char === "\r") i++;
80
+ currentRow.push(trimValues ? currentField.trim() : currentField);
81
+ rows.push(currentRow);
82
+ currentRow = [];
83
+ currentField = "";
84
+ } else currentField += char;
85
+ }
86
+ if (currentField || currentRow.length > 0) {
87
+ currentRow.push(trimValues ? currentField.trim() : currentField);
88
+ rows.push(currentRow);
89
+ }
90
+ if (rows.length <= 1) return [];
91
+ const headers = rows[0];
92
+ return rows.slice(1).filter((row) => row.some((field) => field.trim().length > 0)).map((values) => {
93
+ return Object.fromEntries(headers.map((header, index) => [header, index < values.length ? values[index] : ""]));
94
+ });
95
+ }
96
+
97
+ //#endregion
98
+ export { createCSV, escapeCSVValue, parseCSV };
package/dist/defu.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ //#region src/defu.d.ts
2
+ // Forked from unjs/defu (MIT)
3
+ type PlainObject = Record<PropertyKey, any>;
4
+ type DefuMerger<T extends PlainObject = PlainObject> = (target: T, key: PropertyKey, value: any, namespace: string) => boolean | void;
5
+ /**
6
+ * Defu function type that accepts a source and multiple defaults
7
+ */
8
+ type DefuFn = <T extends PlainObject>(source: T, ...defaults: PlainObject[]) => T;
9
+ /**
10
+ * Create a defu function with optional custom merger
11
+ */
12
+ declare function createDefu(merger?: DefuMerger): DefuFn;
13
+ declare const defu: DefuFn;
14
+ //#endregion
15
+ export { DefuFn, DefuMerger, createDefu, defu };
package/dist/defu.js ADDED
@@ -0,0 +1,34 @@
1
+ //#region src/defu.ts
2
+ /**
3
+ * Create a defu function with optional custom merger
4
+ */
5
+ function createDefu(merger) {
6
+ return (source, ...defaults) => {
7
+ return defaults.reduce((acc, current) => _defu(acc, current ?? {}, "", merger), source ?? {});
8
+ };
9
+ }
10
+ const defu = createDefu();
11
+ function _defu(source, defaults, namespace = "", merger) {
12
+ if (!isPlainObject(defaults)) return source;
13
+ const result = { ...defaults };
14
+ for (const [key, value] of Object.entries(source)) {
15
+ if (key === "__proto__" || key === "constructor") continue;
16
+ if (value == null) continue;
17
+ if (merger?.(result, key, value, namespace)) continue;
18
+ const currentNamespace = namespace ? `${namespace}.${key}` : key;
19
+ if (Array.isArray(value) && Array.isArray(result[key])) result[key] = [...value, ...result[key]];
20
+ else if (isPlainObject(value) && isPlainObject(result[key])) result[key] = _defu(value, result[key], currentNamespace, merger);
21
+ else result[key] = value;
22
+ }
23
+ return result;
24
+ }
25
+ function isPlainObject(value) {
26
+ if (value === null || typeof value !== "object") return false;
27
+ const prototype = Object.getPrototypeOf(value);
28
+ if (prototype !== null && prototype !== Object.prototype && Object.getPrototypeOf(prototype) !== null) return false;
29
+ if (Symbol.iterator in value) return false;
30
+ return true;
31
+ }
32
+
33
+ //#endregion
34
+ export { createDefu, defu };
package/dist/emitter.d.ts CHANGED
@@ -1,24 +1,29 @@
1
+ //#region src/emitter.d.ts
2
+ /* eslint-disable ts/method-signature-style */
1
3
  type EventType = string | symbol;
4
+ // An event handler can take an optional event argument
5
+ // and should not return a value
2
6
  type Handler<T = unknown> = (event: T) => void;
3
7
  type WildcardHandler<T = Record<string, unknown>> = (type: keyof T, event: T[keyof T]) => void;
4
8
  type EventHandlerList<T = unknown> = Handler<T>[];
5
9
  type WildCardEventHandlerList<T = Record<string, unknown>> = WildcardHandler<T>[];
6
- type EventHandlerMap<Events extends Record<EventType, unknown>> = Map<keyof Events | '*', EventHandlerList<Events[keyof Events]> | WildCardEventHandlerList<Events>>;
10
+ // A map of event types and their corresponding event handlers.
11
+ type EventHandlerMap<Events extends Record<EventType, unknown>> = Map<keyof Events | "*", EventHandlerList<Events[keyof Events]> | WildCardEventHandlerList<Events>>;
7
12
  interface Emitter<Events extends Record<EventType, unknown>> {
8
- events: EventHandlerMap<Events>;
9
- on<Key extends keyof Events>(type: Key, handler: Handler<Events[Key]>): void;
10
- on(type: '*', handler: WildcardHandler<Events>): void;
11
- off<Key extends keyof Events>(type: Key, handler?: Handler<Events[Key]>): void;
12
- off(type: '*', handler: WildcardHandler<Events>): void;
13
- emit<Key extends keyof Events>(type: Key, event: Events[Key]): void;
14
- emit<Key extends keyof Events>(type: undefined extends Events[Key] ? Key : never): void;
13
+ events: EventHandlerMap<Events>;
14
+ on<Key extends keyof Events>(type: Key, handler: Handler<Events[Key]>): void;
15
+ on(type: "*", handler: WildcardHandler<Events>): void;
16
+ off<Key extends keyof Events>(type: Key, handler?: Handler<Events[Key]>): void;
17
+ off(type: "*", handler: WildcardHandler<Events>): void;
18
+ emit<Key extends keyof Events>(type: Key, event: Events[Key]): void;
19
+ emit<Key extends keyof Events>(type: undefined extends Events[Key] ? Key : never): void;
15
20
  }
16
21
  /**
17
- * Simple functional event emitter / pubsub.
18
- *
19
- * @remarks Ported from `mitt`.
20
- * @see https://github.com/developit/mitt
21
- */
22
+ * Simple functional event emitter / pubsub.
23
+ *
24
+ * @remarks Ported from `mitt`.
25
+ * @see https://github.com/developit/mitt
26
+ */
22
27
  declare function createEmitter<Events extends Record<EventType, unknown>>(events?: EventHandlerMap<Events>): Emitter<Events>;
23
-
24
- export { type Emitter, type EventHandlerList, type EventHandlerMap, type EventType, type Handler, type WildCardEventHandlerList, type WildcardHandler, createEmitter };
28
+ //#endregion
29
+ export { Emitter, EventHandlerList, EventHandlerMap, EventType, Handler, WildCardEventHandlerList, WildcardHandler, createEmitter };
@@ -0,0 +1,32 @@
1
+ //#region src/emitter.ts
2
+ /**
3
+ * Simple functional event emitter / pubsub.
4
+ *
5
+ * @remarks Ported from `mitt`.
6
+ * @see https://github.com/developit/mitt
7
+ */
8
+ function createEmitter(events) {
9
+ events ||= /* @__PURE__ */ new Map();
10
+ return {
11
+ events,
12
+ on(type, handler) {
13
+ const handlers = events.get(type);
14
+ if (handlers) handlers.push(handler);
15
+ else events.set(type, [handler]);
16
+ },
17
+ off(type, handler) {
18
+ const handlers = events.get(type);
19
+ if (handlers) if (handler) handlers.splice(handlers.indexOf(handler) >>> 0, 1);
20
+ else events.set(type, []);
21
+ },
22
+ emit(type, evt) {
23
+ let handlers = events.get(type);
24
+ if (handlers) for (const handler of [...handlers]) handler(evt);
25
+ handlers = events.get("*");
26
+ if (handlers) for (const handler of [...handlers]) handler(type, evt);
27
+ }
28
+ };
29
+ }
30
+
31
+ //#endregion
32
+ export { createEmitter };
package/dist/index.d.ts CHANGED
@@ -1,10 +1,12 @@
1
- export { MaybeArray, toArray } from './array.js';
2
- export { CSVRow, createCSV, escapeCSVValue, parseCSV } from './csv.js';
3
- export { Emitter, EventHandlerList, EventHandlerMap, EventType, Handler, WildCardEventHandlerList, WildcardHandler, createEmitter } from './emitter.js';
4
- export { cloneJSON, tryParseJSON } from './json.js';
5
- export { interopDefault } from './module.js';
6
- export { deepApply, memoize, objectEntries, objectKeys } from './object.js';
7
- export { QueryObject, QueryValue, getPathname, joinURL, withBase, withLeadingSlash, withQuery, withTrailingSlash, withoutBase, withoutLeadingSlash, withoutTrailingSlash } from './path.js';
8
- export { Err, ErrData, Ok, OkData, Result, ResultData, err, ok, toResult, tryCatch, unwrapResult } from './result.js';
9
- export { generateRandomId, template } from './string.js';
10
- export { AutocompletableString, LooseAutocomplete, UnifyIntersection } from './types.js';
1
+ import { MaybeArray, toArray } from "./array.js";
2
+ import { CSVRow, createCSV, escapeCSVValue, parseCSV } from "./csv.js";
3
+ import { DefuFn, DefuMerger, createDefu, defu } from "./defu.js";
4
+ import { Emitter, EventHandlerList, EventHandlerMap, EventType, Handler, WildCardEventHandlerList, WildcardHandler, createEmitter } from "./emitter.js";
5
+ import { cloneJSON, tryParseJSON } from "./json.js";
6
+ import { interopDefault } from "./module.js";
7
+ import { deepApply, memoize, objectEntries, objectKeys } from "./object.js";
8
+ import { QueryObject, QueryValue, getPathname, joinURL, withBase, withLeadingSlash, withQuery, withTrailingSlash, withoutBase, withoutLeadingSlash, withoutTrailingSlash } from "./path.js";
9
+ import { Err, ErrData, Ok, OkData, Result, ResultData, err, ok, toResult, tryCatch, unwrapResult } from "./result.js";
10
+ import { generateRandomId, template } from "./string.js";
11
+ import { AutocompletableString, LooseAutocomplete, UnifyIntersection } from "./types.js";
12
+ export { AutocompletableString, CSVRow, DefuFn, DefuMerger, Emitter, Err, ErrData, EventHandlerList, EventHandlerMap, EventType, Handler, LooseAutocomplete, MaybeArray, Ok, OkData, QueryObject, QueryValue, Result, ResultData, UnifyIntersection, WildCardEventHandlerList, WildcardHandler, cloneJSON, createCSV, createDefu, createEmitter, deepApply, defu, err, escapeCSVValue, generateRandomId, getPathname, interopDefault, joinURL, memoize, objectEntries, objectKeys, ok, parseCSV, template, toArray, toResult, tryCatch, tryParseJSON, unwrapResult, withBase, withLeadingSlash, withQuery, withTrailingSlash, withoutBase, withoutLeadingSlash, withoutTrailingSlash };
package/dist/index.js ADDED
@@ -0,0 +1,12 @@
1
+ import { toArray } from "./array.js";
2
+ import { createCSV, escapeCSVValue, parseCSV } from "./csv.js";
3
+ import { createDefu, defu } from "./defu.js";
4
+ import { createEmitter } from "./emitter.js";
5
+ import { cloneJSON, tryParseJSON } from "./json.js";
6
+ import { interopDefault } from "./module.js";
7
+ import { deepApply, memoize, objectEntries, objectKeys } from "./object.js";
8
+ import { getPathname, joinURL, withBase, withLeadingSlash, withQuery, withTrailingSlash, withoutBase, withoutLeadingSlash, withoutTrailingSlash } from "./path.js";
9
+ import { Err, Ok, err, ok, toResult, tryCatch, unwrapResult } from "./result.js";
10
+ import { generateRandomId, template } from "./string.js";
11
+
12
+ export { Err, Ok, cloneJSON, createCSV, createDefu, createEmitter, deepApply, defu, err, escapeCSVValue, generateRandomId, getPathname, interopDefault, joinURL, memoize, objectEntries, objectKeys, ok, parseCSV, template, toArray, toResult, tryCatch, tryParseJSON, unwrapResult, withBase, withLeadingSlash, withQuery, withTrailingSlash, withoutBase, withoutLeadingSlash, withoutTrailingSlash };
package/dist/json.d.ts CHANGED
@@ -1,16 +1,17 @@
1
+ //#region src/json.d.ts
1
2
  /**
2
- * Type-safe wrapper around `JSON.stringify`.
3
- *
4
- * @remarks
5
- * Falls back to the original value if the JSON serialization fails or the value is not a string.
6
- */
3
+ * Type-safe wrapper around `JSON.stringify`.
4
+ *
5
+ * @remarks
6
+ * Falls back to the original value if the JSON serialization fails or the value is not a string.
7
+ */
7
8
  declare function tryParseJSON<T = unknown>(value: unknown): T;
8
9
  /**
9
- * Clones the given JSON value.
10
- *
11
- * @remarks
12
- * The value must not contain circular references as JSON does not support them. It also must contain JSON serializable values.
13
- */
10
+ * Clones the given JSON value.
11
+ *
12
+ * @remarks
13
+ * The value must not contain circular references as JSON does not support them. It also must contain JSON serializable values.
14
+ */
14
15
  declare function cloneJSON<T>(value: T): T;
15
-
16
- export { cloneJSON, tryParseJSON };
16
+ //#endregion
17
+ export { cloneJSON, tryParseJSON };
package/dist/json.js ADDED
@@ -0,0 +1,34 @@
1
+ //#region src/json.ts
2
+ /**
3
+ * Type-safe wrapper around `JSON.stringify`.
4
+ *
5
+ * @remarks
6
+ * Falls back to the original value if the JSON serialization fails or the value is not a string.
7
+ */
8
+ function tryParseJSON(value) {
9
+ if (typeof value !== "string") return value;
10
+ try {
11
+ return JSON.parse(value);
12
+ } catch {
13
+ return value;
14
+ }
15
+ }
16
+ /**
17
+ * Clones the given JSON value.
18
+ *
19
+ * @remarks
20
+ * The value must not contain circular references as JSON does not support them. It also must contain JSON serializable values.
21
+ */
22
+ function cloneJSON(value) {
23
+ if (typeof value !== "object" || value === null) return value;
24
+ if (Array.isArray(value)) return value.map((e) => typeof e !== "object" || e === null ? e : cloneJSON(e));
25
+ const result = {};
26
+ for (const k in value) {
27
+ const v = value[k];
28
+ result[k] = typeof v !== "object" || v === null ? v : cloneJSON(v);
29
+ }
30
+ return result;
31
+ }
32
+
33
+ //#endregion
34
+ export { cloneJSON, tryParseJSON };